sequel 3.0.0 → 3.1.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 +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
|