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.
- checksums.yaml +4 -4
- data/CHANGELOG +80 -0
- data/README.rdoc +12 -5
- data/doc/migration.rdoc +1 -1
- data/doc/opening_databases.rdoc +1 -1
- data/doc/postgresql.rdoc +8 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/testing.rdoc +3 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +3 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +9 -11
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +42 -44
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +2 -0
- data/lib/sequel/adapters/shared/mysql.rb +4 -2
- data/lib/sequel/adapters/shared/postgres.rb +59 -6
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +16 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/dataset/actions.rb +2 -2
- data/lib/sequel/dataset/query.rb +45 -3
- data/lib/sequel/dataset/sql.rb +18 -9
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array_ops.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +1 -0
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +71 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +4 -12
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +108 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/model/associations.rb +3 -1
- data/lib/sequel/model/base.rb +9 -13
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_validations.rb +25 -5
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/composition.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/unused_associations.rb +2 -2
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +7 -1
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- 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
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
-
|
115
|
-
|
116
|
-
if
|
117
|
-
|
118
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
245
|
+
unless defined?(yield)
|
248
246
|
return enum_for(:paged_each, opts)
|
249
247
|
end
|
250
248
|
stream.each(&block)
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
-
|
121
|
-
rescue
|
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
|
-
|
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
|
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
|
401
|
+
if defined?(yield) && data
|
404
402
|
raise Error, "Cannot provide both a :data option and a block to copy_into"
|
405
|
-
elsif !
|
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
|
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
|
-
|
522
|
-
|
523
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
#
|
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?
|
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 =
|
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
|
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]||
|
1507
|
+
"char(#{column[:size]||default_string_column_size})"
|
1507
1508
|
elsif column[:text] == false || column[:size]
|
1508
|
-
"varchar(#{column[:size]||
|
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
|
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
|
396
|
+
yield def_columns if defined?(yield)
|
397
397
|
|
398
398
|
constraints = (opts[:constraints] || []).dup
|
399
399
|
pks = []
|