sqlite3-1.4.2/0000755000004100000410000000000013611407300013125 5ustar www-datawww-datasqlite3-1.4.2/.travis.yml0000644000004100000410000000075613611407300015246 0ustar www-datawww-datalanguage: 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/0000755000004100000410000000000013611407300014104 5ustar www-datawww-datasqlite3-1.4.2/test/helper.rb0000644000004100000410000000051513611407300015711 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000206113611407300017567 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000435713611407300021530 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001134713611407300017264 0ustar www-datawww-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.rb0000644000004100000410000003427213611407300017244 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000725713611407300022140 0ustar www-datawww-datarequire '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.rb0000644000004100000410000002047313611407300022027 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000203613611407300016736 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000131313611407300022217 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000060113611407300017051 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000204113611407300021213 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001577113611407300017507 0ustar www-datawww-datarequire '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.rb0000644000004100000410000003355113611407300020022 0ustar www-datawww-datarequire '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( />>>.*<< 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.rb0000644000004100000410000000203313611407300021127 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000232013611407300021277 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000665713611407300020426 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000172213611407300017663 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000410613611407300017455 0ustar www-datawww-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/0000755000004100000410000000000013611407300013674 5ustar www-datawww-datasqlite3-1.4.2/faq/faq.yml0000644000004100000410000002732313611407300015175 0ustar www-datawww-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.rb0000644000004100000410000000614613611407300014777 0ustar www-datawww-datarequire 'yaml' require 'redcloth' def process_faq_list( faqs ) 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{},"" ) 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{}, "" ) 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.gemspec0000644000004100000410000001252413611407300016062 0ustar www-datawww-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.rdoc0000644000004100000410000000440113611407300015536 0ustar www-datawww-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/0000755000004100000410000000000013611407300014536 5ustar www-datawww-datasqlite3-1.4.2/rakelib/faq.rake0000644000004100000410000000027413611407300016154 0ustar www-datawww-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.rake0000644000004100000410000000604513611407300020350 0ustar www-datawww-datarequire "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.rake0000644000004100000410000000323313611407300016671 0ustar www-datawww-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.rake0000644000004100000410000000216013611407300016151 0ustar www-datawww-databegin 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/LICENSE0000644000004100000410000000273413611407300014140 0ustar www-datawww-dataCopyright (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.rdoc0000644000004100000410000002517013611407300015272 0ustar www-datawww-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/.gemtest0000644000004100000410000000000013611407300014564 0ustar www-datawww-datasqlite3-1.4.2/Rakefile0000644000004100000410000000016613611407300014575 0ustar www-datawww-data# # NOTE: Keep this file clean. # Add your customizations inside tasks directory. # Thank You. # # vim: syntax=ruby sqlite3-1.4.2/lib/0000755000004100000410000000000013611407300013673 5ustar www-datawww-datasqlite3-1.4.2/lib/sqlite3/0000755000004100000410000000000013611407300015257 5ustar www-datawww-datasqlite3-1.4.2/lib/sqlite3/version.rb0000644000004100000410000000071113611407300017270 0ustar www-datawww-datamodule 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.rb0000644000004100000410000000240313611407300017117 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000204113611407300016715 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000775013611407300017621 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001263213611407300017642 0ustar www-datawww-datarequire '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.rb0000644000004100000410000006064613611407300017364 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000335313611407300017624 0ustar www-datawww-datamodule 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.rb0000644000004100000410000003414713611407300017247 0ustar www-datawww-datarequire '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.rb0000644000004100000410000001002213611407300017770 0ustar www-datawww-datarequire '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.rb0000644000004100000410000000055113611407300015605 0ustar www-datawww-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.rdoc0000644000004100000410000000617413611407300014743 0ustar www-datawww-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 {Build Status}[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.cvs0000644000004100000410000000574213611407300015501 0ustar www-datawww-data2005-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/Gemfile0000644000004100000410000000117313611407300014422 0ustar www-datawww-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.rb0000644000004100000410000006741113611407300014623 0ustar www-datawww-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.yml0000644000004100000410000000134213611407300015515 0ustar www-datawww-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.txt0000644000004100000410000000241613611407300015437 0ustar www-datawww-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/0000755000004100000410000000000013611407300013725 5ustar www-datawww-datasqlite3-1.4.2/ext/sqlite3/0000755000004100000410000000000013611407300015311 5ustar www-datawww-datasqlite3-1.4.2/ext/sqlite3/exception.c0000644000004100000410000000555713611407300017467 0ustar www-datawww-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.h0000644000004100000410000000034013611407300017601 0ustar www-datawww-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.c0000644000004100000410000002613713611407300017472 0ustar www-datawww-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.h0000644000004100000410000000044113611407300017465 0ustar www-datawww-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.h0000644000004100000410000000054413611407300017231 0ustar www-datawww-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.c0000644000004100000410000001206613611407300017046 0ustar www-datawww-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.h0000644000004100000410000000050013611407300016722 0ustar www-datawww-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.rb0000644000004100000410000000521113611407300017303 0ustar www-datawww-dataENV['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.c0000644000004100000410000002106713611407300017605 0ustar www-datawww-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.h0000644000004100000410000000206313611407300020110 0ustar www-datawww-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.c0000644000004100000410000001057213611407300016727 0ustar www-datawww-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.h0000644000004100000410000000026513611407300017463 0ustar www-datawww-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.c0000644000004100000410000005161313611407300017227 0ustar www-datawww-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(); }