sequel 3.1.0 → 3.2.0
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/CHANGELOG +76 -0
- data/Rakefile +2 -2
- data/bin/sequel +9 -4
- data/doc/opening_databases.rdoc +279 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/virtual_rows.rdoc +42 -51
- data/lib/sequel/adapters/ado.rb +2 -5
- data/lib/sequel/adapters/db2.rb +5 -0
- data/lib/sequel/adapters/do.rb +3 -0
- data/lib/sequel/adapters/firebird.rb +6 -4
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +10 -8
- data/lib/sequel/adapters/jdbc/h2.rb +17 -4
- data/lib/sequel/adapters/mysql.rb +6 -19
- data/lib/sequel/adapters/odbc.rb +14 -18
- data/lib/sequel/adapters/openbase.rb +8 -0
- data/lib/sequel/adapters/shared/mssql.rb +14 -8
- data/lib/sequel/adapters/shared/mysql.rb +53 -28
- data/lib/sequel/adapters/shared/oracle.rb +21 -12
- data/lib/sequel/adapters/shared/postgres.rb +46 -26
- data/lib/sequel/adapters/shared/progress.rb +10 -5
- data/lib/sequel/adapters/shared/sqlite.rb +28 -12
- data/lib/sequel/adapters/sqlite.rb +4 -3
- data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
- data/lib/sequel/connection_pool.rb +4 -3
- data/lib/sequel/database.rb +110 -10
- data/lib/sequel/database/schema_sql.rb +12 -3
- data/lib/sequel/dataset.rb +40 -3
- data/lib/sequel/dataset/convenience.rb +0 -11
- data/lib/sequel/dataset/graph.rb +25 -11
- data/lib/sequel/dataset/sql.rb +176 -68
- data/lib/sequel/extensions/migration.rb +37 -21
- data/lib/sequel/extensions/schema_dumper.rb +8 -61
- data/lib/sequel/model.rb +3 -3
- data/lib/sequel/model/associations.rb +9 -1
- data/lib/sequel/model/base.rb +8 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/sql.rb +125 -18
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +1 -0
- data/spec/adapters/firebird_spec.rb +1 -0
- data/spec/adapters/informix_spec.rb +1 -0
- data/spec/adapters/mysql_spec.rb +23 -8
- data/spec/adapters/oracle_spec.rb +1 -0
- data/spec/adapters/postgres_spec.rb +52 -4
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +2 -1
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +174 -0
- data/spec/core/dataset_spec.rb +121 -26
- data/spec/core/expression_filters_spec.rb +156 -0
- data/spec/core/object_graph_spec.rb +20 -1
- data/spec/core/schema_spec.rb +5 -5
- data/spec/extensions/migration_spec.rb +140 -74
- data/spec/extensions/schema_dumper_spec.rb +3 -69
- data/spec/extensions/single_table_inheritance_spec.rb +6 -0
- data/spec/integration/dataset_test.rb +84 -2
- data/spec/integration/schema_test.rb +24 -5
- data/spec/integration/spec_helper.rb +8 -6
- data/spec/model/eager_loading_spec.rb +9 -0
- data/spec/model/record_spec.rb +35 -8
- metadata +8 -7
- data/lib/sequel/adapters/utils/date_format.rb +0 -21
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
- data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -1,5 +1,8 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
2
|
|
3
|
+
Regexp.send(:include, Sequel::SQL::StringMethods)
|
4
|
+
String.send(:include, Sequel::SQL::StringMethods)
|
5
|
+
|
3
6
|
context "Blockless Ruby Filters" do
|
4
7
|
before do
|
5
8
|
db = Sequel::Database.new
|
@@ -48,6 +51,14 @@ context "Blockless Ruby Filters" do
|
|
48
51
|
@d.l(:x => nil).should == '(x IS NULL)'
|
49
52
|
@d.l(:x => [1,2,3]).should == '(x IN (1, 2, 3))'
|
50
53
|
end
|
54
|
+
|
55
|
+
it "should use = 't' and != 't' OR IS NULL if IS TRUE is not supported" do
|
56
|
+
@d.meta_def(:supports_is_true?){false}
|
57
|
+
@d.l(:x => true).should == "(x = 't')"
|
58
|
+
@d.l(~{:x => true}).should == "((x != 't') OR (x IS NULL))"
|
59
|
+
@d.l(:x => false).should == "(x = 'f')"
|
60
|
+
@d.l(~{:x => false}).should == "((x != 'f') OR (x IS NULL))"
|
61
|
+
end
|
51
62
|
|
52
63
|
it "should support != via Hash#~" do
|
53
64
|
@d.l(~{:x => 100}).should == '(x != 100)'
|
@@ -71,6 +82,21 @@ context "Blockless Ruby Filters" do
|
|
71
82
|
@d.l(:x.like('a', 'b')).should == '((x LIKE \'a\') OR (x LIKE \'b\'))'
|
72
83
|
@d.l(:x.like(/a/, /b/i)).should == '((x ~ \'a\') OR (x ~* \'b\'))'
|
73
84
|
@d.l(:x.like('a', /b/)).should == '((x LIKE \'a\') OR (x ~ \'b\'))'
|
85
|
+
|
86
|
+
@d.l('a'.like(:x)).should == "('a' LIKE x)"
|
87
|
+
@d.l('a'.like(:x, 'b')).should == "(('a' LIKE x) OR ('a' LIKE 'b'))"
|
88
|
+
@d.l('a'.like(:x, /b/)).should == "(('a' LIKE x) OR ('a' ~ 'b'))"
|
89
|
+
@d.l('a'.like(:x, /b/i)).should == "(('a' LIKE x) OR ('a' ~* 'b'))"
|
90
|
+
|
91
|
+
@d.l(/a/.like(:x)).should == "('a' ~ x)"
|
92
|
+
@d.l(/a/.like(:x, 'b')).should == "(('a' ~ x) OR ('a' ~ 'b'))"
|
93
|
+
@d.l(/a/.like(:x, /b/)).should == "(('a' ~ x) OR ('a' ~ 'b'))"
|
94
|
+
@d.l(/a/.like(:x, /b/i)).should == "(('a' ~ x) OR ('a' ~* 'b'))"
|
95
|
+
|
96
|
+
@d.l(/a/i.like(:x)).should == "('a' ~* x)"
|
97
|
+
@d.l(/a/i.like(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
98
|
+
@d.l(/a/i.like(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
99
|
+
@d.l(/a/i.like(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
74
100
|
end
|
75
101
|
|
76
102
|
it "should support NOT LIKE via Symbol#like and Symbol#~" do
|
@@ -79,6 +105,21 @@ context "Blockless Ruby Filters" do
|
|
79
105
|
@d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\') AND (x NOT LIKE \'b\'))'
|
80
106
|
@d.l(~:x.like(/a/, /b/i)).should == '((x !~ \'a\') AND (x !~* \'b\'))'
|
81
107
|
@d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\') AND (x !~ \'b\'))'
|
108
|
+
|
109
|
+
@d.l(~'a'.like(:x)).should == "('a' NOT LIKE x)"
|
110
|
+
@d.l(~'a'.like(:x, 'b')).should == "(('a' NOT LIKE x) AND ('a' NOT LIKE 'b'))"
|
111
|
+
@d.l(~'a'.like(:x, /b/)).should == "(('a' NOT LIKE x) AND ('a' !~ 'b'))"
|
112
|
+
@d.l(~'a'.like(:x, /b/i)).should == "(('a' NOT LIKE x) AND ('a' !~* 'b'))"
|
113
|
+
|
114
|
+
@d.l(~/a/.like(:x)).should == "('a' !~ x)"
|
115
|
+
@d.l(~/a/.like(:x, 'b')).should == "(('a' !~ x) AND ('a' !~ 'b'))"
|
116
|
+
@d.l(~/a/.like(:x, /b/)).should == "(('a' !~ x) AND ('a' !~ 'b'))"
|
117
|
+
@d.l(~/a/.like(:x, /b/i)).should == "(('a' !~ x) AND ('a' !~* 'b'))"
|
118
|
+
|
119
|
+
@d.l(~/a/i.like(:x)).should == "('a' !~* x)"
|
120
|
+
@d.l(~/a/i.like(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
121
|
+
@d.l(~/a/i.like(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
122
|
+
@d.l(~/a/i.like(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
82
123
|
end
|
83
124
|
|
84
125
|
it "should support ILIKE via Symbol#ilike" do
|
@@ -87,6 +128,21 @@ context "Blockless Ruby Filters" do
|
|
87
128
|
@d.l(:x.ilike('a', 'b')).should == '((x ILIKE \'a\') OR (x ILIKE \'b\'))'
|
88
129
|
@d.l(:x.ilike(/a/, /b/i)).should == '((x ~* \'a\') OR (x ~* \'b\'))'
|
89
130
|
@d.l(:x.ilike('a', /b/)).should == '((x ILIKE \'a\') OR (x ~* \'b\'))'
|
131
|
+
|
132
|
+
@d.l('a'.ilike(:x)).should == "('a' ILIKE x)"
|
133
|
+
@d.l('a'.ilike(:x, 'b')).should == "(('a' ILIKE x) OR ('a' ILIKE 'b'))"
|
134
|
+
@d.l('a'.ilike(:x, /b/)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
|
135
|
+
@d.l('a'.ilike(:x, /b/i)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
|
136
|
+
|
137
|
+
@d.l(/a/.ilike(:x)).should == "('a' ~* x)"
|
138
|
+
@d.l(/a/.ilike(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
139
|
+
@d.l(/a/.ilike(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
140
|
+
@d.l(/a/.ilike(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
141
|
+
|
142
|
+
@d.l(/a/i.ilike(:x)).should == "('a' ~* x)"
|
143
|
+
@d.l(/a/i.ilike(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
144
|
+
@d.l(/a/i.ilike(:x, /b/)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
145
|
+
@d.l(/a/i.ilike(:x, /b/i)).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
90
146
|
end
|
91
147
|
|
92
148
|
it "should support NOT ILIKE via Symbol#ilike and Symbol#~" do
|
@@ -95,6 +151,21 @@ context "Blockless Ruby Filters" do
|
|
95
151
|
@d.l(~:x.ilike('a', 'b')).should == '((x NOT ILIKE \'a\') AND (x NOT ILIKE \'b\'))'
|
96
152
|
@d.l(~:x.ilike(/a/, /b/i)).should == '((x !~* \'a\') AND (x !~* \'b\'))'
|
97
153
|
@d.l(~:x.ilike('a', /b/)).should == '((x NOT ILIKE \'a\') AND (x !~* \'b\'))'
|
154
|
+
|
155
|
+
@d.l(~'a'.ilike(:x)).should == "('a' NOT ILIKE x)"
|
156
|
+
@d.l(~'a'.ilike(:x, 'b')).should == "(('a' NOT ILIKE x) AND ('a' NOT ILIKE 'b'))"
|
157
|
+
@d.l(~'a'.ilike(:x, /b/)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
|
158
|
+
@d.l(~'a'.ilike(:x, /b/i)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
|
159
|
+
|
160
|
+
@d.l(~/a/.ilike(:x)).should == "('a' !~* x)"
|
161
|
+
@d.l(~/a/.ilike(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
162
|
+
@d.l(~/a/.ilike(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
163
|
+
@d.l(~/a/.ilike(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
164
|
+
|
165
|
+
@d.l(~/a/i.ilike(:x)).should == "('a' !~* x)"
|
166
|
+
@d.l(~/a/i.ilike(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
167
|
+
@d.l(~/a/i.ilike(:x, /b/)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
168
|
+
@d.l(~/a/i.ilike(:x, /b/i)).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|
98
169
|
end
|
99
170
|
|
100
171
|
it "should support negating ranges via Hash#~ and Range" do
|
@@ -373,3 +444,88 @@ context "Blockless Ruby Filters" do
|
|
373
444
|
end
|
374
445
|
end
|
375
446
|
end
|
447
|
+
|
448
|
+
context Sequel::SQL::VirtualRow do
|
449
|
+
before do
|
450
|
+
db = Sequel::Database.new
|
451
|
+
db.quote_identifiers = true
|
452
|
+
@d = db[:items]
|
453
|
+
@d.meta_def(:supports_window_functions?){true}
|
454
|
+
def @d.l(*args, &block)
|
455
|
+
literal(filter_expr(*args, &block))
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should treat methods without arguments as identifiers" do
|
460
|
+
@d.l{column}.should == '"column"'
|
461
|
+
end
|
462
|
+
|
463
|
+
it "should treat methods without arguments that have embedded double underscores as qualified identifiers" do
|
464
|
+
@d.l{table__column}.should == '"table"."column"'
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should treat methods with arguments as functions with the arguments" do
|
468
|
+
@d.l{function(arg1, 10, 'arg3')}.should == 'function("arg1", 10, \'arg3\')'
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should treat methods with a block and no arguments as a function call with no arguments" do
|
472
|
+
@d.l{version{}}.should == 'version()'
|
473
|
+
end
|
474
|
+
|
475
|
+
it "should treat methods with a block and a leading argument :* as a function call with the SQL wildcard" do
|
476
|
+
@d.l{count(:*){}}.should == 'count(*)'
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should treat methods with a block and a leading argument :distinct as a function call with DISTINCT and the additional method arguments" do
|
480
|
+
@d.l{count(:distinct, column1){}}.should == 'count(DISTINCT "column1")'
|
481
|
+
@d.l{count(:distinct, column1, column2){}}.should == 'count(DISTINCT "column1", "column2")'
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should raise an error if an unsupported argument is used with a block" do
|
485
|
+
proc{@d.l{count(:blah){}}}.should raise_error(Sequel::Error)
|
486
|
+
end
|
487
|
+
|
488
|
+
it "should treat methods with a block and a leading argument :over as a window function call" do
|
489
|
+
@d.l{rank(:over){}}.should == 'rank() OVER ()'
|
490
|
+
end
|
491
|
+
|
492
|
+
it "should support :partition options for window function calls" do
|
493
|
+
@d.l{rank(:over, :partition=>column1){}}.should == 'rank() OVER (PARTITION BY "column1")'
|
494
|
+
@d.l{rank(:over, :partition=>[column1, column2]){}}.should == 'rank() OVER (PARTITION BY "column1", "column2")'
|
495
|
+
end
|
496
|
+
|
497
|
+
it "should support :args options for window function calls" do
|
498
|
+
@d.l{avg(:over, :args=>column1){}}.should == 'avg("column1") OVER ()'
|
499
|
+
@d.l{avg(:over, :args=>[column1, column2]){}}.should == 'avg("column1", "column2") OVER ()'
|
500
|
+
end
|
501
|
+
|
502
|
+
it "should support :order option for window function calls" do
|
503
|
+
@d.l{rank(:over, :order=>column1){}}.should == 'rank() OVER (ORDER BY "column1")'
|
504
|
+
@d.l{rank(:over, :order=>[column1, column2]){}}.should == 'rank() OVER (ORDER BY "column1", "column2")'
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should support :window option for window function calls" do
|
508
|
+
@d.l{rank(:over, :window=>:win){}}.should == 'rank() OVER ("win")'
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should support :*=>true option for window function calls" do
|
512
|
+
@d.l{count(:over, :* =>true){}}.should == 'count(*) OVER ()'
|
513
|
+
end
|
514
|
+
|
515
|
+
it "should support :frame=>:all option for window function calls" do
|
516
|
+
@d.l{rank(:over, :frame=>:all){}}.should == 'rank() OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)'
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should support :frame=>:rows option for window function calls" do
|
520
|
+
@d.l{rank(:over, :frame=>:rows){}}.should == 'rank() OVER (ROWS UNBOUNDED PRECEDING)'
|
521
|
+
end
|
522
|
+
|
523
|
+
it "should support all these options together" do
|
524
|
+
@d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.should == 'count(*) OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS UNBOUNDED PRECEDING)'
|
525
|
+
end
|
526
|
+
|
527
|
+
it "should raise an error if window functions are not supported" do
|
528
|
+
@d.meta_def(:supports_window_functions?){false}
|
529
|
+
proc{@d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}}.should raise_error(Sequel::Error)
|
530
|
+
end
|
531
|
+
end
|
@@ -30,7 +30,18 @@ describe Sequel::Dataset, " graphing" do
|
|
30
30
|
|
31
31
|
it "#graph should accept a complex dataset and pass it directly to join" do
|
32
32
|
ds = @ds1.graph(@ds2.filter(:x=>1), {:x=>:id})
|
33
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y,
|
33
|
+
ds.sql.should == 'SELECT points.id, points.x, points.y, t1.id AS t1_id, t1.x AS t1_x, t1.y AS t1_y, t1.graph_id FROM points LEFT OUTER JOIN (SELECT * FROM lines WHERE (x = 1)) AS t1 ON (t1.x = points.id)'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "#graph should work on from_self datasets" do
|
37
|
+
ds = @ds1.from_self.graph(@ds2, :x=>:id)
|
38
|
+
ds.sql.should == 'SELECT t1.id, t1.x, t1.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM (SELECT * FROM points) AS t1 LEFT OUTER JOIN lines ON (lines.x = t1.id)'
|
39
|
+
ds = @ds1.graph(@ds2.from_self, :x=>:id)
|
40
|
+
ds.sql.should == 'SELECT points.id, points.x, points.y, t1.id AS t1_id, t1.x AS t1_x, t1.y AS t1_y, t1.graph_id FROM points LEFT OUTER JOIN (SELECT * FROM (SELECT * FROM lines) AS t1) AS t1 ON (t1.x = points.id)'
|
41
|
+
ds = @ds1.from_self.from_self.graph(@ds2.from_self.from_self, :x=>:id)
|
42
|
+
ds.sql.should == 'SELECT t1.id, t1.x, t1.y, t2.id AS t2_id, t2.x AS t2_x, t2.y AS t2_y, t2.graph_id FROM (SELECT * FROM (SELECT * FROM points) AS t1) AS t1 LEFT OUTER JOIN (SELECT * FROM (SELECT * FROM (SELECT * FROM lines) AS t1) AS t1) AS t2 ON (t2.x = t1.id)'
|
43
|
+
ds = @ds1.from(@ds1, @ds3).graph(@ds2.from_self, :x=>:id)
|
44
|
+
ds.sql.should == 'SELECT t1.id, t1.x, t1.y, t3.id AS t3_id, t3.x AS t3_x, t3.y AS t3_y, t3.graph_id FROM (SELECT * FROM points) AS t1, (SELECT * FROM graphs) AS t2 LEFT OUTER JOIN (SELECT * FROM (SELECT * FROM lines) AS t1) AS t3 ON (t3.x = t1.id)'
|
34
45
|
end
|
35
46
|
|
36
47
|
it "#graph should accept a symbol table name as the dataset" do
|
@@ -187,6 +198,14 @@ describe Sequel::Dataset, " graphing" do
|
|
187
198
|
results.first.should == {:points=>{:id=>1, :x=>2, :y=>3}, :lines=>{:id=>4, :x=>5, :y=>6, :graph_id=>7}, :graph=>{:id=>8, :x=>9, :y=>10, :graph_id=>11}}
|
188
199
|
end
|
189
200
|
|
201
|
+
it "#ungraphed should remove the splitting of result sets into component tables" do
|
202
|
+
ds = @ds1.graph(@ds2, :x=>:id).ungraphed
|
203
|
+
def ds.fetch_rows(sql, &block)
|
204
|
+
yield({:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7})
|
205
|
+
end
|
206
|
+
ds.all.should == [{:id=>1,:x=>2,:y=>3,:lines_id=>4,:lines_x=>5,:lines_y=>6,:graph_id=>7}]
|
207
|
+
end
|
208
|
+
|
190
209
|
it "#graph_each should give a nil value instead of a hash when all values for a table are nil" do
|
191
210
|
ds = @ds1.graph(@ds2, :x=>:id)
|
192
211
|
def ds.fetch_rows(sql, &block)
|
data/spec/core/schema_spec.rb
CHANGED
@@ -762,11 +762,11 @@ context "Schema Parser" do
|
|
762
762
|
sqls << t
|
763
763
|
[[:a, {:db_type=>t.to_s}]]
|
764
764
|
end
|
765
|
-
@db.schema(:x).should == [[:a, {:db_type=>"x"}]]
|
765
|
+
@db.schema(:x).should == [[:a, {:db_type=>"x", :ruby_default=>nil}]]
|
766
766
|
@sqls.should == ['x']
|
767
|
-
@db.schema(:x).should == [[:a, {:db_type=>"x"}]]
|
767
|
+
@db.schema(:x).should == [[:a, {:db_type=>"x", :ruby_default=>nil}]]
|
768
768
|
@sqls.should == ['x']
|
769
|
-
@db.schema(:x, :reload=>true).should == [[:a, {:db_type=>"x"}]]
|
769
|
+
@db.schema(:x, :reload=>true).should == [[:a, {:db_type=>"x", :ruby_default=>nil}]]
|
770
770
|
@sqls.should == ['x', 'x']
|
771
771
|
end
|
772
772
|
|
@@ -775,11 +775,11 @@ context "Schema Parser" do
|
|
775
775
|
[[t, {:db_type=>t}]]
|
776
776
|
end
|
777
777
|
s1 = @db.schema(:x)
|
778
|
-
s1.should == [['x', {:db_type=>'x'}]]
|
778
|
+
s1.should == [['x', {:db_type=>'x', :ruby_default=>nil}]]
|
779
779
|
@db.schema(:x).object_id.should == s1.object_id
|
780
780
|
@db.schema(:x.identifier).object_id.should == s1.object_id
|
781
781
|
s2 = @db.schema(:x__y)
|
782
|
-
s2.should == [['y', {:db_type=>'y'}]]
|
782
|
+
s2.should == [['y', {:db_type=>'y', :ruby_default=>nil}]]
|
783
783
|
@db.schema(:x__y).object_id.should == s2.object_id
|
784
784
|
@db.schema(:y.qualify(:x)).object_id.should == s2.object_id
|
785
785
|
end
|
@@ -47,39 +47,6 @@ context "Migration#apply" do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
class DummyMigrationDataset
|
51
|
-
attr_reader :from
|
52
|
-
|
53
|
-
def initialize(x); @from = x; end
|
54
|
-
|
55
|
-
@@version = nil
|
56
|
-
|
57
|
-
def version; @@version; end
|
58
|
-
def version=(x); @@version = x; end
|
59
|
-
def first; @@version ? {:version => @@version} : nil; end
|
60
|
-
def update(h); @@version = h[:version]; end
|
61
|
-
def <<(h); @@version = h[:version]; end
|
62
|
-
end
|
63
|
-
|
64
|
-
class DummyMigrationDB
|
65
|
-
attr_reader :creates, :drops, :table_created
|
66
|
-
|
67
|
-
def initialize
|
68
|
-
@creates = []
|
69
|
-
@drops = []
|
70
|
-
end
|
71
|
-
|
72
|
-
def create(x); @creates << x; end
|
73
|
-
def drop(x); @drops << x; end
|
74
|
-
|
75
|
-
def [](x); DummyMigrationDataset.new(x); end
|
76
|
-
|
77
|
-
def create_table(x); raise if @table_created == x; @table_created = x; end
|
78
|
-
def table_exists?(x); @table_created == x; end
|
79
|
-
|
80
|
-
def transaction; yield; end
|
81
|
-
end
|
82
|
-
|
83
50
|
MIGRATION_001 = %[
|
84
51
|
class CreateSessions < Sequel::Migration
|
85
52
|
def up
|
@@ -128,16 +95,76 @@ MIGRATION_005 = %[
|
|
128
95
|
end
|
129
96
|
]
|
130
97
|
|
98
|
+
ALT_MIGRATION_001 = %[
|
99
|
+
class CreateAltBasic < Sequel::Migration
|
100
|
+
def up
|
101
|
+
create(11111)
|
102
|
+
end
|
103
|
+
|
104
|
+
def down
|
105
|
+
drop(11111)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
]
|
109
|
+
|
110
|
+
ALT_MIGRATION_003 = %[
|
111
|
+
class CreateAltAdvanced < Sequel::Migration
|
112
|
+
def up
|
113
|
+
create(33333)
|
114
|
+
end
|
115
|
+
|
116
|
+
def down
|
117
|
+
drop(33333)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
]
|
121
|
+
|
131
122
|
context "Sequel::Migrator" do
|
132
123
|
before do
|
133
|
-
|
124
|
+
dbc = Class.new(MockDatabase) do
|
125
|
+
attr_reader :creates, :drops, :tables_created, :columns_created, :versions
|
126
|
+
def initialize(*args)
|
127
|
+
super
|
128
|
+
@creates = []
|
129
|
+
@drops = []
|
130
|
+
@tables_created = []
|
131
|
+
@columns_created = []
|
132
|
+
@versions = {}
|
133
|
+
end
|
134
|
+
|
135
|
+
def create(x); @creates << x; end
|
136
|
+
def drop(x); @drops << x; end
|
137
|
+
|
138
|
+
def create_table(name, opts={}, &block)
|
139
|
+
super
|
140
|
+
@columns_created << / \(?(\w+) integer\)?\z/.match(sqls.last)[1].to_sym
|
141
|
+
@tables_created << name
|
142
|
+
end
|
143
|
+
|
144
|
+
def dataset(opts={})
|
145
|
+
ds = super
|
146
|
+
ds.extend(Module.new do
|
147
|
+
def columns; db.columns_created end
|
148
|
+
def insert(h); db.versions.merge!(h); super(h) end
|
149
|
+
def update(h); db.versions.merge!(h); super(h) end
|
150
|
+
def fetch_rows(sql); db.execute(sql); yield(db.versions) unless db.versions.empty? end
|
151
|
+
end)
|
152
|
+
ds
|
153
|
+
end
|
154
|
+
|
155
|
+
def table_exists?(name)
|
156
|
+
@tables_created.include?(name)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
@db = dbc.new
|
134
160
|
|
135
161
|
File.open('001_create_sessions.rb', 'w') {|f| f << MIGRATION_001}
|
136
162
|
File.open('002_create_nodes.rb', 'w') {|f| f << MIGRATION_002}
|
137
163
|
File.open('003_create_users.rb', 'w') {|f| f << MIGRATION_003}
|
138
164
|
File.open('005_5_create_attributes.rb', 'w') {|f| f << MIGRATION_005}
|
139
|
-
|
140
|
-
|
165
|
+
Dir.mkdir("alt_app")
|
166
|
+
File.open('alt_app/001_create_alt_basic.rb', 'w') {|f| f << ALT_MIGRATION_001}
|
167
|
+
File.open('alt_app/003_create_alt_advanced.rb', 'w') {|f| f << ALT_MIGRATION_003}
|
141
168
|
end
|
142
169
|
|
143
170
|
after do
|
@@ -145,77 +172,97 @@ context "Sequel::Migrator" do
|
|
145
172
|
Object.send(:remove_const, "CreateNodes") if Object.const_defined?("CreateNodes")
|
146
173
|
Object.send(:remove_const, "CreateUsers") if Object.const_defined?("CreateUsers")
|
147
174
|
Object.send(:remove_const, "CreateAttributes") if Object.const_defined?("CreateAttributes")
|
175
|
+
Object.send(:remove_const, "CreateAltBasic") if Object.const_defined?("CreateAltBasic")
|
176
|
+
Object.send(:remove_const, "CreateAltAdvanced") if Object.const_defined?("CreateAltAdvanced")
|
148
177
|
|
149
178
|
File.delete('001_create_sessions.rb')
|
150
179
|
File.delete('002_create_nodes.rb')
|
151
180
|
File.delete('003_create_users.rb')
|
152
181
|
File.delete('005_5_create_attributes.rb')
|
182
|
+
File.delete("alt_app/001_create_alt_basic.rb")
|
183
|
+
File.delete("alt_app/003_create_alt_advanced.rb")
|
184
|
+
Dir.rmdir("alt_app")
|
153
185
|
end
|
154
186
|
|
155
|
-
specify "should return the list of files for a specified version range" do
|
156
|
-
Sequel::Migrator.migration_files('.', 1..1).should ==
|
157
|
-
|
158
|
-
|
159
|
-
Sequel::Migrator.migration_files('.', 1..3).should == \
|
160
|
-
['./001_create_sessions.rb', './002_create_nodes.rb', './003_create_users.rb']
|
161
|
-
|
162
|
-
Sequel::Migrator.migration_files('.', 3..6).should == \
|
163
|
-
['./003_create_users.rb', './005_5_create_attributes.rb']
|
164
|
-
|
187
|
+
specify "#migration_files should return the list of files for a specified version range" do
|
188
|
+
Sequel::Migrator.migration_files('.', 1..1).should == ['./001_create_sessions.rb']
|
189
|
+
Sequel::Migrator.migration_files('.', 1..3).should == ['./001_create_sessions.rb', './002_create_nodes.rb', './003_create_users.rb']
|
190
|
+
Sequel::Migrator.migration_files('.', 3..6).should == ['./003_create_users.rb', './005_5_create_attributes.rb']
|
165
191
|
Sequel::Migrator.migration_files('.', 7..8).should == []
|
192
|
+
Sequel::Migrator.migration_files('alt_app', 1..1).should == ['alt_app/001_create_alt_basic.rb']
|
193
|
+
Sequel::Migrator.migration_files('alt_app', 1..3).should == ['alt_app/001_create_alt_basic.rb','alt_app/003_create_alt_advanced.rb']
|
166
194
|
end
|
167
195
|
|
168
|
-
specify "should return the latest version available" do
|
196
|
+
specify "#latest_migration_version should return the latest version available" do
|
169
197
|
Sequel::Migrator.latest_migration_version('.').should == 5
|
198
|
+
Sequel::Migrator.latest_migration_version('alt_app').should == 3
|
170
199
|
end
|
171
200
|
|
172
|
-
specify "should load the migration classes for the specified range" do
|
173
|
-
Sequel::Migrator.migration_classes('.', 3, 0, :up).should ==
|
174
|
-
|
201
|
+
specify "#migration_classes should load the migration classes for the specified range for the up direction" do
|
202
|
+
Sequel::Migrator.migration_classes('.', 3, 0, :up).should == [CreateSessions, CreateNodes, CreateUsers]
|
203
|
+
Sequel::Migrator.migration_classes('alt_app', 3, 0, :up).should == [CreateAltBasic, CreateAltAdvanced]
|
175
204
|
end
|
176
205
|
|
177
|
-
specify "should load the migration classes for the specified range" do
|
178
|
-
Sequel::Migrator.migration_classes('.', 0, 5, :down).should ==
|
179
|
-
|
206
|
+
specify "#migration_classes should load the migration classes for the specified range for the down direction" do
|
207
|
+
Sequel::Migrator.migration_classes('.', 0, 5, :down).should == [CreateAttributes, CreateUsers, CreateNodes, CreateSessions]
|
208
|
+
Sequel::Migrator.migration_classes('alt_app', 0, 3, :down).should == [CreateAltAdvanced, CreateAltBasic]
|
180
209
|
end
|
181
210
|
|
182
|
-
specify "should start from current + 1 for the up direction" do
|
183
|
-
Sequel::Migrator.migration_classes('.', 3, 1, :up).should ==
|
184
|
-
|
211
|
+
specify "#migration_classes should start from current + 1 for the up direction" do
|
212
|
+
Sequel::Migrator.migration_classes('.', 3, 1, :up).should == [CreateNodes, CreateUsers]
|
213
|
+
Sequel::Migrator.migration_classes('alt_app', 3, 2, :up).should == [CreateAltAdvanced]
|
185
214
|
end
|
186
215
|
|
187
|
-
specify "should end on current + 1 for the down direction" do
|
188
|
-
Sequel::Migrator.migration_classes('.', 2, 5, :down).should ==
|
189
|
-
|
216
|
+
specify "#migration_classes should end on current + 1 for the down direction" do
|
217
|
+
Sequel::Migrator.migration_classes('.', 2, 5, :down).should == [CreateAttributes, CreateUsers]
|
218
|
+
Sequel::Migrator.migration_classes('alt_app', 2, 4, :down).should == [CreateAltAdvanced]
|
190
219
|
end
|
191
220
|
|
192
|
-
specify "should automatically create the schema_info table" do
|
221
|
+
specify "#schema_info_dataset should automatically create the schema_info table" do
|
193
222
|
@db.table_exists?(:schema_info).should be_false
|
194
223
|
Sequel::Migrator.schema_info_dataset(@db)
|
195
224
|
@db.table_exists?(:schema_info).should be_true
|
196
|
-
|
197
|
-
|
198
|
-
|
225
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)"]
|
226
|
+
end
|
227
|
+
|
228
|
+
specify "should automatically create new APP_version column in schema_info" do
|
229
|
+
@db.table_exists?(:schema_info).should be_false
|
230
|
+
Sequel::Migrator.schema_info_dataset(@db, :column => :alt_version)
|
231
|
+
@db.table_exists?(:schema_info).should be_true
|
232
|
+
@db.sqls.should == ["CREATE TABLE schema_info (alt_version integer)"]
|
233
|
+
end
|
234
|
+
|
235
|
+
specify "should automatically create new APP_version column in schema_info" do
|
236
|
+
@db.table_exists?(:alt_table).should be_false
|
237
|
+
Sequel::Migrator.schema_info_dataset(@db, :table => :alt_table)
|
238
|
+
@db.table_exists?(:alt_table).should be_true
|
239
|
+
Sequel::Migrator.schema_info_dataset(@db, :table => :alt_table, :column=>:alt_version)
|
240
|
+
@db.sqls.should == ["CREATE TABLE alt_table (version integer)",
|
241
|
+
"ALTER TABLE alt_table ADD COLUMN alt_version integer"]
|
199
242
|
end
|
200
243
|
|
201
|
-
specify "should return a dataset for the
|
202
|
-
|
203
|
-
|
244
|
+
specify "should return a dataset for the correct table" do
|
245
|
+
Sequel::Migrator.schema_info_dataset(@db).first_source_alias.should == :schema_info
|
246
|
+
Sequel::Migrator.schema_info_dataset(@db, :table=>:blah).first_source_alias.should == :blah
|
204
247
|
end
|
205
248
|
|
206
|
-
specify "should
|
207
|
-
# default is 0
|
249
|
+
specify "should assume a migration version of 0 if no migration information exists in the database" do
|
208
250
|
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
209
|
-
|
210
|
-
|
211
|
-
|
251
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)", "SELECT * FROM schema_info LIMIT 1"]
|
252
|
+
end
|
253
|
+
specify "should use the migration version stored in the database" do
|
254
|
+
Sequel::Migrator.schema_info_dataset(@db).insert(:version => 4321)
|
212
255
|
Sequel::Migrator.get_current_migration_version(@db).should == 4321
|
256
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)", "INSERT INTO schema_info (version) VALUES (4321)", "SELECT * FROM schema_info LIMIT 1"]
|
213
257
|
end
|
214
258
|
|
215
259
|
specify "should set the migration version stored in the database" do
|
216
|
-
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
217
260
|
Sequel::Migrator.set_current_migration_version(@db, 6666)
|
218
261
|
Sequel::Migrator.get_current_migration_version(@db).should == 6666
|
262
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
263
|
+
"SELECT * FROM schema_info LIMIT 1",
|
264
|
+
"INSERT INTO schema_info (version) VALUES (6666)",
|
265
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
219
266
|
end
|
220
267
|
|
221
268
|
specify "should apply migrations correctly in the up direction" do
|
@@ -228,6 +275,14 @@ context "Sequel::Migrator" do
|
|
228
275
|
@db.creates.should == [3333, 5555]
|
229
276
|
|
230
277
|
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
278
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
279
|
+
"SELECT * FROM schema_info LIMIT 1",
|
280
|
+
"INSERT INTO schema_info (version) VALUES (3)",
|
281
|
+
"SELECT * FROM schema_info LIMIT 1",
|
282
|
+
"SELECT * FROM schema_info LIMIT 1",
|
283
|
+
"SELECT * FROM schema_info LIMIT 1",
|
284
|
+
"UPDATE schema_info SET version = 5",
|
285
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
231
286
|
end
|
232
287
|
|
233
288
|
specify "should apply migrations correctly in the down direction" do
|
@@ -235,6 +290,10 @@ context "Sequel::Migrator" do
|
|
235
290
|
@db.drops.should == [5555, 3333, 2222]
|
236
291
|
|
237
292
|
Sequel::Migrator.get_current_migration_version(@db).should == 1
|
293
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
294
|
+
"SELECT * FROM schema_info LIMIT 1",
|
295
|
+
"INSERT INTO schema_info (version) VALUES (1)",
|
296
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
238
297
|
end
|
239
298
|
|
240
299
|
specify "should apply migrations up to the latest version if no target is given" do
|
@@ -242,6 +301,11 @@ context "Sequel::Migrator" do
|
|
242
301
|
@db.creates.should == [1111, 2222, 3333, 5555]
|
243
302
|
|
244
303
|
Sequel::Migrator.get_current_migration_version(@db).should == 5
|
304
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
305
|
+
"SELECT * FROM schema_info LIMIT 1",
|
306
|
+
"SELECT * FROM schema_info LIMIT 1",
|
307
|
+
"INSERT INTO schema_info (version) VALUES (5)",
|
308
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
245
309
|
end
|
246
310
|
|
247
311
|
specify "should apply migrations down to 0 version correctly" do
|
@@ -249,13 +313,15 @@ context "Sequel::Migrator" do
|
|
249
313
|
@db.drops.should == [5555, 3333, 2222, 1111]
|
250
314
|
|
251
315
|
Sequel::Migrator.get_current_migration_version(@db).should == 0
|
316
|
+
@db.sqls.should == ["CREATE TABLE schema_info (version integer)",
|
317
|
+
"SELECT * FROM schema_info LIMIT 1",
|
318
|
+
"INSERT INTO schema_info (version) VALUES (0)",
|
319
|
+
"SELECT * FROM schema_info LIMIT 1"]
|
252
320
|
end
|
253
321
|
|
254
322
|
specify "should return the target version" do
|
255
323
|
Sequel::Migrator.apply(@db, '.', 3, 2).should == 3
|
256
|
-
|
257
324
|
Sequel::Migrator.apply(@db, '.', 0).should == 0
|
258
|
-
|
259
325
|
Sequel::Migrator.apply(@db, '.').should == 5
|
260
326
|
end
|
261
327
|
end
|