sequel 3.48.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -17,20 +17,14 @@ module Sequel
|
|
17
17
|
def self.extended(db)
|
18
18
|
db.extend_datasets(DatasetQuery)
|
19
19
|
end
|
20
|
-
end
|
21
20
|
|
22
|
-
class Database
|
23
21
|
# Return a dataset modified by the query block
|
24
22
|
def query(&block)
|
25
|
-
Sequel::Deprecation.deprecate('Loading the query extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(DatabaseQuery)
|
26
23
|
dataset.query(&block)
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
30
27
|
module DatasetQuery
|
31
|
-
end
|
32
|
-
|
33
|
-
class Dataset
|
34
28
|
# Translates a query block into a dataset. Query blocks are an
|
35
29
|
# alternative to Sequel's usual method chaining, by using
|
36
30
|
# instance_eval with a proxy object:
|
@@ -45,12 +39,13 @@ module Sequel
|
|
45
39
|
#
|
46
40
|
# dataset = DB[:items].select(:x, :y, :z).filter{(x > 1) & (y > 2)}.reverse(:z)
|
47
41
|
def query(&block)
|
48
|
-
|
49
|
-
query = Query.new(self)
|
42
|
+
query = Dataset::Query.new(self)
|
50
43
|
query.instance_eval(&block)
|
51
44
|
query.dataset
|
52
45
|
end
|
46
|
+
end
|
53
47
|
|
48
|
+
class Dataset
|
54
49
|
# Proxy object used by Dataset#query.
|
55
50
|
class Query < Sequel::BasicObject
|
56
51
|
# The current dataset in the query. This changes on each method call.
|
@@ -44,12 +44,8 @@
|
|
44
44
|
|
45
45
|
module Sequel
|
46
46
|
module SchemaCaching
|
47
|
-
end
|
48
|
-
|
49
|
-
class Database
|
50
47
|
# Dump the cached schema to the filename given in Marshal format.
|
51
48
|
def dump_schema_cache(file)
|
52
|
-
Sequel::Deprecation.deprecate('Loading the schema_caching extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaCaching)
|
53
49
|
File.open(file, 'wb'){|f| f.write(Marshal.dump(@schemas))}
|
54
50
|
nil
|
55
51
|
end
|
@@ -57,14 +53,12 @@ module Sequel
|
|
57
53
|
# Dump the cached schema to the filename given unless the file
|
58
54
|
# already exists.
|
59
55
|
def dump_schema_cache?(file)
|
60
|
-
Sequel::Deprecation.deprecate('Loading the schema_caching extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaCaching)
|
61
56
|
dump_schema_cache(file) unless File.exist?(file)
|
62
57
|
end
|
63
58
|
|
64
59
|
# Replace the schema cache with the data from the given file, which
|
65
60
|
# should be in Marshal format.
|
66
61
|
def load_schema_cache(file)
|
67
|
-
Sequel::Deprecation.deprecate('Loading the schema_caching extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaCaching)
|
68
62
|
@schemas = Marshal.load(File.read(file))
|
69
63
|
nil
|
70
64
|
end
|
@@ -72,7 +66,6 @@ module Sequel
|
|
72
66
|
# Replace the schema cache with the data from the given file if the
|
73
67
|
# file exists.
|
74
68
|
def load_schema_cache?(file)
|
75
|
-
Sequel::Deprecation.deprecate('Loading the schema_caching extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaCaching)
|
76
69
|
load_schema_cache(file) if File.exist?(file)
|
77
70
|
end
|
78
71
|
end
|
@@ -12,9 +12,6 @@ Sequel.extension :eval_inspect
|
|
12
12
|
|
13
13
|
module Sequel
|
14
14
|
module SchemaDumper
|
15
|
-
end
|
16
|
-
|
17
|
-
class Database
|
18
15
|
# Dump foreign key constraints for all tables as a migration. This complements
|
19
16
|
# the :foreign_keys=>false option to dump_schema_migration. This only dumps
|
20
17
|
# the constraints (not the columns) using alter_table/add_foreign_key with an
|
@@ -22,8 +19,7 @@ module Sequel
|
|
22
19
|
#
|
23
20
|
# Note that the migration this produces does not have a down
|
24
21
|
# block, so you cannot reverse it.
|
25
|
-
def dump_foreign_key_migration(options=
|
26
|
-
Sequel::Deprecation.deprecate('Loading the schema_dumper extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaDumper)
|
22
|
+
def dump_foreign_key_migration(options=OPTS)
|
27
23
|
ts = tables(options)
|
28
24
|
<<END_MIG
|
29
25
|
Sequel.migration do
|
@@ -41,8 +37,7 @@ END_MIG
|
|
41
37
|
# :index_names :: If set to false, don't record names of indexes. If
|
42
38
|
# set to :namespace, prepend the table name to the index name if the
|
43
39
|
# database does not use a global index namespace.
|
44
|
-
def dump_indexes_migration(options=
|
45
|
-
Sequel::Deprecation.deprecate('Loading the schema_dumper extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaDumper)
|
40
|
+
def dump_indexes_migration(options=OPTS)
|
46
41
|
ts = tables(options)
|
47
42
|
<<END_MIG
|
48
43
|
Sequel.migration do
|
@@ -66,8 +61,7 @@ END_MIG
|
|
66
61
|
# later via #dump_index_migration).
|
67
62
|
# :index_names :: If set to false, don't record names of indexes. If
|
68
63
|
# set to :namespace, prepend the table name to the index name.
|
69
|
-
def dump_schema_migration(options=
|
70
|
-
Sequel::Deprecation.deprecate('Loading the schema_dumper extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaDumper)
|
64
|
+
def dump_schema_migration(options=OPTS)
|
71
65
|
options = options.dup
|
72
66
|
if options[:indexes] == false && !options.has_key?(:foreign_keys)
|
73
67
|
# Unless foreign_keys option is specifically set, disable if indexes
|
@@ -96,8 +90,7 @@ END_MIG
|
|
96
90
|
|
97
91
|
# Return a string with a create table block that will recreate the given
|
98
92
|
# table's schema. Takes the same options as dump_schema_migration.
|
99
|
-
def dump_table_schema(table, options=
|
100
|
-
Sequel::Deprecation.deprecate('Loading the schema_dumper extension globally', "Please use Database#extension to load the extension into this database") unless is_a?(SchemaDumper)
|
93
|
+
def dump_table_schema(table, options=OPTS)
|
101
94
|
table = table.value.to_s if table.is_a?(SQL::Identifier)
|
102
95
|
gen = dump_table_generator(table, options)
|
103
96
|
commands = [gen.dump_columns, gen.dump_constraints, gen.dump_indexes].reject{|x| x == ''}.join("\n\n")
|
@@ -215,7 +208,7 @@ END_MIG
|
|
215
208
|
|
216
209
|
# For the table given, get the list of foreign keys and return an alter_table
|
217
210
|
# string that would add the foreign keys if run in a migration.
|
218
|
-
def dump_table_foreign_keys(table, options=
|
211
|
+
def dump_table_foreign_keys(table, options=OPTS)
|
219
212
|
if supports_foreign_key_parsing?
|
220
213
|
fks = foreign_key_list(table, options).sort_by{|fk| fk[:columns].map{|c| c.to_s}}
|
221
214
|
end
|
@@ -229,7 +222,7 @@ END_MIG
|
|
229
222
|
|
230
223
|
# Return a Schema::Generator object that will recreate the
|
231
224
|
# table's schema. Takes the same options as dump_schema_migration.
|
232
|
-
def dump_table_generator(table, options=
|
225
|
+
def dump_table_generator(table, options=OPTS)
|
233
226
|
table = table.value.to_s if table.is_a?(SQL::Identifier)
|
234
227
|
raise(Error, "must provide table as a Symbol, String, or Sequel::SQL::Identifier") unless [String, Symbol].any?{|c| table.is_a?(c)}
|
235
228
|
s = schema(table).dup
|
@@ -277,7 +270,7 @@ END_MIG
|
|
277
270
|
|
278
271
|
# Return a string that containing add_index/drop_index method calls for
|
279
272
|
# creating the index migration.
|
280
|
-
def dump_table_indexes(table, meth, options=
|
273
|
+
def dump_table_indexes(table, meth, options=OPTS)
|
281
274
|
if supports_index_parsing?
|
282
275
|
indexes = indexes(table).sort_by{|k,v| k.to_s}
|
283
276
|
else
|
@@ -292,7 +285,7 @@ END_MIG
|
|
292
285
|
end
|
293
286
|
|
294
287
|
# Convert the parsed index information into options to the Generators index method.
|
295
|
-
def index_to_generator_opts(table, name, index_opts, options=
|
288
|
+
def index_to_generator_opts(table, name, index_opts, options=OPTS)
|
296
289
|
h = {}
|
297
290
|
if options[:index_names] != false && default_index_name(table, index_opts[:columns]) != name.to_s
|
298
291
|
if options[:index_names] == :namespace && !global_index_namespace?
|
@@ -308,7 +301,7 @@ END_MIG
|
|
308
301
|
|
309
302
|
# Sort the tables so that referenced tables are created before tables that
|
310
303
|
# reference them, and then by name. If foreign keys are disabled, just sort by name.
|
311
|
-
def sort_dumped_tables(tables, options=
|
304
|
+
def sort_dumped_tables(tables, options=OPTS)
|
312
305
|
if options[:foreign_keys] != false && supports_foreign_key_parsing?
|
313
306
|
table_fks = {}
|
314
307
|
tables.each{|t| table_fks[t] = foreign_key_list(t)}
|
@@ -440,7 +433,7 @@ END_MIG
|
|
440
433
|
# The value of this option should be the table name to use.
|
441
434
|
# * :drop_index - Same as add_index, but create drop_index statements.
|
442
435
|
# * :ignore_errors - Add the ignore_errors option to the outputted indexes
|
443
|
-
def dump_indexes(options=
|
436
|
+
def dump_indexes(options=OPTS)
|
444
437
|
is = indexes.map do |c|
|
445
438
|
c = c.dup
|
446
439
|
cols = c.delete(:columns)
|
@@ -14,9 +14,6 @@
|
|
14
14
|
|
15
15
|
module Sequel
|
16
16
|
module SelectRemove
|
17
|
-
end
|
18
|
-
|
19
|
-
class Dataset
|
20
17
|
# Remove columns from the list of selected columns. If any of the currently selected
|
21
18
|
# columns use expressions/aliases, this will remove selected columns with the given
|
22
19
|
# aliases. It will also remove entries from the selection that match exactly:
|
@@ -38,7 +35,6 @@ module Sequel
|
|
38
35
|
#
|
39
36
|
# There may be other cases where this method does not work correctly, use it with caution.
|
40
37
|
def select_remove(*cols)
|
41
|
-
Sequel::Deprecation.deprecate('Loading the select_remove extension globally', "Please use Database/Dataset#extension to load the extension into this dataset") unless is_a?(SelectRemove)
|
42
38
|
if (sel = @opts[:select]) && !sel.empty?
|
43
39
|
select(*(columns.zip(sel).reject{|c, s| cols.include?(c)}.map{|c, s| s} - cols))
|
44
40
|
else
|
@@ -16,8 +16,18 @@
|
|
16
16
|
|
17
17
|
module Sequel
|
18
18
|
module SetOverrides
|
19
|
+
Dataset::NON_SQL_OPTIONS.concat([:defaults, :overrides])
|
19
20
|
Dataset.def_mutation_method(:set_defaults, :set_overrides, :module=>self)
|
20
21
|
|
22
|
+
# Set overrides/defaults for insert hashes
|
23
|
+
def insert_sql(*values)
|
24
|
+
if values.size == 1 && (vals = values.first).is_a?(Hash)
|
25
|
+
super(merge_defaults_overrides(vals))
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
21
31
|
# Set the default values for insert and update statements. The values hash passed
|
22
32
|
# to insert or update are merged into this hash, so any values in the hash passed
|
23
33
|
# to insert or update will override values passed to this method.
|
@@ -37,6 +47,24 @@ module Sequel
|
|
37
47
|
def set_overrides(hash)
|
38
48
|
clone(:overrides=>hash.merge(@opts[:overrides]||{}))
|
39
49
|
end
|
50
|
+
|
51
|
+
# Set overrides/defaults for update hashes
|
52
|
+
def update_sql(values = {})
|
53
|
+
if values.is_a?(Hash)
|
54
|
+
super(merge_defaults_overrides(values))
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Return new hashe with merged defaults and overrides.
|
63
|
+
def merge_defaults_overrides(vals)
|
64
|
+
vals = @opts[:defaults].merge(vals) if @opts[:defaults]
|
65
|
+
vals = vals.merge(@opts[:overrides]) if @opts[:overrides]
|
66
|
+
vals
|
67
|
+
end
|
40
68
|
end
|
41
69
|
|
42
70
|
Dataset.register_extension(:set_overrides, SetOverrides)
|
@@ -10,6 +10,12 @@
|
|
10
10
|
module Sequel
|
11
11
|
class ToDot
|
12
12
|
module DatasetMethods
|
13
|
+
# Return a string that can be processed by the +dot+ program (included
|
14
|
+
# with graphviz) in order to see a visualization of the dataset's
|
15
|
+
# abstract syntax tree.
|
16
|
+
def to_dot
|
17
|
+
ToDot.output(self)
|
18
|
+
end
|
13
19
|
end
|
14
20
|
|
15
21
|
# The option keys that should be included in the dot output.
|
@@ -145,15 +151,5 @@ module Sequel
|
|
145
151
|
end
|
146
152
|
end
|
147
153
|
|
148
|
-
class Dataset
|
149
|
-
# Return a string that can be processed by the +dot+ program (included
|
150
|
-
# with graphviz) in order to see a visualization of the dataset's
|
151
|
-
# abstract syntax tree.
|
152
|
-
def to_dot
|
153
|
-
Sequel::Deprecation.deprecate('Loading the to_dot extension globally', "Please use Database/Dataset#extension to load the extension into this dataset") unless is_a?(ToDot::DatasetMethods)
|
154
|
-
ToDot.output(self)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
154
|
Dataset.register_extension(:to_dot, ToDot::DatasetMethods)
|
159
155
|
end
|
data/lib/sequel/model.rb
CHANGED
@@ -72,6 +72,8 @@ module Sequel
|
|
72
72
|
# You can set the +SEQUEL_NO_ASSOCIATIONS+ constant or environment variable to
|
73
73
|
# make Sequel not load the associations plugin by default.
|
74
74
|
class Model
|
75
|
+
OPTS = Sequel::OPTS
|
76
|
+
|
75
77
|
# Map that stores model classes created with <tt>Sequel::Model()</tt>, to allow the reopening
|
76
78
|
# of classes when dealing with code reloading.
|
77
79
|
ANONYMOUS_MODEL_CLASSES = {}
|
@@ -80,9 +82,6 @@ module Sequel
|
|
80
82
|
DATASET_METHODS = (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS +
|
81
83
|
[:each_server]) - [:and, :or, :[], :[]=, :columns, :columns!, :delete, :update, :add_graph_aliases]
|
82
84
|
|
83
|
-
# Class instance variables to set to nil when a subclass is created, for -w compliance
|
84
|
-
EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]
|
85
|
-
|
86
85
|
# Boolean settings that can be modified at the global, class, or instance level.
|
87
86
|
BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting, \
|
88
87
|
:raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_after_commit_rollback, :use_transactions]
|
@@ -94,7 +93,7 @@ module Sequel
|
|
94
93
|
|
95
94
|
# Hooks that are called after an action. When overriding these, it is recommended to call
|
96
95
|
# +super+ on the first line of your method, so later hooks are called after earlier hooks.
|
97
|
-
AFTER_HOOKS = [:
|
96
|
+
AFTER_HOOKS = [:after_create, :after_update, :after_save, :after_destroy,
|
98
97
|
:after_validation, :after_commit, :after_rollback, :after_destroy_commit, :after_destroy_rollback]
|
99
98
|
|
100
99
|
# Hooks that are called around an action. If overridden, these methods must call super
|
@@ -120,7 +119,7 @@ module Sequel
|
|
120
119
|
:@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
|
121
120
|
:@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil,
|
122
121
|
:@use_after_commit_rollback=>nil, :@fast_pk_lookup_sql=>nil,
|
123
|
-
:@fast_instance_delete_sql=>nil,
|
122
|
+
:@fast_instance_delete_sql=>nil,
|
124
123
|
:@db=>nil, :@default_set_fields_options=>:dup}
|
125
124
|
|
126
125
|
# Regular expression that determines if a method name is normal in the sense that
|
@@ -137,7 +136,7 @@ module Sequel
|
|
137
136
|
@db_schema = nil
|
138
137
|
@dataset = nil
|
139
138
|
@dataset_method_modules = []
|
140
|
-
@default_eager_limit_strategy =
|
139
|
+
@default_eager_limit_strategy = true
|
141
140
|
@default_set_fields_options = {}
|
142
141
|
@overridable_methods_module = nil
|
143
142
|
@fast_pk_lookup_sql = nil
|
@@ -145,7 +144,7 @@ module Sequel
|
|
145
144
|
@plugins = []
|
146
145
|
@primary_key = :id
|
147
146
|
@raise_on_save_failure = true
|
148
|
-
@raise_on_typecast_failure =
|
147
|
+
@raise_on_typecast_failure = false
|
149
148
|
@require_modification = nil
|
150
149
|
@restrict_primary_key = true
|
151
150
|
@restricted_columns = nil
|
@@ -9,6 +9,7 @@ module Sequel
|
|
9
9
|
# Set an empty association reflection hash in the model
|
10
10
|
def self.apply(model)
|
11
11
|
model.instance_variable_set(:@association_reflections, {})
|
12
|
+
model.instance_variable_set(:@autoreloading_associations, {})
|
12
13
|
end
|
13
14
|
|
14
15
|
# AssociationReflection is a Hash subclass that keeps information on Sequel::Model associations. It
|
@@ -189,25 +190,14 @@ module Sequel
|
|
189
190
|
def reciprocal
|
190
191
|
cached_fetch(:reciprocal) do
|
191
192
|
possible_recips = []
|
192
|
-
fallback_recips = []
|
193
193
|
|
194
194
|
associated_class.all_association_reflections.each do |assoc_reflect|
|
195
195
|
if reciprocal_association?(assoc_reflect)
|
196
|
-
|
197
|
-
fallback_recips << assoc_reflect
|
198
|
-
else
|
199
|
-
possible_recips << assoc_reflect
|
200
|
-
end
|
196
|
+
possible_recips << assoc_reflect
|
201
197
|
end
|
202
198
|
end
|
203
199
|
|
204
|
-
|
205
|
-
if possible_recips.empty? && !fallback_recips.empty?
|
206
|
-
possible_recips = fallback_recips
|
207
|
-
Sequel::Deprecation.deprecate("All reciprocal association candidates found for #{self[:name]} association have conditions, blocks, or differing primary keys (#{possible_recips.map{|r| r[:name]}.join(', ')}). Automatic choosing of an reciprocal association with conditions or blocks is", "Please explicitly specify the reciprocal option for the #{self[:name]} association")
|
208
|
-
end
|
209
|
-
|
210
|
-
unless possible_recips.empty?
|
200
|
+
if possible_recips.length == 1
|
211
201
|
cached_set(:reciprocal_type, possible_recips.first[:type]) if reciprocal_type.is_a?(Array)
|
212
202
|
possible_recips.first[:name]
|
213
203
|
end
|
@@ -263,6 +253,14 @@ module Sequel
|
|
263
253
|
:"#{self[:name]}="
|
264
254
|
end
|
265
255
|
|
256
|
+
# The range used for slicing when using the :ruby eager limit strategy.
|
257
|
+
def slice_range
|
258
|
+
limit, offset = limit_and_offset
|
259
|
+
if limit || offset
|
260
|
+
(offset||0)..(limit ? (offset||0)+limit-1 : -1)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
266
264
|
private
|
267
265
|
|
268
266
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
|
@@ -300,14 +298,11 @@ module Sequel
|
|
300
298
|
end
|
301
299
|
end
|
302
300
|
|
303
|
-
# REMOVE40: merge into reciprocal_association?
|
304
|
-
def deprecated_reciprocal_association?(assoc_reflect)
|
305
|
-
assoc_reflect[:conditions] || assoc_reflect[:block]
|
306
|
-
end
|
307
|
-
|
308
301
|
def reciprocal_association?(assoc_reflect)
|
309
302
|
Array(reciprocal_type).include?(assoc_reflect[:type]) &&
|
310
|
-
assoc_reflect.associated_class == self[:model]
|
303
|
+
assoc_reflect.associated_class == self[:model] &&
|
304
|
+
assoc_reflect[:conditions].nil? &&
|
305
|
+
assoc_reflect[:block].nil?
|
311
306
|
end
|
312
307
|
|
313
308
|
# If +s+ is an array, map +s+ over the block. Otherwise, just call the
|
@@ -400,13 +395,8 @@ module Sequel
|
|
400
395
|
|
401
396
|
private
|
402
397
|
|
403
|
-
# REMOVE40: merge into reciprocal_association?
|
404
|
-
def deprecated_reciprocal_association?(assoc_reflect)
|
405
|
-
super || primary_key != assoc_reflect.primary_key
|
406
|
-
end
|
407
|
-
|
408
398
|
def reciprocal_association?(assoc_reflect)
|
409
|
-
super && self[:keys] == assoc_reflect[:keys]
|
399
|
+
super && self[:keys] == assoc_reflect[:keys] && primary_key == assoc_reflect.primary_key
|
410
400
|
end
|
411
401
|
|
412
402
|
# The reciprocal type of a many_to_one association is either
|
@@ -444,7 +434,7 @@ module Sequel
|
|
444
434
|
|
445
435
|
# The column in the current table that the key in the associated table references.
|
446
436
|
def primary_key
|
447
|
-
|
437
|
+
self[:primary_key]
|
448
438
|
end
|
449
439
|
|
450
440
|
# #primary_key qualified by the current table
|
@@ -475,13 +465,8 @@ module Sequel
|
|
475
465
|
|
476
466
|
private
|
477
467
|
|
478
|
-
# REMOVE40: merge into reciprocal_association?
|
479
|
-
def deprecated_reciprocal_association?(assoc_reflect)
|
480
|
-
super || primary_key != assoc_reflect.primary_key
|
481
|
-
end
|
482
|
-
|
483
468
|
def reciprocal_association?(assoc_reflect)
|
484
|
-
super && self[:keys] == assoc_reflect[:keys]
|
469
|
+
super && self[:keys] == assoc_reflect[:keys] && primary_key == assoc_reflect.primary_key
|
485
470
|
end
|
486
471
|
|
487
472
|
# The reciprocal type of a one_to_many association is a many_to_one association.
|
@@ -497,15 +482,18 @@ module Sequel
|
|
497
482
|
# support both DISTINCT ON and window functions as strategies.
|
498
483
|
def eager_limit_strategy
|
499
484
|
cached_fetch(:_eager_limit_strategy) do
|
500
|
-
|
485
|
+
offset = limit_and_offset.last
|
486
|
+
case s = self.fetch(:eager_limit_strategy){(self[:model].default_eager_limit_strategy || :ruby) if offset}
|
501
487
|
when Symbol
|
502
488
|
s
|
503
489
|
when true
|
504
490
|
ds = associated_class.dataset
|
505
|
-
if ds.supports_ordered_distinct_on?
|
491
|
+
if ds.supports_ordered_distinct_on? && offset.nil?
|
506
492
|
:distinct_on
|
507
493
|
elsif ds.supports_window_functions?
|
508
494
|
:window_function
|
495
|
+
else
|
496
|
+
:ruby
|
509
497
|
end
|
510
498
|
else
|
511
499
|
nil
|
@@ -515,7 +503,11 @@ module Sequel
|
|
515
503
|
|
516
504
|
# The limit and offset for this association (returned as a two element array).
|
517
505
|
def limit_and_offset
|
518
|
-
[
|
506
|
+
if (v = self[:limit]).is_a?(Array)
|
507
|
+
v
|
508
|
+
else
|
509
|
+
[v, nil]
|
510
|
+
end
|
519
511
|
end
|
520
512
|
|
521
513
|
# one_to_one associations return a single object, not an array
|
@@ -644,15 +636,12 @@ module Sequel
|
|
644
636
|
|
645
637
|
private
|
646
638
|
|
647
|
-
# REMOVE40: merge into reciprocal_association?
|
648
|
-
def deprecated_reciprocal_association?(assoc_reflect)
|
649
|
-
super || right_primary_keys != assoc_reflect[:left_primary_key_columns] || self[:left_primary_key_columns] != assoc_reflect.right_primary_keys
|
650
|
-
end
|
651
|
-
|
652
639
|
def reciprocal_association?(assoc_reflect)
|
653
640
|
super && assoc_reflect[:left_keys] == self[:right_keys] &&
|
654
641
|
assoc_reflect[:right_keys] == self[:left_keys] &&
|
655
|
-
assoc_reflect[:join_table] == self[:join_table]
|
642
|
+
assoc_reflect[:join_table] == self[:join_table] &&
|
643
|
+
right_primary_keys == assoc_reflect[:left_primary_key_columns] &&
|
644
|
+
self[:left_primary_key_columns] == assoc_reflect.right_primary_keys
|
656
645
|
end
|
657
646
|
|
658
647
|
def reciprocal_type
|
@@ -727,7 +716,13 @@ module Sequel
|
|
727
716
|
# All association reflections defined for this model (default: {}).
|
728
717
|
attr_reader :association_reflections
|
729
718
|
|
730
|
-
#
|
719
|
+
# Hash with column symbol keys and arrays of many_to_one
|
720
|
+
# association symbols that should be cleared when the column
|
721
|
+
# value changes.
|
722
|
+
attr_reader :autoreloading_associations
|
723
|
+
|
724
|
+
# The default :eager_limit_strategy option to use for limited or offset associations (default: true, causing Sequel
|
725
|
+
# to use what it considers the most appropriate strategy).
|
731
726
|
attr_accessor :default_eager_limit_strategy
|
732
727
|
|
733
728
|
# Array of all association reflections for this model class
|
@@ -823,14 +818,8 @@ module Sequel
|
|
823
818
|
# (the alias that was used for the current table), and possibly :eager_block (a callback
|
824
819
|
# proc accepting the associated dataset, for per-call customization).
|
825
820
|
# Should return a copy of the dataset with the association graphed into it.
|
826
|
-
# :eager_limit_strategy :: Determines the strategy used for enforcing limits when eager loading
|
827
|
-
# the +eager+ method.
|
828
|
-
# for *_many associations, the :ruby strategy is used by default, which still retrieves
|
829
|
-
# all records but slices the resulting array after the association is retrieved. You
|
830
|
-
# can pass a +true+ value for this option to have Sequel pick what it thinks is the best
|
831
|
-
# choice for the database, or specify a specific symbol to manually select a strategy.
|
832
|
-
# one_to_one associations support :distinct_on and :window_function.
|
833
|
-
# *_many associations support :ruby, and :window_function.
|
821
|
+
# :eager_limit_strategy :: Determines the strategy used for enforcing limits and offsets when eager loading
|
822
|
+
# associations via the +eager+ method.
|
834
823
|
# :eager_loader :: A proc to use to implement eager loading, overriding the default. Takes a single hash argument,
|
835
824
|
# with at least the keys: :rows, which is an array of current model instances, :associations,
|
836
825
|
# which is a hash of dependent associations, :self, which is the dataset doing the eager loading,
|
@@ -955,18 +944,9 @@ module Sequel
|
|
955
944
|
# object to get the foreign key values for the join table.
|
956
945
|
# Defaults to :right_primary_key option.
|
957
946
|
# :uniq :: Adds a after_load callback that makes the array of objects unique.
|
958
|
-
def associate(type, name, opts =
|
959
|
-
if opts[:one_to_one] && type == :one_to_many
|
960
|
-
Sequel::Deprecation.deprecate('Raising an Error when the one_to_many type uses the :one_to_one option', "Use the one_to_one associationtype")
|
961
|
-
raise(Error, 'one_to_many association type with :one_to_one option removed, used one_to_one association type')
|
962
|
-
end
|
947
|
+
def associate(type, name, opts = OPTS, &block)
|
963
948
|
raise(Error, 'invalid association type') unless assoc_class = ASSOCIATION_TYPES[type]
|
964
949
|
raise(Error, 'Model.associate name argument must be a symbol') unless name.is_a?(Symbol)
|
965
|
-
raise(Error, ':eager_loader option must have an arity of 1 or 3') if opts[:eager_loader] && ![1, 3].include?(opts[:eager_loader].arity)
|
966
|
-
raise(Error, ':eager_grapher option must have an arity of 1 or 3') if opts[:eager_grapher] && ![1, 3].include?(opts[:eager_grapher].arity)
|
967
|
-
|
968
|
-
Sequel::Deprecation.deprecate('The :eager_loader association option accepting 3 arguments', "Please switch to accepting a single options hash") if opts[:eager_loader] && opts[:eager_loader].arity == 3
|
969
|
-
Sequel::Deprecation.deprecate('The :eager_grapher association option accepting 3 arguments', "Please switch to accepting a single options hash") if opts[:eager_grapher] && opts[:eager_grapher].arity == 3
|
970
950
|
|
971
951
|
# dup early so we don't modify opts
|
972
952
|
orig_opts = opts.dup
|
@@ -1017,7 +997,7 @@ module Sequel
|
|
1017
997
|
end
|
1018
998
|
|
1019
999
|
# Modify and return eager loading dataset based on association options.
|
1020
|
-
def eager_loading_dataset(opts, ds, select, associations, eager_options=
|
1000
|
+
def eager_loading_dataset(opts, ds, select, associations, eager_options=OPTS)
|
1021
1001
|
ds = apply_association_dataset_opts(opts, ds)
|
1022
1002
|
ds = ds.select(*select) if select
|
1023
1003
|
if opts[:eager_graph]
|
@@ -1037,93 +1017,44 @@ module Sequel
|
|
1037
1017
|
end
|
1038
1018
|
|
1039
1019
|
# Shortcut for adding a many_to_many association, see #associate
|
1040
|
-
def many_to_many(name, opts=
|
1020
|
+
def many_to_many(name, opts=OPTS, &block)
|
1041
1021
|
associate(:many_to_many, name, opts, &block)
|
1042
1022
|
end
|
1043
1023
|
|
1044
1024
|
# Shortcut for adding a many_to_one association, see #associate
|
1045
|
-
def many_to_one(name, opts=
|
1025
|
+
def many_to_one(name, opts=OPTS, &block)
|
1046
1026
|
associate(:many_to_one, name, opts, &block)
|
1047
1027
|
end
|
1048
1028
|
|
1049
1029
|
# Shortcut for adding a one_to_many association, see #associate
|
1050
|
-
def one_to_many(name, opts=
|
1030
|
+
def one_to_many(name, opts=OPTS, &block)
|
1051
1031
|
associate(:one_to_many, name, opts, &block)
|
1052
1032
|
end
|
1053
1033
|
|
1054
1034
|
# Shortcut for adding a one_to_one association, see #associate.
|
1055
|
-
def one_to_one(name, opts=
|
1035
|
+
def one_to_one(name, opts=OPTS, &block)
|
1056
1036
|
associate(:one_to_one, name, opts, &block)
|
1057
1037
|
end
|
1058
1038
|
|
1059
|
-
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@default_eager_limit_strategy=>nil)
|
1039
|
+
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@default_eager_limit_strategy=>nil)
|
1060
1040
|
Plugins.def_dataset_methods(self, [:eager, :eager_graph])
|
1061
1041
|
|
1062
1042
|
private
|
1063
1043
|
|
1064
|
-
# Use a correlated subquery to limit the results of the eager loading dataset.
|
1065
|
-
def apply_correlated_subquery_eager_limit_strategy(ds, opts)
|
1066
|
-
Sequel::Deprecation.deprecate('The correlated_subquery eager limit strategy', 'Switch to another eager limit strategy.')
|
1067
|
-
klass = opts.associated_class
|
1068
|
-
kds = klass.dataset
|
1069
|
-
dsa = ds.send(:dataset_alias, 1)
|
1070
|
-
raise Error, "can't use a correlated subquery if the associated class (#{opts.associated_class.inspect}) does not have a primary key" unless pk = klass.primary_key
|
1071
|
-
pka = Array(pk)
|
1072
|
-
raise Error, "can't use a correlated subquery if the associated class (#{opts.associated_class.inspect}) has a composite primary key and the database does not support multiple column IN" if pka.length > 1 && !ds.supports_multiple_column_in?
|
1073
|
-
table = kds.opts[:from]
|
1074
|
-
raise Error, "can't use a correlated subquery unless the associated class (#{opts.associated_class.inspect}) uses a single FROM table" unless table && table.length == 1
|
1075
|
-
table = table.first
|
1076
|
-
if order = ds.opts[:order]
|
1077
|
-
oproc = lambda do |x|
|
1078
|
-
case x
|
1079
|
-
when Symbol
|
1080
|
-
t, c, _ = ds.send(:split_symbol, x)
|
1081
|
-
if t && t.to_sym == table
|
1082
|
-
SQL::QualifiedIdentifier.new(dsa, c)
|
1083
|
-
else
|
1084
|
-
x
|
1085
|
-
end
|
1086
|
-
when SQL::QualifiedIdentifier
|
1087
|
-
if x.table == table
|
1088
|
-
SQL::QualifiedIdentifier.new(dsa, x.column)
|
1089
|
-
else
|
1090
|
-
x
|
1091
|
-
end
|
1092
|
-
when SQL::OrderedExpression
|
1093
|
-
SQL::OrderedExpression.new(oproc.call(x.expression), x.descending, :nulls=>x.nulls)
|
1094
|
-
else
|
1095
|
-
x
|
1096
|
-
end
|
1097
|
-
end
|
1098
|
-
order = order.map(&oproc)
|
1099
|
-
end
|
1100
|
-
limit, offset = opts.limit_and_offset
|
1101
|
-
|
1102
|
-
subquery = yield kds.
|
1103
|
-
unlimited.
|
1104
|
-
from(SQL::AliasedExpression.new(table, dsa)).
|
1105
|
-
select(*pka.map{|k| SQL::QualifiedIdentifier.new(dsa, k)}).
|
1106
|
-
order(*order).
|
1107
|
-
limit(limit, offset)
|
1108
|
-
|
1109
|
-
pk = if pk.is_a?(Array)
|
1110
|
-
pk.map{|k| SQL::QualifiedIdentifier.new(table, k)}
|
1111
|
-
else
|
1112
|
-
SQL::QualifiedIdentifier.new(table, pk)
|
1113
|
-
end
|
1114
|
-
ds.where(pk=>subquery)
|
1115
|
-
end
|
1116
|
-
|
1117
1044
|
# Use a window function to limit the results of the eager loading dataset.
|
1118
1045
|
def apply_window_function_eager_limit_strategy(ds, opts)
|
1119
1046
|
rn = ds.row_number_column
|
1120
1047
|
limit, offset = opts.limit_and_offset
|
1121
1048
|
ds = ds.unordered.select_append{row_number(:over, :partition=>opts.predicate_key, :order=>ds.opts[:order]){}.as(rn)}.from_self
|
1122
1049
|
ds = if opts[:type] == :one_to_one
|
1123
|
-
ds.where(rn => 1)
|
1050
|
+
ds.where(rn => offset ? offset+1 : 1)
|
1124
1051
|
elsif offset
|
1125
1052
|
offset += 1
|
1126
|
-
|
1053
|
+
if limit
|
1054
|
+
ds.where(rn => (offset...(offset+limit)))
|
1055
|
+
else
|
1056
|
+
ds.where{SQL::Identifier.new(rn) >= offset}
|
1057
|
+
end
|
1127
1058
|
else
|
1128
1059
|
ds.where{SQL::Identifier.new(rn) <= limit}
|
1129
1060
|
end
|
@@ -1131,19 +1062,19 @@ module Sequel
|
|
1131
1062
|
|
1132
1063
|
# The module to use for the association's methods. Defaults to
|
1133
1064
|
# the overridable_methods_module.
|
1134
|
-
def association_module(opts=
|
1065
|
+
def association_module(opts=OPTS)
|
1135
1066
|
opts.fetch(:methods_module, overridable_methods_module)
|
1136
1067
|
end
|
1137
1068
|
|
1138
1069
|
# Add a method to the module included in the class, so the method
|
1139
1070
|
# can be easily overridden in the class itself while allowing for
|
1140
1071
|
# super to be called.
|
1141
|
-
def association_module_def(name, opts=
|
1072
|
+
def association_module_def(name, opts=OPTS, &block)
|
1142
1073
|
association_module(opts).module_eval{define_method(name, &block)}
|
1143
1074
|
end
|
1144
1075
|
|
1145
1076
|
# Add a private method to the module included in the class.
|
1146
|
-
def association_module_private_def(name, opts=
|
1077
|
+
def association_module_private_def(name, opts=OPTS, &block)
|
1147
1078
|
association_module_def(name, opts, &block)
|
1148
1079
|
association_module(opts).send(:private, name)
|
1149
1080
|
end
|
@@ -1190,6 +1121,7 @@ module Sequel
|
|
1190
1121
|
graph_jt_conds = opts[:graph_join_table_conditions] = opts.fetch(:graph_join_table_conditions, []).to_a
|
1191
1122
|
opts[:graph_join_table_join_type] ||= opts[:graph_join_type]
|
1192
1123
|
opts[:after_load].unshift(:array_uniq!) if opts[:uniq]
|
1124
|
+
slice_range = opts.slice_range
|
1193
1125
|
opts[:dataset] ||= proc{opts.associated_dataset.inner_join(join_table, rcks.zip(opts.right_primary_keys) + opts.predicate_keys.zip(lcpks.map{|k| send(k)}), :qualify=>:deep)}
|
1194
1126
|
|
1195
1127
|
opts[:eager_loader] ||= proc do |eo|
|
@@ -1199,16 +1131,10 @@ module Sequel
|
|
1199
1131
|
r = rcks.zip(opts.right_primary_keys)
|
1200
1132
|
l = [[opts.predicate_key, h.keys]]
|
1201
1133
|
ds = model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, r + l, :qualify=>:deep), nil, eo[:associations], eo)
|
1202
|
-
|
1203
|
-
when :window_function
|
1134
|
+
if opts.eager_limit_strategy == :window_function
|
1204
1135
|
delete_rn = true
|
1205
1136
|
rn = ds.row_number_column
|
1206
1137
|
ds = apply_window_function_eager_limit_strategy(ds, opts)
|
1207
|
-
when :correlated_subquery
|
1208
|
-
ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
|
1209
|
-
dsa = ds.send(:dataset_alias, 2)
|
1210
|
-
xds.inner_join(join_table, r + lcks.map{|k| [k, SQL::QualifiedIdentifier.new(opts.join_table_alias, k)]}, :table_alias=>dsa, :qualify=>:deep)
|
1211
|
-
end
|
1212
1138
|
end
|
1213
1139
|
ds.all do |assoc_record|
|
1214
1140
|
assoc_record.values.delete(rn) if delete_rn
|
@@ -1221,8 +1147,7 @@ module Sequel
|
|
1221
1147
|
objects.each{|object| object.associations[name].push(assoc_record)}
|
1222
1148
|
end
|
1223
1149
|
if opts.eager_limit_strategy == :ruby
|
1224
|
-
|
1225
|
-
rows.each{|o| o.associations[name] = o.associations[name].slice(offset||0, limit) || []}
|
1150
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
|
1226
1151
|
end
|
1227
1152
|
end
|
1228
1153
|
|
@@ -1285,6 +1210,17 @@ module Sequel
|
|
1285
1210
|
end
|
1286
1211
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
1287
1212
|
opts[:cartesian_product_number] ||= 0
|
1213
|
+
|
1214
|
+
if !opts.has_key?(:many_to_one_pk_lookup) &&
|
1215
|
+
(opts[:dataset] || opts[:conditions] || opts[:block] || opts[:select] ||
|
1216
|
+
(opts.has_key?(:key) && opts[:key] == nil))
|
1217
|
+
opts[:many_to_one_pk_lookup] = false
|
1218
|
+
end
|
1219
|
+
auto_assocs = @autoreloading_associations
|
1220
|
+
cks.each do |k|
|
1221
|
+
(auto_assocs[k] ||= []) << name
|
1222
|
+
end
|
1223
|
+
|
1288
1224
|
opts[:dataset] ||= proc do
|
1289
1225
|
opts.associated_dataset.where(opts.predicate_keys.zip(cks.map{|k| send(k)}))
|
1290
1226
|
end
|
@@ -1342,21 +1278,18 @@ module Sequel
|
|
1342
1278
|
pkcs = opts[:primary_key_columns] ||= Array(pkc)
|
1343
1279
|
raise(Error, "mismatched number of keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
|
1344
1280
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
1281
|
+
slice_range = opts.slice_range
|
1345
1282
|
opts[:dataset] ||= proc do
|
1346
1283
|
opts.associated_dataset.where(opts.predicate_keys.zip(cpks.map{|k| send(k)}))
|
1347
1284
|
end
|
1348
1285
|
opts[:eager_loader] ||= proc do |eo|
|
1349
1286
|
h = eo[:id_map]
|
1350
1287
|
rows = eo[:rows]
|
1351
|
-
if one_to_one
|
1352
|
-
rows.each{|object| object.associations[name] = nil}
|
1353
|
-
else
|
1354
|
-
rows.each{|object| object.associations[name] = []}
|
1355
|
-
end
|
1356
1288
|
reciprocal = opts.reciprocal
|
1357
1289
|
klass = opts.associated_class
|
1358
1290
|
filter_keys = opts.predicate_key
|
1359
1291
|
ds = model.eager_loading_dataset(opts, klass.where(filter_keys=>h.keys), nil, eo[:associations], eo)
|
1292
|
+
assign_singular = true if one_to_one
|
1360
1293
|
case opts.eager_limit_strategy
|
1361
1294
|
when :distinct_on
|
1362
1295
|
ds = ds.distinct(*filter_keys).order_prepend(*filter_keys)
|
@@ -1364,16 +1297,19 @@ module Sequel
|
|
1364
1297
|
delete_rn = true
|
1365
1298
|
rn = ds.row_number_column
|
1366
1299
|
ds = apply_window_function_eager_limit_strategy(ds, opts)
|
1367
|
-
when :
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1300
|
+
when :ruby
|
1301
|
+
assign_singular = false if one_to_one && slice_range
|
1302
|
+
end
|
1303
|
+
if assign_singular
|
1304
|
+
rows.each{|object| object.associations[name] = nil}
|
1305
|
+
else
|
1306
|
+
rows.each{|object| object.associations[name] = []}
|
1371
1307
|
end
|
1372
1308
|
ds.all do |assoc_record|
|
1373
1309
|
assoc_record.values.delete(rn) if delete_rn
|
1374
1310
|
hash_key = uses_cks ? km.map{|k| assoc_record.send(k)} : assoc_record.send(km)
|
1375
1311
|
next unless objects = h[hash_key]
|
1376
|
-
if
|
1312
|
+
if assign_singular
|
1377
1313
|
objects.each do |object|
|
1378
1314
|
unless object.associations[name]
|
1379
1315
|
object.associations[name] = assoc_record
|
@@ -1388,8 +1324,13 @@ module Sequel
|
|
1388
1324
|
end
|
1389
1325
|
end
|
1390
1326
|
if opts.eager_limit_strategy == :ruby
|
1391
|
-
|
1392
|
-
|
1327
|
+
if one_to_one
|
1328
|
+
if slice_range
|
1329
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range.begin]}
|
1330
|
+
end
|
1331
|
+
else
|
1332
|
+
rows.each{|o| o.associations[name] = o.associations[name][slice_range] || []}
|
1333
|
+
end
|
1393
1334
|
end
|
1394
1335
|
end
|
1395
1336
|
|
@@ -1492,20 +1433,6 @@ module Sequel
|
|
1492
1433
|
super
|
1493
1434
|
end
|
1494
1435
|
|
1495
|
-
# Clear the associations cache when refreshing
|
1496
|
-
def set_values(hash)
|
1497
|
-
@associations.clear if @associations
|
1498
|
-
super
|
1499
|
-
end
|
1500
|
-
|
1501
|
-
# Formally used internally by the associations code, like pk but doesn't raise
|
1502
|
-
# an Error if the model has no primary key. Not used any longer, deprecated.
|
1503
|
-
def pk_or_nil
|
1504
|
-
Sequel::Deprecation.deprecate('Model#pk_or_nil', 'There is no replacement')
|
1505
|
-
key = primary_key
|
1506
|
-
key.is_a?(Array) ? key.map{|k| @values[k]} : @values[key]
|
1507
|
-
end
|
1508
|
-
|
1509
1436
|
private
|
1510
1437
|
|
1511
1438
|
# Apply the association options such as :order and :limit to the given dataset, returning a modified dataset.
|
@@ -1559,10 +1486,12 @@ module Sequel
|
|
1559
1486
|
|
1560
1487
|
# Return the associated objects from the dataset, without association callbacks, reciprocals, and caching.
|
1561
1488
|
# Still apply the dynamic callback if present.
|
1562
|
-
def _load_associated_objects(opts, dynamic_opts=
|
1489
|
+
def _load_associated_objects(opts, dynamic_opts=OPTS)
|
1563
1490
|
if opts.can_have_associated_objects?(self)
|
1564
1491
|
if opts.returns_array?
|
1565
1492
|
_load_associated_object_array(opts, dynamic_opts)
|
1493
|
+
elsif load_with_primary_key_lookup?(opts, dynamic_opts)
|
1494
|
+
opts.associated_class.send(:primary_key_lookup, ((fk = opts[:key]).is_a?(Array) ? fk.map{|c| send(c)} : send(fk)))
|
1566
1495
|
else
|
1567
1496
|
_load_associated_object(opts, dynamic_opts)
|
1568
1497
|
end
|
@@ -1570,7 +1499,13 @@ module Sequel
|
|
1570
1499
|
[]
|
1571
1500
|
end
|
1572
1501
|
end
|
1573
|
-
|
1502
|
+
|
1503
|
+
# Clear the associations cache when refreshing
|
1504
|
+
def _refresh_set_values(hash)
|
1505
|
+
@associations.clear if @associations
|
1506
|
+
super
|
1507
|
+
end
|
1508
|
+
|
1574
1509
|
# Add the given associated object to the given association
|
1575
1510
|
def add_associated_object(opts, o, *args)
|
1576
1511
|
klass = opts.associated_class
|
@@ -1612,6 +1547,15 @@ module Sequel
|
|
1612
1547
|
a.uniq!
|
1613
1548
|
end
|
1614
1549
|
|
1550
|
+
# If a foreign key column value changes, clear the related
|
1551
|
+
# cached associations.
|
1552
|
+
def change_column_value(column, value)
|
1553
|
+
if assocs = model.autoreloading_associations[column]
|
1554
|
+
assocs.each{|a| associations.delete(a)}
|
1555
|
+
end
|
1556
|
+
super
|
1557
|
+
end
|
1558
|
+
|
1615
1559
|
# Save the associated object if the associated object needs a primary key
|
1616
1560
|
# and the associated object is new and does not have one. Raise an error if
|
1617
1561
|
# the object still does not have a primary key
|
@@ -1655,6 +1599,13 @@ module Sequel
|
|
1655
1599
|
end
|
1656
1600
|
end
|
1657
1601
|
|
1602
|
+
# Whether to use a simple primary key lookup on the associated class when loading.
|
1603
|
+
def load_with_primary_key_lookup?(opts, dynamic_opts)
|
1604
|
+
opts[:type] == :many_to_one &&
|
1605
|
+
!dynamic_opts[:callback] &&
|
1606
|
+
opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
|
1607
|
+
end
|
1608
|
+
|
1658
1609
|
# Remove all associated objects from the given association
|
1659
1610
|
def remove_all_associated_objects(opts, *args)
|
1660
1611
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") unless pk
|
@@ -1922,18 +1873,20 @@ module Sequel
|
|
1922
1873
|
# Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
|
1923
1874
|
# call +each+, it will yield plain hashes, each containing all columns from all the tables.
|
1924
1875
|
def eager_graph(*associations)
|
1925
|
-
|
1876
|
+
if eg = @opts[:eager_graph]
|
1926
1877
|
eg = eg.dup
|
1927
1878
|
[:requirements, :reflections, :reciprocals].each{|k| eg[k] = eg[k].dup}
|
1928
|
-
clone(:eager_graph=>eg)
|
1879
|
+
ds = clone(:eager_graph=>eg)
|
1880
|
+
ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations)
|
1929
1881
|
else
|
1930
1882
|
# Each of the following have a symbol key for the table alias, with the following values:
|
1931
1883
|
# :reciprocals - the reciprocal instance variable to use for this association
|
1932
1884
|
# :reflections - AssociationReflection instance related to this association
|
1933
1885
|
# :requirements - array of requirements for this association
|
1934
|
-
clone(:eager_graph=>{:requirements=>{}, :master=>alias_symbol(first_source), :reflections=>{}, :reciprocals=>{}, :cartesian_product_number=>0})
|
1886
|
+
ds = clone(:eager_graph=>{:requirements=>{}, :master=>alias_symbol(first_source), :reflections=>{}, :reciprocals=>{}, :cartesian_product_number=>0, :row_proc=>row_proc})
|
1887
|
+
ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).
|
1888
|
+
naked
|
1935
1889
|
end
|
1936
|
-
ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations)
|
1937
1890
|
end
|
1938
1891
|
|
1939
1892
|
# Do not attempt to split the result set into associations,
|
@@ -1941,7 +1894,11 @@ module Sequel
|
|
1941
1894
|
# want to use eager_graph as a shortcut to have all of the joins
|
1942
1895
|
# and aliasing set up, but want to do something else with the dataset.
|
1943
1896
|
def ungraphed
|
1944
|
-
super.clone(:eager_graph=>nil)
|
1897
|
+
ds = super.clone(:eager_graph=>nil)
|
1898
|
+
if (eg = @opts[:eager_graph]) && (rp = eg[:row_proc])
|
1899
|
+
ds.row_proc = rp
|
1900
|
+
end
|
1901
|
+
ds
|
1945
1902
|
end
|
1946
1903
|
|
1947
1904
|
protected
|
@@ -1977,11 +1934,7 @@ module Sequel
|
|
1977
1934
|
associations = assoc.is_a?(Array) ? assoc : [assoc]
|
1978
1935
|
end
|
1979
1936
|
end
|
1980
|
-
ds =
|
1981
|
-
loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>ta, :callback=>callback)
|
1982
|
-
else
|
1983
|
-
loader.call(ds, assoc_table_alias, ta)
|
1984
|
-
end
|
1937
|
+
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>ta, :callback=>callback)
|
1985
1938
|
ds = ds.order_more(*qualified_expression(r[:order], assoc_table_alias)) if r[:order] and r[:order_eager_graph]
|
1986
1939
|
eager_graph = ds.opts[:eager_graph]
|
1987
1940
|
eager_graph[:requirements][assoc_table_alias] = requirements.dup
|
@@ -2134,20 +2087,11 @@ module Sequel
|
|
2134
2087
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
2135
2088
|
eager_block, associations = pr_assoc
|
2136
2089
|
end
|
2137
|
-
|
2138
|
-
loader.call(:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map)
|
2139
|
-
else
|
2140
|
-
loader.call(key_hash, a, associations)
|
2141
|
-
end
|
2090
|
+
loader.call(:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map)
|
2142
2091
|
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} unless r[:after_load].empty?
|
2143
2092
|
end
|
2144
2093
|
end
|
2145
2094
|
|
2146
|
-
# Return plain hashes instead of calling the row_proc if eager_graph is being used.
|
2147
|
-
def graph_each(&block)
|
2148
|
-
@opts[:eager_graph] ? fetch_rows(select_sql, &block) : super
|
2149
|
-
end
|
2150
|
-
|
2151
2095
|
# Return a subquery expression for filering by a many_to_many association
|
2152
2096
|
def many_to_many_association_filter_expression(op, ref, obj)
|
2153
2097
|
lpks, lks, rks = ref.values_at(:left_primary_key_columns, :left_keys, :right_keys)
|
@@ -2474,7 +2418,8 @@ module Sequel
|
|
2474
2418
|
list.uniq!
|
2475
2419
|
if lo = limit_map[ta]
|
2476
2420
|
limit, offset = lo
|
2477
|
-
|
2421
|
+
offset ||= 0
|
2422
|
+
list.replace(list[(offset)..(limit ? (offset)+limit-1 : -1)])
|
2478
2423
|
end
|
2479
2424
|
list
|
2480
2425
|
elsif list
|