sequel 5.48.0 → 5.52.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +80 -0
  3. data/README.rdoc +12 -5
  4. data/doc/migration.rdoc +1 -1
  5. data/doc/opening_databases.rdoc +1 -1
  6. data/doc/postgresql.rdoc +8 -0
  7. data/doc/release_notes/5.49.0.txt +59 -0
  8. data/doc/release_notes/5.50.0.txt +78 -0
  9. data/doc/release_notes/5.51.0.txt +47 -0
  10. data/doc/release_notes/5.52.0.txt +87 -0
  11. data/doc/testing.rdoc +3 -1
  12. data/lib/sequel/adapters/ado/access.rb +1 -1
  13. data/lib/sequel/adapters/ado.rb +1 -1
  14. data/lib/sequel/adapters/amalgalite.rb +3 -5
  15. data/lib/sequel/adapters/ibmdb.rb +2 -2
  16. data/lib/sequel/adapters/jdbc/derby.rb +3 -0
  17. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  18. data/lib/sequel/adapters/jdbc.rb +9 -11
  19. data/lib/sequel/adapters/mysql.rb +80 -67
  20. data/lib/sequel/adapters/mysql2.rb +42 -44
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/postgres.rb +27 -29
  24. data/lib/sequel/adapters/shared/access.rb +2 -0
  25. data/lib/sequel/adapters/shared/db2.rb +2 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +4 -2
  27. data/lib/sequel/adapters/shared/postgres.rb +59 -6
  28. data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
  29. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  30. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  31. data/lib/sequel/adapters/sqlite.rb +16 -18
  32. data/lib/sequel/adapters/tinytds.rb +1 -1
  33. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  34. data/lib/sequel/ast_transformer.rb +6 -0
  35. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  36. data/lib/sequel/connection_pool/single.rb +6 -8
  37. data/lib/sequel/core.rb +17 -18
  38. data/lib/sequel/database/connecting.rb +2 -2
  39. data/lib/sequel/database/misc.rb +6 -0
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/dataset/actions.rb +2 -2
  42. data/lib/sequel/dataset/query.rb +45 -3
  43. data/lib/sequel/dataset/sql.rb +18 -9
  44. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  45. data/lib/sequel/extensions/core_refinements.rb +36 -11
  46. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  47. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  48. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  49. data/lib/sequel/extensions/inflector.rb +1 -1
  50. data/lib/sequel/extensions/migration.rb +4 -1
  51. data/lib/sequel/extensions/pagination.rb +1 -1
  52. data/lib/sequel/extensions/pg_array_ops.rb +1 -1
  53. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  54. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  55. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  56. data/lib/sequel/extensions/pg_interval.rb +1 -0
  57. data/lib/sequel/extensions/pg_json.rb +3 -5
  58. data/lib/sequel/extensions/pg_json_ops.rb +71 -1
  59. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  60. data/lib/sequel/extensions/pg_range.rb +4 -12
  61. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  62. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  63. data/lib/sequel/extensions/s.rb +2 -1
  64. data/lib/sequel/extensions/server_block.rb +8 -12
  65. data/lib/sequel/extensions/sql_comments.rb +108 -3
  66. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  67. data/lib/sequel/extensions/string_agg.rb +1 -1
  68. data/lib/sequel/extensions/string_date_time.rb +19 -23
  69. data/lib/sequel/model/associations.rb +3 -1
  70. data/lib/sequel/model/base.rb +9 -13
  71. data/lib/sequel/model/inflections.rb +1 -1
  72. data/lib/sequel/plugins/auto_validations.rb +25 -5
  73. data/lib/sequel/plugins/column_encryption.rb +1 -1
  74. data/lib/sequel/plugins/composition.rb +1 -0
  75. data/lib/sequel/plugins/json_serializer.rb +2 -2
  76. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  77. data/lib/sequel/plugins/serialization.rb +1 -0
  78. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  79. data/lib/sequel/plugins/sql_comments.rb +189 -0
  80. data/lib/sequel/plugins/static_cache.rb +1 -1
  81. data/lib/sequel/plugins/subclasses.rb +28 -11
  82. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
  83. data/lib/sequel/plugins/unused_associations.rb +2 -2
  84. data/lib/sequel/plugins/update_or_create.rb +1 -1
  85. data/lib/sequel/plugins/validation_helpers.rb +7 -1
  86. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  87. data/lib/sequel/sql.rb +1 -1
  88. data/lib/sequel/timezones.rb +12 -14
  89. data/lib/sequel/version.rb +1 -1
  90. metadata +17 -4
@@ -1,7 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  require 'mysql'
4
- raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
4
+ raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!, so it not supported. Please install the mysql or ruby-mysql gem.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
5
5
 
6
6
  require_relative 'utils/mysql_mysql2'
7
7
  require_relative 'utils/mysql_prepared_statements'
@@ -71,21 +71,43 @@ module Sequel
71
71
  # disconnect this connection (a.k.a @@wait_timeout).
72
72
  def connect(server)
73
73
  opts = server_opts(server)
74
- conn = Mysql.init
75
- conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
76
- conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
77
- conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
78
- if encoding = opts[:encoding] || opts[:charset]
79
- # Set encoding before connecting so that the mysql driver knows what
80
- # encoding we want to use, but this can be overridden by READ_DEFAULT_GROUP.
81
- conn.options(Mysql::SET_CHARSET_NAME, encoding)
82
- end
83
- if read_timeout = opts[:read_timeout] and defined? Mysql::OPT_READ_TIMEOUT
84
- conn.options(Mysql::OPT_READ_TIMEOUT, read_timeout)
85
- end
86
- if connect_timeout = opts[:connect_timeout] and defined? Mysql::OPT_CONNECT_TIMEOUT
87
- conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
74
+
75
+ if Mysql.respond_to?(:init)
76
+ conn = Mysql.init
77
+ conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
78
+ conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
79
+ if encoding = opts[:encoding] || opts[:charset]
80
+ # Set encoding before connecting so that the mysql driver knows what
81
+ # encoding we want to use, but this can be overridden by READ_DEFAULT_GROUP.
82
+ conn.options(Mysql::SET_CHARSET_NAME, encoding)
83
+ end
84
+ if read_timeout = opts[:read_timeout] and defined? Mysql::OPT_READ_TIMEOUT
85
+ conn.options(Mysql::OPT_READ_TIMEOUT, read_timeout)
86
+ end
87
+ if connect_timeout = opts[:connect_timeout] and defined? Mysql::OPT_CONNECT_TIMEOUT
88
+ conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
89
+ end
90
+ else
91
+ # ruby-mysql 3 API
92
+ conn = Mysql.new
93
+ # no support for default group
94
+ conn.local_infile = opts[:config_local_infile] if opts.has_key?(:config_local_infile)
95
+ if encoding = opts[:encoding] || opts[:charset]
96
+ conn.charset = encoding
97
+ end
98
+ if read_timeout = opts[:read_timeout]
99
+ conn.read_timeout = read_timeout
100
+ end
101
+ if connect_timeout = opts[:connect_timeout]
102
+ conn.connect_timeout = connect_timeout
103
+ end
104
+ conn.singleton_class.class_eval do
105
+ alias real_connect connect
106
+ alias use_result store_result
107
+ end
88
108
  end
109
+
110
+ conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
89
111
  conn.real_connect(
90
112
  opts[:host] || 'localhost',
91
113
  opts[:user],
@@ -152,56 +174,49 @@ module Sequel
152
174
  super
153
175
  end
154
176
 
155
- # Return the version of the MySQL server to which we are connecting.
156
- def server_version(server=nil)
157
- @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
158
- end
159
-
160
177
  private
161
178
 
162
179
  # Execute the given SQL on the given connection. If the :type
163
180
  # option is :select, yield the result of the query, otherwise
164
181
  # yield the connection if a block is given.
165
182
  def _execute(conn, sql, opts)
166
- begin
167
- r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn){conn.query(sql)}
168
- if opts[:type] == :select
169
- yield r if r
170
- elsif block_given?
171
- yield conn
172
- end
173
- if conn.respond_to?(:more_results?)
174
- while conn.more_results? do
175
- if r
176
- r.free
177
- r = nil
178
- end
179
- begin
180
- conn.next_result
181
- r = conn.use_result
182
- rescue Mysql::Error => e
183
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
184
- break
185
- end
186
- yield r if opts[:type] == :select
183
+ r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn){conn.query(sql)}
184
+ if opts[:type] == :select
185
+ yield r if r
186
+ elsif defined?(yield)
187
+ yield conn
188
+ end
189
+ if conn.respond_to?(:more_results?)
190
+ while conn.more_results? do
191
+ if r
192
+ r.free
193
+ r = nil
194
+ end
195
+ begin
196
+ conn.next_result
197
+ r = conn.use_result
198
+ rescue Mysql::Error => e
199
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
200
+ break
187
201
  end
202
+ yield r if opts[:type] == :select
188
203
  end
189
- rescue Mysql::Error => e
190
- raise_error(e)
191
- ensure
192
- r.free if r
193
- # Use up all results to avoid a commands out of sync message.
194
- if conn.respond_to?(:more_results?)
195
- while conn.more_results? do
196
- begin
197
- conn.next_result
198
- r = conn.use_result
199
- rescue Mysql::Error => e
200
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
201
- break
202
- end
203
- r.free if r
204
+ end
205
+ rescue Mysql::Error => e
206
+ raise_error(e)
207
+ ensure
208
+ r.free if r
209
+ # Use up all results to avoid a commands out of sync message.
210
+ if conn.respond_to?(:more_results?)
211
+ while conn.more_results? do
212
+ begin
213
+ conn.next_result
214
+ r = conn.use_result
215
+ rescue Mysql::Error => e
216
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
217
+ break
204
218
  end
219
+ r.free if r
205
220
  end
206
221
  end
207
222
  end
@@ -233,17 +248,15 @@ module Sequel
233
248
  # the conversion raises an InvalidValue exception, return v
234
249
  # if :string and nil otherwise.
235
250
  def convert_date_time(v)
236
- begin
237
- yield v
238
- rescue InvalidValue
239
- case @convert_invalid_date_time
240
- when nil, :nil
241
- nil
242
- when :string
243
- v
244
- else
245
- raise
246
- end
251
+ yield v
252
+ rescue InvalidValue
253
+ case @convert_invalid_date_time
254
+ when nil, :nil
255
+ nil
256
+ when :string
257
+ v
258
+ else
259
+ raise
247
260
  end
248
261
  end
249
262
 
@@ -111,56 +111,54 @@ module Sequel
111
111
  # option is :select, yield the result of the query, otherwise
112
112
  # yield the connection if a block is given.
113
113
  def _execute(conn, sql, opts)
114
- begin
115
- stream = opts[:stream]
116
- if NativePreparedStatements
117
- if args = opts[:arguments]
118
- args = args.map{|arg| bound_variable_value(arg)}
119
- end
114
+ stream = opts[:stream]
115
+ if NativePreparedStatements
116
+ if args = opts[:arguments]
117
+ args = args.map{|arg| bound_variable_value(arg)}
118
+ end
120
119
 
121
- case sql
122
- when ::Mysql2::Statement
123
- stmt = sql
124
- when Dataset
125
- sql = sql.sql
126
- close_stmt = true
127
- stmt = conn.prepare(sql)
128
- end
120
+ case sql
121
+ when ::Mysql2::Statement
122
+ stmt = sql
123
+ when Dataset
124
+ sql = sql.sql
125
+ close_stmt = true
126
+ stmt = conn.prepare(sql)
129
127
  end
128
+ end
130
129
 
131
- r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
132
- if stmt
133
- conn.query_options.merge!(:cache_rows=>true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream, :cast_booleans=>convert_tinyint_to_bool)
134
- stmt.execute(*args)
135
- else
136
- conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream)
137
- end
130
+ r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
131
+ if stmt
132
+ conn.query_options.merge!(:cache_rows=>true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream, :cast_booleans=>convert_tinyint_to_bool)
133
+ stmt.execute(*args)
134
+ else
135
+ conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream)
138
136
  end
139
- if opts[:type] == :select
140
- if r
141
- if stream
142
- begin
143
- r2 = yield r
144
- ensure
145
- # If r2 is nil, it means the block did not exit normally,
146
- # so the rest of the results must be drained to prevent
147
- # "commands out of sync" errors.
148
- r.each{} unless r2
149
- end
150
- else
151
- yield r
137
+ end
138
+ if opts[:type] == :select
139
+ if r
140
+ if stream
141
+ begin
142
+ r2 = yield r
143
+ ensure
144
+ # If r2 is nil, it means the block did not exit normally,
145
+ # so the rest of the results must be drained to prevent
146
+ # "commands out of sync" errors.
147
+ r.each{} unless r2
152
148
  end
149
+ else
150
+ yield r
153
151
  end
154
- elsif block_given?
155
- yield conn
156
- end
157
- rescue ::Mysql2::Error => e
158
- raise_error(e)
159
- ensure
160
- if stmt
161
- conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
162
- stmt.close if close_stmt
163
152
  end
153
+ elsif defined?(yield)
154
+ yield conn
155
+ end
156
+ rescue ::Mysql2::Error => e
157
+ raise_error(e)
158
+ ensure
159
+ if stmt
160
+ conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
161
+ stmt.close if close_stmt
164
162
  end
165
163
  end
166
164
 
@@ -244,7 +242,7 @@ module Sequel
244
242
  # it hasn't been disabled.
245
243
  def paged_each(opts=OPTS, &block)
246
244
  if STREAMING_SUPPORTED && opts[:stream] != false
247
- unless block_given?
245
+ unless defined?(yield)
248
246
  return enum_for(:paged_each, opts)
249
247
  end
250
248
  stream.each(&block)
@@ -40,7 +40,7 @@ module Sequel
40
40
  synchronize(opts[:server]) do |conn|
41
41
  begin
42
42
  r = log_connection_yield(sql, conn){conn.run(sql)}
43
- yield(r) if block_given?
43
+ yield(r) if defined?(yield)
44
44
  rescue ::ODBC::Error, ArgumentError => e
45
45
  raise_error(e)
46
46
  ensure
@@ -88,11 +88,11 @@ module Sequel
88
88
  r = conn.parse(sql)
89
89
  args = cursor_bind_params(conn, r, args)
90
90
  nr = log_connection_yield(sql, conn, args){r.exec}
91
- r = nr unless block_given?
91
+ r = nr unless defined?(yield)
92
92
  else
93
93
  r = log_connection_yield(sql, conn){conn.exec(sql)}
94
94
  end
95
- if block_given?
95
+ if defined?(yield)
96
96
  yield(r)
97
97
  elsif type == :insert
98
98
  last_insert_id(conn, opts)
@@ -192,7 +192,7 @@ module Sequel
192
192
  log_sql << ")"
193
193
  end
194
194
  r = log_connection_yield(log_sql, conn, args){cursor.exec}
195
- if block_given?
195
+ if defined?(yield)
196
196
  yield(cursor)
197
197
  elsif type == :insert
198
198
  last_insert_id(conn, opts)
@@ -116,25 +116,23 @@ module Sequel
116
116
  # error classes is raised, or a PGError is raised and the connection
117
117
  # status cannot be determined or it is not OK.
118
118
  def check_disconnect_errors
119
+ yield
120
+ rescue *DISCONNECT_ERROR_CLASSES => e
121
+ disconnect = true
122
+ raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
123
+ rescue PGError => e
124
+ disconnect = false
119
125
  begin
120
- yield
121
- rescue *DISCONNECT_ERROR_CLASSES => e
126
+ s = status
127
+ rescue PGError
122
128
  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
129
  end
130
+ status_ok = (s == Adapter::CONNECTION_OK)
131
+ disconnect ||= !status_ok
132
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
133
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
134
+ ensure
135
+ block if status_ok && !disconnect
138
136
  end
139
137
 
140
138
  # Execute the given SQL with this connection. If a block is given,
@@ -143,7 +141,7 @@ module Sequel
143
141
  args = args.map{|v| @db.bound_variable_arg(v, self)} if args
144
142
  q = check_disconnect_errors{execute_query(sql, args)}
145
143
  begin
146
- block_given? ? yield(q) : q.cmd_tuples
144
+ defined?(yield) ? yield(q) : q.cmd_tuples
147
145
  ensure
148
146
  q.clear if q && q.respond_to?(:clear)
149
147
  end
@@ -350,7 +348,7 @@ module Sequel
350
348
  synchronize(opts[:server]) do |conn|
351
349
  conn.execute(copy_table_sql(table, opts))
352
350
  begin
353
- if block_given?
351
+ if defined?(yield)
354
352
  while buf = conn.get_copy_data
355
353
  yield buf
356
354
  end
@@ -400,16 +398,16 @@ module Sequel
400
398
  data = opts[:data]
401
399
  data = Array(data) if data.is_a?(String)
402
400
 
403
- if block_given? && data
401
+ if defined?(yield) && data
404
402
  raise Error, "Cannot provide both a :data option and a block to copy_into"
405
- elsif !block_given? && !data
403
+ elsif !defined?(yield) && !data
406
404
  raise Error, "Must provide either a :data option or a block to copy_into"
407
405
  end
408
406
 
409
407
  synchronize(opts[:server]) do |conn|
410
408
  conn.execute(copy_into_sql(table, opts))
411
409
  begin
412
- if block_given?
410
+ if defined?(yield)
413
411
  while buf = yield
414
412
  conn.put_copy_data(buf)
415
413
  end
@@ -518,11 +516,9 @@ module Sequel
518
516
 
519
517
  # Convert exceptions raised from the block into DatabaseErrors.
520
518
  def check_database_errors
521
- begin
522
- yield
523
- rescue => e
524
- raise_error(e, :classes=>database_error_classes)
525
- end
519
+ yield
520
+ rescue => e
521
+ raise_error(e, :classes=>database_error_classes)
526
522
  end
527
523
 
528
524
  # Set the DateStyle to ISO if configured, for faster date parsing.
@@ -590,7 +586,7 @@ module Sequel
590
586
 
591
587
  q = conn.check_disconnect_errors{log_connection_yield(log_sql, conn, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
592
588
  begin
593
- block_given? ? yield(q) : q.cmd_tuples
589
+ defined?(yield) ? yield(q) : q.cmd_tuples
594
590
  ensure
595
591
  q.clear if q && q.respond_to?(:clear)
596
592
  end
@@ -616,7 +612,7 @@ module Sequel
616
612
 
617
613
  # Use a cursor for paging.
618
614
  def paged_each(opts=OPTS, &block)
619
- unless block_given?
615
+ unless defined?(yield)
620
616
  return enum_for(:paged_each, opts)
621
617
  end
622
618
  use_cursor(opts).each(&block)
@@ -717,7 +713,9 @@ module Sequel
717
713
  sql = String.new
718
714
  sql << "CALL "
719
715
  identifier_append(sql, name)
720
- literal_append(sql, args)
716
+ sql << "("
717
+ expression_list_append(sql, args)
718
+ sql << ")"
721
719
  with_sql_first(sql)
722
720
  end
723
721
 
@@ -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
@@ -83,6 +84,7 @@ module Sequel
83
84
  end)
84
85
  include EmulateOffsetWithReverseAndCount
85
86
  include UnmodifiedIdentifiers::DatasetMethods
87
+ include ::Sequel::Dataset::ColumnsLimit1
86
88
 
87
89
  EXTRACT_MAP = {:year=>"'yyyy'", :month=>"'m'", :day=>"'d'", :hour=>"'h'", :minute=>"'n'", :second=>"'s'"}.freeze
88
90
  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
 
@@ -533,6 +533,7 @@ module Sequel
533
533
  row[:default] = row.delete(:Default)
534
534
  row[:db_type] = row.delete(:Type)
535
535
  row[:type] = schema_column_type(row[:db_type])
536
+ row[:extra] = extra
536
537
  [m.call(row.delete(:Field)), row]
537
538
  end
538
539
  end
@@ -543,9 +544,10 @@ module Sequel
543
544
  server_version >= 50600 && (op[:op] == :drop_index || (op[:op] == :drop_constraint && op[:type] == :unique))
544
545
  end
545
546
 
546
- # Whether the database supports CHECK constraints
547
+ # CHECK constraints only supported on MariaDB 10.2+ and MySQL 8.0.19+
548
+ # (at least MySQL documents DROP CONSTRAINT was supported in 8.0.19+).
547
549
  def supports_check_constraints?
548
- mariadb? && server_version >= 100200
550
+ server_version >= (mariadb? ? 100200 : 80019)
549
551
  end
550
552
 
551
553
  # MySQL can combine multiple alter table ops into a single query.
@@ -87,7 +87,7 @@ module Sequel
87
87
 
88
88
  def self.mock_adapter_setup(db)
89
89
  db.instance_exec do
90
- @server_version = 90500
90
+ @server_version = 140000
91
91
  initialize_postgres_adapter
92
92
  extend(MockAdapterDatabaseMethods)
93
93
  end
@@ -479,6 +479,7 @@ module Sequel
479
479
  # :each_row :: Calls the trigger for each row instead of for each statement.
480
480
  # :events :: Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
481
481
  # the trigger is called for insert, update, or delete.
482
+ # :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
482
483
  # :when :: A filter to use for the trigger
483
484
  def create_trigger(table, name, function, opts=OPTS)
484
485
  self << create_trigger_sql(table, name, function, opts)
@@ -1237,7 +1238,7 @@ module Sequel
1237
1238
  raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
1238
1239
  filter = " WHEN #{filter_expr(filter)}"
1239
1240
  end
1240
- "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
1241
+ "CREATE #{'OR REPLACE ' if opts[:replace]}TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
1241
1242
  end
1242
1243
 
1243
1244
  # DDL fragment for initial part of CREATE VIEW statement
@@ -1335,7 +1336,7 @@ module Sequel
1335
1336
  ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
1336
1337
  ds = filter_schema(ds, opts)
1337
1338
  m = output_identifier_meth
1338
- if block_given?
1339
+ if defined?(yield)
1339
1340
  yield(ds)
1340
1341
  elsif opts[:qualify]
1341
1342
  ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
@@ -1503,9 +1504,9 @@ module Sequel
1503
1504
  if column[:text]
1504
1505
  :text
1505
1506
  elsif column[:fixed]
1506
- "char(#{column[:size]||255})"
1507
+ "char(#{column[:size]||default_string_column_size})"
1507
1508
  elsif column[:text] == false || column[:size]
1508
- "varchar(#{column[:size]||255})"
1509
+ "varchar(#{column[:size]||default_string_column_size})"
1509
1510
  else
1510
1511
  :text
1511
1512
  end
@@ -1727,13 +1728,22 @@ module Sequel
1727
1728
  ds.insert_sql(*values)
1728
1729
  end
1729
1730
 
1731
+ # Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
1732
+ # USING columns.
1733
+ def join_table(type, table, expr=nil, options=OPTS, &block)
1734
+ if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
1735
+ options = options.merge(:join_using=>true)
1736
+ end
1737
+ super
1738
+ end
1739
+
1730
1740
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
1731
1741
  # the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
1732
1742
  # a new transaction, locks the table, and yields. If a block is not given,
1733
1743
  # just locks the tables. Note that PostgreSQL will probably raise an error
1734
1744
  # if you lock the table outside of an existing transaction. Returns nil.
1735
1745
  def lock(mode, opts=OPTS)
1736
- if block_given? # perform locking inside a transaction and yield to block
1746
+ if defined?(yield) # perform locking inside a transaction and yield to block
1737
1747
  @db.transaction(opts){lock(mode, opts); yield}
1738
1748
  else
1739
1749
  sql = 'LOCK TABLE '.dup
@@ -2023,6 +2033,17 @@ module Sequel
2023
2033
  end
2024
2034
  end
2025
2035
 
2036
+ # Support table aliases for USING columns
2037
+ def join_using_clause_using_sql_append(sql, using_columns)
2038
+ if using_columns.is_a?(SQL::AliasedExpression)
2039
+ super(sql, using_columns.expression)
2040
+ sql << ' AS '
2041
+ identifier_append(sql, using_columns.alias)
2042
+ else
2043
+ super
2044
+ end
2045
+ end
2046
+
2026
2047
  # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
2027
2048
  def literal_blob_append(sql, v)
2028
2049
  sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
@@ -2141,6 +2162,38 @@ module Sequel
2141
2162
  opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
2142
2163
  end
2143
2164
 
2165
+ # Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
2166
+ def select_with_sql_cte(sql, cte)
2167
+ super
2168
+
2169
+ if search_opts = cte[:search]
2170
+ sql << if search_opts[:type] == :breadth
2171
+ " SEARCH BREADTH FIRST BY "
2172
+ else
2173
+ " SEARCH DEPTH FIRST BY "
2174
+ end
2175
+
2176
+ identifier_list_append(sql, Array(search_opts[:by]))
2177
+ sql << " SET "
2178
+ identifier_append(sql, search_opts[:set] || :ordercol)
2179
+ end
2180
+
2181
+ if cycle_opts = cte[:cycle]
2182
+ sql << " CYCLE "
2183
+ identifier_list_append(sql, Array(cycle_opts[:columns]))
2184
+ sql << " SET "
2185
+ identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
2186
+ if cycle_opts.has_key?(:cycle_value)
2187
+ sql << " TO "
2188
+ literal_append(sql, cycle_opts[:cycle_value])
2189
+ sql << " DEFAULT "
2190
+ literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
2191
+ end
2192
+ sql << " USING "
2193
+ identifier_append(sql, cycle_opts[:path_column] || :path)
2194
+ end
2195
+ end
2196
+
2144
2197
  # The version of the database server
2145
2198
  def server_version
2146
2199
  db.server_version(@opts[:server])
@@ -1,5 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require_relative '../utils/columns_limit_1'
4
+
3
5
  module Sequel
4
6
  module SqlAnywhere
5
7
  Sequel::Database.set_shared_adapter_scheme(:sqlanywhere, self)
@@ -234,6 +236,7 @@ module Sequel
234
236
  module DatasetMethods
235
237
  Dataset.def_sql_method(self, :insert, %w'insert into columns values')
236
238
  Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from join where group having window compounds order lock')
239
+ include ::Sequel::Dataset::ColumnsLimit1
237
240
 
238
241
  # Whether to convert smallint to boolean arguments for this dataset.
239
242
  # Defaults to the IBMDB module setting.
@@ -393,7 +393,7 @@ module Sequel
393
393
  old_columns = def_columns.map{|c| c[:name]}
394
394
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
395
395
 
396
- yield def_columns if block_given?
396
+ yield def_columns if defined?(yield)
397
397
 
398
398
  constraints = (opts[:constraints] || []).dup
399
399
  pks = []
@@ -120,7 +120,7 @@ module Sequel
120
120
 
121
121
  case type
122
122
  when :select
123
- yield rs if block_given?
123
+ yield rs if defined?(yield)
124
124
  when :rows
125
125
  return @api.sqlany_affected_rows(rs)
126
126
  when :insert