sqlite3 1.3.7 → 1.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,17 +2,17 @@ require "rake/clean"
2
2
  require "rake/extensioncompiler"
3
3
  require "mini_portile"
4
4
 
5
- $recipes = {}
5
+ CLOBBER.include("ports")
6
6
 
7
- $recipes[:sqlite3] = MiniPortile.new "sqlite3", BINARY_VERSION
8
- $recipes[:sqlite3].files << "http://sqlite.org/sqlite-autoconf-#{URL_VERSION}.tar.gz"
7
+ directory "ports"
9
8
 
10
- namespace :ports do
11
- directory "ports"
9
+ def define_sqlite_task(platform, host)
10
+ recipe = MiniPortile.new "sqlite3", BINARY_VERSION
11
+ recipe.files << "http://sqlite.org#{URL_PATH}/sqlite-autoconf-#{URL_VERSION}.tar.gz"
12
+ recipe.host = host
12
13
 
13
- desc "Install port sqlite3 #{$recipes[:sqlite3].version}"
14
- task :sqlite3 => ["ports"] do |t|
15
- recipe = $recipes[:sqlite3]
14
+ desc "Compile sqlite3 for #{platform} (#{host})"
15
+ task "ports:sqlite3:#{platform}" => ["ports"] do |t|
16
16
  checkpoint = "ports/.#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
17
17
 
18
18
  unless File.exist?(checkpoint)
@@ -22,30 +22,76 @@ namespace :ports do
22
22
  recipe.cook
23
23
  touch checkpoint
24
24
  end
25
-
26
- recipe.activate
27
25
  end
26
+
27
+ recipe
28
28
  end
29
29
 
30
+ # native sqlite3 compilation
31
+ recipe = define_sqlite_task(RUBY_PLATFORM, RbConfig::CONFIG["host"])
32
+
33
+ # force compilation of sqlite3 when working natively under MinGW
30
34
  if RUBY_PLATFORM =~ /mingw/
31
- Rake::Task['compile'].prerequisites.unshift "ports:sqlite3"
35
+ RUBY_EXTENSION.config_options << "--with-opt-dir=#{recipe.path}"
36
+
37
+ # also prepend DevKit into compilation phase
38
+ Rake::Task["compile"].prerequisites.unshift "devkit", "ports:sqlite3:#{RUBY_PLATFORM}"
39
+ Rake::Task["native"].prerequisites.unshift "devkit", "ports:sqlite3:#{RUBY_PLATFORM}"
32
40
  end
33
41
 
42
+ # trick to test local compilation of sqlite3
34
43
  if ENV["USE_MINI_PORTILE"] == "true"
35
- Rake::Task["compile"].prerequisites.unshift "ports:sqlite3"
44
+ # fake recipe so we can build a directory to it
45
+ recipe = MiniPortile.new "sqlite3", BINARY_VERSION
46
+ recipe.host = RbConfig::CONFIG["host"]
47
+
48
+ RUBY_EXTENSION.config_options << "--with-opt-dir=#{recipe.path}"
49
+
50
+ # compile sqlite3 first
51
+ Rake::Task["compile"].prerequisites.unshift "ports:sqlite3:#{RUBY_PLATFORM}"
52
+ end
53
+
54
+ # iterate over all cross-compilation platforms and define the proper
55
+ # sqlite3 recipe for it.
56
+ if RUBY_EXTENSION.cross_compile
57
+ config_path = File.expand_path("~/.rake-compiler/config.yml")
58
+ if File.exist?(config_path)
59
+ # obtains platforms from rake-compiler's config.yml
60
+ config_file = YAML.load_file(config_path)
61
+
62
+ Array(RUBY_EXTENSION.cross_platform).each do |platform|
63
+ # obtain platform from rbconfig file
64
+ config_key = config_file.keys.sort.find { |key|
65
+ key.start_with?("rbconfig-#{platform}-")
66
+ }
67
+ rbfile = config_file[config_key]
68
+
69
+ # skip if rbconfig cannot be read
70
+ next unless File.exist?(rbfile)
71
+
72
+ host = IO.read(rbfile).match(/CONFIG\["CC"\] = "(.*)"/)[1].sub(/\-gcc/, '')
73
+ recipe = define_sqlite_task(platform, host)
74
+
75
+ RUBY_EXTENSION.cross_config_options << {
76
+ platform => "--with-opt-dir=#{recipe.path}"
77
+ }
78
+
79
+ # pre-compile sqlite3 port when cross-compiling
80
+ task :cross => "ports:sqlite3:#{platform}"
81
+ end
82
+ else
83
+ warn "rake-compiler configuration doesn't exist, but is required for ports"
84
+ end
36
85
  end
37
86
 
38
87
  task :cross do
39
88
  ["CC", "CXX", "LDFLAGS", "CPPFLAGS", "RUBYOPT"].each do |var|
40
89
  ENV.delete(var)
41
90
  end
42
- host = ENV.fetch("HOST", Rake::ExtensionCompiler.mingw_host)
43
- $recipes.each do |_, recipe|
44
- recipe.host = host
45
- end
46
-
47
- # hook compile task with dependencies
48
- Rake::Task["compile"].prerequisites.unshift "ports:sqlite3"
49
91
  end
50
92
 
51
- CLOBBER.include("ports")
93
+ desc "Build windows binary gems per rake-compiler-dock."
94
+ task "gem:windows" do
95
+ require "rake_compiler_dock"
96
+ RakeCompilerDock.sh "bundle && rake cross native gem MAKE='nice make -j`nproc`'"
97
+ end
data/test/helper.rb CHANGED
@@ -1,14 +1,18 @@
1
1
  require 'sqlite3'
2
- require 'test/unit'
2
+ require 'minitest/autorun'
3
3
 
4
4
  unless RUBY_VERSION >= "1.9"
5
5
  require 'iconv'
6
6
  end
7
7
 
8
8
  module SQLite3
9
- class TestCase < Test::Unit::TestCase
10
- unless RUBY_VERSION >= '1.9'
11
- undef :default_test
9
+ class TestCase < Minitest::Test
10
+ alias :assert_not_equal :refute_equal
11
+ alias :assert_not_nil :refute_nil
12
+ alias :assert_raise :assert_raises
13
+
14
+ def assert_nothing_raised
15
+ yield
12
16
  end
13
17
  end
14
18
  end
@@ -1,4 +1,6 @@
1
1
  require 'helper'
2
+ require 'tempfile'
3
+ require 'pathname'
2
4
 
3
5
  module SQLite3
4
6
  class TestDatabase < SQLite3::TestCase
@@ -6,12 +8,43 @@ module SQLite3
6
8
 
7
9
  def setup
8
10
  @db = SQLite3::Database.new(':memory:')
11
+ super
9
12
  end
10
13
 
11
14
  def test_segv
12
15
  assert_raises(TypeError) { SQLite3::Database.new 1 }
13
16
  end
14
17
 
18
+ def test_db_filename
19
+ tf = nil
20
+ assert_equal '', @db.filename('main')
21
+ tf = Tempfile.new 'thing'
22
+ @db = SQLite3::Database.new tf.path
23
+ assert_equal File.expand_path(tf.path), File.expand_path(@db.filename('main'))
24
+ ensure
25
+ tf.unlink if tf
26
+ end
27
+
28
+ def test_filename
29
+ tf = nil
30
+ assert_equal '', @db.filename
31
+ tf = Tempfile.new 'thing'
32
+ @db = SQLite3::Database.new tf.path
33
+ assert_equal File.expand_path(tf.path), File.expand_path(@db.filename)
34
+ ensure
35
+ tf.unlink if tf
36
+ end
37
+
38
+ def test_filename_with_attachment
39
+ tf = nil
40
+ assert_equal '', @db.filename
41
+ tf = Tempfile.new 'thing'
42
+ @db.execute "ATTACH DATABASE '#{tf.path}' AS 'testing'"
43
+ assert_equal File.expand_path(tf.path), File.expand_path(@db.filename('testing'))
44
+ ensure
45
+ tf.unlink if tf
46
+ end
47
+
15
48
  def test_bignum
16
49
  num = 4907021672125087844
17
50
  db.execute 'CREATE TABLE "employees" ("token" integer(8), "name" varchar(20) NOT NULL)'
@@ -22,9 +55,9 @@ module SQLite3
22
55
 
23
56
  def test_blob
24
57
  @db.execute("CREATE TABLE blobs ( id INTEGER, hash BLOB(10) )")
25
- str = "\0foo"
26
- @db.execute("INSERT INTO blobs VALUES (0, ?)", [str])
27
- assert_equal [[0, str]], @db.execute("SELECT * FROM blobs")
58
+ blob = Blob.new("foo\0bar")
59
+ @db.execute("INSERT INTO blobs VALUES (0, ?)", [blob])
60
+ assert_equal [[0, blob, blob.length, blob.length*2]], @db.execute("SELECT id, hash, length(hash), length(hex(hash)) FROM blobs")
28
61
  end
29
62
 
30
63
  def test_get_first_row
@@ -60,6 +93,14 @@ module SQLite3
60
93
  assert_equal [[30]], @db.execute("select number from items")
61
94
  end
62
95
 
96
+ def test_batch_last_comment_is_processed
97
+ # FIXME: nil as a successful return value is kinda dumb
98
+ assert_nil @db.execute_batch <<-eosql
99
+ CREATE TABLE items (id integer PRIMARY KEY AUTOINCREMENT);
100
+ -- omg
101
+ eosql
102
+ end
103
+
63
104
  def test_new
64
105
  db = SQLite3::Database.new(':memory:')
65
106
  assert db
@@ -101,12 +142,33 @@ module SQLite3
101
142
  assert thing.closed?
102
143
  end
103
144
 
145
+ def test_block_closes_self_even_raised
146
+ thing = nil
147
+ begin
148
+ SQLite3::Database.new(':memory:') do |db|
149
+ thing = db
150
+ raise
151
+ end
152
+ rescue
153
+ end
154
+ assert thing.closed?
155
+ end
156
+
104
157
  def test_prepare
105
158
  db = SQLite3::Database.new(':memory:')
106
159
  stmt = db.prepare('select "hello world"')
107
160
  assert_instance_of(SQLite3::Statement, stmt)
108
161
  end
109
162
 
163
+ def test_block_prepare_does_not_double_close
164
+ db = SQLite3::Database.new(':memory:')
165
+ r = db.prepare('select "hello world"') do |stmt|
166
+ stmt.close
167
+ :foo
168
+ end
169
+ assert_equal :foo, r
170
+ end
171
+
110
172
  def test_total_changes
111
173
  db = SQLite3::Database.new(':memory:')
112
174
  db.execute("create table foo ( a integer primary key, b text )")
@@ -227,18 +289,47 @@ module SQLite3
227
289
  assert_equal [2.2, 'foo', nil], called_with
228
290
  end
229
291
 
292
+ def test_call_func_blob
293
+ called_with = nil
294
+ @db.define_function("hello") do |a, b|
295
+ called_with = [a, b, a.length]
296
+ nil
297
+ end
298
+ blob = Blob.new("a\0fine\0kettle\0of\0fish")
299
+ @db.execute("select hello(?, length(?))", [blob, blob])
300
+ assert_equal [blob, blob.length, 21], called_with
301
+ end
302
+
230
303
  def test_function_return
231
304
  @db.define_function("hello") { |a| 10 }
232
305
  assert_equal [10], @db.execute("select hello('world')").first
233
306
  end
234
307
 
235
308
  def test_function_return_types
236
- [10, 2.2, nil, "foo"].each do |thing|
309
+ [10, 2.2, nil, "foo", Blob.new("foo\0bar")].each do |thing|
237
310
  @db.define_function("hello") { |a| thing }
238
311
  assert_equal [thing], @db.execute("select hello('world')").first
239
312
  end
240
313
  end
241
314
 
315
+ def test_function_gc_segfault
316
+ @db.create_function("bug", -1) { |func, *values| func.result = values.join }
317
+ # With a lot of data and a lot of threads, try to induce a GC segfault.
318
+ params = Array.new(127, "?" * 28000)
319
+ proc = Proc.new {
320
+ db.execute("select bug(#{Array.new(params.length, "?").join(",")})", params)
321
+ }
322
+ m = Mutex.new
323
+ 30.times.map { Thread.new { m.synchronize { proc.call } } }.each(&:join)
324
+ end
325
+
326
+ def test_function_return_type_round_trip
327
+ [10, 2.2, nil, "foo", Blob.new("foo\0bar")].each do |thing|
328
+ @db.define_function("hello") { |a| a }
329
+ assert_equal [thing], @db.execute("select hello(hello(?))", [thing]).first
330
+ end
331
+ end
332
+
242
333
  def test_define_function_closed
243
334
  @db.close
244
335
  assert_raise(SQLite3::Exception) do
@@ -3,7 +3,7 @@ require 'helper'
3
3
  module SQLite3
4
4
  class TestDatabaseReadonly < SQLite3::TestCase
5
5
  def setup
6
- File.unlink 'test-readonly.db' if File.exists?('test-readonly.db')
6
+ File.unlink 'test-readonly.db' if File.exist?('test-readonly.db')
7
7
  @db = SQLite3::Database.new('test-readonly.db')
8
8
  @db.execute("CREATE TABLE foos (id integer)")
9
9
  @db.close
@@ -11,7 +11,7 @@ module SQLite3
11
11
 
12
12
  def teardown
13
13
  @db.close unless @db.closed?
14
- File.unlink 'test-readonly.db'
14
+ File.unlink 'test-readonly.db' if File.exist?('test-readonly.db')
15
15
  end
16
16
 
17
17
  def test_open_readonly_database
@@ -19,6 +19,13 @@ module SQLite3
19
19
  assert @db.readonly?
20
20
  end
21
21
 
22
+ def test_open_readonly_not_exists_database
23
+ File.unlink 'test-readonly.db'
24
+ assert_raise(SQLite3::CantOpenException) do
25
+ @db = SQLite3::Database.new('test-readonly.db', :readonly => true)
26
+ end
27
+ end
28
+
22
29
  def test_insert_readonly_database
23
30
  @db = SQLite3::Database.new('test-readonly.db', :readonly => true)
24
31
  assert_raise(SQLite3::ReadOnlyException) do
@@ -11,6 +11,40 @@ module SQLite3
11
11
  @db.execute(@create);
12
12
  end
13
13
 
14
+ def test_select_encoding_on_utf_16
15
+ str = "foo"
16
+ utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE"
17
+ db = SQLite3::Database.new(':memory:'.encode(utf16))
18
+ db.execute @create
19
+ db.execute "insert into ex (id, data) values (1, \"#{str}\")"
20
+
21
+ stmt = db.prepare 'select * from ex where data = ?'
22
+ ['US-ASCII', utf16, 'EUC-JP', 'UTF-8'].each do |enc|
23
+ stmt.bind_param 1, str.encode(enc)
24
+ assert_equal 1, stmt.to_a.length
25
+ stmt.reset!
26
+ end
27
+ end
28
+
29
+ def test_insert_encoding
30
+ str = "foo"
31
+ utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE"
32
+ db = SQLite3::Database.new(':memory:'.encode(utf16))
33
+ db.execute @create
34
+ stmt = db.prepare @insert
35
+
36
+ ['US-ASCII', utf16, 'EUC-JP', 'UTF-8'].each_with_index do |enc,i|
37
+ stmt.bind_param 1, i
38
+ stmt.bind_param 2, str.encode(enc)
39
+ stmt.to_a
40
+ stmt.reset!
41
+ end
42
+
43
+ db.execute('select data from ex').flatten.each do |s|
44
+ assert_equal str, s
45
+ end
46
+ end
47
+
14
48
  def test_default_internal_is_honored
15
49
  warn_before = $-w
16
50
  $-w = false
@@ -21,12 +21,14 @@ class TC_Database_Integration < SQLite3::TestCase
21
21
 
22
22
  def test_table_info_with_defaults_for_version_3_3_8_and_higher
23
23
  @db.transaction do
24
- @db.execute "create table defaults_test ( a string default NULL, b string default 'Hello' )"
24
+ @db.execute "create table defaults_test ( a string default NULL, b string default 'Hello', c string default '--- []\n' )"
25
25
  data = @db.table_info( "defaults_test" )
26
26
  assert_equal({"name" => "a", "type" => "string", "dflt_value" => nil, "notnull" => 0, "cid" => 0, "pk" => 0},
27
27
  data[0])
28
28
  assert_equal({"name" => "b", "type" => "string", "dflt_value" => "Hello", "notnull" => 0, "cid" => 1, "pk" => 0},
29
29
  data[1])
30
+ assert_equal({"name" => "c", "type" => "string", "dflt_value" => "--- []\n", "notnull" => 0, "cid" => 2, "pk" => 0},
31
+ data[2])
30
32
  end
31
33
  end
32
34
 
@@ -236,7 +238,7 @@ class TC_Database_Integration < SQLite3::TestCase
236
238
 
237
239
  def test_execute2_with_block_with_bind_with_match
238
240
  called = 0
239
- @db.execute2( "select * from foo where a = ?", 1 ) do |row|
241
+ @db.execute2( "select * from foo where a = ?", 1 ) do
240
242
  called += 1
241
243
  end
242
244
  assert_equal 2, called
@@ -497,6 +499,10 @@ class TC_Database_Integration < SQLite3::TestCase
497
499
 
498
500
  value = @db.get_first_value( "select accumulate(a) from foo" )
499
501
  assert_equal 6, value
502
+
503
+ # calling #get_first_value twice don't add up to the latest result
504
+ value = @db.get_first_value( "select accumulate(a) from foo" )
505
+ assert_equal 6, value
500
506
  end
501
507
 
502
508
  def test_create_aggregate_with_block
@@ -528,21 +534,36 @@ class TC_Database_Integration < SQLite3::TestCase
528
534
  assert_equal 0, value
529
535
  end
530
536
 
531
- def test_create_aggregate_handler
532
- handler = Class.new do
533
- class << self
534
- def arity; 1; end
535
- def text_rep; SQLite3::Constants::TextRep::ANY; end
536
- def name; "multiply"; end
537
- end
538
- def step(ctx, a)
539
- ctx[:buffer] ||= 1
540
- ctx[:buffer] *= a.to_i
537
+ class AggregateHandler
538
+ class << self
539
+ def arity; 1; end
540
+ def text_rep; SQLite3::Constants::TextRep::ANY; end
541
+ def name; "multiply"; end
542
+ end
543
+ def step(ctx, a)
544
+ ctx[:buffer] ||= 1
545
+ ctx[:buffer] *= a.to_i
546
+ end
547
+ def finalize(ctx); ctx.result = ctx[:buffer]; end
548
+ end
549
+
550
+ def test_aggregate_initialized_twice
551
+ initialized = 0
552
+ handler = Class.new(AggregateHandler) do
553
+ define_method(:initialize) do
554
+ initialized += 1
555
+ super()
541
556
  end
542
- def finalize(ctx); ctx.result = ctx[:buffer]; end
543
557
  end
544
558
 
545
- @db.create_aggregate_handler( handler )
559
+ @db.create_aggregate_handler handler
560
+ @db.get_first_value( "select multiply(a) from foo" )
561
+ @db.get_first_value( "select multiply(a) from foo" )
562
+ assert_equal 2, initialized
563
+ end
564
+
565
+ def test_create_aggregate_handler
566
+ @db.create_aggregate_handler AggregateHandler
546
567
  value = @db.get_first_value( "select multiply(a) from foo" )
547
568
  assert_equal 6, value
548
569
  end
@@ -69,7 +69,7 @@ class TC_Integration_Pending < SQLite3::TestCase
69
69
  end
70
70
  sleep 1
71
71
 
72
- @db.busy_handler do |count|
72
+ @db.busy_handler do
73
73
  handler_call_count += 1
74
74
  false
75
75
  end
data/test/test_sqlite3.rb CHANGED
@@ -5,5 +5,17 @@ module SQLite3
5
5
  def test_libversion
6
6
  assert_not_nil SQLite3.libversion
7
7
  end
8
+
9
+ def test_threadsafe
10
+ assert_not_nil SQLite3.threadsafe
11
+ end
12
+
13
+ def test_threadsafe?
14
+ if SQLite3.threadsafe > 0
15
+ assert SQLite3.threadsafe?
16
+ else
17
+ refute SQLite3.threadsafe?
18
+ end
19
+ end
8
20
  end
9
21
  end
@@ -24,6 +24,19 @@ module SQLite3
24
24
  end
25
25
  end
26
26
 
27
+ def test_insert_duplicate_records
28
+ @db.execute 'CREATE TABLE "things" ("name" varchar(20) CONSTRAINT "index_things_on_name" UNIQUE)'
29
+ stmt = @db.prepare("INSERT INTO things(name) VALUES(?)")
30
+ stmt.execute('ruby')
31
+
32
+ exception = assert_raises(SQLite3::ConstraintException) { stmt.execute('ruby') }
33
+ # SQLite 3.8.2 returns new error message:
34
+ # UNIQUE constraint failed: *table_name*.*column_name*
35
+ # Older versions of SQLite return:
36
+ # column *column_name* is not unique
37
+ assert_match(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/, exception.message)
38
+ end
39
+
27
40
  ###
28
41
  # This method may not exist depending on how sqlite3 was compiled
29
42
  def test_database_name
@@ -227,9 +240,7 @@ module SQLite3
227
240
  stmt.execute('employee-1')
228
241
  stmt.execute('employee-1') rescue SQLite3::ConstraintException
229
242
  stmt.reset!
230
- assert_nothing_raised(SQLite3::ConstraintException) {
231
- stmt.execute('employee-2')
232
- }
243
+ assert stmt.execute('employee-2')
233
244
  end
234
245
 
235
246
  def test_clear_bindings