sequel 5.58.0 → 5.78.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +288 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +24 -23
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +20 -12
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- 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/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +62 -16
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +80 -1
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +494 -164
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +40 -4
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- 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/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/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/database/schema_methods.rb +13 -3
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +62 -37
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -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 +21 -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/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- 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 +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- 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 +50 -11
- data/lib/sequel/model/base.rb +45 -21
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/composition.rb +2 -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/finder.rb +4 -2
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- 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_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- 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/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +5 -5
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +1 -1
- metadata +76 -6
@@ -127,6 +127,18 @@ module Sequel
|
|
127
127
|
#
|
128
128
|
# DB[:table].delete # DELETE * FROM table
|
129
129
|
# # => 3
|
130
|
+
#
|
131
|
+
# Some databases support using multiple tables in a DELETE query. This requires
|
132
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
133
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
134
|
+
# appropriate filters for the FROM tables:
|
135
|
+
#
|
136
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
|
137
|
+
# delete
|
138
|
+
# # DELETE FROM a
|
139
|
+
# # USING b
|
140
|
+
# # INNER JOIN c ON (c.d = b.e)
|
141
|
+
# # WHERE ((a.f = b.g) AND (a.id = c.h))
|
130
142
|
def delete(&block)
|
131
143
|
sql = delete_sql
|
132
144
|
if uses_returning?(:delete)
|
@@ -162,7 +174,7 @@ module Sequel
|
|
162
174
|
# # => false
|
163
175
|
def empty?
|
164
176
|
cached_dataset(:_empty_ds) do
|
165
|
-
single_value_ds.unordered.select(EMPTY_SELECT)
|
177
|
+
(@opts[:sql] ? from_self : self).single_value_ds.unordered.select(EMPTY_SELECT)
|
166
178
|
end.single_value!.nil?
|
167
179
|
end
|
168
180
|
|
@@ -313,14 +325,18 @@ module Sequel
|
|
313
325
|
|
314
326
|
# Inserts multiple records into the associated table. This method can be
|
315
327
|
# used to efficiently insert a large number of records into a table in a
|
316
|
-
# single query if the database supports it. Inserts
|
317
|
-
#
|
328
|
+
# single query if the database supports it. Inserts are automatically
|
329
|
+
# wrapped in a transaction if necessary.
|
318
330
|
#
|
319
331
|
# This method is called with a columns array and an array of value arrays:
|
320
332
|
#
|
321
333
|
# DB[:table].import([:x, :y], [[1, 2], [3, 4]])
|
322
334
|
# # INSERT INTO table (x, y) VALUES (1, 2)
|
323
|
-
# # INSERT INTO table (x, y) VALUES (3, 4)
|
335
|
+
# # INSERT INTO table (x, y) VALUES (3, 4)
|
336
|
+
#
|
337
|
+
# or, if the database supports it:
|
338
|
+
#
|
339
|
+
# # INSERT INTO table (x, y) VALUES (1, 2), (3, 4)
|
324
340
|
#
|
325
341
|
# This method also accepts a dataset instead of an array of value arrays:
|
326
342
|
#
|
@@ -328,17 +344,23 @@ module Sequel
|
|
328
344
|
# # INSERT INTO table (x, y) SELECT a, b FROM table2
|
329
345
|
#
|
330
346
|
# Options:
|
331
|
-
# :commit_every :: Open a new transaction for every given number of
|
332
|
-
# For example, if you provide a value of 50,
|
333
|
-
# after every 50 records.
|
347
|
+
# :commit_every :: Open a new transaction for every given number of
|
348
|
+
# records. For example, if you provide a value of 50,
|
349
|
+
# will commit after every 50 records. When a
|
350
|
+
# transaction is not required, this option controls
|
351
|
+
# the maximum number of values to insert with a single
|
352
|
+
# statement; it does not force the use of a
|
353
|
+
# transaction.
|
334
354
|
# :return :: When this is set to :primary_key, returns an array of
|
335
355
|
# autoincremented primary key values for the rows inserted.
|
336
356
|
# This does not have an effect if +values+ is a Dataset.
|
337
357
|
# :server :: Set the server/shard to use for the transaction and insert
|
338
358
|
# queries.
|
359
|
+
# :skip_transaction :: Do not use a transaction even when using multiple
|
360
|
+
# INSERT queries.
|
339
361
|
# :slice :: Same as :commit_every, :commit_every takes precedence.
|
340
362
|
def import(columns, values, opts=OPTS)
|
341
|
-
return
|
363
|
+
return insert(columns, values) if values.is_a?(Dataset)
|
342
364
|
|
343
365
|
return if values.empty?
|
344
366
|
raise(Error, 'Using Sequel::Dataset#import with an empty column array is not allowed') if columns.empty?
|
@@ -568,6 +590,8 @@ module Sequel
|
|
568
590
|
# if your ORDER BY expressions are not simple columns, if they contain
|
569
591
|
# qualified identifiers that would be ambiguous unqualified, if they contain
|
570
592
|
# any identifiers that are aliased in SELECT, and potentially other cases.
|
593
|
+
# :skip_transaction :: Do not use a transaction. This can be useful if you want to prevent
|
594
|
+
# a lock on the database table, at the expense of consistency.
|
571
595
|
#
|
572
596
|
# Examples:
|
573
597
|
#
|
@@ -576,7 +600,7 @@ module Sequel
|
|
576
600
|
# # SELECT * FROM table ORDER BY id LIMIT 1000 OFFSET 1000
|
577
601
|
# # ...
|
578
602
|
#
|
579
|
-
# DB[:table].order(:id).paged_each(:
|
603
|
+
# DB[:table].order(:id).paged_each(rows_per_fetch: 100){|row| }
|
580
604
|
# # SELECT * FROM table ORDER BY id LIMIT 100
|
581
605
|
# # SELECT * FROM table ORDER BY id LIMIT 100 OFFSET 100
|
582
606
|
# # ...
|
@@ -918,6 +942,19 @@ module Sequel
|
|
918
942
|
#
|
919
943
|
# DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
|
920
944
|
# # => 10
|
945
|
+
#
|
946
|
+
# Some databases support using multiple tables in an UPDATE query. This requires
|
947
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
948
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
949
|
+
# appropriate filters for the FROM tables:
|
950
|
+
#
|
951
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
|
952
|
+
# update(:f=>Sequel[:c][:h])
|
953
|
+
# # UPDATE a
|
954
|
+
# # SET f = c.h
|
955
|
+
# # FROM b
|
956
|
+
# # INNER JOIN c ON (c.d = b.e)
|
957
|
+
# # WHERE ((a.f = b.g) AND (a.id = 10))
|
921
958
|
def update(values=OPTS, &block)
|
922
959
|
sql = update_sql(values)
|
923
960
|
if uses_returning?(:update)
|
@@ -1023,18 +1060,19 @@ module Sequel
|
|
1023
1060
|
|
1024
1061
|
# Internals of #import. If primary key values are requested, use
|
1025
1062
|
# separate insert commands for each row. Otherwise, call #multi_insert_sql
|
1026
|
-
# and execute each statement it gives separately.
|
1063
|
+
# and execute each statement it gives separately. A transaction is only used
|
1064
|
+
# if there are multiple statements to execute.
|
1027
1065
|
def _import(columns, values, opts)
|
1028
1066
|
trans_opts = Hash[opts]
|
1029
1067
|
trans_opts[:server] = @opts[:server]
|
1030
1068
|
if opts[:return] == :primary_key
|
1031
|
-
|
1069
|
+
_import_transaction(values, trans_opts){values.map{|v| insert(columns, v)}}
|
1032
1070
|
else
|
1033
1071
|
stmts = multi_insert_sql(columns, values)
|
1034
|
-
|
1072
|
+
_import_transaction(stmts, trans_opts){stmts.each{|st| execute_dui(st)}}
|
1035
1073
|
end
|
1036
1074
|
end
|
1037
|
-
|
1075
|
+
|
1038
1076
|
# Return an array of arrays of values given by the symbols in ret_cols.
|
1039
1077
|
def _select_map_multiple(ret_cols)
|
1040
1078
|
map{|r| r.values_at(*ret_cols)}
|
@@ -1073,6 +1111,15 @@ module Sequel
|
|
1073
1111
|
end
|
1074
1112
|
end
|
1075
1113
|
|
1114
|
+
# Use a transaction when yielding to the block if multiple values/statements
|
1115
|
+
# are provided. When only a single value or statement is provided, then yield
|
1116
|
+
# without using a transaction.
|
1117
|
+
def _import_transaction(values, trans_opts, &block)
|
1118
|
+
# OK to mutate trans_opts as it is generated by _import
|
1119
|
+
trans_opts[:skip_transaction] = true if values.length <= 1
|
1120
|
+
@db.transaction(trans_opts, &block)
|
1121
|
+
end
|
1122
|
+
|
1076
1123
|
# Internals of +select_hash+ and +select_hash_groups+
|
1077
1124
|
def _select_hash(meth, key_column, value_column, opts=OPTS)
|
1078
1125
|
select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).
|
@@ -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.
|
@@ -152,6 +157,11 @@ module Sequel
|
|
152
157
|
supports_distinct_on?
|
153
158
|
end
|
154
159
|
|
160
|
+
# Whether placeholder literalizers are supported, true by default.
|
161
|
+
def supports_placeholder_literalizer?
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
155
165
|
# Whether the dataset supports pattern matching by regular expressions, false by default.
|
156
166
|
def supports_regexp?
|
157
167
|
false
|
@@ -178,10 +188,14 @@ module Sequel
|
|
178
188
|
true
|
179
189
|
end
|
180
190
|
|
191
|
+
# :nocov:
|
192
|
+
|
181
193
|
# Whether the dataset supports timezones in literal timestamps, false by default.
|
182
194
|
def supports_timestamp_timezones?
|
195
|
+
# SEQUEL6: Remove
|
183
196
|
false
|
184
197
|
end
|
198
|
+
# :nocov:
|
185
199
|
|
186
200
|
# Whether the dataset supports fractional seconds in literal timestamps, true by default.
|
187
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:
|
@@ -787,7 +797,7 @@ module Sequel
|
|
787
797
|
# DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
|
788
798
|
# DB[:items].order(Sequel[:a] + :b) # SELECT * FROM items ORDER BY (a + b)
|
789
799
|
# DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
|
790
|
-
# DB[:items].order(Sequel.asc(:name, :
|
800
|
+
# DB[:items].order(Sequel.asc(:name, nulls: :last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
|
791
801
|
# DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
|
792
802
|
# DB[:items].order(nil) # SELECT * FROM items
|
793
803
|
def order(*columns, &block)
|
@@ -857,13 +867,13 @@ module Sequel
|
|
857
867
|
# DB[:items].returning(nil) # RETURNING NULL
|
858
868
|
# DB[:items].returning(:id, :name) # RETURNING id, name
|
859
869
|
#
|
860
|
-
# DB[:items].returning.insert(:
|
870
|
+
# DB[:items].returning.insert(a: 1) do |hash|
|
861
871
|
# # hash for each row inserted, with values for all columns
|
862
872
|
# end
|
863
|
-
# DB[:items].returning.update(:
|
873
|
+
# DB[:items].returning.update(a: 1) do |hash|
|
864
874
|
# # hash for each row updated, with values for all columns
|
865
875
|
# end
|
866
|
-
# DB[:items].returning.delete(:
|
876
|
+
# DB[:items].returning.delete(a: 1) do |hash|
|
867
877
|
# # hash for each row deleted, with values for all columns
|
868
878
|
# end
|
869
879
|
def returning(*values)
|
@@ -1102,7 +1112,7 @@ module Sequel
|
|
1102
1112
|
# referenced in window functions. See Sequel::SQL::Window for a list of
|
1103
1113
|
# options that can be passed in. Example:
|
1104
1114
|
#
|
1105
|
-
# DB[:items].window(:w, :
|
1115
|
+
# DB[:items].window(:w, partition: :c1, order: :c2)
|
1106
1116
|
# # SELECT * FROM items WINDOW w AS (PARTITION BY c1 ORDER BY c2)
|
1107
1117
|
def window(name, opts)
|
1108
1118
|
clone(:window=>((@opts[:window]||EMPTY_ARRAY) + [[name, SQL::Window.new(opts)].freeze]).freeze)
|
@@ -1163,7 +1173,7 @@ module Sequel
|
|
1163
1173
|
# DB[:t].with_recursive(:t,
|
1164
1174
|
# DB[:i1].select(:id, :parent_id).where(parent_id: nil),
|
1165
1175
|
# DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
|
1166
|
-
# :
|
1176
|
+
# args: [:id, :parent_id])
|
1167
1177
|
#
|
1168
1178
|
# # WITH RECURSIVE t(id, parent_id) AS (
|
1169
1179
|
# # SELECT id, parent_id FROM i1 WHERE (parent_id IS NULL)
|
@@ -1199,16 +1209,27 @@ module Sequel
|
|
1199
1209
|
end
|
1200
1210
|
|
1201
1211
|
if TRUE_FREEZE
|
1202
|
-
#
|
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
|
+
#
|
1203
1219
|
# Note that like Object#extend, when multiple modules are provided
|
1204
|
-
# as arguments the
|
1205
|
-
# order. If a block is provided, a DatasetModule is created using the block and
|
1206
|
-
# the clone is extended with that module after any modules given as arguments.
|
1220
|
+
# as arguments the subclass includes the modules in reverse order.
|
1207
1221
|
def with_extend(*mods, &block)
|
1208
|
-
c =
|
1209
|
-
c.
|
1210
|
-
c.
|
1211
|
-
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
|
1212
1233
|
end
|
1213
1234
|
else
|
1214
1235
|
# :nocov:
|
@@ -1241,7 +1262,7 @@ module Sequel
|
|
1241
1262
|
#
|
1242
1263
|
# You can also provide a method name and arguments to call to get the SQL:
|
1243
1264
|
#
|
1244
|
-
# DB[:items].with_sql(:insert_sql, :
|
1265
|
+
# DB[:items].with_sql(:insert_sql, b: 1) # INSERT INTO items (b) VALUES (1)
|
1245
1266
|
#
|
1246
1267
|
# Note that datasets that specify custom SQL using this method will generally
|
1247
1268
|
# ignore future dataset methods that modify the SQL used, as specifying custom SQL
|
@@ -1315,18 +1336,22 @@ module Sequel
|
|
1315
1336
|
|
1316
1337
|
private
|
1317
1338
|
|
1318
|
-
#
|
1319
|
-
|
1320
|
-
|
1321
|
-
exts
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
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
|
1326
1350
|
end
|
1351
|
+
self
|
1327
1352
|
end
|
1328
|
-
self
|
1329
1353
|
end
|
1354
|
+
# :nocov:
|
1330
1355
|
|
1331
1356
|
# If invert is true, invert the condition.
|
1332
1357
|
def _invert_filter(cond, invert)
|