libsql 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +60 -0
  3. data/HISTORY.md +6 -0
  4. data/LICENSE +31 -0
  5. data/Manifest.txt +96 -0
  6. data/README.md +59 -0
  7. data/Rakefile +28 -0
  8. data/TODO.md +57 -0
  9. data/examples/a.rb +9 -0
  10. data/examples/blob.rb +106 -0
  11. data/examples/define_aggregate.rb +75 -0
  12. data/examples/define_function.rb +104 -0
  13. data/examples/fts5.rb +152 -0
  14. data/examples/gem-db.rb +94 -0
  15. data/examples/schema-info.rb +34 -0
  16. data/ext/libsql/c/extconf.rb +86 -0
  17. data/ext/libsql/c/gen_constants.rb +353 -0
  18. data/ext/libsql/c/libsql_blob.c +240 -0
  19. data/ext/libsql/c/libsql_constants.c +1518 -0
  20. data/ext/libsql/c/libsql_database.c +1188 -0
  21. data/ext/libsql/c/libsql_ext.c +383 -0
  22. data/ext/libsql/c/libsql_ext.h +149 -0
  23. data/ext/libsql/c/libsql_statement.c +649 -0
  24. data/ext/libsql/c/notes.txt +134 -0
  25. data/ext/libsql/c/sqlite3.c +247030 -0
  26. data/ext/libsql/c/sqlite3.h +13436 -0
  27. data/lib/libsql/aggregate.rb +73 -0
  28. data/lib/libsql/blob.rb +186 -0
  29. data/lib/libsql/boolean.rb +42 -0
  30. data/lib/libsql/busy_timeout.rb +47 -0
  31. data/lib/libsql/column.rb +99 -0
  32. data/lib/libsql/csv_table_importer.rb +75 -0
  33. data/lib/libsql/database.rb +933 -0
  34. data/lib/libsql/function.rb +61 -0
  35. data/lib/libsql/index.rb +43 -0
  36. data/lib/libsql/memory_database.rb +15 -0
  37. data/lib/libsql/paths.rb +80 -0
  38. data/lib/libsql/profile_tap.rb +131 -0
  39. data/lib/libsql/progress_handler.rb +21 -0
  40. data/lib/libsql/schema.rb +225 -0
  41. data/lib/libsql/sqlite3/constants.rb +95 -0
  42. data/lib/libsql/sqlite3/database/function.rb +48 -0
  43. data/lib/libsql/sqlite3/database/status.rb +68 -0
  44. data/lib/libsql/sqlite3/libsql_version.rb +32 -0
  45. data/lib/libsql/sqlite3/status.rb +60 -0
  46. data/lib/libsql/sqlite3/version.rb +55 -0
  47. data/lib/libsql/sqlite3.rb +7 -0
  48. data/lib/libsql/statement.rb +421 -0
  49. data/lib/libsql/table.rb +91 -0
  50. data/lib/libsql/taps/console.rb +27 -0
  51. data/lib/libsql/taps/io.rb +74 -0
  52. data/lib/libsql/taps.rb +2 -0
  53. data/lib/libsql/trace_tap.rb +35 -0
  54. data/lib/libsql/type_map.rb +63 -0
  55. data/lib/libsql/type_maps/default_map.rb +166 -0
  56. data/lib/libsql/type_maps/storage_map.rb +38 -0
  57. data/lib/libsql/type_maps/text_map.rb +21 -0
  58. data/lib/libsql/version.rb +8 -0
  59. data/lib/libsql/view.rb +26 -0
  60. data/lib/libsql-ruby.rb +1 -0
  61. data/lib/libsql.rb +51 -0
  62. data/spec/aggregate_spec.rb +158 -0
  63. data/spec/blob_spec.rb +78 -0
  64. data/spec/boolean_spec.rb +24 -0
  65. data/spec/busy_handler.rb +157 -0
  66. data/spec/data/iso-3166-country.txt +242 -0
  67. data/spec/data/iso-3166-schema.sql +22 -0
  68. data/spec/data/iso-3166-subcountry.txt +3995 -0
  69. data/spec/data/make-iso-db.sh +12 -0
  70. data/spec/database_spec.rb +505 -0
  71. data/spec/default_map_spec.rb +92 -0
  72. data/spec/function_spec.rb +78 -0
  73. data/spec/integeration_spec.rb +97 -0
  74. data/spec/iso_3166_database.rb +58 -0
  75. data/spec/json_spec.rb +24 -0
  76. data/spec/libsql_spec.rb +4 -0
  77. data/spec/paths_spec.rb +28 -0
  78. data/spec/progress_handler_spec.rb +91 -0
  79. data/spec/rtree_spec.rb +66 -0
  80. data/spec/schema_spec.rb +131 -0
  81. data/spec/spec_helper.rb +48 -0
  82. data/spec/sqlite3/constants_spec.rb +108 -0
  83. data/spec/sqlite3/database_status_spec.rb +36 -0
  84. data/spec/sqlite3/libsql_version_spec.rb +16 -0
  85. data/spec/sqlite3/status_spec.rb +22 -0
  86. data/spec/sqlite3/version_spec.rb +28 -0
  87. data/spec/sqlite3_spec.rb +53 -0
  88. data/spec/statement_spec.rb +168 -0
  89. data/spec/storage_map_spec.rb +38 -0
  90. data/spec/tap_spec.rb +57 -0
  91. data/spec/text_map_spec.rb +20 -0
  92. data/spec/type_map_spec.rb +14 -0
  93. data/spec/version_spec.rb +8 -0
  94. data/tasks/custom.rake +134 -0
  95. data/tasks/default.rake +257 -0
  96. data/tasks/extension.rake +29 -0
  97. data/tasks/this.rb +208 -0
  98. metadata +325 -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
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Libsql do
4
+ end
@@ -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
@@ -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
+
@@ -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
@@ -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