sequel 5.39.0 → 5.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +336 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/release_notes/5.65.0.txt +21 -0
  41. data/doc/release_notes/5.66.0.txt +24 -0
  42. data/doc/schema_modification.rdoc +1 -1
  43. data/doc/security.rdoc +9 -9
  44. data/doc/sql.rdoc +28 -16
  45. data/doc/testing.rdoc +22 -11
  46. data/doc/transactions.rdoc +6 -6
  47. data/doc/virtual_rows.rdoc +2 -2
  48. data/lib/sequel/adapters/ado/access.rb +1 -1
  49. data/lib/sequel/adapters/ado.rb +17 -17
  50. data/lib/sequel/adapters/amalgalite.rb +3 -5
  51. data/lib/sequel/adapters/ibmdb.rb +2 -2
  52. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  53. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  54. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  56. data/lib/sequel/adapters/jdbc.rb +16 -18
  57. data/lib/sequel/adapters/mysql.rb +80 -67
  58. data/lib/sequel/adapters/mysql2.rb +54 -49
  59. data/lib/sequel/adapters/odbc.rb +6 -2
  60. data/lib/sequel/adapters/oracle.rb +4 -3
  61. data/lib/sequel/adapters/postgres.rb +83 -40
  62. data/lib/sequel/adapters/shared/access.rb +11 -1
  63. data/lib/sequel/adapters/shared/db2.rb +30 -0
  64. data/lib/sequel/adapters/shared/mssql.rb +83 -7
  65. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  66. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  67. data/lib/sequel/adapters/shared/postgres.rb +430 -174
  68. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  69. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  70. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  71. data/lib/sequel/adapters/sqlite.rb +60 -18
  72. data/lib/sequel/adapters/tinytds.rb +1 -1
  73. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  74. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  75. data/lib/sequel/ast_transformer.rb +6 -0
  76. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  77. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  78. data/lib/sequel/connection_pool/single.rb +6 -8
  79. data/lib/sequel/connection_pool/threaded.rb +8 -8
  80. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  81. data/lib/sequel/connection_pool.rb +47 -30
  82. data/lib/sequel/core.rb +28 -18
  83. data/lib/sequel/database/connecting.rb +26 -2
  84. data/lib/sequel/database/misc.rb +69 -14
  85. data/lib/sequel/database/query.rb +73 -2
  86. data/lib/sequel/database/schema_generator.rb +45 -52
  87. data/lib/sequel/database/schema_methods.rb +17 -1
  88. data/lib/sequel/dataset/actions.rb +108 -14
  89. data/lib/sequel/dataset/features.rb +20 -0
  90. data/lib/sequel/dataset/misc.rb +12 -2
  91. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  92. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  93. data/lib/sequel/dataset/query.rb +118 -16
  94. data/lib/sequel/dataset/sql.rb +182 -47
  95. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  96. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  97. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  98. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  99. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  100. data/lib/sequel/extensions/blank.rb +8 -0
  101. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  102. data/lib/sequel/extensions/core_refinements.rb +36 -11
  103. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  104. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  105. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  106. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  107. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  108. data/lib/sequel/extensions/inflector.rb +9 -1
  109. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  110. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  111. data/lib/sequel/extensions/migration.rb +7 -2
  112. data/lib/sequel/extensions/named_timezones.rb +26 -6
  113. data/lib/sequel/extensions/pagination.rb +1 -1
  114. data/lib/sequel/extensions/pg_array.rb +23 -3
  115. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  116. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  117. data/lib/sequel/extensions/pg_enum.rb +1 -1
  118. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  119. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  120. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  121. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  122. data/lib/sequel/extensions/pg_inet.rb +10 -11
  123. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  124. data/lib/sequel/extensions/pg_interval.rb +45 -19
  125. data/lib/sequel/extensions/pg_json.rb +13 -15
  126. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  127. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  128. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  129. data/lib/sequel/extensions/pg_range.rb +10 -23
  130. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  131. data/lib/sequel/extensions/pg_row.rb +19 -13
  132. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  133. data/lib/sequel/extensions/query.rb +2 -0
  134. data/lib/sequel/extensions/s.rb +2 -1
  135. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  136. data/lib/sequel/extensions/server_block.rb +8 -12
  137. data/lib/sequel/extensions/sql_comments.rb +110 -3
  138. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  139. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  140. data/lib/sequel/extensions/string_agg.rb +1 -1
  141. data/lib/sequel/extensions/string_date_time.rb +19 -23
  142. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  143. data/lib/sequel/model/associations.rb +345 -101
  144. data/lib/sequel/model/base.rb +51 -27
  145. data/lib/sequel/model/errors.rb +10 -1
  146. data/lib/sequel/model/inflections.rb +1 -1
  147. data/lib/sequel/model/plugins.rb +5 -0
  148. data/lib/sequel/plugins/association_proxies.rb +2 -0
  149. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  150. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  151. data/lib/sequel/plugins/auto_validations.rb +87 -15
  152. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  153. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  154. data/lib/sequel/plugins/column_encryption.rb +728 -0
  155. data/lib/sequel/plugins/composition.rb +10 -4
  156. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  157. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  158. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  159. data/lib/sequel/plugins/dirty.rb +1 -1
  160. data/lib/sequel/plugins/enum.rb +124 -0
  161. data/lib/sequel/plugins/finder.rb +4 -2
  162. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  163. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  164. data/lib/sequel/plugins/json_serializer.rb +39 -24
  165. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  166. data/lib/sequel/plugins/list.rb +3 -1
  167. data/lib/sequel/plugins/many_through_many.rb +109 -10
  168. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  169. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  170. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  171. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  172. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  173. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  174. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  175. data/lib/sequel/plugins/serialization.rb +9 -3
  176. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  177. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  178. data/lib/sequel/plugins/sql_comments.rb +189 -0
  179. data/lib/sequel/plugins/static_cache.rb +1 -1
  180. data/lib/sequel/plugins/subclasses.rb +28 -11
  181. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  182. data/lib/sequel/plugins/timestamps.rb +1 -1
  183. data/lib/sequel/plugins/unused_associations.rb +521 -0
  184. data/lib/sequel/plugins/update_or_create.rb +1 -1
  185. data/lib/sequel/plugins/validate_associated.rb +22 -12
  186. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  187. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  188. data/lib/sequel/sql.rb +1 -1
  189. data/lib/sequel/timezones.rb +12 -14
  190. data/lib/sequel/version.rb +1 -1
  191. metadata +103 -43
@@ -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)
@@ -548,9 +557,9 @@ module Sequel
548
557
  when :'||'
549
558
  super(sql, :+, args)
550
559
  when :LIKE, :"NOT LIKE"
551
- super(sql, op, args.map{|a| Sequel.lit(["(", " COLLATE Latin1_General_CS_AS)"], a)})
560
+ super(sql, op, complex_expression_sql_like_args(args, " COLLATE Latin1_General_CS_AS)"))
552
561
  when :ILIKE, :"NOT ILIKE"
553
- super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args.map{|a| Sequel.lit(["(", " COLLATE Latin1_General_CI_AS)"], a)})
562
+ super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), complex_expression_sql_like_args(args, " COLLATE Latin1_General_CI_AS)"))
554
563
  when :<<, :>>
555
564
  complex_expression_emulate_append(sql, op, args)
556
565
  when :extract
@@ -582,6 +591,18 @@ module Sequel
582
591
  end
583
592
  end
584
593
 
594
+ # For a dataset with custom SQL, since it may include ORDER BY, you
595
+ # cannot wrap it in a subquery. Load entire query in this case to get
596
+ # the number of rows. In general, you should avoid calling this method
597
+ # on datasets with custom SQL.
598
+ def count(*a, &block)
599
+ if (@opts[:sql] && a.empty? && !block)
600
+ naked.to_a.length
601
+ else
602
+ super
603
+ end
604
+ end
605
+
585
606
  # Uses CROSS APPLY to join the given table into the current dataset.
586
607
  def cross_apply(table)
587
608
  join_table(:cross_apply, table)
@@ -592,6 +613,19 @@ module Sequel
592
613
  clone(:disable_insert_output=>true)
593
614
  end
594
615
 
616
+ # For a dataset with custom SQL, since it may include ORDER BY, you
617
+ # cannot wrap it in a subquery. Run query, and if it returns any
618
+ # records, return true. In general, you should avoid calling this method
619
+ # on datasets with custom SQL.
620
+ def empty?
621
+ if @opts[:sql]
622
+ naked.each{return false}
623
+ true
624
+ else
625
+ super
626
+ end
627
+ end
628
+
595
629
  # MSSQL treats [] as a metacharacter in LIKE expresions.
596
630
  def escape_like(string)
597
631
  string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
@@ -730,6 +764,11 @@ module Sequel
730
764
  false
731
765
  end
732
766
 
767
+ # MSSQL 2008+ supports MERGE
768
+ def supports_merge?
769
+ is_2008_or_later?
770
+ end
771
+
733
772
  # MSSQL 2005+ supports modifying joined datasets
734
773
  def supports_modifying_joins?
735
774
  is_2005_or_later?
@@ -791,11 +830,10 @@ module Sequel
791
830
  if opts[:return] == :primary_key && !@opts[:output]
792
831
  output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
793
832
  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}
833
+ # no transaction: our multi_insert_sql_strategy should guarantee
834
+ # that there's only ever a single statement.
835
+ sql = multi_insert_sql(columns, values)[0]
836
+ naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v}
799
837
  else
800
838
  super
801
839
  end
@@ -820,6 +858,35 @@ module Sequel
820
858
 
821
859
  private
822
860
 
861
+ # Normalize conditions for MERGE WHEN.
862
+ def _merge_when_conditions_sql(sql, data)
863
+ if data.has_key?(:conditions)
864
+ sql << " AND "
865
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
866
+ end
867
+ end
868
+
869
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
870
+ # type error.
871
+ def _normalize_merge_when_conditions(conditions)
872
+ case conditions
873
+ when nil, false
874
+ {1=>0}
875
+ when true
876
+ {1=>1}
877
+ when Sequel::SQL::DelayedEvaluation
878
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
879
+ else
880
+ conditions
881
+ end
882
+ end
883
+
884
+ # MSSQL requires a semicolon at the end of MERGE.
885
+ def _merge_when_sql(sql)
886
+ super
887
+ sql << ';'
888
+ end
889
+
823
890
  # MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
824
891
  def aggregate_dataset
825
892
  (options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
@@ -847,6 +914,15 @@ module Sequel
847
914
  server_version >= 11000000
848
915
  end
849
916
 
917
+ # Determine whether to add the COLLATE for LIKE arguments, based on the Database setting.
918
+ def complex_expression_sql_like_args(args, collation)
919
+ if db.like_without_collate
920
+ args
921
+ else
922
+ args.map{|a| Sequel.lit(["(", collation], a)}
923
+ end
924
+ end
925
+
850
926
  # Use strict ISO-8601 format with T between date and time,
851
927
  # since that is the format that is multilanguage and not
852
928
  # 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.