sequel 5.45.0 → 5.77.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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +434 -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 +27 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +28 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.46.0.txt +87 -0
  17. data/doc/release_notes/5.47.0.txt +59 -0
  18. data/doc/release_notes/5.48.0.txt +14 -0
  19. data/doc/release_notes/5.49.0.txt +59 -0
  20. data/doc/release_notes/5.50.0.txt +78 -0
  21. data/doc/release_notes/5.51.0.txt +47 -0
  22. data/doc/release_notes/5.52.0.txt +87 -0
  23. data/doc/release_notes/5.53.0.txt +23 -0
  24. data/doc/release_notes/5.54.0.txt +27 -0
  25. data/doc/release_notes/5.55.0.txt +21 -0
  26. data/doc/release_notes/5.56.0.txt +51 -0
  27. data/doc/release_notes/5.57.0.txt +23 -0
  28. data/doc/release_notes/5.58.0.txt +31 -0
  29. data/doc/release_notes/5.59.0.txt +73 -0
  30. data/doc/release_notes/5.60.0.txt +22 -0
  31. data/doc/release_notes/5.61.0.txt +43 -0
  32. data/doc/release_notes/5.62.0.txt +132 -0
  33. data/doc/release_notes/5.63.0.txt +33 -0
  34. data/doc/release_notes/5.64.0.txt +50 -0
  35. data/doc/release_notes/5.65.0.txt +21 -0
  36. data/doc/release_notes/5.66.0.txt +24 -0
  37. data/doc/release_notes/5.67.0.txt +32 -0
  38. data/doc/release_notes/5.68.0.txt +61 -0
  39. data/doc/release_notes/5.69.0.txt +26 -0
  40. data/doc/release_notes/5.70.0.txt +35 -0
  41. data/doc/release_notes/5.71.0.txt +21 -0
  42. data/doc/release_notes/5.72.0.txt +33 -0
  43. data/doc/release_notes/5.73.0.txt +66 -0
  44. data/doc/release_notes/5.74.0.txt +45 -0
  45. data/doc/release_notes/5.75.0.txt +35 -0
  46. data/doc/release_notes/5.76.0.txt +86 -0
  47. data/doc/release_notes/5.77.0.txt +63 -0
  48. data/doc/schema_modification.rdoc +1 -1
  49. data/doc/security.rdoc +9 -9
  50. data/doc/sharding.rdoc +3 -1
  51. data/doc/sql.rdoc +27 -15
  52. data/doc/testing.rdoc +23 -13
  53. data/doc/transactions.rdoc +6 -6
  54. data/doc/virtual_rows.rdoc +1 -1
  55. data/lib/sequel/adapters/ado/access.rb +1 -1
  56. data/lib/sequel/adapters/ado.rb +1 -1
  57. data/lib/sequel/adapters/amalgalite.rb +3 -5
  58. data/lib/sequel/adapters/ibmdb.rb +3 -3
  59. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  60. data/lib/sequel/adapters/jdbc/h2.rb +63 -10
  61. data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
  62. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  63. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  64. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  65. data/lib/sequel/adapters/jdbc.rb +24 -22
  66. data/lib/sequel/adapters/mysql.rb +92 -67
  67. data/lib/sequel/adapters/mysql2.rb +56 -51
  68. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  69. data/lib/sequel/adapters/odbc.rb +1 -1
  70. data/lib/sequel/adapters/oracle.rb +4 -3
  71. data/lib/sequel/adapters/postgres.rb +89 -45
  72. data/lib/sequel/adapters/shared/access.rb +11 -1
  73. data/lib/sequel/adapters/shared/db2.rb +42 -0
  74. data/lib/sequel/adapters/shared/mssql.rb +91 -10
  75. data/lib/sequel/adapters/shared/mysql.rb +78 -3
  76. data/lib/sequel/adapters/shared/oracle.rb +86 -7
  77. data/lib/sequel/adapters/shared/postgres.rb +576 -171
  78. data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
  79. data/lib/sequel/adapters/shared/sqlite.rb +92 -8
  80. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  81. data/lib/sequel/adapters/sqlite.rb +99 -18
  82. data/lib/sequel/adapters/tinytds.rb +1 -1
  83. data/lib/sequel/adapters/trilogy.rb +117 -0
  84. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  85. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  86. data/lib/sequel/ast_transformer.rb +6 -0
  87. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  89. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  90. data/lib/sequel/connection_pool/single.rb +6 -8
  91. data/lib/sequel/connection_pool/threaded.rb +14 -8
  92. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  93. data/lib/sequel/connection_pool.rb +57 -31
  94. data/lib/sequel/core.rb +17 -18
  95. data/lib/sequel/database/connecting.rb +27 -3
  96. data/lib/sequel/database/dataset.rb +16 -6
  97. data/lib/sequel/database/misc.rb +70 -14
  98. data/lib/sequel/database/query.rb +73 -2
  99. data/lib/sequel/database/schema_generator.rb +11 -6
  100. data/lib/sequel/database/schema_methods.rb +23 -4
  101. data/lib/sequel/database/transactions.rb +6 -0
  102. data/lib/sequel/dataset/actions.rb +111 -15
  103. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  104. data/lib/sequel/dataset/features.rb +20 -1
  105. data/lib/sequel/dataset/misc.rb +12 -2
  106. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  107. data/lib/sequel/dataset/query.rb +170 -41
  108. data/lib/sequel/dataset/sql.rb +190 -71
  109. data/lib/sequel/dataset.rb +4 -0
  110. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  111. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  112. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  113. data/lib/sequel/extensions/async_thread_pool.rb +14 -13
  114. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  115. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  116. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  117. data/lib/sequel/extensions/connection_validator.rb +16 -11
  118. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  119. data/lib/sequel/extensions/core_refinements.rb +36 -11
  120. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  121. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  122. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  123. data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
  124. data/lib/sequel/extensions/index_caching.rb +5 -1
  125. data/lib/sequel/extensions/inflector.rb +1 -1
  126. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  127. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  128. data/lib/sequel/extensions/migration.rb +57 -15
  129. data/lib/sequel/extensions/named_timezones.rb +22 -6
  130. data/lib/sequel/extensions/pagination.rb +1 -1
  131. data/lib/sequel/extensions/pg_array.rb +33 -4
  132. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  133. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  134. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  135. data/lib/sequel/extensions/pg_enum.rb +1 -2
  136. data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
  137. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  138. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  139. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  140. data/lib/sequel/extensions/pg_inet.rb +10 -11
  141. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  142. data/lib/sequel/extensions/pg_interval.rb +11 -11
  143. data/lib/sequel/extensions/pg_json.rb +13 -15
  144. data/lib/sequel/extensions/pg_json_ops.rb +125 -2
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +13 -26
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +20 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  151. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  152. data/lib/sequel/extensions/s.rb +2 -1
  153. data/lib/sequel/extensions/schema_caching.rb +1 -1
  154. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  155. data/lib/sequel/extensions/server_block.rb +10 -13
  156. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  157. data/lib/sequel/extensions/sql_comments.rb +110 -3
  158. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  159. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  160. data/lib/sequel/extensions/string_agg.rb +1 -1
  161. data/lib/sequel/extensions/string_date_time.rb +19 -23
  162. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  163. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  164. data/lib/sequel/model/associations.rb +286 -92
  165. data/lib/sequel/model/base.rb +53 -33
  166. data/lib/sequel/model/dataset_module.rb +3 -0
  167. data/lib/sequel/model/errors.rb +10 -1
  168. data/lib/sequel/model/exceptions.rb +15 -3
  169. data/lib/sequel/model/inflections.rb +1 -1
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +74 -16
  172. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  173. data/lib/sequel/plugins/column_encryption.rb +29 -8
  174. data/lib/sequel/plugins/composition.rb +3 -2
  175. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  176. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  177. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  178. data/lib/sequel/plugins/dirty.rb +1 -1
  179. data/lib/sequel/plugins/enum.rb +124 -0
  180. data/lib/sequel/plugins/finder.rb +4 -2
  181. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  182. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  183. data/lib/sequel/plugins/json_serializer.rb +2 -2
  184. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  185. data/lib/sequel/plugins/list.rb +8 -3
  186. data/lib/sequel/plugins/many_through_many.rb +109 -10
  187. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  188. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  189. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  190. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  191. data/lib/sequel/plugins/paged_operations.rb +181 -0
  192. data/lib/sequel/plugins/pg_array_associations.rb +46 -34
  193. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  194. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  195. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  196. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  197. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  198. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  199. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  200. data/lib/sequel/plugins/serialization.rb +1 -0
  201. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  202. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  203. data/lib/sequel/plugins/sql_comments.rb +189 -0
  204. data/lib/sequel/plugins/static_cache.rb +39 -1
  205. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  206. data/lib/sequel/plugins/subclasses.rb +28 -11
  207. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  208. data/lib/sequel/plugins/timestamps.rb +1 -1
  209. data/lib/sequel/plugins/unused_associations.rb +521 -0
  210. data/lib/sequel/plugins/update_or_create.rb +1 -1
  211. data/lib/sequel/plugins/validate_associated.rb +22 -12
  212. data/lib/sequel/plugins/validation_helpers.rb +41 -11
  213. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  214. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  215. data/lib/sequel/sql.rb +1 -1
  216. data/lib/sequel/timezones.rb +12 -14
  217. data/lib/sequel/version.rb +1 -1
  218. metadata +109 -19
@@ -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}
173
- when DateTime, Time
174
- literal(arg)
188
+ # :nocov:
189
+ # Not covered by tests as tests use pg_extended_date_support
190
+ # extension, which has basically the same code.
191
+ when Time, DateTime
192
+ @default_dataset.literal_date_or_time(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)
@@ -635,6 +672,7 @@ module Sequel
635
672
  # cursor usage.
636
673
  # :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
637
674
  # numbers result in fewer queries but greater memory use.
675
+ # :skip_transaction :: Same as :hold, but :hold takes priority.
638
676
  #
639
677
  # Usage:
640
678
  #
@@ -660,7 +698,9 @@ module Sequel
660
698
  clone(:where=>Sequel.lit(['CURRENT OF '], Sequel.identifier(cursor_name)))
661
699
  end
662
700
 
701
+ # :nocov:
663
702
  if USES_PG
703
+ # :nocov:
664
704
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
665
705
 
666
706
  # PostgreSQL specific argument mapper used for mapping the named
@@ -717,19 +757,21 @@ module Sequel
717
757
  sql = String.new
718
758
  sql << "CALL "
719
759
  identifier_append(sql, name)
720
- literal_append(sql, args)
760
+ sql << "("
761
+ expression_list_append(sql, args)
762
+ sql << ")"
721
763
  with_sql_first(sql)
722
764
  end
723
765
 
724
766
  # Use a cursor to fetch groups of records at a time, yielding them to the block.
725
767
  def cursor_fetch_rows(sql)
726
- server_opts = {:server=>@opts[:server] || :read_only}
727
768
  cursor = @opts[:cursor]
728
- hold = cursor[:hold]
769
+ hold = cursor.fetch(:hold){cursor[:skip_transaction]}
770
+ server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
729
771
  cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
730
772
  rows_per_fetch = cursor[:rows_per_fetch].to_i
731
773
 
732
- db.public_send(*(hold ? [:synchronize, server_opts[:server]] : [:transaction, server_opts])) do
774
+ db.transaction(server_opts) do
733
775
  begin
734
776
  execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
735
777
  rows_per_fetch = 1000 if rows_per_fetch <= 0
@@ -805,6 +847,7 @@ module Sequel
805
847
  end
806
848
  end
807
849
 
850
+ # :nocov:
808
851
  if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
809
852
  begin
810
853
  require 'sequel_pg'
@@ -816,3 +859,4 @@ if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
816
859
  rescue LoadError
817
860
  end
818
861
  end
862
+ # :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
@@ -214,6 +215,18 @@ module Sequel
214
215
  DATABASE_ERROR_REGEXPS
215
216
  end
216
217
 
218
+ DISCONNECT_SQL_STATES = %w'40003 08001 08003'.freeze
219
+ def disconnect_error?(exception, opts)
220
+ sqlstate = database_exception_sqlstate(exception, opts)
221
+
222
+ case sqlstate
223
+ when *DISCONNECT_SQL_STATES
224
+ true
225
+ else
226
+ super
227
+ end
228
+ end
229
+
217
230
  # DB2 has issues with quoted identifiers, so
218
231
  # turn off database quoting by default.
219
232
  def quote_identifiers_default
@@ -273,6 +286,7 @@ module Sequel
273
286
 
274
287
  module DatasetMethods
275
288
  include EmulateOffsetWithRowNumber
289
+ include ::Sequel::Dataset::ColumnsLimit1
276
290
 
277
291
  BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}.freeze
278
292
 
@@ -336,6 +350,11 @@ module Sequel
336
350
  true
337
351
  end
338
352
 
353
+ # DB2 supports MERGE
354
+ def supports_merge?
355
+ true
356
+ end
357
+
339
358
  # DB2 does not support multiple columns in IN.
340
359
  def supports_multiple_column_in?
341
360
  false
@@ -358,6 +377,29 @@ module Sequel
358
377
 
359
378
  private
360
379
 
380
+ # Normalize conditions for MERGE WHEN.
381
+ def _merge_when_conditions_sql(sql, data)
382
+ if data.has_key?(:conditions)
383
+ sql << " AND "
384
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
385
+ end
386
+ end
387
+
388
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
389
+ # type error.
390
+ def _normalize_merge_when_conditions(conditions)
391
+ case conditions
392
+ when nil, false
393
+ {1=>0}
394
+ when true
395
+ {1=>1}
396
+ when Sequel::SQL::DelayedEvaluation
397
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
398
+ else
399
+ conditions
400
+ end
401
+ end
402
+
361
403
  def empty_from_sql
362
404
  ' FROM "SYSIBM"."SYSDUMMY1"'
363
405
  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,11 +919,20 @@ 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.
853
934
  def default_timestamp_format
854
- "'%Y-%m-%dT%H:%M:%S%N%z'"
935
+ "'%Y-%m-%dT%H:%M:%S.%3N'"
855
936
  end
856
937
 
857
938
  # Only include the primary table in the main delete clause