libsql 0.1.0-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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +60 -0
- data/HISTORY.md +6 -0
- data/LICENSE +31 -0
- data/Manifest.txt +96 -0
- data/README.md +59 -0
- data/Rakefile +28 -0
- data/TODO.md +57 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +106 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/fts5.rb +152 -0
- data/examples/gem-db.rb +94 -0
- data/examples/schema-info.rb +34 -0
- data/ext/libsql/c/extconf.rb +86 -0
- data/ext/libsql/c/gen_constants.rb +353 -0
- data/ext/libsql/c/libsql_blob.c +240 -0
- data/ext/libsql/c/libsql_constants.c +1518 -0
- data/ext/libsql/c/libsql_database.c +1188 -0
- data/ext/libsql/c/libsql_ext.c +383 -0
- data/ext/libsql/c/libsql_ext.h +149 -0
- data/ext/libsql/c/libsql_statement.c +649 -0
- data/ext/libsql/c/notes.txt +134 -0
- data/ext/libsql/c/sqlite3.c +247030 -0
- data/ext/libsql/c/sqlite3.h +13436 -0
- data/lib/libsql/3.0/libsql_ext.so +0 -0
- data/lib/libsql/3.1/libsql_ext.so +0 -0
- data/lib/libsql/3.2/libsql_ext.so +0 -0
- data/lib/libsql/aggregate.rb +73 -0
- data/lib/libsql/blob.rb +186 -0
- data/lib/libsql/boolean.rb +42 -0
- data/lib/libsql/busy_timeout.rb +47 -0
- data/lib/libsql/column.rb +99 -0
- data/lib/libsql/csv_table_importer.rb +75 -0
- data/lib/libsql/database.rb +933 -0
- data/lib/libsql/function.rb +61 -0
- data/lib/libsql/index.rb +43 -0
- data/lib/libsql/memory_database.rb +15 -0
- data/lib/libsql/paths.rb +80 -0
- data/lib/libsql/profile_tap.rb +131 -0
- data/lib/libsql/progress_handler.rb +21 -0
- data/lib/libsql/schema.rb +225 -0
- data/lib/libsql/sqlite3/constants.rb +95 -0
- data/lib/libsql/sqlite3/database/function.rb +48 -0
- data/lib/libsql/sqlite3/database/status.rb +68 -0
- data/lib/libsql/sqlite3/libsql_version.rb +32 -0
- data/lib/libsql/sqlite3/status.rb +60 -0
- data/lib/libsql/sqlite3/version.rb +55 -0
- data/lib/libsql/sqlite3.rb +7 -0
- data/lib/libsql/statement.rb +421 -0
- data/lib/libsql/table.rb +91 -0
- data/lib/libsql/taps/console.rb +27 -0
- data/lib/libsql/taps/io.rb +74 -0
- data/lib/libsql/taps.rb +2 -0
- data/lib/libsql/trace_tap.rb +35 -0
- data/lib/libsql/type_map.rb +63 -0
- data/lib/libsql/type_maps/default_map.rb +166 -0
- data/lib/libsql/type_maps/storage_map.rb +38 -0
- data/lib/libsql/type_maps/text_map.rb +21 -0
- data/lib/libsql/version.rb +8 -0
- data/lib/libsql/view.rb +26 -0
- data/lib/libsql-ruby.rb +1 -0
- data/lib/libsql.rb +51 -0
- data/spec/aggregate_spec.rb +158 -0
- data/spec/blob_spec.rb +78 -0
- data/spec/boolean_spec.rb +24 -0
- data/spec/busy_handler.rb +157 -0
- data/spec/data/iso-3166-country.txt +242 -0
- data/spec/data/iso-3166-schema.sql +22 -0
- data/spec/data/iso-3166-subcountry.txt +3995 -0
- data/spec/data/make-iso-db.sh +12 -0
- data/spec/database_spec.rb +505 -0
- data/spec/default_map_spec.rb +92 -0
- data/spec/function_spec.rb +78 -0
- data/spec/integeration_spec.rb +97 -0
- data/spec/iso_3166_database.rb +58 -0
- data/spec/json_spec.rb +24 -0
- data/spec/libsql_spec.rb +4 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +91 -0
- data/spec/rtree_spec.rb +66 -0
- data/spec/schema_spec.rb +131 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/sqlite3/constants_spec.rb +108 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/libsql_version_spec.rb +16 -0
- data/spec/sqlite3/status_spec.rb +22 -0
- data/spec/sqlite3/version_spec.rb +28 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +168 -0
- data/spec/storage_map_spec.rb +38 -0
- data/spec/tap_spec.rb +57 -0
- data/spec/text_map_spec.rb +20 -0
- data/spec/type_map_spec.rb +14 -0
- data/spec/version_spec.rb +8 -0
- data/tasks/custom.rake +134 -0
- data/tasks/default.rake +257 -0
- data/tasks/extension.rake +29 -0
- data/tasks/this.rb +208 -0
- metadata +330 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
describe "Integration specifications" do
|
7
|
+
|
8
|
+
describe " - invalid queries" do
|
9
|
+
it "raises error with an invalid syntax" do
|
10
|
+
lambda{ @iso_db.prepare "SELECT from country" }.should raise_error( ::Libsql::SQLite3::Error )
|
11
|
+
end
|
12
|
+
|
13
|
+
it "raises error with invalid table" do
|
14
|
+
lambda{ @iso_db.prepare "SELECT * FROM foo" }.should raise_error( ::Libsql::SQLite3::Error )
|
15
|
+
end
|
16
|
+
|
17
|
+
it "raises error with invalid column" do
|
18
|
+
lambda{ @iso_db.prepare "SELECT foo FROM country" }.should raise_error( ::Libsql::SQLite3::Error )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe " - default types conversion" do
|
23
|
+
|
24
|
+
{
|
25
|
+
"datetime" => { :value => DateTime.now, :klass => DateTime },
|
26
|
+
"timestamp" => { :value => Time.now, :klass => Time } ,
|
27
|
+
"date" => { :value => Date.today, :klass => Date },
|
28
|
+
"integer" => { :value => 42, :klass => Integer },
|
29
|
+
"double" => { :value => 3.14, :klass => Float },
|
30
|
+
"varchar" => { :value => "foobarbaz", :klass => String },
|
31
|
+
"boolean" => { :value => true, :klass => TrueClass },
|
32
|
+
"varchar(2)"=> { :value => nil, :klass => NilClass }
|
33
|
+
}.each_pair do |sql_type, ruby_info|
|
34
|
+
it "converts a ruby obj (#{ruby_info[:value].to_s}) of #{ruby_info[:klass]} to an SQL type of #{sql_type} and back again " do
|
35
|
+
db = ::Libsql::Database.new( SpecInfo.test_db )
|
36
|
+
db.execute "CREATE TABLE t( c #{sql_type} )"
|
37
|
+
db.execute "insert into t (c) values ( ? )", ruby_info[:value]
|
38
|
+
rows = db.execute "select * from t"
|
39
|
+
rows.first['c'].should be_kind_of(ruby_info[:klass])
|
40
|
+
|
41
|
+
if [ DateTime, Time ].include?( ruby_info[:klass] ) then
|
42
|
+
rows.first['c'].strftime("%Y-%m-%d %H:%M:%S").should eql(ruby_info[:value].strftime("%Y-%m-%d %H:%M:%S"))
|
43
|
+
else
|
44
|
+
rows.first['c'].should eql(ruby_info[:value])
|
45
|
+
end
|
46
|
+
db.close
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe " - storage type conversion" do
|
52
|
+
{
|
53
|
+
"datetime" => { :value => DateTime.now, :result => DateTime.now.strftime("%Y-%m-%dT%H:%M:%S%Z") } ,
|
54
|
+
"timestamp" => { :value => Time.now, :result => Time.now.to_s },
|
55
|
+
"date" => { :value => Date.today, :result => Date.today.to_s },
|
56
|
+
"integer" => { :value => 42, :result => 42 } ,
|
57
|
+
"double" => { :value => 3.14, :result => 3.14 } ,
|
58
|
+
"varchar" => { :value => "foobarbaz", :result => "foobarbaz" },
|
59
|
+
"boolean" => { :value => true, :result => "true" },
|
60
|
+
"varchar(2)"=> { :value => nil, :result => nil }
|
61
|
+
}.each_pair do |sql_type, ruby_info|
|
62
|
+
it "converts a ruby obj (#{ruby_info[:value].to_s}) of class #{ruby_info[:value].class.name} to an SQL type of #{sql_type} and back to a storage type" do
|
63
|
+
db = ::Libsql::Database.new( SpecInfo.test_db )
|
64
|
+
db.type_map = ::Libsql::TypeMaps::StorageMap.new
|
65
|
+
db.execute "CREATE TABLE t( c #{sql_type} )"
|
66
|
+
db.execute "insert into t (c) values ( ? )", ruby_info[:value]
|
67
|
+
rows = db.execute "select * from t"
|
68
|
+
rows.first['c'].should eql(ruby_info[:result])
|
69
|
+
db.close
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe " - text type conversion" do
|
75
|
+
{
|
76
|
+
"datetime" => { :value => DateTime.now, :result => DateTime.now.strftime("%Y-%m-%dT%H:%M:%S%Z") } ,
|
77
|
+
"timestamp" => { :value => Time.now, :result => Time.now.to_s },
|
78
|
+
"date" => { :value => Date.today, :result => Date.today.to_s },
|
79
|
+
"integer" => { :value => 42, :result => "42" } ,
|
80
|
+
"double" => { :value => 3.14, :result => "3.14" } ,
|
81
|
+
"varchar" => { :value => "foobarbaz", :result => "foobarbaz" },
|
82
|
+
"boolean" => { :value => true, :result => "true" },
|
83
|
+
"varchar(2)"=> { :value => nil, :result => "" }
|
84
|
+
}.each_pair do |sql_type, ruby_info|
|
85
|
+
it "converts a ruby obj (#{ruby_info[:value].to_s}) of class #{ruby_info[:value].class.name} to an SQL type of #{sql_type} and back to text" do
|
86
|
+
db = ::Libsql::Database.new( SpecInfo.test_db )
|
87
|
+
db.type_map = ::Libsql::TypeMaps::TextMap.new
|
88
|
+
db.execute "CREATE TABLE t( c #{sql_type} )"
|
89
|
+
db.execute "insert into t (c) values ( ? )", ruby_info[:value]
|
90
|
+
rows = db.execute "select * from t"
|
91
|
+
rows.first['c'].should eql(ruby_info[:result])
|
92
|
+
db.close
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'libsql'
|
2
|
+
|
3
|
+
module ::Libsql
|
4
|
+
class Iso3166Database < Database
|
5
|
+
def self.country_data_file
|
6
|
+
@country_data_file ||= File.expand_path( File.join( File.dirname(__FILE__), "data", "iso-3166-country.txt" ) )
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.subcountry_data_file
|
10
|
+
@subcountry_data_file ||= File.expand_path( File.join( File.dirname(__FILE__), "data", "iso-3166-subcountry.txt" ) )
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.schema_file
|
14
|
+
@schema_file ||= File.expand_path(File.join(File.dirname(__FILE__), "data", "iso-3166-schema.sql"))
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.default_db_file
|
18
|
+
@db_file ||= File.expand_path(File.join(File.dirname(__FILE__), "data", "iso-3166.db"))
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.memory
|
23
|
+
Iso3166Database.new( ":memory:" )
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize( path = Iso3166Database.default_db_file )
|
27
|
+
@path = path
|
28
|
+
super( @path )
|
29
|
+
install_schema( self )
|
30
|
+
populate( self )
|
31
|
+
end
|
32
|
+
|
33
|
+
def duplicate( slug )
|
34
|
+
dirname = File.dirname( @path )
|
35
|
+
bname = File.basename( @path, ".db" )
|
36
|
+
new_name = File.join( dirname, "#{bname}_#{slug}.db" )
|
37
|
+
File.unlink( new_name ) if File.exist?( new_name )
|
38
|
+
new_db = replicate_to( new_name )
|
39
|
+
new_db.close
|
40
|
+
return new_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def install_schema( db )
|
44
|
+
db.execute_batch( IO.read( Iso3166Database.schema_file ) );
|
45
|
+
end
|
46
|
+
|
47
|
+
def populate( db )
|
48
|
+
db.import_csv_to_table( Iso3166Database.country_data_file, "country", :col_sep => "|" )
|
49
|
+
db.import_csv_to_table( Iso3166Database.subcountry_data_file, "subcountry", :col_sep => "|" )
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove
|
53
|
+
File.unlink( @path ) if File.exist?( @path )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
data/spec/json_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'libsql/sqlite3'
|
3
|
+
require 'rbconfig'
|
4
|
+
|
5
|
+
describe "::Libsql handles the JSON extension" do
|
6
|
+
it "can parse a `json_each` call" do
|
7
|
+
db = ::Libsql::Database.new( ":memory:" )
|
8
|
+
values = %w[ a b c d e f g ]
|
9
|
+
db.execute("CREATE TABLE jtest(id, json)")
|
10
|
+
db.execute("INSERT INTO jtest(id, json) values (1, json($json))", { "$json" => values })
|
11
|
+
rows = db.execute("SELECT jtest.id as i, value as v FROM jtest, json_each(jtest.json)")
|
12
|
+
|
13
|
+
rows.size.should eq(values.size)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can return a proper json column" do
|
17
|
+
db = ::Libsql::Database.new( ":memory:" )
|
18
|
+
values = %w[ a b c d e f g ]
|
19
|
+
db.execute("CREATE TABLE jtest(id INTEGER, json JSON)")
|
20
|
+
db.execute("INSERT INTO jtest(id, json) values (1, json($json))", { "$json" => values })
|
21
|
+
|
22
|
+
db.execute("select * from jtest")
|
23
|
+
end
|
24
|
+
end
|
data/spec/libsql_spec.rb
ADDED
data/spec/paths_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ::Libsql::Paths do
|
4
|
+
before(:each) do
|
5
|
+
@root_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
6
|
+
@root_dir += "/"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "root dir should be correct" do
|
10
|
+
::Libsql::Paths.root_dir.should == @root_dir
|
11
|
+
end
|
12
|
+
|
13
|
+
it "config_path should be correct" do
|
14
|
+
::Libsql::Paths.config_path.should == File.join(@root_dir, "config/")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "data path should be correct" do
|
18
|
+
::Libsql::Paths.data_path.should == File.join(@root_dir, "data/")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "lib path should be correct" do
|
22
|
+
::Libsql::Paths.lib_path.should == File.join(@root_dir, "lib/")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "ext path should be correct" do
|
26
|
+
::Libsql::Paths.ext_path.should == File.join(@root_dir, "ext/")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class PH < ::Libsql::ProgressHandler
|
4
|
+
attr_reader :call_count
|
5
|
+
def initialize( max = nil )
|
6
|
+
@call_count = 0
|
7
|
+
@max = max
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
@call_count += 1
|
12
|
+
if @max && ( @call_count >= @max ) then
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
return true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_thread( database )
|
20
|
+
Thread.new( database ) do |db|
|
21
|
+
begin
|
22
|
+
db.execute("select count(id) from country")
|
23
|
+
rescue => e
|
24
|
+
Thread.current[:exception] = e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "Progress Handlers" do
|
30
|
+
|
31
|
+
it "raises NotImplemented if #call is not overwritten" do
|
32
|
+
bh = ::Libsql::ProgressHandler.new
|
33
|
+
lambda { bh.call }.should raise_error( ::NotImplementedError, /The progress handler call\(\) method must be implemented/ )
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can be registered as block" do
|
37
|
+
call_count = 0
|
38
|
+
@iso_db.progress_handler( 50 ) do ||
|
39
|
+
call_count += 1
|
40
|
+
true
|
41
|
+
end
|
42
|
+
qt = query_thread( @iso_db )
|
43
|
+
qt.join
|
44
|
+
call_count.should > 10
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can be registered as lambda" do
|
48
|
+
call_count = 0
|
49
|
+
callable = lambda { || call_count += 1; true }
|
50
|
+
@iso_db.progress_handler( 42, callable )
|
51
|
+
qt = query_thread( @iso_db )
|
52
|
+
qt.join
|
53
|
+
call_count.should > 10
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can be registered as a class" do
|
57
|
+
ph = PH.new
|
58
|
+
@iso_db.progress_handler( 5, ph )
|
59
|
+
qt = query_thread( @iso_db )
|
60
|
+
qt.join
|
61
|
+
ph.call_count.should > 100
|
62
|
+
end
|
63
|
+
|
64
|
+
it "behaves like #interrupt! if returning a false value" do
|
65
|
+
ph = PH.new( 25 )
|
66
|
+
@iso_db.progress_handler( 5, ph )
|
67
|
+
qt = query_thread( @iso_db )
|
68
|
+
qt.join
|
69
|
+
ph.call_count.should eql(25)
|
70
|
+
qt[:exception].should be_instance_of( ::Libsql::SQLite3::Error )
|
71
|
+
@iso_db.api.last_error_code.should be == 9
|
72
|
+
@iso_db.api.last_error_message.should =~ /interrupted/
|
73
|
+
qt[:exception].message.should =~ /interrupted/
|
74
|
+
end
|
75
|
+
|
76
|
+
it "cannot register a block with the wrong arity" do
|
77
|
+
lambda do
|
78
|
+
@iso_db.define_progress_handler { |x,y| puts "What!" }
|
79
|
+
end.should raise_error( ::Libsql::Database::ProgressHandlerError, /A progress handler expects 0 arguments, not 2/)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "can remove a progress handler" do
|
83
|
+
ph = PH.new
|
84
|
+
@iso_db.progress_handler( 5, ph )
|
85
|
+
@iso_db.remove_progress_handler
|
86
|
+
qt = query_thread( @iso_db )
|
87
|
+
qt.join
|
88
|
+
ph.call_count.should eql(0)
|
89
|
+
qt[:exception].should be_nil
|
90
|
+
end
|
91
|
+
end
|
data/spec/rtree_spec.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Example from http://sqlite.org/rtree.html
|
5
|
+
#
|
6
|
+
describe "SQLite3 R*Tree extension" do
|
7
|
+
before( :each ) do
|
8
|
+
@db = ::Libsql::Database.new( ":memory:" )
|
9
|
+
x = @db.execute_batch <<-sql
|
10
|
+
CREATE VIRTUAL TABLE demo_index USING rtree(
|
11
|
+
id, -- Integer primary key
|
12
|
+
minX, maxX, -- Minimum and maximum X coordinate
|
13
|
+
minY, maxY -- Minimum and maximum Y coordinate
|
14
|
+
);
|
15
|
+
--
|
16
|
+
INSERT INTO demo_index VALUES(
|
17
|
+
1, -- Primary key
|
18
|
+
-80.7749, -80.7747, -- Longitude range
|
19
|
+
30.3776, 30.3778 -- Latitude range
|
20
|
+
);
|
21
|
+
INSERT INTO demo_index VALUES(
|
22
|
+
2,
|
23
|
+
-81.0, -79.6,
|
24
|
+
35.0, 36.2
|
25
|
+
);
|
26
|
+
sql
|
27
|
+
x.should == 3
|
28
|
+
end
|
29
|
+
|
30
|
+
after( :each ) do
|
31
|
+
@db.close
|
32
|
+
end
|
33
|
+
|
34
|
+
it "has 2 rows" do
|
35
|
+
r = @db.first_value_from( "SELECT count(*) FROM demo_index")
|
36
|
+
r.should == 2
|
37
|
+
end
|
38
|
+
|
39
|
+
it "queries normally" do
|
40
|
+
r = @db.execute "SELECT * FROM demo_index WHERE id=1;"
|
41
|
+
r.size.should be == 1
|
42
|
+
row = r.first
|
43
|
+
row['id'].should be == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does a 'contained within' query" do
|
47
|
+
r = @db.execute <<-sql
|
48
|
+
SELECT id FROM demo_index
|
49
|
+
WHERE minX>=-81.08 AND maxX<=-80.58
|
50
|
+
AND minY>=30.00 AND maxY<=30.44;
|
51
|
+
sql
|
52
|
+
|
53
|
+
r.size.should be == 1
|
54
|
+
r.first['id'].should be == 1
|
55
|
+
end
|
56
|
+
|
57
|
+
it "does an 'overlapping' query" do
|
58
|
+
r = @db.execute <<-sql
|
59
|
+
SELECT id FROM demo_index
|
60
|
+
WHERE maxX>=-81.08 AND minX<=-80.58
|
61
|
+
AND maxY>=30.00 AND minY<=35.44;
|
62
|
+
sql
|
63
|
+
r.size.should == 2
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/spec/schema_spec.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'libsql'
|
4
|
+
require 'libsql/schema'
|
5
|
+
|
6
|
+
describe ::Libsql::Schema do
|
7
|
+
|
8
|
+
it "loads the schema of a database" do
|
9
|
+
schema = @iso_db.schema
|
10
|
+
schema.load_tables
|
11
|
+
schema.tables.size.should eql(2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "loads the views in the database" do
|
15
|
+
s = @iso_db.schema
|
16
|
+
sql = "CREATE VIEW v1 AS SELECT c.name, c.two_letter, s.name, s.subdivision FROM country AS c JOIN subcountry AS s ON c.two_letter = s.country"
|
17
|
+
@iso_db.execute( sql )
|
18
|
+
s.dirty?.should be == true
|
19
|
+
@iso_db.schema.load_views
|
20
|
+
@iso_db.schema.views.size.should eql(1)
|
21
|
+
@iso_db.schema.views["v1"].sql.should eql(sql)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "removes quotes from around default values in columns" do
|
25
|
+
s = @iso_db.schema
|
26
|
+
sql = "CREATE TABLE t1( d1 default 't' )"
|
27
|
+
@iso_db.execute( sql )
|
28
|
+
s.dirty?.should be == true
|
29
|
+
tt = @iso_db.schema.tables['t1']
|
30
|
+
tt.columns['d1'].default_value.should be == "t"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "loads the tables and columns" do
|
34
|
+
ct = @iso_db.schema.tables['country']
|
35
|
+
ct.name.should eql("country")
|
36
|
+
ct.columns.size.should eql(3)
|
37
|
+
ct.indexes.size.should eql(2)
|
38
|
+
ct.column_names.should eql(%w[ name two_letter id ])
|
39
|
+
@iso_db.schema.tables.size.should eql(2)
|
40
|
+
|
41
|
+
|
42
|
+
ct.columns['two_letter'].should be_primary_key
|
43
|
+
ct.columns['two_letter'].declared_data_type.should eql("TEXT")
|
44
|
+
ct.columns['name'].should_not be_nullable
|
45
|
+
ct.columns['name'].should be_not_null_constraint
|
46
|
+
ct.columns['name'].should_not be_has_default_value
|
47
|
+
ct.columns['id'].should_not be_auto_increment
|
48
|
+
end
|
49
|
+
|
50
|
+
it "knows what the primary key of a table is" do
|
51
|
+
ct = @iso_db.schema.tables['country']
|
52
|
+
ct.primary_key.should == [ ct.columns['two_letter'] ]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "knows the primary key of a table even without an explicity unique index" do
|
56
|
+
s = @iso_db.schema
|
57
|
+
sql = "CREATE TABLE u( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , other text )"
|
58
|
+
@iso_db.execute( sql )
|
59
|
+
s.dirty?.should be == true
|
60
|
+
ut = @iso_db.schema.tables['u']
|
61
|
+
ut.primary_key.should == [ ut.columns['id'] ]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "knows the primary key of a temporary table" do
|
65
|
+
@iso_db.execute "CREATE TEMPORARY TABLE tt( a, b INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, c )"
|
66
|
+
tt = @iso_db.schema.tables[ 'tt' ]
|
67
|
+
tt.primary_key.should == [ tt.columns['b'] ]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "knows what the primary key of a table is when it is a multiple column primary key" do
|
71
|
+
sql = "CREATE TABLE m ( id1, id2, PRIMARY KEY (id2, id1) )"
|
72
|
+
s = @iso_db.schema
|
73
|
+
@iso_db.execute( sql )
|
74
|
+
s.dirty?.should be == true
|
75
|
+
mt = @iso_db.schema.tables['m']
|
76
|
+
mt.primary_key.should == [ mt.columns['id2'], mt.columns['id1'] ]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "loads the indexes" do
|
80
|
+
c = @iso_db.schema.tables['country']
|
81
|
+
c.indexes.size.should eql(2)
|
82
|
+
c.indexes['country_name'].columns.size.should eql(1)
|
83
|
+
c.indexes['country_name'].should_not be_unique
|
84
|
+
c.indexes['country_name'].sequence_number.should eql(0)
|
85
|
+
c.indexes['country_name'].columns.first.should eql(@iso_db.schema.tables['country'].columns['name'])
|
86
|
+
c.indexes['sqlite_autoindex_country_1'].should be_unique
|
87
|
+
|
88
|
+
subc = @iso_db.schema.tables['subcountry']
|
89
|
+
subc.indexes.size.should eql(3)
|
90
|
+
subc.indexes['subcountry_country'].columns.first.should eql(@iso_db.schema.tables['subcountry'].columns['country'])
|
91
|
+
end
|
92
|
+
|
93
|
+
it "knows the schema is dirty when a table is created" do
|
94
|
+
s = @iso_db.schema
|
95
|
+
s.tables['country']
|
96
|
+
s.dirty?.should be == false
|
97
|
+
@iso_db.execute( "create table x1( a, b )" )
|
98
|
+
s.dirty?.should be == true
|
99
|
+
end
|
100
|
+
|
101
|
+
it "knows the schema is dirty when a table is dropped" do
|
102
|
+
s = @iso_db.schema
|
103
|
+
s.tables['country']
|
104
|
+
@iso_db.execute( "create table x1( a, b )" )
|
105
|
+
s.dirty?.should be == true
|
106
|
+
|
107
|
+
@iso_db.schema.load_schema!
|
108
|
+
s = @iso_db.schema
|
109
|
+
|
110
|
+
s.dirty?.should be == false
|
111
|
+
@iso_db.execute("drop table x1")
|
112
|
+
s.dirty?.should be == true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "knows if a temporary table exists" do
|
116
|
+
@iso_db.execute "CREATE TEMPORARY TABLE tt(a,b,c)"
|
117
|
+
@iso_db.schema.tables.keys.include?('tt').should be == true
|
118
|
+
@iso_db.schema.tables['tt'].temporary?.should be == true
|
119
|
+
end
|
120
|
+
|
121
|
+
it "sees that temporary tables shadow real tables" do
|
122
|
+
@iso_db.execute "CREATE TABLE tt(x)"
|
123
|
+
@iso_db.schema.tables['tt'].temporary?.should be == false
|
124
|
+
@iso_db.execute "CREATE TEMP TABLE tt(a,b,c)"
|
125
|
+
@iso_db.schema.tables['tt'].temporary?.should be == true
|
126
|
+
@iso_db.execute "DROP TABLE tt"
|
127
|
+
@iso_db.schema.tables['tt'].temporary?.should be == false
|
128
|
+
@iso_db.schema.tables['tt'].columns.size.should be == 1
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'libsql'
|
5
|
+
require ::Libsql::Paths.spec_path( "iso_3166_database.rb" )
|
6
|
+
|
7
|
+
class SpecInfo
|
8
|
+
class << self
|
9
|
+
def test_db
|
10
|
+
@test_db ||= ::Libsql::Paths.spec_path("data", "test.db")
|
11
|
+
end
|
12
|
+
|
13
|
+
def make_master_iso_db
|
14
|
+
@master_db ||= ::Libsql::Iso3166Database.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def make_clone_iso_db
|
18
|
+
make_master_iso_db.duplicate( 'testing' )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.expect_with :rspec do |c|
|
25
|
+
c.syntax = [:should, :expect]
|
26
|
+
end
|
27
|
+
|
28
|
+
config.before(:all) do
|
29
|
+
SpecInfo.make_master_iso_db
|
30
|
+
end
|
31
|
+
|
32
|
+
config.after(:all) do
|
33
|
+
File.unlink( ::Libsql::Iso3166Database.default_db_file ) if File.exist?( ::Libsql::Iso3166Database.default_db_file )
|
34
|
+
end
|
35
|
+
|
36
|
+
config.before( :each ) do
|
37
|
+
@iso_db_path = SpecInfo.make_clone_iso_db
|
38
|
+
@iso_db = ::Libsql::Database.new( @iso_db_path )
|
39
|
+
@schema = IO.read( ::Libsql::Iso3166Database.schema_file )
|
40
|
+
end
|
41
|
+
|
42
|
+
config.after( :each ) do
|
43
|
+
@iso_db.close
|
44
|
+
File.unlink( @iso_db_path ) if File.exist?( @iso_db_path )
|
45
|
+
File.unlink( SpecInfo.test_db ) if File.exist?( SpecInfo.test_db )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'libsql/sqlite3/constants'
|
4
|
+
|
5
|
+
describe ::Libsql::SQLite3::Constants do
|
6
|
+
|
7
|
+
it "has Open constants" do
|
8
|
+
::Libsql::SQLite3::Constants::Open::READONLY.should > 0
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'ResultCode' do
|
12
|
+
it "has constants" do
|
13
|
+
::Libsql::SQLite3::Constants::ResultCode::OK.should == 0
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can return the constant from a number" do
|
17
|
+
c = ::Libsql::SQLite3::Constants::ResultCode.name_from_value( 21 )
|
18
|
+
c.should == "MISUSE"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can return the number from a name" do
|
22
|
+
v = ::Libsql::SQLite3::Constants::ResultCode.value_from_name( "MISUSE" )
|
23
|
+
v.should == 21
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "DataType" do
|
28
|
+
it "has constants" do
|
29
|
+
::Libsql::SQLite3::Constants::DataType::NULL.should == 5
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can return the constant from a number" do
|
33
|
+
c = ::Libsql::SQLite3::Constants::DataType.name_from_value( 5 )
|
34
|
+
c.should == "NULL"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can return the number from a name" do
|
38
|
+
v = ::Libsql::SQLite3::Constants::DataType.value_from_name( "Null" )
|
39
|
+
v.should == 5
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "Config" do
|
45
|
+
it "has constants" do
|
46
|
+
::Libsql::SQLite3::Constants::Config::HEAP.should == 8
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can return the constant from a number" do
|
50
|
+
c = ::Libsql::SQLite3::Constants::Config.name_from_value( 8 )
|
51
|
+
c.should == "HEAP"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can return the number from a name" do
|
55
|
+
v = ::Libsql::SQLite3::Constants::Config.value_from_name( "heap" )
|
56
|
+
v.should == 8
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'Status' do
|
62
|
+
it "has constants" do
|
63
|
+
::Libsql::SQLite3::Constants::Status::MEMORY_USED.should == 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it "can return the constant from a number" do
|
67
|
+
c = ::Libsql::SQLite3::Constants::Status.name_from_value( 3 )
|
68
|
+
c.should == "SCRATCH_USED"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "can return the number from a name" do
|
72
|
+
v = ::Libsql::SQLite3::Constants::Status.value_from_name( "memory_used" )
|
73
|
+
v.should == 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'DBStatus' do
|
78
|
+
it "has constants" do
|
79
|
+
::Libsql::SQLite3::Constants::DBStatus::LOOKASIDE_USED.should == 0
|
80
|
+
end
|
81
|
+
|
82
|
+
it "can return the constant from a number" do
|
83
|
+
c = ::Libsql::SQLite3::Constants::DBStatus.name_from_value( 0 )
|
84
|
+
c.should == "LOOKASIDE_USED"
|
85
|
+
end
|
86
|
+
|
87
|
+
it "can return the number from a name" do
|
88
|
+
v = ::Libsql::SQLite3::Constants::DBStatus.value_from_name( "lookaside_used" )
|
89
|
+
v.should == 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "StatementStatus" do
|
94
|
+
it "has constants" do
|
95
|
+
::Libsql::SQLite3::Constants::StatementStatus::AUTOINDEX.should == 3
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can return the constant from a number" do
|
99
|
+
c = ::Libsql::SQLite3::Constants::StatementStatus.name_from_value( 3 )
|
100
|
+
c.should == "AUTOINDEX"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can return the number from a name" do
|
104
|
+
v = ::Libsql::SQLite3::Constants::StatementStatus.value_from_name( "autoindex" )
|
105
|
+
v.should == 3
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|