sequel 5.58.0 → 5.78.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 +288 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +24 -23
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +20 -12
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +62 -16
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +80 -1
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +494 -164
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +40 -4
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/database/schema_methods.rb +13 -3
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +62 -37
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +21 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +50 -11
- data/lib/sequel/model/base.rb +45 -21
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +5 -5
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +1 -1
- metadata +76 -6
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -396,11 +396,16 @@ module Sequel
|
|
396
396
|
|
397
397
|
def database_exception_sqlstate(exception, opts)
|
398
398
|
if database_exception_use_sqlstates?
|
399
|
-
|
400
|
-
exception = exception.cause
|
401
|
-
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
402
|
-
end
|
399
|
+
_database_exception_sqlstate(exception, opts)
|
403
400
|
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def _database_exception_sqlstate(exception, opts)
|
404
|
+
16.times do
|
405
|
+
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
406
|
+
break unless exception.respond_to?(:cause) && (exception = exception.cause)
|
407
|
+
end
|
408
|
+
|
404
409
|
nil
|
405
410
|
end
|
406
411
|
|
@@ -415,8 +420,7 @@ module Sequel
|
|
415
420
|
|
416
421
|
# Raise a disconnect error if the SQL state of the cause of the exception indicates so.
|
417
422
|
def disconnect_error?(exception, opts)
|
418
|
-
|
419
|
-
super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
|
423
|
+
super || (_database_exception_sqlstate(exception, opts) =~ /^08/)
|
420
424
|
end
|
421
425
|
|
422
426
|
# Execute the prepared statement. If the provided name is a
|
@@ -29,6 +29,21 @@ module Sequel
|
|
29
29
|
end
|
30
30
|
MYSQL_TYPES.freeze
|
31
31
|
|
32
|
+
RUBY_MYSQL_3 = !Mysql.respond_to?(:init)
|
33
|
+
RUBY_MYSQL_4 = RUBY_MYSQL_3 && ::Mysql::VERSION.to_i >= 4
|
34
|
+
|
35
|
+
if RUBY_MYSQL_3
|
36
|
+
class Adapter < ::Mysql
|
37
|
+
alias real_connect connect
|
38
|
+
alias use_result store_result
|
39
|
+
if RUBY_MYSQL_4
|
40
|
+
def initialize(**opts)
|
41
|
+
super(**opts.merge(:cast=>false))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
32
47
|
class Database < Sequel::Database
|
33
48
|
include Sequel::MySQL::DatabaseMethods
|
34
49
|
include Sequel::MySQL::MysqlMysql2::DatabaseMethods
|
@@ -72,7 +87,7 @@ module Sequel
|
|
72
87
|
def connect(server)
|
73
88
|
opts = server_opts(server)
|
74
89
|
|
75
|
-
if
|
90
|
+
if !RUBY_MYSQL_3
|
76
91
|
conn = Mysql.init
|
77
92
|
conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
|
78
93
|
conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
|
@@ -88,8 +103,8 @@ module Sequel
|
|
88
103
|
conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
|
89
104
|
end
|
90
105
|
else
|
91
|
-
# ruby-mysql 3 API
|
92
|
-
conn =
|
106
|
+
# ruby-mysql 3+ API
|
107
|
+
conn = Adapter.new
|
93
108
|
# no support for default group
|
94
109
|
conn.local_infile = opts[:config_local_infile] if opts.has_key?(:config_local_infile)
|
95
110
|
if encoding = opts[:encoding] || opts[:charset]
|
@@ -101,10 +116,7 @@ module Sequel
|
|
101
116
|
if connect_timeout = opts[:connect_timeout]
|
102
117
|
conn.connect_timeout = connect_timeout
|
103
118
|
end
|
104
|
-
|
105
|
-
alias real_connect connect
|
106
|
-
alias use_result store_result
|
107
|
-
end
|
119
|
+
opts[:compress] = false
|
108
120
|
end
|
109
121
|
|
110
122
|
conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
|
@@ -43,7 +43,7 @@ module Sequel
|
|
43
43
|
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
44
44
|
# some translation, but allow for millisecond precision.
|
45
45
|
def default_timestamp_format
|
46
|
-
"{ts '%Y-%m-%d %H:%M:%S
|
46
|
+
"{ts '%Y-%m-%d %H:%M:%S.%3N'}"
|
47
47
|
end
|
48
48
|
|
49
49
|
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
@@ -312,6 +312,7 @@ module Sequel
|
|
312
312
|
:char_used => column.char_used?,
|
313
313
|
:char_size => column.char_size,
|
314
314
|
:data_size => column.data_size,
|
315
|
+
:column_size => column.precision,
|
315
316
|
:precision => column.precision,
|
316
317
|
:scale => column.scale,
|
317
318
|
:fsprecision => column.fsprecision,
|
@@ -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
|
27
|
-
Sequel::Postgres::USES_PG = false
|
30
|
+
require 'sequel/postgres-pr'
|
28
31
|
rescue LoadError
|
29
|
-
|
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
|
-
|
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
|
-
|
78
|
-
#
|
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,6 +127,7 @@ 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
|
@@ -168,8 +185,12 @@ module Sequel
|
|
168
185
|
case arg
|
169
186
|
when Sequel::SQL::Blob
|
170
187
|
{:value=>arg, :type=>17, :format=>1}
|
171
|
-
|
172
|
-
|
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:
|
173
194
|
else
|
174
195
|
arg
|
175
196
|
end
|
@@ -204,7 +225,9 @@ module Sequel
|
|
204
225
|
:sslmode => opts[:sslmode],
|
205
226
|
:sslrootcert => opts[:sslrootcert]
|
206
227
|
}.delete_if { |key, value| blank_object?(value) }
|
228
|
+
# :nocov:
|
207
229
|
connection_params.merge!(opts[:driver_options]) if opts[:driver_options]
|
230
|
+
# :nocov:
|
208
231
|
conn = Adapter.connect(opts[:conn_str] || connection_params)
|
209
232
|
|
210
233
|
conn.instance_variable_set(:@prepared_statements, {})
|
@@ -212,6 +235,13 @@ module Sequel
|
|
212
235
|
if receiver = opts[:notice_receiver]
|
213
236
|
conn.set_notice_receiver(&receiver)
|
214
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:
|
215
245
|
else
|
216
246
|
unless typecast_value_boolean(@opts.fetch(:force_standard_strings, true))
|
217
247
|
raise Error, "Cannot create connection using postgres-pr unless force_standard_strings is set"
|
@@ -226,12 +256,11 @@ module Sequel
|
|
226
256
|
opts[:password]
|
227
257
|
)
|
228
258
|
end
|
259
|
+
# :nocov:
|
229
260
|
|
230
261
|
conn.instance_variable_set(:@db, self)
|
231
|
-
if USES_PG && conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
|
232
|
-
conn.type_map_for_queries = PG_QUERY_TYPE_MAP
|
233
|
-
end
|
234
262
|
|
263
|
+
# :nocov:
|
235
264
|
if encoding = opts[:encoding] || opts[:charset]
|
236
265
|
if conn.respond_to?(:set_client_encoding)
|
237
266
|
conn.set_client_encoding(encoding)
|
@@ -239,6 +268,7 @@ module Sequel
|
|
239
268
|
conn.async_exec("set client_encoding to '#{encoding}'")
|
240
269
|
end
|
241
270
|
end
|
271
|
+
# :nocov:
|
242
272
|
|
243
273
|
connection_configuration_sqls(opts).each{|sql| conn.execute(sql)}
|
244
274
|
conn
|
@@ -265,7 +295,9 @@ module Sequel
|
|
265
295
|
nil
|
266
296
|
end
|
267
297
|
|
298
|
+
# :nocov:
|
268
299
|
if USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
|
300
|
+
# :nocov:
|
269
301
|
# Return a hash of information about the related PGError (or Sequel::DatabaseError that
|
270
302
|
# wraps a PGError), with the following entries (any of which may be +nil+):
|
271
303
|
#
|
@@ -316,7 +348,9 @@ module Sequel
|
|
316
348
|
synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
|
317
349
|
end
|
318
350
|
|
351
|
+
# :nocov:
|
319
352
|
if USES_PG
|
353
|
+
# :nocov:
|
320
354
|
# +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
|
321
355
|
# results directly to the caller. This method is only supported if pg is the
|
322
356
|
# underlying ruby driver. This method should only be called if you want
|
@@ -509,8 +543,10 @@ module Sequel
|
|
509
543
|
def adapter_initialize
|
510
544
|
@use_iso_date_format = typecast_value_boolean(@opts.fetch(:use_iso_date_format, true))
|
511
545
|
initialize_postgres_adapter
|
546
|
+
# :nocov:
|
512
547
|
add_conversion_proc(17, method(:unescape_bytea)) if USES_PG
|
513
548
|
add_conversion_proc(1082, TYPE_TRANSLATOR_DATE) if @use_iso_date_format
|
549
|
+
# :nocov:
|
514
550
|
self.convert_infinite_timestamps = @opts[:convert_infinite_timestamps]
|
515
551
|
end
|
516
552
|
|
@@ -520,19 +556,22 @@ module Sequel
|
|
520
556
|
rescue => e
|
521
557
|
raise_error(e, :classes=>database_error_classes)
|
522
558
|
end
|
523
|
-
|
524
559
|
# Set the DateStyle to ISO if configured, for faster date parsing.
|
525
560
|
def connection_configuration_sqls(opts=@opts)
|
526
561
|
sqls = super
|
562
|
+
# :nocov:
|
527
563
|
sqls << "SET DateStyle = 'ISO'" if @use_iso_date_format
|
564
|
+
# :nocov:
|
528
565
|
sqls
|
529
566
|
end
|
530
567
|
|
568
|
+
# :nocov:
|
531
569
|
if USES_PG
|
532
570
|
def unescape_bytea(s)
|
533
571
|
::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s))
|
534
572
|
end
|
535
573
|
end
|
574
|
+
# :nocov:
|
536
575
|
|
537
576
|
DATABASE_ERROR_CLASSES = [PGError].freeze
|
538
577
|
def database_error_classes
|
@@ -546,7 +585,9 @@ module Sequel
|
|
546
585
|
end
|
547
586
|
|
548
587
|
def database_exception_sqlstate(exception, opts)
|
588
|
+
# :nocov:
|
549
589
|
if exception.respond_to?(:result) && (result = exception.result)
|
590
|
+
# :nocov:
|
550
591
|
result.error_field(PGresult::PG_DIAG_SQLSTATE)
|
551
592
|
end
|
552
593
|
end
|
@@ -631,6 +672,7 @@ module Sequel
|
|
631
672
|
# cursor usage.
|
632
673
|
# :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
|
633
674
|
# numbers result in fewer queries but greater memory use.
|
675
|
+
# :skip_transaction :: Same as :hold, but :hold takes priority.
|
634
676
|
#
|
635
677
|
# Usage:
|
636
678
|
#
|
@@ -656,7 +698,9 @@ module Sequel
|
|
656
698
|
clone(:where=>Sequel.lit(['CURRENT OF '], Sequel.identifier(cursor_name)))
|
657
699
|
end
|
658
700
|
|
701
|
+
# :nocov:
|
659
702
|
if USES_PG
|
703
|
+
# :nocov:
|
660
704
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
661
705
|
|
662
706
|
# PostgreSQL specific argument mapper used for mapping the named
|
@@ -721,13 +765,13 @@ module Sequel
|
|
721
765
|
|
722
766
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
723
767
|
def cursor_fetch_rows(sql)
|
724
|
-
server_opts = {:server=>@opts[:server] || :read_only}
|
725
768
|
cursor = @opts[:cursor]
|
726
|
-
hold = cursor[:
|
769
|
+
hold = cursor.fetch(:hold){cursor[:skip_transaction]}
|
770
|
+
server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
|
727
771
|
cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
|
728
772
|
rows_per_fetch = cursor[:rows_per_fetch].to_i
|
729
773
|
|
730
|
-
db.
|
774
|
+
db.transaction(server_opts) do
|
731
775
|
begin
|
732
776
|
execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
|
733
777
|
rows_per_fetch = 1000 if rows_per_fetch <= 0
|
@@ -803,6 +847,7 @@ module Sequel
|
|
803
847
|
end
|
804
848
|
end
|
805
849
|
|
850
|
+
# :nocov:
|
806
851
|
if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
|
807
852
|
begin
|
808
853
|
require 'sequel_pg'
|
@@ -814,3 +859,4 @@ if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
|
|
814
859
|
rescue LoadError
|
815
860
|
end
|
816
861
|
end
|
862
|
+
# :nocov:
|
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
|
18
18
|
# Doesn't work, due to security restrictions on MSysObjects
|
19
19
|
#def tables
|
20
|
-
# from(:MSysObjects).where(:
|
20
|
+
# from(:MSysObjects).where(Type: 1, Flags: 0).select_map(:Name).map(&:to_sym)
|
21
21
|
#end
|
22
22
|
|
23
23
|
# Access doesn't support renaming tables from an SQL query,
|
@@ -57,6 +57,14 @@ module Sequel
|
|
57
57
|
DATABASE_ERROR_REGEXPS
|
58
58
|
end
|
59
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
|
+
|
60
68
|
def drop_index_sql(table, op)
|
61
69
|
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
62
70
|
end
|
@@ -215,6 +215,18 @@ module Sequel
|
|
215
215
|
DATABASE_ERROR_REGEXPS
|
216
216
|
end
|
217
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
|
+
|
218
230
|
# DB2 has issues with quoted identifiers, so
|
219
231
|
# turn off database quoting by default.
|
220
232
|
def quote_identifiers_default
|
@@ -32,7 +32,7 @@ module Sequel
|
|
32
32
|
#
|
33
33
|
# Options:
|
34
34
|
# :args :: Arguments to stored procedure. For named arguments, this should be a
|
35
|
-
# hash keyed by argument
|
35
|
+
# hash keyed by argument name. For unnamed arguments, this should be an
|
36
36
|
# array. Output parameters to the function are specified using :output.
|
37
37
|
# You can also name output parameters and provide a type by using an
|
38
38
|
# array containing :output, the type name, and the parameter name.
|
@@ -246,6 +246,34 @@ module Sequel
|
|
246
246
|
information_schema_tables('VIEW', opts)
|
247
247
|
end
|
248
248
|
|
249
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will
|
250
|
+
# be converted to a string). If successful, yield to the block, then release the advisory lock
|
251
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
252
|
+
#
|
253
|
+
# Options:
|
254
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
255
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
256
|
+
lock_id = lock_id.to_s
|
257
|
+
timeout = opts[:wait] ? -1 : 0
|
258
|
+
server = opts[:server]
|
259
|
+
|
260
|
+
synchronize(server) do
|
261
|
+
begin
|
262
|
+
res = call_mssql_sproc(:sp_getapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockTimeout'=>timeout, 'LockMode'=>'Exclusive', 'LockOwner'=>'Session'})
|
263
|
+
|
264
|
+
unless locked = res[:result] >= 0
|
265
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
266
|
+
end
|
267
|
+
|
268
|
+
yield
|
269
|
+
ensure
|
270
|
+
if locked
|
271
|
+
call_mssql_sproc(:sp_releaseapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockOwner'=>'Session'})
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
249
277
|
private
|
250
278
|
|
251
279
|
# Add CLUSTERED or NONCLUSTERED as needed
|
@@ -325,6 +353,11 @@ module Sequel
|
|
325
353
|
false
|
326
354
|
end
|
327
355
|
|
356
|
+
# MSSQL tinyint types are unsigned.
|
357
|
+
def column_schema_tinyint_type_is_unsigned?
|
358
|
+
true
|
359
|
+
end
|
360
|
+
|
328
361
|
# Handle MSSQL specific default format.
|
329
362
|
def column_schema_normalize_default(default, type)
|
330
363
|
if m = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/.match(default)
|
@@ -399,10 +432,15 @@ module Sequel
|
|
399
432
|
# Backbone of the tables and views support.
|
400
433
|
def information_schema_tables(type, opts)
|
401
434
|
m = output_identifier_meth
|
402
|
-
|
435
|
+
schema = opts[:schema]||'dbo'
|
436
|
+
tables = metadata_dataset.from(Sequel[:information_schema][:tables].as(:t)).
|
403
437
|
select(:table_name).
|
404
|
-
where(:table_type=>type, :table_schema=>
|
438
|
+
where(:table_type=>type, :table_schema=>schema.to_s).
|
405
439
|
map{|x| m.call(x[:table_name])}
|
440
|
+
|
441
|
+
tables.map!{|t| Sequel.qualify(m.call(schema).to_s, m.call(t).to_s)} if opts[:qualify]
|
442
|
+
|
443
|
+
tables
|
406
444
|
end
|
407
445
|
|
408
446
|
# Always quote identifiers in the metadata_dataset, so schema parsing works.
|
@@ -586,6 +624,18 @@ module Sequel
|
|
586
624
|
end
|
587
625
|
end
|
588
626
|
|
627
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
628
|
+
# cannot wrap it in a subquery. Load entire query in this case to get
|
629
|
+
# the number of rows. In general, you should avoid calling this method
|
630
|
+
# on datasets with custom SQL.
|
631
|
+
def count(*a, &block)
|
632
|
+
if (@opts[:sql] && a.empty? && !block)
|
633
|
+
naked.to_a.length
|
634
|
+
else
|
635
|
+
super
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
589
639
|
# Uses CROSS APPLY to join the given table into the current dataset.
|
590
640
|
def cross_apply(table)
|
591
641
|
join_table(:cross_apply, table)
|
@@ -596,6 +646,19 @@ module Sequel
|
|
596
646
|
clone(:disable_insert_output=>true)
|
597
647
|
end
|
598
648
|
|
649
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
650
|
+
# cannot wrap it in a subquery. Run query, and if it returns any
|
651
|
+
# records, return true. In general, you should avoid calling this method
|
652
|
+
# on datasets with custom SQL.
|
653
|
+
def empty?
|
654
|
+
if @opts[:sql]
|
655
|
+
naked.each{return false}
|
656
|
+
true
|
657
|
+
else
|
658
|
+
super
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
599
662
|
# MSSQL treats [] as a metacharacter in LIKE expresions.
|
600
663
|
def escape_like(string)
|
601
664
|
string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
|
@@ -800,11 +863,10 @@ module Sequel
|
|
800
863
|
if opts[:return] == :primary_key && !@opts[:output]
|
801
864
|
output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
|
802
865
|
elsif @opts[:output]
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
866
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
867
|
+
# that there's only ever a single statement.
|
868
|
+
sql = multi_insert_sql(columns, values)[0]
|
869
|
+
naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v}
|
808
870
|
else
|
809
871
|
super
|
810
872
|
end
|
@@ -898,7 +960,7 @@ module Sequel
|
|
898
960
|
# since that is the format that is multilanguage and not
|
899
961
|
# DATEFORMAT dependent.
|
900
962
|
def default_timestamp_format
|
901
|
-
"'%Y-%m-%dT%H:%M:%S
|
963
|
+
"'%Y-%m-%dT%H:%M:%S.%3N'"
|
902
964
|
end
|
903
965
|
|
904
966
|
# Only include the primary table in the main delete clause
|
@@ -197,6 +197,41 @@ module Sequel
|
|
197
197
|
renames.each{|from,| remove_cached_schema(from)}
|
198
198
|
end
|
199
199
|
|
200
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will be
|
201
|
+
# converted to a string). If successful, yield to the block, then release the advisory lock
|
202
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
203
|
+
#
|
204
|
+
# DB.with_advisory_lock(1357){DB.get(1)}
|
205
|
+
# # SELECT GET_LOCK('1357', 0) LIMIT 1
|
206
|
+
# # SELECT 1 AS v LIMIT 1
|
207
|
+
# # SELECT RELEASE_LOCK('1357') LIMIT 1
|
208
|
+
#
|
209
|
+
# Options:
|
210
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
211
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
212
|
+
lock_id = lock_id.to_s
|
213
|
+
ds = dataset
|
214
|
+
if server = opts[:server]
|
215
|
+
ds = ds.server(server)
|
216
|
+
end
|
217
|
+
|
218
|
+
# MariaDB doesn't support negative values for infinite wait. A wait of 34 years
|
219
|
+
# should be reasonably similar to infinity for this case.
|
220
|
+
timeout = opts[:wait] ? 1073741823 : 0
|
221
|
+
|
222
|
+
synchronize(server) do |c|
|
223
|
+
begin
|
224
|
+
unless locked = ds.get{GET_LOCK(lock_id, timeout)} == 1
|
225
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
226
|
+
end
|
227
|
+
|
228
|
+
yield
|
229
|
+
ensure
|
230
|
+
ds.get{RELEASE_LOCK(lock_id)} if locked
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
200
235
|
private
|
201
236
|
|
202
237
|
def alter_table_add_column_sql(table, op)
|
@@ -550,6 +585,20 @@ module Sequel
|
|
550
585
|
end
|
551
586
|
end
|
552
587
|
|
588
|
+
# Return nil if CHECK constraints are not supported, because
|
589
|
+
# versions that don't support check constraints don't raise
|
590
|
+
# errors for values outside of range.
|
591
|
+
def column_schema_integer_min_max_values(column)
|
592
|
+
super if supports_check_constraints?
|
593
|
+
end
|
594
|
+
|
595
|
+
# Return nil if CHECK constraints are not supported, because
|
596
|
+
# versions that don't support check constraints don't raise
|
597
|
+
# errors for values outside of range.
|
598
|
+
def column_schema_decimal_min_max_values(column)
|
599
|
+
super if supports_check_constraints?
|
600
|
+
end
|
601
|
+
|
553
602
|
# Split DROP INDEX ops on MySQL 5.6+, as dropping them in the same
|
554
603
|
# statement as dropping a related foreign key causes an error.
|
555
604
|
def split_alter_table_op?(op)
|
@@ -632,7 +681,7 @@ module Sequel
|
|
632
681
|
MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
|
633
682
|
|
634
683
|
Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
|
635
|
-
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
|
684
|
+
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update returning')
|
636
685
|
Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having window compounds order limit lock')
|
637
686
|
Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
|
638
687
|
|
@@ -760,6 +809,21 @@ module Sequel
|
|
760
809
|
clone(:insert_ignore=>true)
|
761
810
|
end
|
762
811
|
|
812
|
+
# Support insert select for associations, so that the model code can use
|
813
|
+
# returning instead of a separate query.
|
814
|
+
def insert_select(*values)
|
815
|
+
return unless supports_insert_select?
|
816
|
+
# Handle case where query does not return a row
|
817
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
818
|
+
end
|
819
|
+
|
820
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
821
|
+
# unless the RETURNING clause is already present.
|
822
|
+
def insert_select_sql(*values)
|
823
|
+
ds = opts[:returning] ? self : returning
|
824
|
+
ds.insert_sql(*values)
|
825
|
+
end
|
826
|
+
|
763
827
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
764
828
|
# If you pass no arguments, ALL fields will be
|
765
829
|
# updated with the new values. If you pass the fields you
|
@@ -857,6 +921,11 @@ module Sequel
|
|
857
921
|
true
|
858
922
|
end
|
859
923
|
|
924
|
+
# MariaDB 10.5.0 supports INSERT RETURNING.
|
925
|
+
def supports_returning?(type)
|
926
|
+
(type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
|
927
|
+
end
|
928
|
+
|
860
929
|
# MySQL 8+ supports SKIP LOCKED.
|
861
930
|
def supports_skip_locked?
|
862
931
|
!db.mariadb? && db.server_version >= 80000
|
@@ -895,6 +964,16 @@ module Sequel
|
|
895
964
|
super if type == :truncate || @opts[:offset]
|
896
965
|
end
|
897
966
|
|
967
|
+
# The strftime format to use when literalizing time (Sequel::SQLTime) values.
|
968
|
+
def default_time_format
|
969
|
+
db.supports_timestamp_usecs? ? super : "'%H:%M:%S'"
|
970
|
+
end
|
971
|
+
|
972
|
+
# The strftime format to use when literalizing timestamp (Time/DateTime) values.
|
973
|
+
def default_timestamp_format
|
974
|
+
db.supports_timestamp_usecs? ? super : "'%Y-%m-%d %H:%M:%S'"
|
975
|
+
end
|
976
|
+
|
898
977
|
# Consider the first table in the joined dataset is the table to delete
|
899
978
|
# from, but include the others for the purposes of selecting rows.
|
900
979
|
def delete_from_sql(sql)
|