amalgalite 0.10.1-x86-mingw32

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