sequel 5.39.0 → 5.72.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +408 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +13 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +26 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.40.0.txt +40 -0
  17. data/doc/release_notes/5.41.0.txt +25 -0
  18. data/doc/release_notes/5.42.0.txt +136 -0
  19. data/doc/release_notes/5.43.0.txt +98 -0
  20. data/doc/release_notes/5.44.0.txt +32 -0
  21. data/doc/release_notes/5.45.0.txt +34 -0
  22. data/doc/release_notes/5.46.0.txt +87 -0
  23. data/doc/release_notes/5.47.0.txt +59 -0
  24. data/doc/release_notes/5.48.0.txt +14 -0
  25. data/doc/release_notes/5.49.0.txt +59 -0
  26. data/doc/release_notes/5.50.0.txt +78 -0
  27. data/doc/release_notes/5.51.0.txt +47 -0
  28. data/doc/release_notes/5.52.0.txt +87 -0
  29. data/doc/release_notes/5.53.0.txt +23 -0
  30. data/doc/release_notes/5.54.0.txt +27 -0
  31. data/doc/release_notes/5.55.0.txt +21 -0
  32. data/doc/release_notes/5.56.0.txt +51 -0
  33. data/doc/release_notes/5.57.0.txt +23 -0
  34. data/doc/release_notes/5.58.0.txt +31 -0
  35. data/doc/release_notes/5.59.0.txt +73 -0
  36. data/doc/release_notes/5.60.0.txt +22 -0
  37. data/doc/release_notes/5.61.0.txt +43 -0
  38. data/doc/release_notes/5.62.0.txt +132 -0
  39. data/doc/release_notes/5.63.0.txt +33 -0
  40. data/doc/release_notes/5.64.0.txt +50 -0
  41. data/doc/release_notes/5.65.0.txt +21 -0
  42. data/doc/release_notes/5.66.0.txt +24 -0
  43. data/doc/release_notes/5.67.0.txt +32 -0
  44. data/doc/release_notes/5.68.0.txt +61 -0
  45. data/doc/release_notes/5.69.0.txt +26 -0
  46. data/doc/release_notes/5.70.0.txt +35 -0
  47. data/doc/release_notes/5.71.0.txt +21 -0
  48. data/doc/release_notes/5.72.0.txt +33 -0
  49. data/doc/schema_modification.rdoc +1 -1
  50. data/doc/security.rdoc +9 -9
  51. data/doc/sharding.rdoc +3 -1
  52. data/doc/sql.rdoc +28 -16
  53. data/doc/testing.rdoc +22 -11
  54. data/doc/transactions.rdoc +6 -6
  55. data/doc/virtual_rows.rdoc +2 -2
  56. data/lib/sequel/adapters/ado/access.rb +1 -1
  57. data/lib/sequel/adapters/ado.rb +17 -17
  58. data/lib/sequel/adapters/amalgalite.rb +3 -5
  59. data/lib/sequel/adapters/ibmdb.rb +2 -2
  60. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  61. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  62. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  64. data/lib/sequel/adapters/jdbc.rb +16 -18
  65. data/lib/sequel/adapters/mysql.rb +92 -67
  66. data/lib/sequel/adapters/mysql2.rb +54 -49
  67. data/lib/sequel/adapters/odbc.rb +6 -2
  68. data/lib/sequel/adapters/oracle.rb +4 -3
  69. data/lib/sequel/adapters/postgres.rb +83 -40
  70. data/lib/sequel/adapters/shared/access.rb +11 -1
  71. data/lib/sequel/adapters/shared/db2.rb +30 -0
  72. data/lib/sequel/adapters/shared/mssql.rb +90 -9
  73. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  74. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  75. data/lib/sequel/adapters/shared/postgres.rb +496 -178
  76. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  77. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  78. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  79. data/lib/sequel/adapters/sqlite.rb +60 -18
  80. data/lib/sequel/adapters/tinytds.rb +1 -1
  81. data/lib/sequel/adapters/trilogy.rb +117 -0
  82. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  83. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  84. data/lib/sequel/ast_transformer.rb +6 -0
  85. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  86. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  87. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  88. data/lib/sequel/connection_pool/single.rb +6 -8
  89. data/lib/sequel/connection_pool/threaded.rb +14 -8
  90. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  91. data/lib/sequel/connection_pool.rb +55 -31
  92. data/lib/sequel/core.rb +28 -18
  93. data/lib/sequel/database/connecting.rb +27 -3
  94. data/lib/sequel/database/dataset.rb +16 -6
  95. data/lib/sequel/database/misc.rb +69 -14
  96. data/lib/sequel/database/query.rb +73 -2
  97. data/lib/sequel/database/schema_generator.rb +46 -53
  98. data/lib/sequel/database/schema_methods.rb +18 -2
  99. data/lib/sequel/dataset/actions.rb +108 -14
  100. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  101. data/lib/sequel/dataset/features.rb +20 -0
  102. data/lib/sequel/dataset/misc.rb +12 -2
  103. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  104. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  105. data/lib/sequel/dataset/query.rb +171 -44
  106. data/lib/sequel/dataset/sql.rb +182 -47
  107. data/lib/sequel/dataset.rb +4 -0
  108. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  109. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  110. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  111. data/lib/sequel/extensions/async_thread_pool.rb +439 -0
  112. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  113. data/lib/sequel/extensions/blank.rb +8 -0
  114. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  115. data/lib/sequel/extensions/connection_validator.rb +16 -11
  116. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  117. data/lib/sequel/extensions/core_refinements.rb +36 -11
  118. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  119. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  120. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  121. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  122. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  123. data/lib/sequel/extensions/index_caching.rb +5 -1
  124. data/lib/sequel/extensions/inflector.rb +9 -1
  125. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  126. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  127. data/lib/sequel/extensions/migration.rb +11 -2
  128. data/lib/sequel/extensions/named_timezones.rb +26 -6
  129. data/lib/sequel/extensions/pagination.rb +1 -1
  130. data/lib/sequel/extensions/pg_array.rb +32 -4
  131. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  132. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  133. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  134. data/lib/sequel/extensions/pg_enum.rb +2 -3
  135. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  136. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  137. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  138. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  139. data/lib/sequel/extensions/pg_inet.rb +10 -11
  140. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  141. data/lib/sequel/extensions/pg_interval.rb +45 -19
  142. data/lib/sequel/extensions/pg_json.rb +13 -15
  143. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  144. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +11 -24
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +21 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/query.rb +2 -0
  151. data/lib/sequel/extensions/s.rb +2 -1
  152. data/lib/sequel/extensions/schema_caching.rb +1 -1
  153. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  154. data/lib/sequel/extensions/server_block.rb +10 -13
  155. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  156. data/lib/sequel/extensions/sql_comments.rb +110 -3
  157. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  158. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  159. data/lib/sequel/extensions/string_agg.rb +1 -1
  160. data/lib/sequel/extensions/string_date_time.rb +19 -23
  161. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  162. data/lib/sequel/model/associations.rb +345 -101
  163. data/lib/sequel/model/base.rb +51 -27
  164. data/lib/sequel/model/dataset_module.rb +3 -0
  165. data/lib/sequel/model/errors.rb +10 -1
  166. data/lib/sequel/model/inflections.rb +1 -1
  167. data/lib/sequel/model/plugins.rb +5 -0
  168. data/lib/sequel/plugins/association_proxies.rb +2 -0
  169. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +87 -15
  172. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  173. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  174. data/lib/sequel/plugins/column_encryption.rb +728 -0
  175. data/lib/sequel/plugins/composition.rb +10 -4
  176. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  177. data/lib/sequel/plugins/constraint_validations.rb +10 -6
  178. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  179. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  180. data/lib/sequel/plugins/dirty.rb +1 -1
  181. data/lib/sequel/plugins/enum.rb +124 -0
  182. data/lib/sequel/plugins/finder.rb +4 -2
  183. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  184. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  185. data/lib/sequel/plugins/json_serializer.rb +39 -24
  186. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  187. data/lib/sequel/plugins/list.rb +3 -1
  188. data/lib/sequel/plugins/many_through_many.rb +109 -10
  189. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  190. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  191. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  192. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  193. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  194. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
  195. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  196. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  197. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  198. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  199. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  200. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  201. data/lib/sequel/plugins/serialization.rb +9 -3
  202. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  203. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  204. data/lib/sequel/plugins/sql_comments.rb +189 -0
  205. data/lib/sequel/plugins/static_cache.rb +39 -1
  206. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  207. data/lib/sequel/plugins/subclasses.rb +28 -11
  208. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  209. data/lib/sequel/plugins/timestamps.rb +1 -1
  210. data/lib/sequel/plugins/unused_associations.rb +521 -0
  211. data/lib/sequel/plugins/update_or_create.rb +1 -1
  212. data/lib/sequel/plugins/validate_associated.rb +22 -12
  213. data/lib/sequel/plugins/validation_helpers.rb +46 -12
  214. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  215. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  216. data/lib/sequel/sql.rb +1 -1
  217. data/lib/sequel/timezones.rb +12 -14
  218. data/lib/sequel/version.rb +1 -1
  219. metadata +132 -38
@@ -5,6 +5,7 @@ require_relative 'shared/postgres'
5
5
  begin
6
6
  require 'pg'
7
7
 
8
+ # :nocov:
8
9
  Sequel::Postgres::PGError = PG::Error if defined?(PG::Error)
9
10
  Sequel::Postgres::PGconn = PG::Connection if defined?(PG::Connection)
10
11
  Sequel::Postgres::PGresult = PG::Result if defined?(PG::Result)
@@ -14,30 +15,40 @@ begin
14
15
  raise LoadError unless defined?(PGconn::CONNECTION_OK)
15
16
  end
16
17
 
17
- Sequel::Postgres::USES_PG = true
18
18
  if defined?(PG::TypeMapByClass)
19
+ # :nocov:
19
20
  type_map = Sequel::Postgres::PG_QUERY_TYPE_MAP = PG::TypeMapByClass.new
20
21
  type_map[Integer] = PG::TextEncoder::Integer.new
21
22
  type_map[FalseClass] = type_map[TrueClass] = PG::TextEncoder::Boolean.new
22
23
  type_map[Float] = PG::TextEncoder::Float.new
23
24
  end
25
+
26
+ Sequel::Postgres::USES_PG = true
24
27
  rescue LoadError => e
28
+ # :nocov:
25
29
  begin
26
- require 'postgres-pr/postgres-compat'
27
- Sequel::Postgres::USES_PG = false
30
+ require 'sequel/postgres-pr'
28
31
  rescue LoadError
29
- raise e
32
+ begin
33
+ require 'postgres-pr/postgres-compat'
34
+ rescue LoadError
35
+ raise e
36
+ end
30
37
  end
38
+ Sequel::Postgres::USES_PG = false
39
+ # :nocov:
31
40
  end
32
41
 
33
42
  module Sequel
34
43
  module Postgres
35
- if Sequel::Postgres::USES_PG
44
+ # :nocov:
45
+ if USES_PG
36
46
  # Whether the given sequel_pg version integer is supported.
37
47
  def self.sequel_pg_version_supported?(version)
38
48
  version >= 10617
39
49
  end
40
50
  end
51
+ # :nocov:
41
52
 
42
53
  # PGconn subclass for connection specific methods used with the
43
54
  # pg or postgres-pr driver.
@@ -45,7 +56,9 @@ module Sequel
45
56
  # The underlying exception classes to reraise as disconnect errors
46
57
  # instead of regular database errors.
47
58
  DISCONNECT_ERROR_CLASSES = [IOError, Errno::EPIPE, Errno::ECONNRESET]
59
+ # :nocov:
48
60
  if defined?(::PG::ConnectionBad)
61
+ # :nocov:
49
62
  DISCONNECT_ERROR_CLASSES << ::PG::ConnectionBad
50
63
  end
51
64
  DISCONNECT_ERROR_CLASSES.freeze
@@ -71,11 +84,14 @@ module Sequel
71
84
  # are SQL strings.
72
85
  attr_reader :prepared_statements
73
86
 
87
+ # :nocov:
74
88
  unless public_method_defined?(:async_exec_params)
75
89
  alias async_exec_params async_exec
76
90
  end
77
- else
78
- # Make postgres-pr look like pg
91
+ elsif !const_defined?(:CONNECTION_OK)
92
+ # Handle old postgres-pr
93
+ # sequel-postgres-pr already implements this API
94
+
79
95
  CONNECTION_OK = -1
80
96
 
81
97
  # Escape bytea values. Uses historical format instead of hex
@@ -111,30 +127,29 @@ module Sequel
111
127
  alias cmd_tuples cmdtuples
112
128
  end
113
129
  end
130
+ # :nocov:
114
131
 
115
132
  # Raise a Sequel::DatabaseDisconnectError if a one of the disconnect
116
133
  # error classes is raised, or a PGError is raised and the connection
117
134
  # status cannot be determined or it is not OK.
118
135
  def check_disconnect_errors
136
+ yield
137
+ rescue *DISCONNECT_ERROR_CLASSES => e
138
+ disconnect = true
139
+ raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
140
+ rescue PGError => e
141
+ disconnect = false
119
142
  begin
120
- yield
121
- rescue *DISCONNECT_ERROR_CLASSES => e
143
+ s = status
144
+ rescue PGError
122
145
  disconnect = true
123
- raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
124
- rescue PGError => e
125
- disconnect = false
126
- begin
127
- s = status
128
- rescue PGError
129
- disconnect = true
130
- end
131
- status_ok = (s == Adapter::CONNECTION_OK)
132
- disconnect ||= !status_ok
133
- disconnect ||= e.message =~ DISCONNECT_ERROR_RE
134
- disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
135
- ensure
136
- block if status_ok && !disconnect
137
146
  end
147
+ status_ok = (s == Adapter::CONNECTION_OK)
148
+ disconnect ||= !status_ok
149
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
150
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
151
+ ensure
152
+ block if status_ok && !disconnect
138
153
  end
139
154
 
140
155
  # Execute the given SQL with this connection. If a block is given,
@@ -143,7 +158,7 @@ module Sequel
143
158
  args = args.map{|v| @db.bound_variable_arg(v, self)} if args
144
159
  q = check_disconnect_errors{execute_query(sql, args)}
145
160
  begin
146
- block_given? ? yield(q) : q.cmd_tuples
161
+ defined?(yield) ? yield(q) : q.cmd_tuples
147
162
  ensure
148
163
  q.clear if q && q.respond_to?(:clear)
149
164
  end
@@ -170,8 +185,12 @@ module Sequel
170
185
  case arg
171
186
  when Sequel::SQL::Blob
172
187
  {:value=>arg, :type=>17, :format=>1}
188
+ # :nocov:
189
+ # Not covered by tests as tests use pg_extended_date_support
190
+ # extension, which has basically the same code.
173
191
  when DateTime, Time
174
192
  literal(arg)
193
+ # :nocov:
175
194
  else
176
195
  arg
177
196
  end
@@ -206,7 +225,9 @@ module Sequel
206
225
  :sslmode => opts[:sslmode],
207
226
  :sslrootcert => opts[:sslrootcert]
208
227
  }.delete_if { |key, value| blank_object?(value) }
228
+ # :nocov:
209
229
  connection_params.merge!(opts[:driver_options]) if opts[:driver_options]
230
+ # :nocov:
210
231
  conn = Adapter.connect(opts[:conn_str] || connection_params)
211
232
 
212
233
  conn.instance_variable_set(:@prepared_statements, {})
@@ -214,6 +235,13 @@ module Sequel
214
235
  if receiver = opts[:notice_receiver]
215
236
  conn.set_notice_receiver(&receiver)
216
237
  end
238
+
239
+ # :nocov:
240
+ if conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
241
+ # :nocov:
242
+ conn.type_map_for_queries = PG_QUERY_TYPE_MAP
243
+ end
244
+ # :nocov:
217
245
  else
218
246
  unless typecast_value_boolean(@opts.fetch(:force_standard_strings, true))
219
247
  raise Error, "Cannot create connection using postgres-pr unless force_standard_strings is set"
@@ -228,12 +256,11 @@ module Sequel
228
256
  opts[:password]
229
257
  )
230
258
  end
259
+ # :nocov:
231
260
 
232
261
  conn.instance_variable_set(:@db, self)
233
- if USES_PG && conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
234
- conn.type_map_for_queries = PG_QUERY_TYPE_MAP
235
- end
236
262
 
263
+ # :nocov:
237
264
  if encoding = opts[:encoding] || opts[:charset]
238
265
  if conn.respond_to?(:set_client_encoding)
239
266
  conn.set_client_encoding(encoding)
@@ -241,6 +268,7 @@ module Sequel
241
268
  conn.async_exec("set client_encoding to '#{encoding}'")
242
269
  end
243
270
  end
271
+ # :nocov:
244
272
 
245
273
  connection_configuration_sqls(opts).each{|sql| conn.execute(sql)}
246
274
  conn
@@ -267,7 +295,9 @@ module Sequel
267
295
  nil
268
296
  end
269
297
 
298
+ # :nocov:
270
299
  if USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
300
+ # :nocov:
271
301
  # Return a hash of information about the related PGError (or Sequel::DatabaseError that
272
302
  # wraps a PGError), with the following entries (any of which may be +nil+):
273
303
  #
@@ -318,7 +348,9 @@ module Sequel
318
348
  synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
319
349
  end
320
350
 
351
+ # :nocov:
321
352
  if USES_PG
353
+ # :nocov:
322
354
  # +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
323
355
  # results directly to the caller. This method is only supported if pg is the
324
356
  # underlying ruby driver. This method should only be called if you want
@@ -350,7 +382,7 @@ module Sequel
350
382
  synchronize(opts[:server]) do |conn|
351
383
  conn.execute(copy_table_sql(table, opts))
352
384
  begin
353
- if block_given?
385
+ if defined?(yield)
354
386
  while buf = conn.get_copy_data
355
387
  yield buf
356
388
  end
@@ -400,16 +432,16 @@ module Sequel
400
432
  data = opts[:data]
401
433
  data = Array(data) if data.is_a?(String)
402
434
 
403
- if block_given? && data
435
+ if defined?(yield) && data
404
436
  raise Error, "Cannot provide both a :data option and a block to copy_into"
405
- elsif !block_given? && !data
437
+ elsif !defined?(yield) && !data
406
438
  raise Error, "Must provide either a :data option or a block to copy_into"
407
439
  end
408
440
 
409
441
  synchronize(opts[:server]) do |conn|
410
442
  conn.execute(copy_into_sql(table, opts))
411
443
  begin
412
- if block_given?
444
+ if defined?(yield)
413
445
  while buf = yield
414
446
  conn.put_copy_data(buf)
415
447
  end
@@ -511,32 +543,35 @@ module Sequel
511
543
  def adapter_initialize
512
544
  @use_iso_date_format = typecast_value_boolean(@opts.fetch(:use_iso_date_format, true))
513
545
  initialize_postgres_adapter
546
+ # :nocov:
514
547
  add_conversion_proc(17, method(:unescape_bytea)) if USES_PG
515
548
  add_conversion_proc(1082, TYPE_TRANSLATOR_DATE) if @use_iso_date_format
549
+ # :nocov:
516
550
  self.convert_infinite_timestamps = @opts[:convert_infinite_timestamps]
517
551
  end
518
552
 
519
553
  # Convert exceptions raised from the block into DatabaseErrors.
520
554
  def check_database_errors
521
- begin
522
- yield
523
- rescue => e
524
- raise_error(e, :classes=>database_error_classes)
525
- end
555
+ yield
556
+ rescue => e
557
+ raise_error(e, :classes=>database_error_classes)
526
558
  end
527
-
528
559
  # Set the DateStyle to ISO if configured, for faster date parsing.
529
560
  def connection_configuration_sqls(opts=@opts)
530
561
  sqls = super
562
+ # :nocov:
531
563
  sqls << "SET DateStyle = 'ISO'" if @use_iso_date_format
564
+ # :nocov:
532
565
  sqls
533
566
  end
534
567
 
568
+ # :nocov:
535
569
  if USES_PG
536
570
  def unescape_bytea(s)
537
571
  ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s))
538
572
  end
539
573
  end
574
+ # :nocov:
540
575
 
541
576
  DATABASE_ERROR_CLASSES = [PGError].freeze
542
577
  def database_error_classes
@@ -550,7 +585,9 @@ module Sequel
550
585
  end
551
586
 
552
587
  def database_exception_sqlstate(exception, opts)
588
+ # :nocov:
553
589
  if exception.respond_to?(:result) && (result = exception.result)
590
+ # :nocov:
554
591
  result.error_field(PGresult::PG_DIAG_SQLSTATE)
555
592
  end
556
593
  end
@@ -590,7 +627,7 @@ module Sequel
590
627
 
591
628
  q = conn.check_disconnect_errors{log_connection_yield(log_sql, conn, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
592
629
  begin
593
- block_given? ? yield(q) : q.cmd_tuples
630
+ defined?(yield) ? yield(q) : q.cmd_tuples
594
631
  ensure
595
632
  q.clear if q && q.respond_to?(:clear)
596
633
  end
@@ -616,7 +653,7 @@ module Sequel
616
653
 
617
654
  # Use a cursor for paging.
618
655
  def paged_each(opts=OPTS, &block)
619
- unless block_given?
656
+ unless defined?(yield)
620
657
  return enum_for(:paged_each, opts)
621
658
  end
622
659
  use_cursor(opts).each(&block)
@@ -660,7 +697,9 @@ module Sequel
660
697
  clone(:where=>Sequel.lit(['CURRENT OF '], Sequel.identifier(cursor_name)))
661
698
  end
662
699
 
700
+ # :nocov:
663
701
  if USES_PG
702
+ # :nocov:
664
703
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
665
704
 
666
705
  # PostgreSQL specific argument mapper used for mapping the named
@@ -717,7 +756,9 @@ module Sequel
717
756
  sql = String.new
718
757
  sql << "CALL "
719
758
  identifier_append(sql, name)
720
- literal_append(sql, args)
759
+ sql << "("
760
+ expression_list_append(sql, args)
761
+ sql << ")"
721
762
  with_sql_first(sql)
722
763
  end
723
764
 
@@ -805,6 +846,7 @@ module Sequel
805
846
  end
806
847
  end
807
848
 
849
+ # :nocov:
808
850
  if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
809
851
  begin
810
852
  require 'sequel_pg'
@@ -816,3 +858,4 @@ if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
816
858
  rescue LoadError
817
859
  end
818
860
  end
861
+ # :nocov:
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../utils/emulate_offset_with_reverse_and_count'
4
4
  require_relative '../utils/unmodified_identifiers'
5
+ require_relative '../utils/columns_limit_1'
5
6
 
6
7
  module Sequel
7
8
  module Access
@@ -16,7 +17,7 @@ module Sequel
16
17
 
17
18
  # Doesn't work, due to security restrictions on MSysObjects
18
19
  #def tables
19
- # from(:MSysObjects).where(:Type=>1, :Flags=>0).select_map(:Name).map(&:to_sym)
20
+ # from(:MSysObjects).where(Type: 1, Flags: 0).select_map(:Name).map(&:to_sym)
20
21
  #end
21
22
 
22
23
  # Access doesn't support renaming tables from an SQL query,
@@ -56,6 +57,14 @@ module Sequel
56
57
  DATABASE_ERROR_REGEXPS
57
58
  end
58
59
 
60
+ # Access's Byte type will accept much larger values,
61
+ # even though it only stores 0-255. Do not set min/max
62
+ # values for the Byte type.
63
+ def column_schema_integer_min_max_values(column)
64
+ return if /byte/i =~ column[:db_type]
65
+ super
66
+ end
67
+
59
68
  def drop_index_sql(table, op)
60
69
  "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
61
70
  end
@@ -83,6 +92,7 @@ module Sequel
83
92
  end)
84
93
  include EmulateOffsetWithReverseAndCount
85
94
  include UnmodifiedIdentifiers::DatasetMethods
95
+ include ::Sequel::Dataset::ColumnsLimit1
86
96
 
87
97
  EXTRACT_MAP = {:year=>"'yyyy'", :month=>"'m'", :day=>"'d'", :hour=>"'h'", :minute=>"'n'", :second=>"'s'"}.freeze
88
98
  EXTRACT_MAP.each_value(&:freeze)
@@ -1,6 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  require_relative '../utils/emulate_offset_with_row_number'
4
+ require_relative '../utils/columns_limit_1'
4
5
 
5
6
  module Sequel
6
7
  module DB2
@@ -273,6 +274,7 @@ module Sequel
273
274
 
274
275
  module DatasetMethods
275
276
  include EmulateOffsetWithRowNumber
277
+ include ::Sequel::Dataset::ColumnsLimit1
276
278
 
277
279
  BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}.freeze
278
280
 
@@ -336,6 +338,11 @@ module Sequel
336
338
  true
337
339
  end
338
340
 
341
+ # DB2 supports MERGE
342
+ def supports_merge?
343
+ true
344
+ end
345
+
339
346
  # DB2 does not support multiple columns in IN.
340
347
  def supports_multiple_column_in?
341
348
  false
@@ -358,6 +365,29 @@ module Sequel
358
365
 
359
366
  private
360
367
 
368
+ # Normalize conditions for MERGE WHEN.
369
+ def _merge_when_conditions_sql(sql, data)
370
+ if data.has_key?(:conditions)
371
+ sql << " AND "
372
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
373
+ end
374
+ end
375
+
376
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
377
+ # type error.
378
+ def _normalize_merge_when_conditions(conditions)
379
+ case conditions
380
+ when nil, false
381
+ {1=>0}
382
+ when true
383
+ {1=>1}
384
+ when Sequel::SQL::DelayedEvaluation
385
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
386
+ else
387
+ conditions
388
+ end
389
+ end
390
+
361
391
  def empty_from_sql
362
392
  ' FROM "SYSIBM"."SYSDUMMY1"'
363
393
  end
@@ -24,6 +24,10 @@ module Sequel
24
24
  # Database object.
25
25
  attr_accessor :mssql_unicode_strings
26
26
 
27
+ # Whether to use LIKE without COLLATE Latin1_General_CS_AS. Skipping the COLLATE
28
+ # can significantly increase performance in some cases.
29
+ attr_accessor :like_without_collate
30
+
27
31
  # Execute the given stored procedure with the given name.
28
32
  #
29
33
  # Options:
@@ -321,6 +325,11 @@ module Sequel
321
325
  false
322
326
  end
323
327
 
328
+ # MSSQL tinyint types are unsigned.
329
+ def column_schema_tinyint_type_is_unsigned?
330
+ true
331
+ end
332
+
324
333
  # Handle MSSQL specific default format.
325
334
  def column_schema_normalize_default(default, type)
326
335
  if m = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/.match(default)
@@ -395,10 +404,15 @@ module Sequel
395
404
  # Backbone of the tables and views support.
396
405
  def information_schema_tables(type, opts)
397
406
  m = output_identifier_meth
398
- metadata_dataset.from(Sequel[:information_schema][:tables].as(:t)).
407
+ schema = opts[:schema]||'dbo'
408
+ tables = metadata_dataset.from(Sequel[:information_schema][:tables].as(:t)).
399
409
  select(:table_name).
400
- where(:table_type=>type, :table_schema=>(opts[:schema]||'dbo').to_s).
410
+ where(:table_type=>type, :table_schema=>schema.to_s).
401
411
  map{|x| m.call(x[:table_name])}
412
+
413
+ tables.map!{|t| Sequel.qualify(m.call(schema).to_s, m.call(t).to_s)} if opts[:qualify]
414
+
415
+ tables
402
416
  end
403
417
 
404
418
  # Always quote identifiers in the metadata_dataset, so schema parsing works.
@@ -548,9 +562,9 @@ module Sequel
548
562
  when :'||'
549
563
  super(sql, :+, args)
550
564
  when :LIKE, :"NOT LIKE"
551
- super(sql, op, args.map{|a| Sequel.lit(["(", " COLLATE Latin1_General_CS_AS)"], a)})
565
+ super(sql, op, complex_expression_sql_like_args(args, " COLLATE Latin1_General_CS_AS)"))
552
566
  when :ILIKE, :"NOT ILIKE"
553
- super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args.map{|a| Sequel.lit(["(", " COLLATE Latin1_General_CI_AS)"], a)})
567
+ super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), complex_expression_sql_like_args(args, " COLLATE Latin1_General_CI_AS)"))
554
568
  when :<<, :>>
555
569
  complex_expression_emulate_append(sql, op, args)
556
570
  when :extract
@@ -582,6 +596,18 @@ module Sequel
582
596
  end
583
597
  end
584
598
 
599
+ # For a dataset with custom SQL, since it may include ORDER BY, you
600
+ # cannot wrap it in a subquery. Load entire query in this case to get
601
+ # the number of rows. In general, you should avoid calling this method
602
+ # on datasets with custom SQL.
603
+ def count(*a, &block)
604
+ if (@opts[:sql] && a.empty? && !block)
605
+ naked.to_a.length
606
+ else
607
+ super
608
+ end
609
+ end
610
+
585
611
  # Uses CROSS APPLY to join the given table into the current dataset.
586
612
  def cross_apply(table)
587
613
  join_table(:cross_apply, table)
@@ -592,6 +618,19 @@ module Sequel
592
618
  clone(:disable_insert_output=>true)
593
619
  end
594
620
 
621
+ # For a dataset with custom SQL, since it may include ORDER BY, you
622
+ # cannot wrap it in a subquery. Run query, and if it returns any
623
+ # records, return true. In general, you should avoid calling this method
624
+ # on datasets with custom SQL.
625
+ def empty?
626
+ if @opts[:sql]
627
+ naked.each{return false}
628
+ true
629
+ else
630
+ super
631
+ end
632
+ end
633
+
595
634
  # MSSQL treats [] as a metacharacter in LIKE expresions.
596
635
  def escape_like(string)
597
636
  string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
@@ -730,6 +769,11 @@ module Sequel
730
769
  false
731
770
  end
732
771
 
772
+ # MSSQL 2008+ supports MERGE
773
+ def supports_merge?
774
+ is_2008_or_later?
775
+ end
776
+
733
777
  # MSSQL 2005+ supports modifying joined datasets
734
778
  def supports_modifying_joins?
735
779
  is_2005_or_later?
@@ -791,11 +835,10 @@ module Sequel
791
835
  if opts[:return] == :primary_key && !@opts[:output]
792
836
  output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
793
837
  elsif @opts[:output]
794
- statements = multi_insert_sql(columns, values)
795
- ds = naked
796
- @db.transaction(opts.merge(:server=>@opts[:server])) do
797
- statements.map{|st| ds.with_sql(st)}
798
- end.first.map{|v| v.length == 1 ? v.values.first : v}
838
+ # no transaction: our multi_insert_sql_strategy should guarantee
839
+ # that there's only ever a single statement.
840
+ sql = multi_insert_sql(columns, values)[0]
841
+ naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v}
799
842
  else
800
843
  super
801
844
  end
@@ -820,6 +863,35 @@ module Sequel
820
863
 
821
864
  private
822
865
 
866
+ # Normalize conditions for MERGE WHEN.
867
+ def _merge_when_conditions_sql(sql, data)
868
+ if data.has_key?(:conditions)
869
+ sql << " AND "
870
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
871
+ end
872
+ end
873
+
874
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
875
+ # type error.
876
+ def _normalize_merge_when_conditions(conditions)
877
+ case conditions
878
+ when nil, false
879
+ {1=>0}
880
+ when true
881
+ {1=>1}
882
+ when Sequel::SQL::DelayedEvaluation
883
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
884
+ else
885
+ conditions
886
+ end
887
+ end
888
+
889
+ # MSSQL requires a semicolon at the end of MERGE.
890
+ def _merge_when_sql(sql)
891
+ super
892
+ sql << ';'
893
+ end
894
+
823
895
  # MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
824
896
  def aggregate_dataset
825
897
  (options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
@@ -847,6 +919,15 @@ module Sequel
847
919
  server_version >= 11000000
848
920
  end
849
921
 
922
+ # Determine whether to add the COLLATE for LIKE arguments, based on the Database setting.
923
+ def complex_expression_sql_like_args(args, collation)
924
+ if db.like_without_collate
925
+ args
926
+ else
927
+ args.map{|a| Sequel.lit(["(", collation], a)}
928
+ end
929
+ end
930
+
850
931
  # Use strict ISO-8601 format with T between date and time,
851
932
  # since that is the format that is multilanguage and not
852
933
  # DATEFORMAT dependent.
@@ -187,6 +187,15 @@ module Sequel
187
187
  def views(opts=OPTS)
188
188
  full_tables('VIEW', opts)
189
189
  end
190
+
191
+ # Renames multiple tables in a single call.
192
+ #
193
+ # DB.rename_tables [:items, :old_items], [:other_items, :old_other_items]
194
+ # # RENAME TABLE items TO old_items, other_items TO old_other_items
195
+ def rename_tables(*renames)
196
+ execute_ddl(rename_tables_sql(renames))
197
+ renames.each{|from,| remove_cached_schema(from)}
198
+ end
190
199
 
191
200
  private
192
201
 
@@ -324,6 +333,12 @@ module Sequel
324
333
  sqls << "SET sql_mode = '#{sql_mode}'"
325
334
  end
326
335
 
336
+ # Disable the use of split_materialized in the optimizer. This is
337
+ # needed to pass association tests on MariaDB 10.5+.
338
+ if opts[:disable_split_materialized] && typecast_value_boolean(opts[:disable_split_materialized])
339
+ sqls << "SET SESSION optimizer_switch='split_materialized=off'"
340
+ end
341
+
327
342
  sqls
328
343
  end
329
344
 
@@ -347,6 +362,12 @@ module Sequel
347
362
  end
348
363
  end
349
364
 
365
+ # Support :on_update_current_timestamp option.
366
+ def column_definition_default_sql(sql, column)
367
+ super
368
+ sql << " ON UPDATE CURRENT_TIMESTAMP" if column[:on_update_current_timestamp]
369
+ end
370
+
350
371
  # Add generation clause SQL fragment to column creation SQL.
351
372
  def column_definition_generated_sql(sql, column)
352
373
  if (generated_expression = column[:generated_always_as])
@@ -473,6 +494,14 @@ module Sequel
473
494
  schema(table).select{|a| a[1][:primary_key]}.map{|a| a[0]}
474
495
  end
475
496
 
497
+ # SQL statement for renaming multiple tables.
498
+ def rename_tables_sql(renames)
499
+ rename_tos = renames.map do |from, to|
500
+ "#{quote_schema_table(from)} TO #{quote_schema_table(to)}"
501
+ end.join(', ')
502
+ "RENAME TABLE #{rename_tos}"
503
+ end
504
+
476
505
  # Rollback the currently open XA transaction
477
506
  def rollback_transaction(conn, opts=OPTS)
478
507
  if (s = opts[:prepare]) && savepoint_level(conn) <= 1
@@ -516,19 +545,35 @@ module Sequel
516
545
  row[:default] = row.delete(:Default)
517
546
  row[:db_type] = row.delete(:Type)
518
547
  row[:type] = schema_column_type(row[:db_type])
548
+ row[:extra] = extra
519
549
  [m.call(row.delete(:Field)), row]
520
550
  end
521
551
  end
522
552
 
553
+ # Return nil if CHECK constraints are not supported, because
554
+ # versions that don't support check constraints don't raise
555
+ # errors for values outside of range.
556
+ def column_schema_integer_min_max_values(column)
557
+ super if supports_check_constraints?
558
+ end
559
+
560
+ # Return nil if CHECK constraints are not supported, because
561
+ # versions that don't support check constraints don't raise
562
+ # errors for values outside of range.
563
+ def column_schema_decimal_min_max_values(column)
564
+ super if supports_check_constraints?
565
+ end
566
+
523
567
  # Split DROP INDEX ops on MySQL 5.6+, as dropping them in the same
524
568
  # statement as dropping a related foreign key causes an error.
525
569
  def split_alter_table_op?(op)
526
570
  server_version >= 50600 && (op[:op] == :drop_index || (op[:op] == :drop_constraint && op[:type] == :unique))
527
571
  end
528
572
 
529
- # Whether the database supports CHECK constraints
573
+ # CHECK constraints only supported on MariaDB 10.2+ and MySQL 8.0.19+
574
+ # (at least MySQL documents DROP CONSTRAINT was supported in 8.0.19+).
530
575
  def supports_check_constraints?
531
- mariadb? && server_version >= 100200
576
+ server_version >= (mariadb? ? 100200 : 80019)
532
577
  end
533
578
 
534
579
  # MySQL can combine multiple alter table ops into a single query.