sequel 5.48.0 → 5.52.0

Sign up to get free protection for your applications and to get access to all the features.
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