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
@@ -146,6 +146,27 @@ module Sequel
146
146
  ds
147
147
  end
148
148
 
149
+ def lateral_subquery_filter_limit_strategy_conditions_key
150
+ qualify(reverse_edges.first[:table], reverse_edges.first[:left])
151
+ end
152
+
153
+ def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
154
+ ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
155
+ end
156
+
157
+ def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
158
+ first_edge, *remaining_edges = edges
159
+ filter_ds = ds.db.from(first_edge[:table]).
160
+ select(*qualify(first_edge[:table], first_edge[:right])).
161
+ where(lateral_subquery_filter_limit_strategy_conditions(obj))
162
+
163
+ remaining_edges.each do |edge|
164
+ filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
165
+ end
166
+
167
+ ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
168
+ end
169
+
149
170
  # Make sure to use unique table aliases when lazy loading or eager loading
150
171
  def calculate_reverse_edge_aliases(reverse_edges)
151
172
  aliases = [associated_class.table_name]
@@ -34,6 +34,8 @@ module Sequel
34
34
  # Album.plugin :modification_detection
35
35
  module ModificationDetection
36
36
  module ClassMethods
37
+ Plugins.model_instance_variables(self, :@values_hashes)
38
+
37
39
  # Calculate the hashes for all of the column values, so that they
38
40
  # can be compared later to determine if the column value has changed.
39
41
  def call(_)
@@ -55,7 +55,7 @@ module Sequel
55
55
  def _update_without_checking(columns)
56
56
  ds = _update_dataset
57
57
  lc = model.lock_column
58
- rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
58
+ rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
59
59
  values[lc] = rows.first[lc] unless rows.empty?
60
60
  rows.length
61
61
  end
@@ -129,7 +129,7 @@ module Sequel
129
129
  #
130
130
  # If a block is provided, it is used to set the :reject_if option.
131
131
  def nested_attributes(*associations, &block)
132
- include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module
132
+ include(@nested_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@nested_attributes_module"}) unless @nested_attributes_module
133
133
  opts = associations.last.is_a?(Hash) ? associations.pop : OPTS
134
134
  reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")}
135
135
  reflections.each do |r|
@@ -188,11 +188,10 @@ module Sequel
188
188
 
189
189
  # Create a new associated object with the given attributes, validate
190
190
  # it when the parent is validated, and save it when the object is saved.
191
- # Returns the object created.
191
+ # Returns the new object.
192
192
  def nested_attributes_create(meta, attributes)
193
+ obj = nested_attributes_new(meta, attributes)
193
194
  reflection = meta[:reflection]
194
- obj = reflection.associated_class.new
195
- nested_attributes_set_attributes(meta, obj, attributes)
196
195
  delay_validate_associated_object(reflection, obj)
197
196
  if reflection.returns_array?
198
197
  public_send(reflection[:name]) << obj
@@ -254,7 +253,13 @@ module Sequel
254
253
  end
255
254
  obj
256
255
  end
257
-
256
+
257
+ # Returns a new object of the associated class with the given attributes set.
258
+ def nested_attributes_new(meta, attributes)
259
+ obj = meta[:reflection].associated_class.new
260
+ nested_attributes_set_attributes(meta, obj, attributes)
261
+ end
262
+
258
263
  # Set the fields in the obj based on the association, only allowing
259
264
  # specific :fields if configured.
260
265
  def nested_attributes_set_attributes(meta, obj, attributes)
@@ -157,13 +157,16 @@ module Sequel
157
157
  raise Error, "the paged_operations plugin is not supported on DB2 when using emulated offsets, set the :offset_strategy Database option to 'limit_offset' or 'offset_fetch'"
158
158
  end
159
159
 
160
- case pk = model.primary_key
160
+ case pk = unambiguous_primary_key
161
161
  when Symbol
162
162
  Sequel.identifier(pk)
163
163
  when Array
164
164
  raise Error, "cannot use #{meth} on a model with a composite primary key"
165
- else
165
+ when nil
166
166
  raise Error, "cannot use #{meth} on a model without a primary key"
167
+ else
168
+ # Likely SQL::QualifiedIdentifier, if the dataset is joined.
169
+ pk
167
170
  end
168
171
  end
169
172
 
@@ -541,7 +541,7 @@ module Sequel
541
541
  if (assoc_pks = obj.get_column_value(key)) && !assoc_pks.empty?
542
542
  Sequel[pk=>assoc_pks.to_a]
543
543
  end
544
- when Array
544
+ when Array, Set
545
545
  if (assoc_pks = obj.map{|o| o.get_column_value(key)}.flatten.compact.uniq) && !assoc_pks.empty?
546
546
  Sequel[pk=>assoc_pks]
547
547
  end
@@ -563,7 +563,7 @@ module Sequel
563
563
  if pkv = obj.get_column_value(ref.primary_key_method)
564
564
  Sequel.pg_array_op(key).contains(Sequel.pg_array([pkv], ref.array_type))
565
565
  end
566
- when Array
566
+ when Array, Set
567
567
  if (pkvs = obj.map{|o| o.get_column_value(ref.primary_key_method)}.compact) && !pkvs.empty?
568
568
  Sequel.pg_array(key).overlaps(Sequel.pg_array(pkvs, ref.array_type))
569
569
  end
@@ -135,7 +135,12 @@ module Sequel
135
135
  raise Error, "No pg_auto_constraint_validations setup" unless file = @pg_auto_constraint_validations_cache_file
136
136
  pg_auto_constraint_validations_cache = {}
137
137
  @pg_auto_constraint_validations_cache.sort.each do |k, v|
138
- pg_auto_constraint_validations_cache[k] = v
138
+ h = {}
139
+ v.each do |k, entry|
140
+ entry = Hash[entry.sort] if entry.is_a?(Hash)
141
+ h[k] = entry
142
+ end
143
+ pg_auto_constraint_validations_cache[k] = h
139
144
  end
140
145
  File.open(file, 'wb'){|f| f.write(Marshal.dump(pg_auto_constraint_validations_cache))}
141
146
  nil
@@ -0,0 +1,88 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The pg_auto_validate_enums plugin implements automatic validations for
6
+ # enum columns, ensuring that enum columns have a valid value. With this
7
+ # plugin, trying to save with an invalid enum value results in
8
+ # Sequel::ValidationFailed before saving, instead of Sequel::DatabaseError
9
+ # (wrapping PG::InvalidTextRepresentation or similar exception) during saving.
10
+ #
11
+ # class Person < Sequel::Model
12
+ # # assume state enum column with allowed values active and inactive
13
+ # plugin :pg_auto_validate_enums
14
+ # end
15
+ # p = Person.new(state: "active").valid? # => true
16
+ # p = Person.new(state: "inactive").valid? # => true
17
+ # p = Person.new(state: "other").valid? # => false
18
+ #
19
+ # While you can load this into individual model classes, typical use would
20
+ # be to load it into Sequel::Model or the appropriate model base class,
21
+ # and have all models that inherit from that class automatically pick it up.
22
+ #
23
+ # This plugin depends on the validation_helpers plugin.
24
+ module PgAutoValidateEnums
25
+ # Load the validation_helpers plugin.
26
+ def self.apply(model, opts=OPTS)
27
+ model.plugin(:validation_helpers)
28
+ end
29
+
30
+ # Load the pg_enum extension into the database, and reload the schema
31
+ # if it is already loaded. The opts given are used for the validates_includes
32
+ # validations (with allow_nil: true and from: :values enabled by default,
33
+ # to avoid issues with nullable enum columns and cases where the column
34
+ # method has been overridden.
35
+ def self.configure(model, opts=OPTS)
36
+ model.instance_exec do
37
+ db.extension(:pg_enum) unless @db.instance_variable_get(:@enum_labels)
38
+ if @db_schema
39
+ get_db_schema(true)
40
+ _get_pg_pg_auto_validate_enums_metadata
41
+ end
42
+ @pg_auto_validate_enums_opts = {allow_nil: true, from: :values}.merge!(opts).freeze
43
+ end
44
+ end
45
+
46
+ module ClassMethods
47
+ # Hash with enum column symbol values and arrays of valid string values.
48
+ attr_reader :pg_auto_validate_enums_metadata
49
+
50
+ # Options to pass to the validates_includes calls used by the plugin.
51
+ attr_reader :pg_auto_validate_enums_opts
52
+
53
+ Plugins.after_set_dataset(self, :_get_pg_pg_auto_validate_enums_metadata)
54
+
55
+ Plugins.inherited_instance_variables(self,
56
+ :@pg_auto_validate_enums_metadata=>nil,
57
+ :@pg_auto_validate_enums_opts=>nil)
58
+
59
+ private
60
+
61
+ # Parse the column schema to find columns with :enum_values entries,
62
+ # which will be used to setup validations.
63
+ def _get_pg_pg_auto_validate_enums_metadata
64
+ metadata = {}
65
+ @db_schema.each do |key, sch|
66
+ if enum_values = sch[:enum_values]
67
+ metadata[key] = enum_values
68
+ end
69
+ end
70
+ @pg_auto_validate_enums_metadata = metadata.freeze
71
+ end
72
+ end
73
+
74
+ module InstanceMethods
75
+ # Validate that all of the model's enum columns have valid values.
76
+ def validate
77
+ super
78
+
79
+ klass = self.class
80
+ opts = klass.pg_auto_validate_enums_opts
81
+ klass.pg_auto_validate_enums_metadata.each do |column, values|
82
+ validates_includes(values, column, opts)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,95 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The pg_eager_any_typed_array plugin automatically converts
6
+ # the predicate expressions used for eager loading from:
7
+ #
8
+ # table.column IN (value_list)
9
+ #
10
+ # to:
11
+ #
12
+ # table.column = ANY(array_expr::type[])
13
+ #
14
+ # This makes it easier to use the pg_auto_parameterize_in_array
15
+ # extension with the :treat_string_list_as_text_array option,
16
+ # when using foreign keys with non-text database types that are represented
17
+ # by Ruby strings, such as enum and uuid types.
18
+ #
19
+ # Most association types that ship with Sequel have their predicate
20
+ # expressions converted by this plugin. Here are the exceptions:
21
+ #
22
+ # * associations using composite predicate keys
23
+ # * many_to_pg_array associations
24
+ # * many_to_many/one_through_one associations using :join_table_db option
25
+ # * many_through_many/one_through_many associations using
26
+ # :separate_table_per_query option
27
+ #
28
+ # To avoid predicate conversion for particular associations, set the
29
+ # :eager_loading_predicate_transform association option to nil/false.
30
+ #
31
+ # This plugin loads the pg_array extension into the model's Database.
32
+ module PgEagerAnyTypedArray
33
+ # Add the pg_array extension to the database
34
+ def self.apply(model)
35
+ model.db.extension(:pg_array)
36
+ end
37
+
38
+ module ClassMethods
39
+ TRANSFORM = proc do |values, ref|
40
+ type = ref.send(:cached_fetch, :_pg_eager_any_typed_array_type) do
41
+ key = ref.predicate_key
42
+ next if key.is_a?(Array)
43
+
44
+ while key.is_a?(SQL::QualifiedIdentifier)
45
+ key = key.column
46
+ end
47
+
48
+ # :nocov:
49
+ # many_to_pg_array association type does not need changes, as it
50
+ # already converts the values to a typed postgres array, it does
51
+ # not call the code that uses :eager_loading_predicate_transform.
52
+ #
53
+ # No association type that ships with Sequel can reach this code
54
+ # unless it is one of these types, but external association types
55
+ # could potentially reach it.
56
+ sch = case ref[:type]
57
+ # :nocov:
58
+ when :many_to_one, :one_to_one, :one_to_many, :pg_array_to_many
59
+ ref.associated_class.db_schema
60
+ when :many_to_many, :one_through_one
61
+ # Not compatible with the :join_table_db option, but that option
62
+ # does not call into this code.
63
+ Hash[ref.associated_class.db.schema(ref.join_table_source)]
64
+ when :many_through_many, :one_through_many
65
+ # Not compatible with the :separate_query_per_table option, but
66
+ # that option does not call into this code.
67
+ Hash[ref.associated_class.db.schema(ref[:through][0][:table])]
68
+ end
69
+
70
+ if sch && (sch = sch[key])
71
+ sch[:db_type]
72
+ end
73
+ end
74
+
75
+ if type
76
+ Sequel.function(:ANY, Sequel.pg_array(values, type))
77
+ else
78
+ values
79
+ end
80
+ end
81
+
82
+ # Set the :eager_loading_predicate_transform option if not already set
83
+ def associate(type, name, opts = OPTS, &block)
84
+ res = super
85
+
86
+ unless res.has_key?(:eager_loading_predicate_transform)
87
+ res[:eager_loading_predicate_transform] = TRANSFORM
88
+ end
89
+
90
+ res
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -99,7 +99,7 @@ module Sequel
99
99
  # Add an RETURNING clause to fetch the updated xmin when updating the row.
100
100
  def _update_without_checking(columns)
101
101
  ds = _update_dataset
102
- rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
102
+ rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
103
103
  values[:xmin] = rows.first[:xmin] unless rows.empty?
104
104
  rows.length
105
105
  end
@@ -81,7 +81,7 @@ module Sequel
81
81
 
82
82
  opts = opts.dup
83
83
  opts[:class] = model
84
- opts[:methods_module] = Module.new
84
+ opts[:methods_module] = Sequel.set_temp_name(Module.new){"#{model.name}::_rcte_tree[:methods_module]"}
85
85
  opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all]
86
86
  model.send(:include, opts[:methods_module])
87
87
 
@@ -111,7 +111,7 @@ module Sequel
111
111
  ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
112
112
  descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
113
113
  recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
114
- extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.values.delete(ka_)]}}
114
+ extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.remove_key!(ka_)]}}
115
115
  else
116
116
  key_present = key_conv = lambda{|m| m[key]}
117
117
  prkey_conv = lambda{|m| m[prkey]}
@@ -119,7 +119,7 @@ module Sequel
119
119
  ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
120
120
  descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
121
121
  recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
122
- extract_key_alias = lambda{|m| bd_conv[m.values.delete(ka)]}
122
+ extract_key_alias = lambda{|m| bd_conv[m.remove_key!(ka)]}
123
123
  end
124
124
 
125
125
  parent = opts.merge(opts.fetch(:parent, OPTS)).fetch(:name, :parent)
@@ -200,7 +200,7 @@ module Sequel
200
200
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
201
201
  opk = prkey_conv[obj]
202
202
  if idm_obj = parent_map[opk]
203
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
203
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
204
204
  obj = idm_obj
205
205
  else
206
206
  obj.associations[parent] = nil
@@ -307,12 +307,12 @@ module Sequel
307
307
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
308
308
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
309
309
  if level
310
- no_cache = no_cache_level == obj.values.delete(la)
310
+ no_cache = no_cache_level == obj.remove_key!(la)
311
311
  end
312
312
 
313
313
  opk = prkey_conv[obj]
314
314
  if idm_obj = parent_map[opk]
315
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
315
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
316
316
  obj = idm_obj
317
317
  else
318
318
  obj.associations[childrena] = [] unless no_cache
@@ -30,6 +30,15 @@ module Sequel
30
30
  # Otherwise, it is possible that the default column accessors will take
31
31
  # precedence.
32
32
  #
33
+ # Note that use of an unsafe serialization method can result in an attack vector
34
+ # (potentially allowing remote code execution) if an attacker has the ability to
35
+ # store data directly in the underlying column. This would affect the marshal
36
+ # serialization format, and on older versions of Ruby, potentially the yaml and
37
+ # json serialization formats as well. It can also affect custom formats. You
38
+ # should ensure that attackers do not have access to store data directly in the
39
+ # underlying column when using this plugin (especially when using an unsafe
40
+ # serialization method).
41
+ #
33
42
  # == Example
34
43
  #
35
44
  # # Require json if you plan to use it, as the plugin doesn't require it for you.
@@ -37,7 +46,8 @@ module Sequel
37
46
  #
38
47
  # # Register custom serializer/deserializer pair, if desired
39
48
  # require 'sequel/plugins/serialization'
40
- # Sequel::Plugins::Serialization.register_format(:reverse, :reverse.to_proc, :reverse.to_proc)
49
+ # require 'base64'
50
+ # Sequel::Plugins::Serialization.register_format(:base64, Base64.method(:encode64), Base64.method(:decode64))
41
51
  #
42
52
  # class User < Sequel::Model
43
53
  # # Built-in format support when loading the plugin
@@ -48,10 +58,10 @@ module Sequel
48
58
  # serialize_attributes :marshal, :permissions
49
59
  #
50
60
  # # Use custom registered serialization format just like built-in format
51
- # serialize_attributes :reverse, :password
61
+ # serialize_attributes :base64, :password
52
62
  #
53
63
  # # Use a custom serializer/deserializer pair without registering
54
- # serialize_attributes [:reverse.to_proc, :reverse.to_proc], :password
64
+ # serialize_attributes [ Base64.method(:encode64), Base64.method(:decode64)], :password
55
65
  # end
56
66
  # user = User.create
57
67
  # user.permissions = {global: 'read-only'}
@@ -96,13 +106,15 @@ module Sequel
96
106
  register_format(:marshal, lambda{|v| [Marshal.dump(v)].pack('m')},
97
107
  lambda do |v|
98
108
  # Handle unpacked marshalled data for backwards compat
99
- v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
109
+ v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
100
110
  Marshal.load(v)
101
111
  end)
102
112
  register_format(:yaml, :to_yaml.to_proc, lambda{|s| YAML.load(s)})
103
113
  register_format(:json, Sequel.method(:object_to_json), Sequel.method(:parse_json))
104
114
 
105
115
  module ClassMethods
116
+ Plugins.model_instance_variables(self, :@deserialized_values)
117
+
106
118
  # A hash with column name symbols and callable values, with the value
107
119
  # called to deserialize the column.
108
120
  attr_reader :deserialization_map
@@ -123,7 +135,12 @@ module Sequel
123
135
  end
124
136
 
125
137
  # Create instance level reader that deserializes column values on request,
126
- # and instance level writer that stores new deserialized values.
138
+ # and instance level writer that stores new deserialized values. If +format+
139
+ # is a symbol, it should correspond to a previously-registered format using +register_format+.
140
+ # Otherwise, +format+ is expected to be a 2-element array of callables,
141
+ # with the first element being the serializer, used to convert the value used by the application
142
+ # to the value that will be stored in the database, and the second element being the deserializer,
143
+ # used to convert the value stored the database to the value used by the application.
127
144
  def serialize_attributes(format, *columns)
128
145
  if format.is_a?(Symbol)
129
146
  unless format = Sequel.synchronize{REGISTERED_FORMATS[format]}
@@ -140,7 +157,7 @@ module Sequel
140
157
  # Add serializated attribute acessor methods to the serialization_module
141
158
  def define_serialized_attribute_accessor(serializer, deserializer, *columns)
142
159
  m = self
143
- include(@serialization_module ||= Module.new) unless @serialization_module
160
+ include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module
144
161
  @serialization_module.class_eval do
145
162
  columns.each do |column|
146
163
  m.serialization_map[column] = serializer
@@ -30,6 +30,10 @@ module Sequel
30
30
  def self.apply(model)
31
31
  model.plugin :serialization
32
32
  end
33
+
34
+ module ClassMethods
35
+ Plugins.model_instance_variables(self, :@original_deserialized_values)
36
+ end
33
37
 
34
38
  module InstanceMethods
35
39
  # Clear the cache of original deserialized values after saving so that it doesn't
@@ -0,0 +1,49 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The single_statement_dataset_destroy plugin makes the
6
+ # model dataset.destroy method delete all rows in a
7
+ # single DELETE statement. It runs all before_destroy
8
+ # hooks before the DELETE, and all after_destroy hooks
9
+ # after the delete.
10
+ #
11
+ # This is not compatible with around_destroy hooks,
12
+ # so if the model is using custom around_destroy hooks,
13
+ # dataset.destroy falls back to a separate DELETE statement
14
+ # per row.
15
+ #
16
+ # Usage:
17
+ #
18
+ # # Make all model subclasses use a single DELETE
19
+ # # statement for dataset.destroy
20
+ # Sequel::Model.plugin :single_statement_dataset_destroy
21
+ #
22
+ # # Make the Album class use a single DELETE
23
+ # # statement for dataset.destroy
24
+ # Album.plugin :single_statement_dataset_destroy
25
+ module SingleStatementDatasetDestroy
26
+ module DatasetMethods
27
+ # Destroy all rows in a single DELETE statement. Run the before_destroy
28
+ # hooks for all rows before the DELETE, and all after_destroy hooks
29
+ # for all rows after the DELETE. If the model uses an around_destroy
30
+ # hook, fallback to using a separate DELETE statement per row.
31
+ def destroy
32
+ return super unless model.instance_method(:around_destroy).owner == Sequel::Model::InstanceMethods
33
+
34
+ db.transaction do
35
+ rows = all
36
+ rows.each(&:before_destroy)
37
+ expected_rows = rows.length
38
+ n = delete
39
+ unless n == expected_rows
40
+ raise Error, "dataset changed during destroy, expected rows: #{expected_rows}, actual rows: #{n}"
41
+ end
42
+ rows.each(&:after_destroy)
43
+ n
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -36,6 +36,8 @@ module Sequel
36
36
  # Album.plugin :split_values
37
37
  module SplitValues
38
38
  module ClassMethods
39
+ Plugins.model_instance_variables(self, :@noncolumn_values)
40
+
39
41
  # Split the noncolumn values when creating a new object retrieved from
40
42
  # the database.
41
43
  def call(_)
@@ -54,6 +56,16 @@ module Sequel
54
56
  end
55
57
  end
56
58
 
59
+ # Remove the key from noncolumn values if it is present there. If it is not
60
+ # present there, then use the default behavior of removing it from values.
61
+ def remove_key!(key)
62
+ if @noncolumn_values && @noncolumn_values.key?(key)
63
+ @noncolumn_values.delete(key)
64
+ else
65
+ super
66
+ end
67
+ end
68
+
57
69
  # Check all entries in the values hash. If any of the keys are not columns,
58
70
  # move the entry into the noncolumn_values hash.
59
71
  def split_noncolumn_values
@@ -40,7 +40,8 @@ module Sequel
40
40
  # Album.sql_comments_dataset_methods :to_csv # csv_serializer plugin
41
41
  #
42
42
  # In order for the sql_comments plugin to work, the sql_comments
43
- # Database extension must be loaded into the model's database.
43
+ # Database extension must be loaded into the model's database, so
44
+ # loading the plugin does this automatically.
44
45
  #
45
46
  # Note that in order to make sure SQL comments are included, some
46
47
  # optimizations are disabled if this plugin is loaded.
@@ -67,6 +68,10 @@ module Sequel
67
68
  # :nocov:
68
69
  end
69
70
 
71
+ def self.apply(model)
72
+ model.db.extension(:sql_comments)
73
+ end
74
+
70
75
  def self.configure(model)
71
76
  model.send(:reset_fast_pk_lookup_sql)
72
77
  end
@@ -85,7 +90,7 @@ module Sequel
85
90
  # Use automatic SQL comments for the given dataset methods.
86
91
  def sql_comments_dataset_methods(*meths)
87
92
  unless @_sql_comments_dataset_module
88
- dataset_module(@_sql_comments_dataset_module = Module.new)
93
+ dataset_module(@_sql_comments_dataset_module = Sequel.set_temp_name(Module.new){"#{name}::@_sql_comments_dataset_module"})
89
94
  end
90
95
  _sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths)
91
96
  end
@@ -176,6 +176,19 @@ module Sequel
176
176
  h
177
177
  end
178
178
 
179
+ # Use the cache instead of a query to get the results.
180
+ def as_set(column)
181
+ set = Set.new
182
+
183
+ if column.is_a?(Array)
184
+ @all.each{|r| set.add(r.values.values_at(*column))}
185
+ else
186
+ @all.each{|r| set.add(r[column])}
187
+ end
188
+
189
+ set
190
+ end
191
+
179
192
  # Alias of as_hash for backwards compatibility.
180
193
  def to_hash(*a)
181
194
  as_hash(*a)