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
@@ -46,8 +46,10 @@ module Sequel
46
46
  # Initialize the data structures used by this extension.
47
47
  def self.extended(pool)
48
48
  case pool.pool_type
49
- when :single, :sharded_single
50
- raise Error, "cannot load connection_expiration extension if using single or sharded_single connection pool"
49
+ when :threaded, :sharded_threaded, :timed_queue, :sharded_timed_queue
50
+ nil
51
+ else
52
+ raise Error, "cannot load connection_expiration extension if using single or sharded_single connection pool (or other unsupported connection pool)"
51
53
  end
52
54
 
53
55
  pool.instance_exec do
@@ -55,6 +57,39 @@ module Sequel
55
57
  @connection_expiration_timestamps ||= {}
56
58
  @connection_expiration_timeout ||= 14400
57
59
  @connection_expiration_random_delay ||= 0
60
+
61
+ # Record an expiration timestamp for any connections that already
62
+ # exist in the pool, so that a connection opened before the extension
63
+ # was loaded (e.g. via Sequel.connect) will eventually be expired.
64
+ register = method(:register_connection_expiration_time)
65
+
66
+ case pool_type
67
+ when :timed_queue, :sharded_timed_queue
68
+ register_queued_connections = lambda do |queue|
69
+ conns = []
70
+ while conn = queue.pop(timeout: 0)
71
+ conns << conn
72
+ end
73
+ conns.each do |conn|
74
+ queue.push(register_connection_expiration_time(conn))
75
+ end
76
+ end
77
+ end
78
+
79
+ case pool_type
80
+ when :threaded
81
+ @available_connections.each(&register)
82
+ @allocated.each_value(&register)
83
+ when :sharded_threaded
84
+ @available_connections.each_value{|conns| conns.each(&register)}
85
+ @allocated.each_value{|threads| threads.each_value(&register)}
86
+ when :timed_queue
87
+ register_queued_connections.call(@queue)
88
+ @allocated.each_value(&register)
89
+ else # when :sharded_timed_queue
90
+ @queues.each_value(&register_queued_connections)
91
+ @allocated.each_value{|threads| threads.each_value(&register)}
92
+ end
58
93
  end
59
94
  end
60
95
  end
@@ -69,8 +104,12 @@ module Sequel
69
104
 
70
105
  # Record the time the connection was created.
71
106
  def make_new(*)
72
- conn = super
73
- @connection_expiration_timestamps[conn] = [Sequel.start_timer, @connection_expiration_timeout + (rand * @connection_expiration_random_delay)].freeze
107
+ register_connection_expiration_time(super)
108
+ end
109
+
110
+ # Record an expiration entry for a connection, returns the connection.
111
+ def register_connection_expiration_time(conn)
112
+ @connection_expiration_timestamps[conn] ||= [Sequel.start_timer, @connection_expiration_timeout + (rand * @connection_expiration_random_delay)].freeze
74
113
  conn
75
114
  end
76
115
 
@@ -91,7 +130,7 @@ module Sequel
91
130
  sync{@allocated.delete(Sequel.current)}
92
131
  end
93
132
 
94
- disconnect_connection(conn)
133
+ disconnect_acquired_connection(conn, *a)
95
134
  redo
96
135
  end
97
136
  end
@@ -105,18 +105,23 @@ module Sequel
105
105
  1.times do
106
106
  if (conn = super) &&
107
107
  (timer = sync{@connection_timestamps.delete(conn)}) &&
108
- Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
109
- !db.valid_connection?(conn)
108
+ Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout
110
109
 
111
- case pool_type
112
- when :sharded_threaded, :sharded_timed_queue
113
- sync{@allocated[a.last].delete(Sequel.current)}
114
- else
115
- sync{@allocated.delete(Sequel.current)}
116
- end
110
+ begin
111
+ valid = db.valid_connection?(conn)
112
+ ensure
113
+ unless valid
114
+ case pool_type
115
+ when :sharded_threaded, :sharded_timed_queue
116
+ sync{@allocated[a.last].delete(Sequel.current)}
117
+ else
118
+ sync{@allocated.delete(Sequel.current)}
119
+ end
117
120
 
118
- disconnect_connection(conn)
119
- redo
121
+ disconnect_acquired_connection(conn, *a)
122
+ redo if valid == false
123
+ end
124
+ end
120
125
  end
121
126
  end
122
127
 
@@ -436,7 +436,7 @@ module Sequel
436
436
  else
437
437
  raise Error, "validates includes with a range only supports integers currently, cannot handle: #{arg.inspect}"
438
438
  end
439
- elsif arg.is_a?(Array)
439
+ elsif arg.is_a?(Array) || arg.is_a?(Set)
440
440
  if arg.all?{|x| x.is_a?(Integer)}
441
441
  validation_type = :includes_int_array
442
442
  elsif arg.all?{|x| x.is_a?(String)}
@@ -444,9 +444,9 @@ module Sequel
444
444
  else
445
445
  raise Error, "validates includes with an array only supports strings and integers currently, cannot handle: #{arg.inspect}"
446
446
  end
447
- arg = arg.join(',')
447
+ arg = Sequel.array_or_set_join(arg, ',')
448
448
  else
449
- raise Error, "validates includes only supports arrays and ranges currently, cannot handle: #{arg.inspect}"
449
+ raise Error, "validates includes only supports arrays, sets, and ranges currently, cannot handle: #{arg.inspect}"
450
450
  end
451
451
  when :like, :ilike
452
452
  generator.constraint(constraint, constraint_validation_expression(columns, allow_nil){|c| Sequel.public_send(validation_type, c, arg)})
@@ -0,0 +1,41 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The dataset_run extension is designed for cases where you want
4
+ # to use dataset methods to build a query, but want to run that
5
+ # query without returning a result. The most common need would
6
+ # be to easily use placeholders in an SQL string, which Database#run
7
+ # does not support directly.
8
+ #
9
+ # You can load this extension into specific datasets:
10
+ #
11
+ # ds = DB["GRANT SELECT ON ? TO ?", :table, :user]
12
+ # ds = ds.extension(:dataset_run)
13
+ # ds.run
14
+ #
15
+ # Or you can load it into all of a database's datasets, which
16
+ # is probably the desired behavior if you are using this extension:
17
+ #
18
+ # DB.extension(:dataset_run)
19
+ # DB["GRANT SELECT ON ? TO ?", :table, :user].run
20
+ #
21
+ # Related module: Sequel::DatasetRun
22
+
23
+ #
24
+ module Sequel
25
+ module DatasetRun
26
+ # Run the dataset's SQL on the database. Returns NULL. This is
27
+ # useful when you want to run SQL without returning a result.
28
+ #
29
+ # DB["GRANT SELECT ON ? TO ?", :table, :user].run
30
+ # # GRANT SELECT ON "table" TO "user"
31
+ def run
32
+ if server = @opts[:server]
33
+ db.run(sql.freeze, :server=>server)
34
+ else
35
+ db.run(sql.freeze)
36
+ end
37
+ end
38
+ end
39
+
40
+ Dataset.register_extension(:dataset_run, DatasetRun)
41
+ end
@@ -82,12 +82,12 @@ module Sequel
82
82
  DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
83
83
  DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
84
84
  POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
85
- MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
86
- MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
85
+ MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
86
+ MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
87
87
  H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
88
- DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
88
+ DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
89
89
  ACCESS_DURATION_UNITS = DURATION_UNITS.zip(%w'yyyy m d h n s'.map(&:freeze)).freeze
90
- DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
90
+ DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
91
91
 
92
92
  # Append the SQL fragment for the DateAdd expression to the SQL query.
93
93
  def date_add_sql_append(sql, da)
@@ -107,7 +107,7 @@ module Sequel
107
107
  placeholder = []
108
108
  vals = []
109
109
  each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
110
- placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
110
+ placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
111
111
  vals << value
112
112
  end
113
113
  interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
@@ -156,7 +156,7 @@ module Sequel
156
156
  expr = Sequel.cast_string(expr) + ' 00:00:00'
157
157
  end
158
158
  each_valid_interval_unit(h, DERBY_DURATION_UNITS) do |value, sql_unit|
159
- expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
159
+ expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
160
160
  end
161
161
  when :oracle
162
162
  each_valid_interval_unit(h, MYSQL_DURATION_UNITS) do |value, sql_unit|
@@ -9,6 +9,13 @@
9
9
  # DB[:test].exclude(name: [])
10
10
  # # SELECT * FROM test WHERE (name = name)
11
11
  #
12
+ # This works for sets in addition to arrays:
13
+ #
14
+ # DB[:test].where(name: Set[])
15
+ # # SELECT * FROM test WHERE (name != name)
16
+ # DB[:test].exclude(name: Set[])
17
+ # # SELECT * FROM test WHERE (name = name)
18
+ #
12
19
  # The default Sequel behavior is to ignore NULLs, as the above
13
20
  # query is not generally optimized well by databases.
14
21
  #
@@ -34,6 +34,8 @@ module Sequel
34
34
  "#{obj.class}.new(#{obj.to_a.inspect})"
35
35
  when Array
36
36
  "[#{obj.map{|o| eval_inspect(o)}.join(', ')}]"
37
+ when Set
38
+ "Set[#{obj.map{|o| eval_inspect(o)}.join(', ')}]"
37
39
  when Hash
38
40
  "{#{obj.map{|k, v| "#{eval_inspect(k)} => #{eval_inspect(v)}"}.join(', ')}}"
39
41
  when Time
@@ -66,7 +68,7 @@ module Sequel
66
68
  # in the order they were defined.
67
69
  klass = self.class
68
70
  args = inspect_args.map do |arg|
69
- if arg.is_a?(String) && arg =~ /\A\*/
71
+ if arg.is_a?(String) && arg.start_with?('*')
70
72
  # Special case string arguments starting with *, indicating that
71
73
  # they should return an array to be splatted as the remaining arguments.
72
74
  # Allow calling private methods to get inspect output.
@@ -159,7 +159,7 @@ class String
159
159
  # Example
160
160
  # "puni_puni".dasherize #=> "puni-puni"
161
161
  def dasherize
162
- gsub('_', '-')
162
+ tr('_', '-')
163
163
  end
164
164
 
165
165
  # Removes the module part from the expression in the string
@@ -189,7 +189,7 @@ class String
189
189
  # "employee_salary" #=> "Employee salary"
190
190
  # "author_id" #=> "Author"
191
191
  def humanize
192
- gsub(/_id$/, "").gsub('_', " ").capitalize
192
+ gsub(/_id$/, "").tr('_', " ").capitalize
193
193
  end
194
194
 
195
195
  # Returns the plural form of the word in the string.
@@ -0,0 +1,131 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The lit_require_frozen extension disallows the use of unfrozen strings
4
+ # as literal strings in database and dataset methods. If you try to use an
5
+ # unfrozen string as a literal string for a dataset using this extension,
6
+ # an exception will be raised.
7
+ #
8
+ # While this works for all Ruby versions, it is designed for use on Ruby 3+
9
+ # where all files are using the frozen-string-literal magic comment. In this
10
+ # case, uninterpolated literal strings are frozen, but interpolated strings
11
+ # are not frozen. This allows you to catch potentially dangerous code:
12
+ #
13
+ # # Probably safe, no exception raised
14
+ # DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
15
+ #
16
+ # # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
17
+ # DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
18
+ #
19
+ # The assumption made is that a frozen string is unlikely to contain unsafe
20
+ # input, while an unfrozen string has potentially been interpolated and may
21
+ # contain unsafe input.
22
+ #
23
+ # This disallows the the following cases:
24
+ #
25
+ # * Sequel::LiteralString instances that are unfrozen and are not based on a
26
+ # frozen string
27
+ # * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
28
+ # is not frozen
29
+ # * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
30
+ #
31
+ # To use this extension, load it into the database:
32
+ #
33
+ # DB.extension :lit_require_frozen
34
+ #
35
+ # It can also be loaded into individual datasets:
36
+ #
37
+ # ds = DB[:t].extension(:lit_require_frozen)
38
+ #
39
+ # Assuming you have good test coverage, it is recommended to only load
40
+ # this extension when testing.
41
+ #
42
+ # Related module: Sequel::LitRequireFrozen
43
+
44
+ #
45
+ module Sequel
46
+ class LiteralString
47
+ # The string used when creating the literal string (first argument to
48
+ # Sequel::LiteralString.new). This may be nil if no string was provided,
49
+ # or if the litral string was created before this extension was required.
50
+ attr_reader :source
51
+
52
+ def initialize(*a)
53
+ @source = a.first
54
+ super
55
+ end
56
+ # :nocov:
57
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
58
+ # :nocov:
59
+ end
60
+
61
+ module LitRequireFrozen
62
+ # Error class raised for using unfrozen literal string.
63
+ class Error < Sequel::Error
64
+ end
65
+
66
+ module DatabaseMethods
67
+ def self.extended(db)
68
+ db.extend_datasets(DatasetMethods)
69
+ end
70
+
71
+ # Check given SQL is frozen before running it.
72
+ def run(sql, opts=OPTS)
73
+ @default_dataset.with_sql(sql)
74
+ super
75
+ end
76
+ end
77
+
78
+ module DatasetMethods
79
+ # Check given SQL is not an unfrozen string.
80
+ def with_sql(sql, *args)
81
+ _check_unfrozen_literal_string(sql)
82
+ super
83
+ end
84
+
85
+ # Check that placeholder string is frozen (or all entries
86
+ # in placeholder array are frozen).
87
+ def placeholder_literal_string_sql_append(sql, pls)
88
+ str = pls.str
89
+
90
+ if str.is_a?(Array)
91
+ str.each do |s|
92
+ _check_unfrozen_literal_string(s)
93
+ end
94
+ else
95
+ _check_unfrozen_literal_string(str)
96
+ end
97
+
98
+ super
99
+ end
100
+
101
+ private
102
+
103
+ # Base method that other methods used to check for whether a string should be allowed
104
+ # as literal SQL. Allows non-strings as well as frozen strings.
105
+ def _check_unfrozen_literal_string(str)
106
+ return if !str.is_a?(String) || str.frozen?
107
+
108
+ if str.is_a?(LiteralString)
109
+ _check_unfrozen_literal_string(str.source)
110
+ else
111
+ raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
112
+ end
113
+ end
114
+
115
+ # Check literal strings appended to SQL.
116
+ def literal_literal_string_append(sql, v)
117
+ _check_unfrozen_literal_string(v)
118
+ super
119
+ end
120
+
121
+ # Check static SQL is not frozen.
122
+ def static_sql(sql)
123
+ _check_unfrozen_literal_string(sql)
124
+ super
125
+ end
126
+ end
127
+ end
128
+
129
+ Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
130
+ Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
131
+ end
@@ -37,18 +37,11 @@ module Sequel
37
37
  value.to_s
38
38
  end
39
39
 
40
- if RUBY_VERSION >= '2.4'
41
- def _typecast_value_string_to_decimal(value)
42
- BigDecimal(value)
43
- rescue
44
- BigDecimal('0.0')
45
- end
46
- else
47
- # :nocov:
48
- def _typecast_value_string_to_decimal(value)
49
- BigDecimal(value)
50
- end
51
- # :nocov:
40
+ # Typecast invalid BigDecimal to 0.0.
41
+ def _typecast_value_string_to_decimal(value)
42
+ BigDecimal(value)
43
+ rescue
44
+ BigDecimal('0.0')
52
45
  end
53
46
  end
54
47
 
@@ -186,22 +186,19 @@ module Sequel
186
186
  # and returns a new block that reverses the actions taken by
187
187
  # the given block.
188
188
  def reverse(&block)
189
- begin
190
- instance_exec(&block)
191
- rescue
192
- just_raise = true
193
- end
194
- if just_raise
195
- Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
196
- else
197
- actions = @actions.reverse
198
- Proc.new do
199
- actions.each do |a|
200
- pr = a.last.is_a?(Proc) ? a.pop : nil
201
- # Allow calling private methods as the reversing methods are private
202
- send(*a, &pr)
203
- end
204
- end
189
+ instance_exec(&block)
190
+ rescue NoMethodError => e
191
+ Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
192
+ rescue => e
193
+ Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
194
+ else
195
+ actions = @actions.reverse
196
+ Proc.new do
197
+ actions.each do |a|
198
+ pr = a.last.is_a?(Proc) ? a.pop : nil
199
+ # Allow calling private methods as the reversing methods are private
200
+ send(*a, &pr)
201
+ end
205
202
  end
206
203
  end
207
204
 
@@ -223,7 +220,7 @@ module Sequel
223
220
  @actions << [:drop_join_table, *args]
224
221
  end
225
222
 
226
- def create_table(name, opts=OPTS)
223
+ def create_table(name, opts=OPTS, &_)
227
224
  @actions << [:drop_table, name, opts]
228
225
  end
229
226
 
@@ -270,7 +267,7 @@ module Sequel
270
267
  end
271
268
 
272
269
  def add_primary_key(*args)
273
- raise if args.first.is_a?(Array)
270
+ super if args.first.is_a?(Array)
274
271
  @actions << [:drop_column, args.first]
275
272
  end
276
273
 
@@ -287,6 +284,10 @@ module Sequel
287
284
  def set_column_allow_null(name, allow_null=true)
288
285
  @actions << [:set_column_allow_null, name, !allow_null]
289
286
  end
287
+
288
+ def set_column_not_null(name)
289
+ @actions << [:set_column_allow_null, name]
290
+ end
290
291
  end
291
292
 
292
293
  # The preferred method for writing Sequel migrations, using a DSL:
@@ -371,7 +372,7 @@ module Sequel
371
372
  #
372
373
  # Part of the +migration+ extension.
373
374
  class Migrator
374
- MIGRATION_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
375
+ MIGRATION_FILE_PATTERN = /\A(\d+)_(.+)\.rb\z/i.freeze
375
376
 
376
377
  # Mutex used around migration file loading
377
378
  MUTEX = Mutex.new
@@ -791,7 +792,23 @@ module Sequel
791
792
  next unless MIGRATION_FILE_PATTERN.match(file)
792
793
  files << File.join(directory, file)
793
794
  end
794
- files.sort_by{|f| MIGRATION_FILE_PATTERN.match(File.basename(f))[1].to_i}
795
+ files.sort! do |a, b|
796
+ a_ver, a_name = split_migration_filename(a)
797
+ b_ver, b_name = split_migration_filename(b)
798
+ x = a_ver <=> b_ver
799
+ if x.zero?
800
+ x = a_name <=> b_name
801
+ end
802
+ x
803
+ end
804
+ files
805
+ end
806
+
807
+ # Return an integer and name (without extension) for the given path.
808
+ def split_migration_filename(path)
809
+ version, name = MIGRATION_FILE_PATTERN.match(File.basename(path)).captures
810
+ version = version.to_i
811
+ [version, name]
795
812
  end
796
813
 
797
814
  # Returns tuples of migration, filename, and direction
@@ -825,7 +842,7 @@ module Sequel
825
842
  begin
826
843
  db.create_table(table){String c, :primary_key=>true}
827
844
  rescue Sequel::DatabaseError => e
828
- if db.database_type == :mysql && e.message =~ /max key length/
845
+ if db.database_type == :mysql && e.message.include?('max key length')
829
846
  # Handle case where MySQL is used with utf8mb4 charset default, which
830
847
  # only allows a maximum length of about 190 characters for string
831
848
  # primary keys due to InnoDB limitations.
@@ -63,12 +63,12 @@ module Sequel
63
63
  end
64
64
 
65
65
  # Return self without sending a database query, never yielding.
66
- def each
66
+ def each(&_)
67
67
  self
68
68
  end
69
69
 
70
70
  # Return nil without sending a database query, never yielding.
71
- def fetch_rows(sql)
71
+ def fetch_rows(sql, &_)
72
72
  nil
73
73
  end
74
74
 
@@ -56,6 +56,13 @@
56
56
  # ia.join(':', ' ') # array_to_string(int_array_column, ':', ' ')
57
57
  # ia.unnest # unnest(int_array_column)
58
58
  # ia.unnest(:b) # unnest(int_array_column, b)
59
+ #
60
+ # On PostgreSQL 18+, the following are supported:
61
+ #
62
+ # ia.sort # array_sort(int_array_column)
63
+ # ia.sort(desc: true) # array_sort(int_array_column, true)
64
+ # ia.sort(nulls: :first) # array_sort(int_array_column, false, true)
65
+ # ia.reverse # array_reverse(int_array_column)
59
66
  #
60
67
  # See the PostgreSQL array function and operator documentation for more
61
68
  # details on what these functions and operators do.
@@ -217,6 +224,40 @@ module Sequel
217
224
  ArrayOp.new(function(:array_replace, element, replacement))
218
225
  end
219
226
 
227
+ # Call the array_reverse method:
228
+ #
229
+ # array_op.reverse # array_reverse(array)
230
+ def reverse
231
+ function(:array_reverse)
232
+ end
233
+
234
+ # Call the array_sort method. Options:
235
+ #
236
+ # :desc :: Sort in descending order instead of ascending order.
237
+ # :nulls :: If sorting in ascending order and value is :first, include NULL
238
+ # values before non-NULL values. If sorting in descending order and
239
+ # value is :last, include non-NULL values before NULL values.
240
+ #
241
+ # array_op.sort # array_sort(array)
242
+ # array_op.sort(desc: true) # array_sort(array, true)
243
+ # array_op.sort(nulls: :first) # array_sort(array, false, true)
244
+ # array_op.sort(desc: true, nulls: :last) # array_sort(array, true, false)
245
+ def sort(opts=OPTS)
246
+ desc = opts[:desc]
247
+ nulls = opts[:nulls]
248
+ if desc
249
+ if nulls == :last
250
+ function(:array_sort, true, false)
251
+ else
252
+ function(:array_sort, true)
253
+ end
254
+ elsif nulls == :first
255
+ function(:array_sort, false, true)
256
+ else
257
+ function(:array_sort)
258
+ end
259
+ end
260
+
220
261
  # Call the array_to_string method:
221
262
  #
222
263
  # array_op.join # array_to_string(array, '')
@@ -348,6 +348,8 @@ module Sequel
348
348
  # automatically parameterized.
349
349
  def auto_param_type(v)
350
350
  case v
351
+ when Symbol
352
+ false
351
353
  when String
352
354
  case v
353
355
  when LiteralString
@@ -394,7 +396,7 @@ module Sequel
394
396
  # there can be more than one parameter per column, so this doesn't prevent going
395
397
  # over the limit, though it does make it less likely.
396
398
  def default_import_slice
397
- 40
399
+ @opts[:no_auto_parameterize] ? super : 40
398
400
  end
399
401
 
400
402
  # Handle parameterization of multi_insert_sql
@@ -406,9 +408,9 @@ module Sequel
406
408
  end
407
409
  end
408
410
 
409
- # Whether the given argument is an array of integers or NULL values, recursively.
411
+ # Whether the given argument is an array or set of integers or NULL values, recursively.
410
412
  def _integer_array?(v)
411
- Array === v && v.all?{|x| nil == x || Integer === x}
413
+ (Array === v || Set === v) && v.all?{|x| nil == x || Integer === x}
412
414
  end
413
415
 
414
416
  # Create the bound variable string that will be used for the IN (int, ...) to = ANY($)
@@ -463,6 +465,11 @@ module Sequel
463
465
  @opts[:no_auto_parameterize] ? super : QueryString.new
464
466
  end
465
467
 
468
+ # A mutable string used as the prefix when explaining a query.
469
+ def explain_sql_string_origin(opts)
470
+ @opts[:no_auto_parameterize] ? super : (QueryString.new << super)
471
+ end
472
+
466
473
  # If subquery uses with_sql with a method name symbol, get the dataset
467
474
  # with_sql was called on, and use that as the subquery, recording the
468
475
  # arguments to with_sql that will be used to calculate the sql.