[#38392] Enumerable#gather_each — Tanaka Akira <akr@...>

ときに、複数行をまとめて扱いたいことがあります。

47 messages 2009/05/09
[#38394] Re: Enumerable#gather_each — ujihisa <ujihisa@...> 2009/05/09

ujihisaと申します。

[#38400] Re: Enumerable#gather_each — Yukihiro Matsumoto <matz@...> 2009/05/09

まつもと ゆきひろです

[#38399] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/09

At Sat, 9 May 2009 15:30:20 +0900,

[#38405] Re: Enumerable#gather_each — Tanaka Akira <akr@...> 2009/05/10

In article <86r5yy2nrg.knu@iDaemons.org>,

[#38417] Re: Enumerable#gather_each — "Akinori MUSHA" <knu@...> 2009/05/10

At Sun, 10 May 2009 10:08:47 +0900,

[#38524] [Bug #1503] -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる — sinnichi eguchi <redmine@...>

Bug #1503: -Kuをつけた時、/[#{s}]/n と Regexp.new("[#{s}]",nil,"n") で実行結果が異なる

8 messages 2009/05/22

[ruby-dev:38464] Re: SQLiteライブラリ

From: Hirotsugu Asari <asari.ruby@...>
Date: 2009-05-15 14:21:35 UTC
List: ruby-dev #38464
浅里と申します。

JRubyを使う身分としてはFFI経由でやってくれると有り難いです。
「標準」という訳には行かなくなっちゃいますけど。

--  
Hirotsugu Asari



On May 15, 2009, at 9:12 AM, NARUSE, Yui wrote:

> 成瀬です。
>
> Ruby 1.9でSQLiteライブラリを標準添付しませんか?
>
> 旧来からsqlite3-rubyという物は存在していましたが、
> http://rubyforge.org/projects/sqlite-ruby
> http://sqlite-ruby.rubyforge.org/sqlite3/
>
> * nativeでなくdl経由
> * 作者がメンテナンス終了を宣言
>  http://weblog.jamisbuck.org/2009/2/25/net-ssh-capistrano-and-saying-goodbye
> という致命的な物を含む問題があります。
>
>
> これに対して今回新しく起こしたライブラリでは、
> * nativeかつコンパクトな実装
> * それなりにやる気のあるメンテナ
> という利点があります。
>
> 使う際には以下のような感じになります。
> 多段transactionやbackupが特徴でしょうか。
>
>  SQLite.open(":memory:") do |db|
>    db.transaction do
>    db.exec("CREATE TABLE foo (int INTEGER, text TEXT)")
>      10.times do |i|
>        db.exec("INSERT INTO foo VALUES (#{i}, 'foo')")
>      end
>
>      p db.exec("SELECT * FROM foo")[0][9999]
>      p db.exec("SELECT * FROM foo")[0].colnames
>
>      db.transaction do
>        10.times do |i|
>          db.exec("INSERT INTO foo VALUES (#{i}, 'foo')")
>        end
>        db.rollback
>      end
>
>      SQLite.open(":memory:") do |db2|
>        db.backup(db2)
>      end
>    end
>  end
>
> ご意見等あればよろしくお願いします。
>
> --  
> NARUSE, Yui  <naruse@airemix.jp>
> diff --git a/ext/sqlite/extconf.rb b/ext/sqlite/extconf.rb
> new file mode 100644
> index 0000000..e042531
> --- /dev/null
> +++ b/ext/sqlite/extconf.rb
> @@ -0,0 +1,5 @@
> +require 'mkmf'
> +
> +dir_config('sqlite')
> +have_library('sqlite3')
> +create_makefile('sqlite')
> diff --git a/ext/sqlite/sqlite.c b/ext/sqlite/sqlite.c
> new file mode 100644
> index 0000000..d4d5644
> --- /dev/null
> +++ b/ext/sqlite/sqlite.c
> @@ -0,0 +1,421 @@
> +/************************************************
> + *
> + * sqlite.c -
> + *
> + ************************************************/
> +
> +#include "ruby/ruby.h"
> +#include "ruby/encoding.h"
> +#include "sqlite3.h"
> +
> +static VALUE rb_cSQLite;
> +
> +struct rb_sqlite {
> +    sqlite3 *db;
> +    VALUE transactions;
> +    int success;
> +};
> +
> +static void
> +rb_sqlite_closed()
> +{
> +    rb_raise(rb_eArgError, "closed SQLite Database");
> +}
> +
> +static struct rb_sqlite *
> +rb_sqlite_struct(VALUE obj)
> +{
> +    struct rb_sqlite *sdb;
> +    Data_Get_Struct(obj, struct rb_sqlite, sdb);
> +    if (sdb == 0) rb_sqlite_closed();
> +    if (sdb->db == 0) rb_sqlite_closed();
> +    return sdb;
> +}
> +
> +static void
> +rb_sqlite_mark(void *p)
> +{
> +    struct rb_sqlite *sdb = p;
> +    if (sdb) {
> +        rb_gc_mark(sdb->transactions);
> +    }
> +}
> +
> +static int
> +rb_sqlite_p(VALUE obj)
> +{
> +    struct rb_sqlite *ptr;
> +    if (TYPE(time) == T_DATA && RDATA(obj)->dmark !=  
> rb_sqlite_mark) {
> +        return Qfalse;
> +    }
> +    return Qtrue;
> +}
> +
> +#define RSQLITE(obj) rb_sqlite_struct(obj)
> +#define RSQLITE_DB(obj) (RSQLITE(obj)->db)
> +#define RSQLITE_P(obj) rb_sqlite_p(obj)
> +#define rb_sqlite_transactions(obj) (RSQLITE(obj)->transactions)
> +
> +static void
> +rb_sqlite_free(struct rb_sqlite *sdb)
> +{
> +    if (sdb->db) sqlite3_close(sdb->db);
> +    xfree(sdb);
> +}
> +
> +static VALUE
> +rb_sqlite_alloc(VALUE klass)
> +{
> +    struct rb_sqlite *sdb;
> +    VALUE obj = Data_Make_Struct(klass, struct rb_sqlite,
> +            rb_sqlite_mark, rb_sqlite_free, sdb);
> +    sdb->db = NULL;
> +    sdb->transactions = rb_ary_new();
> +    sdb->success = Qfalse;
> +    return obj;
> +}
> +
> +static int
> +rb_sqlite_sleep_callback(void *arg, int count)
> +{
> +    if (count > 4) return 0;
> +    rb_thread_sleep(250);
> +    return 1;
> +}
> +
> +static VALUE
> +rb_sqlite_initialize(VALUE obj, VALUE path)
> +{
> +    sqlite3 *db;
> +    struct rb_sqlite *sdb;
> +    int rc;
> +
> +    FilePathValue(path);
> +
> +    rc = sqlite3_open(RSTRING_PTR(path), &db);
> +    if (rc != SQLITE_OK) {
> +        VALUE msg = rb_str_new_cstr(sqlite3_errmsg(db));
> +        sqlite3_close(db);
> +        rb_raise(rb_eArgError, "Can't open database: %s (%s)",
> +                RSTRING_PTR(path), RSTRING_PTR(msg));
> +    }
> +    rc = sqlite3_busy_handler(db, rb_sqlite_sleep_callback, NULL);
> +    Data_Get_Struct(obj, struct rb_sqlite, sdb);
> +    sdb->db = db;
> +    return obj;
> +}
> +
> +/*
> + * call-seq: SQLite#close
> + *
> + * close database.
> + */
> +static VALUE
> +rb_sqlite_close(VALUE obj)
> +{
> +    struct rb_sqlite *sdb = RSQLITE(obj);
> +    sqlite3_close(sdb->db);
> +    sdb->db = 0;
> +
> +    return Qnil;
> +}
> +
> +/*
> + * call-seq: SQLite.open(filename)
> + *
> + * open SQLite database.
> + */
> +static VALUE
> +rb_sqlite_s_open(VALUE klass, VALUE path)
> +{
> +    VALUE obj = rb_sqlite_alloc(klass);
> +
> +    if (NIL_P(rb_sqlite_initialize(obj, path))) {
> +        return Qnil;
> +    }
> +
> +    if (rb_block_given_p()) {
> +        return rb_ensure(rb_yield, obj, rb_sqlite_close, obj);
> +    }
> +
> +    return obj;
> +}
> +
> +static VALUE
> +rb_sqlite_cols_colnames(VALUE self)
> +{
> +    return rb_ivar_get(self, rb_intern("colnames"));
> +}
> +
> +static VALUE
> +rb_sqlite_cols_aref(int argc, VALUE *argv, VALUE self)
> +{
> +    if (argc == 1 && TYPE(argv[0]) == T_STRING) {
> +        VALUE colname = argv[0];
> +        int i;
> +        VALUE colnames = rb_sqlite_cols_colnames(self);
> +        for (i = 0; i < RARRAY_LEN(colnames); i++) {
> +            if (rb_str_equal(colname, rb_ary_entry(colnames, i))) {
> +                return rb_ary_entry(self, i);
> +            }
> +        }
> +    }
> +    else {
> +        return rb_ary_aref(argc, argv, self);
> +    }
> +}
> +
> +static void
> +rb_sqlite_work_init(VALUE work, sqlite3_stmt *stmt)
> +{
> +    VALUE colnames, proto_cols;
> +    int i, count = sqlite3_column_count(stmt);
> +
> +    colnames = rb_ary_new2(count);
> +    for (i = 0; i < count; i++) {
> +        rb_ary_push(colnames,  
> rb_str_new_cstr(sqlite3_column_name(stmt, i)));
> +    }
> +
> +    proto_cols = rb_ary_new2(count);
> +    rb_ivar_set(proto_cols, rb_intern("colnames"), colnames);
> +    rb_define_singleton_method(proto_cols, "[]",  
> rb_sqlite_cols_aref, -1);
> +    rb_define_singleton_method(proto_cols, "colnames",  
> rb_sqlite_cols_colnames, 0);
> +    rb_ary_push(work, proto_cols);
> +}
> +
> +#define rb_sqlite_work_proto_cols(work) rb_ary_entry(work, 0)
> +
> +static VALUE
> +rb_sqlite_work_new(sqlite3_stmt *stmt)
> +{
> +    VALUE work = rb_ary_new2(1); /* [proto_cols] */
> +    rb_sqlite_work_init(work, stmt);
> +    return work;
> +}
> +
> +static VALUE
> +rb_sqlite_work_cols_new(VALUE work, sqlite3_stmt *stmt)
> +{
> +    VALUE proto_cols, cols;
> +    int i, count = sqlite3_column_count(stmt);
> +
> +    proto_cols = rb_sqlite_work_proto_cols(work);
> +    cols = rb_obj_clone(proto_cols);
> +    for (i = 0; i < count; i++) {
> +        VALUE value;
> +        int type = sqlite3_column_type(stmt, i);
> +        switch (type) {
> +          case SQLITE_INTEGER:
> +#ifdef HAVE_LONG_LONG
> +            {
> +                sqlite3_int64 intval = sqlite3_column_int64(stmt, i);
> +                value = LL2NUM((LONG_LONG)intval);
> +            }
> +#else
> +            value = sqlite3_column_int(stmt, i);
> +#endif
> +            break;
> +          case SQLITE_FLOAT:
> +            {
> +                double dblval = sqlite3_column_double(stmt, i);
> +                value = DBL2NUM(dblval);
> +            }
> +            break;
> +          case SQLITE_TEXT:
> +          case SQLITE_BLOB:
> +            {
> +                int len = sqlite3_column_bytes(stmt, i);
> +                const unsigned char *text =  
> sqlite3_column_text(stmt, i);
> +                rb_encoding *enc =
> +                    type == SQLITE_TEXT ? rb_utf8_encoding() :  
> rb_ascii8bit_encoding();
> +                value = rb_str_new_cstr(text);
> +            }
> +            break;
> +          case SQLITE_NULL:
> +            value = Qnil;
> +            break;
> +        }
> +        rb_ary_push(cols, value);
> +    }
> +
> +    return cols;
> +}
> +
> +static VALUE
> +rb_sqlite_exec(VALUE obj, VALUE sql)
> +{
> +    VALUE work = Qnil, result = Qnil;
> +    sqlite3_stmt *stmt;
> +    int i, rc;
> +
> +    rc = sqlite3_prepare_v2(RSQLITE_DB(obj),
> +            RSTRING_PTR(sql), RSTRING_LEN(sql), &stmt, NULL);
> +    if (rc != SQLITE_OK) {
> +        rb_raise(rb_eArgError, "SQL error: %d %s", rc,  
> sqlite3_errmsg(RSQLITE_DB(obj)));
> +    }
> +
> +    for (;;) {
> +        rc = sqlite3_step(stmt);
> +        switch (rc) {
> +          case SQLITE_ROW:
> +            {
> +                VALUE cols;
> +                if (NIL_P(work)) {
> +                    work = rb_sqlite_work_new(stmt);
> +                    if (!rb_block_given_p()) {
> +                        result =  
> rb_ary_new2(sqlite3_data_count(stmt));
> +                    }
> +                }
> +                cols = rb_sqlite_work_cols_new(work, stmt);
> +                if (rb_block_given_p()) {
> +                    rb_yield(cols);
> +                }
> +                else {
> +                    rb_ary_push(result, cols);
> +                }
> +            }
> +            continue;
> +          case SQLITE_DONE:
> +            break;
> +          case SQLITE_BUSY:
> +            /* after process busy handler */
> +          default:
> +            sqlite3_finalize(stmt);
> +            rb_raise(rb_eArgError, "SQL error: %d %s", rc,  
> sqlite3_errmsg(RSQLITE_DB(obj)));
> +        }
> +        break;
> +    }
> +
> +    sqlite3_finalize(stmt);
> +    return result;
> +}
> +
> +static VALUE
> +rb_sqlite_release(VALUE self)
> +{
> +    int rc;
> +    char *errmsg;
> +    VALUE transactions, sql, savepoint;
> +
> +    transactions = rb_sqlite_transactions(self);
> +    savepoint = rb_ary_entry(transactions, -1);
> +    if (NIL_P(savepoint)) return Qnil;
> +
> +    sql = rb_str_append(rb_str_new_cstr("RELEASE  
> RSQLITE_SAVEPOINT_"), savepoint);
> +    rc = sqlite3_exec(RSQLITE_DB(self), RSTRING_PTR(sql), NULL,  
> NULL, &errmsg);
> +    if (rc != SQLITE_OK) {
> +        VALUE msg = rb_str_new_cstr(errmsg);
> +        sqlite3_free(errmsg);
> +        rb_raise(rb_eRuntimeError, "Can't release (%s)",  
> RSTRING_PTR(msg));
> +    }
> +    rb_ary_pop(transactions);
> +    RSQLITE(self)->success = Qfalse;
> +    return Qnil;
> +}
> +
> +static VALUE
> +rb_sqlite_rollback(VALUE self)
> +{
> +    int rc;
> +    char *errmsg;
> +    VALUE transactions, sql, savepoint;
> +
> +    transactions = rb_sqlite_transactions(self);
> +    savepoint = rb_ary_entry(transactions, -1);
> +    if (NIL_P(savepoint)) return Qnil;
> +
> +    sql = rb_str_append(rb_str_new_cstr("ROLLBACK TO  
> RSQLITE_SAVEPOINT_"), savepoint);
> +    rc = sqlite3_exec(RSQLITE_DB(self), RSTRING_PTR(sql), NULL,  
> NULL, &errmsg);
> +    if (rc != SQLITE_OK) {
> +        VALUE msg = rb_str_new_cstr(errmsg);
> +        sqlite3_free(errmsg);
> +        rb_raise(rb_eRuntimeError, "Can't rollback (%s)",  
> RSTRING_PTR(msg));
> +    }
> +    return Qnil;
> +}
> +
> +static VALUE
> +rb_sqlite_transaction_end(VALUE self)
> +{
> +    if (RSQLITE(self)->success == Qfalse) {
> +        rb_sqlite_rollback(self);
> +    }
> +    return rb_sqlite_release(self);
> +}
> +
> +static VALUE
> +rb_sqlite_transaction_i(VALUE self)
> +{
> +    VALUE res = rb_yield(self);
> +    RSQLITE(self)->success = Qtrue;
> +    return res;
> +}
> +
> +static VALUE
> +rb_sqlite_transaction(VALUE self)
> +{
> +    VALUE result, transactions, sql, savepoint;
> +    int rc;
> +    char *errmsg;
> +
> +    RETURN_ENUMERATOR(self, 0, 0);
> +
> +    transactions = rb_sqlite_transactions(self);
> +    savepoint = rb_sprintf("%d", RARRAY_LEN(transactions));
> +
> +    sql = rb_str_append(rb_str_new_cstr("SAVEPOINT  
> RSQLITE_SAVEPOINT_"), savepoint);
> +    rc = sqlite3_exec(RSQLITE_DB(self), RSTRING_PTR(sql), NULL,  
> NULL, &errmsg);
> +
> +    if (rc != SQLITE_OK) {
> +        VALUE msg = rb_str_new_cstr(errmsg);
> +        sqlite3_free(errmsg);
> +        rb_raise(rb_eRuntimeError, "Can't begin transaction (%s)",  
> RSTRING_PTR(msg));
> +    }
> +
> +    rb_ary_push(transactions, savepoint);
> +    RSQLITE(self)->success = Qfalse;
> +
> +    return rb_ensure(rb_sqlite_transaction_i, self,  
> rb_sqlite_transaction_end, self);
> +}
> +
> +/*
> + * call-seq: SQLite#backup(database)
> + *
> + */
> +static VALUE
> +rb_sqlite_backup(VALUE self, VALUE dst)
> +{
> +    sqlite3 *db, *dstdb;
> +    sqlite3_backup *backup;
> +    int rc = 0;
> +
> +    db = RSQLITE_DB(self);
> +    dstdb = RSQLITE_DB(dst);
> +
> +    backup = sqlite3_backup_init(dstdb, "main", db, "main");
> +    if (backup) {
> +        sqlite3_backup_step(backup, -1);
> +        sqlite3_backup_finish(backup);
> +    }
> +    rc = sqlite3_errcode(dstdb);
> +    if (rc != SQLITE_OK) {
> +        rb_raise(rb_eArgError, "Can't backup: %s",  
> sqlite3_errmsg(dstdb));
> +    }
> +
> +    return Qnil;
> +}
> +
> +void
> +Init_sqlite()
> +{
> +    rb_cSQLite = rb_define_class("SQLite", rb_cObject);
> +    rb_define_alloc_func(rb_cSQLite, rb_sqlite_alloc);
> +    rb_define_singleton_method(rb_cSQLite, "open",  
> rb_sqlite_s_open, 1);
> +    rb_define_method(rb_cSQLite, "initialize",  
> rb_sqlite_initialize, 1);
> +    rb_define_method(rb_cSQLite, "close", rb_sqlite_close, 0);
> +    rb_define_method(rb_cSQLite, "exec", rb_sqlite_exec, 1);
> +    rb_define_method(rb_cSQLite, "rollback", rb_sqlite_rollback, 0);
> +    rb_define_method(rb_cSQLite, "transaction",  
> rb_sqlite_transaction, 0);
> +    rb_define_method(rb_cSQLite, "backup", rb_sqlite_backup, 1);
> +    rb_define_const(rb_cSQLite, "VERSION",   
> rb_str_new_cstr(SQLITE_VERSION));
> +}
> diff --git a/test/sqlite/test_sqlite.rb b/test/sqlite/test_sqlite.rb
> new file mode 100644
> index 0000000..06a5973
> --- /dev/null
> +++ b/test/sqlite/test_sqlite.rb
> @@ -0,0 +1,86 @@
> +require 'test/unit'
> +
> +begin
> +  require 'sqlite'
> +rescue LoadError
> +end
> +
> +class TestSQLite < Test::Unit::TestCase
> +  def test_sqlite
> +    assert_kind_of String, SQLite::VERSION
> +    SQLite.open(":memory:") do |db|
> +      assert_nil db.exec("CREATE TABLE foo (int INTEGER, text TEXT)")
> +      10.times do |i|
> +        assert_nil db.exec("INSERT INTO foo VALUES (#{i}, 'foo')")
> +      end
> +
> +      assert_nil db.exec("SELECT * FROM foo")[0][9999]
> +
> +      p db.exec("SELECT * FROM foo")[0].colnames
> +      assert_equal %w/int text/, db.exec("SELECT * FROM foo") 
> [0].colnames
> +
> +      pp rows = db.exec("SELECT * FROM foo")
> +      db.exec("SELECT * FROM foo") do |cols|
> +       assert_kind_of Array, cols
> +      end
> +
> +      db.transaction do
> +        4294967295.upto(4294967297) do |i|
> +          assert_nil db.exec("INSERT INTO foo VALUES (#{i}, 'foo')")
> +        end
> +
> +        i = (1 << 63) - 1
> +        assert_nil db.exec("INSERT INTO foo VALUES (#{i},  
> '63bit-1')")
> +        value = db.exec("SELECT int from foo where text='63bit-1'") 
> [0][0]
> +        assert_kind_of Integer, value
> +        assert_equal i, value
> +
> +        i = (1 << 63)
> +        assert_nil db.exec("INSERT INTO foo VALUES (#{i}, '63bit')")
> +        value = db.exec("SELECT int from foo where text='63bit'")[0] 
> [0]
> +        assert_kind_of Float, value
> +        assert_equal i, value
> +        db.rollback
> +      end
> +      assert_equal rows, db.exec("SELECT * FROM foo")
> +
> +      db.transaction do
> +        assert_nil db.exec("INSERT INTO foo VALUES (11, '11')")
> +      end
> +      assert_not_equal rows, db.exec("SELECT * FROM foo")
> +
> +      rows = db.exec("SELECT * FROM foo")
> +      rows1 = nil
> +      db.transaction do
> +        assert_nil db.exec("INSERT INTO foo VALUES (12, '12')")
> +        rows1 = db.exec("SELECT * FROM foo")
> +        assert_not_equal rows, db.exec("SELECT * FROM foo")
> +        db.transaction do
> +          assert_nil db.exec("INSERT INTO foo VALUES (13, '13')")
> +          assert_not_equal rows1, db.exec("SELECT * FROM foo")
> +          db.rollback
> +          db.rollback
> +        end
> +        assert_equal rows1, db.exec("SELECT * FROM foo")
> +      end
> +      assert_not_equal rows, db.exec("SELECT * FROM foo")
> +      assert_equal rows1, db.exec("SELECT * FROM foo")
> +    end
> +  end
> +
> +  def test_backup
> +    SQLite.open(":memory:") do |db1|
> +      SQLite.open(":memory:") do |db2|
> +        db1.transaction do
> +          assert_nil db1.exec("CREATE TABLE foo (int INTEGER, text  
> TEXT)")
> +          10.times do |i|
> +            assert_nil db1.exec("INSERT INTO foo VALUES (#{i},  
> 'foo')")
> +          end
> +        end
> +        assert_nil db1.backup(db2)
> +        rows = db1.exec("SELECT * FROM foo")
> +        assert_equal rows, db2.exec("SELECT * FROM foo")
> +      end
> +    end
> +  end
> +end


In This Thread