sqlite3-1.4.2/ 0000755 0000041 0000041 00000000000 13611407300 013125 5 ustar www-data www-data sqlite3-1.4.2/.travis.yml 0000644 0000041 0000041 00000000756 13611407300 015246 0 ustar www-data www-data language: ruby cache: bundler before_install: - gem update --system 2.7.7 - gem install bundler -v 1.16.2 addons: apt: packages: - libgmp-dev after_failure: - "find . -name mkmf.log -exec cat {} \\;" after_success: - "find . -name mkmf.log -exec cat {} \\;" env: - USE_MINI_PORTILE=true - USE_MINI_PORTILE=false rvm: - 1.9.3 - 2.0.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - ruby-head matrix: allow_failures: - env: USE_MINI_PORTILE=false sqlite3-1.4.2/test/ 0000755 0000041 0000041 00000000000 13611407300 014104 5 ustar www-data www-data sqlite3-1.4.2/test/helper.rb 0000644 0000041 0000041 00000000515 13611407300 015711 0 ustar www-data www-data require 'sqlite3' require 'minitest/autorun' unless RUBY_VERSION >= "1.9" require 'iconv' end module SQLite3 class TestCase < Minitest::Test alias :assert_not_equal :refute_equal alias :assert_not_nil :refute_nil alias :assert_raise :assert_raises def assert_nothing_raised yield end end end sqlite3-1.4.2/test/test_deprecated.rb 0000644 0000041 0000041 00000002061 13611407300 017567 0 ustar www-data www-data require 'helper' module SQLite3 class TestDeprecated < SQLite3::TestCase attr_reader :db def setup super @warn_before = $-w $-w = false @db = SQLite3::Database.new(':memory:') @db.execute 'CREATE TABLE test_table (name text, age int)' end def teardown super $-w = @warn_before end def test_query_with_many_bind_params_not_nil assert_equal [[1, 2]], db.query('select ?, ?', 1, 2).to_a end def test_execute_with_many_bind_params_not_nil assert_equal [[1, 2]], @db.execute("select ?, ?", 1, 2).to_a end def test_query_with_many_bind_params assert_equal [[nil, 1]], @db.query("select ?, ?", nil, 1).to_a end def test_query_with_nil_bind_params assert_equal [['foo']], @db.query("select 'foo'", nil).to_a end def test_execute_with_many_bind_params assert_equal [[nil, 1]], @db.execute("select ?, ?", nil, 1) end def test_execute_with_nil_bind_params assert_equal [['foo']], @db.execute("select 'foo'", nil) end end end sqlite3-1.4.2/test/test_integration_pending.rb 0000644 0000041 0000041 00000004357 13611407300 021530 0 ustar www-data www-data require 'helper' require 'thread' require 'benchmark' class TC_Integration_Pending < SQLite3::TestCase def setup @db = SQLite3::Database.new("test.db") @db.transaction do @db.execute "create table foo ( a integer primary key, b text )" @db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'bar' )" @db.execute "insert into foo ( b ) values ( 'baz' )" end end def teardown @db.close File.delete( "test.db" ) end def test_busy_handler_outwait skip("not working in 1.9") if RUBY_VERSION >= '1.9' busy = Mutex.new busy.lock handler_call_count = 0 t = Thread.new(busy) do |locker| begin db2 = SQLite3::Database.open( "test.db" ) db2.transaction( :exclusive ) do locker.lock end ensure db2.close if db2 end end @db.busy_handler do |data,count| handler_call_count += 1 busy.unlock true end assert_nothing_raised do @db.execute "insert into foo (b) values ( 'from 2' )" end t.join assert_equal 1, handler_call_count end def test_busy_handler_impatient busy = Mutex.new busy.lock handler_call_count = 0 t = Thread.new do begin db2 = SQLite3::Database.open( "test.db" ) db2.transaction( :exclusive ) do busy.lock end ensure db2.close if db2 end end sleep 1 @db.busy_handler do handler_call_count += 1 false end assert_raise( SQLite3::BusyException ) do @db.execute "insert into foo (b) values ( 'from 2' )" end busy.unlock t.join assert_equal 1, handler_call_count end def test_busy_timeout @db.busy_timeout 1000 busy = Mutex.new busy.lock t = Thread.new do begin db2 = SQLite3::Database.open( "test.db" ) db2.transaction( :exclusive ) do busy.lock end ensure db2.close if db2 end end sleep 1 time = Benchmark.measure do assert_raise( SQLite3::BusyException ) do @db.execute "insert into foo (b) values ( 'from 2' )" end end busy.unlock t.join assert time.real*1000 >= 1000 end end sqlite3-1.4.2/test/test_encoding.rb 0000644 0000041 0000041 00000011347 13611407300 017264 0 ustar www-data www-data # -*- coding: utf-8 -*- require 'helper' module SQLite3 class TestEncoding < SQLite3::TestCase def setup @db = SQLite3::Database.new(':memory:') @create = "create table ex(id int, data string)" @insert = "insert into ex(id, data) values (?, ?)" @db.execute(@create); end def test_select_encoding_on_utf_16 str = "foo" utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE" db = SQLite3::Database.new(':memory:'.encode(utf16)) db.execute @create db.execute "insert into ex (id, data) values (1, \"#{str}\")" stmt = db.prepare 'select * from ex where data = ?' ['US-ASCII', utf16, 'EUC-JP', 'UTF-8'].each do |enc| stmt.bind_param 1, str.encode(enc) assert_equal 1, stmt.to_a.length stmt.reset! end end def test_insert_encoding str = "foo" utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE" db = SQLite3::Database.new(':memory:'.encode(utf16)) db.execute @create stmt = db.prepare @insert ['US-ASCII', utf16, 'EUC-JP', 'UTF-8'].each_with_index do |enc,i| stmt.bind_param 1, i stmt.bind_param 2, str.encode(enc) stmt.to_a stmt.reset! end db.execute('select data from ex').flatten.each do |s| assert_equal str, s end end def test_default_internal_is_honored warn_before = $-w $-w = false before_enc = Encoding.default_internal str = "壁に耳あり、障子に目あり" stmt = @db.prepare('insert into ex(data) values (?)') stmt.bind_param 1, str stmt.step Encoding.default_internal = 'EUC-JP' string = @db.execute('select data from ex').first.first assert_equal Encoding.default_internal, string.encoding assert_equal str.encode('EUC-JP'), string assert_equal str, string.encode(str.encoding) ensure Encoding.default_internal = before_enc $-w = warn_before end def test_blob_is_binary str = "猫舌" @db.execute('create table foo(data text)') stmt = @db.prepare('insert into foo(data) values (?)') stmt.bind_param(1, SQLite3::Blob.new(str)) stmt.step string = @db.execute('select data from foo').first.first assert_equal Encoding.find('ASCII-8BIT'), string.encoding assert_equal str, string.force_encoding('UTF-8') end def test_blob_is_ascii8bit str = "猫舌" @db.execute('create table foo(data text)') stmt = @db.prepare('insert into foo(data) values (?)') stmt.bind_param(1, str.dup.force_encoding("ASCII-8BIT")) stmt.step string = @db.execute('select data from foo').first.first assert_equal Encoding.find('ASCII-8BIT'), string.encoding assert_equal str, string.force_encoding('UTF-8') end def test_blob_with_eucjp str = "猫舌".encode("EUC-JP") @db.execute('create table foo(data text)') stmt = @db.prepare('insert into foo(data) values (?)') stmt.bind_param(1, SQLite3::Blob.new(str)) stmt.step string = @db.execute('select data from foo').first.first assert_equal Encoding.find('ASCII-8BIT'), string.encoding assert_equal str, string.force_encoding('EUC-JP') end def test_db_with_eucjp db = SQLite3::Database.new(':memory:'.encode('EUC-JP')) assert_equal(Encoding.find('UTF-8'), db.encoding) end def test_db_with_utf16 utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE" db = SQLite3::Database.new(':memory:'.encode(utf16)) assert_equal(Encoding.find(utf16), db.encoding) end def test_statement_eucjp str = "猫舌" @db.execute("insert into ex(data) values ('#{str}')".encode('EUC-JP')) row = @db.execute("select data from ex") assert_equal @db.encoding, row.first.first.encoding assert_equal str, row.first.first end def test_statement_utf8 str = "猫舌" @db.execute("insert into ex(data) values ('#{str}')") row = @db.execute("select data from ex") assert_equal @db.encoding, row.first.first.encoding assert_equal str, row.first.first end def test_encoding assert_equal Encoding.find("UTF-8"), @db.encoding end def test_utf_8 str = "猫舌" @db.execute(@insert, [10, str]) row = @db.execute("select data from ex") assert_equal @db.encoding, row.first.first.encoding assert_equal str, row.first.first end def test_euc_jp str = "猫舌".encode('EUC-JP') @db.execute(@insert, [10, str]) row = @db.execute("select data from ex") assert_equal @db.encoding, row.first.first.encoding assert_equal str.encode('UTF-8'), row.first.first end end if RUBY_VERSION >= '1.9.1' end sqlite3-1.4.2/test/test_database.rb 0000644 0000041 0000041 00000034272 13611407300 017244 0 ustar www-data www-data require 'helper' require 'tempfile' require 'pathname' module SQLite3 class TestDatabase < SQLite3::TestCase attr_reader :db def setup @db = SQLite3::Database.new(':memory:') super end def test_segv assert_raises { SQLite3::Database.new 1 } end def test_db_filename tf = nil assert_equal '', @db.filename('main') tf = Tempfile.new 'thing' @db = SQLite3::Database.new tf.path assert_equal File.expand_path(tf.path), File.expand_path(@db.filename('main')) ensure tf.unlink if tf end def test_filename tf = nil assert_equal '', @db.filename tf = Tempfile.new 'thing' @db = SQLite3::Database.new tf.path assert_equal File.expand_path(tf.path), File.expand_path(@db.filename) ensure tf.unlink if tf end def test_filename_with_attachment tf = nil assert_equal '', @db.filename tf = Tempfile.new 'thing' @db.execute "ATTACH DATABASE '#{tf.path}' AS 'testing'" assert_equal File.expand_path(tf.path), File.expand_path(@db.filename('testing')) ensure tf.unlink if tf end def test_error_code begin db.execute 'SELECT' rescue SQLite3::SQLException => e end assert_equal 1, e.code end def test_extended_error_code db.extended_result_codes = true db.execute 'CREATE TABLE "employees" ("token" integer NOT NULL)' begin db.execute 'INSERT INTO employees (token) VALUES (NULL)' rescue SQLite3::ConstraintException => e end assert_equal 1299, e.code end def test_bignum num = 4907021672125087844 db.execute 'CREATE TABLE "employees" ("token" integer(8), "name" varchar(20) NOT NULL)' db.execute "INSERT INTO employees(name, token) VALUES('employee-1', ?)", [num] rows = db.execute 'select token from employees' assert_equal num, rows.first.first end def test_blob @db.execute("CREATE TABLE blobs ( id INTEGER, hash BLOB(10) )") blob = Blob.new("foo\0bar") @db.execute("INSERT INTO blobs VALUES (0, ?)", [blob]) assert_equal [[0, blob, blob.length, blob.length*2]], @db.execute("SELECT id, hash, length(hash), length(hex(hash)) FROM blobs") end def test_get_first_row assert_equal [1], @db.get_first_row('SELECT 1') end def test_get_first_row_with_type_translation_and_hash_results @db.results_as_hash = true @db.type_translation = true assert_equal({"1"=>1}, @db.get_first_row('SELECT 1')) end def test_execute_with_type_translation_and_hash @db.results_as_hash = true @db.type_translation = true rows = [] @db.execute('SELECT 1') { |row| rows << row } assert_equal({"1"=>1}, rows.first) end def test_encoding assert @db.encoding, 'database has encoding' end def test_changes @db.execute("CREATE TABLE items (id integer PRIMARY KEY AUTOINCREMENT, number integer)") assert_equal 0, @db.changes @db.execute("INSERT INTO items (number) VALUES (10)") assert_equal 1, @db.changes @db.execute_batch( "UPDATE items SET number = (number + :nn) WHERE (number = :n)", {"nn" => 20, "n" => 10}) assert_equal 1, @db.changes assert_equal [[30]], @db.execute("select number from items") end def test_batch_last_comment_is_processed # FIXME: nil as a successful return value is kinda dumb assert_nil @db.execute_batch <<-eosql CREATE TABLE items (id integer PRIMARY KEY AUTOINCREMENT); -- omg eosql end def test_execute_batch2 @db.results_as_hash = true return_value = @db.execute_batch2 <<-eosql CREATE TABLE items (id integer PRIMARY KEY AUTOINCREMENT, name string); INSERT INTO items (name) VALUES ("foo"); INSERT INTO items (name) VALUES ("bar"); SELECT * FROM items; eosql assert_equal return_value, [{"id"=>"1","name"=>"foo"}, {"id"=>"2", "name"=>"bar"}] return_value = @db.execute_batch2('SELECT * FROM items;') do |result| result["id"] = result["id"].to_i result end assert_equal return_value, [{"id"=>1,"name"=>"foo"}, {"id"=>2, "name"=>"bar"}] return_value = @db.execute_batch2('INSERT INTO items (name) VALUES ("oof")') assert_equal return_value, [] return_value = @db.execute_batch2( 'CREATE TABLE employees (id integer PRIMARY KEY AUTOINCREMENT, name string, age integer(3)); INSERT INTO employees (age) VALUES (30); INSERT INTO employees (age) VALUES (40); INSERT INTO employees (age) VALUES (20); SELECT age FROM employees;') do |result| result["age"] = result["age"].to_i result end assert_equal return_value, [{"age"=>30}, {"age"=>40}, {"age"=>20}] return_value = @db.execute_batch2('SELECT name FROM employees'); assert_equal return_value, [{"name"=>nil}, {"name"=>nil}, {"name"=>nil}] @db.results_as_hash = false return_value = @db.execute_batch2( 'CREATE TABLE managers (id integer PRIMARY KEY AUTOINCREMENT, age integer(3)); INSERT INTO managers (age) VALUES (50); INSERT INTO managers (age) VALUES (60); SELECT id, age from managers;') do |result| result = result.map do |res| res.to_i end result end assert_equal return_value, [[1, 50], [2, 60]] assert_raises (RuntimeError) do # "names" is not a valid column @db.execute_batch2 'INSERT INTO items (names) VALUES ("bazz")' end end def test_new db = SQLite3::Database.new(':memory:') assert db end def test_new_yields_self thing = nil SQLite3::Database.new(':memory:') do |db| thing = db end assert_instance_of(SQLite3::Database, thing) end def test_new_with_options # determine if Ruby is running on Big Endian platform utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE" if RUBY_VERSION >= "1.9" db = SQLite3::Database.new(':memory:'.encode(utf16), :utf16 => true) else db = SQLite3::Database.new(Iconv.conv(utf16, 'UTF-8', ':memory:'), :utf16 => true) end assert db end def test_close db = SQLite3::Database.new(':memory:') db.close assert db.closed? end def test_block_closes_self thing = nil SQLite3::Database.new(':memory:') do |db| thing = db assert !thing.closed? end assert thing.closed? end def test_block_closes_self_even_raised thing = nil begin SQLite3::Database.new(':memory:') do |db| thing = db raise end rescue end assert thing.closed? end def test_prepare db = SQLite3::Database.new(':memory:') stmt = db.prepare('select "hello world"') assert_instance_of(SQLite3::Statement, stmt) end def test_block_prepare_does_not_double_close db = SQLite3::Database.new(':memory:') r = db.prepare('select "hello world"') do |stmt| stmt.close :foo end assert_equal :foo, r end def test_total_changes db = SQLite3::Database.new(':memory:') db.execute("create table foo ( a integer primary key, b text )") db.execute("insert into foo (b) values ('hello')") assert_equal 1, db.total_changes end def test_execute_returns_list_of_hash db = SQLite3::Database.new(':memory:', :results_as_hash => true) db.execute("create table foo ( a integer primary key, b text )") db.execute("insert into foo (b) values ('hello')") rows = db.execute("select * from foo") assert_equal [{"a"=>1, "b"=>"hello"}], rows end def test_execute_yields_hash db = SQLite3::Database.new(':memory:', :results_as_hash => true) db.execute("create table foo ( a integer primary key, b text )") db.execute("insert into foo (b) values ('hello')") db.execute("select * from foo") do |row| assert_equal({"a"=>1, "b"=>"hello"}, row) end end def test_table_info db = SQLite3::Database.new(':memory:', :results_as_hash => true) db.execute("create table foo ( a integer primary key, b text )") info = [{ "name" => "a", "pk" => 1, "notnull" => 0, "type" => "integer", "dflt_value" => nil, "cid" => 0 }, { "name" => "b", "pk" => 0, "notnull" => 0, "type" => "text", "dflt_value" => nil, "cid" => 1 }] assert_equal info, db.table_info('foo') end def test_total_changes_closed db = SQLite3::Database.new(':memory:') db.close assert_raise(SQLite3::Exception) do db.total_changes end end def test_trace_requires_opendb @db.close assert_raise(SQLite3::Exception) do @db.trace { |x| } end end def test_trace_with_block result = nil @db.trace { |sql| result = sql } @db.execute "select 'foo'" assert_equal "select 'foo'", result end def test_trace_with_object obj = Class.new { attr_accessor :result def call sql; @result = sql end }.new @db.trace(obj) @db.execute "select 'foo'" assert_equal "select 'foo'", obj.result end def test_trace_takes_nil @db.trace(nil) @db.execute "select 'foo'" end def test_last_insert_row_id_closed @db.close assert_raise(SQLite3::Exception) do @db.last_insert_row_id end end def test_define_function called_with = nil @db.define_function("hello") do |value| called_with = value end @db.execute("select hello(10)") assert_equal 10, called_with end def test_call_func_arg_type called_with = nil @db.define_function("hello") do |b, c, d| called_with = [b, c, d] nil end @db.execute("select hello(2.2, 'foo', NULL)") assert_equal [2.2, 'foo', nil], called_with end def test_define_varargs called_with = nil @db.define_function("hello") do |*args| called_with = args nil end @db.execute("select hello(2.2, 'foo', NULL)") assert_equal [2.2, 'foo', nil], called_with end def test_call_func_blob called_with = nil @db.define_function("hello") do |a, b| called_with = [a, b, a.length] nil end blob = Blob.new("a\0fine\0kettle\0of\0fish") @db.execute("select hello(?, length(?))", [blob, blob]) assert_equal [blob, blob.length, 21], called_with end def test_function_return @db.define_function("hello") { |a| 10 } assert_equal [10], @db.execute("select hello('world')").first end def test_function_return_types [10, 2.2, nil, "foo", Blob.new("foo\0bar")].each do |thing| @db.define_function("hello") { |a| thing } assert_equal [thing], @db.execute("select hello('world')").first end end def test_function_gc_segfault @db.create_function("bug", -1) { |func, *values| func.result = values.join } # With a lot of data and a lot of threads, try to induce a GC segfault. params = Array.new(127, "?" * 28000) proc = Proc.new { db.execute("select bug(#{Array.new(params.length, "?").join(",")})", params) } m = Mutex.new 30.times.map { Thread.new { m.synchronize { proc.call } } }.each(&:join) end def test_function_return_type_round_trip [10, 2.2, nil, "foo", Blob.new("foo\0bar")].each do |thing| @db.define_function("hello") { |a| a } assert_equal [thing], @db.execute("select hello(hello(?))", [thing]).first end end def test_define_function_closed @db.close assert_raise(SQLite3::Exception) do @db.define_function('foo') { } end end def test_inerrupt_closed @db.close assert_raise(SQLite3::Exception) do @db.interrupt end end def test_define_aggregate @db.execute "create table foo ( a integer primary key, b text )" @db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'bar' )" @db.execute "insert into foo ( b ) values ( 'baz' )" acc = Class.new { attr_reader :sum alias :finalize :sum def initialize @sum = 0 end def step a @sum += a end }.new @db.define_aggregator("accumulate", acc) value = @db.get_first_value( "select accumulate(a) from foo" ) assert_equal 6, value end def test_authorizer_ok @db.authorizer = Class.new { def call action, a, b, c, d; true end }.new @db.prepare("select 'fooooo'") @db.authorizer = Class.new { def call action, a, b, c, d; 0 end }.new @db.prepare("select 'fooooo'") end def test_authorizer_ignore @db.authorizer = Class.new { def call action, a, b, c, d; nil end }.new stmt = @db.prepare("select 'fooooo'") assert_nil stmt.step end def test_authorizer_fail @db.authorizer = Class.new { def call action, a, b, c, d; false end }.new assert_raises(SQLite3::AuthorizationException) do @db.prepare("select 'fooooo'") end end def test_remove_auth @db.authorizer = Class.new { def call action, a, b, c, d; false end }.new assert_raises(SQLite3::AuthorizationException) do @db.prepare("select 'fooooo'") end @db.authorizer = nil @db.prepare("select 'fooooo'") end def test_close_with_open_statements @db.prepare("select 'foo'") assert_raises(SQLite3::BusyException) do @db.close end end def test_execute_with_empty_bind_params assert_equal [['foo']], @db.execute("select 'foo'", []) end def test_query_with_named_bind_params assert_equal [['foo']], @db.query("select :n", {'n' => 'foo'}).to_a end def test_execute_with_named_bind_params assert_equal [['foo']], @db.execute("select :n", {'n' => 'foo'}) end end end sqlite3-1.4.2/test/test_integration_resultset.rb 0000644 0000041 0000041 00000007257 13611407300 022140 0 ustar www-data www-data require 'helper' class TC_ResultSet < SQLite3::TestCase def setup @db = SQLite3::Database.new(":memory:") @db.transaction do @db.execute "create table foo ( a integer primary key, b text )" @db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'bar' )" @db.execute "insert into foo ( b ) values ( 'baz' )" end @stmt = @db.prepare( "select * from foo where a in ( ?, ? )" ) @result = @stmt.execute end def teardown @stmt.close @db.close end def test_reset_unused assert_nothing_raised { @result.reset } assert @result.to_a.empty? end def test_reset_used @result.to_a assert_nothing_raised { @result.reset } assert @result.to_a.empty? end def test_reset_with_bind @result.to_a assert_nothing_raised { @result.reset( 1, 2 ) } assert_equal 2, @result.to_a.length end def test_eof_inner @result.reset( 1 ) assert !@result.eof? end def test_eof_edge @result.reset( 1 ) @result.next # to first row @result.next # to end of result set assert @result.eof? end def test_next_eof @result.reset( 1 ) assert_not_nil @result.next assert_nil @result.next end def test_next_no_type_translation_no_hash @result.reset( 1 ) assert_equal [ 1, "foo" ], @result.next end def test_next_type_translation @result.reset( 1 ) assert_equal [ 1, "foo" ], @result.next end def test_next_type_translation_with_untyped_column @db.query( "select count(*) from foo" ) do |result| assert_equal [3], result.next end end def test_type_translation_with_null_column time = '1974-07-25 14:39:00' @db.execute "create table bar ( a integer, b time, c string )" @db.execute "insert into bar (a, b, c) values (NULL, '#{time}', 'hello')" @db.execute "insert into bar (a, b, c) values (1, NULL, 'hello')" @db.execute "insert into bar (a, b, c) values (2, '#{time}', NULL)" @db.query( "select * from bar" ) do |result| assert_equal [nil, time, 'hello'], result.next assert_equal [1, nil, 'hello'], result.next assert_equal [2, time, nil], result.next end end def test_real_translation @db.execute('create table foo_real(a real)') @db.execute('insert into foo_real values (42)' ) @db.query('select a, sum(a), typeof(a), typeof(sum(a)) from foo_real') do |result| result = result.next assert result[0].is_a?(Float) assert result[1].is_a?(Float) assert result[2].is_a?(String) assert result[3].is_a?(String) end end def test_next_results_as_hash @db.results_as_hash = true @result.reset( 1 ) hash = @result.next assert_equal( { "a" => 1, "b" => "foo" }, hash ) assert_equal hash[0], 1 assert_equal hash[1], "foo" end def test_each called = 0 @result.reset( 1, 2 ) @result.each { |row| called += 1 } assert_equal 2, called end def test_enumerable @result.reset( 1, 2 ) assert_equal 2, @result.to_a.length end def test_types assert_equal [ "integer", "text" ], @result.types end def test_columns assert_equal [ "a", "b" ], @result.columns end def test_close stmt = @db.prepare( "select * from foo" ) result = stmt.execute assert !result.closed? result.close assert result.closed? assert stmt.closed? assert_raise( SQLite3::Exception ) { result.reset } assert_raise( SQLite3::Exception ) { result.next } assert_raise( SQLite3::Exception ) { result.each } assert_raise( SQLite3::Exception ) { result.close } assert_raise( SQLite3::Exception ) { result.types } assert_raise( SQLite3::Exception ) { result.columns } end end sqlite3-1.4.2/test/test_integration_aggregate.rb 0000644 0000041 0000041 00000020473 13611407300 022027 0 ustar www-data www-data require 'helper' class TC_Integration_Aggregate < SQLite3::TestCase def setup @db = SQLite3::Database.new(":memory:") @db.transaction do @db.execute "create table foo ( a integer primary key, b text, c integer )" @db.execute "insert into foo ( b, c ) values ( 'foo', 10 )" @db.execute "insert into foo ( b, c ) values ( 'bar', 11 )" @db.execute "insert into foo ( b, c ) values ( 'bar', 12 )" end end def teardown @db.close end def test_create_aggregate_without_block step = proc do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end final = proc { |ctx| ctx.result = ctx[:sum] } @db.create_aggregate( "accumulate", 1, step, final ) value = @db.get_first_value( "select accumulate(a) from foo" ) assert_equal 6, value # calling #get_first_value twice don't add up to the latest result value = @db.get_first_value( "select accumulate(a) from foo" ) assert_equal 6, value end def test_create_aggregate_with_block @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end value = @db.get_first_value( "select accumulate(a) from foo" ) assert_equal 6, value end def test_create_aggregate_with_group_by @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end values = @db.execute( "select b, accumulate(c) from foo group by b order by b" ) assert_equal "bar", values[0][0] assert_equal 23, values[0][1] assert_equal "foo", values[1][0] assert_equal 10, values[1][1] end def test_create_aggregate_with_the_same_function_twice_in_a_query @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end values = @db.get_first_row( "select accumulate(a), accumulate(c) from foo" ) assert_equal 6, values[0] assert_equal 33, values[1] end def test_create_aggregate_with_two_different_functions @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end @db.create_aggregate( "multiply", 1 ) do step do |ctx,a| ctx[:sum] ||= 1 ctx[:sum] *= a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end GC.start values = @db.get_first_row( "select accumulate(a), multiply(c) from foo" ) assert_equal 6, values[0] assert_equal 1320, values[1] value = @db.get_first_value( "select accumulate(c) from foo") assert_equal 33, value value = @db.get_first_value( "select multiply(a) from foo") assert_equal 6, value end def test_create_aggregate_overwrite_function @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end value = @db.get_first_value( "select accumulate(c) from foo") assert_equal 33, value GC.start @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 1 ctx[:sum] *= a.to_i end finalize { |ctx| ctx.result = ctx[:sum] } end value = @db.get_first_value( "select accumulate(c) from foo") assert_equal 1320, value end def test_create_aggregate_overwrite_function_with_different_arity @db.create_aggregate( "accumulate", -1 ) do step do |ctx,*args| ctx[:sum] ||= 0 args.each { |a| ctx[:sum] += a.to_i } end finalize { |ctx| ctx.result = ctx[:sum] } end @db.create_aggregate( "accumulate", 2 ) do step do |ctx,a,b| ctx[:sum] ||= 1 ctx[:sum] *= (a.to_i + b.to_i) end finalize { |ctx| ctx.result = ctx[:sum] } end GC.start values = @db.get_first_row( "select accumulate(c), accumulate(a,c) from foo") assert_equal 33, values[0] assert_equal 2145, values[1] end def test_create_aggregate_with_invalid_arity assert_raise ArgumentError do @db.create_aggregate( "accumulate", 1000 ) do step {|ctx,*args| } finalize { |ctx| } end end end class CustomException < Exception end def test_create_aggregate_with_exception_in_step @db.create_aggregate( "raiseexception", 1 ) do step do |ctx,a| raise CustomException.new( "bogus aggregate handler" ) end finalize { |ctx| ctx.result = 42 } end assert_raise CustomException do @db.get_first_value( "select raiseexception(a) from foo") end end def test_create_aggregate_with_exception_in_finalize @db.create_aggregate( "raiseexception", 1 ) do step do |ctx,a| raise CustomException.new( "bogus aggregate handler" ) end finalize do |ctx| raise CustomException.new( "bogus aggregate handler" ) end end assert_raise CustomException do @db.get_first_value( "select raiseexception(a) from foo") end end def test_create_aggregate_with_no_data @db.create_aggregate( "accumulate", 1 ) do step do |ctx,a| ctx[:sum] ||= 0 ctx[:sum] += a.to_i end finalize { |ctx| ctx.result = ctx[:sum] || 0 } end value = @db.get_first_value( "select accumulate(a) from foo where a = 100" ) assert_equal 0, value end class AggregateHandler class << self def arity; 1; end def text_rep; SQLite3::Constants::TextRep::ANY; end def name; "multiply"; end end def step(ctx, a) ctx[:buffer] ||= 1 ctx[:buffer] *= a.to_i end def finalize(ctx); ctx.result = ctx[:buffer]; end end def test_aggregate_initialized_twice initialized = 0 handler = Class.new(AggregateHandler) do define_method(:initialize) do initialized += 1 super() end end @db.create_aggregate_handler handler @db.get_first_value( "select multiply(a) from foo" ) @db.get_first_value( "select multiply(a) from foo" ) assert_equal 2, initialized end def test_create_aggregate_handler_call_with_wrong_arity @db.create_aggregate_handler AggregateHandler assert_raise(SQLite3::SQLException) do @db.get_first_value( "select multiply(a,c) from foo" ) end end class RaiseExceptionStepAggregateHandler class << self def arity; 1; end def text_rep; SQLite3::Constants::TextRep::ANY; end def name; "raiseexception"; end end def step(ctx, a) raise CustomException.new( "bogus aggregate handler" ) end def finalize(ctx); ctx.result = nil; end end def test_create_aggregate_handler_with_exception_step @db.create_aggregate_handler RaiseExceptionStepAggregateHandler assert_raise CustomException do @db.get_first_value( "select raiseexception(a) from foo") end end class RaiseExceptionNewAggregateHandler class << self def name; "raiseexception"; end end def initialize raise CustomException.new( "bogus aggregate handler" ) end def step(ctx, a); end def finalize(ctx); ctx.result = nil; end end def test_create_aggregate_handler_with_exception_new @db.create_aggregate_handler RaiseExceptionNewAggregateHandler assert_raise CustomException do @db.get_first_value( "select raiseexception(a) from foo") end end def test_create_aggregate_handler @db.create_aggregate_handler AggregateHandler value = @db.get_first_value( "select multiply(a) from foo" ) assert_equal 6, value end class AccumulateAggregator def step(*args) @sum ||= 0 args.each { |a| @sum += a.to_i } end def finalize @sum end end class AccumulateAggregator2 def step(a, b) @sum ||= 1 @sum *= (a.to_i + b.to_i) end def finalize @sum end end def test_define_aggregator_with_two_different_arities @db.define_aggregator( "accumulate", AccumulateAggregator.new ) @db.define_aggregator( "accumulate", AccumulateAggregator2.new ) GC.start values = @db.get_first_row( "select accumulate(c), accumulate(a,c) from foo") assert_equal 33, values[0] assert_equal 2145, values[1] end end sqlite3-1.4.2/test/test_backup.rb 0000644 0000041 0000041 00000002036 13611407300 016736 0 ustar www-data www-data require 'helper' module SQLite3 class TestBackup < SQLite3::TestCase def setup @sdb = SQLite3::Database.new(':memory:') @ddb = SQLite3::Database.new(':memory:') @sdb.execute('CREATE TABLE foo (idx, val);'); @data = ('A'..'Z').map{|x|x * 40} @data.each_with_index do |v, i| @sdb.execute('INSERT INTO foo (idx, val) VALUES (?, ?);', [i, v]) end end def test_backup_step b = SQLite3::Backup.new(@ddb, 'main', @sdb, 'main') while b.step(1) == SQLite3::Constants::ErrorCode::OK assert_not_equal(0, b.remaining) end assert_equal(0, b.remaining) b.finish assert_equal(@data.length, @ddb.execute('SELECT * FROM foo;').length) end def test_backup_all b = SQLite3::Backup.new(@ddb, 'main', @sdb, 'main') assert_equal(SQLite3::Constants::ErrorCode::DONE, b.step(-1)) assert_equal(0, b.remaining) b.finish assert_equal(@data.length, @ddb.execute('SELECT * FROM foo;').length) end end if defined?(SQLite3::Backup) end sqlite3-1.4.2/test/test_integration_open_close.rb 0000644 0000041 0000041 00000001313 13611407300 022217 0 ustar www-data www-data require 'helper' class TC_OpenClose < SQLite3::TestCase def test_create_close begin db = SQLite3::Database.new( "test-create.db" ) assert File.exist?( "test-create.db" ) assert_nothing_raised { db.close } ensure File.delete( "test-create.db" ) rescue nil end end def test_open_close begin File.open( "test-open.db", "w" ) { |f| } assert File.exist?( "test-open.db" ) db = SQLite3::Database.new( "test-open.db" ) assert_nothing_raised { db.close } ensure File.delete( "test-open.db" ) rescue nil end end def test_bad_open assert_raise( SQLite3::CantOpenException ) do SQLite3::Database.new( "." ) end end end sqlite3-1.4.2/test/test_sqlite3.rb 0000644 0000041 0000041 00000000601 13611407300 017051 0 ustar www-data www-data require 'helper' module SQLite3 class TestSQLite3 < SQLite3::TestCase def test_libversion assert_not_nil SQLite3.libversion end def test_threadsafe assert_not_nil SQLite3.threadsafe end def test_threadsafe? if SQLite3.threadsafe > 0 assert SQLite3.threadsafe? else refute SQLite3.threadsafe? end end end end sqlite3-1.4.2/test/test_statement_execute.rb 0000644 0000041 0000041 00000002041 13611407300 021213 0 ustar www-data www-data require 'helper' module SQLite3 class TestStatementExecute < SQLite3::TestCase def setup @db = SQLite3::Database.new(':memory:') @db.execute_batch( "CREATE TABLE items (id integer PRIMARY KEY, number integer)") end def test_execute_insert ps = @db.prepare("INSERT INTO items (number) VALUES (:n)") ps.execute('n'=>10) assert_equal 1, @db.get_first_value("SELECT count(*) FROM items") ps.close end def test_execute_update @db.execute("INSERT INTO items (number) VALUES (?)", [10]) ps = @db.prepare("UPDATE items SET number = :new WHERE number = :old") ps.execute('old'=>10, 'new'=>20) assert_equal 20, @db.get_first_value("SELECT number FROM items") ps.close end def test_execute_delete @db.execute("INSERT INTO items (number) VALUES (?)", [20]) ps = @db.prepare("DELETE FROM items WHERE number = :n") ps.execute('n' => 20) assert_equal 0, @db.get_first_value("SELECT count(*) FROM items") ps.close end end end sqlite3-1.4.2/test/test_statement.rb 0000644 0000041 0000041 00000015771 13611407300 017507 0 ustar www-data www-data require 'helper' module SQLite3 class TestStatement < SQLite3::TestCase def setup @db = SQLite3::Database.new(':memory:') @stmt = SQLite3::Statement.new(@db, "select 'foo'") end def test_double_close_does_not_segv @db.execute 'CREATE TABLE "things" ("number" float NOT NULL)' stmt = @db.prepare 'INSERT INTO things (number) VALUES (?)' assert_raises(SQLite3::ConstraintException) { stmt.execute(nil) } stmt.close assert_raises(SQLite3::Exception) { stmt.close } end def test_raises_type_error assert_raises(TypeError) do SQLite3::Statement.new( @db, nil ) end end def test_insert_duplicate_records @db.execute 'CREATE TABLE "things" ("name" varchar(20) CONSTRAINT "index_things_on_name" UNIQUE)' stmt = @db.prepare("INSERT INTO things(name) VALUES(?)") stmt.execute('ruby') exception = assert_raises(SQLite3::ConstraintException) { stmt.execute('ruby') } # SQLite 3.8.2 returns new error message: # UNIQUE constraint failed: *table_name*.*column_name* # Older versions of SQLite return: # column *column_name* is not unique assert_match(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/, exception.message) end ### # This method may not exist depending on how sqlite3 was compiled def test_database_name @db.execute('create table foo(text BLOB)') @db.execute('insert into foo(text) values (?)',SQLite3::Blob.new('hello')) stmt = @db.prepare('select text from foo') if stmt.respond_to?(:database_name) assert_equal 'main', stmt.database_name(0) end end def test_prepare_blob @db.execute('create table foo(text BLOB)') stmt = @db.prepare('insert into foo(text) values (?)') stmt.bind_param(1, SQLite3::Blob.new('hello')) stmt.step stmt.close end def test_select_blob @db.execute('create table foo(text BLOB)') @db.execute('insert into foo(text) values (?)',SQLite3::Blob.new('hello')) assert_equal 'hello', @db.execute('select * from foo').first.first end def test_new assert @stmt end def test_new_closed_handle @db = SQLite3::Database.new(':memory:') @db.close assert_raises(ArgumentError) do SQLite3::Statement.new(@db, 'select "foo"') end end def test_new_with_remainder stmt = SQLite3::Statement.new(@db, "select 'foo';bar") assert_equal 'bar', stmt.remainder end def test_empty_remainder assert_equal '', @stmt.remainder end def test_close @stmt.close assert @stmt.closed? end def test_double_close @stmt.close assert_raises(SQLite3::Exception) do @stmt.close end end def test_bind_param_string stmt = SQLite3::Statement.new(@db, "select ?") stmt.bind_param(1, "hello") result = nil stmt.each { |x| result = x } assert_equal ['hello'], result end def test_bind_param_int stmt = SQLite3::Statement.new(@db, "select ?") stmt.bind_param(1, 10) result = nil stmt.each { |x| result = x } assert_equal [10], result end def test_bind_nil stmt = SQLite3::Statement.new(@db, "select ?") stmt.bind_param(1, nil) result = nil stmt.each { |x| result = x } assert_equal [nil], result end def test_bind_blob @db.execute('create table foo(text BLOB)') stmt = SQLite3::Statement.new(@db, 'insert into foo(text) values (?)') stmt.bind_param(1, SQLite3::Blob.new('hello')) stmt.execute row = @db.execute('select * from foo') assert_equal ['hello'], row.first assert_equal row.first.types, ['BLOB'] end def test_bind_64 stmt = SQLite3::Statement.new(@db, "select ?") stmt.bind_param(1, 2 ** 31) result = nil stmt.each { |x| result = x } assert_equal [2 ** 31], result end def test_bind_double stmt = SQLite3::Statement.new(@db, "select ?") stmt.bind_param(1, 2.2) result = nil stmt.each { |x| result = x } assert_equal [2.2], result end def test_named_bind stmt = SQLite3::Statement.new(@db, "select :foo") stmt.bind_param(':foo', 'hello') result = nil stmt.each { |x| result = x } assert_equal ['hello'], result end def test_named_bind_no_colon stmt = SQLite3::Statement.new(@db, "select :foo") stmt.bind_param('foo', 'hello') result = nil stmt.each { |x| result = x } assert_equal ['hello'], result end def test_named_bind_symbol stmt = SQLite3::Statement.new(@db, "select :foo") stmt.bind_param(:foo, 'hello') result = nil stmt.each { |x| result = x } assert_equal ['hello'], result end def test_named_bind_not_found stmt = SQLite3::Statement.new(@db, "select :foo") assert_raises(SQLite3::Exception) do stmt.bind_param('bar', 'hello') end end def test_each r = nil @stmt.each do |row| r = row end assert_equal(['foo'], r) end def test_reset! r = [] @stmt.each { |row| r << row } @stmt.reset! @stmt.each { |row| r << row } assert_equal [['foo'], ['foo']], r end def test_step r = @stmt.step assert_equal ['foo'], r end def test_step_twice assert_not_nil @stmt.step assert !@stmt.done? assert_nil @stmt.step assert @stmt.done? @stmt.reset! assert !@stmt.done? end def test_step_never_moves_past_done 10.times { @stmt.step } @stmt.done? end def test_column_count assert_equal 1, @stmt.column_count end def test_column_name assert_equal "'foo'", @stmt.column_name(0) assert_nil @stmt.column_name(10) end def test_bind_parameter_count stmt = SQLite3::Statement.new(@db, "select ?, ?, ?") assert_equal 3, stmt.bind_parameter_count end def test_execute_with_varargs stmt = @db.prepare('select ?, ?') assert_equal [[nil, nil]], stmt.execute(nil, nil).to_a end def test_execute_with_hash stmt = @db.prepare('select :n, :h') assert_equal [[10, nil]], stmt.execute('n' => 10, 'h' => nil).to_a end def test_with_error @db.execute('CREATE TABLE "employees" ("name" varchar(20) NOT NULL CONSTRAINT "index_employees_on_name" UNIQUE)') stmt = @db.prepare("INSERT INTO Employees(name) VALUES(?)") stmt.execute('employee-1') stmt.execute('employee-1') rescue SQLite3::ConstraintException stmt.reset! assert stmt.execute('employee-2') end def test_clear_bindings! stmt = @db.prepare('select ?, ?') stmt.bind_param 1, "foo" stmt.bind_param 2, "bar" # We can't fetch bound parameters back out of sqlite3, so just call # the clear_bindings! method and assert that nil is returned stmt.clear_bindings! while x = stmt.step assert_equal [nil, nil], x end end end end sqlite3-1.4.2/test/test_integration.rb 0000644 0000041 0000041 00000033551 13611407300 020022 0 ustar www-data www-data require 'helper' class TC_Database_Integration < SQLite3::TestCase def setup @db = SQLite3::Database.new(":memory:") @db.transaction do @db.execute "create table foo ( a integer primary key, b text )" @db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'bar' )" @db.execute "insert into foo ( b ) values ( 'baz' )" end end def teardown @db.close end def test_table_info_with_type_translation_active assert_nothing_raised { @db.table_info("foo") } end def test_table_info_with_defaults_for_version_3_3_8_and_higher @db.transaction do @db.execute "create table defaults_test ( a string default NULL, b string default 'Hello', c string default '--- []\n' )" data = @db.table_info( "defaults_test" ) assert_equal({"name" => "a", "type" => "string", "dflt_value" => nil, "notnull" => 0, "cid" => 0, "pk" => 0}, data[0]) assert_equal({"name" => "b", "type" => "string", "dflt_value" => "Hello", "notnull" => 0, "cid" => 1, "pk" => 0}, data[1]) assert_equal({"name" => "c", "type" => "string", "dflt_value" => "--- []\n", "notnull" => 0, "cid" => 2, "pk" => 0}, data[2]) end end def test_table_info_without_defaults_for_version_3_3_8_and_higher @db.transaction do @db.execute "create table no_defaults_test ( a integer default 1, b integer )" data = @db.table_info( "no_defaults_test" ) assert_equal({"name" => "a", "type" => "integer", "dflt_value" => "1", "notnull" => 0, "cid" => 0, "pk" => 0}, data[0]) assert_equal({"name" => "b", "type" => "integer", "dflt_value" => nil, "notnull" => 0, "cid" => 1, "pk" => 0}, data[1]) end end def test_complete_fail assert !@db.complete?( "select * from foo" ) end def test_complete_success assert @db.complete?( "select * from foo;" ) end # FIXME: do people really need UTF16 sql statements? #def test_complete_fail_utf16 # assert !@db.complete?( "select * from foo".to_utf16(false), true ) #end # FIXME: do people really need UTF16 sql statements? #def test_complete_success_utf16 # assert @db.complete?( "select * from foo;".to_utf16(true), true ) #end def test_errmsg assert_equal "not an error", @db.errmsg end # FIXME: do people really need UTF16 error messages? #def test_errmsg_utf16 # msg = Iconv.conv('UTF-16', 'UTF-8', 'not an error') # assert_equal msg, @db.errmsg(true) #end def test_errcode assert_equal 0, @db.errcode end def test_trace result = nil @db.trace { |sql| result = sql } @db.execute "select * from foo" assert_equal "select * from foo", result end def test_authorizer_okay @db.authorizer { |type,a,b,c,d| 0 } rows = @db.execute "select * from foo" assert_equal 3, rows.length end def test_authorizer_error @db.authorizer { |type,a,b,c,d| 1 } assert_raise( SQLite3::AuthorizationException ) do @db.execute "select * from foo" end end def test_authorizer_silent @db.authorizer { |type,a,b,c,d| 2 } rows = @db.execute "select * from foo" assert rows.empty? end def test_prepare_invalid_syntax assert_raise( SQLite3::SQLException ) do @db.prepare "select from foo" end end def test_prepare_invalid_column assert_raise( SQLite3::SQLException ) do @db.prepare "select k from foo" end end def test_prepare_invalid_table assert_raise( SQLite3::SQLException ) do @db.prepare "select * from barf" end end def test_prepare_no_block stmt = @db.prepare "select * from foo" assert stmt.respond_to?(:execute) stmt.close end def test_prepare_with_block called = false @db.prepare "select * from foo" do |stmt| called = true assert stmt.respond_to?(:execute) end assert called end def test_execute_no_block_no_bind_no_match rows = @db.execute( "select * from foo where a > 100" ) assert rows.empty? end def test_execute_with_block_no_bind_no_match called = false @db.execute( "select * from foo where a > 100" ) do |row| called = true end assert !called end def test_execute_no_block_with_bind_no_match rows = @db.execute( "select * from foo where a > ?", 100 ) assert rows.empty? end def test_execute_with_block_with_bind_no_match called = false @db.execute( "select * from foo where a > ?", 100 ) do |row| called = true end assert !called end def test_execute_no_block_no_bind_with_match rows = @db.execute( "select * from foo where a = 1" ) assert_equal 1, rows.length end def test_execute_with_block_no_bind_with_match called = 0 @db.execute( "select * from foo where a = 1" ) do |row| called += 1 end assert_equal 1, called end def test_execute_no_block_with_bind_with_match rows = @db.execute( "select * from foo where a = ?", 1 ) assert_equal 1, rows.length end def test_execute_with_block_with_bind_with_match called = 0 @db.execute( "select * from foo where a = ?", 1 ) do |row| called += 1 end assert_equal 1, called end def test_execute2_no_block_no_bind_no_match columns, *rows = @db.execute2( "select * from foo where a > 100" ) assert rows.empty? assert_equal [ "a", "b" ], columns end def test_execute2_with_block_no_bind_no_match called = 0 @db.execute2( "select * from foo where a > 100" ) do |row| assert [ "a", "b" ], row unless called == 0 called += 1 end assert_equal 1, called end def test_execute2_no_block_with_bind_no_match columns, *rows = @db.execute2( "select * from foo where a > ?", 100 ) assert rows.empty? assert_equal [ "a", "b" ], columns end def test_execute2_with_block_with_bind_no_match called = 0 @db.execute2( "select * from foo where a > ?", 100 ) do |row| assert_equal [ "a", "b" ], row unless called == 0 called += 1 end assert_equal 1, called end def test_execute2_no_block_no_bind_with_match columns, *rows = @db.execute2( "select * from foo where a = 1" ) assert_equal 1, rows.length assert_equal [ "a", "b" ], columns end def test_execute2_with_block_no_bind_with_match called = 0 @db.execute2( "select * from foo where a = 1" ) do |row| assert_equal [ 1, "foo" ], row unless called == 0 called += 1 end assert_equal 2, called end def test_execute2_no_block_with_bind_with_match columns, *rows = @db.execute2( "select * from foo where a = ?", 1 ) assert_equal 1, rows.length assert_equal [ "a", "b" ], columns end def test_execute2_with_block_with_bind_with_match called = 0 @db.execute2( "select * from foo where a = ?", 1 ) do called += 1 end assert_equal 2, called end def test_execute_batch_empty assert_nothing_raised { @db.execute_batch "" } end def test_execute_batch_no_bind @db.transaction do @db.execute_batch <<-SQL create table bar ( a, b, c ); insert into bar values ( 'one', 2, 'three' ); insert into bar values ( 'four', 5, 'six' ); insert into bar values ( 'seven', 8, 'nine' ); SQL end rows = @db.execute( "select * from bar" ) assert_equal 3, rows.length end def test_execute_batch_with_bind @db.execute_batch( <<-SQL, [1] ) create table bar ( a, b, c ); insert into bar values ( 'one', 2, ? ); insert into bar values ( 'four', 5, ? ); insert into bar values ( 'seven', 8, ? ); SQL rows = @db.execute( "select * from bar" ).map { |a,b,c| c } assert_equal [1, 1, 1], rows end def test_query_no_block_no_bind_no_match result = @db.query( "select * from foo where a > 100" ) assert_nil result.next result.close end def test_query_with_block_no_bind_no_match r = nil @db.query( "select * from foo where a > 100" ) do |result| assert_nil result.next r = result end assert r.closed? end def test_query_no_block_with_bind_no_match result = @db.query( "select * from foo where a > ?", 100 ) assert_nil result.next result.close end def test_query_with_block_with_bind_no_match r = nil @db.query( "select * from foo where a > ?", 100 ) do |result| assert_nil result.next r = result end assert r.closed? end def test_query_no_block_no_bind_with_match result = @db.query( "select * from foo where a = 1" ) assert_not_nil result.next assert_nil result.next result.close end def test_query_with_block_no_bind_with_match r = nil @db.query( "select * from foo where a = 1" ) do |result| assert_not_nil result.next assert_nil result.next r = result end assert r.closed? end def test_query_no_block_with_bind_with_match result = @db.query( "select * from foo where a = ?", 1 ) assert_not_nil result.next assert_nil result.next result.close end def test_query_with_block_with_bind_with_match r = nil @db.query( "select * from foo where a = ?", 1 ) do |result| assert_not_nil result.next assert_nil result.next r = result end assert r.closed? end def test_get_first_row_no_bind_no_match result = @db.get_first_row( "select * from foo where a=100" ) assert_nil result end def test_get_first_row_no_bind_with_match result = @db.get_first_row( "select * from foo where a=1" ) assert_equal [ 1, "foo" ], result end def test_get_first_row_with_bind_no_match result = @db.get_first_row( "select * from foo where a=?", 100 ) assert_nil result end def test_get_first_row_with_bind_with_match result = @db.get_first_row( "select * from foo where a=?", 1 ) assert_equal [ 1, "foo" ], result end def test_get_first_value_no_bind_no_match result = @db.get_first_value( "select b, a from foo where a=100" ) assert_nil result @db.results_as_hash = true result = @db.get_first_value( "select b, a from foo where a=100" ) assert_nil result end def test_get_first_value_no_bind_with_match result = @db.get_first_value( "select b, a from foo where a=1" ) assert_equal "foo", result @db.results_as_hash = true result = @db.get_first_value( "select b, a from foo where a=1" ) assert_equal "foo", result end def test_get_first_value_with_bind_no_match result = @db.get_first_value( "select b, a from foo where a=?", 100 ) assert_nil result @db.results_as_hash = true result = @db.get_first_value( "select b, a from foo where a=?", 100 ) assert_nil result end def test_get_first_value_with_bind_with_match result = @db.get_first_value( "select b, a from foo where a=?", 1 ) assert_equal "foo", result @db.results_as_hash = true result = @db.get_first_value( "select b, a from foo where a=?", 1 ) assert_equal "foo", result end def test_last_insert_row_id @db.execute "insert into foo ( b ) values ( 'test' )" assert_equal 4, @db.last_insert_row_id @db.execute "insert into foo ( b ) values ( 'again' )" assert_equal 5, @db.last_insert_row_id end def test_changes @db.execute "insert into foo ( b ) values ( 'test' )" assert_equal 1, @db.changes @db.execute "delete from foo where 1=1" assert_equal 4, @db.changes end def test_total_changes assert_equal 3, @db.total_changes @db.execute "insert into foo ( b ) values ( 'test' )" @db.execute "delete from foo where 1=1" assert_equal 8, @db.total_changes end def test_transaction_nest assert_raise( SQLite3::SQLException ) do @db.transaction do @db.transaction do end end end end def test_transaction_rollback @db.transaction @db.execute_batch <<-SQL insert into foo (b) values ( 'test1' ); insert into foo (b) values ( 'test2' ); insert into foo (b) values ( 'test3' ); insert into foo (b) values ( 'test4' ); SQL assert_equal 7, @db.get_first_value("select count(*) from foo").to_i @db.rollback assert_equal 3, @db.get_first_value("select count(*) from foo").to_i end def test_transaction_commit @db.transaction @db.execute_batch <<-SQL insert into foo (b) values ( 'test1' ); insert into foo (b) values ( 'test2' ); insert into foo (b) values ( 'test3' ); insert into foo (b) values ( 'test4' ); SQL assert_equal 7, @db.get_first_value("select count(*) from foo").to_i @db.commit assert_equal 7, @db.get_first_value("select count(*) from foo").to_i end def test_transaction_rollback_in_block assert_raise( SQLite3::SQLException ) do @db.transaction do @db.rollback end end end def test_transaction_commit_in_block assert_raise( SQLite3::SQLException ) do @db.transaction do @db.commit end end end def test_transaction_active assert !@db.transaction_active? @db.transaction assert @db.transaction_active? @db.commit assert !@db.transaction_active? end def test_transaction_implicit_rollback assert !@db.transaction_active? @db.transaction @db.execute('create table bar (x CHECK(1 = 0))') assert @db.transaction_active? assert_raises( SQLite3::ConstraintException ) do @db.execute("insert or rollback into bar (x) VALUES ('x')") end assert !@db.transaction_active? end def test_interrupt @db.create_function( "abort", 1 ) do |func,x| @db.interrupt func.result = x end assert_raise( SQLite3::InterruptException ) do @db.execute "select abort(a) from foo" end end def test_create_function @db.create_function( "munge", 1 ) do |func,x| func.result = ">>>#{x}<<<" end value = @db.get_first_value( "select munge(b) from foo where a=1" ) assert_match( />>>.*<<, value ) end def test_bind_array_parameter result = @db.get_first_value( "select b from foo where a=? and b=?", [ 1, "foo" ] ) assert_equal "foo", result end end sqlite3-1.4.2/test/test_integration_statement.rb 0000644 0000041 0000041 00000011725 13611407300 022105 0 ustar www-data www-data require 'helper' class TC_Statement < SQLite3::TestCase def setup @db = SQLite3::Database.new(":memory:") @db.transaction do @db.execute "create table foo ( a integer primary key, b text )" @db.execute "insert into foo ( b ) values ( 'foo' )" @db.execute "insert into foo ( b ) values ( 'bar' )" @db.execute "insert into foo ( b ) values ( 'baz' )" end @stmt = @db.prepare( "select * from foo where a in ( ?, :named )" ) end def teardown @stmt.close @db.close end def test_remainder_empty assert_equal "", @stmt.remainder end def test_remainder_nonempty called = false @db.prepare( "select * from foo;\n blah" ) do |stmt| called = true assert_equal "\n blah", stmt.remainder end assert called end def test_bind_params_empty assert_nothing_raised { @stmt.bind_params } assert @stmt.execute!.empty? end def test_bind_params_array @stmt.bind_params 1, 2 assert_equal 2, @stmt.execute!.length end def test_bind_params_hash @stmt.bind_params ":named" => 2 assert_equal 1, @stmt.execute!.length end def test_bind_params_hash_without_colon @stmt.bind_params "named" => 2 assert_equal 1, @stmt.execute!.length end def test_bind_params_hash_as_symbol @stmt.bind_params :named => 2 assert_equal 1, @stmt.execute!.length end def test_bind_params_mixed @stmt.bind_params( 1, ":named" => 2 ) assert_equal 2, @stmt.execute!.length end def test_bind_param_by_index @stmt.bind_params( 1, 2 ) assert_equal 2, @stmt.execute!.length end def test_bind_param_by_name_bad assert_raise( SQLite3::Exception ) { @stmt.bind_param( "@named", 2 ) } end def test_bind_param_by_name_good @stmt.bind_param( ":named", 2 ) assert_equal 1, @stmt.execute!.length end def test_bind_param_with_various_types @db.transaction do @db.execute "create table all_types ( a integer primary key, b float, c string, d integer )" @db.execute "insert into all_types ( b, c, d ) values ( 1.4, 'hello', 68719476735 )" end assert_equal 1, @db.execute( "select * from all_types where b = ?", 1.4 ).length assert_equal 1, @db.execute( "select * from all_types where c = ?", 'hello').length assert_equal 1, @db.execute( "select * from all_types where d = ?", 68719476735).length end def test_execute_no_bind_no_block assert_instance_of SQLite3::ResultSet, @stmt.execute end def test_execute_with_bind_no_block assert_instance_of SQLite3::ResultSet, @stmt.execute( 1, 2 ) end def test_execute_no_bind_with_block called = false @stmt.execute { |row| called = true } assert called end def test_execute_with_bind_with_block called = 0 @stmt.execute( 1, 2 ) { |row| called += 1 } assert_equal 1, called end def test_reexecute r = @stmt.execute( 1, 2 ) assert_equal 2, r.to_a.length assert_nothing_raised { r = @stmt.execute( 1, 2 ) } assert_equal 2, r.to_a.length end def test_execute_bang_no_bind_no_block assert @stmt.execute!.empty? end def test_execute_bang_with_bind_no_block assert_equal 2, @stmt.execute!( 1, 2 ).length end def test_execute_bang_no_bind_with_block called = 0 @stmt.execute! { |row| called += 1 } assert_equal 0, called end def test_execute_bang_with_bind_with_block called = 0 @stmt.execute!( 1, 2 ) { |row| called += 1 } assert_equal 2, called end def test_columns c1 = @stmt.columns c2 = @stmt.columns assert_same c1, c2 assert_equal 2, c1.length end def test_columns_computed called = false @db.prepare( "select count(*) from foo" ) do |stmt| called = true assert_equal [ "count(*)" ], stmt.columns end assert called end def test_types t1 = @stmt.types t2 = @stmt.types assert_same t1, t2 assert_equal 2, t1.length end def test_types_computed called = false @db.prepare( "select count(*) from foo" ) do |stmt| called = true assert_equal [ nil ], stmt.types end assert called end def test_close stmt = @db.prepare( "select * from foo" ) assert !stmt.closed? stmt.close assert stmt.closed? assert_raise( SQLite3::Exception ) { stmt.execute } assert_raise( SQLite3::Exception ) { stmt.execute! } assert_raise( SQLite3::Exception ) { stmt.close } assert_raise( SQLite3::Exception ) { stmt.bind_params 5 } assert_raise( SQLite3::Exception ) { stmt.bind_param 1, 5 } assert_raise( SQLite3::Exception ) { stmt.columns } assert_raise( SQLite3::Exception ) { stmt.types } end def test_committing_tx_with_statement_active called = false @db.prepare( "select count(*) from foo" ) do |stmt| called = true count = stmt.execute!.first.first.to_i @db.transaction do @db.execute "insert into foo ( b ) values ( 'hello' )" end new_count = stmt.execute!.first.first.to_i assert_equal new_count, count+1 end assert called end end sqlite3-1.4.2/test/test_database_readonly.rb 0000644 0000041 0000041 00000002033 13611407300 021127 0 ustar www-data www-data require 'helper' module SQLite3 class TestDatabaseReadonly < SQLite3::TestCase def setup File.unlink 'test-readonly.db' if File.exist?('test-readonly.db') @db = SQLite3::Database.new('test-readonly.db') @db.execute("CREATE TABLE foos (id integer)") @db.close end def teardown @db.close unless @db.closed? File.unlink 'test-readonly.db' if File.exist?('test-readonly.db') end def test_open_readonly_database @db = SQLite3::Database.new('test-readonly.db', :readonly => true) assert @db.readonly? end def test_open_readonly_not_exists_database File.unlink 'test-readonly.db' assert_raise(SQLite3::CantOpenException) do @db = SQLite3::Database.new('test-readonly.db', :readonly => true) end end def test_insert_readonly_database @db = SQLite3::Database.new('test-readonly.db', :readonly => true) assert_raise(SQLite3::ReadOnlyException) do @db.execute("INSERT INTO foos (id) VALUES (12)") end end end end sqlite3-1.4.2/test/test_database_readwrite.rb 0000644 0000041 0000041 00000002320 13611407300 021277 0 ustar www-data www-data require 'helper' module SQLite3 class TestDatabaseReadwrite < SQLite3::TestCase def setup File.unlink 'test-readwrite.db' if File.exist?('test-readwrite.db') @db = SQLite3::Database.new('test-readwrite.db') @db.execute("CREATE TABLE foos (id integer)") @db.close end def teardown @db.close unless @db.closed? File.unlink 'test-readwrite.db' if File.exist?('test-readwrite.db') end def test_open_readwrite_database @db = SQLite3::Database.new('test-readwrite.db', :readwrite => true) assert !@db.readonly? end def test_open_readwrite_readonly_database assert_raise(RuntimeError) do @db = SQLite3::Database.new('test-readwrite.db', :readwrite => true, :readonly => true) end end def test_open_readwrite_not_exists_database File.unlink 'test-readwrite.db' assert_raise(SQLite3::CantOpenException) do @db = SQLite3::Database.new('test-readwrite.db', :readonly => true) end end def test_insert_readwrite_database @db = SQLite3::Database.new('test-readwrite.db', :readwrite => true) @db.execute("INSERT INTO foos (id) VALUES (12)") assert @db.changes == 1 end end end sqlite3-1.4.2/test/test_database_flags.rb 0000644 0000041 0000041 00000006657 13611407300 020426 0 ustar www-data www-data require 'helper' module SQLite3 class TestDatabaseFlags < SQLite3::TestCase def setup File.unlink 'test-flags.db' if File.exist?('test-flags.db') @db = SQLite3::Database.new('test-flags.db') @db.execute("CREATE TABLE foos (id integer)") @db.close end def teardown @db.close unless @db.closed? File.unlink 'test-flags.db' if File.exist?('test-flags.db') end def test_open_database_flags_constants defined_to_date = [:READONLY, :READWRITE, :CREATE, :DELETEONCLOSE, :EXCLUSIVE, :MAIN_DB, :TEMP_DB, :TRANSIENT_DB, :MAIN_JOURNAL, :TEMP_JOURNAL, :SUBJOURNAL, :MASTER_JOURNAL, :NOMUTEX, :FULLMUTEX] if SQLite3::SQLITE_VERSION_NUMBER > 3007002 defined_to_date += [:AUTOPROXY, :SHAREDCACHE, :PRIVATECACHE, :WAL] end if SQLite3::SQLITE_VERSION_NUMBER > 3007007 defined_to_date += [:URI] end if SQLite3::SQLITE_VERSION_NUMBER > 3007013 defined_to_date += [:MEMORY] end assert defined_to_date.sort == SQLite3::Constants::Open.constants.sort end def test_open_database_flags_conflicts_with_readonly assert_raise(RuntimeError) do @db = SQLite3::Database.new('test-flags.db', :flags => 2, :readonly => true) end end def test_open_database_flags_conflicts_with_readwrite assert_raise(RuntimeError) do @db = SQLite3::Database.new('test-flags.db', :flags => 2, :readwrite => true) end end def test_open_database_readonly_flags @db = SQLite3::Database.new('test-flags.db', :flags => SQLite3::Constants::Open::READONLY) assert @db.readonly? end def test_open_database_readwrite_flags @db = SQLite3::Database.new('test-flags.db', :flags => SQLite3::Constants::Open::READWRITE) assert !@db.readonly? end def test_open_database_readonly_flags_cant_open File.unlink 'test-flags.db' assert_raise(SQLite3::CantOpenException) do @db = SQLite3::Database.new('test-flags.db', :flags => SQLite3::Constants::Open::READONLY) end end def test_open_database_readwrite_flags_cant_open File.unlink 'test-flags.db' assert_raise(SQLite3::CantOpenException) do @db = SQLite3::Database.new('test-flags.db', :flags => SQLite3::Constants::Open::READWRITE) end end def test_open_database_misuse_flags assert_raise(SQLite3::MisuseException) do flags = SQLite3::Constants::Open::READONLY | SQLite3::Constants::Open::READWRITE # <== incompatible flags @db = SQLite3::Database.new('test-flags.db', :flags => flags) end end def test_open_database_create_flags File.unlink 'test-flags.db' flags = SQLite3::Constants::Open::READWRITE | SQLite3::Constants::Open::CREATE @db = SQLite3::Database.new('test-flags.db', :flags => flags) do |db| db.execute("CREATE TABLE foos (id integer)") db.execute("INSERT INTO foos (id) VALUES (12)") end assert File.exist?('test-flags.db') end def test_open_database_exotic_flags flags = SQLite3::Constants::Open::READWRITE | SQLite3::Constants::Open::CREATE exotic_flags = SQLite3::Constants::Open::NOMUTEX | SQLite3::Constants::Open::TEMP_DB @db = SQLite3::Database.new('test-flags.db', :flags => flags | exotic_flags) @db.execute("INSERT INTO foos (id) VALUES (12)") assert @db.changes == 1 end end end sqlite3-1.4.2/test/test_result_set.rb 0000644 0000041 0000041 00000001722 13611407300 017663 0 ustar www-data www-data require 'helper' module SQLite3 class TestResultSet < SQLite3::TestCase def test_each_hash db = SQLite3::Database.new ':memory:' db.execute "create table foo ( a integer primary key, b text )" list = ('a'..'z').to_a list.each do |t| db.execute "insert into foo (b) values (\"#{t}\")" end rs = db.prepare('select * from foo').execute rs.each_hash do |hash| assert_equal list[hash['a'] - 1], hash['b'] end end def test_next_hash db = SQLite3::Database.new ':memory:' db.execute "create table foo ( a integer primary key, b text )" list = ('a'..'z').to_a list.each do |t| db.execute "insert into foo (b) values (\"#{t}\")" end rs = db.prepare('select * from foo').execute rows = [] while row = rs.next_hash rows << row end rows.each do |hash| assert_equal list[hash['a'] - 1], hash['b'] end end end end sqlite3-1.4.2/test/test_collation.rb 0000644 0000041 0000041 00000004106 13611407300 017455 0 ustar www-data www-data # -*- coding: utf-8 -*- require 'helper' module SQLite3 class TestCollation < SQLite3::TestCase class Comparator attr_reader :calls def initialize @calls = [] end def compare left, right @calls << [left, right] left <=> right end end def setup @db = SQLite3::Database.new(':memory:') @create = "create table ex(id int, data string)" @db.execute(@create); [ [1, 'hello'], [2, 'world'] ].each do |vals| @db.execute('insert into ex (id, data) VALUES (?, ?)', vals) end end def test_custom_collation comparator = Comparator.new @db.collation 'foo', comparator assert_equal comparator, @db.collations['foo'] @db.execute('select data from ex order by 1 collate foo') assert_equal 1, comparator.calls.length end def test_remove_collation comparator = Comparator.new @db.collation 'foo', comparator @db.collation 'foo', nil assert_nil @db.collations['foo'] assert_raises(SQLite3::SQLException) do @db.execute('select data from ex order by 1 collate foo') end end if RUBY_VERSION >= '1.9.1' def test_encoding comparator = Comparator.new @db.collation 'foo', comparator @db.execute('select data from ex order by 1 collate foo') a, b = *comparator.calls.first assert_equal Encoding.find('UTF-8'), a.encoding assert_equal Encoding.find('UTF-8'), b.encoding end def test_encoding_default_internal warn_before = $-w $-w = false before_enc = Encoding.default_internal Encoding.default_internal = 'EUC-JP' comparator = Comparator.new @db.collation 'foo', comparator @db.execute('select data from ex order by 1 collate foo') a, b = *comparator.calls.first assert_equal Encoding.find('EUC-JP'), a.encoding assert_equal Encoding.find('EUC-JP'), b.encoding ensure Encoding.default_internal = before_enc $-w = warn_before end end end end sqlite3-1.4.2/faq/ 0000755 0000041 0000041 00000000000 13611407300 013674 5 ustar www-data www-data sqlite3-1.4.2/faq/faq.yml 0000644 0000041 0000041 00000027323 13611407300 015175 0 ustar www-data www-data --- - "How do I do a database query?": - "I just want an array of the rows...": >- Use the Database#execute method. If you don't give it a block, it will return an array of all the rows:
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
rows = db.execute( "select * from test" )
- "I'd like to use a block to iterate through the rows...": >-
Use the Database#execute method. If you give it a block, each row of the
result will be yielded to the block:
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
db.execute( "select * from test" ) do |row|
...
end
- "I need to get the column names as well as the rows...": >-
Use the Database#execute2 method. This works just like Database#execute;
if you don't give it a block, it returns an array of rows; otherwise, it
will yield each row to the block. _However_, the first row returned is
always an array of the column names from the query:
require 'sqlite3'
db = SQLite3::Database.new( "test.db" )
columns, *rows = db.execute2( "select * from test" )
# or use a block:
columns = nil
db.execute2( "select * from test" ) do |row|
if columns.nil?
columns = row
else
# process row
end
end
- "I just want the first row of the result set...": >-
Easy. Just call Database#get_first_row:
row = db.get_first_row( "select * from table" )
This also supports bind variables, just like Database#execute
and friends.
- "I just want the first value of the first row of the result set...": >-
Also easy. Just call Database#get_first_value:
count = db.get_first_value( "select count(*) from table" )
This also supports bind variables, just like Database#execute
and friends.
- "How do I prepare a statement for repeated execution?": >-
If the same statement is going to be executed repeatedly, you can speed
things up a bit by _preparing_ the statement. You do this via the
Database#prepare method. It returns a Statement object, and you can
then invoke #execute on that to get the ResultSet:
stmt = db.prepare( "select * from person" )
1000.times do
stmt.execute do |result|
...
end
end
stmt.close
# or, use a block
db.prepare( "select * from person" ) do |stmt|
1000.times do
stmt.execute do |result|
...
end
end
end
This is made more useful by the ability to bind variables to placeholders
via the Statement#bind_param and Statement#bind_params methods. (See the
next FAQ for details.)
- "How do I use placeholders in an SQL statement?": >-
Placeholders in an SQL statement take any of the following formats:
* @?@
* @?_nnn_@
* @:_word_@
Where _n_ is an integer, and _word_ is an alpha-numeric identifier (or
number). When the placeholder is associated with a number, that number
identifies the index of the bind variable to replace it with. When it
is an identifier, it identifies the name of the correponding bind
variable. (In the instance of the first format--a single question
mark--the placeholder is assigned a number one greater than the last
index used, or 1 if it is the first.)
For example, here is a query using these placeholder formats:
select *
from table
where ( c = ?2 or c = ? )
and d = :name
and e = :1
This defines 5 different placeholders: 1, 2, 3, and "name".
You replace these placeholders by _binding_ them to values. This can be
accomplished in a variety of ways.
The Database#execute, and Database#execute2 methods all accept additional
arguments following the SQL statement. These arguments are assumed to be
bind parameters, and they are bound (positionally) to their corresponding
placeholders:
db.execute( "select * from table where a = ? and b = ?",
"hello",
"world" )
The above would replace the first question mark with 'hello' and the
second with 'world'. If the placeholders have an explicit index given, they
will be replaced with the bind parameter at that index (1-based).
If a Hash is given as a bind parameter, then its key/value pairs are bound
to the placeholders. This is how you bind by name:
db.execute( "select * from table where a = :name and b = :value",
"name" => "bob",
"value" => "priceless" )
You can also bind explicitly using the Statement object itself. Just pass
additional parameters to the Statement#execute statement:
db.prepare( "select * from table where a = :name and b = ?" ) do |stmt|
stmt.execute "value", "name" => "bob"
end
Or do a Database#prepare to get the Statement, and then use either
Statement#bind_param or Statement#bind_params:
stmt = db.prepare( "select * from table where a = :name and b = ?" )
stmt.bind_param( "name", "bob" )
stmt.bind_param( 1, "value" )
# or
stmt.bind_params( "value", "name" => "bob" )
- "How do I discover metadata about a query?": >-
If you ever want to know the names or types of the columns in a result
set, you can do it in several ways.
The first way is to ask the row object itself. Each row will have a
property "fields" that returns an array of the column names. The row
will also have a property "types" that returns an array of the column
types:
rows = db.execute( "select * from table" )
p rows[0].fields
p rows[0].types
Obviously, this approach requires you to execute a statement that actually
returns data. If you don't know if the statement will return any rows, but
you still need the metadata, you can use Database#query and ask the
ResultSet object itself:
db.query( "select * from table" ) do |result|
p result.columns
p result.types
...
end
Lastly, you can use Database#prepare and ask the Statement object what
the metadata are:
stmt = db.prepare( "select * from table" )
p stmt.columns
p stmt.types
- "I'd like the rows to be indexible by column name.": >-
By default, each row from a query is returned as an Array of values. This
means that you can only obtain values by their index. Sometimes, however,
you would like to obtain values by their column name.
The first way to do this is to set the Database property "results_as_hash"
to true. If you do this, then all rows will be returned as Hash objects,
with the column names as the keys. (In this case, the "fields" property
is unavailable on the row, although the "types" property remains.)
db.results_as_hash = true
db.execute( "select * from table" ) do |row|
p row['column1']
p row['column2']
end
The other way is to use Ara Howard's
"ArrayFields":http://rubyforge.org/projects/arrayfields
module. Just require "arrayfields", and all of your rows will be indexable
by column name, even though they are still arrays!
require 'arrayfields'
...
db.execute( "select * from table" ) do |row|
p row[0] == row['column1']
p row[1] == row['column2']
end
- "I'd like the values from a query to be the correct types, instead of String.": >-
You can turn on "type translation" by setting Database#type_translation to
true:
db.type_translation = true
db.execute( "select * from table" ) do |row|
p row
end
By doing this, each return value for each row will be translated to its
correct type, based on its declared column type.
You can even declare your own translation routines, if (for example) you are
using an SQL type that is not handled by default:
# assume "objects" table has the following schema:
# create table objects (
# name varchar2(20),
# thing object
# )
db.type_translation = true
db.translator.add_translator( "object" ) do |type, value|
db.decode( value )
end
h = { :one=>:two, "three"=>"four", 5=>6 }
dump = db.encode( h )
db.execute( "insert into objects values ( ?, ? )", "bob", dump )
obj = db.get_first_value( "select thing from objects where name='bob'" )
p obj == h
- "How do I insert binary data into the database?": >-
Use blobs. Blobs are new features of SQLite3. You have to use bind
variables to make it work:
db.execute( "insert into foo ( ?, ? )",
SQLite3::Blob.new( "\0\1\2\3\4\5" ),
SQLite3::Blob.new( "a\0b\0c\0d ) )
The blob values must be indicated explicitly by binding each parameter to
a value of type SQLite3::Blob.
- "How do I do a DDL (insert, update, delete) statement?": >-
You can actually do inserts, updates, and deletes in exactly the same way
as selects, but in general the Database#execute method will be most
convenient:
db.execute( "insert into table values ( ?, ? )", *bind_vars )
- "How do I execute multiple statements in a single string?": >-
The standard query methods (Database#execute, Database#execute2,
Database#query, and Statement#execute) will only execute the first
statement in the string that is given to them. Thus, if you have a
string with multiple SQL statements, each separated by a string,
you can't use those methods to execute them all at once.
Instead, use Database#execute_batch:
sql = <
Unlike the other query methods, Database#execute_batch accepts no
block. It will also only ever return +nil+. Thus, it is really only
suitable for batch processing of DDL statements.
- "How do I begin/end a transaction?":
Use Database#transaction to start a transaction. If you give it a block,
the block will be automatically committed at the end of the block,
unless an exception was raised, in which case the transaction will be
rolled back. (Never explicitly call Database#commit or Database#rollback
inside of a transaction block--you'll get errors when the block
terminates!)
database.transaction do |db|
db.execute( "insert into table values ( 'a', 'b', 'c' )" )
...
end
Alternatively, if you don't give a block to Database#transaction, the
transaction remains open until you explicitly call Database#commit or
Database#rollback.
db.transaction
db.execute( "insert into table values ( 'a', 'b', 'c' )" )
db.commit
Note that SQLite does not allow nested transactions, so you'll get errors
if you try to open a new transaction while one is already active. Use
Database#transaction_active? to determine whether a transaction is
active or not.
#- "How do I discover metadata about a table/index?":
#
#- "How do I do tweak database settings?":
sqlite3-1.4.2/faq/faq.rb 0000644 0000041 0000041 00000006146 13611407300 014777 0 ustar www-data www-data require 'yaml'
require 'redcloth'
def process_faq_list( faqs )
puts ""
faqs.each do |faq|
process_faq_list_item faq
end
puts "
"
end
def process_faq_list_item( faq )
question = faq.keys.first
answer = faq.values.first
print ""
question_text = RedCloth.new(question).to_html.gsub( %r{?p>},"" )
if answer.is_a?( Array )
puts question_text
process_faq_list answer
else
print "#{question_text}"
end
puts " "
end
def process_faq_descriptions( faqs, path=nil )
faqs.each do |faq|
process_faq_description faq, path
end
end
def process_faq_description( faq, path )
question = faq.keys.first
path = ( path ? path + " " : "" ) + question
answer = faq.values.first
if answer.is_a?( Array )
process_faq_descriptions( answer, path )
else
title = RedCloth.new( path ).to_html.gsub( %r{?p>}, "" )
answer = RedCloth.new( answer || "" )
puts ""
puts "#{title}"
puts "#{add_api_links(answer.to_html)}"
end
end
API_OBJECTS = [ "Database", "Statement", "ResultSet",
"ParsedStatement", "Pragmas", "Translator" ].inject( "(" ) { |acc,name|
acc << "|" if acc.length > 1
acc << name
acc
} + ")"
def add_api_links( text )
text.gsub( /#{API_OBJECTS}(#(\w+))?/ ) do
disp_obj = obj = $1
case obj
when "Pragmas"; disp_obj = "Database"
end
method = $3
s = "#{disp_obj}"
s << "##{method}" if method
s << ""
s
end
end
faqs = YAML.load( File.read( "faq.yml" ) )
puts <<-EOF
SQLite3/Ruby FAQ
SQLite/Ruby FAQ
EOF
process_faq_list( faqs )
puts ""
process_faq_descriptions( faqs )
puts ""
sqlite3-1.4.2/sqlite3.gemspec 0000644 0000041 0000041 00000012524 13611407300 016062 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: sqlite3 1.4.2 ruby lib
# stub: ext/sqlite3/extconf.rb
Gem::Specification.new do |s|
s.name = "sqlite3".freeze
s.version = "1.4.2"
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "msys2_mingw_dependencies" => "sqlite3" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Jamis Buck".freeze, "Luis Lavena".freeze, "Aaron Patterson".freeze]
s.date = "2019-12-18"
s.description = "This module allows Ruby programs to interface with the SQLite3\ndatabase engine (http://www.sqlite.org). You must have the\nSQLite engine installed in order to build this module.\n\nNote that this module is only compatible with SQLite 3.6.16 or newer.".freeze
s.email = ["jamis@37signals.com".freeze, "luislavena@gmail.com".freeze, "aaron@tenderlovemaking.com".freeze]
s.extensions = ["ext/sqlite3/extconf.rb".freeze]
s.extra_rdoc_files = ["API_CHANGES.rdoc".freeze, "CHANGELOG.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "ext/sqlite3/aggregator.c".freeze, "ext/sqlite3/backup.c".freeze, "ext/sqlite3/database.c".freeze, "ext/sqlite3/exception.c".freeze, "ext/sqlite3/sqlite3.c".freeze, "ext/sqlite3/statement.c".freeze]
s.files = [".gemtest".freeze, ".travis.yml".freeze, "API_CHANGES.rdoc".freeze, "CHANGELOG.rdoc".freeze, "ChangeLog.cvs".freeze, "Gemfile".freeze, "LICENSE".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "Rakefile".freeze, "appveyor.yml".freeze, "ext/sqlite3/aggregator.c".freeze, "ext/sqlite3/aggregator.h".freeze, "ext/sqlite3/backup.c".freeze, "ext/sqlite3/backup.h".freeze, "ext/sqlite3/database.c".freeze, "ext/sqlite3/database.h".freeze, "ext/sqlite3/exception.c".freeze, "ext/sqlite3/exception.h".freeze, "ext/sqlite3/extconf.rb".freeze, "ext/sqlite3/sqlite3.c".freeze, "ext/sqlite3/sqlite3_ruby.h".freeze, "ext/sqlite3/statement.c".freeze, "ext/sqlite3/statement.h".freeze, "faq/faq.rb".freeze, "faq/faq.yml".freeze, "lib/sqlite3.rb".freeze, "lib/sqlite3/constants.rb".freeze, "lib/sqlite3/database.rb".freeze, "lib/sqlite3/errors.rb".freeze, "lib/sqlite3/pragmas.rb".freeze, "lib/sqlite3/resultset.rb".freeze, "lib/sqlite3/statement.rb".freeze, "lib/sqlite3/translator.rb".freeze, "lib/sqlite3/value.rb".freeze, "lib/sqlite3/version.rb".freeze, "rakelib/faq.rake".freeze, "rakelib/gem.rake".freeze, "rakelib/native.rake".freeze, "rakelib/vendor_sqlite3.rake".freeze, "setup.rb".freeze, "test/helper.rb".freeze, "test/test_backup.rb".freeze, "test/test_collation.rb".freeze, "test/test_database.rb".freeze, "test/test_database_flags.rb".freeze, "test/test_database_readonly.rb".freeze, "test/test_database_readwrite.rb".freeze, "test/test_deprecated.rb".freeze, "test/test_encoding.rb".freeze, "test/test_integration.rb".freeze, "test/test_integration_aggregate.rb".freeze, "test/test_integration_open_close.rb".freeze, "test/test_integration_pending.rb".freeze, "test/test_integration_resultset.rb".freeze, "test/test_integration_statement.rb".freeze, "test/test_result_set.rb".freeze, "test/test_sqlite3.rb".freeze, "test/test_statement.rb".freeze, "test/test_statement_execute.rb".freeze]
s.homepage = "https://github.com/sparklemotion/sqlite3-ruby".freeze
s.licenses = ["BSD-3-Clause".freeze]
s.rdoc_options = ["--main".freeze, "README.rdoc".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7".freeze)
s.rubygems_version = "2.5.2.1".freeze
s.summary = "This module allows Ruby programs to interface with the SQLite3 database engine (http://www.sqlite.org)".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q.freeze, ["~> 3.20"])
s.add_development_dependency(%q.freeze, ["~> 1.0"])
s.add_development_dependency(%q.freeze, ["~> 1.0"])
s.add_development_dependency(%q.freeze, ["~> 0.6.2"])
s.add_development_dependency(%q.freeze, ["~> 5.13"])
s.add_development_dependency(%q.freeze, ["~> 1.0"])
s.add_development_dependency(%q.freeze, ["~> 0.6.0"])
s.add_development_dependency(%q.freeze, ["< 7", ">= 4.0"])
else
s.add_dependency(%q.freeze, ["~> 3.20"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 0.6.2"])
s.add_dependency(%q.freeze, ["~> 5.13"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 0.6.0"])
s.add_dependency(%q.freeze, ["< 7", ">= 4.0"])
end
else
s.add_dependency(%q.freeze, ["~> 3.20"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 0.6.2"])
s.add_dependency(%q.freeze, ["~> 5.13"])
s.add_dependency(%q.freeze, ["~> 1.0"])
s.add_dependency(%q.freeze, ["~> 0.6.0"])
s.add_dependency(%q.freeze, ["< 7", ">= 4.0"])
end
end
sqlite3-1.4.2/API_CHANGES.rdoc 0000644 0000041 0000041 00000004401 13611407300 015536 0 ustar www-data www-data = API Changes
* SQLite3::Database#execute only accepts an array for bind parameters.
* SQLite3::ResultSet used to query the database for the first row, regardless
of whether the user asked for it or not. I have removed that so that rows
will not be returned until the user asks for them. This is a subtle but
sometimes important change in behavior.
83882d2208ed189361617d5ab8532a325aaf729d
* SQLite3::Database#trace now takes either a block or an object that responds
to "call". The previous implementation passed around a VALUE that was cast
to a void *. This is dangerous because the value could get garbage collected
before the proc was called. If the user wants data passed around with the
block, they should use variables available to the closure or create an
object.
* SQLite3::Statement#step automatically converts to ruby types, where before
all values were automatically yielded as strings. This will only be a
problem for people who were accessing information about the database that
wasn't previously passed through the pure ruby conversion code.
* SQLite3::Database#errmsg no longer takes a parameter to return error
messages as UTF-16. Do people even use that? I opt for staying UTF-8 when
possible. See test_integration.rb test_errmsg_utf16
* SQLite3::Database#authorize same changes as trace
* test/test_tc_database.rb was removed because we no longer use the Driver
design pattern.
= Garbage Collection Strategy
All statements keep pointers back to their respective database connections.
The @connection instance variable on the Statement handle keeps the database
connection alive. Memory allocated for a statement handler will be freed in
two cases:
* close is called on the statement
* The SQLite3::Database object gets garbage collected
We can't free the memory for the statement in the garbage collection function
for the statement handler. The reason is because there exists a race
condition. We cannot guarantee the order in which objects will be garbage
collected. So, it is possible that a connection and a statement are up for
garbage collection. If the database connection were to be free'd before the
statement, then boom. Instead we'll be conservative and free unclosed
statements when the connection is terminated.
sqlite3-1.4.2/rakelib/ 0000755 0000041 0000041 00000000000 13611407300 014536 5 ustar www-data www-data sqlite3-1.4.2/rakelib/faq.rake 0000644 0000041 0000041 00000000274 13611407300 016154 0 ustar www-data www-data # Generate FAQ
desc "Generate the FAQ document"
task :faq => ['faq/faq.html']
file 'faq/faq.html' => ['faq/faq.rb', 'faq/faq.yml'] do
cd 'faq' do
ruby "faq.rb > faq.html"
end
end
sqlite3-1.4.2/rakelib/vendor_sqlite3.rake 0000644 0000041 0000041 00000006045 13611407300 020350 0 ustar www-data www-data require "rake/clean"
require "rake/extensioncompiler"
require "mini_portile"
CLOBBER.include("ports")
directory "ports"
def define_sqlite_task(platform, host)
recipe = MiniPortile.new "sqlite3", BINARY_VERSION
recipe.files << "http://sqlite.org#{URL_PATH}/sqlite-autoconf-#{URL_VERSION}.tar.gz"
recipe.host = host
desc "Compile sqlite3 for #{platform} (#{host})"
task "ports:sqlite3:#{platform}" => ["ports"] do |t|
checkpoint = "ports/.#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
unless File.exist?(checkpoint)
cflags = "-O2 -DSQLITE_ENABLE_COLUMN_METADATA"
cflags << " -fPIC" if recipe.host && recipe.host.include?("x86_64")
recipe.configure_options << "CFLAGS='#{cflags}'"
recipe.cook
touch checkpoint
end
end
recipe
end
# native sqlite3 compilation
recipe = define_sqlite_task(RUBY_PLATFORM, RbConfig::CONFIG["host"])
# force compilation of sqlite3 when working natively under MinGW
if RUBY_PLATFORM =~ /mingw/
RUBY_EXTENSION.config_options << "--with-opt-dir=#{recipe.path}"
# also prepend DevKit into compilation phase
Rake::Task["compile"].prerequisites.unshift "devkit", "ports:sqlite3:#{RUBY_PLATFORM}"
Rake::Task["native"].prerequisites.unshift "devkit", "ports:sqlite3:#{RUBY_PLATFORM}"
end
# trick to test local compilation of sqlite3
if ENV["USE_MINI_PORTILE"] == "true"
# fake recipe so we can build a directory to it
recipe = MiniPortile.new "sqlite3", BINARY_VERSION
recipe.host = RbConfig::CONFIG["host"]
RUBY_EXTENSION.config_options << "--with-opt-dir=#{recipe.path}"
# compile sqlite3 first
Rake::Task["compile"].prerequisites.unshift "ports:sqlite3:#{RUBY_PLATFORM}"
end
# iterate over all cross-compilation platforms and define the proper
# sqlite3 recipe for it.
if RUBY_EXTENSION.cross_compile
config_path = File.expand_path("~/.rake-compiler/config.yml")
if File.exist?(config_path)
# obtains platforms from rake-compiler's config.yml
config_file = YAML.load_file(config_path)
Array(RUBY_EXTENSION.cross_platform).each do |platform|
# obtain platform from rbconfig file
config_key = config_file.keys.sort.find { |key|
key.start_with?("rbconfig-#{platform}-")
}
rbfile = config_file[config_key]
# skip if rbconfig cannot be read
next unless File.exist?(rbfile)
host = IO.read(rbfile).match(/CONFIG\["CC"\] = "(.*)"/)[1].sub(/\-gcc/, '')
recipe = define_sqlite_task(platform, host)
RUBY_EXTENSION.cross_config_options << {
platform => "--with-opt-dir=#{recipe.path}"
}
# pre-compile sqlite3 port when cross-compiling
task :cross => "ports:sqlite3:#{platform}"
end
else
warn "rake-compiler configuration doesn't exist, but is required for ports"
end
end
task :cross do
["CC", "CXX", "LDFLAGS", "CPPFLAGS", "RUBYOPT"].each do |var|
ENV.delete(var)
end
end
desc "Build windows binary gems per rake-compiler-dock."
task "gem:windows" do
require "rake_compiler_dock"
RakeCompilerDock.sh "bundle && rake cross native gem MAKE='nice make -j`nproc`'"
end
sqlite3-1.4.2/rakelib/native.rake 0000644 0000041 0000041 00000003233 13611407300 016671 0 ustar www-data www-data # use rake-compiler for building the extension
require 'rake/extensiontask'
require 'rake/extensioncompiler'
# NOTE: version used by cross compilation of Windows native extension
# It do not affect compilation under other operating systems
# The version indicated is the minimum DLL suggested for correct functionality
BINARY_VERSION = "3.8.11.1"
URL_VERSION = "3081101"
URL_PATH = "/2015"
task :devkit do
begin
require "devkit"
rescue LoadError => e
abort "Failed to activate RubyInstaller's DevKit required for compilation."
end
end
# build sqlite3_native C extension
RUBY_EXTENSION = Rake::ExtensionTask.new('sqlite3_native', HOE.spec) do |ext|
# where to locate the extension
ext.ext_dir = 'ext/sqlite3'
# where native extension will be copied (matches makefile)
ext.lib_dir = "lib/sqlite3"
# clean binary folders always
CLEAN.include("#{ext.lib_dir}/?.?")
# automatically add build options to avoid need of manual input
if RUBY_PLATFORM =~ /mswin|mingw/ then
# define target for extension (supporting fat binaries)
RUBY_VERSION =~ /(\d+\.\d+)/
ext.lib_dir = "lib/sqlite3/#{$1}"
else
# detect cross-compiler available
begin
Rake::ExtensionCompiler.mingw_host
ext.cross_compile = true
ext.cross_platform = ['i386-mswin32-60', 'i386-mingw32', 'x64-mingw32']
ext.cross_compiling do |spec|
# The fat binary gem doesn't depend on the sqlite3 package, since it bundles the library.
spec.metadata.delete('msys2_mingw_dependencies')
end
rescue RuntimeError
# noop
end
end
end
# ensure things are compiled prior testing
task :test => [:compile]
# vim: syntax=ruby
sqlite3-1.4.2/rakelib/gem.rake 0000644 0000041 0000041 00000002160 13611407300 016151 0 ustar www-data www-data begin
require 'hoe'
rescue LoadError
# try with rubygems?
require 'rubygems'
require 'hoe'
end
Hoe.plugin :debugging, :doofus, :git, :minitest, :bundler, :gemspec
HOE = Hoe.spec 'sqlite3' do
developer 'Jamis Buck', 'jamis@37signals.com'
developer 'Luis Lavena', 'luislavena@gmail.com'
developer 'Aaron Patterson', 'aaron@tenderlovemaking.com'
license "BSD-3-Clause"
self.readme_file = 'README.rdoc'
self.history_file = 'CHANGELOG.rdoc'
self.extra_rdoc_files = FileList['*.rdoc', 'ext/**/*.c']
require_ruby_version ">= 1.8.7"
require_rubygems_version ">= 1.3.5"
spec_extras[:extensions] = ["ext/sqlite3/extconf.rb"]
spec_extras[:metadata] = {'msys2_mingw_dependencies' => 'sqlite3'}
extra_dev_deps << ['rake-compiler', "~> 1.0"]
extra_dev_deps << ['rake-compiler-dock', "~> 0.6.0"]
extra_dev_deps << ["mini_portile", "~> 0.6.2"]
extra_dev_deps << ["minitest", "~> 5.0"]
extra_dev_deps << ["hoe-bundler", "~> 1.0"]
extra_dev_deps << ["hoe-gemspec", "~> 1.0"]
clean_globs.push('**/test.db')
end
Hoe.add_include_dirs '.'
# vim: syntax=ruby
sqlite3-1.4.2/LICENSE 0000644 0000041 0000041 00000002734 13611407300 014140 0 ustar www-data www-data Copyright (c) 2004, Jamis Buck (jamis@jamisbuck.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
sqlite3-1.4.2/CHANGELOG.rdoc 0000644 0000041 0000041 00000025170 13611407300 015272 0 ustar www-data www-data === 1.4.2
* Travis: Drop unused setting "sudo: false"
* The taint mechanism will be deprecated in Ruby 2.7
* Fix Ruby 2.7 rb_check_safe_obj warnings
* Update travis config
=== 1.4.1
* Don't mandate dl functions for the extention build
* bumping version
=== 1.4.0
* Enhancements
* Better aggregator support
* Bugfixes
* Various
=== 1.3.13
* Enancements
* Support SQLite flags when defining functions
* Add definition for SQLITE_DETERMINISTIC flag
=== 1.3.12
* Bugfixes:
* OS X install will default to homebrew if available. Fixes #195
=== 1.3.11 / 2015-10-10
* Enhancements:
* Windows: build against SQLite 3.8.11.1
* Internal:
* Use rake-compiler-dock to build Windows binaries. Pull #159 [larskanis]
* Expand Ruby versions being tested for Travis and AppVeyor
=== 1.3.10 / 2014-10-30
* Enhancements:
* Windows: build against SQLite 3.8.6. Closes #135 [Hubro]
=== 1.3.9 / 2014-02-25
* Bugfixes:
* Reset exception message. Closes #80
* Reduce warnings due unused pointers. Closes #89
* Add BSD-3 license reference to gemspec. Refs #99 and #106
=== 1.3.8 / 2013-08-17
* Enhancements:
* Windows: build against SQLite 3.7.17
* Bugfixes:
* Reset exception message. Closes #80
* Correctly convert BLOB values to Ruby. Closes #65
* Add MIT license reference to gemspec. Closes #99
* Remove unused pointer. Closes #89
* Internal:
* Backport improvements in cross compilation for Windows
* Use of Minitest for internal tests
* Use Gemfile (generated by Hoe) to deal with dependencies
* Cleanup Travis CI
=== 1.3.7 / 2013-01-11
* Bugfixes
* Closing a bad statement twice will not segv.
* Aggregate handlers are initialized on each query. Closes #44
* Internal
* Unset environment variables that could affect cross compilation.
=== 1.3.6 / 2012-04-16
* Enhancements
* Windows: build against SQLite 3.7.11
* Added SQLite3::ResultSet#each_hash for fetching each row as a hash.
* Added SQLite3::ResultSet#next_hash for fetching one row as a hash.
* Bugfixes
* Support both UTF-16LE and UTF-16BE encoding modes on PPC. Closes #63
* Protect parameters to custom functions from being garbage collected too
soon. Fixes #60. Thanks hirataya!
* Fix backwards compatibility with 1.2.5 with bind vars and `query` method.
Fixes #35.
* Fix double definition error caused by defining sqlite3_int64/uint64.
* Fix suspicious version regexp.
* Deprecations
* ArrayWithTypesAndFields#types is deprecated and the class will be removed
in version 2.0.0. Please use the `types` method on the ResultSet class
that created this object.
* ArrayWithTypesAndFields#fields is deprecated and the class will be removed
in version 2.0.0. Please use the `columns` method on the ResultSet class
that created this object.
* The ArrayWithTypesAndFields class will be removed in 2.0.0
* The ArrayWithTypes class will be removed in 2.0.0
* HashWithTypesAndFields#types is deprecated and the class will be removed
in version 2.0.0. Please use the `types` method on the ResultSet class
that created this object.
* HashWithTypesAndFields#fields is deprecated and the class will be removed
in version 2.0.0. Please use the `columns` method on the ResultSet class
that created this object.
=== 1.3.5 / 2011-12-03 - ZOMG Holidays are here Edition!
* Enhancements
* Windows: build against SQLite 3.7.9
* Static: enable SQLITE_ENABLE_COLUMN_METADATA
* Added Statement#clear_bindings! to set bindings back to nil
* Bugfixes
* Fixed a segv on Database.new. Fixes #34 (thanks nobu!)
* Database error is not reset, so don't check it in Statement#reset!
* Remove conditional around Bignum statement bindings.
Fixes #52. Fixes #56. Thank you Evgeny Myasishchev.
* Internal
* Use proper endianness when testing database connection with UTF-16.
Fixes #40. Fixes #51
* Use -fPIC for static compilation when host is x86_64.
=== 1.3.4 / 2011-07-25
* Enhancements:
* Windows: build against SQLite 3.7.7.1
* Windows: build static binaries that do not depend on sqlite3.dll be
installed anymore
* Bugfixes
* Backup API is conditionaly required so that older libsqlite3 can be used.
Thanks Hongli Lai.
* Fixed segmentation fault when nil is passed to SQLite3::Statement.new
* Fix extconf's hardcoded path that affected installation on certain systems.
=== 1.3.3 / 2010-01-16
* Bugfixes
* Abort on installation if sqlite3_backup_init is missing. Fixes #19
* Gem has been renamed to 'sqlite3'. Please use `gem install sqlite3`
=== 1.3.2 / 2010-10-30 / RubyConf Uruguay Edition!
* Enhancements:
* Windows: build against 3.7.3 version of SQLite3
* SQLite3::Database can now be open as readonly
db = SQLite3::Database.new('my.db', :readonly => true)
* Added SQLite3::SQLITE_VERSION and SQLite3::SQLITE_VERSION_NUMBER [nurse]
* Bugfixes
* type_translation= works along with Database#execute and a block
* defined functions are kept in a hash to prevent GC. #7
* Removed GCC specific flags from extconf.
* DEPRECATIONS
* SQLite3::Database#type_translation= will be deprecated in the future with
no replacement.
* SQlite3::Version will be deprecated in 2.0.0 with SQLite3::VERSION as the
replacement.
=== 1.3.1 / 2010-07-09
* Enhancements
* Custom collations may be defined using SQLite3::Database#collation
* Bugfixes
* Statements returning 0 columns are automatically stepped. [RF #28308]
* SQLite3::Database#encoding works on 1.8 and 1.9
=== 1.3.0 / 2010-06-06
* Enhancements
* Complete rewrite of C-based adapter from SWIG to hand-crafted one [tenderlove]
See API_CHANGES document for details.
This closes: Bug #27300, Bug #27241, Patch #16020
* Improved UTF, Unicode, M17N, all that handling and proper BLOB handling [tenderlove, nurse]
* Added support for type translations [tenderlove]
@db.translator.add_translator('sometime') do |type, thing|
'output' # this will be returned as value for that column
end
* Experimental
* Added API to access and load extensions. [kashif]
These functions maps directly into SQLite3 own enable_load_extension()
and load_extension() C-API functions. See SQLite3::Database API documentation for details.
This closes: Patches #9178
* Bugfixes
* Corrected gem dependencies (runtime and development)
* Fixed threaded tests [Alexey Borzenkov]
* Removed GitHub gemspec
* Fixed "No definition for" warnings from RDoc
* Generate zip and tgz files for releases
* Added Luis Lavena as gem Author (maintainer)
* Prevent mkmf interfere with Mighty Snow Leopard
* Allow extension compilation search for common lib paths [kashif]
(lookup /usr/local, /opt/local and /usr)
* Corrected extension compilation under MSVC [romuloceccon]
* Define load_extension functionality based on availability [tenderlove]
* Deprecation notices for Database#query. Fixes RF #28192
=== 1.3.0.beta.2 / 2010-05-15
* Enhancements
* Added support for type translations [tenderlove]
@db.translator.add_translator('sometime') do |type, thing|
'output' # this will be returned as value for that column
end
* Bugfixes
* Allow extension compilation search for common lib paths [kashif]
(lookup /usr/local, /opt/local and /usr)
* Corrected extension compilation under MSVC [romuloceccon]
* Define load_extension functionality based on availability [tenderlove]
* Deprecation notices for Database#query. Fixes RF #28192
=== 1.3.0.beta.1 / 2010-05-10
* Enhancements
* Complete rewrite of C-based adapter from SWIG to hand-crafted one [tenderlove]
See API_CHANGES document for details.
This closes: Bug #27300, Bug #27241, Patch #16020
* Improved UTF, Unicode, M17N, all that handling and proper BLOB handling [tenderlove, nurse]
* Experimental
* Added API to access and load extensions. [kashif]
These functions maps directly into SQLite3 own enable_load_extension()
and load_extension() C-API functions. See SQLite3::Database API documentation for details.
This closes: Patches #9178
* Bugfixes
* Corrected gem dependencies (runtime and development)
* Fixed threaded tests [Alexey Borzenkov]
* Removed GitHub gemspec
* Fixed "No definition for" warnings from RDoc
* Generate zip and tgz files for releases
* Added Luis Lavena as gem Author (maintainer)
* Prevent mkmf interfere with Mighty Snow Leopard
=== 1.2.5 / 25 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
* Taint returned values. Patch #20325.
* Database.open and Database.new now take an optional block [Gerrit Kaiser]
=== 1.2.4.1 (internal) / 5 Jul 2009
* Check for illegal nil before executing SQL [Erik Veenstra]
* Switch to Hoe for gem task management and packaging.
* Advertise rake-compiler as development dependency.
* Build gem binaries for Windows.
* Improved Ruby 1.9 support compatibility.
=== 1.2.4 / 27 Aug 2008
* Package the updated C file for source builds. [Jamis Buck]
=== 1.2.3 / 26 Aug 2008
* Fix incorrect permissions on database.rb and translator.rb [Various]
* Avoid using Object#extend for greater speedups [Erik Veenstra]
* Ruby 1.9 compatibility tweaks for Array#zip [jimmy88@gmail.com]
* Fix linking against Ruby 1.8.5 [Rob Holland ]
=== 1.2.2 / 31 May 2008
* Make the table_info method adjust the returned default value for the rows
so that the sqlite3 change in 3.3.8 and greater can be handled
transparently [Jamis Buck ]
* Ruby 1.9 compatibility tweaks [Roman Le Negrate ]
* Various performance enhancements [thanks Erik Veenstra]
* Correct busy_handler documentation [Rob Holland ]
* Use int_bind64 on Fixnum values larger than a 32bit C int can take. [Rob Holland ]
* Work around a quirk in SQLite's error reporting by calling sqlite3_reset
to produce a more informative error code upon a failure from
sqlite3_step. [Rob Holland ]
* Various documentation, test, and style tweaks [Rob Holland ]
* Be more granular with time/data translation [Rob Holland ]
* Use Date directly for parsing rather than going via Time [Rob Holland ]
* Check for the rt library and fdatasync so we link against that when
needed [Rob Holland ]
* Rename data structures to avoid collision on win32. based on patch
by: Luis Lavena [Rob Holland ]
* Add test for defaults [Daniel Rodríguez Troitiño]
* Correctly unquote double-quoted pragma defaults [Łukasz Dargiewicz ]
sqlite3-1.4.2/.gemtest 0000644 0000041 0000041 00000000000 13611407300 014564 0 ustar www-data www-data sqlite3-1.4.2/Rakefile 0000644 0000041 0000041 00000000166 13611407300 014575 0 ustar www-data www-data #
# NOTE: Keep this file clean.
# Add your customizations inside tasks directory.
# Thank You.
#
# vim: syntax=ruby
sqlite3-1.4.2/lib/ 0000755 0000041 0000041 00000000000 13611407300 013673 5 ustar www-data www-data sqlite3-1.4.2/lib/sqlite3/ 0000755 0000041 0000041 00000000000 13611407300 015257 5 ustar www-data www-data sqlite3-1.4.2/lib/sqlite3/version.rb 0000644 0000041 0000041 00000000711 13611407300 017270 0 ustar www-data www-data module SQLite3
VERSION = '1.4.2'
module VersionProxy
MAJOR = 1
MINOR = 4
TINY = 2
BUILD = nil
STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." )
#:beta-tag:
VERSION = ::SQLite3::VERSION
end
def self.const_missing(name)
return super unless name == :Version
warn(<<-eowarn) if $VERBOSE
#{caller[0]}: SQLite::Version will be removed in sqlite3-ruby version 2.0.0
eowarn
VersionProxy
end
end
sqlite3-1.4.2/lib/sqlite3/errors.rb 0000644 0000041 0000041 00000002403 13611407300 017117 0 ustar www-data www-data require 'sqlite3/constants'
module SQLite3
class Exception < ::StandardError
# A convenience for accessing the error code for this exception.
attr_reader :code
end
class SQLException < Exception; end
class InternalException < Exception; end
class PermissionException < Exception; end
class AbortException < Exception; end
class BusyException < Exception; end
class LockedException < Exception; end
class MemoryException < Exception; end
class ReadOnlyException < Exception; end
class InterruptException < Exception; end
class IOException < Exception; end
class CorruptException < Exception; end
class NotFoundException < Exception; end
class FullException < Exception; end
class CantOpenException < Exception; end
class ProtocolException < Exception; end
class EmptyException < Exception; end
class SchemaChangedException < Exception; end
class TooBigException < Exception; end
class ConstraintException < Exception; end
class MismatchException < Exception; end
class MisuseException < Exception; end
class UnsupportedException < Exception; end
class AuthorizationException < Exception; end
class FormatException < Exception; end
class RangeException < Exception; end
class NotADatabaseException < Exception; end
end
sqlite3-1.4.2/lib/sqlite3/value.rb 0000644 0000041 0000041 00000002041 13611407300 016715 0 ustar www-data www-data require 'sqlite3/constants'
module SQLite3
class Value
attr_reader :handle
def initialize( db, handle )
@driver = db.driver
@handle = handle
end
def null?
type == :null
end
def to_blob
@driver.value_blob( @handle )
end
def length( utf16=false )
if utf16
@driver.value_bytes16( @handle )
else
@driver.value_bytes( @handle )
end
end
def to_f
@driver.value_double( @handle )
end
def to_i
@driver.value_int( @handle )
end
def to_int64
@driver.value_int64( @handle )
end
def to_s( utf16=false )
@driver.value_text( @handle, utf16 )
end
def type
case @driver.value_type( @handle )
when Constants::ColumnType::INTEGER then :int
when Constants::ColumnType::FLOAT then :float
when Constants::ColumnType::TEXT then :text
when Constants::ColumnType::BLOB then :blob
when Constants::ColumnType::NULL then :null
end
end
end
end
sqlite3-1.4.2/lib/sqlite3/statement.rb 0000644 0000041 0000041 00000007750 13611407300 017621 0 ustar www-data www-data require 'sqlite3/errors'
require 'sqlite3/resultset'
class String
def to_blob
SQLite3::Blob.new( self )
end
end
module SQLite3
# A statement represents a prepared-but-unexecuted SQL query. It will rarely
# (if ever) be instantiated directly by a client, and is most often obtained
# via the Database#prepare method.
class Statement
include Enumerable
# This is any text that followed the first valid SQL statement in the text
# with which the statement was initialized. If there was no trailing text,
# this will be the empty string.
attr_reader :remainder
# Binds the given variables to the corresponding placeholders in the SQL
# text.
#
# See Database#execute for a description of the valid placeholder
# syntaxes.
#
# Example:
#
# stmt = db.prepare( "select * from table where a=? and b=?" )
# stmt.bind_params( 15, "hello" )
#
# See also #execute, #bind_param, Statement#bind_param, and
# Statement#bind_params.
def bind_params( *bind_vars )
index = 1
bind_vars.flatten.each do |var|
if Hash === var
var.each { |key, val| bind_param key, val }
else
bind_param index, var
index += 1
end
end
end
# Execute the statement. This creates a new ResultSet object for the
# statement's virtual machine. If a block was given, the new ResultSet will
# be yielded to it; otherwise, the ResultSet will be returned.
#
# Any parameters will be bound to the statement using #bind_params.
#
# Example:
#
# stmt = db.prepare( "select * from table" )
# stmt.execute do |result|
# ...
# end
#
# See also #bind_params, #execute!.
def execute( *bind_vars )
reset! if active? || done?
bind_params(*bind_vars) unless bind_vars.empty?
@results = ResultSet.new(@connection, self)
step if 0 == column_count
yield @results if block_given?
@results
end
# Execute the statement. If no block was given, this returns an array of
# rows returned by executing the statement. Otherwise, each row will be
# yielded to the block.
#
# Any parameters will be bound to the statement using #bind_params.
#
# Example:
#
# stmt = db.prepare( "select * from table" )
# stmt.execute! do |row|
# ...
# end
#
# See also #bind_params, #execute.
def execute!( *bind_vars, &block )
execute(*bind_vars)
block_given? ? each(&block) : to_a
end
# Returns true if the statement is currently active, meaning it has an
# open result set.
def active?
!done?
end
# Return an array of the column names for this statement. Note that this
# may execute the statement in order to obtain the metadata; this makes it
# a (potentially) expensive operation.
def columns
get_metadata unless @columns
return @columns
end
def each
loop do
val = step
break self if done?
yield val
end
end
# Return an array of the data types for each column in this statement. Note
# that this may execute the statement in order to obtain the metadata; this
# makes it a (potentially) expensive operation.
def types
must_be_open!
get_metadata unless @types
@types
end
# Performs a sanity check to ensure that the statement is not
# closed. If it is, an exception is raised.
def must_be_open! # :nodoc:
if closed?
raise SQLite3::Exception, "cannot use a closed statement"
end
end
private
# A convenience method for obtaining the metadata about the query. Note
# that this will actually execute the SQL, which means it can be a
# (potentially) expensive operation.
def get_metadata
@columns = Array.new(column_count) do |column|
column_name column
end
@types = Array.new(column_count) do |column|
column_decltype column
end
end
end
end
sqlite3-1.4.2/lib/sqlite3/resultset.rb 0000644 0000041 0000041 00000012632 13611407300 017642 0 ustar www-data www-data require 'sqlite3/constants'
require 'sqlite3/errors'
module SQLite3
# The ResultSet object encapsulates the enumerability of a query's output.
# It is a simple cursor over the data that the query returns. It will
# very rarely (if ever) be instantiated directly. Instead, clients should
# obtain a ResultSet instance via Statement#execute.
class ResultSet
include Enumerable
class ArrayWithTypes < Array # :nodoc:
attr_accessor :types
end
class ArrayWithTypesAndFields < Array # :nodoc:
attr_writer :types
attr_writer :fields
def types
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling #{self.class}#types. This method will be removed in
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
object that created this object
eowarn
@types
end
def fields
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
object that created this object
eowarn
@fields
end
end
# The class of which we return an object in case we want a Hash as
# result.
class HashWithTypesAndFields < Hash # :nodoc:
attr_writer :types
attr_writer :fields
def types
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling #{self.class}#types. This method will be removed in
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
object that created this object
eowarn
@types
end
def fields
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
object that created this object
eowarn
@fields
end
def [] key
key = fields[key] if key.is_a? Numeric
super key
end
end
# Create a new ResultSet attached to the given database, using the
# given sql text.
def initialize db, stmt
@db = db
@stmt = stmt
end
# Reset the cursor, so that a result set which has reached end-of-file
# can be rewound and reiterated.
def reset( *bind_params )
@stmt.reset!
@stmt.bind_params( *bind_params )
@eof = false
end
# Query whether the cursor has reached the end of the result set or not.
def eof?
@stmt.done?
end
# Obtain the next row from the cursor. If there are no more rows to be
# had, this will return +nil+. If type translation is active on the
# corresponding database, the values in the row will be translated
# according to their types.
#
# The returned value will be an array, unless Database#results_as_hash has
# been set to +true+, in which case the returned value will be a hash.
#
# For arrays, the column names are accessible via the +fields+ property,
# and the column types are accessible via the +types+ property.
#
# For hashes, the column names are the keys of the hash, and the column
# types are accessible via the +types+ property.
def next
if @db.results_as_hash
return next_hash
end
row = @stmt.step
return nil if @stmt.done?
row = @db.translate_from_db @stmt.types, row
if row.respond_to?(:fields)
# FIXME: this can only happen if the translator returns something
# that responds to `fields`. Since we're removing the translator
# in 2.0, we can remove this branch in 2.0.
row = ArrayWithTypes.new(row)
else
# FIXME: the `fields` and `types` methods are deprecated on this
# object for version 2.0, so we can safely remove this branch
# as well.
row = ArrayWithTypesAndFields.new(row)
end
row.fields = @stmt.columns
row.types = @stmt.types
row
end
# Required by the Enumerable mixin. Provides an internal iterator over the
# rows of the result set.
def each
while node = self.next
yield node
end
end
# Provides an internal iterator over the rows of the result set where
# each row is yielded as a hash.
def each_hash
while node = next_hash
yield node
end
end
# Closes the statement that spawned this result set.
# Use with caution! Closing a result set will automatically
# close any other result sets that were spawned from the same statement.
def close
@stmt.close
end
# Queries whether the underlying statement has been closed or not.
def closed?
@stmt.closed?
end
# Returns the types of the columns returned by this result set.
def types
@stmt.types
end
# Returns the names of the columns returned by this result set.
def columns
@stmt.columns
end
# Return the next row as a hash
def next_hash
row = @stmt.step
return nil if @stmt.done?
# FIXME: type translation is deprecated, so this can be removed
# in 2.0
row = @db.translate_from_db @stmt.types, row
# FIXME: this can be switched to a regular hash in 2.0
row = HashWithTypesAndFields[*@stmt.columns.zip(row).flatten]
# FIXME: these methods are deprecated for version 2.0, so we can remove
# this code in 2.0
row.fields = @stmt.columns
row.types = @stmt.types
row
end
end
end
sqlite3-1.4.2/lib/sqlite3/database.rb 0000644 0000041 0000041 00000060646 13611407300 017364 0 ustar www-data www-data require 'sqlite3/constants'
require 'sqlite3/errors'
require 'sqlite3/pragmas'
require 'sqlite3/statement'
require 'sqlite3/translator'
require 'sqlite3/value'
module SQLite3
# The Database class encapsulates a single connection to a SQLite3 database.
# Its usage is very straightforward:
#
# require 'sqlite3'
#
# SQLite3::Database.new( "data.db" ) do |db|
# db.execute( "select * from table" ) do |row|
# p row
# end
# end
#
# It wraps the lower-level methods provides by the selected driver, and
# includes the Pragmas module for access to various pragma convenience
# methods.
#
# The Database class provides type translation services as well, by which
# the SQLite3 data types (which are all represented as strings) may be
# converted into their corresponding types (as defined in the schemas
# for their tables). This translation only occurs when querying data from
# the database--insertions and updates are all still typeless.
#
# Furthermore, the Database class has been designed to work well with the
# ArrayFields module from Ara Howard. If you require the ArrayFields
# module before performing a query, and if you have not enabled results as
# hashes, then the results will all be indexible by field name.
class Database
attr_reader :collations
include Pragmas
class << self
alias :open :new
# Quotes the given string, making it safe to use in an SQL statement.
# It replaces all instances of the single-quote character with two
# single-quote characters. The modified string is returned.
def quote( string )
string.gsub( /'/, "''" )
end
end
# A boolean that indicates whether rows in result sets should be returned
# as hashes or not. By default, rows are returned as arrays.
attr_accessor :results_as_hash
# call-seq: SQLite3::Database.new(file, options = {})
#
# Create a new Database object that opens the given file. If utf16
# is +true+, the filename is interpreted as a UTF-16 encoded string.
#
# By default, the new database will return result rows as arrays
# (#results_as_hash) and has type translation disabled (#type_translation=).
def initialize file, options = {}, zvfs = nil
mode = Constants::Open::READWRITE | Constants::Open::CREATE
if file.encoding == ::Encoding::UTF_16LE || file.encoding == ::Encoding::UTF_16BE || options[:utf16]
open16 file
else
# The three primary flag values for sqlite3_open_v2 are:
# SQLITE_OPEN_READONLY
# SQLITE_OPEN_READWRITE
# SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE -- always used for sqlite3_open and sqlite3_open16
mode = Constants::Open::READONLY if options[:readonly]
if options[:readwrite]
raise "conflicting options: readonly and readwrite" if options[:readonly]
mode = Constants::Open::READWRITE
end
if options[:flags]
if options[:readonly] || options[:readwrite]
raise "conflicting options: flags with readonly and/or readwrite"
end
mode = options[:flags]
end
open_v2 file.encode("utf-8"), mode, zvfs
end
@tracefunc = nil
@authorizer = nil
@encoding = nil
@busy_handler = nil
@collations = {}
@functions = {}
@results_as_hash = options[:results_as_hash]
@type_translation = options[:type_translation]
@type_translator = make_type_translator @type_translation
@readonly = mode & Constants::Open::READONLY != 0
if block_given?
begin
yield self
ensure
close
end
end
end
def type_translation= value # :nodoc:
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#type_translation=
SQLite3::Database#type_translation= is deprecated and will be removed
in version 2.0.0.
eowarn
@type_translator = make_type_translator value
@type_translation = value
end
attr_reader :type_translation # :nodoc:
# Return the type translator employed by this database instance. Each
# database instance has its own type translator; this allows for different
# type handlers to be installed in each instance without affecting other
# instances. Furthermore, the translators are instantiated lazily, so that
# if a database does not use type translation, it will not be burdened by
# the overhead of a useless type translator. (See the Translator class.)
def translator
@translator ||= Translator.new
end
# Installs (or removes) a block that will be invoked for every access
# to the database. If the block returns 0 (or +nil+), the statement
# is allowed to proceed. Returning 1 causes an authorization error to
# occur, and returning 2 causes the access to be silently denied.
def authorizer( &block )
self.authorizer = block
end
# Returns a Statement object representing the given SQL. This does not
# execute the statement; it merely prepares the statement for execution.
#
# The Statement can then be executed using Statement#execute.
#
def prepare sql
stmt = SQLite3::Statement.new( self, sql )
return stmt unless block_given?
begin
yield stmt
ensure
stmt.close unless stmt.closed?
end
end
# Returns the filename for the database named +db_name+. +db_name+ defaults
# to "main". Main return `nil` or an empty string if the database is
# temporary or in-memory.
def filename db_name = 'main'
db_filename db_name
end
# Executes the given SQL statement. If additional parameters are given,
# they are treated as bind variables, and are bound to the placeholders in
# the query.
#
# Note that if any of the values passed to this are hashes, then the
# key/value pairs are each bound separately, with the key being used as
# the name of the placeholder to bind the value to.
#
# The block is optional. If given, it will be invoked for each row returned
# by the query. Otherwise, any results are accumulated into an array and
# returned wholesale.
#
# See also #execute2, #query, and #execute_batch for additional ways of
# executing statements.
def execute sql, bind_vars = [], *args, &block
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else
bind_vars = [bind_vars] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
Support for bind parameters as *args will be removed in 2.0.0.
eowarn
end
prepare( sql ) do |stmt|
stmt.bind_params(bind_vars)
stmt = ResultSet.new self, stmt
if block_given?
stmt.each do |row|
yield row
end
else
stmt.to_a
end
end
end
# Executes the given SQL statement, exactly as with #execute. However, the
# first row returned (either via the block, or in the returned array) is
# always the names of the columns. Subsequent rows correspond to the data
# from the result set.
#
# Thus, even if the query itself returns no rows, this method will always
# return at least one row--the names of the columns.
#
# See also #execute, #query, and #execute_batch for additional ways of
# executing statements.
def execute2( sql, *bind_vars )
prepare( sql ) do |stmt|
result = stmt.execute( *bind_vars )
if block_given?
yield stmt.columns
result.each { |row| yield row }
else
return result.inject( [ stmt.columns ] ) { |arr,row|
arr << row; arr }
end
end
end
# Executes all SQL statements in the given string. By contrast, the other
# means of executing queries will only execute the first statement in the
# string, ignoring all subsequent statements. This will execute each one
# in turn. The same bind parameters, if given, will be applied to each
# statement.
#
# This always returns +nil+, making it unsuitable for queries that return
# rows.
#
# See also #execute_batch2 for additional ways of
# executing statments.
def execute_batch( sql, bind_vars = [], *args )
# FIXME: remove this stuff later
unless [Array, Hash].include?(bind_vars.class)
bind_vars = [bind_vars]
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute_batch with bind parameters
that are not a list of a hash. Please switch to passing bind parameters as an
array or hash. Support for this behavior will be removed in version 2.0.0.
eowarn
end
# FIXME: remove this stuff later
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else
bind_vars = [nil] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#execute_batch with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
Support for this behavior will be removed in version 2.0.0.
eowarn
end
sql = sql.strip
until sql.empty? do
prepare( sql ) do |stmt|
unless stmt.closed?
# FIXME: this should probably use sqlite3's api for batch execution
# This implementation requires stepping over the results.
if bind_vars.length == stmt.bind_parameter_count
stmt.bind_params(bind_vars)
end
stmt.step
end
sql = stmt.remainder.strip
end
end
# FIXME: we should not return `nil` as a success return value
nil
end
# Executes all SQL statements in the given string. By contrast, the other
# means of executing queries will only execute the first statement in the
# string, ignoring all subsequent statements. This will execute each one
# in turn. Bind parameters cannot be passed to #execute_batch2.
#
# If a query is made, all values will be returned as strings.
# If no query is made, an empty array will be returned.
#
# Because all values except for 'NULL' are returned as strings,
# a block can be passed to parse the values accordingly.
#
# See also #execute_batch for additional ways of
# executing statments.
def execute_batch2(sql, &block)
if block_given?
result = exec_batch(sql, @results_as_hash)
result.map do |val|
yield val
end
else
exec_batch(sql, @results_as_hash)
end
end
# This is a convenience method for creating a statement, binding
# paramters to it, and calling execute:
#
# result = db.query( "select * from foo where a=?", [5])
# # is the same as
# result = db.prepare( "select * from foo where a=?" ).execute( 5 )
#
# You must be sure to call +close+ on the ResultSet instance that is
# returned, or you could have problems with locks on the table. If called
# with a block, +close+ will be invoked implicitly when the block
# terminates.
def query( sql, bind_vars = [], *args )
if bind_vars.nil? || !args.empty?
if args.empty?
bind_vars = []
else
bind_vars = [bind_vars] + args
end
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling SQLite3::Database#query with nil or multiple bind params
without using an array. Please switch to passing bind parameters as an array.
Support for this will be removed in version 2.0.0.
eowarn
end
result = prepare( sql ).execute( bind_vars )
if block_given?
begin
yield result
ensure
result.close
end
else
return result
end
end
# A convenience method for obtaining the first row of a result set, and
# discarding all others. It is otherwise identical to #execute.
#
# See also #get_first_value.
def get_first_row( sql, *bind_vars )
execute( sql, *bind_vars ).first
end
# A convenience method for obtaining the first value of the first row of a
# result set, and discarding all other values and rows. It is otherwise
# identical to #execute.
#
# See also #get_first_row.
def get_first_value( sql, *bind_vars )
query( sql, bind_vars ) do |rs|
if (row = rs.next)
return @results_as_hash ? row[rs.columns[0]] : row[0]
end
end
nil
end
alias :busy_timeout :busy_timeout=
# Creates a new function for use in SQL statements. It will be added as
# +name+, with the given +arity+. (For variable arity functions, use
# -1 for the arity.)
#
# The block should accept at least one parameter--the FunctionProxy
# instance that wraps this function invocation--and any other
# arguments it needs (up to its arity).
#
# The block does not return a value directly. Instead, it will invoke
# the FunctionProxy#result= method on the +func+ parameter and
# indicate the return value that way.
#
# Example:
#
# db.create_function( "maim", 1 ) do |func, value|
# if value.nil?
# func.result = nil
# else
# func.result = value.split(//).sort.join
# end
# end
#
# puts db.get_first_value( "select maim(name) from table" )
def create_function name, arity, text_rep=Constants::TextRep::UTF8, &block
define_function_with_flags(name, text_rep) do |*args|
fp = FunctionProxy.new
block.call(fp, *args)
fp.result
end
self
end
# Creates a new aggregate function for use in SQL statements. Aggregate
# functions are functions that apply over every row in the result set,
# instead of over just a single row. (A very common aggregate function
# is the "count" function, for determining the number of rows that match
# a query.)
#
# The new function will be added as +name+, with the given +arity+. (For
# variable arity functions, use -1 for the arity.)
#
# The +step+ parameter must be a proc object that accepts as its first
# parameter a FunctionProxy instance (representing the function
# invocation), with any subsequent parameters (up to the function's arity).
# The +step+ callback will be invoked once for each row of the result set.
#
# The +finalize+ parameter must be a +proc+ object that accepts only a
# single parameter, the FunctionProxy instance representing the current
# function invocation. It should invoke FunctionProxy#result= to
# store the result of the function.
#
# Example:
#
# db.create_aggregate( "lengths", 1 ) do
# step do |func, value|
# func[ :total ] ||= 0
# func[ :total ] += ( value ? value.length : 0 )
# end
#
# finalize do |func|
# func.result = func[ :total ] || 0
# end
# end
#
# puts db.get_first_value( "select lengths(name) from table" )
#
# See also #create_aggregate_handler for a more object-oriented approach to
# aggregate functions.
def create_aggregate( name, arity, step=nil, finalize=nil,
text_rep=Constants::TextRep::ANY, &block )
proxy = Class.new do
def self.step( &block )
define_method(:step_with_ctx, &block)
end
def self.finalize( &block )
define_method(:finalize_with_ctx, &block)
end
end
if block_given?
proxy.instance_eval(&block)
else
proxy.class_eval do
define_method(:step_with_ctx, step)
define_method(:finalize_with_ctx, finalize)
end
end
proxy.class_eval do
# class instance variables
@name = name
@arity = arity
def self.name
@name
end
def self.arity
@arity
end
def initialize
@ctx = FunctionProxy.new
end
def step( *args )
step_with_ctx(@ctx, *args)
end
def finalize
finalize_with_ctx(@ctx)
@ctx.result
end
end
define_aggregator2(proxy, name)
end
# This is another approach to creating an aggregate function (see
# #create_aggregate). Instead of explicitly specifying the name,
# callbacks, arity, and type, you specify a factory object
# (the "handler") that knows how to obtain all of that information. The
# handler should respond to the following messages:
#
# +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
# message is optional, and if the handler does not respond to it,
# the function will have an arity of -1.
# +name+:: this is the name of the function. The handler _must_ implement
# this message.
# +new+:: this must be implemented by the handler. It should return a new
# instance of the object that will handle a specific invocation of
# the function.
#
# The handler instance (the object returned by the +new+ message, described
# above), must respond to the following messages:
#
# +step+:: this is the method that will be called for each step of the
# aggregate function's evaluation. It should implement the same
# signature as the +step+ callback for #create_aggregate.
# +finalize+:: this is the method that will be called to finalize the
# aggregate function's evaluation. It should implement the
# same signature as the +finalize+ callback for
# #create_aggregate.
#
# Example:
#
# class LengthsAggregateHandler
# def self.arity; 1; end
# def self.name; 'lengths'; end
#
# def initialize
# @total = 0
# end
#
# def step( ctx, name )
# @total += ( name ? name.length : 0 )
# end
#
# def finalize( ctx )
# ctx.result = @total
# end
# end
#
# db.create_aggregate_handler( LengthsAggregateHandler )
# puts db.get_first_value( "select lengths(name) from A" )
def create_aggregate_handler( handler )
# This is a compatiblity shim so the (basically pointless) FunctionProxy
# "ctx" object is passed as first argument to both step() and finalize().
# Now its up to the library user whether he prefers to store his
# temporaries as instance varibales or fields in the FunctionProxy.
# The library user still must set the result value with
# FunctionProxy.result= as there is no backwards compatible way to
# change this.
proxy = Class.new(handler) do
def initialize
super
@fp = FunctionProxy.new
end
def step( *args )
super(@fp, *args)
end
def finalize
super(@fp)
@fp.result
end
end
define_aggregator2(proxy, proxy.name)
self
end
# Define an aggregate function named +name+ using a object template
# object +aggregator+. +aggregator+ must respond to +step+ and +finalize+.
# +step+ will be called with row information and +finalize+ must return the
# return value for the aggregator function.
#
# _API Change:_ +aggregator+ must also implement +clone+. The provided
# +aggregator+ object will serve as template that is cloned to provide the
# individual instances of the aggregate function. Regular ruby objects
# already provide a suitable +clone+.
# The functions arity is the arity of the +step+ method.
def define_aggregator( name, aggregator )
# Previously, this has been implemented in C. Now this is just yet
# another compatiblity shim
proxy = Class.new do
@template = aggregator
@name = name
def self.template
@template
end
def self.name
@name
end
def self.arity
# this is what sqlite3_obj_method_arity did before
@template.method(:step).arity
end
def initialize
@klass = self.class.template.clone
end
def step(*args)
@klass.step(*args)
end
def finalize
@klass.finalize
end
end
define_aggregator2(proxy, name)
self
end
# Begins a new transaction. Note that nested transactions are not allowed
# by SQLite, so attempting to nest a transaction will result in a runtime
# exception.
#
# The +mode+ parameter may be either :deferred (the default),
# :immediate, or :exclusive.
#
# If a block is given, the database instance is yielded to it, and the
# transaction is committed when the block terminates. If the block
# raises an exception, a rollback will be performed instead. Note that if
# a block is given, #commit and #rollback should never be called
# explicitly or you'll get an error when the block terminates.
#
# If a block is not given, it is the caller's responsibility to end the
# transaction explicitly, either by calling #commit, or by calling
# #rollback.
def transaction( mode = :deferred )
execute "begin #{mode.to_s} transaction"
if block_given?
abort = false
begin
yield self
rescue
abort = true
raise
ensure
abort and rollback or commit
end
end
true
end
# Commits the current transaction. If there is no current transaction,
# this will cause an error to be raised. This returns +true+, in order
# to allow it to be used in idioms like
# abort? and rollback or commit.
def commit
execute "commit transaction"
true
end
# Rolls the current transaction back. If there is no current transaction,
# this will cause an error to be raised. This returns +true+, in order
# to allow it to be used in idioms like
# abort? and rollback or commit.
def rollback
execute "rollback transaction"
true
end
# Returns +true+ if the database has been open in readonly mode
# A helper to check before performing any operation
def readonly?
@readonly
end
# A helper class for dealing with custom functions (see #create_function,
# #create_aggregate, and #create_aggregate_handler). It encapsulates the
# opaque function object that represents the current invocation. It also
# provides more convenient access to the API functions that operate on
# the function object.
#
# This class will almost _always_ be instantiated indirectly, by working
# with the create methods mentioned above.
class FunctionProxy
attr_accessor :result
# Create a new FunctionProxy that encapsulates the given +func+ object.
# If context is non-nil, the functions context will be set to that. If
# it is non-nil, it must quack like a Hash. If it is nil, then none of
# the context functions will be available.
def initialize
@result = nil
@context = {}
end
# Set the result of the function to the given error message.
# The function will then return that error.
def set_error( error )
@driver.result_error( @func, error.to_s, -1 )
end
# (Only available to aggregate functions.) Returns the number of rows
# that the aggregate has processed so far. This will include the current
# row, and so will always return at least 1.
def count
@driver.aggregate_count( @func )
end
# Returns the value with the given key from the context. This is only
# available to aggregate functions.
def []( key )
@context[ key ]
end
# Sets the value with the given key in the context. This is only
# available to aggregate functions.
def []=( key, value )
@context[ key ] = value
end
end
# Translates a +row+ of data from the database with the given +types+
def translate_from_db types, row
@type_translator.call types, row
end
private
NULL_TRANSLATOR = lambda { |_, row| row }
def make_type_translator should_translate
if should_translate
lambda { |types, row|
types.zip(row).map do |type, value|
translator.translate( type, value )
end
}
else
NULL_TRANSLATOR
end
end
end
end
sqlite3-1.4.2/lib/sqlite3/constants.rb 0000644 0000041 0000041 00000003353 13611407300 017624 0 ustar www-data www-data module SQLite3 ; module Constants
module TextRep
UTF8 = 1
UTF16LE = 2
UTF16BE = 3
UTF16 = 4
ANY = 5
DETERMINISTIC = 0x800
end
module ColumnType
INTEGER = 1
FLOAT = 2
TEXT = 3
BLOB = 4
NULL = 5
end
module ErrorCode
OK = 0 # Successful result
ERROR = 1 # SQL error or missing database
INTERNAL = 2 # An internal logic error in SQLite
PERM = 3 # Access permission denied
ABORT = 4 # Callback routine requested an abort
BUSY = 5 # The database file is locked
LOCKED = 6 # A table in the database is locked
NOMEM = 7 # A malloc() failed
READONLY = 8 # Attempt to write a readonly database
INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
IOERR = 10 # Some kind of disk I/O error occurred
CORRUPT = 11 # The database disk image is malformed
NOTFOUND = 12 # (Internal Only) Table or record not found
FULL = 13 # Insertion failed because database is full
CANTOPEN = 14 # Unable to open the database file
PROTOCOL = 15 # Database lock protocol error
EMPTY = 16 # (Internal Only) Database table is empty
SCHEMA = 17 # The database schema changed
TOOBIG = 18 # Too much data for one row of a table
CONSTRAINT = 19 # Abort due to contraint violation
MISMATCH = 20 # Data type mismatch
MISUSE = 21 # Library used incorrectly
NOLFS = 22 # Uses OS features not supported on host
AUTH = 23 # Authorization denied
ROW = 100 # sqlite_step() has another row ready
DONE = 101 # sqlite_step() has finished executing
end
end ; end
sqlite3-1.4.2/lib/sqlite3/pragmas.rb 0000644 0000041 0000041 00000034147 13611407300 017247 0 ustar www-data www-data require 'sqlite3/errors'
module SQLite3
# This module is intended for inclusion solely by the Database class. It
# defines convenience methods for the various pragmas supported by SQLite3.
#
# For a detailed description of these pragmas, see the SQLite3 documentation
# at http://sqlite.org/pragma.html.
module Pragmas
# Returns +true+ or +false+ depending on the value of the named pragma.
def get_boolean_pragma( name )
get_first_value( "PRAGMA #{name}" ) != "0"
end
# Sets the given pragma to the given boolean value. The value itself
# may be +true+ or +false+, or any other commonly used string or
# integer that represents truth.
def set_boolean_pragma( name, mode )
case mode
when String
case mode.downcase
when "on", "yes", "true", "y", "t"; mode = "'ON'"
when "off", "no", "false", "n", "f"; mode = "'OFF'"
else
raise Exception,
"unrecognized pragma parameter #{mode.inspect}"
end
when true, 1
mode = "ON"
when false, 0, nil
mode = "OFF"
else
raise Exception,
"unrecognized pragma parameter #{mode.inspect}"
end
execute( "PRAGMA #{name}=#{mode}" )
end
# Requests the given pragma (and parameters), and if the block is given,
# each row of the result set will be yielded to it. Otherwise, the results
# are returned as an array.
def get_query_pragma( name, *parms, &block ) # :yields: row
if parms.empty?
execute( "PRAGMA #{name}", &block )
else
args = "'" + parms.join("','") + "'"
execute( "PRAGMA #{name}( #{args} )", &block )
end
end
# Return the value of the given pragma.
def get_enum_pragma( name )
get_first_value( "PRAGMA #{name}" )
end
# Set the value of the given pragma to +mode+. The +mode+ parameter must
# conform to one of the values in the given +enum+ array. Each entry in
# the array is another array comprised of elements in the enumeration that
# have duplicate values. See #synchronous, #default_synchronous,
# #temp_store, and #default_temp_store for usage examples.
def set_enum_pragma( name, mode, enums )
match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } }
raise Exception,
"unrecognized #{name} #{mode.inspect}" unless match
execute( "PRAGMA #{name}='#{match.first.upcase}'" )
end
# Returns the value of the given pragma as an integer.
def get_int_pragma( name )
get_first_value( "PRAGMA #{name}" ).to_i
end
# Set the value of the given pragma to the integer value of the +value+
# parameter.
def set_int_pragma( name, value )
execute( "PRAGMA #{name}=#{value.to_i}" )
end
# The enumeration of valid synchronous modes.
SYNCHRONOUS_MODES = [ [ 'full', 2 ], [ 'normal', 1 ], [ 'off', 0 ] ]
# The enumeration of valid temp store modes.
TEMP_STORE_MODES = [ [ 'default', 0 ], [ 'file', 1 ], [ 'memory', 2 ] ]
# The enumeration of valid auto vacuum modes.
AUTO_VACUUM_MODES = [ [ 'none', 0 ], [ 'full', 1 ], [ 'incremental', 2 ] ]
# The list of valid journaling modes.
JOURNAL_MODES = [ [ 'delete' ], [ 'truncate' ], [ 'persist' ], [ 'memory' ],
[ 'wal' ], [ 'off' ] ]
# The list of valid locking modes.
LOCKING_MODES = [ [ 'normal' ], [ 'exclusive' ] ]
# The list of valid encodings.
ENCODINGS = [ [ 'utf-8' ], [ 'utf-16' ], [ 'utf-16le' ], [ 'utf-16be ' ] ]
# The list of valid WAL checkpoints.
WAL_CHECKPOINTS = [ [ 'passive' ], [ 'full' ], [ 'restart' ], [ 'truncate' ] ]
def application_id
get_int_pragma "application_id"
end
def application_id=( integer )
set_int_pragma "application_id", integer
end
def auto_vacuum
get_enum_pragma "auto_vacuum"
end
def auto_vacuum=( mode )
set_enum_pragma "auto_vacuum", mode, AUTO_VACUUM_MODES
end
def automatic_index
get_boolean_pragma "automatic_index"
end
def automatic_index=( mode )
set_boolean_pragma "automatic_index", mode
end
def busy_timeout
get_int_pragma "busy_timeout"
end
def busy_timeout=( milliseconds )
set_int_pragma "busy_timeout", milliseconds
end
def cache_size
get_int_pragma "cache_size"
end
def cache_size=( size )
set_int_pragma "cache_size", size
end
def cache_spill
get_boolean_pragma "cache_spill"
end
def cache_spill=( mode )
set_boolean_pragma "cache_spill", mode
end
def case_sensitive_like=( mode )
set_boolean_pragma "case_sensitive_like", mode
end
def cell_size_check
get_boolean_pragma "cell_size_check"
end
def cell_size_check=( mode )
set_boolean_pragma "cell_size_check", mode
end
def checkpoint_fullfsync
get_boolean_pragma "checkpoint_fullfsync"
end
def checkpoint_fullfsync=( mode )
set_boolean_pragma "checkpoint_fullfsync", mode
end
def collation_list( &block ) # :yields: row
get_query_pragma "collation_list", &block
end
def compile_options( &block ) # :yields: row
get_query_pragma "compile_options", &block
end
def count_changes
get_boolean_pragma "count_changes"
end
def count_changes=( mode )
set_boolean_pragma "count_changes", mode
end
def data_version
get_int_pragma "data_version"
end
def database_list( &block ) # :yields: row
get_query_pragma "database_list", &block
end
def default_cache_size
get_int_pragma "default_cache_size"
end
def default_cache_size=( size )
set_int_pragma "default_cache_size", size
end
def default_synchronous
get_enum_pragma "default_synchronous"
end
def default_synchronous=( mode )
set_enum_pragma "default_synchronous", mode, SYNCHRONOUS_MODES
end
def default_temp_store
get_enum_pragma "default_temp_store"
end
def default_temp_store=( mode )
set_enum_pragma "default_temp_store", mode, TEMP_STORE_MODES
end
def defer_foreign_keys
get_boolean_pragma "defer_foreign_keys"
end
def defer_foreign_keys=( mode )
set_boolean_pragma "defer_foreign_keys", mode
end
def encoding
get_enum_pragma "encoding"
end
def encoding=( mode )
set_enum_pragma "encoding", mode, ENCODINGS
end
def foreign_key_check( *table, &block ) # :yields: row
get_query_pragma "foreign_key_check", *table, &block
end
def foreign_key_list( table, &block ) # :yields: row
get_query_pragma "foreign_key_list", table, &block
end
def foreign_keys
get_boolean_pragma "foreign_keys"
end
def foreign_keys=( mode )
set_boolean_pragma "foreign_keys", mode
end
def freelist_count
get_int_pragma "freelist_count"
end
def full_column_names
get_boolean_pragma "full_column_names"
end
def full_column_names=( mode )
set_boolean_pragma "full_column_names", mode
end
def fullfsync
get_boolean_pragma "fullfsync"
end
def fullfsync=( mode )
set_boolean_pragma "fullfsync", mode
end
def ignore_check_constraints=( mode )
set_boolean_pragma "ignore_check_constraints", mode
end
def incremental_vacuum( pages, &block ) # :yields: row
get_query_pragma "incremental_vacuum", pages, &block
end
def index_info( index, &block ) # :yields: row
get_query_pragma "index_info", index, &block
end
def index_list( table, &block ) # :yields: row
get_query_pragma "index_list", table, &block
end
def index_xinfo( index, &block ) # :yields: row
get_query_pragma "index_xinfo", index, &block
end
def integrity_check( *num_errors, &block ) # :yields: row
get_query_pragma "integrity_check", *num_errors, &block
end
def journal_mode
get_enum_pragma "journal_mode"
end
def journal_mode=( mode )
set_enum_pragma "journal_mode", mode, JOURNAL_MODES
end
def journal_size_limit
get_int_pragma "journal_size_limit"
end
def journal_size_limit=( size )
set_int_pragma "journal_size_limit", size
end
def legacy_file_format
get_boolean_pragma "legacy_file_format"
end
def legacy_file_format=( mode )
set_boolean_pragma "legacy_file_format", mode
end
def locking_mode
get_enum_pragma "locking_mode"
end
def locking_mode=( mode )
set_enum_pragma "locking_mode", mode, LOCKING_MODES
end
def max_page_count
get_int_pragma "max_page_count"
end
def max_page_count=( size )
set_int_pragma "max_page_count", size
end
def mmap_size
get_int_pragma "mmap_size"
end
def mmap_size=( size )
set_int_pragma "mmap_size", size
end
def page_count
get_int_pragma "page_count"
end
def page_size
get_int_pragma "page_size"
end
def page_size=( size )
set_int_pragma "page_size", size
end
def parser_trace=( mode )
set_boolean_pragma "parser_trace", mode
end
def query_only
get_boolean_pragma "query_only"
end
def query_only=( mode )
set_boolean_pragma "query_only", mode
end
def quick_check( *num_errors, &block ) # :yields: row
get_query_pragma "quick_check", *num_errors, &block
end
def read_uncommitted
get_boolean_pragma "read_uncommitted"
end
def read_uncommitted=( mode )
set_boolean_pragma "read_uncommitted", mode
end
def recursive_triggers
get_boolean_pragma "recursive_triggers"
end
def recursive_triggers=( mode )
set_boolean_pragma "recursive_triggers", mode
end
def reverse_unordered_selects
get_boolean_pragma "reverse_unordered_selects"
end
def reverse_unordered_selects=( mode )
set_boolean_pragma "reverse_unordered_selects", mode
end
def schema_cookie
get_int_pragma "schema_cookie"
end
def schema_cookie=( cookie )
set_int_pragma "schema_cookie", cookie
end
def schema_version
get_int_pragma "schema_version"
end
def schema_version=( version )
set_int_pragma "schema_version", version
end
def secure_delete
get_boolean_pragma "secure_delete"
end
def secure_delete=( mode )
set_boolean_pragma "secure_delete", mode
end
def short_column_names
get_boolean_pragma "short_column_names"
end
def short_column_names=( mode )
set_boolean_pragma "short_column_names", mode
end
def shrink_memory
execute( "PRAGMA shrink_memory" )
end
def soft_heap_limit
get_int_pragma "soft_heap_limit"
end
def soft_heap_limit=( mode )
set_int_pragma "soft_heap_limit", mode
end
def stats( &block ) # :yields: row
get_query_pragma "stats", &block
end
def synchronous
get_enum_pragma "synchronous"
end
def synchronous=( mode )
set_enum_pragma "synchronous", mode, SYNCHRONOUS_MODES
end
def temp_store
get_enum_pragma "temp_store"
end
def temp_store=( mode )
set_enum_pragma "temp_store", mode, TEMP_STORE_MODES
end
def threads
get_int_pragma "threads"
end
def threads=( count )
set_int_pragma "threads", count
end
def user_cookie
get_int_pragma "user_cookie"
end
def user_cookie=( cookie )
set_int_pragma "user_cookie", cookie
end
def user_version
get_int_pragma "user_version"
end
def user_version=( version )
set_int_pragma "user_version", version
end
def vdbe_addoptrace=( mode )
set_boolean_pragma "vdbe_addoptrace", mode
end
def vdbe_debug=( mode )
set_boolean_pragma "vdbe_debug", mode
end
def vdbe_listing=( mode )
set_boolean_pragma "vdbe_listing", mode
end
def vdbe_trace
get_boolean_pragma "vdbe_trace"
end
def vdbe_trace=( mode )
set_boolean_pragma "vdbe_trace", mode
end
def wal_autocheckpoint
get_int_pragma "wal_autocheckpoint"
end
def wal_autocheckpoint=( mode )
set_int_pragma "wal_autocheckpoint", mode
end
def wal_checkpoint
get_enum_pragma "wal_checkpoint"
end
def wal_checkpoint=( mode )
set_enum_pragma "wal_checkpoint", mode, WAL_CHECKPOINTS
end
def writable_schema=( mode )
set_boolean_pragma "writable_schema", mode
end
###
# Returns information about +table+. Yields each row of table information
# if a block is provided.
def table_info table
stmt = prepare "PRAGMA table_info(#{table})"
columns = stmt.columns
needs_tweak_default =
version_compare(SQLite3.libversion.to_s, "3.3.7") > 0
result = [] unless block_given?
stmt.each do |row|
new_row = Hash[columns.zip(row)]
# FIXME: This should be removed but is required for older versions
# of rails
if(Object.const_defined?(:ActiveRecord))
new_row['notnull'] = new_row['notnull'].to_s
end
tweak_default(new_row) if needs_tweak_default
if block_given?
yield new_row
else
result << new_row
end
end
stmt.close
result
end
private
# Compares two version strings
def version_compare(v1, v2)
v1 = v1.split(".").map { |i| i.to_i }
v2 = v2.split(".").map { |i| i.to_i }
parts = [v1.length, v2.length].max
v1.push 0 while v1.length < parts
v2.push 0 while v2.length < parts
v1.zip(v2).each do |a,b|
return -1 if a < b
return 1 if a > b
end
return 0
end
# Since SQLite 3.3.8, the table_info pragma has returned the default
# value of the row as a quoted SQL value. This method essentially
# unquotes those values.
def tweak_default(hash)
case hash["dflt_value"]
when /^null$/i
hash["dflt_value"] = nil
when /^'(.*)'$/m
hash["dflt_value"] = $1.gsub(/''/, "'")
when /^"(.*)"$/m
hash["dflt_value"] = $1.gsub(/""/, '"')
end
end
end
end
sqlite3-1.4.2/lib/sqlite3/translator.rb 0000644 0000041 0000041 00000010022 13611407300 017770 0 ustar www-data www-data require 'time'
require 'date'
module SQLite3
# The Translator class encapsulates the logic and callbacks necessary for
# converting string data to a value of some specified type. Every Database
# instance may have a Translator instance, in order to assist in type
# translation (Database#type_translation).
#
# Further, applications may define their own custom type translation logic
# by registering translator blocks with the corresponding database's
# translator instance (Database#translator).
class Translator
# Create a new Translator instance. It will be preinitialized with default
# translators for most SQL data types.
def initialize
@translators = Hash.new( proc { |type,value| value } )
@type_name_cache = {}
register_default_translators
end
# Add a new translator block, which will be invoked to process type
# translations to the given type. The type should be an SQL datatype, and
# may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
# information is stripped off and discarded, so type translation decisions
# are made solely on the "base" type name.
#
# The translator block itself should accept two parameters, "type" and
# "value". In this case, the "type" is the full type name (including
# parentheses), so the block itself may include logic for changing how a
# type is translated based on the additional data. The "value" parameter
# is the (string) data to convert.
#
# The block should return the translated value.
def add_translator( type, &block ) # :yields: type, value
warn(<<-eowarn) if $VERBOSE
#{caller[0]} is calling `add_translator`.
Built in translators are deprecated and will be removed in version 2.0.0
eowarn
@translators[ type_name( type ) ] = block
end
# Translate the given string value to a value of the given type. In the
# absense of an installed translator block for the given type, the value
# itself is always returned. Further, +nil+ values are never translated,
# and are always passed straight through regardless of the type parameter.
def translate( type, value )
unless value.nil?
# FIXME: this is a hack to support Sequel
if type && %w{ datetime timestamp }.include?(type.downcase)
@translators[ type_name( type ) ].call( type, value.to_s )
else
@translators[ type_name( type ) ].call( type, value )
end
end
end
# A convenience method for working with type names. This returns the "base"
# type name, without any parenthetical data.
def type_name( type )
@type_name_cache[type] ||= begin
type = "" if type.nil?
type = $1 if type =~ /^(.*?)\(/
type.upcase
end
end
private :type_name
# Register the default translators for the current Translator instance.
# This includes translators for most major SQL data types.
def register_default_translators
[ "time",
"timestamp" ].each { |type| add_translator( type ) { |t, v| Time.parse( v ) } }
add_translator( "date" ) { |t,v| Date.parse(v) }
add_translator( "datetime" ) { |t,v| DateTime.parse(v) }
[ "decimal",
"float",
"numeric",
"double",
"real",
"dec",
"fixed" ].each { |type| add_translator( type ) { |t,v| v.to_f } }
[ "integer",
"smallint",
"mediumint",
"int",
"bigint" ].each { |type| add_translator( type ) { |t,v| v.to_i } }
[ "bit",
"bool",
"boolean" ].each do |type|
add_translator( type ) do |t,v|
!( v.strip.gsub(/00+/,"0") == "0" ||
v.downcase == "false" ||
v.downcase == "f" ||
v.downcase == "no" ||
v.downcase == "n" )
end
end
add_translator( "tinyint" ) do |type, value|
if type =~ /\(\s*1\s*\)/
value.to_i == 1
else
value.to_i
end
end
end
private :register_default_translators
end
end
sqlite3-1.4.2/lib/sqlite3.rb 0000644 0000041 0000041 00000000551 13611407300 015605 0 ustar www-data www-data # support multiple ruby version (fat binaries under windows)
begin
RUBY_VERSION =~ /(\d+\.\d+)/
require "sqlite3/#{$1}/sqlite3_native"
rescue LoadError
require 'sqlite3/sqlite3_native'
end
require 'sqlite3/database'
require 'sqlite3/version'
module SQLite3
# Was sqlite3 compiled with thread safety on?
def self.threadsafe?; threadsafe > 0; end
end
sqlite3-1.4.2/README.rdoc 0000644 0000041 0000041 00000006174 13611407300 014743 0 ustar www-data www-data = SQLite3/Ruby Interface
* https://github.com/sparklemotion/sqlite3-ruby
* http://groups.google.com/group/sqlite3-ruby
* http://rubygems.org/gems/sqlite3
* http://www.rubydoc.info/gems/sqlite3/frames
{
}[https://travis-ci.org/sparklemotion/sqlite3-ruby]
== DESCRIPTION
This module allows Ruby programs to interface with the SQLite3
database engine (http://www.sqlite.org). You must have the
SQLite engine installed in order to build this module.
Note that this module is only compatible with SQLite 3.6.16 or newer.
== SYNOPSIS
require "sqlite3"
# Open a database
db = SQLite3::Database.new "test.db"
# Create a table
rows = db.execute <<-SQL
create table numbers (
name varchar(30),
val int
);
SQL
# Execute a few inserts
{
"one" => 1,
"two" => 2,
}.each do |pair|
db.execute "insert into numbers values ( ?, ? )", pair
end
# Find a few rows
db.execute( "select * from numbers" ) do |row|
p row
end
# Create another table with multiple columns
db.execute <<-SQL
create table students (
name varchar(50),
email varchar(50),
grade varchar(5),
blog varchar(50)
);
SQL
# Execute inserts with parameter markers
db.execute("INSERT INTO students (name, email, grade, blog)
VALUES (?, ?, ?, ?)", ["Jane", "me@janedoe.com", "A", "http://blog.janedoe.com"])
db.execute( "select * from students" ) do |row|
p row
end
== Compilation and Installation
Install SQLite3, enabling the option SQLITE_ENABLE_COLUMN_METADATA (see
www.sqlite.org/compile.html for details).
Then do the following:
ruby setup.rb config
ruby setup.rb setup
ruby setup.rb install
Alternatively, you can download and install the RubyGem package for
SQLite3/Ruby (you must have RubyGems and SQLite3 installed, first):
gem install sqlite3
If you have sqlite3 installed in a non-standard location, you can specify the location of the include and lib files by doing:
gem install sqlite3 -- --with-sqlite3-include=/opt/local/include \
--with-sqlite3-lib=/opt/local/lib
= SUPPORT!!!
== OMG! Something has gone wrong! Where do I get help?
The best place to get help is from the
{sqlite3-ruby mailing list}[http://groups.google.com/group/sqlite3-ruby] which
can be found here:
* http://groups.google.com/group/sqlite3-ruby
== I've found a bug! Where do I file it?
Uh oh. After contacting the mailing list, you've found that you've actually
discovered a bug. You can file the bug at the
{github issues page}[https://github.com/sparklemotion/sqlite3-ruby/issues]
which can be found here:
* https://github.com/sparklemotion/sqlite3-ruby/issues
== Usage
For help figuring out the SQLite3/Ruby interface, check out the
SYNOPSIS as well as the RDoc. It includes examples of
usage. If you have any questions that you feel should be addressed in the
FAQ, please send them to {the mailing list}[http://groups.google.com/group/sqlite3-ruby]
== Source Code
The source repository is accessible via git:
git clone git://github.com/sparklemotion/sqlite3-ruby.git
sqlite3-1.4.2/ChangeLog.cvs 0000644 0000041 0000041 00000005742 13611407300 015501 0 ustar www-data www-data 2005-01-05 09:40 minam
* Rakefile, sqlite3-ruby-win32.gemspec, sqlite3-ruby.gemspec: Added
win32 gem.
2005-01-05 07:31 minam
* Rakefile, test/tc_integration.rb, test/tests.rb: Added
native-vs-dl benchmark to Rakefile. Added SQLITE3_DRIVERS
environment variable to integration test to specify which
driver(s) should be tested (defaults to "Native").
2005-01-04 14:26 minam
* ext/sqlite3_api/sqlite3_api.i, lib/sqlite3/database.rb,
lib/sqlite3/driver/native/driver.rb, test/tc_database.rb,
test/tc_integration.rb, test/tests.rb: Unit tests: done. Bugs:
fixed.
2005-01-03 23:13 minam
* ext/sqlite3_api/sqlite3_api.i, lib/sqlite3/database.rb,
lib/sqlite3/driver/dl/driver.rb,
lib/sqlite3/driver/native/driver.rb, test/tc_integration.rb:
Custom functions (aggregate and otherwise) are supported by the
native driver now. Test cases for the same.
2005-01-03 13:51 minam
* ext/sqlite3_api/MANIFEST, ext/sqlite3_api/extconf.rb,
ext/sqlite3_api/post-clean.rb, ext/sqlite3_api/post-distclean.rb,
ext/sqlite3_api/sqlite3_api.i, lib/sqlite3/database.rb,
lib/sqlite3/resultset.rb, lib/sqlite3/version.rb,
lib/sqlite3/driver/dl/driver.rb,
lib/sqlite3/driver/native/driver.rb, test/native-vs-dl.rb,
test/tc_integration.rb: Added preliminary implementation of
native driver (swig-based), and integration tests.
2004-12-29 19:37 minam
* lib/sqlite3/driver/dl/driver.rb: Some fixes to allow the DL
driver to work with Ruby 1.8.1.
2004-12-29 14:52 minam
* lib/sqlite3/: database.rb, version.rb: Made #quote a class method
(again). Bumped version to 0.6.
2004-12-25 22:59 minam
* lib/sqlite3/driver/dl/api.rb: Added check for darwin in supported
platforms (thanks to bitsweat).
2004-12-22 12:38 minam
* Rakefile: Rakefile wasn't packaging the README file.
2004-12-21 22:28 minam
* Rakefile, sqlite3-ruby.gemspec, test/bm.rb: Packaging now works.
Added benchmarks.
2004-12-21 21:45 minam
* LICENSE, README, Rakefile, setup.rb, sqlite3-ruby.gemspec,
doc/faq/faq.rb, doc/faq/faq.yml, lib/sqlite3.rb,
lib/sqlite3/statement.rb, lib/sqlite3/constants.rb,
lib/sqlite3/database.rb, lib/sqlite3/resultset.rb,
lib/sqlite3/translator.rb, lib/sqlite3/value.rb,
lib/sqlite3/version.rb, lib/sqlite3/errors.rb,
lib/sqlite3/pragmas.rb, lib/sqlite3/driver/dl/api.rb,
lib/sqlite3/driver/dl/driver.rb, test/mocks.rb,
test/tc_database.rb, test/tests.rb, test/driver/dl/tc_driver.rb:
Initial import
2004-12-21 21:45 minam
* LICENSE, README, Rakefile, setup.rb, sqlite3-ruby.gemspec,
doc/faq/faq.rb, doc/faq/faq.yml, lib/sqlite3.rb,
lib/sqlite3/statement.rb, lib/sqlite3/constants.rb,
lib/sqlite3/database.rb, lib/sqlite3/resultset.rb,
lib/sqlite3/translator.rb, lib/sqlite3/value.rb,
lib/sqlite3/version.rb, lib/sqlite3/errors.rb,
lib/sqlite3/pragmas.rb, lib/sqlite3/driver/dl/api.rb,
lib/sqlite3/driver/dl/driver.rb, test/mocks.rb,
test/tc_database.rb, test/tests.rb, test/driver/dl/tc_driver.rb:
Initial revision
sqlite3-1.4.2/Gemfile 0000644 0000041 0000041 00000001173 13611407300 014422 0 ustar www-data www-data # -*- ruby -*-
# DO NOT EDIT THIS FILE. Instead, edit Rakefile, and run `rake bundler:gemfile`.
source "https://rubygems.org/"
gem "minitest", "~>5.11", :group => [:development, :test]
gem "rake-compiler", "~>1.0", :group => [:development, :test]
gem "rake-compiler-dock", "~>0.6.0", :group => [:development, :test]
gem "mini_portile", "~>0.6.2", :group => [:development, :test]
gem "hoe-bundler", "~>1.0", :group => [:development, :test]
gem "hoe-gemspec", "~>1.0", :group => [:development, :test]
gem "rdoc", ">=4.0", "<6", :group => [:development, :test]
gem "hoe", "~>3.17", :group => [:development, :test]
# vim: syntax=ruby
sqlite3-1.4.2/setup.rb 0000644 0000041 0000041 00000067411 13611407300 014623 0 ustar www-data www-data #
# setup.rb
#
# Copyright (c) 2000-2004 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
#
#
# For backward compatibility
#
unless Enumerable.method_defined?(:map)
module Enumerable
alias map collect
end
end
unless Enumerable.method_defined?(:detect)
module Enumerable
alias detect find
end
end
unless Enumerable.method_defined?(:select)
module Enumerable
alias select find_all
end
end
unless Enumerable.method_defined?(:reject)
module Enumerable
def reject
select {|i| not yield(i) }
end
end
end
unless Enumerable.method_defined?(:inject)
module Enumerable
def inject(result)
each do |i|
result = yield(result, i)
end
result
end
end
end
unless Enumerable.method_defined?(:any?)
module Enumerable
def any?
each do |i|
return true if yield(i)
end
false
end
end
end
unless File.respond_to?(:read)
def File.read(fname)
open(fname) {|f|
return f.read
}
end
end
#
# Application independent utilities
#
def File.binread(fname)
open(fname, 'rb') {|f|
return f.read
}
end
# for corrupted windows stat(2)
def File.dir?(path)
File.directory?((path[-1,1] == '/') ? path : path + '/')
end
#
# Config
#
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
require arg.split(/=/, 2)[1]
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
def multipackage_install?
FileTest.directory?(File.dirname($0) + '/packages')
end
class ConfigTable
c = ::RbConfig::CONFIG
rubypath = c['bindir'] + '/' + c['ruby_install_name']
major = c['MAJOR'].to_i
minor = c['MINOR'].to_i
teeny = c['TEENY'].to_i
version = "#{major}.#{minor}"
# ruby ver. >= 1.4.4?
newpath_p = ((major >= 2) or
((major == 1) and
((minor >= 5) or
((minor == 4) and (teeny >= 4)))))
subprefix = lambda {|path|
path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
}
if c['rubylibdir']
# V < 1.6.3
stdruby = subprefix.call(c['rubylibdir'])
siteruby = subprefix.call(c['sitedir'])
versite = subprefix.call(c['sitelibdir'])
sodir = subprefix.call(c['sitearchdir'])
elsif newpath_p
# 1.4.4 <= V <= 1.6.3
stdruby = "$prefix/lib/ruby/#{version}"
siteruby = subprefix.call(c['sitedir'])
versite = siteruby + '/' + version
sodir = "$site-ruby/#{c['arch']}"
else
# V < 1.4.4
stdruby = "$prefix/lib/ruby/#{version}"
siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
versite = siteruby
sodir = "$site-ruby/#{c['arch']}"
end
if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
else
makeprog = 'make'
end
common_descripters = [
[ 'prefix', [ c['prefix'],
'path',
'path prefix of target environment' ] ],
[ 'std-ruby', [ stdruby,
'path',
'the directory for standard ruby libraries' ] ],
[ 'site-ruby-common', [ siteruby,
'path',
'the directory for version-independent non-standard ruby libraries' ] ],
[ 'site-ruby', [ versite,
'path',
'the directory for non-standard ruby libraries' ] ],
[ 'bin-dir', [ '$prefix/bin',
'path',
'the directory for commands' ] ],
[ 'rb-dir', [ '$site-ruby',
'path',
'the directory for ruby scripts' ] ],
[ 'so-dir', [ sodir,
'path',
'the directory for ruby extentions' ] ],
[ 'data-dir', [ '$prefix/share',
'path',
'the directory for shared data' ] ],
[ 'ruby-path', [ rubypath,
'path',
'path to set to #! line' ] ],
[ 'ruby-prog', [ rubypath,
'name',
'the ruby program using for installation' ] ],
[ 'make-prog', [ makeprog,
'name',
'the make program to compile ruby extentions' ] ],
[ 'without-ext', [ 'no',
'yes/no',
'does not compile/install ruby extentions' ] ]
]
multipackage_descripters = [
[ 'with', [ '',
'name,name...',
'package names that you want to install',
'ALL' ] ],
[ 'without', [ '',
'name,name...',
'package names that you do not want to install',
'NONE' ] ]
]
if multipackage_install?
DESCRIPTER = common_descripters + multipackage_descripters
else
DESCRIPTER = common_descripters
end
SAVE_FILE = '.config'
def ConfigTable.each_name(&block)
keys().each(&block)
end
def ConfigTable.keys
DESCRIPTER.map {|name, *dummy| name }
end
def ConfigTable.each_definition(&block)
DESCRIPTER.each(&block)
end
def ConfigTable.get_entry(name)
name, ent = DESCRIPTER.assoc(name)
ent
end
def ConfigTable.get_entry!(name)
get_entry(name) or raise ArgumentError, "no such config: #{name}"
end
def ConfigTable.add_entry(name, vals)
ConfigTable::DESCRIPTER.push [name,vals]
end
def ConfigTable.remove_entry(name)
get_entry(name) or raise ArgumentError, "no such config: #{name}"
DESCRIPTER.delete_if {|n, arr| n == name }
end
def ConfigTable.config_key?(name)
get_entry(name) ? true : false
end
def ConfigTable.bool_config?(name)
ent = get_entry(name) or return false
ent[1] == 'yes/no'
end
def ConfigTable.value_config?(name)
ent = get_entry(name) or return false
ent[1] != 'yes/no'
end
def ConfigTable.path_config?(name)
ent = get_entry(name) or return false
ent[1] == 'path'
end
class << self
alias newobj new
end
def ConfigTable.new
c = newobj()
c.initialize_from_table
c
end
def ConfigTable.load
c = newobj()
c.initialize_from_file
c
end
def initialize_from_table
@table = {}
DESCRIPTER.each do |k, (default, vname, desc, default2)|
@table[k] = default
end
end
def initialize_from_file
raise InstallError, "#{File.basename $0} config first"\
unless File.file?(SAVE_FILE)
@table = {}
File.foreach(SAVE_FILE) do |line|
k, v = line.split(/=/, 2)
@table[k] = v.strip
end
end
def save
File.open(SAVE_FILE, 'w') {|f|
@table.each do |k, v|
f.printf "%s=%s\n", k, v if v
end
}
end
def []=(k, v)
raise InstallError, "unknown config option #{k}"\
unless ConfigTable.config_key?(k)
@table[k] = v
end
def [](key)
return nil unless @table[key]
@table[key].gsub(%r<\$([^/]+)>) { self[$1] }
end
def set_raw(key, val)
@table[key] = val
end
def get_raw(key)
@table[key]
end
end
module MetaConfigAPI
def eval_file_ifexist(fname)
instance_eval File.read(fname), fname, 1 if File.file?(fname)
end
def config_names
ConfigTable.keys
end
def config?(name)
ConfigTable.config_key?(name)
end
def bool_config?(name)
ConfigTable.bool_config?(name)
end
def value_config?(name)
ConfigTable.value_config?(name)
end
def path_config?(name)
ConfigTable.path_config?(name)
end
def add_config(name, argname, default, desc)
ConfigTable.add_entry name,[default,argname,desc]
end
def add_path_config(name, default, desc)
add_config name, 'path', default, desc
end
def add_bool_config(name, default, desc)
add_config name, 'yes/no', default ? 'yes' : 'no', desc
end
def set_config_default(name, default)
if bool_config?(name)
ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
else
ConfigTable.get_entry!(name)[0] = default
end
end
def remove_config(name)
ent = ConfigTable.get_entry(name)
ConfigTable.remove_entry name
ent
end
end
#
# File Operations
#
module FileOperations
def mkdir_p(dirname, prefix = nil)
dirname = prefix + dirname if prefix
$stderr.puts "mkdir -p #{dirname}" if verbose?
return if no_harm?
# does not check '/'... it's too abnormal case
dirs = dirname.split(%r<(?=/)>)
if /\A[a-z]:\z/i =~ dirs[0]
disk = dirs.shift
dirs[0] = disk + dirs[0]
end
dirs.each_index do |idx|
path = dirs[0..idx].join('')
Dir.mkdir path unless File.dir?(path)
end
end
def rm_f(fname)
$stderr.puts "rm -f #{fname}" if verbose?
return if no_harm?
if File.exist?(fname) or File.symlink?(fname)
File.chmod 0777, fname
File.unlink fname
end
end
def rm_rf(dn)
$stderr.puts "rm -rf #{dn}" if verbose?
return if no_harm?
Dir.chdir dn
Dir.foreach('.') do |fn|
next if fn == '.'
next if fn == '..'
if File.dir?(fn)
verbose_off {
rm_rf fn
}
else
verbose_off {
rm_f fn
}
end
end
Dir.chdir '..'
Dir.rmdir dn
end
def move_file(src, dest)
File.unlink dest if File.exist?(dest)
begin
File.rename src, dest
rescue
File.open(dest, 'wb') {|f| f.write File.binread(src) }
File.chmod File.stat(src).mode, dest
File.unlink src
end
end
def install(from, dest, mode, prefix = nil)
$stderr.puts "install #{from} #{dest}" if verbose?
return if no_harm?
realdest = prefix ? prefix + dest : dest
realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
str = File.binread(from)
if diff?(str, realdest)
verbose_off {
rm_f realdest if File.exist?(realdest)
}
File.open(realdest, 'wb') {|f|
f.write str
}
File.chmod mode, realdest
File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
if prefix
f.puts realdest.sub(prefix, '')
else
f.puts realdest
end
}
end
end
def diff?(new_content, path)
return true unless File.exist?(path)
new_content != File.binread(path)
end
def command(str)
$stderr.puts str if verbose?
system str or raise RuntimeError, "'system #{str}' failed"
end
def ruby(str)
command config('ruby-prog') + ' ' + str
end
def make(task = '')
command config('make-prog') + ' ' + task
end
def extdir?(dir)
File.exist?(dir + '/MANIFEST') or File.exist?("#{dir}/extconf.rb")
end
def all_files_in(dirname)
Dir.open(dirname) {|d|
return d.select {|ent| File.file?("#{dirname}/#{ent}") }
}
end
REJECT_DIRS = %w(
CVS SCCS RCS CVS.adm .svn
)
def all_dirs_in(dirname)
Dir.open(dirname) {|d|
return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
}
end
end
#
# Main Installer
#
class InstallError < StandardError; end
module HookUtils
def run_hook(name)
try_run_hook "#{curr_srcdir()}/#{name}" or
try_run_hook "#{curr_srcdir()}/#{name}.rb"
end
def try_run_hook(fname)
return false unless File.file?(fname)
begin
instance_eval File.read(fname), fname, 1
rescue
raise InstallError, "hook #{fname} failed:\n" + $!.message
end
true
end
end
module HookScriptAPI
def get_config(key)
@config[key]
end
alias config get_config
def set_config(key, val)
@config[key] = val
end
#
# srcdir/objdir (works only in the package directory)
#
#abstract srcdir_root
#abstract objdir_root
#abstract relpath
def curr_srcdir
"#{srcdir_root()}/#{relpath()}"
end
def curr_objdir
"#{objdir_root()}/#{relpath()}"
end
def srcfile(path)
"#{curr_srcdir()}/#{path}"
end
def srcexist?(path)
File.exist?(srcfile(path))
end
def srcdirectory?(path)
File.dir?(srcfile(path))
end
def srcfile?(path)
File.file? srcfile(path)
end
def srcentries(path = '.')
Dir.open("#{curr_srcdir()}/#{path}") {|d|
return d.to_a - %w(. ..)
}
end
def srcfiles(path = '.')
srcentries(path).select {|fname|
File.file?(File.join(curr_srcdir(), path, fname))
}
end
def srcdirectories(path = '.')
srcentries(path).select {|fname|
File.dir?(File.join(curr_srcdir(), path, fname))
}
end
end
class ToplevelInstaller
Version = '3.3.0'
Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
TASKS = [
[ 'all', 'do config, setup, then install' ],
[ 'config', 'saves your configurations' ],
[ 'show', 'shows current configuration' ],
[ 'setup', 'compiles ruby extentions and others' ],
[ 'install', 'installs files' ],
[ 'clean', "does `make clean' for each extention" ],
[ 'distclean',"does `make distclean' for each extention" ]
]
def ToplevelInstaller.invoke
instance().invoke
end
@singleton = nil
def ToplevelInstaller.instance
@singleton ||= new(File.dirname($0))
@singleton
end
include MetaConfigAPI
def initialize(ardir_root)
@config = nil
@options = { 'verbose' => true }
@ardir = File.expand_path(ardir_root)
end
def inspect
"#<#{self.class} #{__id__()}>"
end
def invoke
run_metaconfigs
case task = parsearg_global()
when nil, 'all'
@config = load_config('config')
parsearg_config
init_installers
exec_config
exec_setup
exec_install
else
@config = load_config(task)
__send__ "parsearg_#{task}"
init_installers
__send__ "exec_#{task}"
end
end
def run_metaconfigs
eval_file_ifexist "#{@ardir}/metaconfig"
end
def load_config(task)
case task
when 'config'
ConfigTable.new
when 'clean', 'distclean'
if File.exist?(ConfigTable::SAVE_FILE)
then ConfigTable.load
else ConfigTable.new
end
else
ConfigTable.load
end
end
def init_installers
@installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
end
#
# Hook Script API bases
#
def srcdir_root
@ardir
end
def objdir_root
'.'
end
def relpath
'.'
end
#
# Option Parsing
#
def parsearg_global
valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
while arg = ARGV.shift
case arg
when /\A\w+\z/
raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
return arg
when '-q', '--quiet'
@options['verbose'] = false
when '--verbose'
@options['verbose'] = true
when '-h', '--help'
print_usage $stdout
exit 0
when '-v', '--version'
puts "#{File.basename($0)} version #{Version}"
exit 0
when '--copyright'
puts Copyright
exit 0
else
raise InstallError, "unknown global option '#{arg}'"
end
end
nil
end
def parsearg_no_options
raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
unless ARGV.empty?
end
alias parsearg_show parsearg_no_options
alias parsearg_setup parsearg_no_options
alias parsearg_clean parsearg_no_options
alias parsearg_distclean parsearg_no_options
def parsearg_config
re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
@options['config-opt'] = []
while i = ARGV.shift
if /\A--?\z/ =~ i
@options['config-opt'] = ARGV.dup
break
end
m = re.match(i) or raise InstallError, "config: unknown option #{i}"
name, value = m.to_a[1,2]
if value
if ConfigTable.bool_config?(name)
raise InstallError, "config: --#{name} allows only yes/no for argument"\
unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value
value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
end
else
raise InstallError, "config: --#{name} requires argument"\
unless ConfigTable.bool_config?(name)
value = 'yes'
end
@config[name] = value
end
end
def parsearg_install
@options['no-harm'] = false
@options['install-prefix'] = ''
while a = ARGV.shift
case a
when /\A--no-harm\z/
@options['no-harm'] = true
when /\A--prefix=(.*)\z/
path = $1
path = File.expand_path(path) unless path[0,1] == '/'
@options['install-prefix'] = path
else
raise InstallError, "install: unknown option #{a}"
end
end
end
def print_usage(out)
out.puts 'Typical Installation Procedure:'
out.puts " $ ruby #{File.basename $0} config"
out.puts " $ ruby #{File.basename $0} setup"
out.puts " # ruby #{File.basename $0} install (may require root privilege)"
out.puts
out.puts 'Detailed Usage:'
out.puts " ruby #{File.basename $0} "
out.puts " ruby #{File.basename $0} [] []"
fmt = " %-20s %s\n"
out.puts
out.puts 'Global options:'
out.printf fmt, '-q,--quiet', 'suppress message outputs'
out.printf fmt, ' --verbose', 'output messages verbosely'
out.printf fmt, '-h,--help', 'print this message'
out.printf fmt, '-v,--version', 'print version and quit'
out.printf fmt, ' --copyright', 'print copyright and quit'
out.puts
out.puts 'Tasks:'
TASKS.each do |name, desc|
out.printf " %-10s %s\n", name, desc
end
out.puts
out.puts 'Options for CONFIG or ALL:'
ConfigTable.each_definition do |name, (default, arg, desc, default2)|
out.printf " %-20s %s [%s]\n",
'--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
desc,
default2 || default
end
out.printf " %-20s %s [%s]\n",
'--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
out.puts
out.puts 'Options for INSTALL:'
out.printf " %-20s %s [%s]\n",
'--no-harm', 'only display what to do if given', 'off'
out.printf " %-20s %s [%s]\n",
'--prefix', 'install path prefix', '$prefix'
out.puts
end
#
# Task Handlers
#
def exec_config
@installer.exec_config
@config.save # must be final
end
def exec_setup
@installer.exec_setup
end
def exec_install
@installer.exec_install
end
def exec_show
ConfigTable.each_name do |k|
v = @config.get_raw(k)
if not v or v.empty?
v = '(not specified)'
end
printf "%-10s %s\n", k, v
end
end
def exec_clean
@installer.exec_clean
end
def exec_distclean
@installer.exec_distclean
end
end
class ToplevelInstallerMulti < ToplevelInstaller
include HookUtils
include HookScriptAPI
include FileOperations
def initialize(ardir)
super
@packages = all_dirs_in("#{@ardir}/packages")
raise 'no package exists' if @packages.empty?
end
def run_metaconfigs
eval_file_ifexist "#{@ardir}/metaconfig"
@packages.each do |name|
eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
end
end
def init_installers
@installers = {}
@packages.each do |pack|
@installers[pack] = Installer.new(@config, @options,
"#{@ardir}/packages/#{pack}",
"packages/#{pack}")
end
with = extract_selection(config('with'))
without = extract_selection(config('without'))
@selected = @installers.keys.select {|name|
(with.empty? or with.include?(name)) \
and not without.include?(name)
}
end
def extract_selection(list)
a = list.split(/,/)
a.each do |name|
raise InstallError, "no such package: #{name}" \
unless @installers.key?(name)
end
a
end
def print_usage(f)
super
f.puts 'Inluded packages:'
f.puts ' ' + @packages.sort.join(' ')
f.puts
end
#
# multi-package metaconfig API
#
attr_reader :packages
def declare_packages(list)
raise 'package list is empty' if list.empty?
list.each do |name|
raise "directory packages/#{name} does not exist"\
unless File.dir?("#{@ardir}/packages/#{name}")
end
@packages = list
end
#
# Task Handlers
#
def exec_config
run_hook 'pre-config'
each_selected_installers {|inst| inst.exec_config }
run_hook 'post-config'
@config.save # must be final
end
def exec_setup
run_hook 'pre-setup'
each_selected_installers {|inst| inst.exec_setup }
run_hook 'post-setup'
end
def exec_install
run_hook 'pre-install'
each_selected_installers {|inst| inst.exec_install }
run_hook 'post-install'
end
def exec_clean
rm_f ConfigTable::SAVE_FILE
run_hook 'pre-clean'
each_selected_installers {|inst| inst.exec_clean }
run_hook 'post-clean'
end
def exec_distclean
rm_f ConfigTable::SAVE_FILE
run_hook 'pre-distclean'
each_selected_installers {|inst| inst.exec_distclean }
run_hook 'post-distclean'
end
#
# lib
#
def each_selected_installers
Dir.mkdir 'packages' unless File.dir?('packages')
@selected.each do |pack|
$stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
Dir.chdir "packages/#{pack}"
yield @installers[pack]
Dir.chdir '../..'
end
end
def verbose?
@options['verbose']
end
def no_harm?
@options['no-harm']
end
end
class Installer
FILETYPES = %w( bin lib ext data )
include HookScriptAPI
include HookUtils
include FileOperations
def initialize(config, opt, srcroot, objroot)
@config = config
@options = opt
@srcdir = File.expand_path(srcroot)
@objdir = File.expand_path(objroot)
@currdir = '.'
end
def inspect
"#<#{self.class} #{File.basename(@srcdir)}>"
end
#
# Hook Script API bases
#
def srcdir_root
@srcdir
end
def objdir_root
@objdir
end
def relpath
@currdir
end
#
# configs/options
#
def no_harm?
@options['no-harm']
end
def verbose?
@options['verbose']
end
def verbose_off
begin
save, @options['verbose'] = @options['verbose'], false
yield
ensure
@options['verbose'] = save
end
end
#
# TASK config
#
def exec_config
exec_task_traverse 'config'
end
def config_dir_bin(rel)
end
def config_dir_lib(rel)
end
def config_dir_ext(rel)
extconf if extdir?(curr_srcdir())
end
def extconf
opt = @options['config-opt'].join(' ')
command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
end
def config_dir_data(rel)
end
#
# TASK setup
#
def exec_setup
exec_task_traverse 'setup'
end
def setup_dir_bin(rel)
all_files_in(curr_srcdir()).each do |fname|
adjust_shebang "#{curr_srcdir()}/#{fname}"
end
end
def adjust_shebang(path)
return if no_harm?
tmpfile = File.basename(path) + '.tmp'
begin
File.open(path, 'rb') {|r|
File.open(tmpfile, 'wb') {|w|
first = r.gets
return unless should_modify_shebang?(first)
$stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
w.write r.read
}
}
move_file tmpfile, File.basename(path)
ensure
File.unlink tmpfile if File.exist?(tmpfile)
end
end
def should_modify_shebang?(line)
File.basename(config('ruby-path')) == 'ruby' or
shebang_command(line) == 'ruby'
end
def shebang_command(line)
cmd, arg = *line.sub(/\A\#!/, '').strip.split(/\s+/, 2)
cmd
end
def setup_dir_lib(rel)
end
def setup_dir_ext(rel)
make if extdir?(curr_srcdir())
end
def setup_dir_data(rel)
end
#
# TASK install
#
def exec_install
exec_task_traverse 'install'
end
def install_dir_bin(rel)
install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755
end
def install_dir_lib(rel)
install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
end
def install_dir_ext(rel)
return unless extdir?(curr_srcdir())
install_files ruby_extentions('.'),
"#{config('so-dir')}/#{rel}",
0555
end
def install_dir_data(rel)
install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
end
def install_files(list, dest, mode)
mkdir_p dest, @options['install-prefix']
list.each do |fname|
install fname, dest, mode, @options['install-prefix']
end
end
def ruby_scripts
collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
end
# picked up many entries from cvs-1.11.1/src/ignore.c
reject_patterns = %w(
core RCSLOG tags TAGS .make.state
.nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
*~ *.old *.bak *.BAK *.orig *.rej _$* *$
*.org *.in .*
)
mapping = {
'.' => '\.',
'$' => '\$',
'#' => '\#',
'*' => '.*'
}
REJECT_PATTERNS = Regexp.new('\A(?:' +
reject_patterns.map {|pat|
pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
}.join('|') +
')\z')
def collect_filenames_auto
mapdir((existfiles() - hookfiles()).reject {|fname|
REJECT_PATTERNS =~ fname
})
end
def existfiles
all_files_in(curr_srcdir()) | all_files_in('.')
end
def hookfiles
%w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
%w( config setup install clean ).map {|t| sprintf(fmt, t) }
}.flatten
end
def mapdir(filelist)
filelist.map {|fname|
if File.exist?(fname) # objdir
fname
else # srcdir
File.join(curr_srcdir(), fname)
end
}
end
def ruby_extentions(dir)
_ruby_extentions(dir) or
raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
end
DLEXT = /\.#{ ::RbConfig::CONFIG['DLEXT'] }\z/
def _ruby_extentions(dir)
Dir.open(dir) {|d|
return d.select {|fname| DLEXT =~ fname }
}
end
#
# TASK clean
#
def exec_clean
exec_task_traverse 'clean'
rm_f ConfigTable::SAVE_FILE
rm_f 'InstalledFiles'
end
def clean_dir_bin(rel)
end
def clean_dir_lib(rel)
end
def clean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'clean' if File.file?('Makefile')
end
def clean_dir_data(rel)
end
#
# TASK distclean
#
def exec_distclean
exec_task_traverse 'distclean'
rm_f ConfigTable::SAVE_FILE
rm_f 'InstalledFiles'
end
def distclean_dir_bin(rel)
end
def distclean_dir_lib(rel)
end
def distclean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'distclean' if File.file?('Makefile')
end
#
# lib
#
def exec_task_traverse(task)
run_hook "pre-#{task}"
FILETYPES.each do |type|
if config('without-ext') == 'yes' and type == 'ext'
$stderr.puts 'skipping ext/* by user option' if verbose?
next
end
traverse task, type, "#{task}_dir_#{type}"
end
run_hook "post-#{task}"
end
def traverse(task, rel, mid)
dive_into(rel) {
run_hook "pre-#{task}"
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
all_dirs_in(curr_srcdir()).each do |d|
traverse task, "#{rel}/#{d}", mid
end
run_hook "post-#{task}"
}
end
def dive_into(rel)
return unless File.dir?("#{@srcdir}/#{rel}")
dir = File.basename(rel)
Dir.mkdir dir unless File.dir?(dir)
prevdir = Dir.pwd
Dir.chdir dir
$stderr.puts '---> ' + rel if verbose?
@currdir = rel
yield
Dir.chdir prevdir
$stderr.puts '<--- ' + rel if verbose?
@currdir = File.dirname(rel)
end
end
if $0 == __FILE__
begin
if multipackage_install?
ToplevelInstallerMulti.invoke
else
ToplevelInstaller.invoke
end
rescue
raise if $DEBUG
$stderr.puts $!.message
$stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
exit 1
end
end
sqlite3-1.4.2/appveyor.yml 0000644 0000041 0000041 00000001342 13611407300 015515 0 ustar www-data www-data ---
version: "{build}"
branches:
only:
- master
- 1-3-stable
clone_depth: 10
install:
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- ruby --version
- gem --version
- gem install bundler --quiet --no-ri --no-rdoc
- bundler --version
- bundle install
build_script:
- rake native gem
test_script:
- rake test
artifacts:
- path: pkg\*.gem
environment:
matrix:
- ruby_version: "193"
- ruby_version: "200"
- ruby_version: "200-x64"
- ruby_version: "21"
- ruby_version: "21-x64"
- ruby_version: "22"
- ruby_version: "22-x64"
- ruby_version: "23"
- ruby_version: "23-x64"
- ruby_version: "24"
- ruby_version: "24-x64"
- ruby_version: "25"
- ruby_version: "25-x64"
sqlite3-1.4.2/Manifest.txt 0000644 0000041 0000041 00000002416 13611407300 015437 0 ustar www-data www-data .gemtest
.travis.yml
API_CHANGES.rdoc
CHANGELOG.rdoc
ChangeLog.cvs
Gemfile
LICENSE
Manifest.txt
README.rdoc
Rakefile
appveyor.yml
ext/sqlite3/aggregator.c
ext/sqlite3/aggregator.h
ext/sqlite3/backup.c
ext/sqlite3/backup.h
ext/sqlite3/database.c
ext/sqlite3/database.h
ext/sqlite3/exception.c
ext/sqlite3/exception.h
ext/sqlite3/extconf.rb
ext/sqlite3/sqlite3.c
ext/sqlite3/sqlite3_ruby.h
ext/sqlite3/statement.c
ext/sqlite3/statement.h
faq/faq.rb
faq/faq.yml
lib/sqlite3.rb
lib/sqlite3/constants.rb
lib/sqlite3/database.rb
lib/sqlite3/errors.rb
lib/sqlite3/pragmas.rb
lib/sqlite3/resultset.rb
lib/sqlite3/statement.rb
lib/sqlite3/translator.rb
lib/sqlite3/value.rb
lib/sqlite3/version.rb
rakelib/faq.rake
rakelib/gem.rake
rakelib/native.rake
rakelib/vendor_sqlite3.rake
setup.rb
test/helper.rb
test/test_backup.rb
test/test_collation.rb
test/test_database.rb
test/test_database_flags.rb
test/test_database_readonly.rb
test/test_database_readwrite.rb
test/test_deprecated.rb
test/test_encoding.rb
test/test_integration.rb
test/test_integration_aggregate.rb
test/test_integration_open_close.rb
test/test_integration_pending.rb
test/test_integration_resultset.rb
test/test_integration_statement.rb
test/test_result_set.rb
test/test_sqlite3.rb
test/test_statement.rb
test/test_statement_execute.rb
sqlite3-1.4.2/ext/ 0000755 0000041 0000041 00000000000 13611407300 013725 5 ustar www-data www-data sqlite3-1.4.2/ext/sqlite3/ 0000755 0000041 0000041 00000000000 13611407300 015311 5 ustar www-data www-data sqlite3-1.4.2/ext/sqlite3/exception.c 0000644 0000041 0000041 00000005557 13611407300 017467 0 ustar www-data www-data #include
void rb_sqlite3_raise(sqlite3 * db, int status)
{
VALUE klass = Qnil;
/* Consider only lower 8 bits, to work correctly when
extended result codes are enabled. */
switch(status & 0xff) {
case SQLITE_OK:
return;
break;
case SQLITE_ERROR:
klass = rb_path2class("SQLite3::SQLException");
break;
case SQLITE_INTERNAL:
klass = rb_path2class("SQLite3::InternalException");
break;
case SQLITE_PERM:
klass = rb_path2class("SQLite3::PermissionException");
break;
case SQLITE_ABORT:
klass = rb_path2class("SQLite3::AbortException");
break;
case SQLITE_BUSY:
klass = rb_path2class("SQLite3::BusyException");
break;
case SQLITE_LOCKED:
klass = rb_path2class("SQLite3::LockedException");
break;
case SQLITE_NOMEM:
klass = rb_path2class("SQLite3::MemoryException");
break;
case SQLITE_READONLY:
klass = rb_path2class("SQLite3::ReadOnlyException");
break;
case SQLITE_INTERRUPT:
klass = rb_path2class("SQLite3::InterruptException");
break;
case SQLITE_IOERR:
klass = rb_path2class("SQLite3::IOException");
break;
case SQLITE_CORRUPT:
klass = rb_path2class("SQLite3::CorruptException");
break;
case SQLITE_NOTFOUND:
klass = rb_path2class("SQLite3::NotFoundException");
break;
case SQLITE_FULL:
klass = rb_path2class("SQLite3::FullException");
break;
case SQLITE_CANTOPEN:
klass = rb_path2class("SQLite3::CantOpenException");
break;
case SQLITE_PROTOCOL:
klass = rb_path2class("SQLite3::ProtocolException");
break;
case SQLITE_EMPTY:
klass = rb_path2class("SQLite3::EmptyException");
break;
case SQLITE_SCHEMA:
klass = rb_path2class("SQLite3::SchemaChangedException");
break;
case SQLITE_TOOBIG:
klass = rb_path2class("SQLite3::TooBigException");
break;
case SQLITE_CONSTRAINT:
klass = rb_path2class("SQLite3::ConstraintException");
break;
case SQLITE_MISMATCH:
klass = rb_path2class("SQLite3::MismatchException");
break;
case SQLITE_MISUSE:
klass = rb_path2class("SQLite3::MisuseException");
break;
case SQLITE_NOLFS:
klass = rb_path2class("SQLite3::UnsupportedException");
break;
case SQLITE_AUTH:
klass = rb_path2class("SQLite3::AuthorizationException");
break;
case SQLITE_FORMAT:
klass = rb_path2class("SQLite3::FormatException");
break;
case SQLITE_RANGE:
klass = rb_path2class("SQLite3::RangeException");
break;
case SQLITE_NOTADB:
klass = rb_path2class("SQLite3::NotADatabaseException");
break;
default:
klass = rb_eRuntimeError;
}
klass = rb_exc_new2(klass, sqlite3_errmsg(db));
rb_iv_set(klass, "@code", INT2FIX(status));
rb_exc_raise(klass);
}
sqlite3-1.4.2/ext/sqlite3/aggregator.h 0000644 0000041 0000041 00000000340 13611407300 017601 0 ustar www-data www-data #ifndef SQLITE3_AGGREGATOR_RUBY
#define SQLITE3_AGGREGATOR_RUBY
#include
VALUE
rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name);
void
rb_sqlite3_aggregator_init(void);
#endif
sqlite3-1.4.2/ext/sqlite3/statement.c 0000644 0000041 0000041 00000026137 13611407300 017472 0 ustar www-data www-data #include
#define REQUIRE_OPEN_STMT(_ctxt) \
if(!_ctxt->st) \
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed statement");
VALUE cSqlite3Statement;
static void deallocate(void * ctx)
{
sqlite3StmtRubyPtr c = (sqlite3StmtRubyPtr)ctx;
xfree(c);
}
static VALUE allocate(VALUE klass)
{
sqlite3StmtRubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3StmtRuby));
ctx->st = NULL;
ctx->done_p = 0;
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
}
/* call-seq: SQLite3::Statement.new(db, sql)
*
* Create a new statement attached to the given Database instance, and which
* encapsulates the given SQL text. If the text contains more than one
* statement (i.e., separated by semicolons), then the #remainder property
* will be set to the trailing text.
*/
static VALUE initialize(VALUE self, VALUE db, VALUE sql)
{
sqlite3RubyPtr db_ctx;
sqlite3StmtRubyPtr ctx;
const char *tail = NULL;
int status;
StringValue(sql);
Data_Get_Struct(db, sqlite3Ruby, db_ctx);
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(!db_ctx->db)
rb_raise(rb_eArgError, "prepare called on a closed database");
if(!UTF8_P(sql)) {
sql = rb_str_export_to_enc(sql, rb_utf8_encoding());
}
#ifdef HAVE_SQLITE3_PREPARE_V2
status = sqlite3_prepare_v2(
#else
status = sqlite3_prepare(
#endif
db_ctx->db,
(const char *)StringValuePtr(sql),
(int)RSTRING_LEN(sql),
&ctx->st,
&tail
);
CHECK(db_ctx->db, status);
rb_iv_set(self, "@connection", db);
rb_iv_set(self, "@remainder", rb_str_new2(tail));
rb_iv_set(self, "@columns", Qnil);
rb_iv_set(self, "@types", Qnil);
return self;
}
/* call-seq: stmt.close
*
* Closes the statement by finalizing the underlying statement
* handle. The statement must not be used after being closed.
*/
static VALUE sqlite3_rb_close(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
sqlite3_finalize(ctx->st);
ctx->st = NULL;
return self;
}
/* call-seq: stmt.closed?
*
* Returns true if the statement has been closed.
*/
static VALUE closed_p(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(!ctx->st) return Qtrue;
return Qfalse;
}
static VALUE step(VALUE self)
{
sqlite3StmtRubyPtr ctx;
sqlite3_stmt *stmt;
int value, length;
VALUE list;
rb_encoding * internal_encoding;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
if(ctx->done_p) return Qnil;
{
VALUE db = rb_iv_get(self, "@connection");
rb_funcall(db, rb_intern("encoding"), 0);
internal_encoding = rb_default_internal_encoding();
}
stmt = ctx->st;
value = sqlite3_step(stmt);
if (rb_errinfo() != Qnil) {
/* some user defined function was invoked as a callback during step and
* it raised an exception that has been suppressed until step returns.
* Now re-raise it. */
VALUE exception = rb_errinfo();
rb_set_errinfo(Qnil);
rb_exc_raise(exception);
}
length = sqlite3_column_count(stmt);
list = rb_ary_new2((long)length);
switch(value) {
case SQLITE_ROW:
{
int i;
for(i = 0; i < length; i++) {
switch(sqlite3_column_type(stmt, i)) {
case SQLITE_INTEGER:
rb_ary_push(list, LL2NUM(sqlite3_column_int64(stmt, i)));
break;
case SQLITE_FLOAT:
rb_ary_push(list, rb_float_new(sqlite3_column_double(stmt, i)));
break;
case SQLITE_TEXT:
{
VALUE str = rb_str_new(
(const char *)sqlite3_column_text(stmt, i),
(long)sqlite3_column_bytes(stmt, i)
);
rb_enc_associate_index(str, rb_utf8_encindex());
if(internal_encoding)
str = rb_str_export_to_enc(str, internal_encoding);
rb_ary_push(list, str);
}
break;
case SQLITE_BLOB:
{
VALUE str = rb_str_new(
(const char *)sqlite3_column_blob(stmt, i),
(long)sqlite3_column_bytes(stmt, i)
);
rb_ary_push(list, str);
}
break;
case SQLITE_NULL:
rb_ary_push(list, Qnil);
break;
default:
rb_raise(rb_eRuntimeError, "bad type");
}
}
}
break;
case SQLITE_DONE:
ctx->done_p = 1;
return Qnil;
break;
default:
sqlite3_reset(stmt);
ctx->done_p = 0;
CHECK(sqlite3_db_handle(ctx->st), value);
}
return list;
}
/* call-seq: stmt.bind_param(key, value)
*
* Binds value to the named (or positional) placeholder. If +param+ is a
* Fixnum, it is treated as an index for a positional placeholder.
* Otherwise it is used as the name of the placeholder to bind to.
*
* See also #bind_params.
*/
static VALUE bind_param(VALUE self, VALUE key, VALUE value)
{
sqlite3StmtRubyPtr ctx;
int status;
int index;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
switch(TYPE(key)) {
case T_SYMBOL:
key = rb_funcall(key, rb_intern("to_s"), 0);
case T_STRING:
if(RSTRING_PTR(key)[0] != ':') key = rb_str_plus(rb_str_new2(":"), key);
index = sqlite3_bind_parameter_index(ctx->st, StringValuePtr(key));
break;
default:
index = (int)NUM2INT(key);
}
if(index == 0)
rb_raise(rb_path2class("SQLite3::Exception"), "no such bind parameter");
switch(TYPE(value)) {
case T_STRING:
if(CLASS_OF(value) == cSqlite3Blob
|| rb_enc_get_index(value) == rb_ascii8bit_encindex()
) {
status = sqlite3_bind_blob(
ctx->st,
index,
(const char *)StringValuePtr(value),
(int)RSTRING_LEN(value),
SQLITE_TRANSIENT
);
} else {
if (UTF16_LE_P(value) || UTF16_BE_P(value)) {
status = sqlite3_bind_text16(
ctx->st,
index,
(const char *)StringValuePtr(value),
(int)RSTRING_LEN(value),
SQLITE_TRANSIENT
);
} else {
if (!UTF8_P(value) || !USASCII_P(value)) {
value = rb_str_encode(value, rb_enc_from_encoding(rb_utf8_encoding()), 0, Qnil);
}
status = sqlite3_bind_text(
ctx->st,
index,
(const char *)StringValuePtr(value),
(int)RSTRING_LEN(value),
SQLITE_TRANSIENT
);
}
}
break;
case T_BIGNUM: {
sqlite3_int64 num64;
if (bignum_to_int64(value, &num64)) {
status = sqlite3_bind_int64(ctx->st, index, num64);
break;
}
}
case T_FLOAT:
status = sqlite3_bind_double(ctx->st, index, NUM2DBL(value));
break;
case T_FIXNUM:
status = sqlite3_bind_int64(ctx->st, index, (sqlite3_int64)FIX2LONG(value));
break;
case T_NIL:
status = sqlite3_bind_null(ctx->st, index);
break;
default:
rb_raise(rb_eRuntimeError, "can't prepare %s",
rb_class2name(CLASS_OF(value)));
break;
}
CHECK(sqlite3_db_handle(ctx->st), status);
return self;
}
/* call-seq: stmt.reset!
*
* Resets the statement. This is typically done internally, though it might
* occassionally be necessary to manually reset the statement.
*/
static VALUE reset_bang(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
sqlite3_reset(ctx->st);
ctx->done_p = 0;
return self;
}
/* call-seq: stmt.clear_bindings!
*
* Resets the statement. This is typically done internally, though it might
* occassionally be necessary to manually reset the statement.
*/
static VALUE clear_bindings_bang(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
sqlite3_clear_bindings(ctx->st);
ctx->done_p = 0;
return self;
}
/* call-seq: stmt.done?
*
* returns true if all rows have been returned.
*/
static VALUE done_p(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
if(ctx->done_p) return Qtrue;
return Qfalse;
}
/* call-seq: stmt.column_count
*
* Returns the number of columns to be returned for this statement
*/
static VALUE column_count(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return INT2NUM((long)sqlite3_column_count(ctx->st));
}
/* call-seq: stmt.column_name(index)
*
* Get the column name at +index+. 0 based.
*/
static VALUE column_name(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
const char * name;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
name = sqlite3_column_name(ctx->st, (int)NUM2INT(index));
if(name) return SQLITE3_UTF8_STR_NEW2(name);
return Qnil;
}
/* call-seq: stmt.column_decltype(index)
*
* Get the column type at +index+. 0 based.
*/
static VALUE column_decltype(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
const char * name;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
name = sqlite3_column_decltype(ctx->st, (int)NUM2INT(index));
if(name) return rb_str_new2(name);
return Qnil;
}
/* call-seq: stmt.bind_parameter_count
*
* Return the number of bind parameters
*/
static VALUE bind_parameter_count(VALUE self)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return INT2NUM((long)sqlite3_bind_parameter_count(ctx->st));
}
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
/* call-seq: stmt.database_name(column_index)
*
* Return the database name for the column at +column_index+
*/
static VALUE database_name(VALUE self, VALUE index)
{
sqlite3StmtRubyPtr ctx;
Data_Get_Struct(self, sqlite3StmtRuby, ctx);
REQUIRE_OPEN_STMT(ctx);
return SQLITE3_UTF8_STR_NEW2(
sqlite3_column_database_name(ctx->st, NUM2INT(index)));
}
#endif
void init_sqlite3_statement()
{
cSqlite3Statement = rb_define_class_under(mSqlite3, "Statement", rb_cObject);
rb_define_alloc_func(cSqlite3Statement, allocate);
rb_define_method(cSqlite3Statement, "initialize", initialize, 2);
rb_define_method(cSqlite3Statement, "close", sqlite3_rb_close, 0);
rb_define_method(cSqlite3Statement, "closed?", closed_p, 0);
rb_define_method(cSqlite3Statement, "bind_param", bind_param, 2);
rb_define_method(cSqlite3Statement, "reset!", reset_bang, 0);
rb_define_method(cSqlite3Statement, "clear_bindings!", clear_bindings_bang, 0);
rb_define_method(cSqlite3Statement, "step", step, 0);
rb_define_method(cSqlite3Statement, "done?", done_p, 0);
rb_define_method(cSqlite3Statement, "column_count", column_count, 0);
rb_define_method(cSqlite3Statement, "column_name", column_name, 1);
rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1);
rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0);
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
rb_define_method(cSqlite3Statement, "database_name", database_name, 1);
#endif
}
sqlite3-1.4.2/ext/sqlite3/statement.h 0000644 0000041 0000041 00000000441 13611407300 017465 0 ustar www-data www-data #ifndef SQLITE3_STATEMENT_RUBY
#define SQLITE3_STATEMENT_RUBY
#include
struct _sqlite3StmtRuby {
sqlite3_stmt *st;
int done_p;
};
typedef struct _sqlite3StmtRuby sqlite3StmtRuby;
typedef sqlite3StmtRuby * sqlite3StmtRubyPtr;
void init_sqlite3_statement();
#endif
sqlite3-1.4.2/ext/sqlite3/database.h 0000644 0000041 0000041 00000000544 13611407300 017231 0 ustar www-data www-data #ifndef SQLITE3_DATABASE_RUBY
#define SQLITE3_DATABASE_RUBY
#include
struct _sqlite3Ruby {
sqlite3 *db;
};
typedef struct _sqlite3Ruby sqlite3Ruby;
typedef sqlite3Ruby * sqlite3RubyPtr;
void init_sqlite3_database();
void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result);
VALUE sqlite3val2rb(sqlite3_value * val);
#endif
sqlite3-1.4.2/ext/sqlite3/sqlite3.c 0000644 0000041 0000041 00000012066 13611407300 017046 0 ustar www-data www-data #include
VALUE mSqlite3;
VALUE cSqlite3Blob;
int bignum_to_int64(VALUE value, sqlite3_int64 *result)
{
#ifdef HAVE_RB_INTEGER_PACK
const int nails = 0;
int t = rb_integer_pack(value, result, 1, sizeof(*result), nails,
INTEGER_PACK_NATIVE_BYTE_ORDER|
INTEGER_PACK_2COMP);
switch (t) {
case -2: case +2:
return 0;
case +1:
if (!nails) {
if (*result < 0) return 0;
}
break;
case -1:
if (!nails) {
if (*result >= 0) return 0;
}
else {
*result += INT64_MIN;
}
break;
}
return 1;
#else
# ifndef RBIGNUM_LEN
# define RBIGNUM_LEN(x) RBIGNUM(x)->len
# endif
const long len = RBIGNUM_LEN(value);
if (len == 0) {
*result = 0;
return 1;
}
if (len > 63 / (SIZEOF_BDIGITS * CHAR_BIT) + 1) return 0;
if (len == 63 / (SIZEOF_BDIGITS * CHAR_BIT) + 1) {
const BDIGIT *digits = RBIGNUM_DIGITS(value);
BDIGIT blast = digits[len-1];
BDIGIT bmax = (BDIGIT)1UL << (63 % (CHAR_BIT * SIZEOF_BDIGITS));
if (blast > bmax) return 0;
if (blast == bmax) {
if (RBIGNUM_POSITIVE_P(value)) {
return 0;
}
else {
long i = len-1;
while (i) {
if (digits[--i]) return 0;
}
}
}
}
*result = (sqlite3_int64)NUM2LL(value);
return 1;
#endif
}
static VALUE libversion(VALUE UNUSED(klass))
{
return INT2NUM(sqlite3_libversion_number());
}
static VALUE using_sqlcipher(VALUE UNUSED(klass))
{
#ifdef USING_SQLCIPHER
return Qtrue;
#else
return Qfalse;
#endif
}
/* Returns the compile time setting of the SQLITE_THREADSAFE flag.
* See: https://www.sqlite.org/c3ref/threadsafe.html
*/
static VALUE threadsafe_p(VALUE UNUSED(klass))
{
return INT2NUM(sqlite3_threadsafe());
}
void init_sqlite3_constants()
{
VALUE mSqlite3Constants;
VALUE mSqlite3Open;
mSqlite3Constants = rb_define_module_under(mSqlite3, "Constants");
/* sqlite3_open_v2 flags for Database::new */
mSqlite3Open = rb_define_module_under(mSqlite3Constants, "Open");
/* symbols = IO.readlines('sqlite3.h').map { |n| /\A#define\s+(SQLITE_OPEN_\w+)\s/ =~ n && $1 }.compact
* pad = symbols.map(&:length).max - 9
* symbols.each { |s| printf %Q{ rb_define_const(mSqlite3Open, %-#{pad}s INT2FIX(#{s}));\n}, '"' + s[12..-1] + '",' }
*/
rb_define_const(mSqlite3Open, "READONLY", INT2FIX(SQLITE_OPEN_READONLY));
rb_define_const(mSqlite3Open, "READWRITE", INT2FIX(SQLITE_OPEN_READWRITE));
rb_define_const(mSqlite3Open, "CREATE", INT2FIX(SQLITE_OPEN_CREATE));
rb_define_const(mSqlite3Open, "DELETEONCLOSE", INT2FIX(SQLITE_OPEN_DELETEONCLOSE));
rb_define_const(mSqlite3Open, "EXCLUSIVE", INT2FIX(SQLITE_OPEN_EXCLUSIVE));
rb_define_const(mSqlite3Open, "MAIN_DB", INT2FIX(SQLITE_OPEN_MAIN_DB));
rb_define_const(mSqlite3Open, "TEMP_DB", INT2FIX(SQLITE_OPEN_TEMP_DB));
rb_define_const(mSqlite3Open, "TRANSIENT_DB", INT2FIX(SQLITE_OPEN_TRANSIENT_DB));
rb_define_const(mSqlite3Open, "MAIN_JOURNAL", INT2FIX(SQLITE_OPEN_MAIN_JOURNAL));
rb_define_const(mSqlite3Open, "TEMP_JOURNAL", INT2FIX(SQLITE_OPEN_TEMP_JOURNAL));
rb_define_const(mSqlite3Open, "SUBJOURNAL", INT2FIX(SQLITE_OPEN_SUBJOURNAL));
rb_define_const(mSqlite3Open, "MASTER_JOURNAL", INT2FIX(SQLITE_OPEN_MASTER_JOURNAL));
rb_define_const(mSqlite3Open, "NOMUTEX", INT2FIX(SQLITE_OPEN_NOMUTEX));
rb_define_const(mSqlite3Open, "FULLMUTEX", INT2FIX(SQLITE_OPEN_FULLMUTEX));
#ifdef SQLITE_OPEN_AUTOPROXY
/* SQLITE_VERSION_NUMBER>=3007002 */
rb_define_const(mSqlite3Open, "AUTOPROXY", INT2FIX(SQLITE_OPEN_AUTOPROXY));
rb_define_const(mSqlite3Open, "SHAREDCACHE", INT2FIX(SQLITE_OPEN_SHAREDCACHE));
rb_define_const(mSqlite3Open, "PRIVATECACHE", INT2FIX(SQLITE_OPEN_PRIVATECACHE));
rb_define_const(mSqlite3Open, "WAL", INT2FIX(SQLITE_OPEN_WAL));
#endif
#ifdef SQLITE_OPEN_URI
/* SQLITE_VERSION_NUMBER>=3007007 */
rb_define_const(mSqlite3Open, "URI", INT2FIX(SQLITE_OPEN_URI));
#endif
#ifdef SQLITE_OPEN_MEMORY
/* SQLITE_VERSION_NUMBER>=3007013 */
rb_define_const(mSqlite3Open, "MEMORY", INT2FIX(SQLITE_OPEN_MEMORY));
#endif
}
void Init_sqlite3_native()
{
/*
* SQLite3 is a wrapper around the popular database
* sqlite[http://sqlite.org].
*
* For an example of usage, see SQLite3::Database.
*/
mSqlite3 = rb_define_module("SQLite3");
/* A class for differentiating between strings and blobs, when binding them
* into statements.
*/
cSqlite3Blob = rb_define_class_under(mSqlite3, "Blob", rb_cString);
/* Initialize the sqlite3 library */
#ifdef HAVE_SQLITE3_INITIALIZE
sqlite3_initialize();
#endif
init_sqlite3_constants();
init_sqlite3_database();
init_sqlite3_statement();
#ifdef HAVE_SQLITE3_BACKUP_INIT
init_sqlite3_backup();
#endif
rb_define_singleton_method(mSqlite3, "sqlcipher?", using_sqlcipher, 0);
rb_define_singleton_method(mSqlite3, "libversion", libversion, 0);
rb_define_singleton_method(mSqlite3, "threadsafe", threadsafe_p, 0);
rb_define_const(mSqlite3, "SQLITE_VERSION", rb_str_new2(SQLITE_VERSION));
rb_define_const(mSqlite3, "SQLITE_VERSION_NUMBER", INT2FIX(SQLITE_VERSION_NUMBER));
}
sqlite3-1.4.2/ext/sqlite3/backup.h 0000644 0000041 0000041 00000000500 13611407300 016722 0 ustar www-data www-data #if !defined(SQLITE3_BACKUP_RUBY) && defined(HAVE_SQLITE3_BACKUP_INIT)
#define SQLITE3_BACKUP_RUBY
#include
struct _sqlite3BackupRuby {
sqlite3_backup *p;
};
typedef struct _sqlite3BackupRuby sqlite3BackupRuby;
typedef sqlite3BackupRuby * sqlite3BackupRubyPtr;
void init_sqlite3_backup();
#endif
sqlite3-1.4.2/ext/sqlite3/extconf.rb 0000644 0000041 0000041 00000005211 13611407300 017303 0 ustar www-data www-data ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
require 'mkmf'
# :stopdoc:
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
ldflags = cppflags = nil
if RbConfig::CONFIG["host_os"] =~ /darwin/
begin
if with_config('sqlcipher')
brew_prefix = `brew --prefix sqlcipher`.chomp
ldflags = "#{brew_prefix}/lib"
cppflags = "#{brew_prefix}/include/sqlcipher"
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
else
brew_prefix = `brew --prefix sqlite3`.chomp
ldflags = "#{brew_prefix}/lib"
cppflags = "#{brew_prefix}/include"
pkg_conf = "#{brew_prefix}/lib/pkgconfig"
end
# pkg_config should be less error prone than parsing compiler
# commandline options, but we need to set default ldflags and cpp flags
# in case the user doesn't have pkg-config installed
ENV['PKG_CONFIG_PATH'] ||= pkg_conf
rescue
end
end
if with_config('sqlcipher')
pkg_config("sqlcipher")
else
pkg_config("sqlite3")
end
# --with-sqlite3-{dir,include,lib}
if with_config('sqlcipher')
$CFLAGS << ' -DUSING_SQLCIPHER'
dir_config("sqlcipher", cppflags, ldflags)
else
dir_config("sqlite3", cppflags, ldflags)
end
if RbConfig::CONFIG["host_os"] =~ /mswin/
$CFLAGS << ' -W3'
end
if RUBY_VERSION < '2.7'
$CFLAGS << ' -DTAINTING_SUPPORT'
end
def asplode missing
if RUBY_PLATFORM =~ /mingw|mswin/
abort "#{missing} is missing. Install SQLite3 from " +
"http://www.sqlite.org/ first."
else
abort <<-error
#{missing} is missing. Try 'brew install sqlite3',
'yum install sqlite-devel' or 'apt-get install libsqlite3-dev'
and check your shared library search path (the
location where your sqlite3 shared library is located).
error
end
end
asplode('sqlite3.h') unless find_header 'sqlite3.h'
find_library 'pthread', 'pthread_create' # 1.8 support. *shrug*
have_library 'dl' # for static builds
if with_config('sqlcipher')
asplode('sqlcipher') unless find_library 'sqlcipher', 'sqlite3_libversion_number'
else
asplode('sqlite3') unless find_library 'sqlite3', 'sqlite3_libversion_number'
end
# Functions defined in 1.9 but not 1.8
have_func('rb_proc_arity')
# Functions defined in 2.1 but not 2.0
have_func('rb_integer_pack')
# These functions may not be defined
have_func('sqlite3_initialize')
have_func('sqlite3_backup_init')
have_func('sqlite3_column_database_name')
have_func('sqlite3_enable_load_extension')
have_func('sqlite3_load_extension')
unless have_func('sqlite3_open_v2')
abort "Please use a newer version of SQLite3"
end
have_func('sqlite3_prepare_v2')
have_type('sqlite3_int64', 'sqlite3.h')
have_type('sqlite3_uint64', 'sqlite3.h')
create_makefile('sqlite3/sqlite3_native')
sqlite3-1.4.2/ext/sqlite3/aggregator.c 0000644 0000041 0000041 00000021067 13611407300 017605 0 ustar www-data www-data #include
#include
/* wraps a factory "handler" class. The "-aggregators" instance variable of
* the SQLite3::Database holds an array of all AggrogatorWrappers.
*
* An AggregatorWrapper holds the following instance variables:
* -handler_klass: the handler that creates the instances.
* -instances: array of all the cAggregatorInstance objects currently
* in-flight for this aggregator. */
static VALUE cAggregatorWrapper;
/* wraps a intance of the "handler" class. Loses its reference at the end of
* the xFinal callback.
*
* An AggregatorInstance holds the following instnace variables:
* -handler_instance: the instance to call `step` and `finalize` on.
* -exc_status: status returned by rb_protect.
* != 0 if an exception occurred. If an exception occured
* `step` and `finalize` won't be called any more. */
static VALUE cAggregatorInstance;
typedef struct rb_sqlite3_protected_funcall_args {
VALUE self;
ID method;
int argc;
VALUE *params;
} protected_funcall_args_t;
/* why isn't there something like this in the ruby API? */
static VALUE
rb_sqlite3_protected_funcall_body(VALUE protected_funcall_args_ptr)
{
protected_funcall_args_t *args =
(protected_funcall_args_t*)protected_funcall_args_ptr;
return rb_funcall2(args->self, args->method, args->argc, args->params);
}
static VALUE
rb_sqlite3_protected_funcall(VALUE self, ID method, int argc, VALUE *params,
int* exc_status)
{
protected_funcall_args_t args = {
.self = self, .method = method, .argc = argc, .params = params
};
return rb_protect(rb_sqlite3_protected_funcall_body, (VALUE)(&args), exc_status);
}
/* called in rb_sqlite3_aggregator_step and rb_sqlite3_aggregator_final. It
* checks if the exection context already has an associated instance. If it
* has one, it returns it. If there is no instance yet, it creates one and
* associates it with the context. */
static VALUE
rb_sqlite3_aggregate_instance(sqlite3_context *ctx)
{
VALUE aw = (VALUE) sqlite3_user_data(ctx);
VALUE handler_klass = rb_iv_get(aw, "-handler_klass");
VALUE inst;
VALUE *inst_ptr = sqlite3_aggregate_context(ctx, (int)sizeof(VALUE));
if (!inst_ptr) {
rb_fatal("SQLite is out-of-merory");
}
inst = *inst_ptr;
if (inst == Qfalse) { /* Qfalse == 0 */
VALUE instances = rb_iv_get(aw, "-instances");
int exc_status;
inst = rb_class_new_instance(0, NULL, cAggregatorInstance);
rb_iv_set(inst, "-handler_instance", rb_sqlite3_protected_funcall(
handler_klass, rb_intern("new"), 0, NULL, &exc_status));
rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
rb_ary_push(instances, inst);
*inst_ptr = inst;
}
if (inst == Qnil) {
rb_fatal("SQLite called us back on an already destroyed aggregate instance");
}
return inst;
}
/* called by rb_sqlite3_aggregator_final. Unlinks and frees the
* aggregator_instance_t, so the handler_instance won't be marked any more
* and Ruby's GC may free it. */
static void
rb_sqlite3_aggregate_instance_destroy(sqlite3_context *ctx)
{
VALUE aw = (VALUE) sqlite3_user_data(ctx);
VALUE instances = rb_iv_get(aw, "-instances");
VALUE *inst_ptr = sqlite3_aggregate_context(ctx, 0);
VALUE inst;
if (!inst_ptr || (inst = *inst_ptr)) {
return;
}
if (inst == Qnil) {
rb_fatal("attempt to destroy aggregate instance twice");
}
rb_iv_set(inst, "-handler_instance", Qnil); // may catch use-after-free
if (rb_ary_delete(instances, inst) == Qnil) {
rb_fatal("must be in instances at that point");
}
*inst_ptr = Qnil;
}
static void
rb_sqlite3_aggregator_step(sqlite3_context * ctx, int argc, sqlite3_value **argv)
{
VALUE inst = rb_sqlite3_aggregate_instance(ctx);
VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
VALUE * params = NULL;
VALUE one_param;
int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
int i;
if (exc_status) {
return;
}
if (argc == 1) {
one_param = sqlite3val2rb(argv[0]);
params = &one_param;
}
if (argc > 1) {
params = xcalloc((size_t)argc, sizeof(VALUE));
for(i = 0; i < argc; i++) {
params[i] = sqlite3val2rb(argv[i]);
}
}
rb_sqlite3_protected_funcall(
handler_instance, rb_intern("step"), argc, params, &exc_status);
if (argc > 1) {
xfree(params);
}
rb_iv_set(inst, "-exc_status", INT2NUM(exc_status));
}
/* we assume that this function is only called once per execution context */
static void
rb_sqlite3_aggregator_final(sqlite3_context * ctx)
{
VALUE inst = rb_sqlite3_aggregate_instance(ctx);
VALUE handler_instance = rb_iv_get(inst, "-handler_instance");
int exc_status = NUM2INT(rb_iv_get(inst, "-exc_status"));
if (!exc_status) {
VALUE result = rb_sqlite3_protected_funcall(
handler_instance, rb_intern("finalize"), 0, NULL, &exc_status);
if (!exc_status) {
set_sqlite3_func_result(ctx, result);
}
}
if (exc_status) {
/* the user should never see this, as Statement.step() will pick up the
* outstanding exception and raise it instead of generating a new one
* for SQLITE_ERROR with message "Ruby Exception occured" */
sqlite3_result_error(ctx, "Ruby Exception occured", -1);
}
rb_sqlite3_aggregate_instance_destroy(ctx);
}
/* call-seq: define_aggregator2(aggregator)
*
* Define an aggregrate function according to a factory object (the "handler")
* that knows how to obtain to all the information. The handler must provide
* the following class methods:
*
* +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
* message is optional, and if the handler does not respond to it,
* the function will have an arity of -1.
* +name+:: this is the name of the function. The handler _must_ implement
* this message.
* +new+:: this must be implemented by the handler. It should return a new
* instance of the object that will handle a specific invocation of
* the function.
*
* The handler instance (the object returned by the +new+ message, described
* above), must respond to the following messages:
*
* +step+:: this is the method that will be called for each step of the
* aggregate function's evaluation. It should take parameters according
* to the *arity* definition.
* +finalize+:: this is the method that will be called to finalize the
* aggregate function's evaluation. It should not take arguments.
*
* Note the difference between this function and #create_aggregate_handler
* is that no FunctionProxy ("ctx") object is involved. This manifests in two
* ways: The return value of the aggregate function is the return value of
* +finalize+ and neither +step+ nor +finalize+ take an additional "ctx"
* parameter.
*/
VALUE
rb_sqlite3_define_aggregator2(VALUE self, VALUE aggregator, VALUE ruby_name)
{
/* define_aggregator is added as a method to SQLite3::Database in database.c */
sqlite3RubyPtr ctx;
int arity, status;
VALUE aw;
VALUE aggregators;
Data_Get_Struct(self, sqlite3Ruby, ctx);
if (!ctx->db) {
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
}
if (rb_respond_to(aggregator, rb_intern("arity"))) {
VALUE ruby_arity = rb_funcall(aggregator, rb_intern("arity"), 0);
arity = NUM2INT(ruby_arity);
} else {
arity = -1;
}
if (arity < -1 || arity > 127) {
#ifdef PRIsVALUE
rb_raise(rb_eArgError, "%"PRIsVALUE" arity=%d out of range -1..127",
self, arity);
#else
rb_raise(rb_eArgError, "Aggregator arity=%d out of range -1..127", arity);
#endif
}
if (!rb_ivar_defined(self, rb_intern("-aggregators"))) {
rb_iv_set(self, "-aggregators", rb_ary_new());
}
aggregators = rb_iv_get(self, "-aggregators");
aw = rb_class_new_instance(0, NULL, cAggregatorWrapper);
rb_iv_set(aw, "-handler_klass", aggregator);
rb_iv_set(aw, "-instances", rb_ary_new());
status = sqlite3_create_function(
ctx->db,
StringValueCStr(ruby_name),
arity,
SQLITE_UTF8,
(void*)aw,
NULL,
rb_sqlite3_aggregator_step,
rb_sqlite3_aggregator_final
);
if (status != SQLITE_OK) {
rb_sqlite3_raise(ctx->db, status);
return self; // just in case rb_sqlite3_raise returns.
}
rb_ary_push(aggregators, aw);
return self;
}
void
rb_sqlite3_aggregator_init(void)
{
rb_gc_register_address(&cAggregatorWrapper);
rb_gc_register_address(&cAggregatorInstance);
/* rb_class_new generatos class with undefined allocator in ruby 1.9 */
cAggregatorWrapper = rb_funcall(rb_cClass, rb_intern("new"), 0);
cAggregatorInstance = rb_funcall(rb_cClass, rb_intern("new"), 0);
}
sqlite3-1.4.2/ext/sqlite3/sqlite3_ruby.h 0000644 0000041 0000041 00000002063 13611407300 020110 0 ustar www-data www-data #ifndef SQLITE3_RUBY
#define SQLITE3_RUBY
#include
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
#include
#define USASCII_P(_obj) (rb_enc_get_index(_obj) == rb_usascii_encindex())
#define UTF8_P(_obj) (rb_enc_get_index(_obj) == rb_utf8_encindex())
#define UTF16_LE_P(_obj) (rb_enc_get_index(_obj) == rb_enc_find_index("UTF-16LE"))
#define UTF16_BE_P(_obj) (rb_enc_get_index(_obj) == rb_enc_find_index("UTF-16BE"))
#define SQLITE3_UTF8_STR_NEW2(_obj) \
(rb_enc_associate_index(rb_str_new2(_obj), rb_utf8_encindex()))
#include
#ifndef HAVE_TYPE_SQLITE3_INT64
typedef sqlite_int64 sqlite3_int64;
#endif
#ifndef HAVE_TYPE_SQLITE3_UINT64
typedef sqlite_uint64 sqlite3_uint64;
#endif
extern VALUE mSqlite3;
extern VALUE cSqlite3Blob;
#include
#include
#include
#include
int bignum_to_int64(VALUE big, sqlite3_int64 *result);
#endif
sqlite3-1.4.2/ext/sqlite3/backup.c 0000644 0000041 0000041 00000010572 13611407300 016727 0 ustar www-data www-data #ifdef HAVE_SQLITE3_BACKUP_INIT
#include
#define REQUIRE_OPEN_BACKUP(_ctxt) \
if(!_ctxt->p) \
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed backup");
VALUE cSqlite3Backup;
static void deallocate(void * ctx)
{
sqlite3BackupRubyPtr c = (sqlite3BackupRubyPtr)ctx;
xfree(c);
}
static VALUE allocate(VALUE klass)
{
sqlite3BackupRubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3BackupRuby));
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
}
/* call-seq: SQLite3::Backup.new(dstdb, dstname, srcdb, srcname)
*
* Initialize backup the backup.
*
* dstdb:
* the destination SQLite3::Database object.
* dstname:
* the destination's database name.
* srcdb:
* the source SQLite3::Database object.
* srcname:
* the source's database name.
*
* The database name is "main", "temp", or the name specified in an
* ATTACH statement.
*
* This feature requires SQLite 3.6.11 or later.
*
* require 'sqlite3'
* sdb = SQLite3::Database.new('src.sqlite3')
*
* ddb = SQLite3::Database.new(':memory:')
* b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
* p [b.remaining, b.pagecount] # invalid value; for example [0, 0]
* begin
* p b.step(1) #=> OK or DONE
* p [b.remaining, b.pagecount]
* end while b.remaining > 0
* b.finish
*
* ddb = SQLite3::Database.new(':memory:')
* b = SQLite3::Backup.new(ddb, 'main', sdb, 'main')
* b.step(-1) #=> DONE
* b.finish
*
*/
static VALUE initialize(VALUE self, VALUE dstdb, VALUE dstname, VALUE srcdb, VALUE srcname)
{
sqlite3BackupRubyPtr ctx;
sqlite3RubyPtr ddb_ctx, sdb_ctx;
sqlite3_backup *pBackup;
Data_Get_Struct(self, sqlite3BackupRuby, ctx);
Data_Get_Struct(dstdb, sqlite3Ruby, ddb_ctx);
Data_Get_Struct(srcdb, sqlite3Ruby, sdb_ctx);
if(!sdb_ctx->db)
rb_raise(rb_eArgError, "cannot backup from a closed database");
if(!ddb_ctx->db)
rb_raise(rb_eArgError, "cannot backup to a closed database");
pBackup = sqlite3_backup_init(ddb_ctx->db, StringValuePtr(dstname),
sdb_ctx->db, StringValuePtr(srcname));
if( pBackup ){
ctx->p = pBackup;
}
else {
CHECK(ddb_ctx->db, sqlite3_errcode(ddb_ctx->db));
}
return self;
}
/* call-seq: SQLite3::Backup#step(nPage)
*
* Copy database pages up to +nPage+.
* If negative, copy all remaining source pages.
*
* If all pages are copied, it returns SQLite3::Constants::ErrorCode::DONE.
* When coping is not done, it returns SQLite3::Constants::ErrorCode::OK.
* When some errors occur, it returns the error code.
*/
static VALUE step(VALUE self, VALUE nPage)
{
sqlite3BackupRubyPtr ctx;
int status;
Data_Get_Struct(self, sqlite3BackupRuby, ctx);
REQUIRE_OPEN_BACKUP(ctx);
status = sqlite3_backup_step(ctx->p, NUM2INT(nPage));
return INT2NUM(status);
}
/* call-seq: SQLite3::Backup#finish
*
* Destroy the backup object.
*/
static VALUE finish(VALUE self)
{
sqlite3BackupRubyPtr ctx;
Data_Get_Struct(self, sqlite3BackupRuby, ctx);
REQUIRE_OPEN_BACKUP(ctx);
(void)sqlite3_backup_finish(ctx->p);
ctx->p = NULL;
return Qnil;
}
/* call-seq: SQLite3::Backup#remaining
*
* Returns the number of pages still to be backed up.
*
* Note that the value is only updated after step() is called,
* so before calling step() returned value is invalid.
*/
static VALUE remaining(VALUE self)
{
sqlite3BackupRubyPtr ctx;
Data_Get_Struct(self, sqlite3BackupRuby, ctx);
REQUIRE_OPEN_BACKUP(ctx);
return INT2NUM(sqlite3_backup_remaining(ctx->p));
}
/* call-seq: SQLite3::Backup#pagecount
*
* Returns the total number of pages in the source database file.
*
* Note that the value is only updated after step() is called,
* so before calling step() returned value is invalid.
*/
static VALUE pagecount(VALUE self)
{
sqlite3BackupRubyPtr ctx;
Data_Get_Struct(self, sqlite3BackupRuby, ctx);
REQUIRE_OPEN_BACKUP(ctx);
return INT2NUM(sqlite3_backup_pagecount(ctx->p));
}
void init_sqlite3_backup()
{
#if 0
VALUE mSqlite3 = rb_define_module("SQLite3");
#endif
cSqlite3Backup = rb_define_class_under(mSqlite3, "Backup", rb_cObject);
rb_define_alloc_func(cSqlite3Backup, allocate);
rb_define_method(cSqlite3Backup, "initialize", initialize, 4);
rb_define_method(cSqlite3Backup, "step", step, 1);
rb_define_method(cSqlite3Backup, "finish", finish, 0);
rb_define_method(cSqlite3Backup, "remaining", remaining, 0);
rb_define_method(cSqlite3Backup, "pagecount", pagecount, 0);
}
#endif
sqlite3-1.4.2/ext/sqlite3/exception.h 0000644 0000041 0000041 00000000265 13611407300 017463 0 ustar www-data www-data #ifndef SQLITE3_EXCEPTION_RUBY
#define SQLITE3_EXCEPTION_RUBY
#define CHECK(_db, _status) rb_sqlite3_raise(_db, _status);
void rb_sqlite3_raise(sqlite3 * db, int status);
#endif
sqlite3-1.4.2/ext/sqlite3/database.c 0000644 0000041 0000041 00000051613 13611407300 017227 0 ustar www-data www-data #include
#include
#define REQUIRE_OPEN_DB(_ctxt) \
if(!_ctxt->db) \
rb_raise(rb_path2class("SQLite3::Exception"), "cannot use a closed database");
VALUE cSqlite3Database;
static void deallocate(void * ctx)
{
sqlite3RubyPtr c = (sqlite3RubyPtr)ctx;
sqlite3 * db = c->db;
if(db) sqlite3_close(db);
xfree(c);
}
static VALUE allocate(VALUE klass)
{
sqlite3RubyPtr ctx = xcalloc((size_t)1, sizeof(sqlite3Ruby));
return Data_Wrap_Struct(klass, NULL, deallocate, ctx);
}
static char *
utf16_string_value_ptr(VALUE str)
{
StringValue(str);
rb_str_buf_cat(str, "\x00", 1L);
return RSTRING_PTR(str);
}
static VALUE sqlite3_rb_close(VALUE self);
static VALUE rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)
{
sqlite3RubyPtr ctx;
VALUE flags;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
#if defined TAINTING_SUPPORT
#if defined StringValueCStr
StringValuePtr(file);
rb_check_safe_obj(file);
#else
Check_SafeStr(file);
#endif
#endif
status = sqlite3_open_v2(
StringValuePtr(file),
&ctx->db,
NUM2INT(mode),
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
);
CHECK(ctx->db, status)
return self;
}
/* call-seq: db.close
*
* Closes this database.
*/
static VALUE sqlite3_rb_close(VALUE self)
{
sqlite3RubyPtr ctx;
sqlite3 * db;
Data_Get_Struct(self, sqlite3Ruby, ctx);
db = ctx->db;
CHECK(db, sqlite3_close(ctx->db));
ctx->db = NULL;
rb_iv_set(self, "-aggregators", Qnil);
return self;
}
/* call-seq: db.closed?
*
* Returns +true+ if this database instance has been closed (see #close).
*/
static VALUE closed_p(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
if(!ctx->db) return Qtrue;
return Qfalse;
}
/* call-seq: total_changes
*
* Returns the total number of changes made to this database instance
* since it was opened.
*/
static VALUE total_changes(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM((long)sqlite3_total_changes(ctx->db));
}
static void tracefunc(void * data, const char *sql)
{
VALUE self = (VALUE)data;
VALUE thing = rb_iv_get(self, "@tracefunc");
rb_funcall(thing, rb_intern("call"), 1, rb_str_new2(sql));
}
/* call-seq:
* trace { |sql| ... }
* trace(Class.new { def call sql; end }.new)
*
* Installs (or removes) a block that will be invoked for every SQL
* statement executed. The block receives one parameter: the SQL statement
* executed. If the block is +nil+, any existing tracer will be uninstalled.
*/
static VALUE trace(int argc, VALUE *argv, VALUE self)
{
sqlite3RubyPtr ctx;
VALUE block;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
rb_scan_args(argc, argv, "01", &block);
if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
rb_iv_set(self, "@tracefunc", block);
sqlite3_trace(ctx->db, NIL_P(block) ? NULL : tracefunc, (void *)self);
return self;
}
static int rb_sqlite3_busy_handler(void * ctx, int count)
{
VALUE self = (VALUE)(ctx);
VALUE handle = rb_iv_get(self, "@busy_handler");
VALUE result = rb_funcall(handle, rb_intern("call"), 1, INT2NUM((long)count));
if(Qfalse == result) return 0;
return 1;
}
/* call-seq:
* busy_handler { |count| ... }
* busy_handler(Class.new { def call count; end }.new)
*
* Register a busy handler with this database instance. When a requested
* resource is busy, this handler will be invoked. If the handler returns
* +false+, the operation will be aborted; otherwise, the resource will
* be requested again.
*
* The handler will be invoked with the name of the resource that was
* busy, and the number of times it has been retried.
*
* See also the mutually exclusive #busy_timeout.
*/
static VALUE busy_handler(int argc, VALUE *argv, VALUE self)
{
sqlite3RubyPtr ctx;
VALUE block;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
rb_scan_args(argc, argv, "01", &block);
if(NIL_P(block) && rb_block_given_p()) block = rb_block_proc();
rb_iv_set(self, "@busy_handler", block);
status = sqlite3_busy_handler(
ctx->db, NIL_P(block) ? NULL : rb_sqlite3_busy_handler, (void *)self);
CHECK(ctx->db, status);
return self;
}
/* call-seq: last_insert_row_id
*
* Obtains the unique row ID of the last row to be inserted by this Database
* instance.
*/
static VALUE last_insert_row_id(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return LL2NUM(sqlite3_last_insert_rowid(ctx->db));
}
VALUE sqlite3val2rb(sqlite3_value * val)
{
switch(sqlite3_value_type(val)) {
case SQLITE_INTEGER:
return LL2NUM(sqlite3_value_int64(val));
break;
case SQLITE_FLOAT:
return rb_float_new(sqlite3_value_double(val));
break;
case SQLITE_TEXT:
return rb_str_new2((const char *)sqlite3_value_text(val));
break;
case SQLITE_BLOB: {
/* Sqlite warns calling sqlite3_value_bytes may invalidate pointer from sqlite3_value_blob,
so we explicitly get the length before getting blob pointer.
Note that rb_str_new apparently create string with ASCII-8BIT (BINARY) encoding,
which is what we want, as blobs are binary
*/
int len = sqlite3_value_bytes(val);
return rb_str_new((const char *)sqlite3_value_blob(val), len);
break;
}
case SQLITE_NULL:
return Qnil;
break;
default:
rb_raise(rb_eRuntimeError, "bad type"); /* FIXME */
}
}
void set_sqlite3_func_result(sqlite3_context * ctx, VALUE result)
{
switch(TYPE(result)) {
case T_NIL:
sqlite3_result_null(ctx);
break;
case T_FIXNUM:
sqlite3_result_int64(ctx, (sqlite3_int64)FIX2LONG(result));
break;
case T_BIGNUM: {
#if SIZEOF_LONG < 8
sqlite3_int64 num64;
if (bignum_to_int64(result, &num64)) {
sqlite3_result_int64(ctx, num64);
break;
}
#endif
}
case T_FLOAT:
sqlite3_result_double(ctx, NUM2DBL(result));
break;
case T_STRING:
if(CLASS_OF(result) == cSqlite3Blob
|| rb_enc_get_index(result) == rb_ascii8bit_encindex()
) {
sqlite3_result_blob(
ctx,
(const void *)StringValuePtr(result),
(int)RSTRING_LEN(result),
SQLITE_TRANSIENT
);
} else {
sqlite3_result_text(
ctx,
(const char *)StringValuePtr(result),
(int)RSTRING_LEN(result),
SQLITE_TRANSIENT
);
}
break;
default:
rb_raise(rb_eRuntimeError, "can't return %s",
rb_class2name(CLASS_OF(result)));
}
}
static void rb_sqlite3_func(sqlite3_context * ctx, int argc, sqlite3_value **argv)
{
VALUE callable = (VALUE)sqlite3_user_data(ctx);
VALUE params = rb_ary_new2(argc);
VALUE result;
int i;
if (argc > 0) {
for(i = 0; i < argc; i++) {
VALUE param = sqlite3val2rb(argv[i]);
rb_ary_push(params, param);
}
}
result = rb_apply(callable, rb_intern("call"), params);
set_sqlite3_func_result(ctx, result);
}
#ifndef HAVE_RB_PROC_ARITY
int rb_proc_arity(VALUE self)
{
return (int)NUM2INT(rb_funcall(self, rb_intern("arity"), 0));
}
#endif
/* call-seq: define_function_with_flags(name, flags) { |args,...| }
*
* Define a function named +name+ with +args+ using TextRep bitflags +flags+. The arity of the block
* will be used as the arity for the function defined.
*/
static VALUE define_function_with_flags(VALUE self, VALUE name, VALUE flags)
{
sqlite3RubyPtr ctx;
VALUE block;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
block = rb_block_proc();
status = sqlite3_create_function(
ctx->db,
StringValuePtr(name),
rb_proc_arity(block),
NUM2INT(flags),
(void *)block,
rb_sqlite3_func,
NULL,
NULL
);
CHECK(ctx->db, status);
rb_hash_aset(rb_iv_get(self, "@functions"), name, block);
return self;
}
/* call-seq: define_function(name) { |args,...| }
*
* Define a function named +name+ with +args+. The arity of the block
* will be used as the arity for the function defined.
*/
static VALUE define_function(VALUE self, VALUE name)
{
return define_function_with_flags(self, name, INT2FIX(SQLITE_UTF8));
}
/* call-seq: interrupt
*
* Interrupts the currently executing operation, causing it to abort.
*/
static VALUE interrupt(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
sqlite3_interrupt(ctx->db);
return self;
}
/* call-seq: errmsg
*
* Return a string describing the last error to have occurred with this
* database.
*/
static VALUE errmsg(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return rb_str_new2(sqlite3_errmsg(ctx->db));
}
/* call-seq: errcode
*
* Return an integer representing the last error to have occurred with this
* database.
*/
static VALUE errcode_(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM((long)sqlite3_errcode(ctx->db));
}
/* call-seq: complete?(sql)
*
* Return +true+ if the string is a valid (ie, parsable) SQL statement, and
* +false+ otherwise.
*/
static VALUE complete_p(VALUE UNUSED(self), VALUE sql)
{
if(sqlite3_complete(StringValuePtr(sql)))
return Qtrue;
return Qfalse;
}
/* call-seq: changes
*
* Returns the number of changes made to this database instance by the last
* operation performed. Note that a "delete from table" without a where
* clause will not affect this value.
*/
static VALUE changes(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return INT2NUM(sqlite3_changes(ctx->db));
}
static int rb_sqlite3_auth(
void *ctx,
int _action,
const char * _a,
const char * _b,
const char * _c,
const char * _d)
{
VALUE self = (VALUE)ctx;
VALUE action = INT2NUM(_action);
VALUE a = _a ? rb_str_new2(_a) : Qnil;
VALUE b = _b ? rb_str_new2(_b) : Qnil;
VALUE c = _c ? rb_str_new2(_c) : Qnil;
VALUE d = _d ? rb_str_new2(_d) : Qnil;
VALUE callback = rb_iv_get(self, "@authorizer");
VALUE result = rb_funcall(callback, rb_intern("call"), 5, action, a, b, c, d);
if(T_FIXNUM == TYPE(result)) return (int)NUM2INT(result);
if(Qtrue == result) return SQLITE_OK;
if(Qfalse == result) return SQLITE_DENY;
return SQLITE_IGNORE;
}
/* call-seq: set_authorizer = auth
*
* Set the authorizer for this database. +auth+ must respond to +call+, and
* +call+ must take 5 arguments.
*
* Installs (or removes) a block that will be invoked for every access
* to the database. If the block returns 0 (or +true+), the statement
* is allowed to proceed. Returning 1 or false causes an authorization error to
* occur, and returning 2 or nil causes the access to be silently denied.
*/
static VALUE set_authorizer(VALUE self, VALUE authorizer)
{
sqlite3RubyPtr ctx;
int status;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
status = sqlite3_set_authorizer(
ctx->db, NIL_P(authorizer) ? NULL : rb_sqlite3_auth, (void *)self
);
CHECK(ctx->db, status);
rb_iv_set(self, "@authorizer", authorizer);
return self;
}
/* call-seq: db.busy_timeout = ms
*
* Indicates that if a request for a resource terminates because that
* resource is busy, SQLite should sleep and retry for up to the indicated
* number of milliseconds. By default, SQLite does not retry
* busy resources. To restore the default behavior, send 0 as the
* +ms+ parameter.
*
* See also the mutually exclusive #busy_handler.
*/
static VALUE set_busy_timeout(VALUE self, VALUE timeout)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
CHECK(ctx->db, sqlite3_busy_timeout(ctx->db, (int)NUM2INT(timeout)));
return self;
}
/* call-seq: db.extended_result_codes = true
*
* Enable extended result codes in SQLite. These result codes allow for more
* detailed exception reporting, such a which type of constraint is violated.
*/
static VALUE set_extended_result_codes(VALUE self, VALUE enable)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
CHECK(ctx->db, sqlite3_extended_result_codes(ctx->db, RTEST(enable) ? 1 : 0));
return self;
}
int rb_comparator_func(void * ctx, int a_len, const void * a, int b_len, const void * b)
{
VALUE comparator;
VALUE a_str;
VALUE b_str;
VALUE comparison;
rb_encoding * internal_encoding;
internal_encoding = rb_default_internal_encoding();
comparator = (VALUE)ctx;
a_str = rb_str_new((const char *)a, a_len);
b_str = rb_str_new((const char *)b, b_len);
rb_enc_associate_index(a_str, rb_utf8_encindex());
rb_enc_associate_index(b_str, rb_utf8_encindex());
if(internal_encoding) {
a_str = rb_str_export_to_enc(a_str, internal_encoding);
b_str = rb_str_export_to_enc(b_str, internal_encoding);
}
comparison = rb_funcall(comparator, rb_intern("compare"), 2, a_str, b_str);
return NUM2INT(comparison);
}
/* call-seq: db.collation(name, comparator)
*
* Add a collation with name +name+, and a +comparator+ object. The
* +comparator+ object should implement a method called "compare" that takes
* two parameters and returns an integer less than, equal to, or greater than
* 0.
*/
static VALUE collation(VALUE self, VALUE name, VALUE comparator)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
CHECK(ctx->db, sqlite3_create_collation(
ctx->db,
StringValuePtr(name),
SQLITE_UTF8,
(void *)comparator,
NIL_P(comparator) ? NULL : rb_comparator_func));
/* Make sure our comparator doesn't get garbage collected. */
rb_hash_aset(rb_iv_get(self, "@collations"), name, comparator);
return self;
}
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
/* call-seq: db.load_extension(file)
*
* Loads an SQLite extension library from the named file. Extension
* loading must be enabled using db.enable_load_extension(true) prior
* to calling this API.
*/
static VALUE load_extension(VALUE self, VALUE file)
{
sqlite3RubyPtr ctx;
int status;
char *errMsg;
VALUE errexp;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
status = sqlite3_load_extension(ctx->db, RSTRING_PTR(file), 0, &errMsg);
if (status != SQLITE_OK)
{
errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
sqlite3_free(errMsg);
rb_exc_raise(errexp);
}
return self;
}
#endif
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
/* call-seq: db.enable_load_extension(onoff)
*
* Enable or disable extension loading.
*/
static VALUE enable_load_extension(VALUE self, VALUE onoff)
{
sqlite3RubyPtr ctx;
int onoffparam;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
if (Qtrue == onoff) {
onoffparam = 1;
} else if (Qfalse == onoff) {
onoffparam = 0;
} else {
onoffparam = (int)NUM2INT(onoff);
}
CHECK(ctx->db, sqlite3_enable_load_extension(ctx->db, onoffparam));
return self;
}
#endif
static int enc_cb(void * _self, int UNUSED(columns), char **data, char **UNUSED(names))
{
VALUE self = (VALUE)_self;
int index = rb_enc_find_index(data[0]);
rb_encoding * e = rb_enc_from_index(index);
rb_iv_set(self, "@encoding", rb_enc_from_encoding(e));
return 0;
}
/* call-seq: db.encoding
*
* Fetch the encoding set on this database
*/
static VALUE db_encoding(VALUE self)
{
sqlite3RubyPtr ctx;
VALUE enc;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
enc = rb_iv_get(self, "@encoding");
if(NIL_P(enc)) {
sqlite3_exec(ctx->db, "PRAGMA encoding", enc_cb, (void *)self, NULL);
}
return rb_iv_get(self, "@encoding");
}
/* call-seq: db.transaction_active?
*
* Returns +true+ if there is a transaction active, and +false+ otherwise.
*
*/
static VALUE transaction_active_p(VALUE self)
{
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
return sqlite3_get_autocommit(ctx->db) ? Qfalse : Qtrue;
}
static int hash_callback_function(VALUE callback_ary, int count, char **data, char **columns)
{
VALUE new_hash = rb_hash_new();
int i;
for (i = 0; i < count; i++) {
if (data[i] == NULL) {
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), Qnil);
} else {
rb_hash_aset(new_hash, rb_str_new_cstr(columns[i]), rb_str_new_cstr(data[i]));
}
}
rb_ary_push(callback_ary, new_hash);
return 0;
}
static int regular_callback_function(VALUE callback_ary, int count, char **data, char **columns)
{
VALUE new_ary = rb_ary_new();
int i;
for (i = 0; i < count; i++) {
if (data[i] == NULL) {
rb_ary_push(new_ary, Qnil);
} else {
rb_ary_push(new_ary, rb_str_new_cstr(data[i]));
}
}
rb_ary_push(callback_ary, new_ary);
return 0;
}
/* Is invoked by calling db.execute_batch2(sql, &block)
*
* Executes all statments in a given string separated by semicolons.
* If a query is made, all values returned are strings
* (except for 'NULL' values which return nil),
* so the user may parse values with a block.
* If no query is made, an empty array will be returned.
*/
static VALUE exec_batch(VALUE self, VALUE sql, VALUE results_as_hash)
{
sqlite3RubyPtr ctx;
int status;
VALUE callback_ary = rb_ary_new();
char *errMsg;
VALUE errexp;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
if(results_as_hash == Qtrue) {
status = sqlite3_exec(ctx->db, StringValuePtr(sql), hash_callback_function, callback_ary, &errMsg);
} else {
status = sqlite3_exec(ctx->db, StringValuePtr(sql), regular_callback_function, callback_ary, &errMsg);
}
if (status != SQLITE_OK)
{
errexp = rb_exc_new2(rb_eRuntimeError, errMsg);
sqlite3_free(errMsg);
rb_exc_raise(errexp);
}
return callback_ary;
}
/* call-seq: db.db_filename(database_name)
*
* Returns the file associated with +database_name+. Can return nil or an
* empty string if the database is temporary, or in-memory.
*/
static VALUE db_filename(VALUE self, VALUE db_name)
{
sqlite3RubyPtr ctx;
const char * fname;
Data_Get_Struct(self, sqlite3Ruby, ctx);
REQUIRE_OPEN_DB(ctx);
fname = sqlite3_db_filename(ctx->db, StringValueCStr(db_name));
if(fname) return SQLITE3_UTF8_STR_NEW2(fname);
return Qnil;
}
static VALUE rb_sqlite3_open16(VALUE self, VALUE file)
{
int status;
sqlite3RubyPtr ctx;
Data_Get_Struct(self, sqlite3Ruby, ctx);
#if defined TAINTING_SUPPORT
#if defined StringValueCStr
StringValuePtr(file);
rb_check_safe_obj(file);
#else
Check_SafeStr(file);
#endif
#endif
status = sqlite3_open16(utf16_string_value_ptr(file), &ctx->db);
CHECK(ctx->db, status)
return INT2NUM(status);
}
void init_sqlite3_database()
{
#if 0
VALUE mSqlite3 = rb_define_module("SQLite3");
#endif
cSqlite3Database = rb_define_class_under(mSqlite3, "Database", rb_cObject);
rb_define_alloc_func(cSqlite3Database, allocate);
rb_define_private_method(cSqlite3Database, "open_v2", rb_sqlite3_open_v2, 3);
rb_define_private_method(cSqlite3Database, "open16", rb_sqlite3_open16, 1);
rb_define_method(cSqlite3Database, "collation", collation, 2);
rb_define_method(cSqlite3Database, "close", sqlite3_rb_close, 0);
rb_define_method(cSqlite3Database, "closed?", closed_p, 0);
rb_define_method(cSqlite3Database, "total_changes", total_changes, 0);
rb_define_method(cSqlite3Database, "trace", trace, -1);
rb_define_method(cSqlite3Database, "last_insert_row_id", last_insert_row_id, 0);
rb_define_method(cSqlite3Database, "define_function", define_function, 1);
rb_define_method(cSqlite3Database, "define_function_with_flags", define_function_with_flags, 2);
/* public "define_aggregator" is now a shim around define_aggregator2
* implemented in Ruby */
rb_define_private_method(cSqlite3Database, "define_aggregator2", rb_sqlite3_define_aggregator2, 2);
rb_define_method(cSqlite3Database, "interrupt", interrupt, 0);
rb_define_method(cSqlite3Database, "errmsg", errmsg, 0);
rb_define_method(cSqlite3Database, "errcode", errcode_, 0);
rb_define_method(cSqlite3Database, "complete?", complete_p, 1);
rb_define_method(cSqlite3Database, "changes", changes, 0);
rb_define_method(cSqlite3Database, "authorizer=", set_authorizer, 1);
rb_define_method(cSqlite3Database, "busy_handler", busy_handler, -1);
rb_define_method(cSqlite3Database, "busy_timeout=", set_busy_timeout, 1);
rb_define_method(cSqlite3Database, "extended_result_codes=", set_extended_result_codes, 1);
rb_define_method(cSqlite3Database, "transaction_active?", transaction_active_p, 0);
rb_define_private_method(cSqlite3Database, "exec_batch", exec_batch, 2);
rb_define_private_method(cSqlite3Database, "db_filename", db_filename, 1);
#ifdef HAVE_SQLITE3_LOAD_EXTENSION
rb_define_method(cSqlite3Database, "load_extension", load_extension, 1);
#endif
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
rb_define_method(cSqlite3Database, "enable_load_extension", enable_load_extension, 1);
#endif
rb_define_method(cSqlite3Database, "encoding", db_encoding, 0);
rb_sqlite3_aggregator_init();
}