sequel 5.33.0 → 5.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- 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/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- 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 +3 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- data/lib/sequel/extensions/server_block.rb +8 -12
- 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_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
@@ -52,7 +52,9 @@ module Sequel
|
|
52
52
|
unless select = dataset.opts[:select]
|
53
53
|
select = dataset.columns.map{|c| Sequel.qualify(dataset.first_source, c)}
|
54
54
|
end
|
55
|
+
db_schema = @db_schema
|
55
56
|
set_dataset(dataset.select(*select.reject{|c| attrs.include?(dataset.send(:_hash_key_symbol, c))}))
|
57
|
+
@db_schema = db_schema
|
56
58
|
attrs.each{|a| define_lazy_attribute_getter(a)}
|
57
59
|
end
|
58
60
|
|
@@ -71,6 +73,7 @@ module Sequel
|
|
71
73
|
super()
|
72
74
|
end
|
73
75
|
end
|
76
|
+
alias_method(a, a)
|
74
77
|
end
|
75
78
|
end
|
76
79
|
end
|
@@ -98,7 +101,7 @@ module Sequel
|
|
98
101
|
end
|
99
102
|
|
100
103
|
if retrieved_with
|
101
|
-
|
104
|
+
primary_key = model.primary_key
|
102
105
|
composite_pk = true if primary_key.is_a?(Array)
|
103
106
|
id_map = {}
|
104
107
|
retrieved_with.each{|o| id_map[o.pk] = o unless o.values.has_key?(a) || o.frozen?}
|
@@ -123,16 +123,25 @@ module Sequel
|
|
123
123
|
nil
|
124
124
|
end
|
125
125
|
|
126
|
+
# Whether a separate query should be used for each join table.
|
127
|
+
def separate_query_per_table?
|
128
|
+
self[:separate_query_per_table]
|
129
|
+
end
|
130
|
+
|
126
131
|
private
|
127
132
|
|
128
133
|
def _associated_dataset
|
129
134
|
ds = associated_class
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
135
|
+
if separate_query_per_table?
|
136
|
+
ds = ds.dataset
|
137
|
+
else
|
138
|
+
(reverse_edges + [final_reverse_edge]).each do |t|
|
139
|
+
h = {:qualify=>:deep}
|
140
|
+
if t[:alias] != t[:table]
|
141
|
+
h[:table_alias] = t[:alias]
|
142
|
+
end
|
143
|
+
ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), h)
|
134
144
|
end
|
135
|
-
ds = ds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), h)
|
136
145
|
end
|
137
146
|
ds
|
138
147
|
end
|
@@ -208,6 +217,7 @@ module Sequel
|
|
208
217
|
# :right (last array element) :: The key joining the table to the next table. Can use an
|
209
218
|
# array of symbols for a composite key association.
|
210
219
|
# If a hash is provided, the following keys are respected when using eager_graph:
|
220
|
+
# :db :: The Database containing the table. This changes lookup to use a separate query for each join table.
|
211
221
|
# :block :: A proc to use as the block argument to join.
|
212
222
|
# :conditions :: Extra conditions to add to the JOIN ON clause. Must be a hash or array of two pairs.
|
213
223
|
# :join_type :: The join type to use for the join, defaults to :left_outer.
|
@@ -233,32 +243,121 @@ module Sequel
|
|
233
243
|
opts[:after_load].unshift(:array_uniq!)
|
234
244
|
end
|
235
245
|
opts[:cartesian_product_number] ||= one_through_many ? 0 : 2
|
236
|
-
|
246
|
+
separate_query_per_table = false
|
247
|
+
through = opts[:through] = opts[:through].map do |e|
|
237
248
|
case e
|
238
249
|
when Array
|
239
250
|
raise(Error, "array elements of the through option/argument for many_through_many associations must have at least three elements") unless e.length == 3
|
240
251
|
{:table=>e[0], :left=>e[1], :right=>e[2]}
|
241
252
|
when Hash
|
242
253
|
raise(Error, "hash elements of the through option/argument for many_through_many associations must contain :table, :left, and :right keys") unless e[:table] && e[:left] && e[:right]
|
254
|
+
separate_query_per_table = true if e[:db]
|
243
255
|
e
|
244
256
|
else
|
245
257
|
raise(Error, "the through option/argument for many_through_many associations must be an enumerable of arrays or hashes")
|
246
258
|
end
|
247
259
|
end
|
260
|
+
opts[:separate_query_per_table] = separate_query_per_table
|
248
261
|
|
249
262
|
left_key = opts[:left_key] = opts[:through].first[:left]
|
250
263
|
opts[:left_keys] = Array(left_key)
|
251
|
-
opts[:uses_left_composite_keys] = left_key.is_a?(Array)
|
264
|
+
uses_lcks = opts[:uses_left_composite_keys] = left_key.is_a?(Array)
|
252
265
|
left_pk = (opts[:left_primary_key] ||= self.primary_key)
|
253
266
|
raise(Error, "no primary key specified for #{inspect}") unless left_pk
|
254
267
|
opts[:eager_loader_key] = left_pk unless opts.has_key?(:eager_loader_key)
|
255
268
|
opts[:left_primary_keys] = Array(left_pk)
|
256
269
|
lpkc = opts[:left_primary_key_column] ||= left_pk
|
257
270
|
lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
|
258
|
-
opts[:dataset] ||= opts.association_dataset_proc
|
259
271
|
|
260
272
|
opts[:left_key_alias] ||= opts.default_associated_key_alias
|
261
|
-
|
273
|
+
if separate_query_per_table
|
274
|
+
opts[:use_placeholder_loader] = false
|
275
|
+
opts[:allow_eager_graph] = false
|
276
|
+
opts[:allow_filtering_by] = false
|
277
|
+
opts[:eager_limit_strategy] = nil
|
278
|
+
|
279
|
+
opts[:dataset] ||= proc do |r|
|
280
|
+
def_db = r.associated_class.db
|
281
|
+
vals = uses_lcks ? [lpkcs.map{|k| get_column_value(k)}] : get_column_value(left_pk)
|
282
|
+
|
283
|
+
has_results = through.each do |edge|
|
284
|
+
ds = (edge[:db] || def_db).from(edge[:table]).where(edge[:left]=>vals)
|
285
|
+
ds = ds.where(edge[:conditions]) if edge[:conditions]
|
286
|
+
right = edge[:right]
|
287
|
+
vals = ds.select_map(right)
|
288
|
+
if right.is_a?(Array)
|
289
|
+
vals.delete_if{|v| v.any?(&:nil?)}
|
290
|
+
else
|
291
|
+
vals.delete(nil)
|
292
|
+
end
|
293
|
+
break if vals.empty?
|
294
|
+
end
|
295
|
+
|
296
|
+
ds = r.associated_dataset.where(opts.right_primary_key=>vals)
|
297
|
+
ds = ds.clone(:no_results=>true) unless has_results
|
298
|
+
ds
|
299
|
+
end
|
300
|
+
opts[:eager_loader] ||= proc do |eo|
|
301
|
+
h = eo[:id_map]
|
302
|
+
assign_singular = opts.assign_singular?
|
303
|
+
uses_rcks = opts.right_primary_key.is_a?(Array)
|
304
|
+
rpk = uses_rcks ? opts.right_primary_keys : opts.right_primary_key
|
305
|
+
name = opts[:name]
|
306
|
+
def_db = opts.associated_class.db
|
307
|
+
join_map = h
|
308
|
+
|
309
|
+
run_query = through.each do |edge|
|
310
|
+
ds = (edge[:db] || def_db).from(edge[:table])
|
311
|
+
ds = ds.where(edge[:conditions]) if edge[:conditions]
|
312
|
+
left = edge[:left]
|
313
|
+
right = edge[:right]
|
314
|
+
prev_map = join_map
|
315
|
+
join_map = ds.where(left=>join_map.keys).select_hash_groups(right, left)
|
316
|
+
if right.is_a?(Array)
|
317
|
+
join_map.delete_if{|v,| v.any?(&:nil?)}
|
318
|
+
else
|
319
|
+
join_map.delete(nil)
|
320
|
+
end
|
321
|
+
break if join_map.empty?
|
322
|
+
join_map.each_value do |vs|
|
323
|
+
vs.replace(vs.flat_map{|v| prev_map[v]})
|
324
|
+
vs.uniq!
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
eo = Hash[eo]
|
329
|
+
|
330
|
+
if run_query
|
331
|
+
eo[:loader] = false
|
332
|
+
eo[:right_keys] = join_map.keys
|
333
|
+
else
|
334
|
+
eo[:no_results] = true
|
335
|
+
end
|
336
|
+
|
337
|
+
opts[:model].eager_load_results(opts, eo) do |assoc_record|
|
338
|
+
rpkv = if uses_rcks
|
339
|
+
assoc_record.values.values_at(*rpk)
|
340
|
+
else
|
341
|
+
assoc_record.values[rpk]
|
342
|
+
end
|
343
|
+
|
344
|
+
objects = join_map[rpkv]
|
345
|
+
|
346
|
+
if assign_singular
|
347
|
+
objects.each do |object|
|
348
|
+
object.associations[name] ||= assoc_record
|
349
|
+
end
|
350
|
+
else
|
351
|
+
objects.each do |object|
|
352
|
+
object.associations[name].push(assoc_record)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
else
|
358
|
+
opts[:dataset] ||= opts.association_dataset_proc
|
359
|
+
opts[:eager_loader] ||= opts.method(:default_eager_loader)
|
360
|
+
end
|
262
361
|
|
263
362
|
join_type = opts[:graph_join_type]
|
264
363
|
select = opts[:graph_select]
|
@@ -108,9 +108,10 @@ module Sequel
|
|
108
108
|
# array of the allowable fields.
|
109
109
|
# :limit :: For *_to_many associations, a limit on the number of records
|
110
110
|
# that will be processed, to prevent denial of service attacks.
|
111
|
-
# :reject_if :: A proc that is
|
111
|
+
# :reject_if :: A proc that is called with each attribute hash before it is
|
112
112
|
# passed to its associated object. If the proc returns a truthy
|
113
113
|
# value, the attribute hash is ignored.
|
114
|
+
# :reject_nil :: Ignore nil objects passed to nested attributes setter methods.
|
114
115
|
# :remove :: Allow disassociation of nested records (can remove the associated
|
115
116
|
# object from the parent object, but not destroy the associated object).
|
116
117
|
# :require_modification :: Whether to require modification of nested objects when
|
@@ -145,9 +146,12 @@ module Sequel
|
|
145
146
|
# class.
|
146
147
|
def def_nested_attribute_method(reflection)
|
147
148
|
@nested_attributes_module.class_eval do
|
148
|
-
|
149
|
-
|
149
|
+
meth = :"#{reflection[:name]}_attributes="
|
150
|
+
assoc = reflection[:name]
|
151
|
+
define_method(meth) do |v|
|
152
|
+
set_nested_attributes(assoc, v)
|
150
153
|
end
|
154
|
+
alias_method meth, meth
|
151
155
|
end
|
152
156
|
end
|
153
157
|
end
|
@@ -159,6 +163,7 @@ module Sequel
|
|
159
163
|
def set_nested_attributes(assoc, obj, opts=OPTS)
|
160
164
|
raise(Error, "no association named #{assoc} for #{model.inspect}") unless ref = model.association_reflection(assoc)
|
161
165
|
raise(Error, "nested attributes are not enabled for association #{assoc} for #{model.inspect}") unless meta = ref[:nested_attributes]
|
166
|
+
return if obj.nil? && meta[:reject_nil]
|
162
167
|
meta = meta.merge(opts)
|
163
168
|
meta[:reflection] = ref
|
164
169
|
if ref.returns_array?
|
@@ -341,10 +341,9 @@ module Sequel
|
|
341
341
|
eo[:loader] = false
|
342
342
|
|
343
343
|
eager_load_results(opts, eo) do |assoc_record|
|
344
|
-
if pks
|
344
|
+
if pks = assoc_record.get_column_value(key)
|
345
345
|
pks.each do |pkv|
|
346
|
-
|
347
|
-
objects.each do |object|
|
346
|
+
id_map[pkv].each do |object|
|
348
347
|
object.associations[name].push(assoc_record)
|
349
348
|
end
|
350
349
|
end
|
@@ -385,26 +384,32 @@ module Sequel
|
|
385
384
|
save_opts = {:validate=>opts[:validate]}
|
386
385
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
387
386
|
|
388
|
-
opts
|
389
|
-
|
390
|
-
array
|
391
|
-
|
392
|
-
|
387
|
+
unless opts.has_key?(:adder)
|
388
|
+
opts[:adder] = proc do |o|
|
389
|
+
if array = o.get_column_value(key)
|
390
|
+
array << get_column_value(pk)
|
391
|
+
else
|
392
|
+
o.set_column_value("#{key}=", Sequel.pg_array([get_column_value(pk)], opts.array_type))
|
393
|
+
end
|
394
|
+
o.save(save_opts)
|
393
395
|
end
|
394
|
-
o.save(save_opts)
|
395
396
|
end
|
396
|
-
|
397
|
-
opts
|
398
|
-
|
399
|
-
array.
|
400
|
-
|
397
|
+
|
398
|
+
unless opts.has_key?(:remover)
|
399
|
+
opts[:remover] = proc do |o|
|
400
|
+
if (array = o.get_column_value(key)) && !array.empty?
|
401
|
+
array.delete(get_column_value(pk))
|
402
|
+
o.save(save_opts)
|
403
|
+
end
|
401
404
|
end
|
402
405
|
end
|
403
406
|
|
404
|
-
opts
|
405
|
-
|
406
|
-
|
407
|
-
|
407
|
+
unless opts.has_key?(:clearer)
|
408
|
+
opts[:clearer] = proc do
|
409
|
+
pk_value = get_column_value(pk)
|
410
|
+
db_type = opts.array_type
|
411
|
+
opts.associated_dataset.where(Sequel.pg_array_op(key).contains(Sequel.pg_array([pk_value], db_type))).update(key=>Sequel.function(:array_remove, key, Sequel.cast(pk_value, db_type)))
|
412
|
+
end
|
408
413
|
end
|
409
414
|
end
|
410
415
|
|
@@ -427,10 +432,12 @@ module Sequel
|
|
427
432
|
id_map = {}
|
428
433
|
pkm = opts.primary_key_method
|
429
434
|
|
430
|
-
|
431
|
-
|
432
|
-
associated_pks
|
433
|
-
|
435
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
436
|
+
rows.each do |object|
|
437
|
+
if associated_pks = object.get_column_value(key)
|
438
|
+
associated_pks.each do |apk|
|
439
|
+
(id_map[apk] ||= []) << object
|
440
|
+
end
|
434
441
|
end
|
435
442
|
end
|
436
443
|
end
|
@@ -485,30 +492,36 @@ module Sequel
|
|
485
492
|
end
|
486
493
|
end
|
487
494
|
|
488
|
-
opts
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
+
unless opts.has_key?(:adder)
|
496
|
+
opts[:adder] = proc do |o|
|
497
|
+
opk = o.get_column_value(opts.primary_key)
|
498
|
+
if array = get_column_value(key)
|
499
|
+
modified!(key)
|
500
|
+
array << opk
|
501
|
+
else
|
502
|
+
set_column_value("#{key}=", Sequel.pg_array([opk], opts.array_type))
|
503
|
+
end
|
504
|
+
save_after_modify.call(self) if save_after_modify
|
495
505
|
end
|
496
|
-
save_after_modify.call(self) if save_after_modify
|
497
506
|
end
|
498
|
-
|
499
|
-
opts
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
507
|
+
|
508
|
+
unless opts.has_key?(:remover)
|
509
|
+
opts[:remover] = proc do |o|
|
510
|
+
if (array = get_column_value(key)) && !array.empty?
|
511
|
+
modified!(key)
|
512
|
+
array.delete(o.get_column_value(opts.primary_key))
|
513
|
+
save_after_modify.call(self) if save_after_modify
|
514
|
+
end
|
504
515
|
end
|
505
516
|
end
|
506
517
|
|
507
|
-
opts
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
518
|
+
unless opts.has_key?(:clearer)
|
519
|
+
opts[:clearer] = proc do
|
520
|
+
if (array = get_column_value(key)) && !array.empty?
|
521
|
+
modified!(key)
|
522
|
+
array.clear
|
523
|
+
save_after_modify.call(self) if save_after_modify
|
524
|
+
end
|
512
525
|
end
|
513
526
|
end
|
514
527
|
end
|
@@ -521,7 +534,9 @@ module Sequel
|
|
521
534
|
def many_to_pg_array_association_filter_expression(op, ref, obj)
|
522
535
|
pk = ref.qualify(model.table_name, ref.primary_key)
|
523
536
|
key = ref[:key]
|
537
|
+
# :nocov:
|
524
538
|
expr = case obj
|
539
|
+
# :nocov:
|
525
540
|
when Sequel::Model
|
526
541
|
if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
|
527
542
|
Sequel[pk=>assoc_pks.to_a]
|
@@ -541,7 +556,9 @@ module Sequel
|
|
541
556
|
# Support filtering by pg_array_to_many associations using a subquery.
|
542
557
|
def pg_array_to_many_association_filter_expression(op, ref, obj)
|
543
558
|
key = ref.qualify(model.table_name, ref[:key_column])
|
559
|
+
# :nocov:
|
544
560
|
expr = case obj
|
561
|
+
# :nocov:
|
545
562
|
when Sequel::Model
|
546
563
|
if pkv = obj.get_column_value(ref.primary_key_method)
|
547
564
|
Sequel.pg_array_op(key).contains(Sequel.pg_array([pkv], ref.array_type))
|
@@ -250,7 +250,9 @@ module Sequel
|
|
250
250
|
messages = model.pg_auto_constraint_validations_messages
|
251
251
|
|
252
252
|
unless override
|
253
|
+
# :nocov:
|
253
254
|
case e
|
255
|
+
# :nocov:
|
254
256
|
when Sequel::NotNullConstraintViolation
|
255
257
|
if column = info[:column]
|
256
258
|
add_pg_constraint_validation_error([m.call(column)], messages[:not_null])
|
@@ -41,11 +41,9 @@ module Sequel
|
|
41
41
|
# Create a prepared statement, but modify the SQL used so that the model's columns are explicitly
|
42
42
|
# selected instead of using *, assuming that the dataset selects from a single table.
|
43
43
|
def prepare_explicit_statement(ds, type, vals=OPTS)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if f && f.length == 1 && !ds.opts[:join] && (!s || s.empty?)
|
48
|
-
ds = ds.public_send(meth, *columns.map{|c| Sequel.identifier(c)})
|
44
|
+
s = ds.opts[:returning]
|
45
|
+
if !s || s.empty?
|
46
|
+
ds = ds.returning(*columns.map{|c| Sequel.identifier(c)})
|
49
47
|
end
|
50
48
|
|
51
49
|
prepare_statement(ds, type, vals)
|
@@ -70,9 +68,7 @@ module Sequel
|
|
70
68
|
# Return a prepared statement that can be used to insert a row using the given columns
|
71
69
|
# and return that column values for the row created.
|
72
70
|
def prepared_insert_select(cols)
|
73
|
-
|
74
|
-
cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
|
75
|
-
end
|
71
|
+
cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
|
76
72
|
end
|
77
73
|
|
78
74
|
# Return an array of two element arrays with the column symbol as the first entry and the
|
@@ -138,9 +134,7 @@ module Sequel
|
|
138
134
|
# and return the new column values.
|
139
135
|
def _insert_select_raw(ds)
|
140
136
|
if use_prepared_statements_for?(:insert_select)
|
141
|
-
|
142
|
-
_set_prepared_statement_server(ps).call(@values)
|
143
|
-
end
|
137
|
+
_set_prepared_statement_server(model.send(:prepared_insert_select, @values.keys)).call(@values)
|
144
138
|
else
|
145
139
|
super
|
146
140
|
end
|
@@ -175,8 +169,17 @@ module Sequel
|
|
175
169
|
end
|
176
170
|
|
177
171
|
case type
|
178
|
-
when :insert, :
|
172
|
+
when :insert, :update
|
179
173
|
true
|
174
|
+
when :insert_select
|
175
|
+
# SQLite RETURNING support has a bug that doesn't allow for committing transactions
|
176
|
+
# when a prepared statement with RETURNING has been used on the connection:
|
177
|
+
#
|
178
|
+
# SQLite3::BusyException: cannot commit transaction - SQL statements in progress: COMMIT
|
179
|
+
#
|
180
|
+
# Disabling usage of prepared statements for insert_select on SQLite seems to be the
|
181
|
+
# simplest way to workaround the problem.
|
182
|
+
db.database_type != :sqlite
|
180
183
|
# :nocov:
|
181
184
|
when :delete, :refresh
|
182
185
|
Sequel::Deprecation.deprecate("The :delete and :refresh prepared statement types", "There should be no need to check if these types are supported")
|
@@ -66,9 +66,7 @@ module Sequel
|
|
66
66
|
# Merge the current values into the default values to reduce the number
|
67
67
|
# of free columns.
|
68
68
|
def before_create
|
69
|
-
|
70
|
-
@values = v.merge(values)
|
71
|
-
end
|
69
|
+
@values = model.prepared_statements_column_defaults.merge(@values)
|
72
70
|
super
|
73
71
|
end
|
74
72
|
|
@@ -170,11 +170,13 @@ module Sequel
|
|
170
170
|
id_map = eo[:id_map]
|
171
171
|
parent_map = {}
|
172
172
|
children_map = {}
|
173
|
-
eo[:
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
174
|
+
eo[:rows].each do |obj|
|
175
|
+
parent_map[prkey_conv[obj]] = obj
|
176
|
+
(children_map[key_conv[obj]] ||= []) << obj
|
177
|
+
obj.associations[ancestors] = []
|
178
|
+
obj.associations[parent] = nil
|
179
|
+
end
|
178
180
|
end
|
179
181
|
r = model.association_reflection(ancestors)
|
180
182
|
base_case = model.where(prkey=>id_map.keys).
|
@@ -192,29 +194,27 @@ module Sequel
|
|
192
194
|
:args=>((key_aliases + col_aliases) if col_aliases))
|
193
195
|
ds = r.apply_eager_dataset_changes(ds)
|
194
196
|
ds = ds.select_append(ka) unless ds.opts[:select] == nil
|
195
|
-
model.eager_load_results(r, eo.merge(:loader=>false, :
|
197
|
+
model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
|
196
198
|
opk = prkey_conv[obj]
|
197
|
-
if parent_map
|
198
|
-
|
199
|
-
|
200
|
-
obj = idm_obj
|
201
|
-
end
|
199
|
+
if idm_obj = parent_map[opk]
|
200
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
201
|
+
obj = idm_obj
|
202
202
|
else
|
203
203
|
obj.associations[parent] = nil
|
204
204
|
parent_map[opk] = obj
|
205
205
|
(children_map[key_conv[obj]] ||= []) << obj
|
206
206
|
end
|
207
207
|
|
208
|
-
|
209
|
-
|
210
|
-
root.associations[ancestors] << obj
|
211
|
-
end
|
208
|
+
id_map[extract_key_alias[obj]].each do |root|
|
209
|
+
root.associations[ancestors] << obj
|
212
210
|
end
|
213
211
|
end
|
214
|
-
|
215
|
-
|
216
|
-
children
|
217
|
-
|
212
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
213
|
+
parent_map.each do |parent_id, obj|
|
214
|
+
if children = children_map[parent_id]
|
215
|
+
children.each do |child|
|
216
|
+
child.associations[parent] = obj
|
217
|
+
end
|
218
218
|
end
|
219
219
|
end
|
220
220
|
end
|
@@ -272,10 +272,12 @@ module Sequel
|
|
272
272
|
associations = eo[:associations]
|
273
273
|
parent_map = {}
|
274
274
|
children_map = {}
|
275
|
-
eo[:
|
276
|
-
|
277
|
-
|
278
|
-
|
275
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
276
|
+
eo[:rows].each do |obj|
|
277
|
+
parent_map[prkey_conv[obj]] = obj
|
278
|
+
obj.associations[descendants] = []
|
279
|
+
obj.associations[childrena] = []
|
280
|
+
end
|
279
281
|
end
|
280
282
|
r = model.association_reflection(descendants)
|
281
283
|
base_case = model.where(key=>id_map.keys).
|
@@ -300,17 +302,15 @@ module Sequel
|
|
300
302
|
:args=>((key_aliases + col_aliases + (level ? [la] : [])) if col_aliases))
|
301
303
|
ds = r.apply_eager_dataset_changes(ds)
|
302
304
|
ds = ds.select_append(ka) unless ds.opts[:select] == nil
|
303
|
-
model.eager_load_results(r, eo.merge(:loader=>false, :
|
305
|
+
model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
|
304
306
|
if level
|
305
307
|
no_cache = no_cache_level == obj.values.delete(la)
|
306
308
|
end
|
307
309
|
|
308
310
|
opk = prkey_conv[obj]
|
309
|
-
if parent_map
|
310
|
-
|
311
|
-
|
312
|
-
obj = idm_obj
|
313
|
-
end
|
311
|
+
if idm_obj = parent_map[opk]
|
312
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
313
|
+
obj = idm_obj
|
314
314
|
else
|
315
315
|
obj.associations[childrena] = [] unless no_cache
|
316
316
|
parent_map[opk] = obj
|
@@ -322,12 +322,14 @@ module Sequel
|
|
322
322
|
|
323
323
|
(children_map[key_conv[obj]] ||= []) << obj
|
324
324
|
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
325
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
326
|
+
children_map.each do |parent_id, objs|
|
327
|
+
objs = objs.uniq
|
328
|
+
parent_obj = parent_map[parent_id]
|
329
|
+
parent_obj.associations[childrena] = objs
|
330
|
+
objs.each do |obj|
|
331
|
+
obj.associations[parent] = parent_obj
|
332
|
+
end
|
331
333
|
end
|
332
334
|
end
|
333
335
|
end
|
@@ -127,7 +127,7 @@ module Sequel
|
|
127
127
|
def serialize_attributes(format, *columns)
|
128
128
|
if format.is_a?(Symbol)
|
129
129
|
unless format = Sequel.synchronize{REGISTERED_FORMATS[format]}
|
130
|
-
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.
|
130
|
+
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.inspect})")
|
131
131
|
end
|
132
132
|
end
|
133
133
|
serializer, deserializer = format
|
@@ -154,7 +154,10 @@ module Sequel
|
|
154
154
|
deserialized_values[column] = deserialize_value(column, super())
|
155
155
|
end
|
156
156
|
end
|
157
|
-
|
157
|
+
alias_method(column, column)
|
158
|
+
|
159
|
+
setter = :"#{column}="
|
160
|
+
define_method(setter) do |v|
|
158
161
|
cc = changed_columns
|
159
162
|
if !cc.include?(column) && (new? || get_column_value(column) != v)
|
160
163
|
cc << column
|
@@ -164,6 +167,7 @@ module Sequel
|
|
164
167
|
|
165
168
|
deserialized_values[column] = v
|
166
169
|
end
|
170
|
+
alias_method(setter, setter)
|
167
171
|
end
|
168
172
|
end
|
169
173
|
end
|
@@ -177,8 +181,10 @@ module Sequel
|
|
177
181
|
|
178
182
|
# Freeze the deserialized values
|
179
183
|
def freeze
|
180
|
-
deserialized_values
|
184
|
+
deserialized_values
|
181
185
|
super
|
186
|
+
deserialized_values.freeze
|
187
|
+
self
|
182
188
|
end
|
183
189
|
|
184
190
|
# Serialize deserialized values before saving
|
@@ -50,8 +50,9 @@ module Sequel
|
|
50
50
|
# Freeze the original deserialized values when freezing the instance.
|
51
51
|
def freeze
|
52
52
|
@original_deserialized_values ||= {}
|
53
|
-
@original_deserialized_values.freeze
|
54
53
|
super
|
54
|
+
@original_deserialized_values.freeze
|
55
|
+
self
|
55
56
|
end
|
56
57
|
|
57
58
|
private
|