amalgalite 1.6.0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +49 -0
  3. data/HISTORY.md +346 -0
  4. data/LICENSE +31 -0
  5. data/Manifest.txt +104 -0
  6. data/README.md +65 -0
  7. data/Rakefile +26 -0
  8. data/TODO.md +57 -0
  9. data/bin/amalgalite-pack +147 -0
  10. data/examples/a.rb +9 -0
  11. data/examples/blob.rb +88 -0
  12. data/examples/bootstrap.rb +36 -0
  13. data/examples/define_aggregate.rb +75 -0
  14. data/examples/define_function.rb +104 -0
  15. data/examples/fts5.rb +152 -0
  16. data/examples/gem-db.rb +94 -0
  17. data/examples/require_me.rb +11 -0
  18. data/examples/requires.rb +42 -0
  19. data/examples/schema-info.rb +34 -0
  20. data/ext/amalgalite/c/amalgalite.c +355 -0
  21. data/ext/amalgalite/c/amalgalite.h +151 -0
  22. data/ext/amalgalite/c/amalgalite_blob.c +240 -0
  23. data/ext/amalgalite/c/amalgalite_constants.c +1226 -0
  24. data/ext/amalgalite/c/amalgalite_database.c +1178 -0
  25. data/ext/amalgalite/c/amalgalite_requires_bootstrap.c +282 -0
  26. data/ext/amalgalite/c/amalgalite_statement.c +649 -0
  27. data/ext/amalgalite/c/extconf.rb +62 -0
  28. data/ext/amalgalite/c/gen_constants.rb +330 -0
  29. data/ext/amalgalite/c/notes.txt +134 -0
  30. data/ext/amalgalite/c/sqlite3.c +205352 -0
  31. data/ext/amalgalite/c/sqlite3.h +10727 -0
  32. data/ext/amalgalite/c/sqlite3_options.h +4 -0
  33. data/ext/amalgalite/c/sqlite3ext.h +578 -0
  34. data/lib/amalgalite.rb +51 -0
  35. data/lib/amalgalite/2.0/amalgalite.so +0 -0
  36. data/lib/amalgalite/2.1/amalgalite.so +0 -0
  37. data/lib/amalgalite/2.2/amalgalite.so +0 -0
  38. data/lib/amalgalite/2.3/amalgalite.so +0 -0
  39. data/lib/amalgalite/2.4/amalgalite.so +0 -0
  40. data/lib/amalgalite/aggregate.rb +67 -0
  41. data/lib/amalgalite/blob.rb +186 -0
  42. data/lib/amalgalite/boolean.rb +42 -0
  43. data/lib/amalgalite/busy_timeout.rb +47 -0
  44. data/lib/amalgalite/column.rb +99 -0
  45. data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
  46. data/lib/amalgalite/csv_table_importer.rb +74 -0
  47. data/lib/amalgalite/database.rb +984 -0
  48. data/lib/amalgalite/function.rb +61 -0
  49. data/lib/amalgalite/index.rb +43 -0
  50. data/lib/amalgalite/memory_database.rb +15 -0
  51. data/lib/amalgalite/packer.rb +231 -0
  52. data/lib/amalgalite/paths.rb +80 -0
  53. data/lib/amalgalite/profile_tap.rb +131 -0
  54. data/lib/amalgalite/progress_handler.rb +21 -0
  55. data/lib/amalgalite/requires.rb +151 -0
  56. data/lib/amalgalite/schema.rb +225 -0
  57. data/lib/amalgalite/sqlite3.rb +6 -0
  58. data/lib/amalgalite/sqlite3/constants.rb +95 -0
  59. data/lib/amalgalite/sqlite3/database/function.rb +48 -0
  60. data/lib/amalgalite/sqlite3/database/status.rb +68 -0
  61. data/lib/amalgalite/sqlite3/status.rb +60 -0
  62. data/lib/amalgalite/sqlite3/version.rb +55 -0
  63. data/lib/amalgalite/statement.rb +418 -0
  64. data/lib/amalgalite/table.rb +91 -0
  65. data/lib/amalgalite/taps.rb +2 -0
  66. data/lib/amalgalite/taps/console.rb +27 -0
  67. data/lib/amalgalite/taps/io.rb +71 -0
  68. data/lib/amalgalite/trace_tap.rb +35 -0
  69. data/lib/amalgalite/type_map.rb +63 -0
  70. data/lib/amalgalite/type_maps/default_map.rb +166 -0
  71. data/lib/amalgalite/type_maps/storage_map.rb +38 -0
  72. data/lib/amalgalite/type_maps/text_map.rb +21 -0
  73. data/lib/amalgalite/version.rb +8 -0
  74. data/lib/amalgalite/view.rb +26 -0
  75. data/spec/aggregate_spec.rb +154 -0
  76. data/spec/amalgalite_spec.rb +4 -0
  77. data/spec/blob_spec.rb +78 -0
  78. data/spec/boolean_spec.rb +24 -0
  79. data/spec/busy_handler.rb +157 -0
  80. data/spec/data/iso-3166-country.txt +242 -0
  81. data/spec/data/iso-3166-schema.sql +22 -0
  82. data/spec/data/iso-3166-subcountry.txt +3995 -0
  83. data/spec/data/make-iso-db.sh +12 -0
  84. data/spec/database_spec.rb +508 -0
  85. data/spec/default_map_spec.rb +92 -0
  86. data/spec/function_spec.rb +78 -0
  87. data/spec/integeration_spec.rb +97 -0
  88. data/spec/iso_3166_database.rb +58 -0
  89. data/spec/packer_spec.rb +60 -0
  90. data/spec/paths_spec.rb +28 -0
  91. data/spec/progress_handler_spec.rb +91 -0
  92. data/spec/requires_spec.rb +54 -0
  93. data/spec/rtree_spec.rb +66 -0
  94. data/spec/schema_spec.rb +131 -0
  95. data/spec/spec_helper.rb +48 -0
  96. data/spec/sqlite3/constants_spec.rb +108 -0
  97. data/spec/sqlite3/database_status_spec.rb +36 -0
  98. data/spec/sqlite3/status_spec.rb +22 -0
  99. data/spec/sqlite3/version_spec.rb +28 -0
  100. data/spec/sqlite3_spec.rb +53 -0
  101. data/spec/statement_spec.rb +168 -0
  102. data/spec/storage_map_spec.rb +38 -0
  103. data/spec/tap_spec.rb +57 -0
  104. data/spec/text_map_spec.rb +20 -0
  105. data/spec/type_map_spec.rb +14 -0
  106. data/spec/version_spec.rb +8 -0
  107. data/tasks/custom.rake +102 -0
  108. data/tasks/default.rake +240 -0
  109. data/tasks/extension.rake +38 -0
  110. data/tasks/this.rb +208 -0
  111. metadata +318 -0
@@ -0,0 +1,38 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+
6
+ module Amalgalite::TypeMaps
7
+ ##
8
+ # An Amalagliate TypeMap that has a one-to-one conversion between SQLite types
9
+ # and Ruby classes
10
+ #
11
+ class StorageMap < ::Amalgalite::TypeMap
12
+ ##
13
+ # A straight logical mapping (for me at least) of basic Ruby classes to SQLite types, if
14
+ # nothing can be found then default to TEXT.
15
+ #
16
+ def bind_type_of( obj )
17
+ case obj
18
+ when Float
19
+ ::Amalgalite::SQLite3::Constants::DataType::FLOAT
20
+ when Integer
21
+ ::Amalgalite::SQLite3::Constants::DataType::INTEGER
22
+ when NilClass
23
+ ::Amalgalite::SQLite3::Constants::DataType::NULL
24
+ when ::Amalgalite::Blob
25
+ ::Amalgalite::SQLite3::Constants::DataType::BLOB
26
+ else
27
+ ::Amalgalite::SQLite3::Constants::DataType::TEXT
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Do no mapping, just return the value as it was retrieved from SQLite.
33
+ #
34
+ def result_value_of( delcared_type, value )
35
+ return value
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ #
6
+
7
+ module Amalgalite::TypeMaps
8
+ ##
9
+ # An Amalagliate TypeMap that converts both bind parameters and result
10
+ # parameters to a String, no matter what.
11
+ #
12
+ class TextMap < ::Amalgalite::TypeMap
13
+ def bind_type_of( obj )
14
+ return ::Amalgalite::SQLite3::Constants::DataType::TEXT
15
+ end
16
+
17
+ def result_value_of( delcared_type, value )
18
+ return value.to_s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for licensing details
4
+ #++
5
+
6
+ module Amalgalite
7
+ VERSION = "1.6.0"
8
+ end
@@ -0,0 +1,26 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+
6
+ module Amalgalite
7
+ #
8
+ # a class representing the meta information about an SQLite view
9
+ #
10
+ class View
11
+ # the schame this view is assciated with
12
+ attr_accessor :schema
13
+
14
+ # the table name
15
+ attr_reader :name
16
+
17
+ # the original sql that was used to create this table
18
+ attr_reader :sql
19
+
20
+ def initialize( name, sql )
21
+ @name = name
22
+ @sql = sql
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ class AggregateTest1 < ::Amalgalite::Aggregate
4
+ def initialize
5
+ @name = 'atest1'
6
+ @arity = -1
7
+ @count = 0
8
+ end
9
+ def step( *args )
10
+ @count += 1
11
+ end
12
+ def finalize
13
+ return @count
14
+ end
15
+ end
16
+
17
+
18
+ describe "Aggregate SQL Functions" do
19
+
20
+ it "must have a finalize method implemented" do
21
+ ag = ::Amalgalite::Aggregate.new
22
+ lambda { ag.finalize }.should raise_error( NotImplementedError, /Aggregate#finalize must be implemented/ )
23
+ end
24
+
25
+ it "can define a custom SQL aggregate as a class with N params" do
26
+ @iso_db.define_aggregate("atest1", AggregateTest1 )
27
+ r = @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country")
28
+ r.first['a'].should eql(r.first['c'])
29
+ r.first['a'].should eql(242)
30
+ end
31
+
32
+ it "can remove a custom SQL aggregate by class" do
33
+ @iso_db.define_aggregate("atest1", AggregateTest1 )
34
+ @iso_db.aggregates.size.should eql(1)
35
+ r = @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country")
36
+ r.first['a'].should eql(r.first['c'])
37
+ r.first['a'].should eql(242)
38
+ @iso_db.remove_aggregate( "atest1", AggregateTest1 )
39
+ @iso_db.aggregates.size.should eql(0)
40
+ lambda{ @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country") }.should raise_error(::Amalgalite::SQLite3::Error, /no such function: atest1/ )
41
+ end
42
+
43
+ it "can remove a custom SQL aggregate by arity" do
44
+ @iso_db.define_aggregate("atest1", AggregateTest1 )
45
+ @iso_db.aggregates.size.should eql(1)
46
+ r = @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country")
47
+ r.first['a'].should eql(r.first['c'])
48
+ r.first['a'].should eql(242)
49
+ @iso_db.remove_aggregate( "atest1", -1)
50
+ @iso_db.aggregates.size.should eql(0)
51
+ lambda{ @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country") }.should raise_error(::Amalgalite::SQLite3::Error, /no such function: atest1/ )
52
+ end
53
+
54
+ it "can remove all custom SQL aggregates with the same name" do
55
+ class AT2 < AggregateTest1
56
+ def arity() 1; end
57
+ end
58
+ @iso_db.define_aggregate("atest1", AggregateTest1 )
59
+ @iso_db.define_aggregate("atest1", AT2)
60
+ @iso_db.aggregates.size.should eql(2)
61
+ r = @iso_db.execute("SELECT atest1(id,name) as a, atest1(id), count(*) as c FROM country")
62
+ r.first['a'].should eql(r.first['c'])
63
+ r.first['a'].should eql(242)
64
+ @iso_db.remove_aggregate( "atest1" )
65
+ @iso_db.aggregates.size.should eql(0)
66
+ lambda{ @iso_db.execute("SELECT atest1(id,name) as a, count(*) as c FROM country") }.should raise_error(::Amalgalite::SQLite3::Error, /no such function: atest1/ )
67
+ end
68
+
69
+
70
+
71
+ it "does not allow mixing of arbitrary and mandatory arguments to an SQL function" do
72
+ class AggregateTest2 < AggregateTest1
73
+ def name() "atest2"; end
74
+ def arity() -2; end
75
+ end
76
+ lambda { @iso_db.define_aggregate("atest2", AggregateTest2 ) }.should raise_error( ::Amalgalite::Database::AggregateError,
77
+ /Use only mandatory or arbitrary parameters in an SQL Aggregate, not both/ )
78
+ end
79
+
80
+ it "does not allow outrageous arity" do
81
+ class AggregateTest3 < AggregateTest1
82
+ def name() "atest3"; end
83
+ def arity() 128; end
84
+ end
85
+ lambda { @iso_db.define_aggregate("atest3", AggregateTest3 ) }.should raise_error( ::Amalgalite::SQLite3::Error, /SQLITE_ERROR .* Library used incorrectly/ )
86
+ end
87
+
88
+ it "does not allow registering a function which does not match the defined name " do
89
+ class AggregateTest4 < AggregateTest1
90
+ def name() "name_mismatch"; end
91
+ end
92
+ lambda { @iso_db.define_aggregate("atest4", AggregateTest4 ) }.should raise_error( ::Amalgalite::Database::AggregateError,
93
+ /Aggregate implementation name 'name_mismatch' does not match defined name 'atest4'/)
94
+ end
95
+
96
+ it "handles an error being thrown during the step function" do
97
+ class AggregateTest5 < AggregateTest1
98
+ def initialize
99
+ @name = "atest5"
100
+ @arity = -1
101
+ @count = 0
102
+ end
103
+
104
+ def step( *args )
105
+ @count += 1
106
+ if @count > 50 then
107
+ raise "Stepwise error!" if @count > 50
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ @iso_db.define_aggregate( "atest5", AggregateTest5 )
114
+ lambda { @iso_db.execute( "SELECT atest5(*) AS a FROM country" ) }.should raise_error( ::Amalgalite::SQLite3::Error, /Stepwise error!/ )
115
+ end
116
+
117
+ it "handles an error being thrown during the finalize function" do
118
+ class AggregateTest6 < AggregateTest1
119
+ def initialize
120
+ @name = "atest6"
121
+ @count = 0
122
+ @arity = -1
123
+ end
124
+ def finalize
125
+ raise "Finalize error!"
126
+ end
127
+ end
128
+ @iso_db.define_aggregate( "atest6", AggregateTest6 )
129
+ lambda { @iso_db.execute( "SELECT atest6(*) AS a FROM country" ) }.should raise_error( ::Amalgalite::SQLite3::Error, /Finalize error!/ )
130
+ end
131
+
132
+ it "handles an error being thrown during initialization in the C extension" do
133
+ class AggregateTest7 < AggregateTest1
134
+ def self.called?
135
+ if @called then
136
+ raise "Initialization error!"
137
+ else
138
+ @called = true
139
+ end
140
+ end
141
+
142
+ def initialize
143
+ super
144
+ @name = "atest7"
145
+ @count = 0
146
+ @arity = -1
147
+ self.class.called?
148
+ end
149
+ end
150
+ @iso_db.define_aggregate( "atest7", AggregateTest7 )
151
+ lambda { @iso_db.execute( "SELECT atest7(*) AS a FROM country" ) }.should raise_error( ::Amalgalite::SQLite3::Error, /Initialization error!/ )
152
+ end
153
+ end
154
+
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Amalgalite do
4
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe Amalgalite::Blob do
4
+
5
+ before(:each) do
6
+ @blob_db_name = File.join(File.dirname( __FILE__ ), "blob.db")
7
+ File.unlink @blob_db_name if File.exist?( @blob_db_name )
8
+ @db = Amalgalite::Database.new( @blob_db_name )
9
+ @schema_sql = <<-SQL
10
+ CREATE TABLE blobs(
11
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
12
+ name VARCHAR(128) NOT NULL UNIQUE,
13
+ data TEXT );
14
+ SQL
15
+ @db.execute( @schema_sql )
16
+ @country_data_file = Amalgalite::Iso3166Database.country_data_file
17
+ @junk_file = File.join( File.dirname(__FILE__), "test_output")
18
+ end
19
+
20
+ after(:each) do
21
+ @db.close
22
+ File.unlink @blob_db_name if File.exist?( @blob_db_name )
23
+ File.unlink @junk_file if File.exist?( @junk_file )
24
+ end
25
+
26
+ { :file => Amalgalite::Iso3166Database.country_data_file,
27
+ :string => IO.read( Amalgalite::Iso3166Database.country_data_file),
28
+ :io => StringIO.new( IO.read( Amalgalite::Iso3166Database.country_data_file) ) }.each_pair do |style, data |
29
+ describe "inserts a blob from a #{style}" do
30
+ before(:each) do
31
+ column = @db.schema.tables['blobs'].columns['data']
32
+ @db.execute("INSERT INTO blobs(name, data) VALUES ($name, $data)",
33
+ { "$name" => @country_data_file,
34
+ "$data" => Amalgalite::Blob.new( style => data,
35
+ :column => column ) } )
36
+ @db.execute("VACUUM")
37
+ end
38
+
39
+ after(:each) do
40
+ @db.execute("DELETE FROM blobs")
41
+ data.rewind if data.respond_to?( :rewind )
42
+ end
43
+
44
+ it "and retrieves the data as a single value" do
45
+ all_rows = @db.execute("SELECT name,data FROM blobs")
46
+ all_rows.size.should eql(1)
47
+ all_rows.first['name'].should eql(@country_data_file)
48
+ all_rows.first['data'].should_not be_incremental
49
+ all_rows.first['data'].to_string_io.string.should eql(IO.read( @country_data_file ))
50
+ end
51
+
52
+ it "and retrieves the data using incremental IO" do
53
+ all_rows = @db.execute("SELECT * FROM blobs")
54
+ all_rows.size.should eql(1)
55
+ all_rows.first['name'].should eql(@country_data_file)
56
+ all_rows.first['data'].should be_incremental
57
+ all_rows.first['data'].to_string_io.string.should eql(IO.read( @country_data_file ))
58
+ end
59
+
60
+ it "writes the data to a file " do
61
+ all_rows = @db.execute("SELECT * FROM blobs")
62
+ all_rows.size.should eql(1)
63
+ all_rows.first['name'].should eql(@country_data_file)
64
+ all_rows.first['data'].should be_incremental
65
+ all_rows.first['data'].write_to_file( @junk_file )
66
+ IO.read( @junk_file).should eql(IO.read( @country_data_file ))
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+
73
+ it "raises an error if initialized incorrectly" do
74
+ lambda{ Amalgalite::Blob.new( :file => "/dev/null", :string => "foo" ) }.should raise_error( Amalgalite::Blob::Error )
75
+ end
76
+ end
77
+
78
+
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ require 'amalgalite'
4
+ require 'amalgalite/boolean'
5
+
6
+ describe Amalgalite::Boolean do
7
+ %w[ True Y Yes T 1 ].each do |v|
8
+ it "converts #{v} to true" do
9
+ Amalgalite::Boolean.to_bool(v).should == true
10
+ end
11
+ end
12
+
13
+ %w[ False F f No n 0 ].each do |v|
14
+ it "converts #{v} to false " do
15
+ Amalgalite::Boolean.to_bool(v).should == false
16
+ end
17
+ end
18
+
19
+ %w[ other things nil ].each do |v|
20
+ it "converts #{v} to nil" do
21
+ Amalgalite::Boolean.to_bool(v).should == nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ class BusyHandlerTest < Amalgalite::BusyHandler
4
+ attr_accessor :call_count
5
+ def initialize( max = 5 )
6
+ @max = max
7
+ @call_count = 0
8
+ end
9
+
10
+ def call( c )
11
+ @call_count += 1
12
+ if call_count >= @max then
13
+ return false
14
+ end
15
+ return true
16
+ end
17
+ end
18
+
19
+ describe "Busy Handlers" do
20
+ before(:each) do
21
+ @read_db = Amalgalite::Database.new( @iso_db_path )
22
+ @write_db = Amalgalite::Database.new( @iso_db_path )
23
+ end
24
+
25
+ after(:each) do
26
+ @write_db.close
27
+ @read_db.close
28
+ end
29
+
30
+ it "raises NotImplemented if #call is not overwritten" do
31
+ bh = ::Amalgalite::BusyHandler.new
32
+ lambda { bh.call( 42 ) }.should raise_error( ::NotImplementedError, /The busy handler call\(N\) method must be implemented/ )
33
+ end
34
+
35
+ it "can be registered as block" do
36
+ call_count = 0
37
+ @write_db.busy_handler do |x|
38
+ call_count = x
39
+ if call_count >= 20 then
40
+ false
41
+ else
42
+ true
43
+ end
44
+ end
45
+
46
+ # put a read lock on the database
47
+ @read_db.transaction( "DEFERRED" )
48
+
49
+ # put a read lock on the database, but want to go to an exclusive
50
+ @write_db.transaction( "IMMEDIATE" )
51
+
52
+ # do a read operation
53
+ @read_db.execute("SELECT count(*) FROM subcountry")
54
+
55
+ # attempt to do a write operation and commit it
56
+ @write_db.execute("DELETE FROM subcountry")
57
+ lambda { @write_db.execute("COMMIT"); }.should raise_error( ::Amalgalite::SQLite3::Error, /database is locked/ )
58
+ call_count.should == 20
59
+ end
60
+
61
+ it "can be registered as lambda" do
62
+ call_count = 0
63
+ callable = lambda do |x|
64
+ call_count = x
65
+ if call_count >= 40 then
66
+ false
67
+ else
68
+ true
69
+ end
70
+ end
71
+
72
+ @write_db.busy_handler( callable )
73
+
74
+ # put a read lock on the database
75
+ @read_db.transaction( "DEFERRED" )
76
+
77
+ # put a read lock on the database, but want to go to an exclusive
78
+ @write_db.transaction( "IMMEDIATE" )
79
+
80
+ # do a read operation
81
+ @read_db.execute("SELECT count(*) FROM subcountry")
82
+
83
+ # attempt to do a write operation and commit it
84
+ @write_db.execute("DELETE FROM subcountry")
85
+ lambda { @write_db.execute("COMMIT"); }.should raise_error( ::Amalgalite::SQLite3::Error, /database is locked/ )
86
+ call_count.should == 40
87
+ end
88
+
89
+ it "can be registered as a class" do
90
+ h = BusyHandlerTest.new( 10 )
91
+ @write_db.busy_handler( h )
92
+
93
+ # put a read lock on the database
94
+ @read_db.transaction( "DEFERRED" )
95
+
96
+ # put a read lock on the database, but want to go to an exclusive
97
+ @write_db.transaction( "IMMEDIATE" )
98
+
99
+ # do a read operation
100
+ @read_db.execute("SELECT count(*) FROM subcountry")
101
+
102
+ # attempt to do a write operation and commit it
103
+ @write_db.execute("DELETE FROM subcountry")
104
+ lambda { @write_db.execute("COMMIT"); }.should raise_error( ::Amalgalite::SQLite3::Error, /database is locked/ )
105
+ h.call_count.should == 10
106
+ end
107
+
108
+ it "has a default timeout class available " do
109
+ to = ::Amalgalite::BusyTimeout.new( 5, 10 )
110
+ @write_db.busy_handler( to )
111
+
112
+ # put a read lock on the database
113
+ @read_db.transaction( "DEFERRED" )
114
+
115
+ # put a read lock on the database, but want to go to an exclusive
116
+ @write_db.transaction( "IMMEDIATE" )
117
+
118
+ # do a read operation
119
+ @read_db.execute("SELECT count(*) FROM subcountry")
120
+
121
+ # attempt to do a write operation and commit it
122
+ @write_db.execute("DELETE FROM subcountry")
123
+ before = Time.now
124
+ lambda { @write_db.execute("COMMIT"); }.should raise_error( ::Amalgalite::SQLite3::Error, /database is locked/ )
125
+ after = Time.now
126
+ to.call_count.should > 5
127
+ (after - before).should > 0.05
128
+ end
129
+
130
+ it "cannot register a block with the wrong arity" do
131
+ lambda do
132
+ @write_db.define_busy_handler { |x,y| puts "What!" }
133
+ end.should raise_error( ::Amalgalite::Database::BusyHandlerError, /A busy handler expects 1 and only 1 argument/ )
134
+ end
135
+
136
+ it "can remove a busy handler" do
137
+ bht = BusyHandlerTest.new
138
+
139
+ @write_db.busy_handler( bht )
140
+
141
+ # put a read lock on the database
142
+ @read_db.transaction( "DEFERRED" )
143
+
144
+ # put a read lock on the database, but want to go to an exclusive
145
+ @write_db.transaction( "IMMEDIATE" )
146
+
147
+ # do a read operation
148
+ @read_db.execute("SELECT count(*) FROM subcountry")
149
+
150
+ # attempt to do a write operation and commit it
151
+ @write_db.execute("DELETE FROM subcountry")
152
+ @write_db.remove_busy_handler
153
+ lambda { @write_db.execute("COMMIT"); }.should raise_error( ::Amalgalite::SQLite3::Error, /database is locked/ )
154
+ bht.call_count.should == 0
155
+ end
156
+
157
+ end