sequel 5.43.0 → 5.47.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 +40 -0
- data/README.rdoc +1 -2
- data/doc/association_basics.rdoc +70 -11
- data/doc/migration.rdoc +11 -5
- 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/sql.rdoc +12 -0
- data/doc/testing.rdoc +5 -0
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/odbc.rb +5 -1
- data/lib/sequel/adapters/shared/mysql.rb +17 -0
- data/lib/sequel/adapters/shared/postgres.rb +0 -12
- data/lib/sequel/adapters/shared/sqlite.rb +55 -9
- data/lib/sequel/core.rb +11 -0
- data/lib/sequel/database/schema_generator.rb +25 -46
- data/lib/sequel/database/schema_methods.rb +1 -1
- data/lib/sequel/dataset/query.rb +2 -4
- data/lib/sequel/dataset/sql.rb +7 -0
- data/lib/sequel/extensions/date_arithmetic.rb +29 -15
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +11 -0
- data/lib/sequel/model/associations.rb +275 -89
- data/lib/sequel/plugins/async_thread_pool.rb +1 -1
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/column_encryption.rb +20 -3
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/pg_array_associations.rb +52 -38
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +520 -0
- data/lib/sequel/version.rb +1 -1
- metadata +14 -3
@@ -12,7 +12,9 @@
|
|
12
12
|
#
|
13
13
|
# How accurate this count is depends on the number of rows
|
14
14
|
# added/deleted from the table since the last time it was
|
15
|
-
# analyzed.
|
15
|
+
# analyzed. If the table has not been vacuumed or analyzed
|
16
|
+
# yet, this can return 0 or -1 depending on the PostgreSQL
|
17
|
+
# version in use.
|
16
18
|
#
|
17
19
|
# To load the extension into the database:
|
18
20
|
#
|
@@ -6,6 +6,17 @@
|
|
6
6
|
# the current database). The main interface is through
|
7
7
|
# Sequel::Database#dump_schema_migration.
|
8
8
|
#
|
9
|
+
# The schema_dumper extension is quite limited in what types of
|
10
|
+
# database objects it supports. In general, it only supports
|
11
|
+
# dumping tables, columns, primary key and foreign key constraints,
|
12
|
+
# and some indexes. It does not support most table options, CHECK
|
13
|
+
# constraints, partial indexes, database functions, triggers,
|
14
|
+
# security grants/revokes, and a wide variety of other useful
|
15
|
+
# database properties. Be aware of the limitations when using the
|
16
|
+
# schema_dumper extension. If you are dumping the schema to restore
|
17
|
+
# to the same database type, it is recommended to use your database's
|
18
|
+
# dump and restore programs instead of the schema_dumper extension.
|
19
|
+
#
|
9
20
|
# To load the extension:
|
10
21
|
#
|
11
22
|
# DB.extension :schema_dumper
|
@@ -263,7 +263,9 @@ module Sequel
|
|
263
263
|
# yielding each row to the block.
|
264
264
|
def eager_load_results(eo, &block)
|
265
265
|
rows = eo[:rows]
|
266
|
-
|
266
|
+
unless eo[:initialize_rows] == false
|
267
|
+
Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
|
268
|
+
end
|
267
269
|
if eo[:id_map]
|
268
270
|
ids = eo[:id_map].keys
|
269
271
|
return ids if ids.empty?
|
@@ -272,7 +274,9 @@ module Sequel
|
|
272
274
|
cascade = eo[:associations]
|
273
275
|
eager_limit = nil
|
274
276
|
|
275
|
-
if eo[:
|
277
|
+
if eo[:no_results]
|
278
|
+
no_results = true
|
279
|
+
elsif eo[:eager_block] || eo[:loader] == false
|
276
280
|
ds = eager_loading_dataset(eo)
|
277
281
|
|
278
282
|
strategy = ds.opts[:eager_limit_strategy] || strategy
|
@@ -311,7 +315,8 @@ module Sequel
|
|
311
315
|
objects = loader.all(ids)
|
312
316
|
end
|
313
317
|
|
314
|
-
objects.each(&block)
|
318
|
+
Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
|
319
|
+
|
315
320
|
if strategy == :ruby
|
316
321
|
apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
|
317
322
|
end
|
@@ -635,9 +640,7 @@ module Sequel
|
|
635
640
|
# given the hash passed to the eager loader.
|
636
641
|
def eager_loading_dataset(eo=OPTS)
|
637
642
|
ds = eo[:dataset] || associated_eager_dataset
|
638
|
-
|
639
|
-
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
640
|
-
end
|
643
|
+
ds = eager_loading_set_predicate_condition(ds, eo)
|
641
644
|
if associations = eo[:associations]
|
642
645
|
ds = ds.eager(associations)
|
643
646
|
end
|
@@ -664,6 +667,15 @@ module Sequel
|
|
664
667
|
self[:model].default_eager_limit_strategy || :ruby
|
665
668
|
end
|
666
669
|
|
670
|
+
# Set the predicate condition for the eager loading dataset based on the id map
|
671
|
+
# in the eager loading options.
|
672
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
673
|
+
if id_map = eo[:id_map]
|
674
|
+
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
675
|
+
end
|
676
|
+
ds
|
677
|
+
end
|
678
|
+
|
667
679
|
# The predicate condition to use for the eager_loader.
|
668
680
|
def eager_loading_predicate_condition(keys)
|
669
681
|
{predicate_key=>keys}
|
@@ -1315,7 +1327,7 @@ module Sequel
|
|
1315
1327
|
|
1316
1328
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
1317
1329
|
def eager_loading_use_associated_key?
|
1318
|
-
|
1330
|
+
!separate_query_per_table?
|
1319
1331
|
end
|
1320
1332
|
|
1321
1333
|
# The source of the join table. This is the join table itself, unless it
|
@@ -1372,10 +1384,30 @@ module Sequel
|
|
1372
1384
|
cached_fetch(:select){default_select}
|
1373
1385
|
end
|
1374
1386
|
|
1387
|
+
# Whether a separate query should be used for the join table.
|
1388
|
+
def separate_query_per_table?
|
1389
|
+
self[:join_table_db]
|
1390
|
+
end
|
1391
|
+
|
1375
1392
|
private
|
1376
1393
|
|
1394
|
+
# Join to the the join table, unless using a separate query per table.
|
1377
1395
|
def _associated_dataset
|
1378
|
-
|
1396
|
+
if separate_query_per_table?
|
1397
|
+
super
|
1398
|
+
else
|
1399
|
+
super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
|
1400
|
+
end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
# Use the right_keys from the eager loading options if
|
1404
|
+
# using a separate query per table.
|
1405
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
1406
|
+
if separate_query_per_table?
|
1407
|
+
ds.where(right_primary_key=>eo[:right_keys])
|
1408
|
+
else
|
1409
|
+
super
|
1410
|
+
end
|
1379
1411
|
end
|
1380
1412
|
|
1381
1413
|
# The default selection for associations that require joins. These do not use the default
|
@@ -1592,6 +1624,7 @@ module Sequel
|
|
1592
1624
|
# === Multiple Types
|
1593
1625
|
# :adder :: Proc used to define the private _add_* method for doing the database work
|
1594
1626
|
# to associate the given object to the current object (*_to_many assocations).
|
1627
|
+
# Set to nil to not define a add_* method for the association.
|
1595
1628
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1596
1629
|
# after a new item is added to the association.
|
1597
1630
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1602,6 +1635,8 @@ module Sequel
|
|
1602
1635
|
# after an item is set using the association setter method.
|
1603
1636
|
# :allow_eager :: If set to false, you cannot load the association eagerly
|
1604
1637
|
# via eager or eager_graph
|
1638
|
+
# :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
|
1639
|
+
# :allow_filtering_by :: If set to false, you cannot use the association when filtering
|
1605
1640
|
# :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1606
1641
|
# before a new item is added to the association.
|
1607
1642
|
# :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1620,6 +1655,7 @@ module Sequel
|
|
1620
1655
|
# the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
|
1621
1656
|
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
1622
1657
|
# to remove all objects associated to the current object (*_to_many assocations).
|
1658
|
+
# Set to nil to not define a remove_all_* method for the association.
|
1623
1659
|
# :clone :: Merge the current options and block into the options and block used in defining
|
1624
1660
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
1625
1661
|
# all share the same options such as :class and :key, while changing the order and block used.
|
@@ -1674,7 +1710,7 @@ module Sequel
|
|
1674
1710
|
# :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
|
1675
1711
|
# the association via +eager_graph+, instead of the default conditions specified by the
|
1676
1712
|
# foreign/primary keys. This option causes the :graph_conditions option to be ignored.
|
1677
|
-
# :graph_order ::
|
1713
|
+
# :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
|
1678
1714
|
# in the case where :order contains an identifier qualified by the table's name, which may not match
|
1679
1715
|
# the alias used when eager graphing. By setting this to the unqualified identifier, it will be
|
1680
1716
|
# automatically qualified when using eager_graph.
|
@@ -1686,6 +1722,10 @@ module Sequel
|
|
1686
1722
|
# limit (first element) and an offset (second element).
|
1687
1723
|
# :methods_module :: The module that methods the association creates will be placed into. Defaults
|
1688
1724
|
# to the module containing the model's columns.
|
1725
|
+
# :no_association_method :: Do not add a method for the association. This can save memory if the association
|
1726
|
+
# method is never used.
|
1727
|
+
# :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
|
1728
|
+
# method is never used.
|
1689
1729
|
# :order :: the column(s) by which to order the association dataset. Can be a
|
1690
1730
|
# singular column symbol or an array of column symbols.
|
1691
1731
|
# :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
|
@@ -1698,6 +1738,7 @@ module Sequel
|
|
1698
1738
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
1699
1739
|
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
1700
1740
|
# to remove the association between the given object and the current object (*_to_many assocations).
|
1741
|
+
# Set to nil to not define a remove_* method for the association.
|
1701
1742
|
# :select :: the columns to select. Defaults to the associated class's table_name.* in an association
|
1702
1743
|
# that uses joins, which means it doesn't include the attributes from the
|
1703
1744
|
# join table. If you want to include the join table attributes, you can
|
@@ -1706,6 +1747,7 @@ module Sequel
|
|
1706
1747
|
# the same name in both the join table and the associated table.
|
1707
1748
|
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
1708
1749
|
# between the given object and the current object (*_to_one associations).
|
1750
|
+
# Set to nil to not define a setter method for the association.
|
1709
1751
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1710
1752
|
# loading limited associations using the default :union strategy.
|
1711
1753
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
@@ -1762,6 +1804,9 @@ module Sequel
|
|
1762
1804
|
# underscored, sorted, and joined with '_'.
|
1763
1805
|
# :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
|
1764
1806
|
# methods. Should accept a dataset argument and return a modified dataset if present.
|
1807
|
+
# :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
|
1808
|
+
# a join between to the join table and the associated table, use a separate query for the
|
1809
|
+
# join table using the given Database object.
|
1765
1810
|
# :left_key :: foreign key in join table that points to current model's
|
1766
1811
|
# primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
|
1767
1812
|
# Can use an array of symbols for a composite key association.
|
@@ -1838,8 +1883,7 @@ module Sequel
|
|
1838
1883
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1839
1884
|
opts.delete(:class) unless opts[:class]
|
1840
1885
|
|
1841
|
-
|
1842
|
-
def_association_instance_methods(opts)
|
1886
|
+
def_association(opts)
|
1843
1887
|
|
1844
1888
|
orig_opts.delete(:clone)
|
1845
1889
|
opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
|
@@ -1953,6 +1997,13 @@ module Sequel
|
|
1953
1997
|
association_module(opts).send(:private, name)
|
1954
1998
|
end
|
1955
1999
|
|
2000
|
+
# Delegate to the type-specific association method to setup the
|
2001
|
+
# association, and define the association instance methods.
|
2002
|
+
def def_association(opts)
|
2003
|
+
send(:"def_#{opts[:type]}", opts)
|
2004
|
+
def_association_instance_methods(opts)
|
2005
|
+
end
|
2006
|
+
|
1956
2007
|
# Adds the association method to the association methods module.
|
1957
2008
|
def def_association_method(opts)
|
1958
2009
|
association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
|
@@ -1978,13 +2029,13 @@ module Sequel
|
|
1978
2029
|
opts[:setter_method] = :"#{opts[:name]}="
|
1979
2030
|
end
|
1980
2031
|
|
1981
|
-
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
2032
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
|
1982
2033
|
if opts[:block]
|
1983
2034
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1984
2035
|
end
|
1985
2036
|
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1986
2037
|
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1987
|
-
def_association_method(opts)
|
2038
|
+
def_association_method(opts) unless opts[:no_association_method]
|
1988
2039
|
|
1989
2040
|
return if opts[:read_only]
|
1990
2041
|
|
@@ -2028,7 +2079,7 @@ module Sequel
|
|
2028
2079
|
raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
|
2029
2080
|
end
|
2030
2081
|
opts[:uses_left_composite_keys] = lcks.length > 1
|
2031
|
-
opts[:uses_right_composite_keys] = rcks.length > 1
|
2082
|
+
uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
|
2032
2083
|
opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
|
2033
2084
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
2034
2085
|
opts[:left_key_alias] ||= opts.default_associated_key_alias
|
@@ -2037,8 +2088,75 @@ module Sequel
|
|
2037
2088
|
opts[:after_load] ||= []
|
2038
2089
|
opts[:after_load].unshift(:array_uniq!)
|
2039
2090
|
end
|
2040
|
-
opts[:
|
2041
|
-
|
2091
|
+
if join_table_db = opts[:join_table_db]
|
2092
|
+
opts[:use_placeholder_loader] = false
|
2093
|
+
opts[:allow_eager_graph] = false
|
2094
|
+
opts[:allow_filtering_by] = false
|
2095
|
+
opts[:eager_limit_strategy] = nil
|
2096
|
+
join_table_ds = join_table_db.from(join_table)
|
2097
|
+
opts[:dataset] ||= proc do |r|
|
2098
|
+
vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
|
2099
|
+
ds = r.associated_dataset.where(opts.right_primary_key => vals)
|
2100
|
+
if uses_rcks
|
2101
|
+
vals.delete_if{|v| v.any?(&:nil?)}
|
2102
|
+
else
|
2103
|
+
vals.delete(nil)
|
2104
|
+
end
|
2105
|
+
ds = ds.clone(:no_results=>true) if vals.empty?
|
2106
|
+
ds
|
2107
|
+
end
|
2108
|
+
opts[:eager_loader] ||= proc do |eo|
|
2109
|
+
h = eo[:id_map]
|
2110
|
+
assign_singular = opts.assign_singular?
|
2111
|
+
rpk = opts.right_primary_key
|
2112
|
+
name = opts[:name]
|
2113
|
+
|
2114
|
+
join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
|
2115
|
+
|
2116
|
+
if uses_rcks
|
2117
|
+
join_map.delete_if{|v,| v.any?(&:nil?)}
|
2118
|
+
else
|
2119
|
+
join_map.delete(nil)
|
2120
|
+
end
|
2121
|
+
|
2122
|
+
eo = Hash[eo]
|
2123
|
+
|
2124
|
+
if join_map.empty?
|
2125
|
+
eo[:no_results] = true
|
2126
|
+
else
|
2127
|
+
join_map.each_value do |vs|
|
2128
|
+
vs.replace(vs.flat_map{|v| h[v]})
|
2129
|
+
vs.uniq!
|
2130
|
+
end
|
2131
|
+
|
2132
|
+
eo[:loader] = false
|
2133
|
+
eo[:right_keys] = join_map.keys
|
2134
|
+
end
|
2135
|
+
|
2136
|
+
opts[:model].eager_load_results(opts, eo) do |assoc_record|
|
2137
|
+
rpkv = if uses_rcks
|
2138
|
+
assoc_record.values.values_at(*rpk)
|
2139
|
+
else
|
2140
|
+
assoc_record.values[rpk]
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
objects = join_map[rpkv]
|
2144
|
+
|
2145
|
+
if assign_singular
|
2146
|
+
objects.each do |object|
|
2147
|
+
object.associations[name] ||= assoc_record
|
2148
|
+
end
|
2149
|
+
else
|
2150
|
+
objects.each do |object|
|
2151
|
+
object.associations[name].push(assoc_record)
|
2152
|
+
end
|
2153
|
+
end
|
2154
|
+
end
|
2155
|
+
end
|
2156
|
+
else
|
2157
|
+
opts[:dataset] ||= opts.association_dataset_proc
|
2158
|
+
opts[:eager_loader] ||= opts.method(:default_eager_loader)
|
2159
|
+
end
|
2042
2160
|
|
2043
2161
|
join_type = opts[:graph_join_type]
|
2044
2162
|
select = opts[:graph_select]
|
@@ -2072,50 +2190,60 @@ module Sequel
|
|
2072
2190
|
return if opts[:read_only]
|
2073
2191
|
|
2074
2192
|
if one_through_one
|
2075
|
-
opts
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2193
|
+
unless opts.has_key?(:setter)
|
2194
|
+
opts[:setter] = proc do |o|
|
2195
|
+
h = {}
|
2196
|
+
lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
|
2197
|
+
jtds = _join_table_dataset(opts).where(lh)
|
2079
2198
|
|
2080
|
-
|
2081
|
-
|
2199
|
+
checked_transaction do
|
2200
|
+
current = jtds.first
|
2082
2201
|
|
2083
|
-
if o
|
2084
|
-
new_values = []
|
2085
|
-
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2086
|
-
end
|
2087
|
-
|
2088
|
-
if current
|
2089
|
-
current_values = rcks.map{|k| current[k]}
|
2090
|
-
jtds = jtds.where(rcks.zip(current_values))
|
2091
2202
|
if o
|
2092
|
-
|
2093
|
-
|
2203
|
+
new_values = []
|
2204
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2205
|
+
end
|
2206
|
+
|
2207
|
+
if current
|
2208
|
+
current_values = rcks.map{|k| current[k]}
|
2209
|
+
jtds = jtds.where(rcks.zip(current_values))
|
2210
|
+
if o
|
2211
|
+
if current_values != new_values
|
2212
|
+
jtds.update(h)
|
2213
|
+
end
|
2214
|
+
else
|
2215
|
+
jtds.delete
|
2094
2216
|
end
|
2095
|
-
|
2096
|
-
|
2217
|
+
elsif o
|
2218
|
+
lh.each{|k,v| h[k] = v}
|
2219
|
+
jtds.insert(h)
|
2097
2220
|
end
|
2098
|
-
elsif o
|
2099
|
-
lh.each{|k,v| h[k] = v}
|
2100
|
-
jtds.insert(h)
|
2101
2221
|
end
|
2102
2222
|
end
|
2103
2223
|
end
|
2104
|
-
opts
|
2224
|
+
if opts.fetch(:setter, true)
|
2225
|
+
opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
|
2226
|
+
end
|
2105
2227
|
else
|
2106
|
-
opts
|
2107
|
-
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2228
|
+
unless opts.has_key?(:adder)
|
2229
|
+
opts[:adder] = proc do |o|
|
2230
|
+
h = {}
|
2231
|
+
lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
|
2232
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
|
2233
|
+
_join_table_dataset(opts).insert(h)
|
2234
|
+
end
|
2111
2235
|
end
|
2112
2236
|
|
2113
|
-
opts
|
2114
|
-
|
2237
|
+
unless opts.has_key?(:remover)
|
2238
|
+
opts[:remover] = proc do |o|
|
2239
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.get_column_value(k)})).delete
|
2240
|
+
end
|
2115
2241
|
end
|
2116
2242
|
|
2117
|
-
opts
|
2118
|
-
|
2243
|
+
unless opts.has_key?(:clearer)
|
2244
|
+
opts[:clearer] = proc do
|
2245
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
|
2246
|
+
end
|
2119
2247
|
end
|
2120
2248
|
end
|
2121
2249
|
end
|
@@ -2172,8 +2300,12 @@ module Sequel
|
|
2172
2300
|
|
2173
2301
|
return if opts[:read_only]
|
2174
2302
|
|
2175
|
-
|
2176
|
-
|
2303
|
+
unless opts.has_key?(:setter)
|
2304
|
+
opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
|
2305
|
+
end
|
2306
|
+
if opts.fetch(:setter, true)
|
2307
|
+
opts[:_setter] = proc{|o| set_associated_object(opts, o)}
|
2308
|
+
end
|
2177
2309
|
end
|
2178
2310
|
|
2179
2311
|
# Configures one_to_many and one_to_one association reflections and adds the related association methods
|
@@ -2240,49 +2372,59 @@ module Sequel
|
|
2240
2372
|
cks.each{|k| ck_nil_hash[k] = nil}
|
2241
2373
|
|
2242
2374
|
if one_to_one
|
2243
|
-
opts
|
2244
|
-
|
2375
|
+
unless opts.has_key?(:setter)
|
2376
|
+
opts[:setter] = proc do |o|
|
2377
|
+
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2245
2378
|
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2379
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2380
|
+
if old = up_ds.first
|
2381
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2382
|
+
end
|
2383
|
+
save_old = true
|
2249
2384
|
end
|
2250
|
-
save_old = true
|
2251
|
-
end
|
2252
2385
|
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2386
|
+
if o
|
2387
|
+
if !o.new? && !save_old
|
2388
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2389
|
+
end
|
2390
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2256
2391
|
end
|
2257
|
-
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2258
|
-
end
|
2259
2392
|
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2393
|
+
checked_transaction do
|
2394
|
+
if save_old
|
2395
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2396
|
+
else
|
2397
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2398
|
+
end
|
2266
2399
|
|
2267
|
-
|
2400
|
+
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2401
|
+
end
|
2268
2402
|
end
|
2269
2403
|
end
|
2270
|
-
opts
|
2404
|
+
if opts.fetch(:setter, true)
|
2405
|
+
opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
|
2406
|
+
end
|
2271
2407
|
else
|
2272
2408
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
2273
2409
|
|
2274
|
-
opts
|
2275
|
-
|
2276
|
-
|
2410
|
+
unless opts.has_key?(:adder)
|
2411
|
+
opts[:adder] = proc do |o|
|
2412
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2413
|
+
o.save(save_opts)
|
2414
|
+
end
|
2277
2415
|
end
|
2278
2416
|
|
2279
|
-
opts
|
2280
|
-
|
2281
|
-
|
2417
|
+
unless opts.has_key?(:remover)
|
2418
|
+
opts[:remover] = proc do |o|
|
2419
|
+
cks.each{|k| o.set_column_value(:"#{k}=", nil)}
|
2420
|
+
o.save(save_opts)
|
2421
|
+
end
|
2282
2422
|
end
|
2283
2423
|
|
2284
|
-
opts
|
2285
|
-
|
2424
|
+
unless opts.has_key?(:clearer)
|
2425
|
+
opts[:clearer] = proc do
|
2426
|
+
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
|
2427
|
+
end
|
2286
2428
|
end
|
2287
2429
|
end
|
2288
2430
|
end
|
@@ -2376,7 +2518,7 @@ module Sequel
|
|
2376
2518
|
|
2377
2519
|
# Dataset for the join table of the given many to many association reflection
|
2378
2520
|
def _join_table_dataset(opts)
|
2379
|
-
ds = model.db.from(opts.join_table_source)
|
2521
|
+
ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
|
2380
2522
|
opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
|
2381
2523
|
end
|
2382
2524
|
|
@@ -2397,7 +2539,12 @@ module Sequel
|
|
2397
2539
|
if loader = _associated_object_loader(opts, dynamic_opts)
|
2398
2540
|
loader.all(*opts.predicate_key_values(self))
|
2399
2541
|
else
|
2400
|
-
_associated_dataset(opts, dynamic_opts)
|
2542
|
+
ds = _associated_dataset(opts, dynamic_opts)
|
2543
|
+
if ds.opts[:no_results]
|
2544
|
+
[]
|
2545
|
+
else
|
2546
|
+
ds.all
|
2547
|
+
end
|
2401
2548
|
end
|
2402
2549
|
end
|
2403
2550
|
|
@@ -2858,6 +3005,8 @@ module Sequel
|
|
2858
3005
|
(multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.respond_to?(:all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
|
2859
3006
|
l = args[0]
|
2860
3007
|
if ar = model.association_reflections[l]
|
3008
|
+
raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
|
3009
|
+
|
2861
3010
|
if multiple
|
2862
3011
|
klass = ar.associated_class
|
2863
3012
|
if is_ds
|
@@ -3008,6 +3157,8 @@ module Sequel
|
|
3008
3157
|
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
3009
3158
|
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
3010
3159
|
#
|
3160
|
+
# You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
|
3161
|
+
#
|
3011
3162
|
# Examples:
|
3012
3163
|
#
|
3013
3164
|
# # For each album, eager_graph load the artist
|
@@ -3351,7 +3502,7 @@ module Sequel
|
|
3351
3502
|
# Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
|
3352
3503
|
# per-call determining of the alias base.
|
3353
3504
|
def eager_graph_check_association(model, association)
|
3354
|
-
if association.is_a?(SQL::AliasedExpression)
|
3505
|
+
reflection = if association.is_a?(SQL::AliasedExpression)
|
3355
3506
|
expr = association.expression
|
3356
3507
|
if expr.is_a?(SQL::Identifier)
|
3357
3508
|
expr = expr.value
|
@@ -3360,10 +3511,17 @@ module Sequel
|
|
3360
3511
|
end
|
3361
3512
|
end
|
3362
3513
|
|
3363
|
-
|
3514
|
+
check_reflection = check_association(model, expr)
|
3515
|
+
SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
|
3364
3516
|
else
|
3365
|
-
check_association(model, association)
|
3517
|
+
check_reflection = check_association(model, association)
|
3366
3518
|
end
|
3519
|
+
|
3520
|
+
if check_reflection && check_reflection[:allow_eager_graph] == false
|
3521
|
+
raise Error, "eager_graph not allowed for #{reflection.inspect}"
|
3522
|
+
end
|
3523
|
+
|
3524
|
+
reflection
|
3367
3525
|
end
|
3368
3526
|
|
3369
3527
|
# The EagerGraphLoader instance used for converting eager_graph results.
|
@@ -3374,15 +3532,30 @@ module Sequel
|
|
3374
3532
|
egl.dup
|
3375
3533
|
end
|
3376
3534
|
|
3377
|
-
# Eagerly load all specified associations
|
3535
|
+
# Eagerly load all specified associations.
|
3378
3536
|
def eager_load(a, eager_assoc=@opts[:eager])
|
3379
3537
|
return if a.empty?
|
3538
|
+
|
3539
|
+
# Reflections for all associations to eager load
|
3540
|
+
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3541
|
+
|
3542
|
+
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
3543
|
+
|
3544
|
+
reflections.each do |r|
|
3545
|
+
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
|
3546
|
+
end
|
3547
|
+
|
3548
|
+
nil
|
3549
|
+
end
|
3550
|
+
|
3551
|
+
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3552
|
+
def prepare_eager_load(a, reflections, eager_assoc)
|
3553
|
+
eager_load_data = {}
|
3554
|
+
|
3380
3555
|
# Key is foreign/primary key name symbol.
|
3381
3556
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
3382
3557
|
# and values being an array of current model objects with that specific foreign/primary key
|
3383
3558
|
key_hash = {}
|
3384
|
-
# Reflections for all associations to eager load
|
3385
|
-
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3386
3559
|
|
3387
3560
|
# Populate the key_hash entry for each association being eagerly loaded
|
3388
3561
|
reflections.each do |r|
|
@@ -3413,7 +3586,6 @@ module Sequel
|
|
3413
3586
|
id_map = nil
|
3414
3587
|
end
|
3415
3588
|
|
3416
|
-
loader = r[:eager_loader]
|
3417
3589
|
associations = eager_assoc[r[:name]]
|
3418
3590
|
if associations.respond_to?(:call)
|
3419
3591
|
eager_block = associations
|
@@ -3421,9 +3593,23 @@ module Sequel
|
|
3421
3593
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
3422
3594
|
eager_block, associations = pr_assoc
|
3423
3595
|
end
|
3424
|
-
|
3425
|
-
|
3426
|
-
end
|
3596
|
+
|
3597
|
+
eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
|
3598
|
+
end
|
3599
|
+
|
3600
|
+
eager_load_data
|
3601
|
+
end
|
3602
|
+
|
3603
|
+
# Using the hash of loaders and eager options, perform the eager loading.
|
3604
|
+
def perform_eager_loads(eager_load_data)
|
3605
|
+
eager_load_data.map do |loader, eo|
|
3606
|
+
perform_eager_load(loader, eo)
|
3607
|
+
end
|
3608
|
+
end
|
3609
|
+
|
3610
|
+
# Perform eager loading for a single association using the loader and eager options.
|
3611
|
+
def perform_eager_load(loader, eo)
|
3612
|
+
loader.call(eo)
|
3427
3613
|
end
|
3428
3614
|
|
3429
3615
|
# Return a subquery expression for filering by a many_to_many association
|