amalgalite 0.10.1-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
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,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