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