sequel 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|