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
@@ -19,7 +19,7 @@ module Sequel
19
19
  def with_extend(*mods, &block)
20
20
  c = _clone(:freeze=>false)
21
21
  c.extend(*mods) unless mods.empty?
22
- c.extend(DatasetModule.new(&block)) if block
22
+ c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
23
23
  c.freeze
24
24
  end
25
25
 
@@ -117,6 +117,8 @@ module Sequel
117
117
  frags << final_sql
118
118
  prepared_sql << final_sql
119
119
 
120
+ frags.each(&:freeze)
121
+ frags.freeze
120
122
  [prepared_sql, frags]
121
123
  end
122
124
 
@@ -125,6 +127,7 @@ module Sequel
125
127
  @argn = -1
126
128
  @args = []
127
129
  ds = yield self, dataset
130
+ ds.opts[:sql].freeze
128
131
  sql = ds.clone(:placeholder_literalizer=>self).sql
129
132
 
130
133
  last_offset = 0
@@ -20,7 +20,8 @@ module Sequel
20
20
  def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block)
21
21
  code = PREPARED_STATEMENT_MODULE_CODE[code] || code
22
22
 
23
- Module.new do
23
+ Module.new do
24
+ Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"}
24
25
  Array(mods).each do |mod|
25
26
  include mod
26
27
  end
@@ -53,7 +54,7 @@ module Sequel
53
54
 
54
55
  # Set the bind arguments based on the hash and call super.
55
56
  def call(bind_vars=OPTS, &block)
56
- sql = prepared_sql
57
+ sql = prepared_sql.freeze
57
58
  prepared_args.freeze
58
59
  ps = bind(bind_vars)
59
60
  ps.clone(:bind_arguments=>ps.map_to_prepared_args(ps.opts[:bind_vars]), :sql=>sql, :prepared_sql=>sql).run(&block)
@@ -90,8 +91,14 @@ module Sequel
90
91
  @opts[:log_sql]
91
92
  end
92
93
 
93
- # The type of prepared statement, should be one of :select, :first,
94
- # :insert, :update, :delete, or :single_value
94
+ # The type of SQL to generate for the prepared statement. Generally
95
+ # the same as #prepared_type, but can be different.
96
+ def prepared_sql_type
97
+ @opts[:prepared_sql_type] || prepared_type
98
+ end
99
+
100
+ # The type of prepared statement, which controls how the prepared statement
101
+ # handles results from the database.
95
102
  def prepared_type
96
103
  @opts[:prepared_type]
97
104
  end
@@ -140,7 +147,7 @@ module Sequel
140
147
  # Returns the SQL for the prepared statement, depending on
141
148
  # the type of the statement and the prepared_modify_values.
142
149
  def prepared_sql
143
- case prepared_type
150
+ case prepared_sql_type
144
151
  when :select, :all, :each
145
152
  # Most common scenario, so listed first.
146
153
  select_sql
@@ -181,34 +188,30 @@ module Sequel
181
188
 
182
189
  # Run the method based on the type of prepared statement.
183
190
  def run(&block)
184
- case prepared_type
191
+ case type = prepared_type
185
192
  when :select, :all
186
- all(&block)
193
+ with_sql_all(prepared_sql, &block)
187
194
  when :each
188
- each(&block)
189
- when :insert_select
190
- with_sql(prepared_sql).first
191
- when :first
192
- first
195
+ with_sql_each(prepared_sql, &block)
196
+ when :insert_select, :first
197
+ with_sql_first(prepared_sql)
193
198
  when :insert, :update, :delete
194
199
  if opts[:returning] && supports_returning?(prepared_type)
195
200
  returning_fetch_rows(prepared_sql)
196
- elsif prepared_type == :delete
197
- delete
201
+ elsif type == :delete
202
+ with_sql_delete(prepared_sql)
198
203
  else
199
- public_send(prepared_type, *prepared_modify_values)
204
+ force_prepared_sql.public_send(type, *prepared_modify_values)
200
205
  end
201
- when :insert_pk
202
- fetch_rows(prepared_sql){|r| return r.values.first}
206
+ when :insert_pk, :single_value
207
+ with_sql_single_value(prepared_sql)
203
208
  when Array
204
209
  # :nocov:
205
- case prepared_type[0]
210
+ case type[0]
206
211
  # :nocov:
207
212
  when :map, :as_hash, :to_hash, :to_hash_groups
208
- public_send(*prepared_type, &block)
213
+ force_prepared_sql.public_send(*type, &block)
209
214
  end
210
- when :single_value
211
- single_value
212
215
  else
213
216
  raise Error, "unsupported prepared statement type used: #{prepared_type.inspect}"
214
217
  end
@@ -216,6 +219,16 @@ module Sequel
216
219
 
217
220
  private
218
221
 
222
+ # If the prepared_sql_type does not match the prepared statement, return a clone that
223
+ # with the prepared SQL, to ensure the prepared_sql_type is respected.
224
+ def force_prepared_sql
225
+ if prepared_sql_type != prepared_type
226
+ with_sql(prepared_sql.freeze)
227
+ else
228
+ self
229
+ end
230
+ end
231
+
219
232
  # Returns the value of the prepared_args hash for the given key.
220
233
  def prepared_arg(k)
221
234
  @opts[:bind_vars][k]
@@ -274,7 +287,7 @@ module Sequel
274
287
 
275
288
  def run(&block)
276
289
  if @opts[:prepared_sql_frags]
277
- sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
290
+ sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
278
291
  clone(:prepared_sql_frags=>nil, :sql=>sql, :prepared_sql=>sql).run(&block)
279
292
  else
280
293
  super
@@ -293,11 +306,12 @@ module Sequel
293
306
  prepared_sql, frags = Sequel::Dataset::PlaceholderLiteralizer::Recorder.new.send(:prepared_sql_and_frags, self, prepared_args) do |pl, ds|
294
307
  ds = ds.clone(:recorder=>pl)
295
308
 
296
- case type
309
+ sql_type = prepared_sql_type || type
310
+ case sql_type
297
311
  when :first, :single_value
298
312
  ds.limit(1)
299
313
  when :update, :insert, :insert_select, :delete
300
- ds.with_sql(:"#{type}_sql", *values)
314
+ ds.with_sql(:"#{sql_type}_sql", *values)
301
315
  when :insert_pk
302
316
  ds.with_sql(:insert_sql, *values)
303
317
  else
@@ -306,6 +320,9 @@ module Sequel
306
320
  end
307
321
 
308
322
  prepared_args.freeze
323
+ frags.freeze
324
+ frags.each(&:freeze)
325
+ prepared_sql.freeze
309
326
  clone(:prepared_sql_frags=>frags, :prepared_sql=>prepared_sql, :sql=>prepared_sql)
310
327
  end
311
328
 
@@ -343,13 +360,23 @@ module Sequel
343
360
  clone(:bind_vars=>bind_vars)
344
361
  end
345
362
 
346
- # For the given type (:select, :first, :insert, :insert_select, :update, :delete, or :single_value),
347
- # run the sql with the bind variables specified in the hash. +values+ is a hash passed to
348
- # insert or update (if one of those types is used), which may contain placeholders.
363
+ # For the given type, run the sql with the bind variables specified in the hash.
364
+ # +values+ is a hash passed to insert or update (if one of those types is used),
365
+ # which may contain placeholders.
366
+ #
367
+ # The following types are supported:
368
+ #
369
+ # * :select, :all, :each, :first, :single_value, :insert, :insert_select, :insert_pk, :update, :delete
370
+ # * Array where first element is :map, :as_hash, :to_hash, :to_hash_groups (remaining elements
371
+ # are passed to the related method)
349
372
  #
350
373
  # DB[:table].where(id: :$id).call(:first, id: 1)
351
374
  # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
352
375
  # # => {:id=>1}
376
+ #
377
+ # DB[:table].where(id: :$id).call(:update, {c: 1, id: 2}, col: :$c)
378
+ # # UPDATE table WHERE id = ? SET col = ? -- (2, 1)
379
+ # # => 1
353
380
  def call(type, bind_variables=OPTS, *values, &block)
354
381
  to_prepared_statement(type, values, :extend=>bound_variable_modules).call(bind_variables, &block)
355
382
  end
@@ -370,6 +397,14 @@ module Sequel
370
397
  # # => {:id=>1, :name=>'Blah'}
371
398
  #
372
399
  # DB.call(:select_by_name, name: 'Blah') # Same thing
400
+ #
401
+ # +values+ given are passed to +insert+ or +update+ if they are used:
402
+ #
403
+ # ps = DB[:table].where(id: :$i).prepare(:update, :update_name, name: :$n)
404
+ #
405
+ # ps.call(i: 1, n: 'Blah')
406
+ # # UPDATE table WHERE id = ? SET name = ? -- (1, 'Blah')
407
+ # # => 1
373
408
  def prepare(type, name, *values)
374
409
  ps = to_prepared_statement(type, values, :name=>name, :extend=>prepared_statement_modules, :no_delayed_evaluations=>true)
375
410
 
@@ -377,7 +412,7 @@ module Sequel
377
412
  ps = ps.with_extend(EmulatePreparedStatementMethods)
378
413
  ps.send(:emulated_prepared_statement, type, name, values)
379
414
  else
380
- sql = ps.prepared_sql
415
+ sql = ps.prepared_sql.freeze
381
416
  ps.prepared_args.freeze
382
417
  ps.clone(:prepared_sql=>sql, :sql=>sql)
383
418
  end
@@ -386,6 +421,19 @@ module Sequel
386
421
  ps
387
422
  end
388
423
 
424
+ # Set the type of SQL to use for prepared statements based on this
425
+ # dataset. Prepared statements default to using the same SQL type
426
+ # as the type that is passed to #prepare/#call, but there are cases
427
+ # where it is helpful to use a different SQL type.
428
+ #
429
+ # Available types are: :select, :first, :single_value, :update,
430
+ # :delete, :insert, :insert_select, :insert_pk
431
+ #
432
+ # Other types are treated as :select.
433
+ def prepare_sql_type(type)
434
+ clone(:prepared_sql_type => type)
435
+ end
436
+
389
437
  protected
390
438
 
391
439
  # Return a cloned copy of the current dataset extended with
@@ -204,7 +204,7 @@ module Sequel
204
204
  # If no related extension file exists or the extension does not have
205
205
  # specific support for Dataset objects, an error will be raised.
206
206
  def extension(*exts)
207
- Sequel.extension(*exts)
207
+ exts.each{|ext| Sequel.extension(ext) unless Sequel.synchronize{EXTENSIONS[ext]}}
208
208
  mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
209
209
  if mods.all?
210
210
  with_extend(*mods)
@@ -231,8 +231,7 @@ module Sequel
231
231
  #
232
232
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
233
233
  def for_update
234
- return self if opts[:lock] == :update
235
- cached_dataset(:_for_update_ds){lock_style(:update)}
234
+ cached_lock_style_dataset(:_for_update_ds, :update)
236
235
  end
237
236
 
238
237
  # Returns a copy of the dataset with the source changed. If no
@@ -1238,9 +1237,9 @@ module Sequel
1238
1237
  # Note that like Object#extend, when multiple modules are provided
1239
1238
  # as arguments the subclass includes the modules in reverse order.
1240
1239
  def with_extend(*mods, &block)
1241
- c = Class.new(self.class)
1240
+ c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"}
1242
1241
  c.include(*mods) unless mods.empty?
1243
- c.include(DatasetModule.new(&block)) if block
1242
+ c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"}) if block
1244
1243
  o = c.freeze.allocate
1245
1244
  o.instance_variable_set(:@db, @db)
1246
1245
  o.instance_variable_set(:@opts, @opts)
@@ -1302,7 +1301,7 @@ module Sequel
1302
1301
  # * truncate (if a TRUNCATE statement, with no arguments)
1303
1302
  def with_sql(sql, *args)
1304
1303
  if sql.is_a?(Symbol)
1305
- sql = public_send(sql, *args)
1304
+ sql = public_send(sql, *args).freeze
1306
1305
  else
1307
1306
  sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
1308
1307
  end
@@ -1359,9 +1358,13 @@ module Sequel
1359
1358
  unless TRUE_FREEZE
1360
1359
  # Load the extensions into the receiver, without checking if the receiver is frozen.
1361
1360
  def _extension!(exts)
1362
- Sequel.extension(*exts)
1363
1361
  exts.each do |ext|
1364
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
1362
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
1363
+ Sequel.extension(ext)
1364
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
1365
+ end
1366
+
1367
+ if pr
1365
1368
  pr.call(self)
1366
1369
  else
1367
1370
  raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
@@ -1458,11 +1461,23 @@ module Sequel
1458
1461
  end
1459
1462
  end
1460
1463
 
1464
+ # Internals of for_update and adapter-specific lock methods.
1465
+ # Returns receiver if it already uses this lock style, and a cached
1466
+ # dataset using the given key otherwise. The key could be derived from
1467
+ # the style, but doing so would require allocation, so pass it in as
1468
+ # an argument.
1469
+ def cached_lock_style_dataset(key, style)
1470
+ opts[:lock] == style ? self : cached_dataset(key){lock_style(style)}
1471
+ end
1472
+
1461
1473
  # The default :qualify option to use for join tables if one is not specified.
1462
1474
  def default_join_table_qualification
1463
1475
  :symbol
1464
1476
  end
1465
-
1477
+
1478
+ PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
1479
+ private_constant :PAREN_WRAPPER
1480
+
1466
1481
  # SQL expression object based on the expr type. See +where+.
1467
1482
  def filter_expr(expr = nil, &block)
1468
1483
  expr = nil if expr == EMPTY_ARRAY
@@ -1483,8 +1498,8 @@ module Sequel
1483
1498
  raise Error, "Invalid filter expression: #{expr.inspect}"
1484
1499
  end
1485
1500
  when LiteralString
1486
- LiteralString.new("(#{expr})")
1487
- when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String
1501
+ SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
1502
+ when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String, Set
1488
1503
  raise Error, "Invalid filter expression: #{expr.inspect}"
1489
1504
  when TrueClass, FalseClass
1490
1505
  if supports_where_true?
@@ -53,7 +53,7 @@ module Sequel
53
53
  when String
54
54
  case v
55
55
  when LiteralString
56
- sql << v
56
+ literal_literal_string_append(sql, v)
57
57
  when SQL::Blob
58
58
  literal_blob_append(sql, v)
59
59
  else
@@ -85,6 +85,8 @@ module Sequel
85
85
  literal_date_append(sql, v)
86
86
  when Dataset
87
87
  literal_dataset_append(sql, v)
88
+ when Set
89
+ literal_set_append(sql, v)
88
90
  else
89
91
  literal_other_append(sql, v)
90
92
  end
@@ -375,9 +377,9 @@ module Sequel
375
377
  cols = args[0]
376
378
  vals = args[1]
377
379
  col_array = true if cols.is_a?(Array)
378
- if vals.is_a?(Array)
380
+ if vals.is_a?(Array) || vals.is_a?(Set)
379
381
  val_array = true
380
- empty_val_array = vals == []
382
+ empty_val_array = vals.empty?
381
383
  end
382
384
  if empty_val_array
383
385
  literal_append(sql, empty_array_value(op, cols))
@@ -560,6 +562,7 @@ module Sequel
560
562
  end
561
563
 
562
564
  if window = opts[:over]
565
+ sql << ' IGNORE NULLS' if window.opts[:ignore_nulls]
563
566
  sql << ' OVER '
564
567
  window_sql_append(sql, window.opts)
565
568
  end
@@ -636,12 +639,11 @@ module Sequel
636
639
  if args.empty?
637
640
  sql << str
638
641
  else
639
- re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
640
- while true
642
+ re = /:(#{Regexp.union(args.keys.map(&:to_s))})\b/
643
+ until str.empty?
641
644
  previous, q, str = str.partition(re)
642
645
  sql << previous
643
- literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
644
- break if str.empty?
646
+ literal_append(sql, args[$1.to_sym]) unless q.empty?
645
647
  end
646
648
  end
647
649
  elsif str.is_a?(Array)
@@ -685,10 +687,10 @@ module Sequel
685
687
  # being quoted, returns name as a string. If identifiers are being quoted
686
688
  # quote the name with quoted_identifier.
687
689
  def quote_identifier_append(sql, name)
690
+ name = name.value if name.is_a?(SQL::Identifier)
688
691
  if name.is_a?(LiteralString)
689
692
  sql << name
690
693
  else
691
- name = name.value if name.is_a?(SQL::Identifier)
692
694
  name = input_identifier(name)
693
695
  if quote_identifiers?
694
696
  quoted_identifier_append(sql, name)
@@ -700,11 +702,14 @@ module Sequel
700
702
 
701
703
  # Append literalization of identifier or unqualified identifier to SQL string.
702
704
  def quote_schema_table_append(sql, table)
703
- schema, table = schema_and_table(table)
704
- if schema
705
- quote_identifier_append(sql, schema)
705
+ qualifiers = split_qualifiers(table)
706
+ table = qualifiers.pop
707
+
708
+ qualifiers.each do |q|
709
+ quote_identifier_append(sql, q)
706
710
  sql << '.'
707
711
  end
712
+
708
713
  quote_identifier_append(sql, table)
709
714
  end
710
715
 
@@ -1032,7 +1037,7 @@ module Sequel
1032
1037
  if column_aliases
1033
1038
  raise Error, "#{db.database_type} does not support derived column lists" unless supports_derived_column_lists?
1034
1039
  sql << '('
1035
- identifier_list_append(sql, column_aliases)
1040
+ derived_column_list_sql_append(sql, column_aliases)
1036
1041
  sql << ')'
1037
1042
  end
1038
1043
  end
@@ -1165,6 +1170,11 @@ module Sequel
1165
1170
  end
1166
1171
  end
1167
1172
 
1173
+ # Append the column aliases to the SQL.
1174
+ def derived_column_list_sql_append(sql, column_aliases)
1175
+ identifier_list_append(sql, column_aliases)
1176
+ end
1177
+
1168
1178
  # Disable caching of SQL for the current dataset
1169
1179
  def disable_sql_caching!
1170
1180
  cache_set(:_no_cache_sql, true)
@@ -1331,7 +1341,7 @@ module Sequel
1331
1341
  # SQL fragment specifying a JOIN type, converts underscores to
1332
1342
  # spaces and upcases.
1333
1343
  def join_type_sql(join_type)
1334
- "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
1344
+ "#{join_type.to_s.tr('_', ' ').upcase} JOIN"
1335
1345
  end
1336
1346
 
1337
1347
  # Append USING clause for JOIN USING
@@ -1415,6 +1425,11 @@ module Sequel
1415
1425
  v.to_s
1416
1426
  end
1417
1427
 
1428
+ # Append string to SQL string.
1429
+ def literal_literal_string_append(sql, v)
1430
+ sql << v
1431
+ end
1432
+
1418
1433
  # SQL fragment for nil
1419
1434
  def literal_nil
1420
1435
  "NULL"
@@ -1425,10 +1440,6 @@ module Sequel
1425
1440
  # calls +sql_literal+ if object responds to it, otherwise raises an error.
1426
1441
  # If a database specific type is allowed, this should be overriden in a subclass.
1427
1442
  def literal_other_append(sql, v)
1428
- # We can't be sure if v will always literalize to the same SQL, so
1429
- # don't cache SQL for a dataset that uses this.
1430
- disable_sql_caching!
1431
-
1432
1443
  if v.respond_to?(:sql_literal_append)
1433
1444
  v.sql_literal_append(self, sql)
1434
1445
  elsif v.respond_to?(:sql_literal)
@@ -1436,6 +1447,18 @@ module Sequel
1436
1447
  else
1437
1448
  raise Error, "can't express #{v.inspect} as a SQL literal"
1438
1449
  end
1450
+
1451
+ if !v.respond_to?(:sql_literal_allow_caching?) || !v.sql_literal_allow_caching?(self)
1452
+ # We can't be sure if v will always literalize to the same SQL, so
1453
+ # don't cache SQL for a dataset that uses this.
1454
+ disable_sql_caching!
1455
+ end
1456
+ end
1457
+
1458
+ # Append a literalization of the set to SQL string.
1459
+ # Treats as an expression as an SQL value list.
1460
+ def literal_set_append(sql, v)
1461
+ array_sql_append(sql, v)
1439
1462
  end
1440
1463
 
1441
1464
  # SQL fragment for Sequel::SQLTime, containing just the time part
@@ -357,7 +357,7 @@ module Sequel
357
357
  define_singleton_method(:async_job_class){proxy_klass}
358
358
 
359
359
  queue = @async_thread_queue = Queue.new
360
- pool = @async_thread_pool = num_async_threads.times.map{JobProcessor.new(queue)}
360
+ pool = @async_thread_pool = Array.new(num_async_threads){JobProcessor.new(queue)}
361
361
  ObjectSpace.define_finalizer(db, JobProcessor.create_finalizer(queue, pool))
362
362
 
363
363
  extend_datasets(DatasetMethods)
@@ -60,9 +60,7 @@ module Sequel
60
60
  def external_caller_for_log
61
61
  ignore = caller_logging_ignore
62
62
  c = caller.find do |line|
63
- !(line.start_with?(SEQUEL_LIB_PATH) ||
64
- line.start_with?(RUBY_STDLIB) ||
65
- line.start_with?(INTERNAL) ||
63
+ !(line.start_with?(SEQUEL_LIB_PATH, RUBY_STDLIB, INTERNAL) ||
66
64
  (ignore && line =~ ignore))
67
65
  end
68
66
 
@@ -0,0 +1,151 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The connection_checkout_event_callback extension modifies a database's
4
+ # connection pool to allow for a checkout event callback. This callback is
5
+ # called with the following arguments:
6
+ #
7
+ # :immediately_available :: Connection immediately available and returned
8
+ # :not_immediately_available :: Connection not immediately available
9
+ # :new_connection :: New connection created and returned
10
+ # Float :: Number of seconds waiting to acquire a connection
11
+ #
12
+ # This is a low-level extension that allows for building telemetry
13
+ # information. It doesn't implement any telemetry reporting itself. The
14
+ # main reason for recording this information is to use it to determine the
15
+ # appropriate size for the connection pool. Having too large a connection
16
+ # pool can waste resources, while having too small a connection pool can
17
+ # result in substantial time to check out a connection. In general, you
18
+ # want to use as small a pool as possible while keeping the time to
19
+ # checkout a connection low.
20
+ #
21
+ # To use the connection checkout event callback, you must first load the
22
+ # extension:
23
+ #
24
+ # DB.extension(:connection_checkout_event_callback)
25
+ #
26
+ # By default, an empty proc is used as the callback so that loading the
27
+ # support doesn't break anything. If you are using the extension, you
28
+ # should set the callback at some point during application startup:
29
+ #
30
+ # DB.pool.on_checkout_event = proc do |event|
31
+ # # ...
32
+ # end
33
+ #
34
+ # When using the sharded connection pool, the callback is also
35
+ # passed a second argument, the requested server shard (generally a
36
+ # symbol), allowing for collection of per-shard telemetry:
37
+ #
38
+ # DB.pool.on_checkout_event = proc do |event, server|
39
+ # # ...
40
+ # end
41
+ #
42
+ # Note that the callback may be called currently by multiple threads.
43
+ # You should use some form of concurrency control inside the callback,
44
+ # such as a mutex or queue.
45
+ #
46
+ # Below is a brief example of usage to determine the percentage of
47
+ # connection requests where a connection was immediately available:
48
+ #
49
+ # mutex = Mutex.new
50
+ # total = immediates = 0
51
+ #
52
+ # DB.pool.on_checkout_event = proc do |event|
53
+ # case event
54
+ # when :immediately_available
55
+ # mutex.synchronize do
56
+ # total += 1
57
+ # immediates += 1
58
+ # end
59
+ # when :not_immediately_available
60
+ # mutex.synchronize do
61
+ # total += 1
62
+ # end
63
+ # end
64
+ # end
65
+ #
66
+ # immediate_percentage = lambda do
67
+ # mutex.synchronize do
68
+ # 100.0 * immediates / total
69
+ # end
70
+ # end
71
+ #
72
+ # Note that this extension only works with the timed_queue
73
+ # and sharded_timed_queue connection pools (the default
74
+ # connection pools when using Ruby 3.2+).
75
+ #
76
+ # Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
77
+ # Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
78
+
79
+ #
80
+ module Sequel
81
+ module ConnectionCheckoutEventCallbacks
82
+ module TimedQueue
83
+ # The callback that is called with connection checkout events.
84
+ attr_accessor :on_checkout_event
85
+
86
+ private
87
+
88
+ def available
89
+ conn = super
90
+ @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
91
+ conn
92
+ end
93
+
94
+ def try_make_new
95
+ conn = super
96
+ @on_checkout_event.call(:new_connection) if conn
97
+ conn
98
+ end
99
+
100
+ def wait_until_available
101
+ timer = Sequel.start_timer
102
+ conn = super
103
+ @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
104
+ conn
105
+ end
106
+ end
107
+
108
+ module ShardedTimedQueue
109
+ # The callback that is called with connection checkout events.
110
+ attr_accessor :on_checkout_event
111
+
112
+ private
113
+
114
+ def available(queue, server)
115
+ conn = super
116
+ @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
117
+ conn
118
+ end
119
+
120
+ def try_make_new(server)
121
+ conn = super
122
+ @on_checkout_event.call(:new_connection, server) if conn
123
+ conn
124
+ end
125
+
126
+ def wait_until_available(queue, server)
127
+ timer = Sequel.start_timer
128
+ conn = super
129
+ @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
130
+ conn
131
+ end
132
+ end
133
+ end
134
+
135
+ default_callback = proc{}
136
+
137
+ Database.register_extension(:connection_checkout_event_callback) do |db|
138
+ pool = db.pool
139
+
140
+ case pool.pool_type
141
+ when :timed_queue
142
+ db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
143
+ when :sharded_timed_queue
144
+ db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
145
+ else
146
+ raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
147
+ end
148
+
149
+ pool.on_checkout_event ||= default_callback
150
+ end
151
+ end