libsql 0.1.0-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- 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.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 +329 -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
|