sequel 5.33.0 → 5.58.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 +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
|