sequel 3.48.0 → 4.0.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 +114 -0
- data/Rakefile +10 -7
- data/doc/association_basics.rdoc +25 -23
- data/doc/code_order.rdoc +7 -0
- data/doc/core_extensions.rdoc +0 -10
- data/doc/object_model.rdoc +4 -1
- data/doc/querying.rdoc +3 -3
- data/doc/release_notes/4.0.0.txt +262 -0
- data/doc/security.rdoc +0 -28
- data/doc/testing.rdoc +8 -14
- data/lib/sequel/adapters/ado.rb +7 -11
- data/lib/sequel/adapters/ado/access.rb +8 -8
- data/lib/sequel/adapters/ado/mssql.rb +4 -4
- data/lib/sequel/adapters/amalgalite.rb +6 -6
- data/lib/sequel/adapters/cubrid.rb +7 -7
- data/lib/sequel/adapters/db2.rb +5 -9
- data/lib/sequel/adapters/dbi.rb +2 -6
- data/lib/sequel/adapters/do.rb +4 -4
- data/lib/sequel/adapters/firebird.rb +4 -4
- data/lib/sequel/adapters/ibmdb.rb +8 -8
- data/lib/sequel/adapters/informix.rb +2 -10
- data/lib/sequel/adapters/jdbc.rb +17 -17
- data/lib/sequel/adapters/jdbc/as400.rb +2 -2
- data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
- data/lib/sequel/adapters/jdbc/db2.rb +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
- data/lib/sequel/adapters/jdbc/informix.rb +1 -1
- data/lib/sequel/adapters/jdbc/mssql.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
- data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
- data/lib/sequel/adapters/jdbc/transactions.rb +3 -3
- data/lib/sequel/adapters/mock.rb +7 -7
- data/lib/sequel/adapters/mysql.rb +3 -3
- data/lib/sequel/adapters/mysql2.rb +4 -4
- data/lib/sequel/adapters/odbc.rb +2 -6
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +1 -5
- data/lib/sequel/adapters/oracle.rb +13 -17
- data/lib/sequel/adapters/postgres.rb +20 -25
- data/lib/sequel/adapters/shared/cubrid.rb +3 -3
- data/lib/sequel/adapters/shared/db2.rb +2 -2
- data/lib/sequel/adapters/shared/firebird.rb +7 -7
- data/lib/sequel/adapters/shared/mssql.rb +9 -9
- data/lib/sequel/adapters/shared/mysql.rb +29 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +7 -7
- data/lib/sequel/adapters/shared/oracle.rb +22 -13
- data/lib/sequel/adapters/shared/postgres.rb +61 -46
- data/lib/sequel/adapters/shared/sqlite.rb +9 -9
- data/lib/sequel/adapters/sqlite.rb +17 -11
- data/lib/sequel/adapters/swift.rb +3 -3
- data/lib/sequel/adapters/swift/mysql.rb +1 -1
- data/lib/sequel/adapters/swift/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +8 -8
- data/lib/sequel/ast_transformer.rb +3 -1
- data/lib/sequel/connection_pool.rb +4 -2
- data/lib/sequel/connection_pool/sharded_single.rb +2 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -5
- data/lib/sequel/connection_pool/threaded.rb +7 -7
- data/lib/sequel/core.rb +4 -67
- data/lib/sequel/database.rb +1 -0
- data/lib/sequel/database/connecting.rb +2 -8
- data/lib/sequel/database/dataset.rb +2 -7
- data/lib/sequel/database/dataset_defaults.rb +0 -18
- data/lib/sequel/database/features.rb +4 -4
- data/lib/sequel/database/misc.rb +6 -8
- data/lib/sequel/database/query.rb +5 -61
- data/lib/sequel/database/schema_generator.rb +22 -20
- data/lib/sequel/database/schema_methods.rb +48 -20
- data/lib/sequel/database/transactions.rb +7 -17
- data/lib/sequel/dataset.rb +2 -0
- data/lib/sequel/dataset/actions.rb +23 -91
- data/lib/sequel/dataset/features.rb +1 -4
- data/lib/sequel/dataset/graph.rb +3 -47
- data/lib/sequel/dataset/misc.rb +4 -33
- data/lib/sequel/dataset/prepared_statements.rb +3 -1
- data/lib/sequel/dataset/query.rb +116 -240
- data/lib/sequel/dataset/sql.rb +19 -97
- data/lib/sequel/deprecated.rb +0 -16
- data/lib/sequel/exceptions.rb +0 -3
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/columns_introspection.rb +1 -12
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/core_extensions.rb +0 -9
- data/lib/sequel/extensions/date_arithmetic.rb +1 -2
- data/lib/sequel/extensions/graph_each.rb +11 -0
- data/lib/sequel/extensions/migration.rb +5 -5
- data/lib/sequel/extensions/null_dataset.rb +11 -13
- data/lib/sequel/extensions/pagination.rb +3 -6
- data/lib/sequel/extensions/pg_array.rb +6 -4
- data/lib/sequel/extensions/pg_array_ops.rb +35 -1
- data/lib/sequel/extensions/pg_json.rb +12 -2
- data/lib/sequel/extensions/pg_json_ops.rb +266 -0
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_range_ops.rb +0 -8
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/pretty_table.rb +0 -4
- data/lib/sequel/extensions/query.rb +3 -8
- data/lib/sequel/extensions/schema_caching.rb +0 -7
- data/lib/sequel/extensions/schema_dumper.rb +10 -17
- data/lib/sequel/extensions/select_remove.rb +0 -4
- data/lib/sequel/extensions/set_overrides.rb +28 -0
- data/lib/sequel/extensions/to_dot.rb +6 -10
- data/lib/sequel/model.rb +6 -7
- data/lib/sequel/model/associations.rb +127 -182
- data/lib/sequel/model/base.rb +88 -211
- data/lib/sequel/model/errors.rb +0 -13
- data/lib/sequel/model/plugins.rb +2 -2
- data/lib/sequel/no_core_ext.rb +0 -1
- data/lib/sequel/plugins/after_initialize.rb +11 -17
- data/lib/sequel/plugins/association_autoreloading.rb +1 -47
- data/lib/sequel/plugins/association_dependencies.rb +2 -2
- data/lib/sequel/plugins/auto_validations.rb +2 -8
- data/lib/sequel/plugins/blacklist_security.rb +32 -2
- data/lib/sequel/plugins/caching.rb +1 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/composition.rb +10 -8
- data/lib/sequel/plugins/constraint_validations.rb +2 -2
- data/lib/sequel/plugins/dataset_associations.rb +4 -0
- data/lib/sequel/plugins/defaults_setter.rb +8 -6
- data/lib/sequel/plugins/dirty.rb +6 -6
- data/lib/sequel/plugins/force_encoding.rb +13 -8
- data/lib/sequel/plugins/hook_class_methods.rb +1 -7
- data/lib/sequel/plugins/json_serializer.rb +13 -74
- data/lib/sequel/plugins/lazy_attributes.rb +2 -4
- data/lib/sequel/plugins/list.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +4 -11
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +1 -49
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/optimistic_locking.rb +3 -5
- data/lib/sequel/plugins/pg_array_associations.rb +453 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +23 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +20 -14
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +5 -4
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +7 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/touch.rb +2 -2
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +19 -4
- data/lib/sequel/plugins/validation_class_methods.rb +0 -30
- data/lib/sequel/plugins/validation_helpers.rb +13 -31
- data/lib/sequel/plugins/xml_serializer.rb +18 -57
- data/lib/sequel/sql.rb +20 -22
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/db2_spec.rb +14 -23
- data/spec/adapters/firebird_spec.rb +25 -29
- data/spec/adapters/informix_spec.rb +11 -14
- data/spec/adapters/mssql_spec.rb +71 -77
- data/spec/adapters/mysql_spec.rb +165 -172
- data/spec/adapters/oracle_spec.rb +36 -39
- data/spec/adapters/postgres_spec.rb +175 -100
- data/spec/adapters/spec_helper.rb +13 -11
- data/spec/adapters/sqlite_spec.rb +36 -44
- data/spec/core/connection_pool_spec.rb +2 -1
- data/spec/core/database_spec.rb +55 -55
- data/spec/core/dataset_spec.rb +45 -249
- data/spec/core/deprecated_spec.rb +0 -8
- data/spec/core/expression_filters_spec.rb +23 -5
- data/spec/core/object_graph_spec.rb +4 -66
- data/spec/core/schema_spec.rb +35 -12
- data/spec/core/spec_helper.rb +3 -2
- data/spec/core_extensions_spec.rb +17 -19
- data/spec/extensions/arbitrary_servers_spec.rb +2 -3
- data/spec/extensions/association_dependencies_spec.rb +14 -14
- data/spec/extensions/auto_validations_spec.rb +7 -0
- data/spec/extensions/blacklist_security_spec.rb +5 -5
- data/spec/extensions/blank_spec.rb +2 -0
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/extensions/columns_introspection_spec.rb +2 -29
- data/spec/extensions/composition_spec.rb +10 -17
- data/spec/extensions/core_refinements_spec.rb +5 -1
- data/spec/extensions/dataset_associations_spec.rb +18 -0
- data/spec/extensions/date_arithmetic_spec.rb +2 -2
- data/spec/extensions/defaults_setter_spec.rb +9 -9
- data/spec/extensions/dirty_spec.rb +0 -5
- data/spec/extensions/eval_inspect_spec.rb +2 -0
- data/spec/extensions/force_encoding_spec.rb +2 -18
- data/spec/extensions/hash_aliases_spec.rb +8 -0
- data/spec/extensions/hook_class_methods_spec.rb +39 -58
- data/spec/extensions/inflector_spec.rb +2 -0
- data/spec/extensions/instance_filters_spec.rb +8 -8
- data/spec/extensions/json_serializer_spec.rb +1 -41
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +106 -109
- data/spec/extensions/migration_spec.rb +2 -0
- data/spec/extensions/named_timezones_spec.rb +1 -0
- data/spec/extensions/pg_array_associations_spec.rb +603 -0
- data/spec/extensions/pg_array_ops_spec.rb +25 -0
- data/spec/extensions/pg_array_spec.rb +9 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +13 -0
- data/spec/extensions/pg_hstore_spec.rb +1 -0
- data/spec/extensions/pg_json_ops_spec.rb +131 -0
- data/spec/extensions/pg_json_spec.rb +10 -4
- data/spec/extensions/pg_range_ops_spec.rb +2 -5
- data/spec/extensions/pg_range_spec.rb +6 -2
- data/spec/extensions/pg_row_ops_spec.rb +2 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +26 -5
- data/spec/extensions/rcte_tree_spec.rb +15 -15
- data/spec/extensions/schema_dumper_spec.rb +0 -1
- data/spec/extensions/schema_spec.rb +9 -9
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +18 -29
- data/spec/extensions/set_overrides_spec.rb +4 -0
- data/spec/extensions/{many_to_one_pk_lookup_spec.rb → shared_caching_spec.rb} +1 -4
- data/spec/extensions/single_table_inheritance_spec.rb +4 -4
- data/spec/extensions/spec_helper.rb +8 -9
- data/spec/extensions/sql_expr_spec.rb +2 -0
- data/spec/extensions/string_date_time_spec.rb +2 -0
- data/spec/extensions/string_stripper_spec.rb +2 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +12 -12
- data/spec/extensions/thread_local_timezones_spec.rb +2 -0
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +24 -24
- data/spec/extensions/tree_spec.rb +7 -7
- data/spec/extensions/typecast_on_load_spec.rb +8 -1
- data/spec/extensions/update_primary_key_spec.rb +10 -10
- data/spec/extensions/validation_class_methods_spec.rb +10 -39
- data/spec/extensions/validation_helpers_spec.rb +29 -47
- data/spec/extensions/xml_serializer_spec.rb +1 -23
- data/spec/integration/associations_test.rb +231 -40
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +64 -64
- data/spec/integration/eager_loader_test.rb +28 -28
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +21 -21
- data/spec/integration/prepared_statement_test.rb +7 -7
- data/spec/integration/schema_test.rb +115 -110
- data/spec/integration/spec_helper.rb +17 -27
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +10 -10
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/association_reflection_spec.rb +2 -28
- data/spec/model/associations_spec.rb +239 -188
- data/spec/model/base_spec.rb +27 -68
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +160 -172
- data/spec/model/hooks_spec.rb +62 -79
- data/spec/model/model_spec.rb +36 -51
- data/spec/model/plugins_spec.rb +5 -19
- data/spec/model/record_spec.rb +125 -151
- data/spec/model/spec_helper.rb +8 -6
- data/spec/model/validations_spec.rb +4 -17
- data/spec/spec_config.rb +2 -10
- metadata +50 -56
- data/lib/sequel/deprecated_core_extensions.rb +0 -135
- data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -185
- data/lib/sequel/extensions/pg_statement_cache.rb +0 -318
- data/lib/sequel/plugins/identity_map.rb +0 -260
- data/lib/sequel_core.rb +0 -2
- data/lib/sequel_model.rb +0 -2
- data/spec/extensions/association_autoreloading_spec.rb +0 -102
- data/spec/extensions/identity_map_spec.rb +0 -337
- data/spec/extensions/pg_auto_parameterize_spec.rb +0 -70
- data/spec/extensions/pg_statement_cache_spec.rb +0 -208
- data/spec/rcov.opts +0 -8
- data/spec/spec_config.rb.example +0 -10
|
@@ -11,10 +11,8 @@ module Sequel
|
|
|
11
11
|
# get the reviews for all of those albums:
|
|
12
12
|
#
|
|
13
13
|
# Album.plugin :lazy_attributes, :review
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# a.review
|
|
17
|
-
# end
|
|
14
|
+
# Album.filter{id<100}.all do |a|
|
|
15
|
+
# a.review
|
|
18
16
|
# end
|
|
19
17
|
#
|
|
20
18
|
# # You can specify multiple columns to lazily load:
|
data/lib/sequel/plugins/list.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Sequel
|
|
|
53
53
|
# The <tt>:scope</tt> option can be a symbol, array of symbols, or a proc that
|
|
54
54
|
# accepts a model instance and returns a dataset representing the list.
|
|
55
55
|
# Also, modify the model dataset's order to order by the position and scope fields.
|
|
56
|
-
def self.configure(model, opts =
|
|
56
|
+
def self.configure(model, opts = OPTS)
|
|
57
57
|
model.position_field = opts[:field] || :position
|
|
58
58
|
model.dataset = model.dataset.order_prepend(model.position_field)
|
|
59
59
|
|
|
@@ -157,7 +157,7 @@ module Sequel
|
|
|
157
157
|
# :join_type :: The join type to use for the join, defaults to :left_outer.
|
|
158
158
|
# :only_conditions :: Conditions to use for the join instead of the ones specified by the keys.
|
|
159
159
|
# * opts - The options for the associaion. Takes the same options as many_to_many.
|
|
160
|
-
def many_through_many(name, through, opts=
|
|
160
|
+
def many_through_many(name, through, opts=OPTS, &block)
|
|
161
161
|
associate(:many_through_many, name, opts.merge(through.is_a?(Hash) ? through : {:through=>through}), &block)
|
|
162
162
|
end
|
|
163
163
|
|
|
@@ -198,6 +198,7 @@ module Sequel
|
|
|
198
198
|
ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + opts.predicate_keys.zip(left_pks.map{|k| send(k)}), :table_alias=>ft[:alias], :qualify=>:deep)
|
|
199
199
|
end
|
|
200
200
|
|
|
201
|
+
slice_range = opts.slice_range
|
|
201
202
|
left_key_alias = opts[:left_key_alias] ||= opts.default_associated_key_alias
|
|
202
203
|
opts[:eager_loader] ||= lambda do |eo|
|
|
203
204
|
h = eo[:id_map]
|
|
@@ -208,17 +209,10 @@ module Sequel
|
|
|
208
209
|
ft = opts.final_reverse_edge
|
|
209
210
|
ds = ds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + [[opts.predicate_key, h.keys]], :table_alias=>ft[:alias], :qualify=>:deep)
|
|
210
211
|
ds = model.eager_loading_dataset(opts, ds, nil, eo[:associations], eo)
|
|
211
|
-
|
|
212
|
-
when :window_function
|
|
212
|
+
if opts.eager_limit_strategy == :window_function
|
|
213
213
|
delete_rn = true
|
|
214
214
|
rn = ds.row_number_column
|
|
215
215
|
ds = apply_window_function_eager_limit_strategy(ds, opts)
|
|
216
|
-
when :correlated_subquery
|
|
217
|
-
ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
|
|
218
|
-
dsa = ds.send(:dataset_alias, 2)
|
|
219
|
-
opts.reverse_edges.each{|t| xds = xds.join(t[:table], Array(t[:left]).zip(Array(t[:right])), :table_alias=>t[:alias])}
|
|
220
|
-
xds.join(ft[:table], Array(ft[:left]).zip(Array(ft[:right])) + left_keys.map{|k| [k, SQL::QualifiedIdentifier.new(ft[:table], k)]}, :table_alias=>dsa, :qualify=>:deep)
|
|
221
|
-
end
|
|
222
216
|
end
|
|
223
217
|
ds.all do |assoc_record|
|
|
224
218
|
assoc_record.values.delete(rn) if delete_rn
|
|
@@ -231,8 +225,7 @@ module Sequel
|
|
|
231
225
|
objects.each{|object| object.associations[name].push(assoc_record)}
|
|
232
226
|
end
|
|
233
227
|
if opts.eager_limit_strategy == :ruby
|
|
234
|
-
|
|
235
|
-
rows.each{|o| o.associations[name] = o.associations[name].slice(offset||0, limit) || []}
|
|
228
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
|
|
236
229
|
end
|
|
237
230
|
end
|
|
238
231
|
|
|
@@ -1,55 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
#
|
|
4
|
-
# for many_to_one associations to use a simple primary key lookup on the associated
|
|
5
|
-
# class, which is generally faster as it uses mostly static SQL. Additional, if the
|
|
6
|
-
# associated class is caching primary key lookups, you get the benefit of a cached
|
|
7
|
-
# lookup.
|
|
8
|
-
#
|
|
9
|
-
# This plugin attempts to determine cases where the primary key lookup would have
|
|
10
|
-
# different results than the regular lookup, and use the regular lookup in that case.
|
|
11
|
-
# If you want to explicitly force whether or not to use primary key lookups for
|
|
12
|
-
# a given association, set the :many_to_one_pk_lookup association option.
|
|
13
|
-
#
|
|
14
|
-
# Usage:
|
|
15
|
-
#
|
|
16
|
-
# # Make all model subclass instances use primary key lookups for many_to_one
|
|
17
|
-
# # association loading
|
|
18
|
-
# Sequel::Model.plugin :many_to_one_pk_lookup
|
|
19
|
-
#
|
|
20
|
-
# # Do so for just the album class.
|
|
21
|
-
# Album.plugin :many_to_one_pk_lookup
|
|
3
|
+
# Empty plugin module for backwards compatibility
|
|
22
4
|
module ManyToOnePkLookup
|
|
23
|
-
module ClassMethods
|
|
24
|
-
# Disable primary key lookup in cases where it will result in a different
|
|
25
|
-
# query than the association query.
|
|
26
|
-
def def_many_to_one(opts)
|
|
27
|
-
if !opts.has_key?(:many_to_one_pk_lookup) &&
|
|
28
|
-
(opts[:dataset] || opts[:conditions] || opts[:block] || opts[:select] ||
|
|
29
|
-
(opts.has_key?(:key) && opts[:key] == nil))
|
|
30
|
-
opts[:many_to_one_pk_lookup] = false
|
|
31
|
-
end
|
|
32
|
-
super
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
module InstanceMethods
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
# If the current association is a simple many_to_one association, use
|
|
40
|
-
# a simple primary key lookup on the associated model, which can benefit from
|
|
41
|
-
# caching if the associated model is using caching.
|
|
42
|
-
def _load_associated_objects(opts, dynamic_opts={})
|
|
43
|
-
return super unless opts.can_have_associated_objects?(self) && opts[:type] == :many_to_one
|
|
44
|
-
klass = opts.associated_class
|
|
45
|
-
if !dynamic_opts[:callback] &&
|
|
46
|
-
opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == klass.primary_key}
|
|
47
|
-
klass.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| send(c)} : send(fk)))
|
|
48
|
-
else
|
|
49
|
-
super
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
5
|
end
|
|
54
6
|
end
|
|
55
7
|
end
|
|
@@ -196,7 +196,7 @@ module Sequel
|
|
|
196
196
|
# :destroy option is given, destroy the object after disassociating it
|
|
197
197
|
# (unless destroying the object would automatically disassociate it).
|
|
198
198
|
# Returns the object removed.
|
|
199
|
-
def nested_attributes_remove(reflection, obj, opts=
|
|
199
|
+
def nested_attributes_remove(reflection, obj, opts=OPTS)
|
|
200
200
|
if !opts[:destroy] || reflection.remove_before_destroy?
|
|
201
201
|
before_save_hook do
|
|
202
202
|
if reflection.returns_array?
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
module Sequel
|
|
2
|
-
require 'plugins/instance_filters'
|
|
3
|
-
|
|
4
2
|
module Plugins
|
|
5
3
|
# This plugin implements a simple database-independent locking mechanism
|
|
6
4
|
# to ensure that concurrent updates do not override changes. This is
|
|
@@ -21,16 +19,16 @@ module Sequel
|
|
|
21
19
|
# This plugin relies on the instance_filters plugin.
|
|
22
20
|
module OptimisticLocking
|
|
23
21
|
# Exception class raised when trying to update or destroy a stale object.
|
|
24
|
-
Error =
|
|
22
|
+
Error = Sequel::NoExistingObject
|
|
25
23
|
|
|
26
24
|
# Load the instance_filters plugin into the model.
|
|
27
|
-
def self.apply(model, opts=
|
|
25
|
+
def self.apply(model, opts=OPTS)
|
|
28
26
|
model.plugin :instance_filters
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
# Set the lock_column to the :lock_column option, or :lock_version if
|
|
32
30
|
# that option is not given.
|
|
33
|
-
def self.configure(model, opts=
|
|
31
|
+
def self.configure(model, opts=OPTS)
|
|
34
32
|
model.lock_column = opts[:lock_column] || :lock_version
|
|
35
33
|
end
|
|
36
34
|
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
extension :pg_array, :pg_array_ops
|
|
3
|
+
|
|
4
|
+
module Plugins
|
|
5
|
+
# This plugin allows you to create associations where the foreign keys
|
|
6
|
+
# are stored in a PostgreSQL array column in one of the tables. The
|
|
7
|
+
# model with the table containing the array column has a
|
|
8
|
+
# pg_array_to_many association to the associated model, and the
|
|
9
|
+
# model with the table containing the primary key referenced by
|
|
10
|
+
# elements in the array column has a many_to_pg_array association
|
|
11
|
+
# to the associated model.
|
|
12
|
+
#
|
|
13
|
+
# # Database schema:
|
|
14
|
+
# # tags albums
|
|
15
|
+
# # :id (int4) <--\ :id
|
|
16
|
+
# # :name \-- :tag_ids (int4[])
|
|
17
|
+
# # :name
|
|
18
|
+
#
|
|
19
|
+
# class Album
|
|
20
|
+
# plugin :pg_array_associations
|
|
21
|
+
# pg_array_to_many :tags
|
|
22
|
+
# end
|
|
23
|
+
# class Tag
|
|
24
|
+
# plugin :pg_array_associations
|
|
25
|
+
# many_to_pg_array :albums
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# These association types work similarly to Sequel's other association
|
|
29
|
+
# types, so you can use them as you would any other association. Unlike
|
|
30
|
+
# other associations, they do not support composite keys.
|
|
31
|
+
#
|
|
32
|
+
# One thing that is different is that the modification methods for
|
|
33
|
+
# pg_array_to_many associations do not affect the database, since they
|
|
34
|
+
# operate purely on the receiver. For example:
|
|
35
|
+
#
|
|
36
|
+
# album = Album[1]
|
|
37
|
+
# album.add_tag(Tag[2])
|
|
38
|
+
#
|
|
39
|
+
# does not save the album. This allows you to call add_tag repeatedly
|
|
40
|
+
# and the save after to combine all changes into a single query. Note
|
|
41
|
+
# that the many_to_pg_array association modification methods do save, so:
|
|
42
|
+
#
|
|
43
|
+
# tag = Tag[2]
|
|
44
|
+
# tag.add_album(Album[1])
|
|
45
|
+
#
|
|
46
|
+
# will save the changes to the album.
|
|
47
|
+
#
|
|
48
|
+
# They support some additional options specific to this plugin:
|
|
49
|
+
#
|
|
50
|
+
# :array_type :: This allows you to specify the type of the array. This
|
|
51
|
+
# is only necessary to set in very narrow circumstances,
|
|
52
|
+
# such as when this plugin needs to create an array type,
|
|
53
|
+
# and typecasting is turned off or not setup correctly
|
|
54
|
+
# for the model object.
|
|
55
|
+
# :save_after_modify :: For pg_array_to_many associations, this makes the
|
|
56
|
+
# the modification methods save the current object,
|
|
57
|
+
# so they operate more similarly to the one_to_many
|
|
58
|
+
# and many_to_many association modification methods.
|
|
59
|
+
# :uniq :: Similar to many_to_many associations, this can be used to
|
|
60
|
+
# make sure the returned associated object array has uniq values.
|
|
61
|
+
#
|
|
62
|
+
# Note that until PostgreSQL gains the ability to enforce foreign key
|
|
63
|
+
# constraints in array columns, this plugin is not recommended for
|
|
64
|
+
# production use unless you plan on emulating referential integrity
|
|
65
|
+
# constraints via triggers.
|
|
66
|
+
#
|
|
67
|
+
# This plugin should work on all supported PostgreSQL versions, except
|
|
68
|
+
# the remove_all modification method for many_to_pg_array associations, which
|
|
69
|
+
# requires the array_remove method added in PostgreSQL 9.3.
|
|
70
|
+
module PgArrayAssociations
|
|
71
|
+
# The AssociationReflection subclass for many_to_pg_array associations.
|
|
72
|
+
class ManyToPgArrayAssociationReflection < Sequel::Model::Associations::AssociationReflection
|
|
73
|
+
Sequel::Model::Associations::ASSOCIATION_TYPES[:many_to_pg_array] = self
|
|
74
|
+
|
|
75
|
+
# The array column in the associated model containing foreign keys to
|
|
76
|
+
# the current model.
|
|
77
|
+
def associated_object_keys
|
|
78
|
+
[self[:key]]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# many_to_pg_array associations can have associated objects as long as they have
|
|
82
|
+
# a primary key.
|
|
83
|
+
def can_have_associated_objects?(obj)
|
|
84
|
+
obj.send(self[:primary_key])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Assume that the key in the associated table uses a version of the current
|
|
88
|
+
# model's name suffixed with _ids.
|
|
89
|
+
def default_key
|
|
90
|
+
:"#{underscore(demodulize(self[:model].name))}_ids"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3))
|
|
94
|
+
def predicate_key
|
|
95
|
+
cached_fetch(:predicate_key){qualify_assoc(self[:key_column])}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# The column in the current table that the keys in the array column in the
|
|
99
|
+
# associated table reference.
|
|
100
|
+
def primary_key
|
|
101
|
+
self[:primary_key]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Destroying the associated object automatically removes the association,
|
|
105
|
+
# since the association is stored in the associated object.
|
|
106
|
+
def remove_before_destroy?
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# Only consider an association as a reciprocal if it has matching keys
|
|
113
|
+
# and primary keys.
|
|
114
|
+
def reciprocal_association?(assoc_reflect)
|
|
115
|
+
super && self[:key] == assoc_reflect[:key] && primary_key == assoc_reflect.primary_key
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def reciprocal_type
|
|
119
|
+
:pg_array_to_many
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The AssociationReflection subclass for pg_array_to_many associations.
|
|
124
|
+
class PgArrayToManyAssociationReflection < Sequel::Model::Associations::AssociationReflection
|
|
125
|
+
Sequel::Model::Associations::ASSOCIATION_TYPES[:pg_array_to_many] = self
|
|
126
|
+
|
|
127
|
+
# An array containing the primary key for the associated model.
|
|
128
|
+
def associated_object_keys
|
|
129
|
+
Array(primary_key)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# pg_array_to_many associations can only have associated objects if
|
|
133
|
+
# the array field is not nil or empty.
|
|
134
|
+
def can_have_associated_objects?(obj)
|
|
135
|
+
v = obj.send(self[:key])
|
|
136
|
+
v && !v.empty?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# pg_array_to_many associations do not need a primary key.
|
|
140
|
+
def dataset_need_primary_key?
|
|
141
|
+
false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Use a default key name of *_ids, for similarity to other association types
|
|
145
|
+
# that use *_id for single keys.
|
|
146
|
+
def default_key
|
|
147
|
+
:"#{singularize(self[:name])}_ids"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# A qualified version of the associated primary key.
|
|
151
|
+
def predicate_key
|
|
152
|
+
cached_fetch(:predicate_key){qualify_assoc(primary_key)}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The primary key of the associated model.
|
|
156
|
+
def primary_key
|
|
157
|
+
cached_fetch(:primary_key){associated_class.primary_key}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The method to call to get value of the primary key of the associated model.
|
|
161
|
+
def primary_key_method
|
|
162
|
+
cached_fetch(:primary_key_method){primary_key}
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
# Only consider an association as a reciprocal if it has matching keys
|
|
168
|
+
# and primary keys.
|
|
169
|
+
def reciprocal_association?(assoc_reflect)
|
|
170
|
+
super && self[:key] == assoc_reflect[:key] && primary_key == assoc_reflect.primary_key
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def reciprocal_type
|
|
174
|
+
:many_to_pg_array
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
module ClassMethods
|
|
179
|
+
# Create a many_to_pg_array association, for the case where the associated
|
|
180
|
+
# table contains the array with foreign keys pointing to the current table.
|
|
181
|
+
# See associate for options.
|
|
182
|
+
def many_to_pg_array(name, opts=OPTS, &block)
|
|
183
|
+
associate(:many_to_pg_array, name, opts, &block)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Create a pg_array_to_many association, for the case where the current
|
|
187
|
+
# table contains the array with foreign keys pointing to the associated table.
|
|
188
|
+
# See associate for options.
|
|
189
|
+
def pg_array_to_many(name, opts=OPTS, &block)
|
|
190
|
+
associate(:pg_array_to_many, name, opts, &block)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
# Setup the many_to_pg_array-specific datasets, eager loaders, and modification methods.
|
|
196
|
+
def def_many_to_pg_array(opts)
|
|
197
|
+
name = opts[:name]
|
|
198
|
+
model = self
|
|
199
|
+
pk = opts[:eager_loader_key] = opts[:primary_key] ||= model.primary_key
|
|
200
|
+
opts[:key] = opts.default_key unless opts.has_key?(:key)
|
|
201
|
+
key = opts[:key]
|
|
202
|
+
key_column = opts[:key_column] ||= opts[:key]
|
|
203
|
+
opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
|
|
204
|
+
slice_range = opts.slice_range
|
|
205
|
+
opts[:dataset] ||= lambda do
|
|
206
|
+
opts.associated_dataset.where(Sequel.pg_array_op(opts.predicate_key).contains([send(pk)]))
|
|
207
|
+
end
|
|
208
|
+
opts[:eager_loader] ||= proc do |eo|
|
|
209
|
+
id_map = eo[:id_map]
|
|
210
|
+
rows = eo[:rows]
|
|
211
|
+
rows.each do |object|
|
|
212
|
+
object.associations[name] = []
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
klass = opts.associated_class
|
|
216
|
+
ds = model.eager_loading_dataset(opts, klass.where(Sequel.pg_array_op(opts.predicate_key).overlaps(id_map.keys)), nil, eo[:associations], eo)
|
|
217
|
+
ds.all do |assoc_record|
|
|
218
|
+
if pks ||= assoc_record.send(key)
|
|
219
|
+
pks.each do |pkv|
|
|
220
|
+
next unless objects = id_map[pkv]
|
|
221
|
+
objects.each do |object|
|
|
222
|
+
object.associations[name].push(assoc_record)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
if slice_range
|
|
228
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
join_type = opts[:graph_join_type]
|
|
233
|
+
select = opts[:graph_select]
|
|
234
|
+
opts[:cartesian_product_number] ||= 1
|
|
235
|
+
|
|
236
|
+
if opts.include?(:graph_only_conditions)
|
|
237
|
+
conditions = opts[:graph_only_conditions]
|
|
238
|
+
graph_block = opts[:graph_block]
|
|
239
|
+
else
|
|
240
|
+
conditions = opts[:graph_conditions]
|
|
241
|
+
conditions = nil if conditions.empty?
|
|
242
|
+
graph_block = proc do |j, lj, js|
|
|
243
|
+
Sequel.pg_array_op(Sequel.deep_qualify(j, key_column)).contains([Sequel.deep_qualify(lj, opts.primary_key)])
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
if orig_graph_block = opts[:graph_block]
|
|
247
|
+
pg_array_graph_block = graph_block
|
|
248
|
+
graph_block = proc do |j, lj, js|
|
|
249
|
+
Sequel.&(orig_graph_block.call(j,lj,js), pg_array_graph_block.call(j, lj, js))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
opts[:eager_grapher] ||= proc do |eo|
|
|
255
|
+
ds = eo[:self]
|
|
256
|
+
ds = ds.graph(eager_graph_dataset(opts, eo), conditions, eo.merge(:select=>select, :join_type=>join_type, :qualify=>:deep, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
|
|
257
|
+
ds
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def_association_dataset_methods(opts)
|
|
261
|
+
|
|
262
|
+
unless opts[:read_only]
|
|
263
|
+
validate = opts[:validate]
|
|
264
|
+
|
|
265
|
+
array_type = opts[:array_type] ||= :integer
|
|
266
|
+
adder = opts[:adder] || proc do |o|
|
|
267
|
+
if array = o.send(key)
|
|
268
|
+
array << send(pk)
|
|
269
|
+
else
|
|
270
|
+
o.send("#{key}=", Sequel.pg_array([send(pk)], array_type))
|
|
271
|
+
end
|
|
272
|
+
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
|
273
|
+
end
|
|
274
|
+
association_module_private_def(opts._add_method, opts, &adder)
|
|
275
|
+
|
|
276
|
+
remover = opts[:remover] || proc do |o|
|
|
277
|
+
if (array = o.send(key)) && !array.empty?
|
|
278
|
+
array.delete(send(pk))
|
|
279
|
+
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
association_module_private_def(opts._remove_method, opts, &remover)
|
|
283
|
+
|
|
284
|
+
clearer = opts[:clearer] || proc do
|
|
285
|
+
opts.associated_dataset.where(Sequel.pg_array_op(key).contains([send(pk)])).update(key=>Sequel.function(:array_remove, key, send(pk)))
|
|
286
|
+
end
|
|
287
|
+
association_module_private_def(opts._remove_all_method, opts, &clearer)
|
|
288
|
+
|
|
289
|
+
def_add_method(opts)
|
|
290
|
+
def_remove_methods(opts)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Setup the pg_array_to_many-specific datasets, eager loaders, and modification methods.
|
|
295
|
+
def def_pg_array_to_many(opts)
|
|
296
|
+
name = opts[:name]
|
|
297
|
+
model = self
|
|
298
|
+
opts[:key] = opts.default_key unless opts.has_key?(:key)
|
|
299
|
+
key = opts[:key]
|
|
300
|
+
key_column = opts[:key_column] ||= key
|
|
301
|
+
opts[:eager_loader_key] = nil
|
|
302
|
+
opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
|
|
303
|
+
slice_range = opts.slice_range
|
|
304
|
+
opts[:dataset] ||= lambda do
|
|
305
|
+
opts.associated_dataset.where(opts.predicate_key=>send(key).to_a)
|
|
306
|
+
end
|
|
307
|
+
opts[:eager_loader] ||= proc do |eo|
|
|
308
|
+
rows = eo[:rows]
|
|
309
|
+
id_map = {}
|
|
310
|
+
pkm = opts.primary_key_method
|
|
311
|
+
rows.each do |object|
|
|
312
|
+
object.associations[name] = []
|
|
313
|
+
if associated_pks = object.send(key)
|
|
314
|
+
associated_pks.each do |apk|
|
|
315
|
+
(id_map[apk] ||= []) << object
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
klass = opts.associated_class
|
|
321
|
+
ds = model.eager_loading_dataset(opts, klass.where(opts.predicate_key=>id_map.keys), nil, eo[:associations], eo)
|
|
322
|
+
ds.all do |assoc_record|
|
|
323
|
+
if objects = id_map[assoc_record.send(pkm)]
|
|
324
|
+
objects.each do |object|
|
|
325
|
+
object.associations[name].push(assoc_record)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
if slice_range
|
|
330
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
join_type = opts[:graph_join_type]
|
|
335
|
+
select = opts[:graph_select]
|
|
336
|
+
opts[:cartesian_product_number] ||= 1
|
|
337
|
+
|
|
338
|
+
if opts.include?(:graph_only_conditions)
|
|
339
|
+
conditions = opts[:graph_only_conditions]
|
|
340
|
+
graph_block = opts[:graph_block]
|
|
341
|
+
else
|
|
342
|
+
conditions = opts[:graph_conditions]
|
|
343
|
+
conditions = nil if conditions.empty?
|
|
344
|
+
graph_block = proc do |j, lj, js|
|
|
345
|
+
Sequel.pg_array_op(Sequel.deep_qualify(lj, key_column)).contains([Sequel.deep_qualify(j, opts.primary_key)])
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
if orig_graph_block = opts[:graph_block]
|
|
349
|
+
pg_array_graph_block = graph_block
|
|
350
|
+
graph_block = proc do |j, lj, js|
|
|
351
|
+
Sequel.&(orig_graph_block.call(j,lj,js), pg_array_graph_block.call(j, lj, js))
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
opts[:eager_grapher] ||= proc do |eo|
|
|
357
|
+
ds = eo[:self]
|
|
358
|
+
ds = ds.graph(eager_graph_dataset(opts, eo), conditions, eo.merge(:select=>select, :join_type=>join_type, :qualify=>:deep, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
|
|
359
|
+
ds
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def_association_dataset_methods(opts)
|
|
363
|
+
|
|
364
|
+
unless opts[:read_only]
|
|
365
|
+
validate = opts[:validate]
|
|
366
|
+
array_type = opts[:array_type] ||= :integer
|
|
367
|
+
if opts[:save_after_modify]
|
|
368
|
+
save_after_modify = proc do |obj|
|
|
369
|
+
obj.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
adder = opts[:adder] || proc do |o|
|
|
374
|
+
opk = o.send(opts.primary_key)
|
|
375
|
+
if array = send(key)
|
|
376
|
+
modified!(key)
|
|
377
|
+
array << opk
|
|
378
|
+
else
|
|
379
|
+
send("#{key}=", Sequel.pg_array([opk], array_type))
|
|
380
|
+
end
|
|
381
|
+
save_after_modify.call(self) if save_after_modify
|
|
382
|
+
end
|
|
383
|
+
association_module_private_def(opts._add_method, opts, &adder)
|
|
384
|
+
|
|
385
|
+
remover = opts[:remover] || proc do |o|
|
|
386
|
+
if (array = send(key)) && !array.empty?
|
|
387
|
+
modified!(key)
|
|
388
|
+
array.delete(o.send(opts.primary_key))
|
|
389
|
+
save_after_modify.call(self) if save_after_modify
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
association_module_private_def(opts._remove_method, opts, &remover)
|
|
393
|
+
|
|
394
|
+
clearer = opts[:clearer] || proc do
|
|
395
|
+
if (array = send(key)) && !array.empty?
|
|
396
|
+
modified!(key)
|
|
397
|
+
array.clear
|
|
398
|
+
save_after_modify.call(self) if save_after_modify
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
association_module_private_def(opts._remove_all_method, opts, &clearer)
|
|
402
|
+
|
|
403
|
+
def_add_method(opts)
|
|
404
|
+
def_remove_methods(opts)
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
module DatasetMethods
|
|
410
|
+
private
|
|
411
|
+
|
|
412
|
+
# Support filtering by many_to_pg_array associations using a subquery.
|
|
413
|
+
def many_to_pg_array_association_filter_expression(op, ref, obj)
|
|
414
|
+
pk = ref.qualify(model.table_name, ref.primary_key)
|
|
415
|
+
key = ref[:key]
|
|
416
|
+
expr = case obj
|
|
417
|
+
when Sequel::Model
|
|
418
|
+
if (assoc_pks = obj.send(key)) && !assoc_pks.empty?
|
|
419
|
+
Sequel.expr(pk=>assoc_pks.to_a)
|
|
420
|
+
end
|
|
421
|
+
when Array
|
|
422
|
+
if (assoc_pks = obj.map{|o| o.send(key)}.flatten.compact.uniq) && !assoc_pks.empty?
|
|
423
|
+
Sequel.expr(pk=>assoc_pks)
|
|
424
|
+
end
|
|
425
|
+
when Sequel::Dataset
|
|
426
|
+
Sequel.expr(pk=>obj.select{Sequel.pg_array_op(ref.qualify(obj.model.table_name, ref[:key_column])).unnest})
|
|
427
|
+
end
|
|
428
|
+
expr = Sequel::SQL::Constants::FALSE unless expr
|
|
429
|
+
association_filter_handle_inversion(op, expr, [pk])
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Support filtering by pg_array_to_many associations using a subquery.
|
|
433
|
+
def pg_array_to_many_association_filter_expression(op, ref, obj)
|
|
434
|
+
key = ref.qualify(model.table_name, ref[:key_column])
|
|
435
|
+
expr = case obj
|
|
436
|
+
when Sequel::Model
|
|
437
|
+
if pkv = obj.send(ref.primary_key_method)
|
|
438
|
+
Sequel.pg_array_op(key).contains([pkv])
|
|
439
|
+
end
|
|
440
|
+
when Array
|
|
441
|
+
if (pkvs = obj.map{|o| o.send(ref.primary_key_method)}.compact) && !pkvs.empty?
|
|
442
|
+
Sequel.pg_array(key).overlaps(pkvs)
|
|
443
|
+
end
|
|
444
|
+
when Sequel::Dataset
|
|
445
|
+
Sequel.function(:coalesce, Sequel.pg_array_op(key).overlaps(obj.select{array_agg(ref.qualify(obj.model.table_name, ref.primary_key))}), Sequel::SQL::Constants::FALSE)
|
|
446
|
+
end
|
|
447
|
+
expr = Sequel::SQL::Constants::FALSE unless expr
|
|
448
|
+
association_filter_handle_inversion(op, expr, [key])
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|