sequel 5.45.0 → 5.77.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.
- checksums.yaml +4 -4
- data/CHANGELOG +434 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +41 -11
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +109 -19
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
class Dataset
|
5
|
+
# This module implements methods to support deprecated use of extensions registered
|
6
|
+
# not using a module. In such cases, for backwards compatibility, Sequel has to use
|
7
|
+
# a singleton class for the dataset.
|
8
|
+
module DeprecatedSingletonClassMethods
|
9
|
+
# Load the extension into a clone of the receiver.
|
10
|
+
def extension(*a)
|
11
|
+
c = _clone(:freeze=>false)
|
12
|
+
c.send(:_extension!, a)
|
13
|
+
c.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
# Extend the cloned of the receiver with the given modules, instead of the default
|
17
|
+
# approach of creating a subclass of the receiver's class and including the modules
|
18
|
+
# into that.
|
19
|
+
def with_extend(*mods, &block)
|
20
|
+
c = _clone(:freeze=>false)
|
21
|
+
c.extend(*mods) unless mods.empty?
|
22
|
+
c.extend(DatasetModule.new(&block)) if block
|
23
|
+
c.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Load the extensions into the receiver.
|
29
|
+
def _extension!(exts)
|
30
|
+
Sequel.extension(*exts)
|
31
|
+
exts.each do |ext|
|
32
|
+
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
33
|
+
pr.call(self)
|
34
|
+
else
|
35
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -25,11 +25,16 @@ module Sequel
|
|
25
25
|
false
|
26
26
|
end
|
27
27
|
|
28
|
+
# :nocov:
|
29
|
+
|
28
30
|
# Whether the dataset requires SQL standard datetimes. False by default,
|
29
|
-
# as most allow strings with ISO 8601 format.
|
31
|
+
# as most allow strings with ISO 8601 format. Only for backwards compatibility,
|
32
|
+
# no longer used internally, do not use in new code.
|
30
33
|
def requires_sql_standard_datetimes?
|
34
|
+
# SEQUEL6: Remove
|
31
35
|
false
|
32
36
|
end
|
37
|
+
# :nocov:
|
33
38
|
|
34
39
|
# Whether type specifiers are required for prepared statement/bound
|
35
40
|
# variable argument placeholders (i.e. :bv__integer), false by default.
|
@@ -125,6 +130,11 @@ module Sequel
|
|
125
130
|
false
|
126
131
|
end
|
127
132
|
|
133
|
+
# Whether the MERGE statement is supported, false by default.
|
134
|
+
def supports_merge?
|
135
|
+
false
|
136
|
+
end
|
137
|
+
|
128
138
|
# Whether modifying joined datasets is supported, false by default.
|
129
139
|
def supports_modifying_joins?
|
130
140
|
false
|
@@ -147,6 +157,11 @@ module Sequel
|
|
147
157
|
supports_distinct_on?
|
148
158
|
end
|
149
159
|
|
160
|
+
# Whether placeholder literalizers are supported, true by default.
|
161
|
+
def supports_placeholder_literalizer?
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
150
165
|
# Whether the dataset supports pattern matching by regular expressions, false by default.
|
151
166
|
def supports_regexp?
|
152
167
|
false
|
@@ -173,10 +188,14 @@ module Sequel
|
|
173
188
|
true
|
174
189
|
end
|
175
190
|
|
191
|
+
# :nocov:
|
192
|
+
|
176
193
|
# Whether the dataset supports timezones in literal timestamps, false by default.
|
177
194
|
def supports_timestamp_timezones?
|
195
|
+
# SEQUEL6: Remove
|
178
196
|
false
|
179
197
|
end
|
198
|
+
# :nocov:
|
180
199
|
|
181
200
|
# Whether the dataset supports fractional seconds in literal timestamps, true by default.
|
182
201
|
def supports_timestamp_usecs?
|
data/lib/sequel/dataset/misc.rb
CHANGED
@@ -158,6 +158,16 @@ module Sequel
|
|
158
158
|
!!((opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join])
|
159
159
|
end
|
160
160
|
|
161
|
+
# The class to use for placeholder literalizers for the current dataset.
|
162
|
+
def placeholder_literalizer_class
|
163
|
+
::Sequel::Dataset::PlaceholderLiteralizer
|
164
|
+
end
|
165
|
+
|
166
|
+
# A placeholder literalizer loader for the current dataset.
|
167
|
+
def placeholder_literalizer_loader(&block)
|
168
|
+
placeholder_literalizer_class.loader(self, &block)
|
169
|
+
end
|
170
|
+
|
161
171
|
# The alias to use for the row_number column, used when emulating OFFSET
|
162
172
|
# support and for eager limit strategies
|
163
173
|
def row_number_column
|
@@ -296,13 +306,13 @@ module Sequel
|
|
296
306
|
loader += 1
|
297
307
|
|
298
308
|
if loader >= 3
|
299
|
-
loader =
|
309
|
+
loader = placeholder_literalizer_loader{|pl, _| yield pl}
|
300
310
|
cache_set(key, loader)
|
301
311
|
else
|
302
312
|
cache_set(key, loader + 1)
|
303
313
|
loader = nil
|
304
314
|
end
|
305
|
-
elsif cache_sql?
|
315
|
+
elsif cache_sql? && supports_placeholder_literalizer?
|
306
316
|
cache_set(key, 1)
|
307
317
|
end
|
308
318
|
|
@@ -77,8 +77,8 @@ module Sequel
|
|
77
77
|
# Yields the receiver and the dataset to the block, which should
|
78
78
|
# call #arg on the receiver for each placeholder argument, and
|
79
79
|
# return the dataset that you want to load.
|
80
|
-
def loader(dataset, &block)
|
81
|
-
|
80
|
+
def loader(pl, dataset, &block)
|
81
|
+
pl.new(*process(dataset, &block))
|
82
82
|
end
|
83
83
|
|
84
84
|
# Return an Argument with the specified position, or the next position. In
|
@@ -145,7 +145,7 @@ module Sequel
|
|
145
145
|
# given block, recording the offsets at which the recorders arguments
|
146
146
|
# are used in the query.
|
147
147
|
def self.loader(dataset, &block)
|
148
|
-
Recorder.new.loader(dataset, &block)
|
148
|
+
Recorder.new.loader(self, dataset, &block)
|
149
149
|
end
|
150
150
|
|
151
151
|
# Save the dataset, array of SQL fragments, and ending SQL string.
|
@@ -199,20 +199,31 @@ module Sequel
|
|
199
199
|
# Return the SQL query to use for the given arguments.
|
200
200
|
def sql(*args)
|
201
201
|
raise Error, "wrong number of arguments (#{args.length} for #{@arity})" unless args.length == @arity
|
202
|
-
s =
|
202
|
+
s = sql_origin
|
203
|
+
append_sql(s, *args)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Append the SQL query to use for the given arguments to the given SQL string.
|
207
|
+
def append_sql(sql, *args)
|
203
208
|
ds = @dataset
|
204
|
-
@fragments.each do |
|
205
|
-
|
209
|
+
@fragments.each do |s, i, transformer|
|
210
|
+
sql << s
|
206
211
|
if i.is_a?(Integer)
|
207
212
|
v = args.fetch(i)
|
208
213
|
v = transformer.call(v) if transformer
|
209
214
|
else
|
210
215
|
v = i.call
|
211
216
|
end
|
212
|
-
ds.literal_append(
|
217
|
+
ds.literal_append(sql, v)
|
213
218
|
end
|
214
|
-
|
215
|
-
|
219
|
+
sql << @final_sql
|
220
|
+
sql
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def sql_origin
|
226
|
+
String.new
|
216
227
|
end
|
217
228
|
end
|
218
229
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -12,6 +12,10 @@ module Sequel
|
|
12
12
|
# in the extension).
|
13
13
|
EXTENSIONS = {}
|
14
14
|
|
15
|
+
# Hash of extension name symbols to modules to load to implement the extension.
|
16
|
+
EXTENSION_MODULES = {}
|
17
|
+
private_constant :EXTENSION_MODULES
|
18
|
+
|
15
19
|
EMPTY_ARRAY = [].freeze
|
16
20
|
|
17
21
|
# The dataset options that require the removal of cached columns if changed.
|
@@ -45,12 +49,8 @@ module Sequel
|
|
45
49
|
METHS
|
46
50
|
|
47
51
|
# Register an extension callback for Dataset objects. ext should be the
|
48
|
-
# extension name symbol, and mod should
|
49
|
-
# dataset
|
50
|
-
# object. If mod is not provided, a block can be provided and is treated
|
51
|
-
# as the mod object.
|
52
|
-
#
|
53
|
-
# If mod is a module, this also registers a Database extension that will
|
52
|
+
# extension name symbol, and mod should be a Module that will be
|
53
|
+
# included in the dataset's class. This also registers a Database extension that will
|
54
54
|
# extend all of the database's datasets.
|
55
55
|
def self.register_extension(ext, mod=nil, &block)
|
56
56
|
if mod
|
@@ -58,14 +58,20 @@ module Sequel
|
|
58
58
|
if mod.is_a?(Module)
|
59
59
|
block = proc{|ds| ds.extend(mod)}
|
60
60
|
Sequel::Database.register_extension(ext){|db| db.extend_datasets(mod)}
|
61
|
+
Sequel.synchronize{EXTENSION_MODULES[ext] = mod}
|
61
62
|
else
|
62
63
|
block = mod
|
63
64
|
end
|
64
65
|
end
|
66
|
+
|
67
|
+
unless mod.is_a?(Module)
|
68
|
+
Sequel::Deprecation.deprecate("Providing a block or non-module to Sequel::Dataset.register_extension is deprecated and support for it will be removed in Sequel 6.")
|
69
|
+
end
|
70
|
+
|
65
71
|
Sequel.synchronize{EXTENSIONS[ext] = block}
|
66
72
|
end
|
67
73
|
|
68
|
-
# On Ruby 2.4+, use clone(:
|
74
|
+
# On Ruby 2.4+, use clone(freeze: false) to create clones, because
|
69
75
|
# we use true freezing in that case, and we need to modify the opts
|
70
76
|
# in the frozen copy.
|
71
77
|
#
|
@@ -116,7 +122,7 @@ module Sequel
|
|
116
122
|
# DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
|
117
123
|
# DB[:items].order(:id).distinct{func(:id)} # SQL: SELECT DISTINCT ON (func(id)) * FROM items ORDER BY id
|
118
124
|
#
|
119
|
-
# There is support for
|
125
|
+
# There is support for emulating the DISTINCT ON support in MySQL, but it
|
120
126
|
# does not support the ORDER of the dataset, and also doesn't work in many
|
121
127
|
# cases if the ONLY_FULL_GROUP_BY sql_mode is used, which is the default on
|
122
128
|
# MySQL 5.7.5+.
|
@@ -195,11 +201,15 @@ module Sequel
|
|
195
201
|
if TRUE_FREEZE
|
196
202
|
# Return a clone of the dataset loaded with the given dataset extensions.
|
197
203
|
# If no related extension file exists or the extension does not have
|
198
|
-
# specific support for Dataset objects, an
|
199
|
-
def extension(*
|
200
|
-
|
201
|
-
|
202
|
-
|
204
|
+
# specific support for Dataset objects, an error will be raised.
|
205
|
+
def extension(*exts)
|
206
|
+
Sequel.extension(*exts)
|
207
|
+
mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
|
208
|
+
if mods.all?
|
209
|
+
with_extend(*mods)
|
210
|
+
else
|
211
|
+
with_extend(DeprecatedSingletonClassMethods).extension(*exts)
|
212
|
+
end
|
203
213
|
end
|
204
214
|
else
|
205
215
|
# :nocov:
|
@@ -508,6 +518,7 @@ module Sequel
|
|
508
518
|
# argument.
|
509
519
|
# :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
|
510
520
|
# the last joined or primary table is used.
|
521
|
+
# :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
|
511
522
|
# :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
|
512
523
|
# for implicit conditions.
|
513
524
|
# :qualify :: Can be set to false to not do any implicit qualification. Can be set
|
@@ -541,7 +552,7 @@ module Sequel
|
|
541
552
|
return s.join_table(type, ds, expr, options, &block)
|
542
553
|
end
|
543
554
|
|
544
|
-
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
555
|
+
using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
|
545
556
|
if using_join && !supports_join_using?
|
546
557
|
h = {}
|
547
558
|
expr.each{|e| h[e] = e}
|
@@ -616,7 +627,7 @@ module Sequel
|
|
616
627
|
UNCONDITIONED_JOIN_TYPES.each do |jtype|
|
617
628
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
618
629
|
def #{jtype}_join(table, opts=Sequel::OPTS)
|
619
|
-
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if
|
630
|
+
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
|
620
631
|
raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
|
621
632
|
join_table(:#{jtype}, table, nil, opts)
|
622
633
|
end
|
@@ -677,6 +688,56 @@ module Sequel
|
|
677
688
|
clone(:lock => style)
|
678
689
|
end
|
679
690
|
|
691
|
+
# Return a dataset with a WHEN MATCHED THEN DELETE clause added to the
|
692
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
693
|
+
# use it as additional conditions for the match.
|
694
|
+
#
|
695
|
+
# merge_delete
|
696
|
+
# # WHEN MATCHED THEN DELETE
|
697
|
+
#
|
698
|
+
# merge_delete{a > 30}
|
699
|
+
# # WHEN MATCHED AND (a > 30) THEN DELETE
|
700
|
+
def merge_delete(&block)
|
701
|
+
_merge_when(:type=>:delete, &block)
|
702
|
+
end
|
703
|
+
|
704
|
+
# Return a dataset with a WHEN NOT MATCHED THEN INSERT clause added to the
|
705
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
706
|
+
# use it as additional conditions for the match.
|
707
|
+
#
|
708
|
+
# The arguments provided can be any arguments that would be accepted by
|
709
|
+
# #insert.
|
710
|
+
#
|
711
|
+
# merge_insert(i1: :i2, a: Sequel[:b]+11)
|
712
|
+
# # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
|
713
|
+
#
|
714
|
+
# merge_insert(:i2, Sequel[:b]+11){a > 30}
|
715
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN INSERT VALUES (i2, (b + 11))
|
716
|
+
def merge_insert(*values, &block)
|
717
|
+
_merge_when(:type=>:insert, :values=>values, &block)
|
718
|
+
end
|
719
|
+
|
720
|
+
# Return a dataset with a WHEN MATCHED THEN UPDATE clause added to the
|
721
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
722
|
+
# use it as additional conditions for the match.
|
723
|
+
#
|
724
|
+
# merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
|
725
|
+
# # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
|
726
|
+
#
|
727
|
+
# merge_update(i1: :i2){a > 30}
|
728
|
+
# # WHEN MATCHED AND (a > 30) THEN UPDATE SET i1 = i2
|
729
|
+
def merge_update(values, &block)
|
730
|
+
_merge_when(:type=>:update, :values=>values, &block)
|
731
|
+
end
|
732
|
+
|
733
|
+
# Return a dataset with the source and join condition to use for the MERGE statement.
|
734
|
+
#
|
735
|
+
# merge_using(:m2, i1: :i2)
|
736
|
+
# # USING m2 ON (i1 = i2)
|
737
|
+
def merge_using(source, join_condition)
|
738
|
+
clone(:merge_using => [source, join_condition].freeze)
|
739
|
+
end
|
740
|
+
|
680
741
|
# Returns a cloned dataset without a row_proc.
|
681
742
|
#
|
682
743
|
# ds = DB[:items].with_row_proc(:invert.to_proc)
|
@@ -699,7 +760,7 @@ module Sequel
|
|
699
760
|
end
|
700
761
|
|
701
762
|
# Returns a copy of the dataset with a specified order. Can be safely combined with limit.
|
702
|
-
# If you call limit with an offset, it will override
|
763
|
+
# If you call limit with an offset, it will override the offset if you've called
|
703
764
|
# offset first.
|
704
765
|
#
|
705
766
|
# DB[:items].offset(10) # SELECT * FROM items OFFSET 10
|
@@ -736,7 +797,7 @@ module Sequel
|
|
736
797
|
# DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
|
737
798
|
# DB[:items].order(Sequel[:a] + :b) # SELECT * FROM items ORDER BY (a + b)
|
738
799
|
# DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
|
739
|
-
# DB[:items].order(Sequel.asc(:name, :
|
800
|
+
# DB[:items].order(Sequel.asc(:name, nulls: :last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
|
740
801
|
# DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
|
741
802
|
# DB[:items].order(nil) # SELECT * FROM items
|
742
803
|
def order(*columns, &block)
|
@@ -806,13 +867,13 @@ module Sequel
|
|
806
867
|
# DB[:items].returning(nil) # RETURNING NULL
|
807
868
|
# DB[:items].returning(:id, :name) # RETURNING id, name
|
808
869
|
#
|
809
|
-
# DB[:items].returning.insert(:
|
870
|
+
# DB[:items].returning.insert(a: 1) do |hash|
|
810
871
|
# # hash for each row inserted, with values for all columns
|
811
872
|
# end
|
812
|
-
# DB[:items].returning.update(:
|
873
|
+
# DB[:items].returning.update(a: 1) do |hash|
|
813
874
|
# # hash for each row updated, with values for all columns
|
814
875
|
# end
|
815
|
-
# DB[:items].returning.delete(:
|
876
|
+
# DB[:items].returning.delete(a: 1) do |hash|
|
816
877
|
# # hash for each row deleted, with values for all columns
|
817
878
|
# end
|
818
879
|
def returning(*values)
|
@@ -1051,7 +1112,7 @@ module Sequel
|
|
1051
1112
|
# referenced in window functions. See Sequel::SQL::Window for a list of
|
1052
1113
|
# options that can be passed in. Example:
|
1053
1114
|
#
|
1054
|
-
# DB[:items].window(:w, :
|
1115
|
+
# DB[:items].window(:w, partition: :c1, order: :c2)
|
1055
1116
|
# # SELECT * FROM items WINDOW w AS (PARTITION BY c1 ORDER BY c2)
|
1056
1117
|
def window(name, opts)
|
1057
1118
|
clone(:window=>((@opts[:window]||EMPTY_ARRAY) + [[name, SQL::Window.new(opts)].freeze]).freeze)
|
@@ -1059,6 +1120,7 @@ module Sequel
|
|
1059
1120
|
|
1060
1121
|
# Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
|
1061
1122
|
# A common table expression acts as an inline view for the query.
|
1123
|
+
#
|
1062
1124
|
# Options:
|
1063
1125
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
1064
1126
|
# :recursive :: Specify that this is a recursive CTE
|
@@ -1079,20 +1141,60 @@ module Sequel
|
|
1079
1141
|
|
1080
1142
|
# Add a recursive common table expression (CTE) with the given name, a dataset that
|
1081
1143
|
# defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
|
1082
|
-
# of the CTE.
|
1144
|
+
# of the CTE.
|
1145
|
+
#
|
1146
|
+
# Options:
|
1083
1147
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
1084
1148
|
# :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
|
1085
1149
|
#
|
1150
|
+
# PostgreSQL 14+ Options:
|
1151
|
+
# :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
|
1152
|
+
# result of the CTE, a cycle column indicating whether a cycle was detected for
|
1153
|
+
# the current row, and a path column for the path traversed to get to the current
|
1154
|
+
# row. If given, must be a hash with the following keys:
|
1155
|
+
# :columns :: (required) The column or array of columns to use to detect a cycle.
|
1156
|
+
# If the value of these columns match columns already traversed, then
|
1157
|
+
# a cycle is detected, and recursive searching will not traverse beyond
|
1158
|
+
# the cycle (the CTE will include the row where the cycle was detected).
|
1159
|
+
# :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
|
1160
|
+
# :cycle_value :: The value of the cycle column in the output if the current row was
|
1161
|
+
# detected as a cycle, defaults to true.
|
1162
|
+
# :noncycle_value :: The value of the cycle column in the output if the current row
|
1163
|
+
# was not detected as a cycle, defaults to false. Only respected
|
1164
|
+
# if :cycle_value is given.
|
1165
|
+
# :path_column :: The name of the path column in the output, defaults to :path.
|
1166
|
+
# :search :: Include an order column in the result of the CTE that allows for breadth or
|
1167
|
+
# depth first searching. If given, must be a hash with the following keys:
|
1168
|
+
# :by :: (required) The column or array of columns to search by.
|
1169
|
+
# :order_column :: The name of the order column in the output, defaults to :ordercol.
|
1170
|
+
# :type :: Set to :breadth to use breadth-first searching (depth-first searching
|
1171
|
+
# is the default).
|
1172
|
+
#
|
1086
1173
|
# DB[:t].with_recursive(:t,
|
1087
1174
|
# DB[:i1].select(:id, :parent_id).where(parent_id: nil),
|
1088
1175
|
# DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
|
1089
|
-
# :
|
1176
|
+
# args: [:id, :parent_id])
|
1090
1177
|
#
|
1091
1178
|
# # WITH RECURSIVE t(id, parent_id) AS (
|
1092
1179
|
# # SELECT id, parent_id FROM i1 WHERE (parent_id IS NULL)
|
1093
1180
|
# # UNION ALL
|
1094
1181
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
1095
1182
|
# # ) SELECT * FROM t
|
1183
|
+
#
|
1184
|
+
# DB[:t].with_recursive(:t,
|
1185
|
+
# DB[:i1].where(parent_id: nil),
|
1186
|
+
# DB[:i1].join(:t, id: :parent_id).select_all(:i1),
|
1187
|
+
# search: {by: :id, type: :breadth},
|
1188
|
+
# cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
|
1189
|
+
#
|
1190
|
+
# # WITH RECURSIVE t AS (
|
1191
|
+
# # SELECT * FROM i1 WHERE (parent_id IS NULL)
|
1192
|
+
# # UNION ALL
|
1193
|
+
# # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
|
1194
|
+
# # )
|
1195
|
+
# # SEARCH BREADTH FIRST BY id SET ordercol
|
1196
|
+
# # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
|
1197
|
+
# # SELECT * FROM t
|
1096
1198
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
1097
1199
|
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
1098
1200
|
if hoist_cte?(nonrecursive)
|
@@ -1107,16 +1209,27 @@ module Sequel
|
|
1107
1209
|
end
|
1108
1210
|
|
1109
1211
|
if TRUE_FREEZE
|
1110
|
-
#
|
1212
|
+
# Create a subclass of the receiver's class, and include the given modules
|
1213
|
+
# into it. If a block is provided, a DatasetModule is created using the block and
|
1214
|
+
# is included into the subclass. Create an instance of the subclass using the
|
1215
|
+
# same db and opts, so that the returned dataset operates similarly to a clone
|
1216
|
+
# extended with the given modules. This approach is used to avoid singleton
|
1217
|
+
# classes, which significantly improves performance.
|
1218
|
+
#
|
1111
1219
|
# Note that like Object#extend, when multiple modules are provided
|
1112
|
-
# as arguments the
|
1113
|
-
# order. If a block is provided, a DatasetModule is created using the block and
|
1114
|
-
# the clone is extended with that module after any modules given as arguments.
|
1220
|
+
# as arguments the subclass includes the modules in reverse order.
|
1115
1221
|
def with_extend(*mods, &block)
|
1116
|
-
c =
|
1117
|
-
c.
|
1118
|
-
c.
|
1119
|
-
c.freeze
|
1222
|
+
c = Class.new(self.class)
|
1223
|
+
c.include(*mods) unless mods.empty?
|
1224
|
+
c.include(DatasetModule.new(&block)) if block
|
1225
|
+
o = c.freeze.allocate
|
1226
|
+
o.instance_variable_set(:@db, @db)
|
1227
|
+
o.instance_variable_set(:@opts, @opts)
|
1228
|
+
o.instance_variable_set(:@cache, {})
|
1229
|
+
if cols = cache_get(:_columns)
|
1230
|
+
o.send(:columns=, cols)
|
1231
|
+
end
|
1232
|
+
o.freeze
|
1120
1233
|
end
|
1121
1234
|
else
|
1122
1235
|
# :nocov:
|
@@ -1149,7 +1262,7 @@ module Sequel
|
|
1149
1262
|
#
|
1150
1263
|
# You can also provide a method name and arguments to call to get the SQL:
|
1151
1264
|
#
|
1152
|
-
# DB[:items].with_sql(:insert_sql, :
|
1265
|
+
# DB[:items].with_sql(:insert_sql, b: 1) # INSERT INTO items (b) VALUES (1)
|
1153
1266
|
#
|
1154
1267
|
# Note that datasets that specify custom SQL using this method will generally
|
1155
1268
|
# ignore future dataset methods that modify the SQL used, as specifying custom SQL
|
@@ -1223,18 +1336,22 @@ module Sequel
|
|
1223
1336
|
|
1224
1337
|
private
|
1225
1338
|
|
1226
|
-
#
|
1227
|
-
|
1228
|
-
|
1229
|
-
exts
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1339
|
+
# :nocov:
|
1340
|
+
unless TRUE_FREEZE
|
1341
|
+
# Load the extensions into the receiver, without checking if the receiver is frozen.
|
1342
|
+
def _extension!(exts)
|
1343
|
+
Sequel.extension(*exts)
|
1344
|
+
exts.each do |ext|
|
1345
|
+
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
1346
|
+
pr.call(self)
|
1347
|
+
else
|
1348
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
|
1349
|
+
end
|
1234
1350
|
end
|
1351
|
+
self
|
1235
1352
|
end
|
1236
|
-
self
|
1237
1353
|
end
|
1354
|
+
# :nocov:
|
1238
1355
|
|
1239
1356
|
# If invert is true, invert the condition.
|
1240
1357
|
def _invert_filter(cond, invert)
|
@@ -1245,6 +1362,18 @@ module Sequel
|
|
1245
1362
|
end
|
1246
1363
|
end
|
1247
1364
|
|
1365
|
+
# Append to the current MERGE WHEN clauses.
|
1366
|
+
# Mutates the hash to add the conditions, if a virtual row block is passed.
|
1367
|
+
def _merge_when(hash, &block)
|
1368
|
+
hash[:conditions] = Sequel.virtual_row(&block) if block
|
1369
|
+
|
1370
|
+
if merge_when = @opts[:merge_when]
|
1371
|
+
clone(:merge_when => (merge_when.dup << hash.freeze).freeze)
|
1372
|
+
else
|
1373
|
+
clone(:merge_when => [hash.freeze].freeze)
|
1374
|
+
end
|
1375
|
+
end
|
1376
|
+
|
1248
1377
|
# Add the given filter condition. Arguments:
|
1249
1378
|
# clause :: Symbol or which SQL clause to effect, should be :where or :having
|
1250
1379
|
# cond :: The filter condition to add
|