sequel 3.21.0 → 3.29.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 +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +86 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +997 -331
- data/lib/sequel/model/base.rb +386 -129
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +200 -120
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that return modified datasets
|
|
4
|
+
# :section: 1 - Methods that return modified datasets
|
|
5
5
|
# These methods all return modified copies of the receiver.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -28,12 +28,14 @@ module Sequel
|
|
|
28
28
|
JOIN_METHODS = (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]
|
|
29
29
|
|
|
30
30
|
# Methods that return modified datasets
|
|
31
|
-
QUERY_METHODS =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
|
|
32
|
+
add_graph_aliases and distinct except exclude exclude_having exclude_where
|
|
33
|
+
filter for_update from from_self graph grep group group_and_count group_by having intersect invert
|
|
34
|
+
limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
|
|
35
|
+
reverse reverse_order select select_all select_append select_group select_more server
|
|
36
|
+
set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
|
|
37
|
+
unlimited unordered where with with_recursive with_sql
|
|
38
|
+
METHS
|
|
37
39
|
|
|
38
40
|
# Adds an further filter to an existing filter using AND. If no filter
|
|
39
41
|
# exists an error is raised. This method is identical to #filter except
|
|
@@ -80,7 +82,7 @@ module Sequel
|
|
|
80
82
|
# :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
|
|
81
83
|
#
|
|
82
84
|
# DB[:items].except(DB[:other_items])
|
|
83
|
-
# # SELECT * FROM items EXCEPT SELECT * FROM other_items
|
|
85
|
+
# # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS t1
|
|
84
86
|
#
|
|
85
87
|
# DB[:items].except(DB[:other_items], :all=>true, :from_self=>false)
|
|
86
88
|
# # SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
|
|
@@ -103,12 +105,29 @@ module Sequel
|
|
|
103
105
|
# DB[:items].exclude(:category => 'software', :id=>3)
|
|
104
106
|
# # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
|
|
105
107
|
def exclude(*cond, &block)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
_filter_or_exclude(true, @opts[:having] ? :having : :where, *cond, &block)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Inverts the given conditions and adds them to the HAVING clause.
|
|
112
|
+
#
|
|
113
|
+
# DB[:items].select_group(:name).exclude_having{count(name) < 2}
|
|
114
|
+
# # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
|
|
115
|
+
def exclude_having(*cond, &block)
|
|
116
|
+
_filter_or_exclude(true, :having, *cond, &block)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Inverts the given conditions and adds them to the WHERE clause.
|
|
120
|
+
#
|
|
121
|
+
# DB[:items].select_group(:name).exclude_where(:category => 'software')
|
|
122
|
+
# # SELECT * FROM items WHERE (category != 'software')
|
|
123
|
+
#
|
|
124
|
+
# DB[:items].select_group(:name).
|
|
125
|
+
# exclude_having{count(name) < 2}.
|
|
126
|
+
# exclude_where(:category => 'software')
|
|
127
|
+
# # SELECT name FROM items WHERE (category != 'software')
|
|
128
|
+
# # GROUP BY name HAVING (count(name) >= 2)
|
|
129
|
+
def exclude_where(*cond, &block)
|
|
130
|
+
_filter_or_exclude(true, :where, *cond, &block)
|
|
112
131
|
end
|
|
113
132
|
|
|
114
133
|
# Returns a copy of the dataset with the given conditions imposed upon it.
|
|
@@ -184,11 +203,17 @@ module Sequel
|
|
|
184
203
|
def from(*source)
|
|
185
204
|
table_alias_num = 0
|
|
186
205
|
sources = []
|
|
206
|
+
ctes = nil
|
|
187
207
|
source.each do |s|
|
|
188
208
|
case s
|
|
189
209
|
when Hash
|
|
190
210
|
s.each{|k,v| sources << SQL::AliasedExpression.new(k,v)}
|
|
191
211
|
when Dataset
|
|
212
|
+
if hoist_cte?(s)
|
|
213
|
+
ctes ||= []
|
|
214
|
+
ctes += s.opts[:with]
|
|
215
|
+
s = s.clone(:with=>nil)
|
|
216
|
+
end
|
|
192
217
|
sources << SQL::AliasedExpression.new(s, dataset_alias(table_alias_num+=1))
|
|
193
218
|
when Symbol
|
|
194
219
|
sch, table, aliaz = split_symbol(s)
|
|
@@ -203,6 +228,7 @@ module Sequel
|
|
|
203
228
|
end
|
|
204
229
|
end
|
|
205
230
|
o = {:from=>sources.empty? ? nil : sources}
|
|
231
|
+
o[:with] = (opts[:with] || []) + ctes if ctes
|
|
206
232
|
o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
|
|
207
233
|
clone(o)
|
|
208
234
|
end
|
|
@@ -270,21 +296,25 @@ module Sequel
|
|
|
270
296
|
end
|
|
271
297
|
|
|
272
298
|
# Returns a copy of the dataset with the results grouped by the value of
|
|
273
|
-
# the given columns.
|
|
299
|
+
# the given columns. If a block is given, it is treated
|
|
300
|
+
# as a virtual row block, similar to +filter+.
|
|
274
301
|
#
|
|
275
302
|
# DB[:items].group(:id) # SELECT * FROM items GROUP BY id
|
|
276
303
|
# DB[:items].group(:id, :name) # SELECT * FROM items GROUP BY id, name
|
|
277
|
-
|
|
304
|
+
# DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
|
|
305
|
+
def group(*columns, &block)
|
|
306
|
+
virtual_row_columns(columns, block)
|
|
278
307
|
clone(:group => (columns.compact.empty? ? nil : columns))
|
|
279
308
|
end
|
|
280
309
|
|
|
281
310
|
# Alias of group
|
|
282
|
-
def group_by(*columns)
|
|
283
|
-
group(*columns)
|
|
311
|
+
def group_by(*columns, &block)
|
|
312
|
+
group(*columns, &block)
|
|
284
313
|
end
|
|
285
314
|
|
|
286
315
|
# Returns a dataset grouped by the given column with count by group.
|
|
287
316
|
# Column aliases may be supplied, and will be included in the select clause.
|
|
317
|
+
# If a block is given, it is treated as a virtual row block, similar to +filter+.
|
|
288
318
|
#
|
|
289
319
|
# Examples:
|
|
290
320
|
#
|
|
@@ -299,8 +329,12 @@ module Sequel
|
|
|
299
329
|
# DB[:items].group_and_count(:first_name___name).all
|
|
300
330
|
# # SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
|
|
301
331
|
# # => [{:name=>'a', :count=>1}, ...]
|
|
302
|
-
|
|
303
|
-
|
|
332
|
+
#
|
|
333
|
+
# DB[:items].group_and_count{substr(first_name, 1, 1).as(initial)}.all
|
|
334
|
+
# # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
|
|
335
|
+
# # => [{:initial=>'a', :count=>1}, ...]
|
|
336
|
+
def group_and_count(*columns, &block)
|
|
337
|
+
select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
|
|
304
338
|
end
|
|
305
339
|
|
|
306
340
|
# Returns a copy of the dataset with the HAVING conditions changed. See #filter for argument types.
|
|
@@ -356,7 +390,11 @@ module Sequel
|
|
|
356
390
|
inner_join(*args, &block)
|
|
357
391
|
end
|
|
358
392
|
|
|
359
|
-
# Returns a joined dataset.
|
|
393
|
+
# Returns a joined dataset. Not usually called directly, users should use the
|
|
394
|
+
# appropriate join method (e.g. join, left_join, natural_join, cross_join) which fills
|
|
395
|
+
# in the +type+ argument.
|
|
396
|
+
#
|
|
397
|
+
# Takes the following arguments:
|
|
360
398
|
#
|
|
361
399
|
# * type - The type of join to do (e.g. :inner)
|
|
362
400
|
# * table - Depends on type:
|
|
@@ -386,7 +424,29 @@ module Sequel
|
|
|
386
424
|
# in which case it yields the table alias/name for the table currently being joined,
|
|
387
425
|
# the table alias/name for the last joined (or first table), and an array of previous
|
|
388
426
|
# SQL::JoinClause. Unlike +filter+, this block is not treated as a virtual row block.
|
|
427
|
+
#
|
|
428
|
+
# Examples:
|
|
429
|
+
#
|
|
430
|
+
# DB[:a].join_table(:cross, :b)
|
|
431
|
+
# # SELECT * FROM a CROSS JOIN b
|
|
432
|
+
#
|
|
433
|
+
# DB[:a].join_table(:inner, DB[:b], :c=>d)
|
|
434
|
+
# # SELECT * FROM a INNER JOIN (SELECT * FROM b) AS t1 ON (t1.c = a.d)
|
|
435
|
+
#
|
|
436
|
+
# DB[:a].join_table(:left, :b___c, [:d])
|
|
437
|
+
# # SELECT * FROM a LEFT JOIN b AS c USING (d)
|
|
438
|
+
#
|
|
439
|
+
# DB[:a].natural_join(:b).join_table(:inner, :c) do |ta, jta, js|
|
|
440
|
+
# (:d.qualify(ta) > :e.qualify(jta)) & {:f.qualify(ta)=>DB.from(js.first.table).select(:g)}
|
|
441
|
+
# end
|
|
442
|
+
# # SELECT * FROM a NATURAL JOIN b INNER JOIN c
|
|
443
|
+
# # ON ((c.d > b.e) AND (c.f IN (SELECT g FROM b)))
|
|
389
444
|
def join_table(type, table, expr=nil, options={}, &block)
|
|
445
|
+
if hoist_cte?(table)
|
|
446
|
+
s, ds = hoist_cte(table)
|
|
447
|
+
return s.join_table(type, ds, expr, options, &block)
|
|
448
|
+
end
|
|
449
|
+
|
|
390
450
|
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
|
391
451
|
if using_join && !supports_join_using?
|
|
392
452
|
h = {}
|
|
@@ -431,6 +491,7 @@ module Sequel
|
|
|
431
491
|
v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
|
|
432
492
|
[k,v]
|
|
433
493
|
end
|
|
494
|
+
expr = SQL::BooleanExpression.from_value_pairs(expr)
|
|
434
495
|
end
|
|
435
496
|
if block
|
|
436
497
|
expr2 = yield(table_name, last_alias, @opts[:join] || [])
|
|
@@ -531,7 +592,7 @@ module Sequel
|
|
|
531
592
|
# DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
|
|
532
593
|
# DB[:items].order(nil) # SELECT * FROM items
|
|
533
594
|
def order(*columns, &block)
|
|
534
|
-
columns
|
|
595
|
+
virtual_row_columns(columns, block)
|
|
535
596
|
clone(:order => (columns.compact.empty?) ? nil : columns)
|
|
536
597
|
end
|
|
537
598
|
|
|
@@ -565,7 +626,7 @@ module Sequel
|
|
|
565
626
|
@opts[:order] ? ds.order_more(*@opts[:order]) : ds
|
|
566
627
|
end
|
|
567
628
|
|
|
568
|
-
# Qualify to the given table, or first source if
|
|
629
|
+
# Qualify to the given table, or first source if no table is given.
|
|
569
630
|
#
|
|
570
631
|
# DB[:items].filter(:id=>1).qualify
|
|
571
632
|
# # SELECT items.* FROM items WHERE (items.id = 1)
|
|
@@ -606,6 +667,18 @@ module Sequel
|
|
|
606
667
|
qualify_to(first_source)
|
|
607
668
|
end
|
|
608
669
|
|
|
670
|
+
# Modify the RETURNING clause, only supported on a few databases. If returning
|
|
671
|
+
# is used, instead of insert returning the autogenerated primary key or
|
|
672
|
+
# update/delete returning the number of modified rows, results are
|
|
673
|
+
# returned using +fetch_rows+.
|
|
674
|
+
#
|
|
675
|
+
# DB[:items].returning # RETURNING *
|
|
676
|
+
# DB[:items].returning(nil) # RETURNING NULL
|
|
677
|
+
# DB[:items].returning(:id, :name) # RETURNING id, name
|
|
678
|
+
def returning(*values)
|
|
679
|
+
clone(:returning=>values)
|
|
680
|
+
end
|
|
681
|
+
|
|
609
682
|
# Returns a copy of the dataset with the order reversed. If no order is
|
|
610
683
|
# given, the existing order is inverted.
|
|
611
684
|
#
|
|
@@ -629,7 +702,7 @@ module Sequel
|
|
|
629
702
|
# DB[:items].select(:a, :b) # SELECT a, b FROM items
|
|
630
703
|
# DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
|
|
631
704
|
def select(*columns, &block)
|
|
632
|
-
columns
|
|
705
|
+
virtual_row_columns(columns, block)
|
|
633
706
|
m = []
|
|
634
707
|
columns.each do |i|
|
|
635
708
|
i.is_a?(Hash) ? m.concat(i.map{|k, v| SQL::AliasedExpression.new(k,v)}) : m << i
|
|
@@ -637,11 +710,19 @@ module Sequel
|
|
|
637
710
|
clone(:select => m)
|
|
638
711
|
end
|
|
639
712
|
|
|
640
|
-
# Returns a copy of the dataset selecting the wildcard
|
|
713
|
+
# Returns a copy of the dataset selecting the wildcard if no arguments
|
|
714
|
+
# are given. If arguments are given, treat them as tables and select
|
|
715
|
+
# all columns (using the wildcard) from each table.
|
|
641
716
|
#
|
|
642
717
|
# DB[:items].select(:a).select_all # SELECT * FROM items
|
|
643
|
-
|
|
644
|
-
|
|
718
|
+
# DB[:items].select_all(:items) # SELECT items.* FROM items
|
|
719
|
+
# DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
|
|
720
|
+
def select_all(*tables)
|
|
721
|
+
if tables.empty?
|
|
722
|
+
clone(:select => nil)
|
|
723
|
+
else
|
|
724
|
+
select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
|
|
725
|
+
end
|
|
645
726
|
end
|
|
646
727
|
|
|
647
728
|
# Returns a copy of the dataset with the given columns added
|
|
@@ -653,10 +734,29 @@ module Sequel
|
|
|
653
734
|
# DB[:items].select_append(:b) # SELECT *, b FROM items
|
|
654
735
|
def select_append(*columns, &block)
|
|
655
736
|
cur_sel = @opts[:select]
|
|
656
|
-
|
|
737
|
+
if !cur_sel || cur_sel.empty?
|
|
738
|
+
unless supports_select_all_and_column?
|
|
739
|
+
return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_more(*columns, &block)
|
|
740
|
+
end
|
|
741
|
+
cur_sel = [WILDCARD]
|
|
742
|
+
end
|
|
657
743
|
select(*(cur_sel + columns), &block)
|
|
658
744
|
end
|
|
659
745
|
|
|
746
|
+
# Set both the select and group clauses with the given +columns+.
|
|
747
|
+
# Column aliases may be supplied, and will be included in the select clause.
|
|
748
|
+
# This also takes a virtual row block similar to +filter+.
|
|
749
|
+
#
|
|
750
|
+
# DB[:items].select_group(:a, :b)
|
|
751
|
+
# # SELECT a, b FROM items GROUP BY a, b
|
|
752
|
+
#
|
|
753
|
+
# DB[:items].select_group(:c___a){f(c2)}
|
|
754
|
+
# # SELECT c AS a, f(c2) FROM items GROUP BY c, f(c2)
|
|
755
|
+
def select_group(*columns, &block)
|
|
756
|
+
virtual_row_columns(columns, block)
|
|
757
|
+
select(*columns).group(*columns.map{|c| unaliased_identifier(c)})
|
|
758
|
+
end
|
|
759
|
+
|
|
660
760
|
# Returns a copy of the dataset with the given columns added
|
|
661
761
|
# to the existing selected columns. If no columns are currently selected
|
|
662
762
|
# it will just select the columns given.
|
|
@@ -670,7 +770,7 @@ module Sequel
|
|
|
670
770
|
end
|
|
671
771
|
|
|
672
772
|
# Set the server for this dataset to use. Used to pick a specific database
|
|
673
|
-
# shard to run a query against, or to override the default (
|
|
773
|
+
# shard to run a query against, or to override the default (where SELECT uses
|
|
674
774
|
# :read_only database and all other queries use the :default database). This
|
|
675
775
|
# method is always available but is only useful when database sharding is being
|
|
676
776
|
# used.
|
|
@@ -702,6 +802,22 @@ module Sequel
|
|
|
702
802
|
clone(:overrides=>hash.merge(@opts[:overrides]||{}))
|
|
703
803
|
end
|
|
704
804
|
|
|
805
|
+
# Unbind bound variables from this dataset's filter and return an array of two
|
|
806
|
+
# objects. The first object is a modified dataset where the filter has been
|
|
807
|
+
# replaced with one that uses bound variable placeholders. The second object
|
|
808
|
+
# is the hash of unbound variables. You can then prepare and execute (or just
|
|
809
|
+
# call) the dataset with the bound variables to get results.
|
|
810
|
+
#
|
|
811
|
+
# ds, bv = DB[:items].filter(:a=>1).unbind
|
|
812
|
+
# ds # SELECT * FROM items WHERE (a = $a)
|
|
813
|
+
# bv # {:a => 1}
|
|
814
|
+
# ds.call(:select, bv)
|
|
815
|
+
def unbind
|
|
816
|
+
u = Unbinder.new
|
|
817
|
+
ds = clone(:where=>u.transform(opts[:where]), :join=>u.transform(opts[:join]))
|
|
818
|
+
[ds, u.binds]
|
|
819
|
+
end
|
|
820
|
+
|
|
705
821
|
# Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
|
|
706
822
|
#
|
|
707
823
|
# DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
|
|
@@ -726,8 +842,8 @@ module Sequel
|
|
|
726
842
|
# :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
|
|
727
843
|
# :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
|
|
728
844
|
#
|
|
729
|
-
# DB[:items].union(DB[:other_items])
|
|
730
|
-
#
|
|
845
|
+
# DB[:items].union(DB[:other_items])
|
|
846
|
+
# # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS t1
|
|
731
847
|
#
|
|
732
848
|
# DB[:items].union(DB[:other_items], :all=>true, :from_self=>false)
|
|
733
849
|
# # SELECT * FROM items UNION ALL SELECT * FROM other_items
|
|
@@ -774,7 +890,12 @@ module Sequel
|
|
|
774
890
|
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%')) SELECT * FROM items
|
|
775
891
|
def with(name, dataset, opts={})
|
|
776
892
|
raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
|
|
777
|
-
|
|
893
|
+
if hoist_cte?(dataset)
|
|
894
|
+
s, ds = hoist_cte(dataset)
|
|
895
|
+
s.with(name, ds, opts)
|
|
896
|
+
else
|
|
897
|
+
clone(:with=>(@opts[:with]||[]) + [opts.merge(:name=>name, :dataset=>dataset)])
|
|
898
|
+
end
|
|
778
899
|
end
|
|
779
900
|
|
|
780
901
|
# Add a recursive common table expression (CTE) with the given name, a dataset that
|
|
@@ -796,20 +917,50 @@ module Sequel
|
|
|
796
917
|
# # SELECT i AS id, pi AS parent_id FROM t
|
|
797
918
|
def with_recursive(name, nonrecursive, recursive, opts={})
|
|
798
919
|
raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
|
|
799
|
-
|
|
920
|
+
if hoist_cte?(nonrecursive)
|
|
921
|
+
s, ds = hoist_cte(nonrecursive)
|
|
922
|
+
s.with_recursive(name, ds, recursive, opts)
|
|
923
|
+
elsif hoist_cte?(recursive)
|
|
924
|
+
s, ds = hoist_cte(recursive)
|
|
925
|
+
s.with_recursive(name, nonrecursive, ds, opts)
|
|
926
|
+
else
|
|
927
|
+
clone(:with=>(@opts[:with]||[]) + [opts.merge(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
|
|
928
|
+
end
|
|
800
929
|
end
|
|
801
930
|
|
|
802
931
|
# Returns a copy of the dataset with the static SQL used. This is useful if you want
|
|
803
932
|
# to keep the same row_proc/graph, but change the SQL used to custom SQL.
|
|
804
933
|
#
|
|
805
934
|
# DB[:items].with_sql('SELECT * FROM foo') # SELECT * FROM foo
|
|
935
|
+
#
|
|
936
|
+
# You can use placeholders in your SQL and provide arguments for those placeholders:
|
|
937
|
+
#
|
|
938
|
+
# DB[:items].with_sql('SELECT ? FROM foo', 1) # SELECT 1 FROM foo
|
|
939
|
+
#
|
|
940
|
+
# You can also provide a method name and arguments to call to get the SQL:
|
|
941
|
+
#
|
|
942
|
+
# DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
|
|
806
943
|
def with_sql(sql, *args)
|
|
807
|
-
sql
|
|
944
|
+
if sql.is_a?(Symbol)
|
|
945
|
+
sql = send(sql, *args)
|
|
946
|
+
else
|
|
947
|
+
sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
|
|
948
|
+
end
|
|
808
949
|
clone(:sql=>sql)
|
|
809
950
|
end
|
|
810
951
|
|
|
811
952
|
protected
|
|
812
953
|
|
|
954
|
+
# Add the dataset to the list of compounds
|
|
955
|
+
def compound_clone(type, dataset, opts)
|
|
956
|
+
if hoist_cte?(dataset)
|
|
957
|
+
s, ds = hoist_cte(dataset)
|
|
958
|
+
return s.compound_clone(type, ds, opts)
|
|
959
|
+
end
|
|
960
|
+
ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
|
|
961
|
+
opts[:from_self] == false ? ds : ds.from_self(opts)
|
|
962
|
+
end
|
|
963
|
+
|
|
813
964
|
# Return true if the dataset has a non-nil value for any key in opts.
|
|
814
965
|
def options_overlap(opts)
|
|
815
966
|
!(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
|
|
@@ -823,22 +974,22 @@ module Sequel
|
|
|
823
974
|
|
|
824
975
|
private
|
|
825
976
|
|
|
826
|
-
# Internal filter method so it works on either the having or where clauses.
|
|
827
|
-
def
|
|
977
|
+
# Internal filter/exclude method so it works on either the having or where clauses.
|
|
978
|
+
def _filter_or_exclude(invert, clause, *cond, &block)
|
|
828
979
|
cond = cond.first if cond.size == 1
|
|
829
980
|
if cond.respond_to?(:empty?) && cond.empty? && !block
|
|
830
981
|
clone
|
|
831
982
|
else
|
|
832
983
|
cond = filter_expr(cond, &block)
|
|
984
|
+
cond = SQL::BooleanExpression.invert(cond) if invert
|
|
833
985
|
cond = SQL::BooleanExpression.new(:AND, @opts[clause], cond) if @opts[clause]
|
|
834
986
|
clone(clause => cond)
|
|
835
987
|
end
|
|
836
988
|
end
|
|
837
|
-
|
|
838
|
-
#
|
|
839
|
-
def
|
|
840
|
-
|
|
841
|
-
opts[:from_self] == false ? ds : ds.from_self(opts)
|
|
989
|
+
|
|
990
|
+
# Internal filter method so it works on either the having or where clauses.
|
|
991
|
+
def _filter(clause, *cond, &block)
|
|
992
|
+
_filter_or_exclude(false, clause, *cond, &block)
|
|
842
993
|
end
|
|
843
994
|
|
|
844
995
|
# SQL expression object based on the expr type. See +filter+.
|
|
@@ -867,7 +1018,13 @@ module Sequel
|
|
|
867
1018
|
when Symbol, SQL::Expression
|
|
868
1019
|
expr
|
|
869
1020
|
when TrueClass, FalseClass
|
|
870
|
-
|
|
1021
|
+
if supports_where_true?
|
|
1022
|
+
SQL::BooleanExpression.new(:NOOP, expr)
|
|
1023
|
+
elsif expr
|
|
1024
|
+
SQL::Constants::SQLTRUE
|
|
1025
|
+
else
|
|
1026
|
+
SQL::Constants::SQLFALSE
|
|
1027
|
+
end
|
|
871
1028
|
when String
|
|
872
1029
|
LiteralString.new("(#{expr})")
|
|
873
1030
|
else
|
|
@@ -875,6 +1032,18 @@ module Sequel
|
|
|
875
1032
|
end
|
|
876
1033
|
end
|
|
877
1034
|
|
|
1035
|
+
# Return two datasets, the first a clone of the receiver with the WITH
|
|
1036
|
+
# clause from the given dataset added to it, and the second a clone of
|
|
1037
|
+
# the given dataset with the WITH clause removed.
|
|
1038
|
+
def hoist_cte(ds)
|
|
1039
|
+
[clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
# Whether CTEs need to be hoisted from the given ds into the current ds.
|
|
1043
|
+
def hoist_cte?(ds)
|
|
1044
|
+
ds.is_a?(Dataset) && ds.opts[:with] && !supports_cte_in_subqueries?
|
|
1045
|
+
end
|
|
1046
|
+
|
|
878
1047
|
# Inverts the given order by breaking it into a list of column references
|
|
879
1048
|
# and inverting them.
|
|
880
1049
|
#
|
|
@@ -892,5 +1061,17 @@ module Sequel
|
|
|
892
1061
|
end
|
|
893
1062
|
end
|
|
894
1063
|
end
|
|
1064
|
+
|
|
1065
|
+
# Return self if the dataset already has a server, or a cloned dataset with the
|
|
1066
|
+
# default server otherwise.
|
|
1067
|
+
def default_server
|
|
1068
|
+
@opts[:server] ? self : clone(:server=>:default)
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
# Treat the +block+ as a virtual_row block if not +nil+ and
|
|
1072
|
+
# add the resulting columns to the +columns+ array (modifies +columns+).
|
|
1073
|
+
def virtual_row_columns(columns, block)
|
|
1074
|
+
columns.concat(Array(Sequel.virtual_row(&block))) if block
|
|
1075
|
+
end
|
|
895
1076
|
end
|
|
896
1077
|
end
|