sequel 5.74.0 → 5.76.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/opening_databases.rdoc +4 -2
  4. data/doc/release_notes/5.75.0.txt +35 -0
  5. data/doc/release_notes/5.76.0.txt +86 -0
  6. data/doc/testing.rdoc +1 -0
  7. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  8. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  9. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +11 -0
  10. data/lib/sequel/adapters/mysql2.rb +2 -2
  11. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  12. data/lib/sequel/adapters/postgres.rb +6 -5
  13. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  14. data/lib/sequel/adapters/shared/mysql.rb +31 -1
  15. data/lib/sequel/adapters/shared/oracle.rb +4 -6
  16. data/lib/sequel/adapters/shared/postgres.rb +76 -1
  17. data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -4
  18. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  19. data/lib/sequel/adapters/sqlite.rb +24 -3
  20. data/lib/sequel/dataset/features.rb +10 -1
  21. data/lib/sequel/dataset/sql.rb +47 -34
  22. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  23. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  24. data/lib/sequel/extensions/migration.rb +34 -8
  25. data/lib/sequel/extensions/named_timezones.rb +1 -1
  26. data/lib/sequel/extensions/pg_array.rb +2 -0
  27. data/lib/sequel/extensions/pg_extended_date_support.rb +4 -4
  28. data/lib/sequel/extensions/pg_range.rb +2 -2
  29. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  30. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  31. data/lib/sequel/plugins/column_encryption.rb +1 -1
  32. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  33. data/lib/sequel/version.rb +1 -1
  34. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16c90ef17199e4e48f39edee069981ba0030cf63dc481b136f637b1fe069743e
4
- data.tar.gz: 52063f32827a04c33173867207290da294878d898d25b80c37666cf66a54b780
3
+ metadata.gz: ca7f95e54ed437de173bbdfac08f4091f0e8debfb1a89bb168cd6157341afa9d
4
+ data.tar.gz: f49e07aec7ae48c509abf3965bc10ec102616d5ccc99ecb49d0116a9162760af
5
5
  SHA512:
6
- metadata.gz: d808534a13ae702a884524ceae4adcdb8eff9d46681a62d5b2c0d926b46279743350520248311c41f8a385c7fc66d17cbc44c7f1af52f1cffd356fca498d6bb2
7
- data.tar.gz: 309d0f0c0a7c47a4f1acc90107443e7fd2aa51cb09038082354e822711b11986ae271bb784e710336cebafc83b8b5a99adf088bfd19e2353728d25de86f942c4
6
+ metadata.gz: 9b8453bf85f48038160481a3b795a692a670b3e73084e6f531f353739c2549f809af5d45368db4dbcc0ac0bbd318eb4a94a02d0289d8c09117641f0ea4dc493d
7
+ data.tar.gz: a7daa11f1dc3d757f1618f4e8ee832c33e7072f375cfeea028e68b340414a159c92d05d76d75fd9bbb35cb498699ed9db32c9e7cbb158bb2b8a88e598ade34e6
data/CHANGELOG CHANGED
@@ -1,3 +1,43 @@
1
+ === 5.76.0 (2024-01-01)
2
+
3
+ * Improve performance and flexibility of regexp matching in sqlite adapter (paddor) (#2108)
4
+
5
+ * Support SQL::Identifier for Database#tables :schema option values on PostgreSQL (jeremyevans)
6
+
7
+ * Support generating rcte queries using UNION or UNION ALL in the rcte plugin (jonathanfrias) (#2107)
8
+
9
+ * Make Database#table_exists? on PostgreSQL handle lock or statement timeout errors as evidence the table exists (jeremyevans) (#2106)
10
+
11
+ * Work around DateTime.jd fractional second bug on JRuby in named_timezones extension (jeremyevans)
12
+
13
+ * Support fractional times and timestamps on SQLAnywhere (jeremyevans)
14
+
15
+ * Make round_timestamps extension use Dataset#sqltime_precision for rounding Sequel::SQLTime values (jeremyevans)
16
+
17
+ * Remove special handling of %N modifier in Dataset#default_timestamp_format (jeremyevans)
18
+
19
+ * Add Dataset#default_time_format private method, for adapters to override for time (not timestamp) formatting (jeremyevans)
20
+
21
+ * Remove Dataset#format_timestamp_offset private method (jeremyevans)
22
+
23
+ * Remove special handling of %z modifier in Dataset#default_timestamp_format (jeremyevans)
24
+
25
+ * Add Dataset#literal_date_or_time, for simpler use by bound argument code (jeremyevans)
26
+
27
+ * Add auto_cast_date_and_time extension, for casting date and time values using SQL standard functions (jeremyevans)
28
+
29
+ === 5.75.0 (2023-12-01)
30
+
31
+ * Make any_not_empty? extension support passing pattern argument to any? (jeremyevans) (#2100)
32
+
33
+ * Respect :skip_transaction option in PostgreSQL Dataset#paged_each (jeremyevans) (#2097)
34
+
35
+ * Add TimestampMigrator.run_single to run a single migration file up or down (opya, jeremyevans) (#2093)
36
+
37
+ * Support INSERT RETURNING on MariaDB 10.5+, and use it when saving new model objects (jeremyevans)
38
+
39
+ * Add Database#{defer,immediate}_constraints on PostgreSQL for changing handling of deferrable constraints in a transaction (jeremyevans)
40
+
1
41
  === 5.74.0 (2023-11-01)
2
42
 
3
43
  * Make generated columns show up in Database#schema when using SQLite 3.37+ (jeremyevans) (#2087)
@@ -388,8 +388,10 @@ The following additional options are supported:
388
388
  :readonly :: open database in read-only mode
389
389
  :timeout :: the busy timeout to use in milliseconds (default: 5000).
390
390
  :setup_regexp_function :: Whether to setup a REGEXP function in the underlying SQLite3::Database object. Doing so
391
- allows you to use regexp support in dataset expressions. Note that this creates a new
392
- Regexp object per call to the function, so it is not an efficient implementation.
391
+ allows you to use regexp support in dataset expressions. If +:cached+ or <tt>"cached"</tt>+, caches each
392
+ unique regex (more efficient but risk of memory leak). If a Proc is provided, it will be called with
393
+ a string for the regexp and a string for the value to compare, and should return whether the regexp
394
+ string matches the string value to compare. The default Proc used does <tt>Regexp.new(regexp_str).match(str)</tt>.
393
395
 
394
396
  Note that SQLite memory databases are restricted to a single connection by
395
397
  default. This is because SQLite does not allow multiple connections to
@@ -0,0 +1,35 @@
1
+ = New Features
2
+
3
+ * Database#{defer,immediate}_constraints methods have been added on
4
+ PostgreSQL for changing handling of deferrable constraints inside
5
+ a transaction. defer_constraints sets deferrable constraints to
6
+ be deferred (not checked until transaction commit), and
7
+ immediate_constraints sets deferrable constraints to be checked
8
+ as part of the related query, and any already deferred constraint
9
+ checks to be applied immediately. You can pass the :constraints
10
+ option to only apply the changes to specific constraints.
11
+
12
+ * TimestampMigrator.run_single has been added, to migrate a single
13
+ migration up or down.
14
+
15
+ = Other Improvements
16
+
17
+ * INSERT RETURNING is now supported on MariaDB 10.5+, and used
18
+ automatically when saving new model objects. Note that this
19
+ is not supported when using the jdbc adapter, because the
20
+ jdbc-mysql driver doesn't support it. A jdbc/mariadb adapter
21
+ could be added, as it's likely recent versions of the
22
+ jdbc-mariadb driver would support it, but the jdbc-mariadb gem
23
+ hasn't been updated in over 4 years. Talk to the jdbc-mariadb
24
+ gem maintainers if you want to use this feature with the jdbc
25
+ adapter.
26
+
27
+ * The Dataset#paged_each optimization in the postgres adapter
28
+ now respects the :skip_transaction option, making it the
29
+ same as the :hold option. Note that this has effects beyond
30
+ just skipping the transaction, but non-HOLD cursors are only
31
+ supported inside transactions.
32
+
33
+ * The any_not_empty? extension's Dataset#any? method now supports
34
+ an argument, passing it to Enumerable#any? (which has supported
35
+ an argument since Ruby 2.5).
@@ -0,0 +1,86 @@
1
+ = New Features
2
+
3
+ * An auto_cast_date_and_time extension has been added, which will
4
+ automatically cast date and time values using SQL standard functions.
5
+ This makes sure the database will treat the value as a date, time,
6
+ or timestamp, instead of treating it as a string or unknown type:
7
+
8
+ DB.get(Date.today).class
9
+ # SELECT '2024-01-01' AS v LIMIT 1
10
+ String
11
+
12
+ DB.extension(:auto_cast_date_and_time)
13
+ DB.get(Date.today).class
14
+ # SELECT DATE '2024-01-01' AS v LIMIT 1
15
+ Date
16
+
17
+ This was already Sequel's default behavior on adapters that required
18
+ it. This extension is usable on PostgreSQL and MySQL. It is not
19
+ usable on SQLite (no date/time types) or Microsoft SQL Server (no
20
+ support for the SQL standard conversion syntax).
21
+
22
+ This extension can break code that currently works. If using it on
23
+ PostgreSQL, it will cast the values to TIMESTAMP, not TIMESTAMP
24
+ WITH TIME ZONE, which can break code that depended on an implicit
25
+ conversion to TIMESTAMP WITH TIME ZONE. The pg_timestamptz
26
+ extension integrates with the the auto_cast_date_and_time extension
27
+ and will implicitly cast Time/DateTime to TIMESTAMP WITH TIME ZONE.
28
+
29
+ * The sqlite adapter now supports a :cached value for the
30
+ :setup_regexp_function Database option, which will cache regexp
31
+ values instead of creating a new regexp per value to compare. This
32
+ is much faster when using a regexp comparison on a large dataset,
33
+ but can result in a memory leak if using dynamic regexps. You can
34
+ also provide a Proc value for the :setup_regexp_function option,
35
+ which will be passed both the regexp source string and the database
36
+ string to compare, and should return whether the database string
37
+ matches the regexp string.
38
+
39
+ * The rcte_tree plugin now supports a :union_all option, which can
40
+ be set to false to use UNION instead of UNION ALL in the recursive
41
+ common table expression.
42
+
43
+ = Other Improvements
44
+
45
+ * Time/DateTime/SQLite literalization speed has more than doubled
46
+ compared to the previous version. The internal code is also much
47
+ simpler, as the speedup resulted from removing multiple abstraction
48
+ layers that mostly existed for Ruby 1.8 support.
49
+
50
+ * Database#table_exists? on PostgreSQL now handles lock or statement
51
+ timeout errors as evidence the table exists.
52
+
53
+ * The round_timestamps extension now correctly rounds SQLTime values
54
+ on Microsoft SQL Server (the only database Sequel supports where
55
+ time precision is different than timestamp precision).
56
+
57
+ * Fractional times and timestamps are now supported on SQLAnywhere,
58
+ except for time values when using the jdbc adapter due to a
59
+ limitation in the JDBC sqlanywhere driver.
60
+
61
+ * Database#tables and #views on PostgreSQL now supports
62
+ SQL::Identifier values for the :schema option.
63
+
64
+ * The named_timezones extension now works around a bug in DateTime.jd
65
+ on JRuby.
66
+
67
+ = Backwards Compatibility
68
+
69
+ * Time/DateTime/SQLTime literalization internals have changed.
70
+ If you are using an external adapter and the external adapter
71
+ overrides or calls any of the following methods:
72
+
73
+ * requires_sql_standard_datetimes?
74
+ * supports_timestamp_usecs?
75
+ * supports_timestamp_timezones?
76
+ * timestamp_precision
77
+ * sqltime_precision
78
+
79
+ then the adapter may need to be updated to support Sequel 5.76.0.
80
+ Additionally, if the adapter uses %N or %z in
81
+ default_timestamp_format, it may need to be updated. Adapters
82
+ should now just override default_timestamp_format and/or
83
+ default_time_format methods as appropriate for the database.
84
+
85
+ * The Dataset#format_timestamp_offset private method has been
86
+ removed.
data/doc/testing.rdoc CHANGED
@@ -159,6 +159,7 @@ 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
@@ -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
@@ -56,6 +56,17 @@ module Sequel
56
56
 
57
57
  private
58
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
+
59
70
  SMALLINT_TYPE = Java::JavaSQL::Types::SMALLINT
60
71
  BOOLEAN_METHOD = Object.new
61
72
  def BOOLEAN_METHOD.call(r, i)
@@ -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
@@ -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
@@ -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'
@@ -917,6 +917,11 @@ module Sequel
917
917
  500
918
918
  end
919
919
 
920
+ # The strftime format to use when literalizing the time.
921
+ def default_timestamp_format
922
+ db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super
923
+ end
924
+
920
925
  # SQL fragment specifying a list of identifiers
921
926
  def identifier_list(columns)
922
927
  columns.map{|i| quote_identifier(i)}.join(', ')
@@ -111,6 +111,13 @@ module Sequel
111
111
  # static data that you do not want to modify
112
112
  # :timeout :: how long to wait for the database to be available if it
113
113
  # is locked, given in milliseconds (default is 5000)
114
+ # :setup_regexp_function :: enable use of Regexp objects with SQL
115
+ # 'REGEXP' operator. If the value is :cached or "cached",
116
+ # caches the generated regexps, which can result in a memory
117
+ # leak if dynamic regexps are used. If the value is a Proc,
118
+ # it will be called with a string for the regexp and a string
119
+ # for the value to compare, and should return whether the regexp
120
+ # matches.
114
121
  def connect(server)
115
122
  opts = server_opts(server)
116
123
  opts[:database] = ':memory:' if blank_object?(opts[:database])
@@ -126,9 +133,7 @@ module Sequel
126
133
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
134
 
128
135
  if typecast_value_boolean(opts[:setup_regexp_function])
129
- db.create_function("regexp", 2) do |func, regexp_str, string|
130
- func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
- end
136
+ setup_regexp_function(db, opts[:setup_regexp_function])
132
137
  end
133
138
 
134
139
  class << db
@@ -202,6 +207,22 @@ module Sequel
202
207
  @conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
203
208
  set_integer_booleans
204
209
  end
210
+
211
+ def setup_regexp_function(db, how)
212
+ case how
213
+ when Proc
214
+ # nothing
215
+ when :cached, "cached"
216
+ cache = Hash.new{|h,k| h[k] = Regexp.new(k)}
217
+ how = lambda{|regexp_str, str| cache[regexp_str].match(str)}
218
+ else
219
+ how = lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
220
+ end
221
+
222
+ db.create_function("regexp", 2) do |func, regexp_str, str|
223
+ func.result = how.call(regexp_str, str) ? 1 : 0
224
+ end
225
+ end
205
226
 
206
227
  # Yield an available connection. Rescue
207
228
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
@@ -25,11 +25,16 @@ module Sequel
25
25
  false
26
26
  end
27
27
 
28
+ # :nocov:
29
+
28
30
  # Whether the dataset requires SQL standard datetimes. False by default,
29
- # as most allow strings with ISO 8601 format.
31
+ # as most allow strings with ISO 8601 format. Only for backwards compatibility,
32
+ # no longer used internally, do not use in new code.
30
33
  def requires_sql_standard_datetimes?
34
+ # SEQUEL6: Remove
31
35
  false
32
36
  end
37
+ # :nocov:
33
38
 
34
39
  # Whether type specifiers are required for prepared statement/bound
35
40
  # variable argument placeholders (i.e. :bv__integer), false by default.
@@ -183,10 +188,14 @@ module Sequel
183
188
  true
184
189
  end
185
190
 
191
+ # :nocov:
192
+
186
193
  # Whether the dataset supports timezones in literal timestamps, false by default.
187
194
  def supports_timestamp_timezones?
195
+ # SEQUEL6: Remove
188
196
  false
189
197
  end
198
+ # :nocov:
190
199
 
191
200
  # Whether the dataset supports fractional seconds in literal timestamps, true by default.
192
201
  def supports_timestamp_usecs?
@@ -82,7 +82,7 @@ module Sequel
82
82
  when DateTime
83
83
  literal_datetime_append(sql, v)
84
84
  when Date
85
- sql << literal_date(v)
85
+ literal_date_append(sql, v)
86
86
  when Dataset
87
87
  literal_dataset_append(sql, v)
88
88
  else
@@ -115,6 +115,33 @@ module Sequel
115
115
  sql
116
116
  end
117
117
 
118
+ # Literalize a date or time value, as a SQL string value with no
119
+ # typecasting. If +raw+ is true, remove the surrounding single
120
+ # quotes. This is designed for usage by bound argument code that
121
+ # can work even if the auto_cast_date_and_time extension is
122
+ # used (either manually or implicitly in the related adapter).
123
+ def literal_date_or_time(dt, raw=false)
124
+ value = case dt
125
+ when SQLTime
126
+ literal_sqltime(dt)
127
+ when Time
128
+ literal_time(dt)
129
+ when DateTime
130
+ literal_datetime(dt)
131
+ when Date
132
+ literal_date(dt)
133
+ else
134
+ raise TypeError, "unsupported type: #{dt.inspect}"
135
+ end
136
+
137
+ if raw
138
+ value.sub!(/\A'/, '')
139
+ value.sub!(/'\z/, '')
140
+ end
141
+
142
+ value
143
+ end
144
+
118
145
  # Returns an array of insert statements for inserting multiple records.
119
146
  # This method is used by +multi_insert+ to format insert statements and
120
147
  # expects a keys array and and an array of value arrays.
@@ -1104,9 +1131,14 @@ module Sequel
1104
1131
  :"t#{number}"
1105
1132
  end
1106
1133
 
1107
- # The strftime format to use when literalizing the time.
1134
+ # The strftime format to use when literalizing time (Sequel::SQLTime) values.
1135
+ def default_time_format
1136
+ "'%H:%M:%S.%6N'"
1137
+ end
1138
+
1139
+ # The strftime format to use when literalizing timestamp (Time/DateTime) values.
1108
1140
  def default_timestamp_format
1109
- requires_sql_standard_datetimes? ? "TIMESTAMP '%Y-%m-%d %H:%M:%S%N%z'" : "'%Y-%m-%d %H:%M:%S%N%z'"
1141
+ "'%Y-%m-%d %H:%M:%S.%6N'"
1110
1142
  end
1111
1143
 
1112
1144
  def delete_delete_sql(sql)
@@ -1169,43 +1201,23 @@ module Sequel
1169
1201
  {1 => ((op == :IN) ? 0 : 1)}
1170
1202
  end
1171
1203
 
1172
- # Format the timestamp based on the default_timestamp_format, with a couple
1173
- # of modifiers. First, allow %N to be used for fractions seconds (if the
1174
- # database supports them), and override %z to always use a numeric offset
1175
- # of hours and minutes.
1204
+ # Format the timestamp based on the default_timestamp_format.
1176
1205
  def format_timestamp(v)
1177
- v2 = db.from_application_timestamp(v)
1178
- fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
1179
- if m == '%N'
1180
- # Ruby 1.9 supports %N in timestamp formats, but Sequel has supported %N
1181
- # for longer in a different way, where the . is already appended and only 6
1182
- # decimal places are used by default.
1183
- format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(1000000) : v.usec) if supports_timestamp_usecs?
1184
- else
1185
- if supports_timestamp_timezones?
1186
- # Would like to just use %z format, but it doesn't appear to work on Windows
1187
- # Instead, the offset fragment is constructed manually
1188
- minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
1189
- format_timestamp_offset(*minutes.divmod(60))
1190
- end
1191
- end
1192
- end
1193
- v2.strftime(fmt)
1206
+ db.from_application_timestamp(v).strftime(default_timestamp_format)
1194
1207
  end
1195
1208
 
1196
- # Return the SQL timestamp fragment to use for the timezone offset.
1197
- def format_timestamp_offset(hour, minute)
1198
- sprintf("%+03i%02i", hour, minute)
1199
- end
1209
+ # :nocov:
1200
1210
 
1201
1211
  # Return the SQL timestamp fragment to use for the fractional time part.
1202
1212
  # Should start with the decimal point. Uses 6 decimal places by default.
1203
1213
  def format_timestamp_usec(usec, ts=timestamp_precision)
1214
+ # SEQUEL6: Remove
1204
1215
  unless ts == 6
1205
1216
  usec = usec/(10 ** (6 - ts))
1206
1217
  end
1207
1218
  sprintf(".%0#{ts}d", usec)
1208
1219
  end
1220
+ # :nocov:
1209
1221
 
1210
1222
  # Append literalization of identifier to SQL string, considering regular strings
1211
1223
  # as SQL identifiers instead of SQL strings.
@@ -1347,11 +1359,12 @@ module Sequel
1347
1359
 
1348
1360
  # SQL fragment for Date, using the ISO8601 format.
1349
1361
  def literal_date(v)
1350
- if requires_sql_standard_datetimes?
1351
- v.strftime("DATE '%Y-%m-%d'")
1352
- else
1353
- v.strftime("'%Y-%m-%d'")
1354
- end
1362
+ v.strftime("'%Y-%m-%d'")
1363
+ end
1364
+
1365
+ # Append literalization of date to SQL string.
1366
+ def literal_date_append(sql, v)
1367
+ sql << literal_date(v)
1355
1368
  end
1356
1369
 
1357
1370
  # SQL fragment for DateTime
@@ -1414,7 +1427,7 @@ module Sequel
1414
1427
 
1415
1428
  # SQL fragment for Sequel::SQLTime, containing just the time part
1416
1429
  def literal_sqltime(v)
1417
- v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec, sqltime_precision) if supports_timestamp_usecs?}'")
1430
+ v.strftime(default_time_format)
1418
1431
  end
1419
1432
 
1420
1433
  # Append literalization of Sequel::SQLTime to SQL string.
@@ -32,8 +32,8 @@
32
32
  module Sequel
33
33
  module AnyNotEmpty
34
34
  # If a block is not given, return whether the dataset is not empty.
35
- def any?
36
- if defined?(yield)
35
+ def any?(*a)
36
+ if !a.empty? || defined?(yield)
37
37
  super
38
38
  else
39
39
  !empty?
@@ -0,0 +1,94 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The auto_cast_date_and_time extension uses SQL standard type casting
4
+ # when literalizing date, time, and timestamp values:
5
+ #
6
+ # DB.literal(Time.now)
7
+ # # => "TIMESTAMP '...'"
8
+ #
9
+ # DB.literal(Date.today)
10
+ # # => "DATE '...'"
11
+ #
12
+ # DB.literal(Sequel::SQLTime.create(10, 20, 30))
13
+ # # => "TIME '10:20:30.000000'"
14
+ #
15
+ # The default behavior of Sequel on adapters that do not require the
16
+ # SQL standard behavior is to format the date or time value without:
17
+ # casting
18
+ #
19
+ # DB.literal(Sequel::SQLTime.create(10, 20, 30))
20
+ # # => "'10:20:30.000000'"
21
+ #
22
+ # However, then the database cannot determine the type of the string,
23
+ # and must perform some implicit casting. If implicit casting cannot
24
+ # be used, it will probably treat the value as a string:
25
+ #
26
+ # DB.get(Time.now).class
27
+ # # Without auto_cast_date_and_time: String
28
+ # # With auto_cast_date_and_time: Time
29
+ #
30
+ # Note that not all databases support this extension. PostgreSQL and
31
+ # MySQL support it, but SQLite and Microsoft SQL Server do not.
32
+ #
33
+ # You can load this extension into specific datasets:
34
+ #
35
+ # ds = DB[:table]
36
+ # ds = ds.extension(:auto_cast_date_and_time)
37
+ #
38
+ # Or you can load it into all of a database's datasets, which
39
+ # is probably the desired behavior if you are using this extension:
40
+ #
41
+ # DB.extension(:auto_cast_date_and_time)
42
+ #
43
+ # Related module: Sequel::AutoCastDateAndTime
44
+
45
+ #
46
+ module Sequel
47
+ module AutoCastDateAndTime
48
+ # :nocov:
49
+
50
+ # Mark the datasets as requiring sql standard date times. This is only needed
51
+ # for backwards compatibility.
52
+ def requires_sql_standard_datetimes?
53
+ # SEQUEL6: Remove
54
+ true
55
+ end
56
+ # :nocov:
57
+
58
+ private
59
+
60
+ # Explicitly cast SQLTime objects to TIME.
61
+ def literal_sqltime_append(sql, v)
62
+ sql << "TIME "
63
+ super
64
+ end
65
+
66
+ # Explicitly cast Time objects to TIMESTAMP.
67
+ def literal_time_append(sql, v)
68
+ sql << literal_datetime_timestamp_cast
69
+ super
70
+ end
71
+
72
+ # Explicitly cast DateTime objects to TIMESTAMP.
73
+ def literal_datetime_append(sql, v)
74
+ sql << literal_datetime_timestamp_cast
75
+ super
76
+ end
77
+
78
+ # Explicitly cast Date objects to DATE.
79
+ def literal_date_append(sql, v)
80
+ sql << "DATE "
81
+ super
82
+ end
83
+
84
+ # The default cast string to use for Time/DateTime objects.
85
+ # Respects existing method if already defined.
86
+ def literal_datetime_timestamp_cast
87
+ return super if defined?(super)
88
+ 'TIMESTAMP '
89
+ end
90
+ end
91
+
92
+ Dataset.register_extension(:auto_cast_date_and_time, AutoCastDateAndTime)
93
+ end
94
+
@@ -693,6 +693,13 @@ module Sequel
693
693
  @migration_tuples = get_migration_tuples
694
694
  end
695
695
 
696
+ # Apply the migration in the given file path. See Migrator.run for the
697
+ # available options. Additionally, this method supports the :direction
698
+ # option for whether to run the migration up (default) or down.
699
+ def self.run_single(db, path, opts=OPTS)
700
+ new(db, File.dirname(path), opts).run_single(path, opts[:direction] || :up)
701
+ end
702
+
696
703
  # The timestamp migrator is current if there are no migrations to apply
697
704
  # in either direction.
698
705
  def is_current?
@@ -702,20 +709,39 @@ module Sequel
702
709
  # Apply all migration tuples on the database
703
710
  def run
704
711
  migration_tuples.each do |m, f, direction|
705
- t = Time.now
706
- db.log_info("Begin applying migration #{f}, direction: #{direction}")
707
- checked_transaction(m) do
708
- m.apply(db, direction)
709
- fi = f.downcase
710
- direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
711
- end
712
- db.log_info("Finished applying migration #{f}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
712
+ apply_migration(m, f, direction)
713
713
  end
714
714
  nil
715
715
  end
716
716
 
717
+ # Apply single migration tuple at the given path with the given direction
718
+ # on the database.
719
+ def run_single(path, direction)
720
+ migration = load_migration_file(path)
721
+ file_name = File.basename(path)
722
+ already_applied = applied_migrations.include?(file_name.downcase)
723
+
724
+ return if direction == :up ? already_applied : !already_applied
725
+
726
+ apply_migration(migration, file_name, direction)
727
+ nil
728
+ end
729
+
717
730
  private
718
731
 
732
+ # Apply a single migration with the given filename in the given direction.
733
+ def apply_migration(migration, file_name, direction)
734
+ fi = file_name.downcase
735
+ t = Time.now
736
+
737
+ db.log_info("Begin applying migration #{file_name}, direction: #{direction}")
738
+ checked_transaction(migration) do
739
+ migration.apply(db, direction)
740
+ direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
741
+ end
742
+ db.log_info("Finished applying migration #{file_name}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
743
+ end
744
+
719
745
  # Convert the schema_info table to the new schema_migrations table format,
720
746
  # using the version of the schema_info table and the current migration files.
721
747
  def convert_from_schema_info
@@ -136,7 +136,7 @@ module Sequel
136
136
  v = output_timezone.utc_to_local(v.new_offset(0))
137
137
 
138
138
  # Force DateTime output instead of TZInfo::DateTimeWithOffset
139
- DateTime.jd(v.jd, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
139
+ DateTime.civil(v.year, v.month, v.day, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
140
140
  end
141
141
  # :nodoc:
142
142
  # :nocov:
@@ -241,6 +241,8 @@ module Sequel
241
241
  else
242
242
  literal(a)
243
243
  end
244
+ when Time, Date
245
+ @default_dataset.literal_date_or_time(a)
244
246
  else
245
247
  if (s = bound_variable_arg(a, nil)).is_a?(String)
246
248
  bound_variable_array_string(s)
@@ -53,8 +53,8 @@ module Sequel
53
53
  # on jdbc.
54
54
  def bound_variable_arg(arg, conn)
55
55
  case arg
56
- when Date, Time
57
- literal(arg)
56
+ when Time, Date
57
+ @default_dataset.literal_date_or_time(arg)
58
58
  else
59
59
  super
60
60
  end
@@ -203,7 +203,7 @@ module Sequel
203
203
  date <<= ((date.year) * 24 - 12)
204
204
  date = db.from_application_timestamp(date)
205
205
  minutes = (date.offset * 1440).to_i
206
- date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
206
+ date.strftime("'%Y-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *minutes.divmod(60))} BC'")
207
207
  else
208
208
  super
209
209
  end
@@ -247,7 +247,7 @@ module Sequel
247
247
  def literal_time(time)
248
248
  if time < TIME_YEAR_1
249
249
  time = db.from_application_timestamp(time)
250
- time.strftime("'#{sprintf('%04i', time.year.abs+1)}-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*(time.utc_offset/RATIONAL_60).divmod(60))} BC'")
250
+ time.strftime("'#{sprintf('%04i', time.year.abs+1)}-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *(time.utc_offset/RATIONAL_60).divmod(60))} BC'")
251
251
  else
252
252
  super
253
253
  end
@@ -494,8 +494,8 @@ module Sequel
494
494
  case k
495
495
  when nil
496
496
  ''
497
- when Date, Time
498
- ds.literal(k)[1...-1]
497
+ when Time, Date
498
+ ds.literal_date_or_time(k, true)
499
499
  when Integer, Float
500
500
  k.to_s
501
501
  when BigDecimal
@@ -1,20 +1,35 @@
1
1
  # frozen-string-literal: true
2
2
  #
3
3
  # The pg_timestamptz extension changes the default timestamp
4
- # type for the database to be +timestamptz+ (+timestamp with time zone+)
5
- # instead of +timestamp+ (+timestamp without time zone+). This is
4
+ # type for the database to be +timestamptz+ (<tt>timestamp with time zone</tt>)
5
+ # instead of +timestamp+ (<tt>timestamp without time zone</tt>). This is
6
6
  # recommended if you are dealing with multiple timezones in your application.
7
+ #
8
+ # If you are using the auto_cast_date_and_time extension, the pg_timestamptz
9
+ # extension will automatically cast Time and DateTime values to
10
+ # <tt>TIMESTAMP WITH TIME ZONE</tt> instead of +TIMESTAMP+.
7
11
  #
8
12
  # To load the extension into the database:
9
13
  #
10
14
  # DB.extension :pg_timestamptz
11
15
  #
12
- # Related module: Sequel::Postgres::Timestamptz
16
+ # To load the extension into individual datasets:
17
+ #
18
+ # ds = ds.extension(:pg_timestamptz)
19
+ #
20
+ # Note that the loading into individual datasets only affects the integration
21
+ # with the auto_cast_date_and_time extension.
22
+ #
23
+ # Related modules: Sequel::Postgres::Timestamptz, Sequel::Postgres::TimestamptzDatasetMethods
13
24
 
14
25
  #
15
26
  module Sequel
16
27
  module Postgres
17
28
  module Timestamptz
29
+ def self.extended(db)
30
+ db.extend_datasets(TimestamptzDatasetMethods)
31
+ end
32
+
18
33
  private
19
34
 
20
35
  # Use timestamptz by default for generic timestamp value.
@@ -22,7 +37,16 @@ module Sequel
22
37
  :timestamptz
23
38
  end
24
39
  end
40
+
41
+ module TimestamptzDatasetMethods
42
+ private
43
+
44
+ def literal_datetime_timestamp_cast
45
+ 'TIMESTAMP WITH TIME ZONE '
46
+ end
47
+ end
25
48
  end
26
49
 
50
+ Dataset.register_extension(:pg_timestamptz, Postgres::TimestamptzDatasetMethods)
27
51
  Database.register_extension(:pg_timestamptz, Postgres::Timestamptz)
28
52
  end
@@ -35,7 +35,7 @@ module Sequel
35
35
 
36
36
  # Round Sequel::SQLTime values before literalizing
37
37
  def literal_sqltime(v)
38
- super(v.round(timestamp_precision))
38
+ super(v.round(sqltime_precision))
39
39
  end
40
40
 
41
41
  # Round Time values before literalizing
@@ -325,7 +325,7 @@ module Sequel
325
325
  # DB.alter_table(:ce_test) do
326
326
  # c = Sequel[:encrypted_column_name]
327
327
  # add_constraint(:enc_base64) do
328
- # octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65}
328
+ # octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65
329
329
  # end
330
330
  # end
331
331
  #
@@ -71,6 +71,8 @@ module Sequel
71
71
  # (default: :t)
72
72
  # :level_alias :: The symbol identifier to use when eagerly loading descendants
73
73
  # up to a given level (default: :x_level_x)
74
+ # :union_all :: Whether to use UNION ALL or UNION with the recursive
75
+ # common table expression (default: true)
74
76
  module RcteTree
75
77
  # Create the appropriate parent, children, ancestors, and descendants
76
78
  # associations for the model.
@@ -80,6 +82,7 @@ module Sequel
80
82
  opts = opts.dup
81
83
  opts[:class] = model
82
84
  opts[:methods_module] = Module.new
85
+ opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all]
83
86
  model.send(:include, opts[:methods_module])
84
87
 
85
88
  key = opts[:key] ||= :parent_id
@@ -142,7 +145,7 @@ module Sequel
142
145
  model.from(SQL::AliasedExpression.new(t, table_alias)).
143
146
  with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
144
147
  recursive_ds.select(*c_all),
145
- :args=>col_aliases)
148
+ :args=>col_aliases, union_all: opts[:union_all])
146
149
  end
147
150
  aal = Array(a[:after_load])
148
151
  aal << proc do |m, ancs|
@@ -191,7 +194,7 @@ module Sequel
191
194
  table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
192
195
  ds = model.from(SQL::AliasedExpression.new(t, table_alias)).
193
196
  with_recursive(t, base_case, recursive_case,
194
- :args=>((key_aliases + col_aliases) if col_aliases))
197
+ :args=>((key_aliases + col_aliases) if col_aliases), union_all: opts[:union_all])
195
198
  ds = r.apply_eager_dataset_changes(ds)
196
199
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
197
200
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
@@ -240,7 +243,7 @@ module Sequel
240
243
  model.from(SQL::AliasedExpression.new(t, table_alias)).
241
244
  with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
242
245
  recursive_ds.select(*c_all),
243
- :args=>col_aliases)
246
+ :args=>col_aliases, union_all: opts[:union_all])
244
247
  end
245
248
  dal = Array(d[:after_load])
246
249
  dal << proc do |m, descs|
@@ -299,7 +302,7 @@ module Sequel
299
302
  table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
300
303
  ds = model.from(SQL::AliasedExpression.new(t, table_alias)).
301
304
  with_recursive(t, base_case, recursive_case,
302
- :args=>((key_aliases + col_aliases + (level ? [la] : [])) if col_aliases))
305
+ :args=>((key_aliases + col_aliases + (level ? [la] : [])) if col_aliases), union_all: opts[:union_all])
303
306
  ds = r.apply_eager_dataset_changes(ds)
304
307
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
305
308
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 74
9
+ MINOR = 76
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.74.0
4
+ version: 5.76.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-01 00:00:00.000000000 Z
11
+ date: 2024-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -221,6 +221,8 @@ extra_rdoc_files:
221
221
  - doc/release_notes/5.72.0.txt
222
222
  - doc/release_notes/5.73.0.txt
223
223
  - doc/release_notes/5.74.0.txt
224
+ - doc/release_notes/5.75.0.txt
225
+ - doc/release_notes/5.76.0.txt
224
226
  - doc/release_notes/5.8.0.txt
225
227
  - doc/release_notes/5.9.0.txt
226
228
  files:
@@ -323,6 +325,8 @@ files:
323
325
  - doc/release_notes/5.72.0.txt
324
326
  - doc/release_notes/5.73.0.txt
325
327
  - doc/release_notes/5.74.0.txt
328
+ - doc/release_notes/5.75.0.txt
329
+ - doc/release_notes/5.76.0.txt
326
330
  - doc/release_notes/5.8.0.txt
327
331
  - doc/release_notes/5.9.0.txt
328
332
  - doc/schema_modification.rdoc
@@ -424,6 +428,7 @@ files:
424
428
  - lib/sequel/extensions/any_not_empty.rb
425
429
  - lib/sequel/extensions/arbitrary_servers.rb
426
430
  - lib/sequel/extensions/async_thread_pool.rb
431
+ - lib/sequel/extensions/auto_cast_date_and_time.rb
427
432
  - lib/sequel/extensions/auto_literal_strings.rb
428
433
  - lib/sequel/extensions/blank.rb
429
434
  - lib/sequel/extensions/caller_logging.rb
@@ -654,7 +659,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
654
659
  - !ruby/object:Gem::Version
655
660
  version: '0'
656
661
  requirements: []
657
- rubygems_version: 3.4.10
662
+ rubygems_version: 3.5.3
658
663
  signing_key:
659
664
  specification_version: 4
660
665
  summary: The Database Toolkit for Ruby