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.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +114 -0
  3. data/Rakefile +10 -7
  4. data/doc/association_basics.rdoc +25 -23
  5. data/doc/code_order.rdoc +7 -0
  6. data/doc/core_extensions.rdoc +0 -10
  7. data/doc/object_model.rdoc +4 -1
  8. data/doc/querying.rdoc +3 -3
  9. data/doc/release_notes/4.0.0.txt +262 -0
  10. data/doc/security.rdoc +0 -28
  11. data/doc/testing.rdoc +8 -14
  12. data/lib/sequel/adapters/ado.rb +7 -11
  13. data/lib/sequel/adapters/ado/access.rb +8 -8
  14. data/lib/sequel/adapters/ado/mssql.rb +4 -4
  15. data/lib/sequel/adapters/amalgalite.rb +6 -6
  16. data/lib/sequel/adapters/cubrid.rb +7 -7
  17. data/lib/sequel/adapters/db2.rb +5 -9
  18. data/lib/sequel/adapters/dbi.rb +2 -6
  19. data/lib/sequel/adapters/do.rb +4 -4
  20. data/lib/sequel/adapters/firebird.rb +4 -4
  21. data/lib/sequel/adapters/ibmdb.rb +8 -8
  22. data/lib/sequel/adapters/informix.rb +2 -10
  23. data/lib/sequel/adapters/jdbc.rb +17 -17
  24. data/lib/sequel/adapters/jdbc/as400.rb +2 -2
  25. data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
  26. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  27. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  29. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  30. data/lib/sequel/adapters/jdbc/informix.rb +1 -1
  31. data/lib/sequel/adapters/jdbc/mssql.rb +2 -2
  32. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  33. data/lib/sequel/adapters/jdbc/oracle.rb +5 -1
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
  35. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  36. data/lib/sequel/adapters/jdbc/transactions.rb +3 -3
  37. data/lib/sequel/adapters/mock.rb +7 -7
  38. data/lib/sequel/adapters/mysql.rb +3 -3
  39. data/lib/sequel/adapters/mysql2.rb +4 -4
  40. data/lib/sequel/adapters/odbc.rb +2 -6
  41. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  42. data/lib/sequel/adapters/openbase.rb +1 -5
  43. data/lib/sequel/adapters/oracle.rb +13 -17
  44. data/lib/sequel/adapters/postgres.rb +20 -25
  45. data/lib/sequel/adapters/shared/cubrid.rb +3 -3
  46. data/lib/sequel/adapters/shared/db2.rb +2 -2
  47. data/lib/sequel/adapters/shared/firebird.rb +7 -7
  48. data/lib/sequel/adapters/shared/mssql.rb +9 -9
  49. data/lib/sequel/adapters/shared/mysql.rb +29 -13
  50. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +7 -7
  51. data/lib/sequel/adapters/shared/oracle.rb +22 -13
  52. data/lib/sequel/adapters/shared/postgres.rb +61 -46
  53. data/lib/sequel/adapters/shared/sqlite.rb +9 -9
  54. data/lib/sequel/adapters/sqlite.rb +17 -11
  55. data/lib/sequel/adapters/swift.rb +3 -3
  56. data/lib/sequel/adapters/swift/mysql.rb +1 -1
  57. data/lib/sequel/adapters/swift/sqlite.rb +1 -1
  58. data/lib/sequel/adapters/tinytds.rb +8 -8
  59. data/lib/sequel/ast_transformer.rb +3 -1
  60. data/lib/sequel/connection_pool.rb +4 -2
  61. data/lib/sequel/connection_pool/sharded_single.rb +2 -2
  62. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -5
  63. data/lib/sequel/connection_pool/threaded.rb +7 -7
  64. data/lib/sequel/core.rb +4 -67
  65. data/lib/sequel/database.rb +1 -0
  66. data/lib/sequel/database/connecting.rb +2 -8
  67. data/lib/sequel/database/dataset.rb +2 -7
  68. data/lib/sequel/database/dataset_defaults.rb +0 -18
  69. data/lib/sequel/database/features.rb +4 -4
  70. data/lib/sequel/database/misc.rb +6 -8
  71. data/lib/sequel/database/query.rb +5 -61
  72. data/lib/sequel/database/schema_generator.rb +22 -20
  73. data/lib/sequel/database/schema_methods.rb +48 -20
  74. data/lib/sequel/database/transactions.rb +7 -17
  75. data/lib/sequel/dataset.rb +2 -0
  76. data/lib/sequel/dataset/actions.rb +23 -91
  77. data/lib/sequel/dataset/features.rb +1 -4
  78. data/lib/sequel/dataset/graph.rb +3 -47
  79. data/lib/sequel/dataset/misc.rb +4 -33
  80. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  81. data/lib/sequel/dataset/query.rb +116 -240
  82. data/lib/sequel/dataset/sql.rb +19 -97
  83. data/lib/sequel/deprecated.rb +0 -16
  84. data/lib/sequel/exceptions.rb +0 -3
  85. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  86. data/lib/sequel/extensions/columns_introspection.rb +1 -12
  87. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  88. data/lib/sequel/extensions/core_extensions.rb +0 -9
  89. data/lib/sequel/extensions/date_arithmetic.rb +1 -2
  90. data/lib/sequel/extensions/graph_each.rb +11 -0
  91. data/lib/sequel/extensions/migration.rb +5 -5
  92. data/lib/sequel/extensions/null_dataset.rb +11 -13
  93. data/lib/sequel/extensions/pagination.rb +3 -6
  94. data/lib/sequel/extensions/pg_array.rb +6 -4
  95. data/lib/sequel/extensions/pg_array_ops.rb +35 -1
  96. data/lib/sequel/extensions/pg_json.rb +12 -2
  97. data/lib/sequel/extensions/pg_json_ops.rb +266 -0
  98. data/lib/sequel/extensions/pg_range.rb +2 -2
  99. data/lib/sequel/extensions/pg_range_ops.rb +0 -8
  100. data/lib/sequel/extensions/pg_row.rb +2 -2
  101. data/lib/sequel/extensions/pretty_table.rb +0 -4
  102. data/lib/sequel/extensions/query.rb +3 -8
  103. data/lib/sequel/extensions/schema_caching.rb +0 -7
  104. data/lib/sequel/extensions/schema_dumper.rb +10 -17
  105. data/lib/sequel/extensions/select_remove.rb +0 -4
  106. data/lib/sequel/extensions/set_overrides.rb +28 -0
  107. data/lib/sequel/extensions/to_dot.rb +6 -10
  108. data/lib/sequel/model.rb +6 -7
  109. data/lib/sequel/model/associations.rb +127 -182
  110. data/lib/sequel/model/base.rb +88 -211
  111. data/lib/sequel/model/errors.rb +0 -13
  112. data/lib/sequel/model/plugins.rb +2 -2
  113. data/lib/sequel/no_core_ext.rb +0 -1
  114. data/lib/sequel/plugins/after_initialize.rb +11 -17
  115. data/lib/sequel/plugins/association_autoreloading.rb +1 -47
  116. data/lib/sequel/plugins/association_dependencies.rb +2 -2
  117. data/lib/sequel/plugins/auto_validations.rb +2 -8
  118. data/lib/sequel/plugins/blacklist_security.rb +32 -2
  119. data/lib/sequel/plugins/caching.rb +1 -1
  120. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  121. data/lib/sequel/plugins/composition.rb +10 -8
  122. data/lib/sequel/plugins/constraint_validations.rb +2 -2
  123. data/lib/sequel/plugins/dataset_associations.rb +4 -0
  124. data/lib/sequel/plugins/defaults_setter.rb +8 -6
  125. data/lib/sequel/plugins/dirty.rb +6 -6
  126. data/lib/sequel/plugins/force_encoding.rb +13 -8
  127. data/lib/sequel/plugins/hook_class_methods.rb +1 -7
  128. data/lib/sequel/plugins/json_serializer.rb +13 -74
  129. data/lib/sequel/plugins/lazy_attributes.rb +2 -4
  130. data/lib/sequel/plugins/list.rb +1 -1
  131. data/lib/sequel/plugins/many_through_many.rb +4 -11
  132. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +1 -49
  133. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  134. data/lib/sequel/plugins/optimistic_locking.rb +3 -5
  135. data/lib/sequel/plugins/pg_array_associations.rb +453 -0
  136. data/lib/sequel/plugins/pg_typecast_on_load.rb +23 -7
  137. data/lib/sequel/plugins/prepared_statements.rb +1 -1
  138. data/lib/sequel/plugins/prepared_statements_associations.rb +20 -14
  139. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -2
  140. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  141. data/lib/sequel/plugins/serialization.rb +5 -4
  142. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  143. data/lib/sequel/plugins/sharding.rb +7 -1
  144. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  145. data/lib/sequel/plugins/timestamps.rb +1 -1
  146. data/lib/sequel/plugins/touch.rb +2 -2
  147. data/lib/sequel/plugins/tree.rb +1 -1
  148. data/lib/sequel/plugins/typecast_on_load.rb +19 -4
  149. data/lib/sequel/plugins/validation_class_methods.rb +0 -30
  150. data/lib/sequel/plugins/validation_helpers.rb +13 -31
  151. data/lib/sequel/plugins/xml_serializer.rb +18 -57
  152. data/lib/sequel/sql.rb +20 -22
  153. data/lib/sequel/version.rb +2 -2
  154. data/spec/adapters/db2_spec.rb +14 -23
  155. data/spec/adapters/firebird_spec.rb +25 -29
  156. data/spec/adapters/informix_spec.rb +11 -14
  157. data/spec/adapters/mssql_spec.rb +71 -77
  158. data/spec/adapters/mysql_spec.rb +165 -172
  159. data/spec/adapters/oracle_spec.rb +36 -39
  160. data/spec/adapters/postgres_spec.rb +175 -100
  161. data/spec/adapters/spec_helper.rb +13 -11
  162. data/spec/adapters/sqlite_spec.rb +36 -44
  163. data/spec/core/connection_pool_spec.rb +2 -1
  164. data/spec/core/database_spec.rb +55 -55
  165. data/spec/core/dataset_spec.rb +45 -249
  166. data/spec/core/deprecated_spec.rb +0 -8
  167. data/spec/core/expression_filters_spec.rb +23 -5
  168. data/spec/core/object_graph_spec.rb +4 -66
  169. data/spec/core/schema_spec.rb +35 -12
  170. data/spec/core/spec_helper.rb +3 -2
  171. data/spec/core_extensions_spec.rb +17 -19
  172. data/spec/extensions/arbitrary_servers_spec.rb +2 -3
  173. data/spec/extensions/association_dependencies_spec.rb +14 -14
  174. data/spec/extensions/auto_validations_spec.rb +7 -0
  175. data/spec/extensions/blacklist_security_spec.rb +5 -5
  176. data/spec/extensions/blank_spec.rb +2 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  178. data/spec/extensions/columns_introspection_spec.rb +2 -29
  179. data/spec/extensions/composition_spec.rb +10 -17
  180. data/spec/extensions/core_refinements_spec.rb +5 -1
  181. data/spec/extensions/dataset_associations_spec.rb +18 -0
  182. data/spec/extensions/date_arithmetic_spec.rb +2 -2
  183. data/spec/extensions/defaults_setter_spec.rb +9 -9
  184. data/spec/extensions/dirty_spec.rb +0 -5
  185. data/spec/extensions/eval_inspect_spec.rb +2 -0
  186. data/spec/extensions/force_encoding_spec.rb +2 -18
  187. data/spec/extensions/hash_aliases_spec.rb +8 -0
  188. data/spec/extensions/hook_class_methods_spec.rb +39 -58
  189. data/spec/extensions/inflector_spec.rb +2 -0
  190. data/spec/extensions/instance_filters_spec.rb +8 -8
  191. data/spec/extensions/json_serializer_spec.rb +1 -41
  192. data/spec/extensions/list_spec.rb +1 -1
  193. data/spec/extensions/many_through_many_spec.rb +106 -109
  194. data/spec/extensions/migration_spec.rb +2 -0
  195. data/spec/extensions/named_timezones_spec.rb +1 -0
  196. data/spec/extensions/pg_array_associations_spec.rb +603 -0
  197. data/spec/extensions/pg_array_ops_spec.rb +25 -0
  198. data/spec/extensions/pg_array_spec.rb +9 -1
  199. data/spec/extensions/pg_hstore_ops_spec.rb +13 -0
  200. data/spec/extensions/pg_hstore_spec.rb +1 -0
  201. data/spec/extensions/pg_json_ops_spec.rb +131 -0
  202. data/spec/extensions/pg_json_spec.rb +10 -4
  203. data/spec/extensions/pg_range_ops_spec.rb +2 -5
  204. data/spec/extensions/pg_range_spec.rb +6 -2
  205. data/spec/extensions/pg_row_ops_spec.rb +2 -0
  206. data/spec/extensions/prepared_statements_associations_spec.rb +26 -5
  207. data/spec/extensions/rcte_tree_spec.rb +15 -15
  208. data/spec/extensions/schema_dumper_spec.rb +0 -1
  209. data/spec/extensions/schema_spec.rb +9 -9
  210. data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
  211. data/spec/extensions/serialization_spec.rb +18 -29
  212. data/spec/extensions/set_overrides_spec.rb +4 -0
  213. data/spec/extensions/{many_to_one_pk_lookup_spec.rb → shared_caching_spec.rb} +1 -4
  214. data/spec/extensions/single_table_inheritance_spec.rb +4 -4
  215. data/spec/extensions/spec_helper.rb +8 -9
  216. data/spec/extensions/sql_expr_spec.rb +2 -0
  217. data/spec/extensions/string_date_time_spec.rb +2 -0
  218. data/spec/extensions/string_stripper_spec.rb +2 -0
  219. data/spec/extensions/tactical_eager_loading_spec.rb +12 -12
  220. data/spec/extensions/thread_local_timezones_spec.rb +2 -0
  221. data/spec/extensions/timestamps_spec.rb +1 -1
  222. data/spec/extensions/to_dot_spec.rb +1 -1
  223. data/spec/extensions/touch_spec.rb +24 -24
  224. data/spec/extensions/tree_spec.rb +7 -7
  225. data/spec/extensions/typecast_on_load_spec.rb +8 -1
  226. data/spec/extensions/update_primary_key_spec.rb +10 -10
  227. data/spec/extensions/validation_class_methods_spec.rb +10 -39
  228. data/spec/extensions/validation_helpers_spec.rb +29 -47
  229. data/spec/extensions/xml_serializer_spec.rb +1 -23
  230. data/spec/integration/associations_test.rb +231 -40
  231. data/spec/integration/database_test.rb +1 -1
  232. data/spec/integration/dataset_test.rb +64 -64
  233. data/spec/integration/eager_loader_test.rb +28 -28
  234. data/spec/integration/migrator_test.rb +1 -1
  235. data/spec/integration/model_test.rb +2 -2
  236. data/spec/integration/plugin_test.rb +21 -21
  237. data/spec/integration/prepared_statement_test.rb +7 -7
  238. data/spec/integration/schema_test.rb +115 -110
  239. data/spec/integration/spec_helper.rb +17 -27
  240. data/spec/integration/timezone_test.rb +1 -1
  241. data/spec/integration/transaction_test.rb +10 -10
  242. data/spec/integration/type_test.rb +2 -2
  243. data/spec/model/association_reflection_spec.rb +2 -28
  244. data/spec/model/associations_spec.rb +239 -188
  245. data/spec/model/base_spec.rb +27 -68
  246. data/spec/model/dataset_methods_spec.rb +4 -4
  247. data/spec/model/eager_loading_spec.rb +160 -172
  248. data/spec/model/hooks_spec.rb +62 -79
  249. data/spec/model/model_spec.rb +36 -51
  250. data/spec/model/plugins_spec.rb +5 -19
  251. data/spec/model/record_spec.rb +125 -151
  252. data/spec/model/spec_helper.rb +8 -6
  253. data/spec/model/validations_spec.rb +4 -17
  254. data/spec/spec_config.rb +2 -10
  255. metadata +50 -56
  256. data/lib/sequel/deprecated_core_extensions.rb +0 -135
  257. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -185
  258. data/lib/sequel/extensions/pg_statement_cache.rb +0 -318
  259. data/lib/sequel/plugins/identity_map.rb +0 -260
  260. data/lib/sequel_core.rb +0 -2
  261. data/lib/sequel_model.rb +0 -2
  262. data/spec/extensions/association_autoreloading_spec.rb +0 -102
  263. data/spec/extensions/identity_map_spec.rb +0 -337
  264. data/spec/extensions/pg_auto_parameterize_spec.rb +0 -70
  265. data/spec/extensions/pg_statement_cache_spec.rb +0 -208
  266. data/spec/rcov.opts +0 -8
  267. 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
- Sequel::Deprecation.deprecate('Loading the query extension globally', "Please use Database/Dataset#extension to load the extension into this dataset") unless is_a?(DatasetQuery)
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
@@ -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 = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy,
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, :@default_eager_limit_strategy=>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 = nil
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 = true
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
- if deprecated_reciprocal_association?(assoc_reflect)
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
- Sequel::Deprecation.deprecate("Multiple reciprocal association candidates found for #{self[:name]} association (#{possible_recips.map{|r| r[:name]}.join(', ')}). Choosing the first candidate is", "Please explicitly specify the reciprocal option for the #{self[:name]} association") if possible_recips.size >= 2
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
- self[:primary_key]
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
- case s = self[:eager_limit_strategy]
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
- [1, nil]
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
- # The default :eager_limit_strategy option to use for *_many associations (default: nil)
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 associations via
827
- # the +eager+ method. For one_to_one associations, no strategy is used by default, and
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 = {}, &block)
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={}, &block)
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={}, &block)
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={}, &block)
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={}, &block)
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
- ds.where(rn => (offset...(offset+limit)))
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={}, &block)
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={}, &block)
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
- case opts.eager_limit_strategy
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
- limit, offset = opts.limit_and_offset
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 :correlated_subquery
1368
- ds = apply_correlated_subquery_eager_limit_strategy(ds, opts) do |xds|
1369
- xds.where(opts.associated_object_keys.map{|k| [SQL::QualifiedIdentifier.new(xds.first_source_alias, k), SQL::QualifiedIdentifier.new(xds.first_source_table, k)]})
1370
- end
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 one_to_one
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
- limit, offset = opts.limit_and_offset
1392
- rows.each{|o| o.associations[name] = o.associations[name].slice(offset||0, limit) || []}
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
- ds = if eg = @opts[:eager_graph]
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 = if loader.arity == 1
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
- if loader.arity == 1
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
- list.replace(list[offset||0, limit])
2421
+ offset ||= 0
2422
+ list.replace(list[(offset)..(limit ? (offset)+limit-1 : -1)])
2478
2423
  end
2479
2424
  list
2480
2425
  elsif list