sequel 5.83.0 → 5.106.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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. data/doc/virtual_rows.rdoc +0 -265
@@ -2,13 +2,15 @@
2
2
 
3
3
  module Sequel
4
4
  class Model
5
+ # SEQUEL6: Remove Enumerable here, and send all Enumerable methods to dataset
6
+ # by default, with a plugin for the current behavior.
5
7
  extend Enumerable
6
8
  extend Inflections
7
9
 
8
10
  # Class methods for Sequel::Model that implement basic model functionality.
9
11
  #
10
12
  # * All of the following methods have class methods created that send the method
11
- # to the model's dataset: all, as_hash, avg, count, cross_join, distinct, each,
13
+ # to the model's dataset: all, any?, as_hash, as_set, avg, count, cross_join, distinct, each,
12
14
  # each_server, empty?, except, exclude, exclude_having, fetch_rows,
13
15
  # filter, first, first!, for_update, from, from_self, full_join, full_outer_join,
14
16
  # get, graph, grep, group, group_and_count, group_append, group_by, having, import,
@@ -17,7 +19,7 @@ module Sequel
17
19
  # natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
18
20
  # order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
19
21
  # right_outer_join, select, select_all, select_append, select_group, select_hash,
20
- # select_hash_groups, select_map, select_more, select_order_map, select_prepend, server,
22
+ # select_hash_groups, select_map, select_more, select_order_map, select_prepend, select_set, server,
21
23
  # single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
22
24
  # truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
23
25
  # where_each, where_single_value, with, with_recursive, with_sql
@@ -82,12 +84,16 @@ module Sequel
82
84
  # underlying table doesn't exist.
83
85
  attr_accessor :require_valid_table
84
86
 
87
+ # Whether the model uses a shape friendly design (initializing all potentially
88
+ # used instance variables to nil).
89
+ attr_reader :shape_friendly
90
+
85
91
  # Should be the literal primary key column name if this Model's table has a simple primary key, or
86
92
  # nil if the model has a compound primary key or no primary key.
87
93
  attr_reader :simple_pk
88
94
 
89
95
  # Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
90
- # or nil otherwise. This and simple_pk are used for an optimization in Model.[].
96
+ # or nil otherwise. This and simple_pk are used for an optimization in Model[].
91
97
  attr_reader :simple_table
92
98
 
93
99
  # Whether mass assigning via .create/.new/#set/#update should raise an error
@@ -184,7 +190,7 @@ module Sequel
184
190
  end
185
191
  end
186
192
 
187
- klass = Class.new(self)
193
+ klass = Sequel.set_temp_name(Class.new(self)){"Sequel::_Model(#{source.inspect})"}
188
194
 
189
195
  if source.is_a?(::Sequel::Database)
190
196
  klass.db = source
@@ -219,9 +225,7 @@ module Sequel
219
225
  # Requires that values be a hash where all keys are symbols. It
220
226
  # probably should not be used by external code.
221
227
  def call(values)
222
- o = allocate
223
- o.instance_variable_set(:@values, values)
224
- o
228
+ allocate.initialize_from_db(values)
225
229
  end
226
230
 
227
231
  # Clear the setter_methods cache
@@ -398,7 +402,7 @@ module Sequel
398
402
  end
399
403
 
400
404
  # Finds a single record according to the supplied filter.
401
- # You are encouraged to use Model.[] or Model.first instead of this method.
405
+ # You are encouraged to use Model[] or Model.first instead of this method.
402
406
  #
403
407
  # Artist.find(name: 'Bob')
404
408
  # # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
@@ -499,7 +503,13 @@ module Sequel
499
503
  unless @plugins.include?(m)
500
504
  @plugins << m
501
505
  m.apply(self, *args, &block) if m.respond_to?(:apply)
502
- extend(m::ClassMethods) if m.const_defined?(:ClassMethods, false)
506
+ if m.const_defined?(:ClassMethods, false)
507
+ class_methods = m::ClassMethods
508
+ extend(class_methods)
509
+ if class_methods.private_method_defined?(:each_model_instance_variable)
510
+ def_initialize_nil_instance_variables
511
+ end
512
+ end
503
513
  include(m::InstanceMethods) if m.const_defined?(:InstanceMethods, false)
504
514
  if m.const_defined?(:DatasetMethods, false)
505
515
  dataset_extend(m::DatasetMethods, :create_class_methods=>false)
@@ -533,10 +543,28 @@ module Sequel
533
543
  end
534
544
  end
535
545
 
546
+ # Return a qualified identifier or array of qualified identifiers for
547
+ # the model's primary key. Uses the given qualifier if provided, or
548
+ # the table_name otherwise. If the model does not have a primary key,
549
+ # raises an +Error+.
550
+ #
551
+ # Artist.order(Artist.qualified_primary_key)
552
+ # # SELECT * FROM artists ORDER BY artists.id
553
+ def qualified_primary_key(qualifier=table_name)
554
+ case key = @primary_key
555
+ when Symbol
556
+ SQL::QualifiedIdentifier.new(qualifier, key)
557
+ when Array
558
+ key.map{|k| SQL::QualifiedIdentifier.new(qualifier, k)}
559
+ else
560
+ raise(Error, "#{self} does not have a primary key")
561
+ end
562
+ end
563
+
536
564
  # Return a hash where the keys are qualified column references. Uses the given
537
565
  # qualifier if provided, or the table_name otherwise. This is useful if you
538
566
  # plan to join other tables to this table and you want the column references
539
- # to be qualified.
567
+ # to be qualified. If the model does not have a primary key, raises an +Error+.
540
568
  #
541
569
  # Artist.where(Artist.qualified_primary_key_hash(1))
542
570
  # # SELECT * FROM artists WHERE (artists.id = 1)
@@ -611,6 +639,13 @@ module Sequel
611
639
  self
612
640
  end
613
641
 
642
+ # Set instance variables used by instances of this model class.
643
+ # Only has an effect if shape_friendly is true.
644
+ def set_model_instance_variables(*ivs)
645
+ Plugins.model_instance_variables(singleton_class, *ivs)
646
+ def_initialize_nil_instance_variables
647
+ end
648
+
614
649
  # Sets the primary key for this model. You can use either a regular
615
650
  # or a composite primary key. To not use a primary key, set to nil
616
651
  # or use +no_primary_key+. On most adapters, Sequel can automatically
@@ -645,6 +680,12 @@ module Sequel
645
680
  @setter_methods || (@setter_methods = get_setter_methods)
646
681
  end
647
682
 
683
+ # Set whether the model should be shape friendly.
684
+ def shape_friendly=(v)
685
+ @shape_friendly = v
686
+ def_initialize_nil_instance_variables
687
+ end
688
+
648
689
  # Returns name of primary table for the dataset. If the table for the dataset
649
690
  # is aliased, returns the aliased name.
650
691
  #
@@ -677,7 +718,7 @@ module Sequel
677
718
  end
678
719
 
679
720
  # Add model methods that call dataset methods
680
- Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
721
+ Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:any?, :each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
681
722
 
682
723
  private
683
724
 
@@ -762,22 +803,90 @@ module Sequel
762
803
  end
763
804
  end
764
805
  end
806
+
807
+ # Module that the class methods that call dataset methods are kept in.
808
+ # This allows the methods to be overridden and call super with the
809
+ # default behavior.
810
+ def dataset_methods_module
811
+ return @dataset_methods_module if defined?(@dataset_methods_module)
812
+ mod_name = "#{name}::@dataset_methods_module"
813
+ Sequel.synchronize{@dataset_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
814
+ extend(@dataset_methods_module)
815
+ @dataset_methods_module
816
+ end
765
817
 
766
- # Define a model method that calls the dataset method with the same name,
767
- # only used for methods with names that can't be represented directly in
768
- # ruby code.
818
+ # Define a model method that calls the dataset method with the same name.
769
819
  def def_model_dataset_method(meth)
770
820
  return if respond_to?(meth, true)
771
821
 
822
+ mod = dataset_methods_module
823
+
772
824
  if meth.to_s =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
773
- instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
825
+ mod.module_eval(<<END, __FILE__, __LINE__ + 1)
826
+ def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end
827
+ ruby2_keywords :#{meth} if respond_to?(:ruby2_keywords, true)
828
+ END
774
829
  else
775
- define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
830
+ mod.send(:define_method, meth){|*args, &block| dataset.public_send(meth, *args, &block)}
831
+ # :nocov:
832
+ mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
833
+ # :nocov:
834
+ end
835
+ mod.send(:alias_method, meth, meth)
836
+ end
837
+
838
+ # Defines the private _initialize_nil_instance_variables method.
839
+ # If shape_friendly is true, defines a method that initials the
840
+ # model's instance variables to nil. If shape_friendly is not
841
+ # true, does nothing.
842
+ def def_initialize_nil_instance_variables
843
+ if @shape_friendly
844
+ ivs = []
845
+ each_model_instance_variable do |iv|
846
+ unless iv.match(/\A@[a-z_][a-z0-9_]*\z/)
847
+ raise Error, "invalid model instance variable used"
848
+ end
849
+
850
+ ivs << iv
851
+ end
852
+ ivs.uniq!
853
+ ivs = ivs.reverse.join(" = ")
854
+ new_method_content = "#{ivs} = nil"
855
+ new_from_db_method_content = "#{ivs} = @new = @modified = nil"
776
856
  end
777
- singleton_class.send(:alias_method, meth, meth)
778
- # :nocov:
779
- singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
780
- # :nocov:
857
+
858
+ class_eval(<<-RUBY, __FILE__, __LINE__+1)
859
+ def _initialize_nil_instance_variables
860
+ #{new_method_content}
861
+ end
862
+ def _initialize_from_db_nil_instance_variables
863
+ #{new_from_db_method_content}
864
+ end
865
+ RUBY
866
+ private :_initialize_nil_instance_variables, :_initialize_from_db_nil_instance_variables
867
+ alias_method :_initialize_nil_instance_variables, :_initialize_nil_instance_variables
868
+ alias_method :_initialize_from_db_nil_instance_variables, :_initialize_from_db_nil_instance_variables
869
+ nil
870
+ end
871
+
872
+ # Yield the default model instance variables. Designed only for
873
+ # use by def_initialize_nil_instance_variables.
874
+ def each_model_instance_variable
875
+ [
876
+ :@changed_columns,
877
+ :@errors,
878
+ :@raise_on_save_failure,
879
+ :@raise_on_typecast_failure,
880
+ :@require_modification,
881
+ :@server,
882
+ :@singleton_setter_added,
883
+ :@skip_validation_on_next_save,
884
+ :@strict_param_setting,
885
+ :@this,
886
+ :@typecast_empty_string_to_nil,
887
+ :@typecast_on_assignment,
888
+ :@use_transactions,
889
+ ].each{|iv| yield iv}
781
890
  end
782
891
 
783
892
  # Get the schema from the database, fall back on checking the columns
@@ -903,6 +1012,7 @@ module Sequel
903
1012
  :@require_valid_table=>nil,
904
1013
  :@restrict_primary_key=>nil,
905
1014
  :@setter_methods=>nil,
1015
+ :@shape_friendly=>nil,
906
1016
  :@simple_pk=>nil,
907
1017
  :@simple_table=>nil,
908
1018
  :@strict_param_setting=>nil,
@@ -943,7 +1053,10 @@ module Sequel
943
1053
  # Module that the class includes that holds methods the class adds for column accessors and
944
1054
  # associations so that the methods can be overridden with +super+.
945
1055
  def overridable_methods_module
946
- include(@overridable_methods_module = Module.new) unless @overridable_methods_module
1056
+ return @overridable_methods_module if defined?(@overridable_methods_module)
1057
+ mod_name = "#{name}::@overridable_methods_module"
1058
+ Sequel.synchronize{@overridable_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
1059
+ include(@overridable_methods_module)
947
1060
  @overridable_methods_module
948
1061
  end
949
1062
 
@@ -1057,7 +1170,7 @@ module Sequel
1057
1170
  # standard attr_writer method for modifying that instance variable.
1058
1171
  [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting,
1059
1172
  :raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_transactions].each do |meth|
1060
- class_eval("def #{meth}; !defined?(@#{meth}) ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)
1173
+ class_eval("def #{meth}; @#{meth}.nil? ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)
1061
1174
  attr_writer(meth)
1062
1175
  end
1063
1176
 
@@ -1098,13 +1211,23 @@ module Sequel
1098
1211
  # end
1099
1212
  def initialize(values = OPTS)
1100
1213
  @values = {}
1101
- @new = true
1102
- @modified = true
1214
+ @new = @modified = true
1215
+ _initialize_nil_instance_variables
1216
+
1103
1217
  initialize_set(values)
1104
1218
  _clear_changed_columns(:initialize)
1105
1219
  yield self if defined?(yield)
1106
1220
  end
1107
1221
 
1222
+ # Initialize a new record using values retrieved from a database.
1223
+ # This should not be called directly, only via Model.call.
1224
+ def initialize_from_db(values) # :nodoc:
1225
+ @values = values
1226
+ _initialize_from_db_nil_instance_variables
1227
+
1228
+ self
1229
+ end
1230
+
1108
1231
  # Returns value of the column's attribute.
1109
1232
  #
1110
1233
  # Artist[1][:id] #=> 1
@@ -1311,7 +1434,7 @@ module Sequel
1311
1434
  # Returns a string representation of the model instance including
1312
1435
  # the class name and values.
1313
1436
  def inspect
1314
- "#<#{model.name} @values=#{inspect_values}>"
1437
+ "#<#{inspect_prefix} @values=#{inspect_values}>"
1315
1438
  end
1316
1439
 
1317
1440
  # Returns the keys in +values+. May not include all column names.
@@ -1323,17 +1446,16 @@ module Sequel
1323
1446
  @values.keys
1324
1447
  end
1325
1448
 
1326
- # Refresh this record using +for_update+ (by default, or the specified style when given)
1449
+ # Refresh this record using +:update+ lock style (by default, or the specified style when given),
1327
1450
  # unless this is a new record. Returns self. This can be used to make sure no other
1328
- # process is updating the record at the same time.
1451
+ # process can modify the record during the transaction containing this call. Using
1452
+ # this method only makes sense inside transactions.
1329
1453
  #
1330
1454
  # If style is a string, it will be used directly. You should never pass a string
1331
1455
  # to this method that is derived from user input, as that can lead to
1332
1456
  # SQL injection.
1333
1457
  #
1334
- # A symbol may be used for database independent locking behavior, but
1335
- # all supported symbols have separate methods (e.g. for_update).
1336
- #
1458
+ # A symbol may be used if the adapter supports that lock style.
1337
1459
  #
1338
1460
  # a = Artist[1]
1339
1461
  # Artist.db.transaction do
@@ -1343,7 +1465,7 @@ module Sequel
1343
1465
  #
1344
1466
  # a = Artist[2]
1345
1467
  # Artist.db.transaction do
1346
- # a.lock!('FOR NO KEY UPDATE')
1468
+ # a.lock!(:no_key_update)
1347
1469
  # a.update(name: 'B')
1348
1470
  # end
1349
1471
  def lock!(style=:update)
@@ -1409,7 +1531,7 @@ module Sequel
1409
1531
  # Artist.new.new? # => true
1410
1532
  # Artist[1].new? # => false
1411
1533
  def new?
1412
- defined?(@new) ? @new : (@new = false)
1534
+ @new || false
1413
1535
  end
1414
1536
 
1415
1537
  # Returns the primary key value identifying the model instance.
@@ -1465,6 +1587,20 @@ module Sequel
1465
1587
  refresh
1466
1588
  end
1467
1589
 
1590
+ # Remove a key from the instances values, and return the value
1591
+ # of the key.
1592
+ #
1593
+ # a = Album[1]
1594
+ # a.values
1595
+ # # => {id: 1, artist_id: 2}
1596
+ # a.remove_key!(:artist_id)
1597
+ # # => 2
1598
+ # a.values
1599
+ # # => {id: 1}
1600
+ def remove_key!(key)
1601
+ @values.delete(key)
1602
+ end
1603
+
1468
1604
  # Creates or updates the record, after making sure the record
1469
1605
  # is valid and before hooks execute successfully. Fails if:
1470
1606
  #
@@ -1597,7 +1733,7 @@ module Sequel
1597
1733
  @skip_validation_on_next_save = true
1598
1734
  end
1599
1735
 
1600
- # Returns (naked) dataset that should return only this instance.
1736
+ # Returns naked dataset that should return only the row related to this instance.
1601
1737
  #
1602
1738
  # Artist[1].this
1603
1739
  # # SELECT * FROM artists WHERE (id = 1) LIMIT 1
@@ -1994,7 +2130,12 @@ module Sequel
1994
2130
  set(h) unless h.empty?
1995
2131
  end
1996
2132
 
1997
- # Default inspection output for the values hash, overwrite to change what #inspect displays.
2133
+ # Default inspect output for the inspect, by default, just showing the class.
2134
+ def inspect_prefix
2135
+ model.name
2136
+ end
2137
+
2138
+ # Default inspect output for the values hash, overwrite to change what #inspect displays.
1998
2139
  def inspect_values
1999
2140
  @values.inspect
2000
2141
  end
@@ -2238,7 +2379,7 @@ module Sequel
2238
2379
  # Return the dataset ordered by the model's primary key. This should not
2239
2380
  # be used if the model does not have a primary key.
2240
2381
  def _force_primary_key_order
2241
- cached_dataset(:_pk_order_ds){order(*model.primary_key)}
2382
+ cached_dataset(:_pk_order_ds){order(*unambiguous_primary_key)}
2242
2383
  end
2243
2384
 
2244
2385
  # If the dataset is not already ordered, and the model has a primary key,
@@ -2266,6 +2407,15 @@ module Sequel
2266
2407
  end
2267
2408
  end
2268
2409
 
2410
+ # The primary key for the dataset's model, qualified if the dataset is joined.
2411
+ def unambiguous_primary_key
2412
+ if joined_dataset?
2413
+ model.qualified_primary_key
2414
+ else
2415
+ model.primary_key
2416
+ end
2417
+ end
2418
+
2269
2419
  def non_sql_option?(key)
2270
2420
  super || key == :model
2271
2421
  end
@@ -2273,6 +2423,7 @@ module Sequel
2273
2423
 
2274
2424
  extend ClassMethods
2275
2425
  plugin self
2426
+ def_initialize_nil_instance_variables
2276
2427
 
2277
2428
  singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
2278
2429
  # :nocov:
@@ -2280,5 +2431,17 @@ module Sequel
2280
2431
  # :nocov:
2281
2432
  singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
2282
2433
  end
2434
+
2435
+ # :nocov:
2436
+ if defined?(Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER) && Sequel::Postgres::SEQUEL_PG_VERSION_INTEGER >= 11800
2437
+ # Automatically optimize model loading when sequel/core was loaded,
2438
+ # then sequel/adapters/postgres (with sequel_pg), then sequel/model
2439
+ begin
2440
+ require "sequel_pg/model"
2441
+ rescue LoadError
2442
+ # nothing
2443
+ end
2444
+ end
2445
+ # :nocov:
2283
2446
  end
2284
2447
  end
@@ -38,7 +38,8 @@ module Sequel
38
38
  end
39
39
 
40
40
  # Add method to +mod+ that overrides inherited_instance_variables to include the
41
- # values in this hash.
41
+ # values in this hash. These affects how class instance variables will be treated
42
+ # during subclassing.
42
43
  def self.inherited_instance_variables(mod, hash)
43
44
  mod.send(:define_method, :inherited_instance_variables) do ||
44
45
  super().merge!(hash)
@@ -46,6 +47,16 @@ module Sequel
46
47
  mod.send(:private, :inherited_instance_variables)
47
48
  end
48
49
 
50
+ # Sets the model instance variables used by the plugin. These instance variables
51
+ # will be initialized to nil for created model objects.
52
+ def self.model_instance_variables(mod, *ivs)
53
+ mod.send(:define_method, :each_model_instance_variable) do |&block|
54
+ super(&block)
55
+ ivs.each(&block)
56
+ end
57
+ mod.send(:private, :each_model_instance_variable)
58
+ end
59
+
49
60
  # Add method to +mod+ that overrides set_dataset to call the method afterward.
50
61
  def self.after_set_dataset(mod, meth)
51
62
  mod.send(:define_method, :set_dataset) do |*a|
data/lib/sequel/model.rb CHANGED
@@ -55,6 +55,7 @@ module Sequel
55
55
  @require_valid_table = true
56
56
  @restrict_primary_key = true
57
57
  @setter_methods = nil
58
+ @shape_friendly = false
58
59
  @simple_pk = nil
59
60
  @simple_table = nil
60
61
  @strict_param_setting = true
@@ -26,6 +26,10 @@ module Sequel
26
26
  # # Make the Album instances record accessed columns
27
27
  # Album.plugin :accessed_columns
28
28
  module AccessedColumns
29
+ module ClassMethods
30
+ Plugins.model_instance_variables(self, :@accessed_columns)
31
+ end
32
+
29
33
  module InstanceMethods
30
34
  # Record the column access before retrieving the value.
31
35
  def [](c)
@@ -29,6 +29,8 @@ module Sequel
29
29
  module ClassMethods
30
30
  include ::ActiveModel::Naming
31
31
 
32
+ Plugins.model_instance_variables(self, :@destroyed, :@rollback_checker)
33
+
32
34
  # Cache model_name and to_partial path value before freezing.
33
35
  def freeze
34
36
  model_name
@@ -58,13 +60,10 @@ module Sequel
58
60
 
59
61
  # False if the object is new? or has been destroyed, true otherwise.
60
62
  def persisted?
61
- return false if new?
62
- return false if defined?(@destroyed)
63
+ return false if new? || @destroyed
63
64
 
64
- if defined?(@rollback_checker)
65
- if @rollback_checker.call
66
- return false
67
- end
65
+ if @rollback_checker && @rollback_checker.call
66
+ return false
68
67
  end
69
68
 
70
69
  true
@@ -67,6 +67,8 @@ module Sequel
67
67
  # Album.plugin :association_pks
68
68
  module AssociationPks
69
69
  module ClassMethods
70
+ Plugins.model_instance_variables(self, :@_association_pks)
71
+
70
72
  private
71
73
 
72
74
  # Define a association_pks method using the block for the association reflection
@@ -131,6 +131,8 @@ module Sequel
131
131
  end
132
132
 
133
133
  module ClassMethods
134
+ Plugins.model_instance_variables(self, :@_skip_auto_validations)
135
+
134
136
  # The columns with automatic no_null_byte validations
135
137
  attr_reader :auto_validate_no_null_byte_columns
136
138
 
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'constraint_validations'
4
+ require_relative 'class_table_inheritance'
5
+
6
+ module Sequel
7
+ module Plugins
8
+ # = Overview
9
+ #
10
+ # The class_table_inheritance_constraint_validations plugin extends the
11
+ # constraint_validations plugin to work correctly with the
12
+ # class_table_inheritance plugin. It ensures that constraint_validations
13
+ # are loaded from all tables in the class table inheritance hierarchy,
14
+ # not just the base table.
15
+ #
16
+ # = Example
17
+ #
18
+ # For example, with this hierarchy, where each model has its own table with
19
+ # constraint validations:
20
+ #
21
+ # Employee
22
+ # / \
23
+ # Staff Manager
24
+ # |
25
+ # Executive
26
+ #
27
+ # # Loads constraint_validations from the employees table
28
+ # class Employee < Sequel::Model
29
+ # plugin :class_table_inheritance
30
+ # plugin :constraint_validations
31
+ # plugin :class_table_inheritance_constraint_validations
32
+ # end
33
+ #
34
+ # # Loads constraint_validations from managers and employees tables
35
+ # class Manager < Employee
36
+ # end
37
+ #
38
+ # # Loads constraint_validations from executives, managers, and
39
+ # # employees tables
40
+ # class Executive < Manager
41
+ # end
42
+ #
43
+ # # Loads constraint_validations from staff and employees tables
44
+ # class Staff < Employee
45
+ # end
46
+ module ClassTableInheritanceConstraintValidations
47
+ def self.apply(model)
48
+ unless ConstraintValidations::InstanceMethods > model && ClassTableInheritance::InstanceMethods > model
49
+ raise Error, "must load the constraint_validations and class_table_inheritance plugins into #{model} before loading class_table_inheritance_constraint_validations plugin"
50
+ end
51
+ end
52
+
53
+ module ClassMethods
54
+ private
55
+
56
+ def inherited(subclass)
57
+ super
58
+
59
+ # constraint_validations will parse_constraint_validations in the
60
+ # classes after_set_dataset hook. That runs before cti_tables are
61
+ # updated for subclasses in class_table_inheritance's inherited
62
+ # so re-parsing them here.
63
+ subclass.send(:parse_constraint_validations)
64
+ end
65
+
66
+ def parse_constraint_validations_dataset
67
+ reflections = {}
68
+ allow_missing_columns = db_schema.select{|col, sch| sch[:allow_null] == false && nil != sch[:default]}.map(&:first)
69
+ hash = Sequel.synchronize{db.constraint_validations}
70
+ cv = []
71
+ ds = @dataset.with_quote_identifiers(false)
72
+ cti_tables.each do |table_name|
73
+ table_name = ds.literal(table_name)
74
+ cv += (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections, allow_missing_columns)}
75
+ end
76
+ @constraint_validations = cv
77
+ @constraint_validation_reflections = reflections
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -15,6 +15,10 @@ module Sequel
15
15
  # # Make the Album class store the columns hash used for updating
16
16
  # Album.plugin :columns_updated
17
17
  module ColumnsUpdated
18
+ module ClassMethods
19
+ Plugins.model_instance_variables(self, :@columns_updated)
20
+ end
21
+
18
22
  module InstanceMethods
19
23
  private
20
24
 
@@ -61,11 +61,13 @@ module Sequel
61
61
  def self.apply(model)
62
62
  model.instance_exec do
63
63
  @compositions = {}
64
- include(@composition_module ||= Module.new)
64
+ include(@composition_module ||= Sequel.set_temp_name(Module.new){"#{name}::@composition_module"})
65
65
  end
66
66
  end
67
67
 
68
68
  module ClassMethods
69
+ Plugins.model_instance_variables(self, :@compositions)
70
+
69
71
  # A hash with composition name keys and composition reflection
70
72
  # hash values.
71
73
  attr_reader :compositions