sequel 5.68.0 → 5.77.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +134 -0
  3. data/README.rdoc +3 -3
  4. data/doc/mass_assignment.rdoc +1 -1
  5. data/doc/migration.rdoc +15 -0
  6. data/doc/opening_databases.rdoc +12 -3
  7. data/doc/release_notes/5.69.0.txt +26 -0
  8. data/doc/release_notes/5.70.0.txt +35 -0
  9. data/doc/release_notes/5.71.0.txt +21 -0
  10. data/doc/release_notes/5.72.0.txt +33 -0
  11. data/doc/release_notes/5.73.0.txt +66 -0
  12. data/doc/release_notes/5.74.0.txt +45 -0
  13. data/doc/release_notes/5.75.0.txt +35 -0
  14. data/doc/release_notes/5.76.0.txt +86 -0
  15. data/doc/release_notes/5.77.0.txt +63 -0
  16. data/doc/sharding.rdoc +3 -1
  17. data/doc/testing.rdoc +4 -2
  18. data/lib/sequel/adapters/ibmdb.rb +1 -1
  19. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  20. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  21. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  22. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  23. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  24. data/lib/sequel/adapters/jdbc.rb +10 -6
  25. data/lib/sequel/adapters/mysql.rb +19 -7
  26. data/lib/sequel/adapters/mysql2.rb +2 -2
  27. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  28. data/lib/sequel/adapters/postgres.rb +6 -5
  29. data/lib/sequel/adapters/shared/db2.rb +12 -0
  30. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  31. data/lib/sequel/adapters/shared/mysql.rb +31 -1
  32. data/lib/sequel/adapters/shared/oracle.rb +4 -6
  33. data/lib/sequel/adapters/shared/postgres.rb +79 -4
  34. data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -4
  35. data/lib/sequel/adapters/shared/sqlite.rb +20 -3
  36. data/lib/sequel/adapters/sqlite.rb +42 -3
  37. data/lib/sequel/adapters/trilogy.rb +117 -0
  38. data/lib/sequel/connection_pool/sharded_threaded.rb +11 -10
  39. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  40. data/lib/sequel/connection_pool/threaded.rb +6 -0
  41. data/lib/sequel/connection_pool/timed_queue.rb +16 -3
  42. data/lib/sequel/connection_pool.rb +10 -1
  43. data/lib/sequel/database/connecting.rb +1 -1
  44. data/lib/sequel/database/misc.rb +2 -2
  45. data/lib/sequel/database/schema_methods.rb +9 -2
  46. data/lib/sequel/database/transactions.rb +6 -0
  47. data/lib/sequel/dataset/actions.rb +8 -6
  48. data/lib/sequel/dataset/features.rb +10 -1
  49. data/lib/sequel/dataset/sql.rb +47 -34
  50. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  51. data/lib/sequel/extensions/async_thread_pool.rb +3 -2
  52. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  53. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  54. data/lib/sequel/extensions/connection_validator.rb +15 -10
  55. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  56. data/lib/sequel/extensions/index_caching.rb +5 -1
  57. data/lib/sequel/extensions/migration.rb +52 -13
  58. data/lib/sequel/extensions/named_timezones.rb +1 -1
  59. data/lib/sequel/extensions/pg_array.rb +10 -0
  60. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  61. data/lib/sequel/extensions/pg_extended_date_support.rb +4 -4
  62. data/lib/sequel/extensions/pg_json_ops.rb +52 -0
  63. data/lib/sequel/extensions/pg_range.rb +2 -2
  64. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  65. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  66. data/lib/sequel/extensions/schema_caching.rb +1 -1
  67. data/lib/sequel/extensions/server_block.rb +2 -1
  68. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  69. data/lib/sequel/model/associations.rb +9 -2
  70. data/lib/sequel/model/base.rb +25 -12
  71. data/lib/sequel/model/dataset_module.rb +3 -0
  72. data/lib/sequel/model/exceptions.rb +15 -3
  73. data/lib/sequel/plugins/column_encryption.rb +27 -6
  74. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  75. data/lib/sequel/plugins/list.rb +5 -2
  76. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  77. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  78. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  79. data/lib/sequel/plugins/paged_operations.rb +181 -0
  80. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +5 -1
  81. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  82. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  83. data/lib/sequel/plugins/static_cache.rb +38 -0
  84. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  85. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  86. data/lib/sequel/version.rb +1 -1
  87. metadata +43 -3
data/doc/sharding.rdoc CHANGED
@@ -39,7 +39,9 @@ is the simplest configuration:
39
39
  servers: {read_only: {host: 'replica_server'}})
40
40
 
41
41
  This will use the replica_server for SELECT queries and primary_server for
42
- other queries.
42
+ other queries. The :read_only key in the :servers hash is special in that
43
+ it sets the default database for Dataset methods that use SELECT queries
44
+ (which are generally read queries that do not modify the database).
43
45
 
44
46
  If you want to ensure your queries are going to a specific database, you
45
47
  can force this for a given query by using the .server method and passing
data/doc/testing.rdoc CHANGED
@@ -159,12 +159,13 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
159
159
 
160
160
  === Other
161
161
 
162
+ SEQUEL_AUTO_CAST_DATE_TIME :: Use the auto_cast_date_and_time extension when running the specs
162
163
  SEQUEL_ASYNC_THREAD_POOL :: Use the async_thread_pool extension when running the specs
163
164
  SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when running the specs, with the :preempt_async_thread option
164
165
  SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
165
166
  SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
166
167
  SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
167
- SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
168
+ SEQUEL_CONNECTION_VALIDATOR :: Use the connection_validator extension when running the adapter/integration specs
168
169
  SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
169
170
  SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
170
171
  SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
@@ -176,7 +177,7 @@ SEQUEL_MODEL_PREPARED_STATEMENTS :: Use the prepared_statements plugin when runn
176
177
  SEQUEL_MODEL_THROW_FAILURES :: Use the throw_failures plugin when running the specs
177
178
  SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
178
179
  SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
179
- SEQUEL_PG_AUTO_PARAMETERIZE :: Use the pg_auto_parameterize extension when running the postgres specs
180
+ SEQUEL_PG_AUTO_PARAMETERIZE :: Use the pg_auto_parameterize extension when running the postgres specs. Value can be +in_array+ to test the pg_auto_parameterize_in_array extension, and +in_array_string+ to test the pg_auto_parameterize_in_array extension with the +:treat_in_string_list_as_text_array+ Database option set.
180
181
  SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
181
182
  SEQUEL_PRIMARY_KEY_LOOKUP_CHECK_VALUES :: Use the primary_key_lookup_check_values extension when running the adapter or integration specs
182
183
  SEQUEL_QUERY_PER_ASSOCIATION_DB_0_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
@@ -185,4 +186,5 @@ SEQUEL_QUERY_PER_ASSOCIATION_DB_2_URL :: Run query-per-association integration t
185
186
  SEQUEL_QUERY_PER_ASSOCIATION_DB_3_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
186
187
  SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
187
188
  SEQUEL_SYNCHRONIZE_SQL :: Use the synchronize_sql extension when running the specs
189
+ SEQUEL_TRANSACTION_CONNECTION_VALIDATOR :: Use the transaction_connection_validator extension when running the adapter/integration specs
188
190
  SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
@@ -301,7 +301,7 @@ module Sequel
301
301
  end
302
302
 
303
303
  def database_exception_sqlstate(exception, opts)
304
- exception.sqlstate
304
+ exception.sqlstate if exception.respond_to?(:sqlstate)
305
305
  end
306
306
 
307
307
  def dataset_class_default
@@ -1,6 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  Sequel::JDBC.load_driver('org.h2.Driver', :H2)
4
+ require_relative '../../extensions/auto_cast_date_and_time'
4
5
 
5
6
  module Sequel
6
7
  module JDBC
@@ -14,6 +15,8 @@ module Sequel
14
15
 
15
16
  module H2
16
17
  module DatabaseMethods
18
+ include AutoCastDateAndTime
19
+
17
20
  def commit_prepared_transaction(transaction_id, opts=OPTS)
18
21
  run("COMMIT TRANSACTION #{transaction_id}", opts)
19
22
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  Sequel::JDBC.load_driver('org.hsqldb.jdbcDriver', :HSQLDB)
4
4
  require_relative 'transactions'
5
+ require_relative '../../extensions/auto_cast_date_and_time'
5
6
 
6
7
  module Sequel
7
8
  module JDBC
@@ -15,6 +16,7 @@ module Sequel
15
16
 
16
17
  module HSQLDB
17
18
  module DatabaseMethods
19
+ include AutoCastDateAndTime
18
20
  include ::Sequel::JDBC::Transactions
19
21
 
20
22
  def database_type
@@ -199,6 +199,7 @@ module Sequel
199
199
  v.strftime("'%H:%M:%S#{sprintf(".%03d", (v.usec/1000.0).round)}'")
200
200
  end
201
201
 
202
+ INTEGER_TYPE = Java::JavaSQL::Types::INTEGER
202
203
  STRING_TYPE = Java::JavaSQL::Types::VARCHAR
203
204
  ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
204
205
  PG_SPECIFIC_TYPES = [Java::JavaSQL::Types::ARRAY, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT, Java::JavaSQL::Types::TIME_WITH_TIMEZONE, Java::JavaSQL::Types::TIME].freeze
@@ -219,6 +220,8 @@ module Sequel
219
220
  oid = meta.getField(i).getOID
220
221
  if pr = db.oid_convertor_proc(oid)
221
222
  pr
223
+ elsif oid == 28 # XID (Transaction ID)
224
+ map[INTEGER_TYPE]
222
225
  elsif oid == 2950 # UUID
223
226
  map[STRING_TYPE]
224
227
  elsif meta.getPGType(i) == 'hstore'
@@ -36,6 +36,10 @@ module Sequel
36
36
 
37
37
  private
38
38
 
39
+ def database_exception_use_sqlstates?
40
+ false
41
+ end
42
+
39
43
  # Use @@IDENTITY to get the last inserted id
40
44
  def last_insert_id(conn, opts=OPTS)
41
45
  statement(conn) do |stmt|
@@ -52,6 +56,17 @@ module Sequel
52
56
 
53
57
  private
54
58
 
59
+ # JDBC SQLAnywhere driver does not appear to handle fractional
60
+ # times correctly.
61
+ def default_time_format
62
+ "'%H:%M:%S'"
63
+ end
64
+
65
+ # Set to zero to work around JDBC SQLAnywhere driver bug.
66
+ def sqltime_precision
67
+ 0
68
+ end
69
+
55
70
  SMALLINT_TYPE = Java::JavaSQL::Types::SMALLINT
56
71
  BOOLEAN_METHOD = Object.new
57
72
  def BOOLEAN_METHOD.call(r, i)
@@ -79,6 +79,10 @@ module Sequel
79
79
  super.with_extend(MetadataDatasetMethods)
80
80
  end
81
81
 
82
+ def database_exception_use_sqlstates?
83
+ false
84
+ end
85
+
82
86
  def disconnect_error?(exception, opts)
83
87
  super || (exception.message =~ /connection is closed/)
84
88
  end
@@ -396,11 +396,16 @@ module Sequel
396
396
 
397
397
  def database_exception_sqlstate(exception, opts)
398
398
  if database_exception_use_sqlstates?
399
- while exception.respond_to?(:cause)
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
- cause = exception.respond_to?(:cause) ? exception.cause : exception
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 Mysql.respond_to?(:init)
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 = Mysql.new
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
- conn.singleton_class.class_eval do
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]
@@ -182,8 +182,8 @@ module Sequel
182
182
  1
183
183
  when false
184
184
  0
185
- when DateTime, Time
186
- literal(arg)[1...-1]
185
+ when Time, Date
186
+ @default_dataset.literal_date_or_time(arg, true)
187
187
  else
188
188
  arg
189
189
  end
@@ -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%N'}"
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
@@ -188,8 +188,8 @@ module Sequel
188
188
  # :nocov:
189
189
  # Not covered by tests as tests use pg_extended_date_support
190
190
  # extension, which has basically the same code.
191
- when DateTime, Time
192
- literal(arg)
191
+ when Time, DateTime
192
+ @default_dataset.literal_date_or_time(arg)
193
193
  # :nocov:
194
194
  else
195
195
  arg
@@ -672,6 +672,7 @@ module Sequel
672
672
  # cursor usage.
673
673
  # :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
674
674
  # numbers result in fewer queries but greater memory use.
675
+ # :skip_transaction :: Same as :hold, but :hold takes priority.
675
676
  #
676
677
  # Usage:
677
678
  #
@@ -764,13 +765,13 @@ module Sequel
764
765
 
765
766
  # Use a cursor to fetch groups of records at a time, yielding them to the block.
766
767
  def cursor_fetch_rows(sql)
767
- server_opts = {:server=>@opts[:server] || :read_only}
768
768
  cursor = @opts[:cursor]
769
- hold = cursor[:hold]
769
+ hold = cursor.fetch(:hold){cursor[:skip_transaction]}
770
+ server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
770
771
  cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
771
772
  rows_per_fetch = cursor[:rows_per_fetch].to_i
772
773
 
773
- db.public_send(*(hold ? [:synchronize, server_opts[:server]] : [:transaction, server_opts])) do
774
+ db.transaction(server_opts) do
774
775
  begin
775
776
  execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
776
777
  rows_per_fetch = 1000 if rows_per_fetch <= 0
@@ -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
@@ -932,7 +932,7 @@ module Sequel
932
932
  # since that is the format that is multilanguage and not
933
933
  # DATEFORMAT dependent.
934
934
  def default_timestamp_format
935
- "'%Y-%m-%dT%H:%M:%S%N%z'"
935
+ "'%Y-%m-%dT%H:%M:%S.%3N'"
936
936
  end
937
937
 
938
938
  # Only include the primary table in the main delete clause
@@ -646,7 +646,7 @@ module Sequel
646
646
  MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
647
647
 
648
648
  Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
649
- Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
649
+ Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update returning')
650
650
  Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having window compounds order limit lock')
651
651
  Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
652
652
 
@@ -774,6 +774,21 @@ module Sequel
774
774
  clone(:insert_ignore=>true)
775
775
  end
776
776
 
777
+ # Support insert select for associations, so that the model code can use
778
+ # returning instead of a separate query.
779
+ def insert_select(*values)
780
+ return unless supports_insert_select?
781
+ # Handle case where query does not return a row
782
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
783
+ end
784
+
785
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
786
+ # unless the RETURNING clause is already present.
787
+ def insert_select_sql(*values)
788
+ ds = opts[:returning] ? self : returning
789
+ ds.insert_sql(*values)
790
+ end
791
+
777
792
  # Sets up the insert methods to use ON DUPLICATE KEY UPDATE
778
793
  # If you pass no arguments, ALL fields will be
779
794
  # updated with the new values. If you pass the fields you
@@ -871,6 +886,11 @@ module Sequel
871
886
  true
872
887
  end
873
888
 
889
+ # MariaDB 10.5.0 supports INSERT RETURNING.
890
+ def supports_returning?(type)
891
+ (type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
892
+ end
893
+
874
894
  # MySQL 8+ supports SKIP LOCKED.
875
895
  def supports_skip_locked?
876
896
  !db.mariadb? && db.server_version >= 80000
@@ -909,6 +929,16 @@ module Sequel
909
929
  super if type == :truncate || @opts[:offset]
910
930
  end
911
931
 
932
+ # The strftime format to use when literalizing time (Sequel::SQLTime) values.
933
+ def default_time_format
934
+ db.supports_timestamp_usecs? ? super : "'%H:%M:%S'"
935
+ end
936
+
937
+ # The strftime format to use when literalizing timestamp (Time/DateTime) values.
938
+ def default_timestamp_format
939
+ db.supports_timestamp_usecs? ? super : "'%Y-%m-%d %H:%M:%S'"
940
+ end
941
+
912
942
  # Consider the first table in the joined dataset is the table to delete
913
943
  # from, but include the others for the purposes of selecting rows.
914
944
  def delete_from_sql(sql)
@@ -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 '../../extensions/auto_cast_date_and_time'
4
5
 
5
6
  module Sequel
6
7
  module Oracle
@@ -326,6 +327,8 @@ module Sequel
326
327
  end
327
328
 
328
329
  module DatasetMethods
330
+ include AutoCastDateAndTime
331
+
329
332
  ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
330
333
  BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
331
334
 
@@ -623,7 +626,7 @@ module Sequel
623
626
 
624
627
  # The strftime format to use when literalizing the time.
625
628
  def default_timestamp_format
626
- "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'"
629
+ "'%Y-%m-%d %H:%M:%S.%6N %:z'"
627
630
  end
628
631
 
629
632
  def empty_from_sql
@@ -660,11 +663,6 @@ module Sequel
660
663
  super
661
664
  end
662
665
 
663
- # Use a colon for the timestamp offset, since Oracle appears to require it.
664
- def format_timestamp_offset(hour, minute)
665
- sprintf("%+03i:%02i", hour, minute)
666
- end
667
-
668
666
  # Oracle doesn't support empty values when inserting.
669
667
  def insert_supports_empty_values?
670
668
  false
@@ -498,6 +498,25 @@ module Sequel
498
498
  :postgres
499
499
  end
500
500
 
501
+ # For constraints that are deferrable, defer constraints until
502
+ # transaction commit. Options:
503
+ #
504
+ # :constraints :: An identifier of the constraint, or an array of
505
+ # identifiers for constraints, to apply this
506
+ # change to specific constraints.
507
+ # :server :: The server/shard on which to run the query.
508
+ #
509
+ # Examples:
510
+ #
511
+ # DB.defer_constraints
512
+ # # SET CONSTRAINTS ALL DEFERRED
513
+ #
514
+ # DB.defer_constraints(constraints: [:c1, Sequel[:sc][:c2]])
515
+ # # SET CONSTRAINTS "c1", "sc"."s2" DEFERRED
516
+ def defer_constraints(opts=OPTS)
517
+ _set_constraints(' DEFERRED', opts)
518
+ end
519
+
501
520
  # Use PostgreSQL's DO syntax to execute an anonymous code block. The code should
502
521
  # be the literal code string to use in the underlying procedural language. Options:
503
522
  #
@@ -611,6 +630,24 @@ module Sequel
611
630
  super
612
631
  end
613
632
 
633
+ # Immediately apply deferrable constraints.
634
+ #
635
+ # :constraints :: An identifier of the constraint, or an array of
636
+ # identifiers for constraints, to apply this
637
+ # change to specific constraints.
638
+ # :server :: The server/shard on which to run the query.
639
+ #
640
+ # Examples:
641
+ #
642
+ # DB.immediate_constraints
643
+ # # SET CONSTRAINTS ALL IMMEDIATE
644
+ #
645
+ # DB.immediate_constraints(constraints: [:c1, Sequel[:sc][:c2]])
646
+ # # SET CONSTRAINTS "c1", "sc"."s2" IMMEDIATE
647
+ def immediate_constraints(opts=OPTS)
648
+ _set_constraints(' IMMEDIATE', opts)
649
+ end
650
+
614
651
  # Use the pg_* system tables to determine indexes on a table
615
652
  def indexes(table, opts=OPTS)
616
653
  m = output_identifier_meth
@@ -1038,6 +1075,31 @@ module Sequel
1038
1075
  end
1039
1076
  end
1040
1077
 
1078
+ # Internals of defer_constraints/immediate_constraints
1079
+ def _set_constraints(type, opts)
1080
+ execute_ddl(_set_constraints_sql(type, opts), opts)
1081
+ end
1082
+
1083
+ # SQL to use for SET CONSTRAINTS
1084
+ def _set_constraints_sql(type, opts)
1085
+ sql = String.new
1086
+ sql << "SET CONSTRAINTS "
1087
+ if constraints = opts[:constraints]
1088
+ dataset.send(:source_list_append, sql, Array(constraints))
1089
+ else
1090
+ sql << "ALL"
1091
+ end
1092
+ sql << type
1093
+ end
1094
+
1095
+ # Consider lock or statement timeout errors as evidence that the table exists
1096
+ # but is locked.
1097
+ def _table_exists?(ds)
1098
+ super
1099
+ rescue DatabaseError => e
1100
+ raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
1101
+ end
1102
+
1041
1103
  def alter_table_add_column_sql(table, op)
1042
1104
  "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
1043
1105
  end
@@ -1451,7 +1513,11 @@ module Sequel
1451
1513
  # currently visible schemas.
1452
1514
  def filter_schema(ds, opts)
1453
1515
  expr = if schema = opts[:schema]
1454
- schema.to_s
1516
+ if schema.is_a?(SQL::Identifier)
1517
+ schema.value.to_s
1518
+ else
1519
+ schema.to_s
1520
+ end
1455
1521
  else
1456
1522
  Sequel.function(:any, Sequel.function(:current_schemas, false))
1457
1523
  end
@@ -1745,8 +1811,6 @@ module Sequel
1745
1811
  literal_append(sql, args[0])
1746
1812
  sql << ' ' << op.to_s << ' '
1747
1813
  literal_append(sql, args[1])
1748
- sql << " ESCAPE "
1749
- literal_append(sql, "\\")
1750
1814
  sql << ')'
1751
1815
  else
1752
1816
  super
@@ -1800,7 +1864,7 @@ module Sequel
1800
1864
  # :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
1801
1865
  # returned rows also include the exact phrase used.
1802
1866
  # :rank :: Set to true to order by the rank, so that closer matches are returned first.
1803
- # :to_tsquery :: Can be set to :plain or :phrase to specify the function to use to
1867
+ # :to_tsquery :: Can be set to :plain, :phrase, or :websearch to specify the function to use to
1804
1868
  # convert the terms to a ts_query.
1805
1869
  # :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
1806
1870
  # tsquery, and can be used directly in the query.
@@ -1820,6 +1884,8 @@ module Sequel
1820
1884
  query_func = case to_tsquery = opts[:to_tsquery]
1821
1885
  when :phrase, :plain
1822
1886
  :"#{to_tsquery}to_tsquery"
1887
+ when :websearch
1888
+ :"websearch_to_tsquery"
1823
1889
  else
1824
1890
  (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1825
1891
  end
@@ -2084,10 +2150,14 @@ module Sequel
2084
2150
  server_version >= 90500
2085
2151
  end
2086
2152
 
2153
+ # :nocov:
2154
+
2087
2155
  # PostgreSQL supports timezones in literal timestamps
2088
2156
  def supports_timestamp_timezones?
2157
+ # SEQUEL6: Remove
2089
2158
  true
2090
2159
  end
2160
+ # :nocov:
2091
2161
 
2092
2162
  # PostgreSQL 8.4+ supports WINDOW clause.
2093
2163
  def supports_window_clause?
@@ -2207,6 +2277,11 @@ module Sequel
2207
2277
  raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
2208
2278
  end
2209
2279
 
2280
+ # The strftime format to use when literalizing the time.
2281
+ def default_timestamp_format
2282
+ "'%Y-%m-%d %H:%M:%S.%6N%z'"
2283
+ end
2284
+
2210
2285
  # Only include the primary table in the main delete clause
2211
2286
  def delete_from_sql(sql)
2212
2287
  sql << ' FROM '
@@ -281,10 +281,6 @@ module Sequel
281
281
  false
282
282
  end
283
283
 
284
- def supports_timestamp_usecs?
285
- false
286
- end
287
-
288
284
  def supports_window_clause?
289
285
  true
290
286
  end
@@ -378,6 +374,16 @@ module Sequel
378
374
 
379
375
  private
380
376
 
377
+ # SQLAnywhere only supports 3 digits after the decimal point for times.
378
+ def default_time_format
379
+ "'%H:%M:%S.%3N'"
380
+ end
381
+
382
+ # SQLAnywhere only supports 3 digits after the decimal point for timestamps.
383
+ def default_timestamp_format
384
+ "'%Y-%m-%d %H:%M:%S.%3N'"
385
+ end
386
+
381
387
  # Use 1 for true on Sybase
382
388
  def literal_true
383
389
  '1'
@@ -145,6 +145,11 @@ module Sequel
145
145
  sqlite_version >= 30608
146
146
  end
147
147
 
148
+ # SQLite 3.8.2+ supports the without rowid table constraint
149
+ def support_without_rowid?
150
+ sqlite_version >= 30802
151
+ end
152
+
148
153
  # Override the default setting for whether to use timezones in timestamps.
149
154
  # It is set to +false+ by default, as SQLite's date/time methods do not
150
155
  # support timezones in timestamps.
@@ -344,9 +349,17 @@ module Sequel
344
349
  ps
345
350
  end
346
351
 
347
- # Support creating STRICT tables via :strict option
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
348
353
  def create_table_sql(name, generator, options)
349
- "#{super}#{' STRICT' if options[:strict]}"
354
+ if options[:strict] && options[:without_rowid]
355
+ "#{super} STRICT, WITHOUT ROWID"
356
+ elsif options[:strict]
357
+ "#{super} STRICT"
358
+ elsif options[:without_rowid]
359
+ "#{super} WITHOUT ROWID"
360
+ else
361
+ super
362
+ end
350
363
  end
351
364
 
352
365
  # SQLite support creating temporary views.
@@ -504,7 +517,6 @@ module Sequel
504
517
  # table_xinfo PRAGMA used, remove hidden columns
505
518
  # that are not generated columns
506
519
  if row[:generated] = (row.delete(:hidden) != 0)
507
- next unless row[:type].end_with?(' GENERATED ALWAYS')
508
520
  row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
509
521
  end
510
522
  end
@@ -918,6 +930,11 @@ module Sequel
918
930
  500
919
931
  end
920
932
 
933
+ # The strftime format to use when literalizing the time.
934
+ def default_timestamp_format
935
+ db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super
936
+ end
937
+
921
938
  # SQL fragment specifying a list of identifiers
922
939
  def identifier_list(columns)
923
940
  columns.map{|i| quote_identifier(i)}.join(', ')