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