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,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
|