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.
- data/HISTORY +201 -0
- data/LICENSE +29 -0
- data/README +51 -0
- data/bin/amalgalite-pack +126 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +88 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/gem-db.rb +94 -0
- data/examples/gems.db +0 -0
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +42 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite/amalgalite3.c +290 -0
- data/ext/amalgalite/amalgalite3.h +151 -0
- data/ext/amalgalite/amalgalite3_blob.c +240 -0
- data/ext/amalgalite/amalgalite3_constants.c +221 -0
- data/ext/amalgalite/amalgalite3_database.c +1148 -0
- data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite/amalgalite3_statement.c +639 -0
- data/ext/amalgalite/extconf.rb +36 -0
- data/ext/amalgalite/gen_constants.rb +130 -0
- data/ext/amalgalite/sqlite3.c +106729 -0
- data/ext/amalgalite/sqlite3.h +5626 -0
- data/ext/amalgalite/sqlite3_options.h +4 -0
- data/ext/amalgalite/sqlite3ext.h +380 -0
- data/gemspec.rb +60 -0
- data/lib/amalgalite.rb +43 -0
- data/lib/amalgalite/1.8/amalgalite3.so +0 -0
- data/lib/amalgalite/1.9/amalgalite3.so +0 -0
- data/lib/amalgalite/aggregate.rb +67 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/busy_timeout.rb +47 -0
- data/lib/amalgalite/column.rb +97 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
- data/lib/amalgalite/database.rb +947 -0
- data/lib/amalgalite/function.rb +61 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/packer.rb +226 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +131 -0
- data/lib/amalgalite/progress_handler.rb +21 -0
- data/lib/amalgalite/requires.rb +120 -0
- data/lib/amalgalite/schema.rb +191 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +80 -0
- data/lib/amalgalite/sqlite3/database/function.rb +48 -0
- data/lib/amalgalite/sqlite3/database/status.rb +68 -0
- data/lib/amalgalite/sqlite3/status.rb +60 -0
- data/lib/amalgalite/sqlite3/version.rb +37 -0
- data/lib/amalgalite/statement.rb +414 -0
- data/lib/amalgalite/table.rb +90 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +40 -0
- data/lib/amalgalite/type_maps/text_map.rb +22 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/spec/aggregate_spec.rb +169 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/busy_handler.rb +165 -0
- data/spec/database_spec.rb +494 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/function_spec.rb +94 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/packer_spec.rb +60 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +105 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/rtree_spec.rb +71 -0
- data/spec/schema_spec.rb +120 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +161 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +15 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +77 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/extension.rake +141 -0
- data/tasks/rspec.rake +33 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +237 -0
@@ -0,0 +1,494 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require File.expand_path( File.join( File.dirname(__FILE__), 'spec_helper'))
|
4
|
+
|
5
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
|
6
|
+
require 'amalgalite'
|
7
|
+
require 'amalgalite/taps/io'
|
8
|
+
require 'amalgalite/taps/console'
|
9
|
+
require 'amalgalite/database'
|
10
|
+
|
11
|
+
describe Amalgalite::Database do
|
12
|
+
before(:each) do
|
13
|
+
@schema = IO.read( SpecInfo.test_schema_file )
|
14
|
+
@iso_db_file = SpecInfo.make_iso_db
|
15
|
+
@iso_db = Amalgalite::Database.new( SpecInfo.make_iso_db )
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:each) do
|
19
|
+
File.unlink SpecInfo.test_db if File.exist?( SpecInfo.test_db )
|
20
|
+
@iso_db.close
|
21
|
+
File.unlink @iso_db_file if File.exist?( @iso_db_file )
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can create a new database" do
|
25
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
26
|
+
db.instance_of?(Amalgalite::Database)
|
27
|
+
db.api.instance_of?(Amalgalite::SQLite3::Database)
|
28
|
+
File.exist?( SpecInfo.test_db ).should eql(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates a new UTF-8 database (need exec to check pragma encoding)" do
|
32
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
33
|
+
db.execute_batch( @schema );
|
34
|
+
db.should_not be_utf16
|
35
|
+
db.encoding.should eql("UTF-8")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "creates a new UTF-16 database (need exec to check pragma encoding)"
|
39
|
+
|
40
|
+
it "raises an error if the file does not exist and the database is opened with a non-create mode" do
|
41
|
+
lambda { Amalgalite::Database.new( SpecInfo.test_db, "r") }.should raise_error(Amalgalite::SQLite3::Error)
|
42
|
+
lambda { Amalgalite::Database.new( SpecInfo.test_db, "r+") }.should raise_error(Amalgalite::SQLite3::Error)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises an error if an invalid mode is used" do
|
46
|
+
lambda { Amalgalite::Database.new( SpecInfo.test_db, "b+" ) }.should raise_error(Amalgalite::Database::InvalidModeError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can be in autocommit mode, and is by default" do
|
50
|
+
@iso_db.autocommit?.should eql(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "reports false for autocommit? when inside a transaction" do
|
54
|
+
@iso_db.execute(" BEGIN ")
|
55
|
+
@iso_db.autocommit?.should eql(false)
|
56
|
+
@iso_db.in_transaction?.should eql(true)
|
57
|
+
@iso_db.execute(" COMMIT")
|
58
|
+
@iso_db.in_transaction?.should eql(false)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "prepares a statment" do
|
62
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
63
|
+
stmt = db.prepare("SELECT datetime()")
|
64
|
+
stmt.instance_of?(Amalgalite::Statement)
|
65
|
+
stmt.api.instance_of?(Amalgalite::SQLite3::Statement)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "raises an error on invalid syntax when preparing a bad sql statement" do
|
69
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
70
|
+
lambda { db.prepare("SELECT nothing FROM stuf") }.should raise_error(Amalgalite::SQLite3::Error)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "closes normally" do
|
74
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
75
|
+
lambda { db.close }.should_not raise_error( Amalgalite::SQLite3::Error )
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns the id of the last inserted row" do
|
79
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
80
|
+
db.last_insert_rowid.should eql(0)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "is in autocommit mode by default" do
|
84
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
85
|
+
db.should be_autocommit
|
86
|
+
end
|
87
|
+
|
88
|
+
it "report the number of rows changed with an insert" do
|
89
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
90
|
+
db.execute_batch <<-sql
|
91
|
+
CREATE TABLE t1( x );
|
92
|
+
INSERT INTO t1( x ) values ( 1 );
|
93
|
+
INSERT INTO t1( x ) values ( 2 );
|
94
|
+
INSERT INTO t1( x ) values ( 3 );
|
95
|
+
sql
|
96
|
+
|
97
|
+
db.row_changes.should eql(1)
|
98
|
+
db.total_changes.should eql(3)
|
99
|
+
db.close
|
100
|
+
end
|
101
|
+
|
102
|
+
it "reports the number of rows deleted" do
|
103
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
104
|
+
db.execute_batch <<-sql
|
105
|
+
CREATE TABLE t1( x );
|
106
|
+
INSERT INTO t1( x ) values ( 1 );
|
107
|
+
INSERT INTO t1( x ) values ( 2 );
|
108
|
+
INSERT INTO t1( x ) values ( 3 );
|
109
|
+
DELETE FROM t1 where x < 3;
|
110
|
+
sql
|
111
|
+
db.row_changes.should eql(2)
|
112
|
+
db.close
|
113
|
+
end
|
114
|
+
|
115
|
+
it "can immediately execute an sql statement " do
|
116
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
117
|
+
db.execute( "CREATE TABLE t1( x, y, z )" ).should be_empty
|
118
|
+
end
|
119
|
+
|
120
|
+
it "can execute a batch of commands" do
|
121
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
122
|
+
db.execute_batch( @schema ).should eql(5)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns an empty array when there are no results" do
|
126
|
+
row = @iso_db.execute("SELECT * from subcountry where country = 'Antarctica'")
|
127
|
+
row.should be_empty
|
128
|
+
end
|
129
|
+
|
130
|
+
it "traces the execution of code" do
|
131
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
132
|
+
sql = "CREATE TABLE trace_test( x, y, z)"
|
133
|
+
s = db.trace_tap = ::Amalgalite::Taps::StringIO.new
|
134
|
+
db.execute( sql )
|
135
|
+
db.trace_tap.string.should eql("registered as trace tap\n#{sql}\n")
|
136
|
+
db.trace_tap = nil
|
137
|
+
s.string.should eql("registered as trace tap\n#{sql}\nunregistered as trace tap\n")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "raises an exception if the wrong type of object is used for tracing" do
|
141
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
142
|
+
lambda { db.trace_tap = Object.new }.should raise_error(Amalgalite::Error)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "raises an exception if the wrong type of object is used for profile" do
|
146
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
147
|
+
lambda { db.profile_tap = Object.new }.should raise_error(Amalgalite::Error)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "profiles the execution of code" do
|
151
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
152
|
+
s = db.profile_tap = ::Amalgalite::Taps::StringIO.new
|
153
|
+
db.execute_batch( @schema )
|
154
|
+
db.profile_tap.samplers.size.should eql(6)
|
155
|
+
db.profile_tap = nil
|
156
|
+
s.string.should =~ /unregistered as profile tap/m
|
157
|
+
end
|
158
|
+
|
159
|
+
it "#execute yields each row when called with a block" do
|
160
|
+
count = 0
|
161
|
+
@iso_db.execute( "SELECT * FROM country LIMIT 10") do |row|
|
162
|
+
count += 1
|
163
|
+
end
|
164
|
+
count.should eql(10)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "#pragma yields each row when called with a block" do
|
168
|
+
count = 0
|
169
|
+
@iso_db.pragma( "index_info( subcountry_country )" ) do |row|
|
170
|
+
count += 1
|
171
|
+
end
|
172
|
+
count.should eql(1)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "can use something that responds to 'write' as a tap" do
|
176
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
177
|
+
s2 = db.trace_tap = StringIO.new
|
178
|
+
s2.string.should eql("registered as trace tap")
|
179
|
+
end
|
180
|
+
|
181
|
+
it "can clear all registered taps" do
|
182
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
183
|
+
s = db.profile_tap = ::Amalgalite::Taps::StringIO.new
|
184
|
+
db.trace_tap = s
|
185
|
+
db.execute_batch( @schema )
|
186
|
+
db.profile_tap.samplers.size.should eql(6)
|
187
|
+
db.clear_taps!
|
188
|
+
s.string.should =~ /unregistered as trace tap/m
|
189
|
+
s.string.should =~ /unregistered as profile tap/m
|
190
|
+
end
|
191
|
+
|
192
|
+
it "allows nested transactions even if SQLite under the covers does not" do
|
193
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
194
|
+
r = db.transaction do |db2|
|
195
|
+
r2 = db.transaction { 42 }
|
196
|
+
r2.should eql(42)
|
197
|
+
r2
|
198
|
+
end
|
199
|
+
r.should eql(42)
|
200
|
+
end
|
201
|
+
|
202
|
+
%w[ transaction deferred_transaction immediate_transaction exclusive_transaction ].each do |trans|
|
203
|
+
it "returns the result of the #{trans} when a block is yielded" do
|
204
|
+
db = Amalgalite::Database.new( SpecInfo.test_db )
|
205
|
+
(db.send( trans ){ 42 }).should eql(42)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "#reload_schema!" do
|
210
|
+
@iso_db = Amalgalite::Database.new( SpecInfo.make_iso_db )
|
211
|
+
schema = @iso_db.schema
|
212
|
+
schema.instance_of?( Amalgalite::Schema ).should eql(true)
|
213
|
+
s2 = @iso_db.reload_schema!
|
214
|
+
s2.object_id.should_not eql(schema.object_id)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "can rollback a transaction" do
|
218
|
+
@iso_db.transaction
|
219
|
+
r = @iso_db.execute("SELECT count(1) as cnt FROM country");
|
220
|
+
r.first['cnt'].should eql(242)
|
221
|
+
@iso_db.execute("DELETE FROM country")
|
222
|
+
r = @iso_db.execute("SELECT count(1) as cnt FROM country");
|
223
|
+
r.first['cnt'].should eql(0)
|
224
|
+
@iso_db.rollback
|
225
|
+
|
226
|
+
r = @iso_db.execute("SELECT count(1) as cnt FROM country");
|
227
|
+
r.first['cnt'].should eql(242)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "rolls back if an exception happens during a transaction block" do
|
231
|
+
begin
|
232
|
+
@iso_db.transaction do |db|
|
233
|
+
r = db.execute("SELECT count(1) as cnt FROM country");
|
234
|
+
r.first['cnt'].should eql(242)
|
235
|
+
db.execute("DELETE FROM country")
|
236
|
+
db.in_transaction?.should eql(true)
|
237
|
+
raise "testing rollback"
|
238
|
+
end
|
239
|
+
rescue => e
|
240
|
+
@iso_db.in_transaction?.should eql(false)
|
241
|
+
@iso_db.execute("SELECT count(1) as cnt FROM country").first['cnt'].should eql(242)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "commits if an exception happens during a transaction block but is rescued within the block" do
|
246
|
+
@iso_db.transaction do |db|
|
247
|
+
begin
|
248
|
+
r = db.execute("SELECT count(1) as cnt FROM country");
|
249
|
+
r.first['cnt'].should eql(242)
|
250
|
+
db.execute("DELETE FROM country")
|
251
|
+
db.in_transaction?.should eql(true)
|
252
|
+
raise "testing rollback"
|
253
|
+
rescue => e
|
254
|
+
e.message.should == "testing rollback"
|
255
|
+
end
|
256
|
+
$!.should == nil
|
257
|
+
end
|
258
|
+
@iso_db.in_transaction?.should eql(false)
|
259
|
+
@iso_db.first_value_from("select count(1) as cnt from country").should eql(0)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "does not reraise an exception that exits before the transaction starts" do
|
263
|
+
class MyExceptionTest < RuntimeError; end
|
264
|
+
db = Amalgalite::Database.new( ":memory:" )
|
265
|
+
|
266
|
+
lambda {
|
267
|
+
begin
|
268
|
+
raise MyExceptionTest, "James pointed this out"
|
269
|
+
rescue MyExceptionTest
|
270
|
+
db.transaction("EXCLUSIVE") { }
|
271
|
+
end
|
272
|
+
}.should_not raise_error( MyExceptionTest )
|
273
|
+
end
|
274
|
+
|
275
|
+
describe "#define_function" do
|
276
|
+
it "does not allow mixing of arbitrary and mandatory arguments to an SQL function" do
|
277
|
+
class FunctionTest2 < ::Amalgalite::Function
|
278
|
+
def initialize
|
279
|
+
super( 'ftest2', -2 )
|
280
|
+
end
|
281
|
+
def call( a, *args ); end
|
282
|
+
end
|
283
|
+
lambda { @iso_db.define_function("ftest2", FunctionTest2.new ) }.should raise_error( ::Amalgalite::Database::FunctionError )
|
284
|
+
end
|
285
|
+
|
286
|
+
it "does not allow outrageous arity" do
|
287
|
+
class FunctionTest3 < ::Amalgalite::Function
|
288
|
+
def initialize
|
289
|
+
super( 'ftest3', 128 )
|
290
|
+
end
|
291
|
+
def call( *args) ; end
|
292
|
+
end
|
293
|
+
lambda { @iso_db.define_function("ftest3", FunctionTest3.new ) }.should raise_error( ::Amalgalite::SQLite3::Error )
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
describe "#remove_function" do
|
299
|
+
it "unregisters a single function by name and arity" do
|
300
|
+
@iso_db.define_function( "rtest1" ) do
|
301
|
+
"rtest1 called"
|
302
|
+
end
|
303
|
+
|
304
|
+
@iso_db.functions.size.should eql(1 )
|
305
|
+
|
306
|
+
r = @iso_db.execute( "select rtest1() AS r" )
|
307
|
+
r.first['r'].should eql("rtest1 called")
|
308
|
+
#@iso_db.remove_function("rtest1", -1)
|
309
|
+
# the arity of rtest1 is different in 1.9 vs. 1.8
|
310
|
+
@iso_db.remove_function("rtest1")
|
311
|
+
|
312
|
+
lambda { @iso_db.execute( "select rtest1() as r" )}.should raise_error( ::Amalgalite::SQLite3::Error, /no such function: rtest1/ )
|
313
|
+
@iso_db.functions.size.should eql(0)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "unregisters a function by instances" do
|
317
|
+
class FunctionTest5 < ::Amalgalite::Function
|
318
|
+
def initialize
|
319
|
+
super( 'ftest5', 0)
|
320
|
+
end
|
321
|
+
def call( *args) "ftest5 called"; end
|
322
|
+
end
|
323
|
+
@iso_db.define_function("ftest5", FunctionTest5.new )
|
324
|
+
@iso_db.functions.size.should eql(1)
|
325
|
+
r = @iso_db.execute( "select ftest5() AS r" )
|
326
|
+
r.first['r'].should eql("ftest5 called")
|
327
|
+
@iso_db.remove_function("ftest5", FunctionTest5.new )
|
328
|
+
lambda { @iso_db.execute( "select ftest5() as r" )}.should raise_error( ::Amalgalite::SQLite3::Error, /no such function: ftest5/ )
|
329
|
+
@iso_db.functions.size.should eql(0)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "unregisters all functions with the same name" do
|
333
|
+
@iso_db.function( "rtest" ) do |x|
|
334
|
+
"rtest #{x} called"
|
335
|
+
end
|
336
|
+
|
337
|
+
@iso_db.function( "rtest" ) do ||
|
338
|
+
"rtest/0 called"
|
339
|
+
end
|
340
|
+
|
341
|
+
@iso_db.functions.size.should eql(2)
|
342
|
+
r = @iso_db.execute( "select rtest(1) AS r")
|
343
|
+
r.first['r'].should eql("rtest 1 called")
|
344
|
+
r = @iso_db.execute( "select rtest() AS r")
|
345
|
+
r.first['r'].should eql("rtest/0 called")
|
346
|
+
@iso_db.remove_function( 'rtest' )
|
347
|
+
lambda { @iso_db.execute( "select rtest(1) AS r") }.should raise_error( ::Amalgalite::SQLite3::Error )
|
348
|
+
lambda { @iso_db.execute( "select rtest() AS r") }.should raise_error( ::Amalgalite::SQLite3::Error )
|
349
|
+
@iso_db.functions.size.should eql(0)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it "can interrupt another thread that is also running in this database" do
|
354
|
+
executions = 0
|
355
|
+
other = Thread.new( @iso_db ) do |db|
|
356
|
+
loop do
|
357
|
+
begin
|
358
|
+
db.execute("select count(id) from country")
|
359
|
+
executions += 1
|
360
|
+
rescue => e
|
361
|
+
Thread.current[:had_error] = e
|
362
|
+
break
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
rudeness = Thread.new( @iso_db ) do |db|
|
368
|
+
sleep 0.05
|
369
|
+
@iso_db.interrupt!
|
370
|
+
end
|
371
|
+
|
372
|
+
rudeness.join
|
373
|
+
|
374
|
+
executions.should > 10
|
375
|
+
other[:had_error].should be_an_instance_of( ::Amalgalite::SQLite3::Error )
|
376
|
+
other[:had_error].message.should =~ / interrupted/
|
377
|
+
end
|
378
|
+
|
379
|
+
it "savepoints are considered 'in_transaction'" do
|
380
|
+
@iso_db.savepoint( 'test1' ) do |db|
|
381
|
+
db.should be_in_transaction
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
it "releases a savepoint" do
|
386
|
+
us_sub = @iso_db.execute( "select count(1) as cnt from subcountry where country = 'US'" ).first['cnt']
|
387
|
+
us_sub.should eql(57)
|
388
|
+
other_sub = @iso_db.execute( "select count(1) as cnt from subcountry where country != 'US'" ).first['cnt']
|
389
|
+
|
390
|
+
@iso_db.transaction
|
391
|
+
@iso_db.savepoint( "t1" ) do |s|
|
392
|
+
s.execute("DELETE FROM subcountry where country = 'US'")
|
393
|
+
end
|
394
|
+
|
395
|
+
all_sub = @iso_db.execute("SELECT count(*) as cnt from subcountry").first['cnt']
|
396
|
+
|
397
|
+
all_sub.should eql(other_sub)
|
398
|
+
@iso_db.rollback
|
399
|
+
all_sub = @iso_db.execute("SELECT count(*) as cnt from subcountry").first['cnt']
|
400
|
+
all_sub.should eql(( us_sub + other_sub ))
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
it "rolls back a savepoint" do
|
405
|
+
all_sub = @iso_db.execute("SELECT count(*) as cnt from subcountry").first['cnt']
|
406
|
+
lambda {
|
407
|
+
@iso_db.savepoint( "t1" ) do |s|
|
408
|
+
s.execute("DELETE FROM subcountry where country = 'US'")
|
409
|
+
raise "sample error"
|
410
|
+
end
|
411
|
+
}.should raise_error( StandardError, /sample error/ )
|
412
|
+
|
413
|
+
@iso_db.execute("SELECT count(*) as cnt from subcountry").first['cnt'].should eql(all_sub)
|
414
|
+
end
|
415
|
+
|
416
|
+
it "rolling back the outermost savepoint is still 'in_transaction'" do
|
417
|
+
@iso_db.savepoint( "t1" )
|
418
|
+
@iso_db.execute("DELETE FROM subcountry where country = 'US'")
|
419
|
+
@iso_db.rollback_to( "t1" )
|
420
|
+
@iso_db.should be_in_transaction
|
421
|
+
@iso_db.rollback
|
422
|
+
@iso_db.should_not be_in_transaction
|
423
|
+
end
|
424
|
+
|
425
|
+
it "can escape quoted strings" do
|
426
|
+
@iso_db.escape( "It's a happy day!" ).should eql("It''s a happy day!")
|
427
|
+
end
|
428
|
+
|
429
|
+
it "can quote and escape single quoted strings" do
|
430
|
+
@iso_db.quote( "It's a happy day!" ).should eql("'It''s a happy day!'")
|
431
|
+
end
|
432
|
+
|
433
|
+
it "can escape a symbol" do
|
434
|
+
@iso_db.escape( :stuff ).should eql("stuff")
|
435
|
+
end
|
436
|
+
|
437
|
+
it "can quote a symbol" do
|
438
|
+
@iso_db.quote( :stuff ).should eql("'stuff'")
|
439
|
+
end
|
440
|
+
|
441
|
+
it "returns the first row of results as a convenience" do
|
442
|
+
row = @iso_db.first_row_from("SELECT c.name, c.two_letter, count(*) AS count
|
443
|
+
FROM country c
|
444
|
+
JOIN subcountry sc
|
445
|
+
ON c.two_letter = sc.country
|
446
|
+
GROUP BY c.name, c.two_letter
|
447
|
+
ORDER BY count DESC")
|
448
|
+
row.length.should eql(3)
|
449
|
+
row[0].should eql("United Kingdom")
|
450
|
+
row[1].should eql("GB")
|
451
|
+
row[2].should eql(232)
|
452
|
+
row['name'].should eql("United Kingdom")
|
453
|
+
row['two_letter'].should eql("GB")
|
454
|
+
row['count'].should eql(232)
|
455
|
+
end
|
456
|
+
|
457
|
+
it "returns and empty row if there are no results for the first row" do
|
458
|
+
row = @iso_db.first_row_from("SELECT * from subcountry where country = 'Antarctica'")
|
459
|
+
row.should be_empty
|
460
|
+
end
|
461
|
+
|
462
|
+
it "returns nil if there is no value in the first value" do
|
463
|
+
val = @iso_db.first_value_from("select * from subcountry where country = 'Antarctica'" )
|
464
|
+
val.should eql(nil)
|
465
|
+
end
|
466
|
+
|
467
|
+
it "returns the first value of results as a conveinience" do
|
468
|
+
val = @iso_db.first_value_from("SELECT count(*) from subcountry ")
|
469
|
+
val.should eql(3995)
|
470
|
+
end
|
471
|
+
|
472
|
+
it "replicates a database to memory" do
|
473
|
+
mem_db = @iso_db.replicate_to( ":memory:" )
|
474
|
+
@iso_db.close
|
475
|
+
val = mem_db.first_value_from("SELECT count(*) from subcountry" )
|
476
|
+
val.should eql(3995)
|
477
|
+
end
|
478
|
+
|
479
|
+
it "replicates a database to a database file" do
|
480
|
+
all_sub = @iso_db.execute("SELECT count(*) as cnt from subcountry").first['cnt']
|
481
|
+
|
482
|
+
fdb = Amalgalite::Database.new( SpecInfo.test_db )
|
483
|
+
@iso_db.replicate_to( fdb )
|
484
|
+
@iso_db.close
|
485
|
+
|
486
|
+
File.exist?( SpecInfo.test_db ).should == true
|
487
|
+
fdb.execute("SELECT count(*) as cnt from subcountry").first['cnt'].should == all_sub
|
488
|
+
end
|
489
|
+
|
490
|
+
it "raises an error if it is given an invalid location to replicate to" do
|
491
|
+
lambda { @iso_db.replicate_to( false ) }.should raise_error( ArgumentError, /must be a String or a Database/ )
|
492
|
+
end
|
493
|
+
|
494
|
+
end
|