sequel 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
@@ -5,6 +5,15 @@ unless defined?(FIREBIRD_DB)
|
|
5
5
|
FIREBIRD_DB = Sequel.connect(ENV['SEQUEL_FB_SPEC_DB']||FIREBIRD_URL)
|
6
6
|
end
|
7
7
|
|
8
|
+
def FIREBIRD_DB.sqls
|
9
|
+
(@sqls ||= [])
|
10
|
+
end
|
11
|
+
logger = Object.new
|
12
|
+
def logger.method_missing(m, msg)
|
13
|
+
FIREBIRD_DB.sqls.push(msg)
|
14
|
+
end
|
15
|
+
FIREBIRD_DB.logger = logger
|
16
|
+
|
8
17
|
FIREBIRD_DB.create_table! :test do
|
9
18
|
varchar :name, :size => 50
|
10
19
|
integer :val, :index => true
|
@@ -30,7 +39,7 @@ FIREBIRD_DB.create_table! :test6 do
|
|
30
39
|
blob :val
|
31
40
|
String :val2
|
32
41
|
varchar :val3, :size=>200
|
33
|
-
|
42
|
+
String :val4, :text=>true
|
34
43
|
end
|
35
44
|
|
36
45
|
context "A Firebird database" do
|
@@ -54,6 +63,7 @@ context "A Firebird dataset" do
|
|
54
63
|
before do
|
55
64
|
@d = FIREBIRD_DB[:test]
|
56
65
|
@d.delete # remove all records
|
66
|
+
@d.quote_identifiers = true
|
57
67
|
end
|
58
68
|
|
59
69
|
specify "should return the correct record count" do
|
@@ -236,81 +246,74 @@ end
|
|
236
246
|
context "A Firebird database" do
|
237
247
|
before do
|
238
248
|
@db = FIREBIRD_DB
|
249
|
+
@db.drop_table(:posts) rescue nil
|
250
|
+
@db.sqls.clear
|
239
251
|
end
|
240
252
|
|
241
253
|
specify "should allow us to name the sequences" do
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
254
|
+
@db.create_table(:posts){primary_key :id, :sequence_name => "seq_test"}
|
255
|
+
@db.sqls.should == [
|
256
|
+
"DROP SEQUENCE SEQ_TEST",
|
246
257
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
247
258
|
"CREATE SEQUENCE SEQ_TEST",
|
248
259
|
" CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
|
249
|
-
]
|
260
|
+
]
|
250
261
|
end
|
251
262
|
|
252
263
|
specify "should allow us to set the starting position for the sequences" do
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
264
|
+
@db.create_table(:posts){primary_key :id, :sequence_start_position => 999}
|
265
|
+
@db.sqls.should == [
|
266
|
+
"DROP SEQUENCE SEQ_POSTS_ID",
|
257
267
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
258
268
|
"CREATE SEQUENCE SEQ_POSTS_ID",
|
259
269
|
"ALTER SEQUENCE SEQ_POSTS_ID RESTART WITH 999",
|
260
270
|
" CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
|
261
|
-
]
|
271
|
+
]
|
262
272
|
end
|
263
273
|
|
264
274
|
specify "should allow us to name and set the starting position for the sequences" do
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
275
|
+
@db.create_table(:posts){primary_key :id, :sequence_name => "seq_test", :sequence_start_position => 999}
|
276
|
+
@db.sqls.should == [
|
277
|
+
"DROP SEQUENCE SEQ_TEST",
|
269
278
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
270
279
|
"CREATE SEQUENCE SEQ_TEST",
|
271
280
|
"ALTER SEQUENCE SEQ_TEST RESTART WITH 999",
|
272
281
|
" CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_test;\n end\n end\n\n"
|
273
|
-
]
|
282
|
+
]
|
274
283
|
end
|
275
284
|
|
276
285
|
specify "should allow us to name the triggers" do
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
286
|
+
@db.create_table(:posts){primary_key :id, :trigger_name => "trig_test"}
|
287
|
+
@db.sqls.should == [
|
288
|
+
"DROP SEQUENCE SEQ_POSTS_ID",
|
281
289
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
282
290
|
"CREATE SEQUENCE SEQ_POSTS_ID",
|
283
291
|
" CREATE TRIGGER TRIG_TEST for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
|
284
|
-
]
|
292
|
+
]
|
285
293
|
end
|
286
294
|
|
287
295
|
specify "should allow us to not create the sequence" do
|
288
|
-
|
289
|
-
|
290
|
-
end
|
291
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
296
|
+
@db.create_table(:posts){primary_key :id, :create_sequence => false}
|
297
|
+
@db.sqls.should == [
|
292
298
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
293
299
|
" CREATE TRIGGER BI_POSTS_ID for POSTS\n ACTIVE BEFORE INSERT position 0\n as begin\n if ((new.ID is null) or (new.ID = 0)) then\n begin\n new.ID = next value for seq_posts_id;\n end\n end\n\n"
|
294
|
-
]
|
300
|
+
]
|
295
301
|
end
|
296
302
|
|
297
303
|
specify "should allow us to not create the trigger" do
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
304
|
+
@db.create_table(:posts){primary_key :id, :create_trigger => false}
|
305
|
+
@db.sqls.should == [
|
306
|
+
"DROP SEQUENCE SEQ_POSTS_ID",
|
302
307
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )",
|
303
308
|
"CREATE SEQUENCE SEQ_POSTS_ID",
|
304
|
-
]
|
309
|
+
]
|
305
310
|
end
|
306
311
|
|
307
312
|
specify "should allow us to not create either the sequence nor the trigger" do
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
FIREBIRD_DB.send(:create_table_sql_list, :posts, *g.create_info).should == [[
|
313
|
+
@db.create_table(:posts){primary_key :id, :create_sequence => false, :create_trigger => false}
|
314
|
+
@db.sqls.should == [
|
312
315
|
"CREATE TABLE POSTS (ID integer PRIMARY KEY )"
|
313
|
-
]
|
316
|
+
]
|
314
317
|
end
|
315
318
|
|
316
319
|
specify "should support column operations" do
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -29,15 +29,9 @@ MYSQL_DB.drop_table(:items) rescue nil
|
|
29
29
|
MYSQL_DB.drop_table(:dolls) rescue nil
|
30
30
|
MYSQL_DB.drop_table(:booltest) rescue nil
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
SQL_COMMIT = 'Transaction.commit'
|
36
|
-
else
|
37
|
-
SQL_BEGIN = 'BEGIN'
|
38
|
-
SQL_ROLLBACK = 'ROLLBACK'
|
39
|
-
SQL_COMMIT = 'COMMIT'
|
40
|
-
end
|
32
|
+
SQL_BEGIN = 'BEGIN'
|
33
|
+
SQL_ROLLBACK = 'ROLLBACK'
|
34
|
+
SQL_COMMIT = 'COMMIT'
|
41
35
|
|
42
36
|
context "MySQL", '#create_table' do
|
43
37
|
before do
|
@@ -57,6 +51,16 @@ context "MySQL", '#create_table' do
|
|
57
51
|
@db.create_table(:tmp_dolls, :temp => true, :engine => 'MyISAM', :charset => 'latin2'){text :name}
|
58
52
|
@db.sqls.should == ["CREATE TEMPORARY TABLE tmp_dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
|
59
53
|
end
|
54
|
+
|
55
|
+
specify "should not use a default for a String :text=>true type" do
|
56
|
+
@db.create_table(:dolls){String :name, :text=>true, :default=>'blah'}
|
57
|
+
@db.sqls.should == ["CREATE TABLE dolls (name text)"]
|
58
|
+
end
|
59
|
+
|
60
|
+
specify "should not use a default for a File type" do
|
61
|
+
@db.create_table(:dolls){File :name, :default=>'blah'}
|
62
|
+
@db.sqls.should == ["CREATE TABLE dolls (name blob)"]
|
63
|
+
end
|
60
64
|
end
|
61
65
|
|
62
66
|
context "A MySQL database" do
|
@@ -577,6 +581,12 @@ context "A MySQL database" do
|
|
577
581
|
"CREATE UNIQUE INDEX posts_id_index USING btree ON posts (id)"
|
578
582
|
]
|
579
583
|
end
|
584
|
+
|
585
|
+
specify "should not dump partial indexes" do
|
586
|
+
@db.create_table(:posts){text :id}
|
587
|
+
@db << "CREATE INDEX posts_id_index ON posts (id(10))"
|
588
|
+
@db.indexes(:posts).should == {}
|
589
|
+
end
|
580
590
|
end
|
581
591
|
|
582
592
|
context "MySQL::Dataset#insert and related methods" do
|
@@ -710,6 +720,12 @@ context "MySQL::Dataset#insert and related methods" do
|
|
710
720
|
]
|
711
721
|
end
|
712
722
|
|
723
|
+
specify "#insert_ignore should add the IGNORE keyword for single inserts" do
|
724
|
+
@d.insert_ignore.insert(:name => 'ghi')
|
725
|
+
MYSQL_DB.sqls.should == ["INSERT IGNORE INTO items (name) VALUES ('ghi')"]
|
726
|
+
@d.all.should == [{:name => 'ghi', :value => nil}]
|
727
|
+
end
|
728
|
+
|
713
729
|
specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and ALL columns when no args given" do
|
714
730
|
@d.on_duplicate_key_update.import([:name,:value],
|
715
731
|
[['abc', 1], ['def',2]]
|
@@ -121,30 +121,6 @@ context "A PostgreSQL dataset" do
|
|
121
121
|
@d.reverse_order(:name.desc, :test).sql.should == \
|
122
122
|
'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
|
123
123
|
end
|
124
|
-
|
125
|
-
specify "should support nested transactions through savepoints using the savepoint option" do
|
126
|
-
POSTGRES_DB.transaction do
|
127
|
-
@d << {:name => '1'}
|
128
|
-
POSTGRES_DB.transaction(:savepoint=>true) do
|
129
|
-
@d << {:name => '2'}
|
130
|
-
POSTGRES_DB.transaction do
|
131
|
-
@d << {:name => '3'}
|
132
|
-
raise Sequel::Rollback
|
133
|
-
end
|
134
|
-
end
|
135
|
-
@d << {:name => '4'}
|
136
|
-
POSTGRES_DB.transaction do
|
137
|
-
@d << {:name => '6'}
|
138
|
-
POSTGRES_DB.transaction(:savepoint=>true) do
|
139
|
-
@d << {:name => '7'}
|
140
|
-
raise Sequel::Rollback
|
141
|
-
end
|
142
|
-
end
|
143
|
-
@d << {:name => '5'}
|
144
|
-
end
|
145
|
-
|
146
|
-
@d.order(:name).map(:name).should == %w{1 4 5 6}
|
147
|
-
end
|
148
124
|
|
149
125
|
specify "should support regexps" do
|
150
126
|
@d << {:name => 'abc', :value => 1}
|
@@ -227,6 +203,11 @@ context "A PostgreSQL database" do
|
|
227
203
|
@db[:posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
|
228
204
|
end
|
229
205
|
|
206
|
+
specify "should not raise an error if attempting to resetting the primary key sequence for a table without a primary key" do
|
207
|
+
@db.create_table(:posts){Integer :a}
|
208
|
+
@db.reset_primary_key_sequence(:posts).should == nil
|
209
|
+
end
|
210
|
+
|
230
211
|
specify "should support fulltext indexes and searching" do
|
231
212
|
@db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
|
232
213
|
@db.sqls.should == [
|
@@ -340,6 +321,12 @@ context "Postgres::Dataset#insert" do
|
|
340
321
|
@db.drop_table(:test5) rescue nil
|
341
322
|
end
|
342
323
|
|
324
|
+
specify "should work with static SQL" do
|
325
|
+
@ds.with_sql('INSERT INTO test5 (value) VALUES (10)').insert.should == nil
|
326
|
+
@db['INSERT INTO test5 (value) VALUES (20)'].insert.should == nil
|
327
|
+
@ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}]
|
328
|
+
end
|
329
|
+
|
343
330
|
specify "should work regardless of how it is used" do
|
344
331
|
@ds.insert(:value=>10).should == 1
|
345
332
|
@ds.disable_insert_returning.insert(:value=>20).should == 2
|
data/spec/core/database_spec.rb
CHANGED
@@ -188,6 +188,12 @@ context "Database#disconnect" do
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
+
context "Sequel.extension" do
|
192
|
+
specify "should attempt to load the given extension" do
|
193
|
+
proc{Sequel.extension :blah}.should raise_error(LoadError)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
191
197
|
context "Database#connect" do
|
192
198
|
specify "should raise NotImplementedError" do
|
193
199
|
proc {Sequel::Database.new.connect}.should raise_error(NotImplementedError)
|
@@ -551,19 +557,10 @@ context "Database#rename_table" do
|
|
551
557
|
end
|
552
558
|
|
553
559
|
context "Database#table_exists?" do
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
end
|
559
|
-
|
560
|
-
specify "should use schema information if available" do
|
561
|
-
@db.table_exists?(:a).should be_true
|
562
|
-
end
|
563
|
-
|
564
|
-
specify "should otherwise try to select the first record from the table's dataset" do
|
565
|
-
@db2.table_exists?(:a).should be_false
|
566
|
-
@db2.table_exists?(:b).should be_true
|
560
|
+
specify "should try to select the first record from the table's dataset" do
|
561
|
+
db2 = DummyDatabase.new
|
562
|
+
db2.table_exists?(:a).should be_false
|
563
|
+
db2.table_exists?(:b).should be_true
|
567
564
|
end
|
568
565
|
end
|
569
566
|
|
@@ -617,6 +614,14 @@ context "Database#transaction" do
|
|
617
614
|
@db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
|
618
615
|
end
|
619
616
|
|
617
|
+
specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
|
618
|
+
@db.meta_def(:commit_transaction){raise ArgumentError}
|
619
|
+
lambda{@db.transaction{}}.should raise_error(ArgumentError)
|
620
|
+
|
621
|
+
@db.meta_def(:database_error_classes){[ArgumentError]}
|
622
|
+
lambda{@db.transaction{}}.should raise_error(Sequel::DatabaseError)
|
623
|
+
end
|
624
|
+
|
620
625
|
specify "should be re-entrant" do
|
621
626
|
stop = false
|
622
627
|
cc = nil
|
@@ -1091,6 +1096,10 @@ context "Database#raise_error" do
|
|
1091
1096
|
specify "should convert the exception to a DatabaseError if opts[:classes] if not present" do
|
1092
1097
|
proc{MockDatabase.new.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
|
1093
1098
|
end
|
1099
|
+
|
1100
|
+
specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
|
1101
|
+
proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
|
1102
|
+
end
|
1094
1103
|
end
|
1095
1104
|
|
1096
1105
|
context "Database#typecast_value" do
|
@@ -1140,3 +1149,35 @@ context "Database#schema_autoincrementing_primary_key?" do
|
|
1140
1149
|
m.call(:primary_key=>false).should == false
|
1141
1150
|
end
|
1142
1151
|
end
|
1152
|
+
|
1153
|
+
context "Database#supports_savepoints?" do
|
1154
|
+
specify "should be false by default" do
|
1155
|
+
Sequel::Database.new.supports_savepoints?.should == false
|
1156
|
+
end
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
context "Database#input_identifier_meth" do
|
1160
|
+
specify "should be the input_identifer method of a default dataset for this database" do
|
1161
|
+
db = Sequel::Database.new
|
1162
|
+
db.send(:input_identifier_meth).call(:a).should == 'a'
|
1163
|
+
db.identifier_input_method = :upcase
|
1164
|
+
db.send(:input_identifier_meth).call(:a).should == 'A'
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
context "Database#output_identifier_meth" do
|
1169
|
+
specify "should be the output_identifer method of a default dataset for this database" do
|
1170
|
+
db = Sequel::Database.new
|
1171
|
+
db.send(:output_identifier_meth).call('A').should == :A
|
1172
|
+
db.identifier_output_method = :downcase
|
1173
|
+
db.send(:output_identifier_meth).call('A').should == :a
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
context "Database#metadata_dataset" do
|
1178
|
+
specify "should be a dataset with the default settings for identifier_input_method and identifier_output_method" do
|
1179
|
+
ds = Sequel::Database.new.send(:metadata_dataset)
|
1180
|
+
ds.literal(:a).should == 'A'
|
1181
|
+
ds.send(:output_identifier, 'A').should == :a
|
1182
|
+
end
|
1183
|
+
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -1858,46 +1858,63 @@ context "Dataset compound operations" do
|
|
1858
1858
|
|
1859
1859
|
specify "should support UNION and UNION ALL" do
|
1860
1860
|
@a.union(@b).sql.should == \
|
1861
|
-
"SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)"
|
1861
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
|
1862
1862
|
@b.union(@a, true).sql.should == \
|
1863
|
-
"SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
|
1863
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1864
1864
|
end
|
1865
1865
|
|
1866
1866
|
specify "should support INTERSECT and INTERSECT ALL" do
|
1867
1867
|
@a.intersect(@b).sql.should == \
|
1868
|
-
"SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)"
|
1868
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
|
1869
1869
|
@b.intersect(@a, true).sql.should == \
|
1870
|
-
"SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
|
1870
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1871
1871
|
end
|
1872
1872
|
|
1873
1873
|
specify "should support EXCEPT and EXCEPT ALL" do
|
1874
1874
|
@a.except(@b).sql.should == \
|
1875
|
-
"SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)"
|
1875
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
|
1876
1876
|
@b.except(@a, true).sql.should == \
|
1877
|
-
"SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
|
1877
|
+
"SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1878
1878
|
end
|
1879
1879
|
|
1880
1880
|
specify "should handle chained compound operations" do
|
1881
1881
|
@a.union(@b).union(@a, true).sql.should == \
|
1882
|
-
"SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
|
1882
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1 UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1883
1883
|
@a.intersect(@b, true).intersect(@a).sql.should == \
|
1884
|
-
"SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
|
1884
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2)) AS t1 INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1"
|
1885
1885
|
@a.except(@b).except(@a, true).sql.should == \
|
1886
|
-
"SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
|
1887
|
-
@a.union(@b, true).intersect(@a).except(@b, true).union(@a).intersect(@b, true).except(@a).sql.should == \
|
1888
|
-
"SELECT * FROM a WHERE (z = 1) UNION ALL SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1) EXCEPT ALL SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
|
1886
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1 EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
|
1889
1887
|
end
|
1890
1888
|
|
1891
1889
|
specify "should use a subselect when using a compound operation with a dataset that already has a compound operation" do
|
1892
1890
|
@a.union(@b.union(@a, true)).sql.should == \
|
1893
|
-
"SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1))"
|
1891
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
|
1894
1892
|
@a.intersect(@b.intersect(@a), true).sql.should == \
|
1895
|
-
"SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1))"
|
1893
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
|
1896
1894
|
@a.except(@b.except(@a, true)).sql.should == \
|
1897
|
-
"SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1))"
|
1898
|
-
@a.union(@b.intersect(@a.except(@b, true)), true).union(@a.intersect(@b.except(@a), true)).sql.should == \
|
1899
|
-
"SELECT * FROM a WHERE (z = 1) UNION ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT ALL SELECT * FROM b WHERE (z = 2))) UNION SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)))"
|
1895
|
+
"SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
|
1900
1896
|
end
|
1897
|
+
|
1898
|
+
specify "should order and limit properly when using UNION, INTERSECT, or EXCEPT" do
|
1899
|
+
@dataset = Sequel::Dataset.new(nil).from(:test)
|
1900
|
+
@dataset.union(@dataset).limit(2).sql.should ==
|
1901
|
+
"SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
|
1902
|
+
@dataset.limit(2).intersect(@dataset).sql.should ==
|
1903
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
|
1904
|
+
@dataset.except(@dataset.limit(2)).sql.should ==
|
1905
|
+
"SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
|
1906
|
+
|
1907
|
+
@dataset.union(@dataset).order(:num).sql.should ==
|
1908
|
+
"SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
|
1909
|
+
@dataset.order(:num).intersect(@dataset).sql.should ==
|
1910
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1 INTERSECT SELECT * FROM test) AS t1"
|
1911
|
+
@dataset.except(@dataset.order(:num)).sql.should ==
|
1912
|
+
"SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1) AS t1"
|
1913
|
+
|
1914
|
+
@dataset.limit(2).order(:a).union(@dataset.limit(3).order(:b)).order(:c).limit(4).sql.should ==
|
1915
|
+
"SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY a LIMIT 2) AS t1 UNION SELECT * FROM (SELECT * FROM test ORDER BY b LIMIT 3) AS t1) AS t1 ORDER BY c LIMIT 4"
|
1916
|
+
end
|
1917
|
+
|
1901
1918
|
end
|
1902
1919
|
|
1903
1920
|
context "Dataset#[]" do
|
@@ -2404,6 +2421,10 @@ context "Dataset#insert_sql" do
|
|
2404
2421
|
specify "should accept array subscript references" do
|
2405
2422
|
@ds.insert_sql((:day.sql_subscript(1)) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
|
2406
2423
|
end
|
2424
|
+
|
2425
|
+
specify "should raise an Error if the dataset has no sources" do
|
2426
|
+
proc{Sequel::Database.new.dataset.insert_sql}.should raise_error(Sequel::Error)
|
2427
|
+
end
|
2407
2428
|
end
|
2408
2429
|
|
2409
2430
|
class DummyMummyDataset < Sequel::Dataset
|
@@ -2431,17 +2452,11 @@ end
|
|
2431
2452
|
context "Dataset#table_exists?" do
|
2432
2453
|
before do
|
2433
2454
|
@db = DummyMummyDatabase.new
|
2434
|
-
@db.instance_variable_set(:@schemas, {:a=>[]})
|
2435
|
-
@db2 = DummyMummyDatabase.new
|
2436
|
-
end
|
2437
|
-
|
2438
|
-
specify "should use the database schema if available" do
|
2439
|
-
@db[:a].table_exists?.should be_true
|
2440
2455
|
end
|
2441
2456
|
|
2442
2457
|
specify "should otherwise try to select the first record from the table's dataset" do
|
2443
|
-
@
|
2444
|
-
@
|
2458
|
+
@db[:a].table_exists?.should be_false
|
2459
|
+
@db[:b].table_exists?.should be_true
|
2445
2460
|
end
|
2446
2461
|
|
2447
2462
|
specify "should raise Sequel::Error if dataset references more than one table" do
|
@@ -2669,17 +2684,35 @@ context Sequel::Dataset::UnnumberedArgumentMapper do
|
|
2669
2684
|
def @ds.execute(sql, opts={}, &block)
|
2670
2685
|
@db.execute(sql, {:arguments=>bind_arguments}.merge(opts))
|
2671
2686
|
end
|
2672
|
-
|
2673
|
-
|
2687
|
+
def @ds.execute_dui(*args, &block)
|
2688
|
+
execute(*args, &block)
|
2689
|
+
end
|
2690
|
+
def @ds.execute_insert(*args, &block)
|
2691
|
+
execute(*args, &block)
|
2692
|
+
end
|
2693
|
+
@ps = []
|
2694
|
+
@ps << @ds.prepare(:select, :s)
|
2695
|
+
@ps << @ds.prepare(:all, :a)
|
2696
|
+
@ps << @ds.prepare(:first, :f)
|
2697
|
+
@ps << @ds.prepare(:delete, :d)
|
2698
|
+
@ps << @ds.prepare(:insert, :i, :num=>:$n)
|
2699
|
+
@ps << @ds.prepare(:update, :u, :num=>:$n)
|
2700
|
+
@ps.each{|p| p.extend(Sequel::Dataset::UnnumberedArgumentMapper)}
|
2674
2701
|
end
|
2675
2702
|
|
2676
2703
|
specify "#inspect should show the actual SQL submitted to the database" do
|
2677
|
-
@ps.inspect.should == '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
|
2704
|
+
@ps.first.inspect.should == '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
|
2678
2705
|
end
|
2679
2706
|
|
2680
2707
|
specify "should submitted the SQL to the database with placeholders and bind variables" do
|
2681
|
-
@ps.call(:n=>1)
|
2682
|
-
@db.sqls.should == [["SELECT * FROM items WHERE (num = ?)", 1]
|
2708
|
+
@ps.each{|p| p.call(:n=>1)}
|
2709
|
+
@db.sqls.should == [["SELECT * FROM items WHERE (num = ?)", 1],
|
2710
|
+
["SELECT * FROM items WHERE (num = ?)", 1],
|
2711
|
+
["SELECT * FROM items WHERE (num = ?) LIMIT 1", 1],
|
2712
|
+
["DELETE FROM items WHERE (num = ?)", 1],
|
2713
|
+
["INSERT INTO items (num) VALUES (?)", 1],
|
2714
|
+
["UPDATE items SET num = ? WHERE (num = ?)", 1, 1],
|
2715
|
+
]
|
2683
2716
|
end
|
2684
2717
|
end
|
2685
2718
|
|
@@ -2748,3 +2781,88 @@ context "Sequel::Dataset #set_overrides" do
|
|
2748
2781
|
@ds.set_overrides(:x=>2).update_sql.should == "UPDATE items SET x = 1"
|
2749
2782
|
end
|
2750
2783
|
end
|
2784
|
+
|
2785
|
+
context "Sequel::Dataset#qualify_to" do
|
2786
|
+
specify "should qualify_to the first source" do
|
2787
|
+
MockDatabase.new[:t].filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
|
2788
|
+
end
|
2789
|
+
end
|
2790
|
+
|
2791
|
+
context "Sequel::Dataset#qualify_to_first_source" do
|
2792
|
+
before do
|
2793
|
+
@ds = MockDatabase.new[:t]
|
2794
|
+
end
|
2795
|
+
|
2796
|
+
specify "should qualify_to the first source" do
|
2797
|
+
@ds.qualify_to_first_source.sql.should == 'SELECT t.* FROM t'
|
2798
|
+
@ds.should_receive(:qualify_to).with(:t).once
|
2799
|
+
@ds.qualify_to_first_source
|
2800
|
+
end
|
2801
|
+
|
2802
|
+
specify "should handle the select, order, where, having, and group options/clauses" do
|
2803
|
+
@ds.select(:a).filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
|
2804
|
+
'SELECT t.a FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
|
2805
|
+
end
|
2806
|
+
|
2807
|
+
specify "should handle the select using a table.* if all columns are currently selected" do
|
2808
|
+
@ds.filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
|
2809
|
+
'SELECT t.* FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
|
2810
|
+
end
|
2811
|
+
|
2812
|
+
specify "should handle hashes in select option" do
|
2813
|
+
@ds.select(:a=>:b).qualify_to_first_source.sql.should == 'SELECT t.a AS b FROM t'
|
2814
|
+
end
|
2815
|
+
|
2816
|
+
specify "should handle symbols" do
|
2817
|
+
@ds.select(:a, :b__c, :d___e, :f__g___h).qualify_to_first_source.sql.should == 'SELECT t.a, b.c, t.d AS e, f.g AS h FROM t'
|
2818
|
+
end
|
2819
|
+
|
2820
|
+
specify "should handle arrays" do
|
2821
|
+
@ds.filter(:a=>[:b, :c]).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.c))'
|
2822
|
+
end
|
2823
|
+
|
2824
|
+
specify "should handle hashes" do
|
2825
|
+
@ds.select({:b=>{:c=>1}}.case(false)).qualify_to_first_source.sql.should == "SELECT (CASE WHEN t.b THEN (t.c = 1) ELSE 'f' END) FROM t"
|
2826
|
+
end
|
2827
|
+
|
2828
|
+
specify "should handle SQL::Identifiers" do
|
2829
|
+
@ds.select{a}.qualify_to_first_source.sql.should == 'SELECT t.a FROM t'
|
2830
|
+
end
|
2831
|
+
|
2832
|
+
specify "should handle SQL::OrderedExpressions" do
|
2833
|
+
@ds.order(:a.desc, :b.asc).qualify_to_first_source.sql.should == 'SELECT t.* FROM t ORDER BY t.a DESC, t.b ASC'
|
2834
|
+
end
|
2835
|
+
|
2836
|
+
specify "should handle SQL::AliasedExpressions" do
|
2837
|
+
@ds.select(:a.as(:b)).qualify_to_first_source.sql.should == 'SELECT t.a AS b FROM t'
|
2838
|
+
end
|
2839
|
+
|
2840
|
+
specify "should handle SQL::CaseExpressions" do
|
2841
|
+
@ds.filter{{a=>b}.case(c, d)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (CASE t.d WHEN t.a THEN t.b ELSE t.c END)'
|
2842
|
+
end
|
2843
|
+
|
2844
|
+
specify "should handle SQL:Casts" do
|
2845
|
+
@ds.filter{a.cast(:boolean)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE CAST(t.a AS boolean)'
|
2846
|
+
end
|
2847
|
+
|
2848
|
+
specify "should handle SQL::Functions" do
|
2849
|
+
@ds.filter{a(b, 1)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE a(t.b, 1)'
|
2850
|
+
end
|
2851
|
+
|
2852
|
+
specify "should handle SQL::ComplexExpressions" do
|
2853
|
+
@ds.filter{(a+b)<(c-3)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE ((t.a + t.b) < (t.c - 3))'
|
2854
|
+
end
|
2855
|
+
|
2856
|
+
specify "should handle SQL::SQLArrays" do
|
2857
|
+
@ds.filter(:a=>[:b, :c].sql_array).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a IN (t.b, t.c))'
|
2858
|
+
end
|
2859
|
+
|
2860
|
+
specify "should handle SQL::Subscripts" do
|
2861
|
+
@ds.filter{a.sql_subscript(b,3)}.qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE t.a[t.b, 3]'
|
2862
|
+
end
|
2863
|
+
|
2864
|
+
specify "should handle all other objects by returning them unchanged" do
|
2865
|
+
@ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('?'.lit(:a)).having(false).qualify_to_first_source.sql.should == \
|
2866
|
+
"SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a HAVING 'f' ORDER BY true"
|
2867
|
+
end
|
2868
|
+
end
|