sequel 5.19.0 → 5.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +102 -0
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/sharding.rdoc +2 -0
- data/doc/testing.rdoc +1 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/ado.rb +27 -19
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/lib/sequel/adapters/shared/mssql.rb +7 -7
- data/lib/sequel/adapters/shared/postgres.rb +37 -19
- data/lib/sequel/adapters/shared/sqlite.rb +27 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +52 -8
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +387 -123
- data/lib/sequel/extensions/pg_range.rb +3 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/server_block.rb +15 -4
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
- data/lib/sequel/plugins/rcte_tree.rb +6 -0
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/sql.rb +15 -3
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +24 -0
- data/spec/adapters/mysql_spec.rb +0 -5
- data/spec/adapters/postgres_spec.rb +319 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +123 -2
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +25 -1
- data/spec/core/schema_spec.rb +24 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
- data/spec/extensions/pg_json_spec.rb +218 -29
- data/spec/extensions/pg_range_spec.rb +76 -9
- data/spec/extensions/rcte_tree_spec.rb +6 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/extensions/server_block_spec.rb +38 -0
- data/spec/extensions/spec_helper.rb +8 -1
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +25 -9
- data/spec/integration/plugin_test.rb +42 -0
- data/spec/integration/schema_test.rb +7 -2
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- metadata +16 -2
|
@@ -112,7 +112,7 @@ module Sequel
|
|
|
112
112
|
sqlite3_opts = {}
|
|
113
113
|
sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
|
|
114
114
|
db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
|
|
115
|
-
db.busy_timeout(opts.fetch(:timeout, 5000))
|
|
115
|
+
db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
|
|
116
116
|
|
|
117
117
|
if USE_EXTENDED_RESULT_CODES
|
|
118
118
|
db.extended_result_codes = true
|
|
@@ -16,6 +16,18 @@ module Sequel
|
|
|
16
16
|
c = TinyTds::Client.new(opts)
|
|
17
17
|
c.query_options.merge!(:cache_rows=>false)
|
|
18
18
|
|
|
19
|
+
if opts[:ansi]
|
|
20
|
+
sql = %w(
|
|
21
|
+
ANSI_NULLS
|
|
22
|
+
ANSI_PADDING
|
|
23
|
+
ANSI_WARNINGS
|
|
24
|
+
ANSI_NULL_DFLT_ON
|
|
25
|
+
QUOTED_IDENTIFIER
|
|
26
|
+
CONCAT_NULL_YIELDS_NULL
|
|
27
|
+
).map{|v| "SET #{v} ON"}.join(";")
|
|
28
|
+
log_connection_yield(sql, c){c.execute(sql)}
|
|
29
|
+
end
|
|
30
|
+
|
|
19
31
|
if (ts = opts[:textsize])
|
|
20
32
|
sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
|
|
21
33
|
log_connection_yield(sql, c){c.execute(sql)}
|
|
@@ -35,7 +35,7 @@ module Sequel
|
|
|
35
35
|
# Yield to the block, logging any errors at error level to all loggers,
|
|
36
36
|
# and all other queries with the duration at warn or info level.
|
|
37
37
|
def log_connection_yield(sql, conn, args=nil)
|
|
38
|
-
return yield if
|
|
38
|
+
return yield if skip_logging?
|
|
39
39
|
sql = "#{connection_info(conn) if conn && log_connection_info}#{sql}#{"; #{args.inspect}" if args}"
|
|
40
40
|
timer = Sequel.start_timer
|
|
41
41
|
|
|
@@ -58,6 +58,12 @@ module Sequel
|
|
|
58
58
|
|
|
59
59
|
private
|
|
60
60
|
|
|
61
|
+
# Determine if logging should be skipped. Defaults to true if no loggers
|
|
62
|
+
# have been specified.
|
|
63
|
+
def skip_logging?
|
|
64
|
+
@loggers.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
61
67
|
# String including information about the connection, for use when logging
|
|
62
68
|
# connection info.
|
|
63
69
|
def connection_info(conn)
|
|
@@ -331,7 +331,7 @@ module Sequel
|
|
|
331
331
|
:time
|
|
332
332
|
when /\A(bool(ean)?)\z/io
|
|
333
333
|
:boolean
|
|
334
|
-
when /\A(real|float
|
|
334
|
+
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
|
|
335
335
|
:float
|
|
336
336
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
|
|
337
337
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
|
@@ -110,6 +110,9 @@ module Sequel
|
|
|
110
110
|
# yet exist on referenced table (but will exist before the transaction commits).
|
|
111
111
|
# Basically it adds DEFERRABLE INITIALLY DEFERRED on key creation.
|
|
112
112
|
# If you use :immediate as the value, uses DEFERRABLE INITIALLY IMMEDIATE.
|
|
113
|
+
# :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
|
|
114
|
+
# if generated columns are supported (PostgreSQL 12+, MariaDB 5.2.0+,
|
|
115
|
+
# and MySQL 5.7.6+).
|
|
113
116
|
# :index :: Create an index on this column. If given a hash, use the hash as the
|
|
114
117
|
# options for the index.
|
|
115
118
|
# :key :: For foreign key columns, the column in the associated table
|
|
@@ -126,15 +129,21 @@ module Sequel
|
|
|
126
129
|
# be used if you have a single, nonautoincrementing primary key column
|
|
127
130
|
# (use the primary_key method in that case).
|
|
128
131
|
# :primary_key_constraint_name :: The name to give the primary key constraint
|
|
132
|
+
# :primary_key_deferrable :: Similar to :deferrable, but for the primary key constraint
|
|
133
|
+
# if :primary_key is used.
|
|
129
134
|
# :type :: Overrides the type given as the argument. Generally not used by column
|
|
130
135
|
# itself, but can be passed as an option to other methods that call column.
|
|
131
136
|
# :unique :: Mark the column as unique, generally has the same effect as
|
|
132
137
|
# creating a unique index on the column.
|
|
133
138
|
# :unique_constraint_name :: The name to give the unique key constraint
|
|
139
|
+
# :unique_deferrable :: Similar to :deferrable, but for the unique constraint if :unique
|
|
140
|
+
# is used.
|
|
141
|
+
#
|
|
142
|
+
# PostgreSQL specific options:
|
|
143
|
+
#
|
|
144
|
+
# :identity :: Create an identity column.
|
|
134
145
|
#
|
|
135
146
|
# MySQL specific options:
|
|
136
|
-
# :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
|
|
137
|
-
# if generated columns are supported.
|
|
138
147
|
# :generated_type :: Set the type of column when using :generated_always_as,
|
|
139
148
|
# should be :virtual or :stored to force a type.
|
|
140
149
|
def column(name, type, opts = OPTS)
|
|
@@ -634,7 +643,7 @@ module Sequel
|
|
|
634
643
|
|
|
635
644
|
# Drop a composite foreign key constraint
|
|
636
645
|
def drop_composite_foreign_key(columns, opts)
|
|
637
|
-
@operations <<
|
|
646
|
+
@operations << opts.merge(:op => :drop_constraint, :type => :foreign_key, :columns => columns)
|
|
638
647
|
nil
|
|
639
648
|
end
|
|
640
649
|
end
|
|
@@ -586,6 +586,7 @@ module Sequel
|
|
|
586
586
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
|
587
587
|
end
|
|
588
588
|
sql << ' PRIMARY KEY'
|
|
589
|
+
constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
|
|
589
590
|
end
|
|
590
591
|
end
|
|
591
592
|
|
|
@@ -606,6 +607,7 @@ module Sequel
|
|
|
606
607
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
|
607
608
|
end
|
|
608
609
|
sql << ' UNIQUE'
|
|
610
|
+
constraint_deferrable_sql_append(sql, column[:unique_deferrable])
|
|
609
611
|
end
|
|
610
612
|
end
|
|
611
613
|
|
|
@@ -25,13 +25,19 @@ module Sequel
|
|
|
25
25
|
# Otherwise, add the block to the list of blocks to call after the currently
|
|
26
26
|
# in progress transaction commits (and only if it commits).
|
|
27
27
|
# Options:
|
|
28
|
+
# :savepoint :: If currently inside a savepoint, only run this hook on transaction
|
|
29
|
+
# commit if all enclosing savepoints have been released.
|
|
28
30
|
# :server :: The server/shard to use.
|
|
29
31
|
def after_commit(opts=OPTS, &block)
|
|
30
32
|
raise Error, "must provide block to after_commit" unless block
|
|
31
33
|
synchronize(opts[:server]) do |conn|
|
|
32
34
|
if h = _trans(conn)
|
|
33
35
|
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
|
34
|
-
|
|
36
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
|
37
|
+
add_savepoint_hook(conn, :after_commit, block)
|
|
38
|
+
else
|
|
39
|
+
add_transaction_hook(conn, :after_commit, block)
|
|
40
|
+
end
|
|
35
41
|
else
|
|
36
42
|
yield
|
|
37
43
|
end
|
|
@@ -42,13 +48,20 @@ module Sequel
|
|
|
42
48
|
# Otherwise, add the block to the list of the blocks to call after the currently
|
|
43
49
|
# in progress transaction rolls back (and only if it rolls back).
|
|
44
50
|
# Options:
|
|
51
|
+
# :savepoint :: If currently inside a savepoint, run this hook immediately when
|
|
52
|
+
# any enclosing savepoint is rolled back, which may be before the transaction
|
|
53
|
+
# commits or rollsback.
|
|
45
54
|
# :server :: The server/shard to use.
|
|
46
55
|
def after_rollback(opts=OPTS, &block)
|
|
47
56
|
raise Error, "must provide block to after_rollback" unless block
|
|
48
57
|
synchronize(opts[:server]) do |conn|
|
|
49
58
|
if h = _trans(conn)
|
|
50
59
|
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
|
51
|
-
|
|
60
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
|
61
|
+
add_savepoint_hook(conn, :after_rollback, block)
|
|
62
|
+
else
|
|
63
|
+
add_transaction_hook(conn, :after_rollback, block)
|
|
64
|
+
end
|
|
52
65
|
end
|
|
53
66
|
end
|
|
54
67
|
end
|
|
@@ -298,6 +311,13 @@ module Sequel
|
|
|
298
311
|
Sequel.synchronize{@transactions[conn] = hash}
|
|
299
312
|
end
|
|
300
313
|
|
|
314
|
+
# Set the given callable as a hook to be called. Type should be either
|
|
315
|
+
# :after_commit or :after_rollback.
|
|
316
|
+
def add_savepoint_hook(conn, type, block)
|
|
317
|
+
savepoint = _trans(conn)[:savepoints].last
|
|
318
|
+
(savepoint[type] ||= []) << block
|
|
319
|
+
end
|
|
320
|
+
|
|
301
321
|
# Set the given callable as a hook to be called. Type should be either
|
|
302
322
|
# :after_commit or :after_rollback.
|
|
303
323
|
def add_transaction_hook(conn, type, block)
|
|
@@ -401,6 +421,14 @@ module Sequel
|
|
|
401
421
|
supports_savepoints? && savepoint_level(conn) > 1
|
|
402
422
|
end
|
|
403
423
|
|
|
424
|
+
# Retrieve the savepoint hooks that should be run for the given
|
|
425
|
+
# connection and commit status.
|
|
426
|
+
def savepoint_hooks(conn, committed)
|
|
427
|
+
if in_savepoint?(conn)
|
|
428
|
+
_trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
404
432
|
# Retrieve the transaction hooks that should be run for the given
|
|
405
433
|
# connection and commit status.
|
|
406
434
|
def transaction_hooks(conn, committed)
|
|
@@ -411,16 +439,40 @@ module Sequel
|
|
|
411
439
|
|
|
412
440
|
# Remove the current thread from the list of active transactions
|
|
413
441
|
def remove_transaction(conn, committed)
|
|
414
|
-
|
|
442
|
+
if in_savepoint?(conn)
|
|
443
|
+
savepoint_callbacks = savepoint_hooks(conn, committed)
|
|
444
|
+
if committed
|
|
445
|
+
savepoint_rollback_callbacks = savepoint_hooks(conn, false)
|
|
446
|
+
end
|
|
447
|
+
else
|
|
448
|
+
callbacks = transaction_hooks(conn, committed)
|
|
449
|
+
end
|
|
415
450
|
|
|
416
451
|
if transaction_finished?(conn)
|
|
417
452
|
h = _trans(conn)
|
|
418
453
|
rolled_back = !committed
|
|
419
454
|
Sequel.synchronize{h[:rolled_back] = rolled_back}
|
|
420
455
|
Sequel.synchronize{@transactions.delete(conn)}
|
|
456
|
+
callbacks.each(&:call) if callbacks
|
|
457
|
+
elsif savepoint_callbacks || savepoint_rollback_callbacks
|
|
458
|
+
if committed
|
|
459
|
+
meth = in_savepoint?(conn) ? :add_savepoint_hook : :add_transaction_hook
|
|
460
|
+
|
|
461
|
+
if savepoint_callbacks
|
|
462
|
+
savepoint_callbacks.each do |block|
|
|
463
|
+
send(meth, conn, :after_commit, block)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
if savepoint_rollback_callbacks
|
|
468
|
+
savepoint_rollback_callbacks.each do |block|
|
|
469
|
+
send(meth, conn, :after_rollback, block)
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
else
|
|
473
|
+
savepoint_callbacks.each(&:call)
|
|
474
|
+
end
|
|
421
475
|
end
|
|
422
|
-
|
|
423
|
-
callbacks.each(&:call) if callbacks
|
|
424
476
|
end
|
|
425
477
|
|
|
426
478
|
# SQL to rollback to a savepoint
|
data/lib/sequel/dataset.rb
CHANGED
|
@@ -20,8 +20,10 @@ module Sequel
|
|
|
20
20
|
# old_posts = posts.where{stamp < Date.today - 7}
|
|
21
21
|
# davids_old_posts = davids_posts.where{stamp < Date.today - 7}
|
|
22
22
|
#
|
|
23
|
-
# Datasets are Enumerable objects, so they can be manipulated using
|
|
24
|
-
# of the Enumerable methods, such as map
|
|
23
|
+
# Datasets are Enumerable objects, so they can be manipulated using many
|
|
24
|
+
# of the Enumerable methods, such as +map+ and +inject+. Note that there are some methods
|
|
25
|
+
# that Dataset defines that override methods defined in Enumerable and result in different
|
|
26
|
+
# behavior, such as +select+ and +group_by+.
|
|
25
27
|
#
|
|
26
28
|
# For more information, see the {"Dataset Basics" guide}[rdoc-ref:doc/dataset_basics.rdoc].
|
|
27
29
|
class Dataset
|
|
@@ -333,6 +333,7 @@ module Sequel
|
|
|
333
333
|
# after every 50 records.
|
|
334
334
|
# :return :: When this is set to :primary_key, returns an array of
|
|
335
335
|
# autoincremented primary key values for the rows inserted.
|
|
336
|
+
# This does not have an effect if +values+ is a Dataset.
|
|
336
337
|
# :server :: Set the server/shard to use for the transaction and insert
|
|
337
338
|
# queries.
|
|
338
339
|
# :slice :: Same as :commit_every, :commit_every takes precedence.
|
|
@@ -1069,7 +1070,7 @@ module Sequel
|
|
|
1069
1070
|
|
|
1070
1071
|
# Set the server to use to :default unless it is already set in the passed opts
|
|
1071
1072
|
def default_server_opts(opts)
|
|
1072
|
-
if @db.sharded?
|
|
1073
|
+
if @db.sharded? && !opts.has_key?(:server)
|
|
1073
1074
|
opts = Hash[opts]
|
|
1074
1075
|
opts[:server] = @opts[:server] || :default
|
|
1075
1076
|
end
|
|
@@ -1080,7 +1081,7 @@ module Sequel
|
|
|
1080
1081
|
# :read_only server unless a specific server is set.
|
|
1081
1082
|
def execute(sql, opts=OPTS, &block)
|
|
1082
1083
|
db = @db
|
|
1083
|
-
if db.sharded?
|
|
1084
|
+
if db.sharded? && !opts.has_key?(:server)
|
|
1084
1085
|
opts = Hash[opts]
|
|
1085
1086
|
opts[:server] = @opts[:server] || (@opts[:lock] ? :default : :read_only)
|
|
1086
1087
|
opts
|
|
@@ -170,7 +170,10 @@ module Sequel
|
|
|
170
170
|
# receiver's dataset to the block, and the block should return the new dataset
|
|
171
171
|
# to use.
|
|
172
172
|
def with_dataset
|
|
173
|
-
|
|
173
|
+
dataset = yield @dataset
|
|
174
|
+
other = dup
|
|
175
|
+
other.instance_variable_set(:@dataset, dataset)
|
|
176
|
+
other.freeze
|
|
174
177
|
end
|
|
175
178
|
|
|
176
179
|
# Return an array of all objects by running the SQL query for the given arguments.
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -1062,6 +1062,10 @@ module Sequel
|
|
|
1062
1062
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
|
1063
1063
|
# :recursive :: Specify that this is a recursive CTE
|
|
1064
1064
|
#
|
|
1065
|
+
# PostgreSQL Specific Options:
|
|
1066
|
+
# :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
|
|
1067
|
+
# the CTE (PostgreSQL 12+).
|
|
1068
|
+
#
|
|
1065
1069
|
# DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
|
|
1066
1070
|
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
|
|
1067
1071
|
def with(name, dataset, opts=OPTS)
|
|
@@ -1091,7 +1095,7 @@ module Sequel
|
|
|
1091
1095
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
|
1092
1096
|
# # ) SELECT * FROM t
|
|
1093
1097
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
|
1094
|
-
raise(Error, 'This
|
|
1098
|
+
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
|
1095
1099
|
if hoist_cte?(nonrecursive)
|
|
1096
1100
|
s, ds = hoist_cte(nonrecursive)
|
|
1097
1101
|
s.with_recursive(name, ds, recursive, opts)
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -1510,13 +1510,7 @@ module Sequel
|
|
|
1510
1510
|
comma = ', '
|
|
1511
1511
|
ws.each do |w|
|
|
1512
1512
|
sql << comma if c
|
|
1513
|
-
|
|
1514
|
-
if args = w[:args]
|
|
1515
|
-
sql << '('
|
|
1516
|
-
identifier_list_append(sql, args)
|
|
1517
|
-
sql << ')'
|
|
1518
|
-
end
|
|
1519
|
-
sql << ' AS '
|
|
1513
|
+
select_with_sql_prefix(sql, w)
|
|
1520
1514
|
literal_dataset_append(sql, w[:dataset])
|
|
1521
1515
|
c ||= true
|
|
1522
1516
|
end
|
|
@@ -1530,6 +1524,16 @@ module Sequel
|
|
|
1530
1524
|
"WITH "
|
|
1531
1525
|
end
|
|
1532
1526
|
|
|
1527
|
+
def select_with_sql_prefix(sql, w)
|
|
1528
|
+
quote_identifier_append(sql, w[:name])
|
|
1529
|
+
if args = w[:args]
|
|
1530
|
+
sql << '('
|
|
1531
|
+
identifier_list_append(sql, args)
|
|
1532
|
+
sql << ')'
|
|
1533
|
+
end
|
|
1534
|
+
sql << ' AS '
|
|
1535
|
+
end
|
|
1536
|
+
|
|
1533
1537
|
# Whether the symbol cache should be skipped when literalizing the dataset
|
|
1534
1538
|
def skip_symbol_cache?
|
|
1535
1539
|
@opts[:skip_symbol_cache]
|
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Allows the use of named timezones via TZInfo (requires tzinfo).
|
|
4
4
|
# Forces the use of DateTime as Sequel's datetime_class, since
|
|
5
|
-
#
|
|
6
|
-
# and UTC.
|
|
5
|
+
# historically, Ruby's Time class doesn't support timezones other
|
|
6
|
+
# than local and UTC. To continue using Ruby's Time class when using
|
|
7
|
+
# the named_timezones extension:
|
|
8
|
+
#
|
|
9
|
+
# # Load the extension
|
|
10
|
+
# Sequel.extension :named_timezones
|
|
11
|
+
#
|
|
12
|
+
# # Set Sequel.datetime_class back to Time
|
|
13
|
+
# Sequel.datetime_class = Time
|
|
7
14
|
#
|
|
8
15
|
# This allows you to either pass strings or TZInfo::Timezone
|
|
9
16
|
# instance to Sequel.database_timezone=, application_timezone=, and
|
|
10
17
|
# typecast_timezone=. If a string is passed, it is converted to a
|
|
11
18
|
# TZInfo::Timezone using TZInfo::Timezone.get.
|
|
12
19
|
#
|
|
13
|
-
# To load the extension:
|
|
14
|
-
#
|
|
15
|
-
# Sequel.extension :named_timezones
|
|
16
|
-
#
|
|
17
20
|
# Let's say you have the database server in New York and the
|
|
18
21
|
# application server in Los Angeles. For historical reasons, data
|
|
19
22
|
# is stored in local New York time, but the application server only
|
|
@@ -37,7 +40,8 @@
|
|
|
37
40
|
# Note that typecasting from the database timezone to the application
|
|
38
41
|
# timezone when fetching rows is dependent on the database adapter,
|
|
39
42
|
# and only works on adapters where Sequel itself does the conversion.
|
|
40
|
-
# It should work
|
|
43
|
+
# It should work with the mysql, postgres, sqlite, ibmdb, and jdbc
|
|
44
|
+
# adapters.
|
|
41
45
|
#
|
|
42
46
|
# Related module: Sequel::NamedTimezones
|
|
43
47
|
|
|
@@ -63,9 +67,48 @@ module Sequel
|
|
|
63
67
|
|
|
64
68
|
private
|
|
65
69
|
|
|
70
|
+
if RUBY_VERSION >= '2.6'
|
|
71
|
+
# Convert the given input Time (which must be in UTC) to the given input timezone,
|
|
72
|
+
# which should be a TZInfo::Timezone instance.
|
|
73
|
+
def convert_input_time_other(v, input_timezone)
|
|
74
|
+
Time.new(v.year, v.mon, v.day, v.hour, v.min, (v.sec + Rational(v.nsec, 1000000000)), input_timezone)
|
|
75
|
+
rescue TZInfo::AmbiguousTime
|
|
76
|
+
raise unless disamb = tzinfo_disambiguator_for(v)
|
|
77
|
+
period = input_timezone.period_for_local(v, &disamb)
|
|
78
|
+
offset = period.utc_total_offset
|
|
79
|
+
Time.at(v.to_i - offset, :in => input_timezone)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Convert the given input Time to the given output timezone,
|
|
83
|
+
# which should be a TZInfo::Timezone instance.
|
|
84
|
+
def convert_output_time_other(v, output_timezone)
|
|
85
|
+
Time.at(v.to_i, :in => output_timezone)
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
# :nodoc:
|
|
89
|
+
# :nocov:
|
|
90
|
+
def convert_input_time_other(v, input_timezone)
|
|
91
|
+
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
92
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
|
96
|
+
def convert_output_time_other(v, output_timezone)
|
|
97
|
+
v = output_timezone.utc_to_local(v.getutc)
|
|
98
|
+
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
99
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + local_offset
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
def convert_output_time_other(v, output_timezone)
|
|
103
|
+
v = output_timezone.utc_to_local(v.getutc)
|
|
104
|
+
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
|
105
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
66
110
|
# Handle both TZInfo 1 and TZInfo 2
|
|
67
111
|
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
|
68
|
-
# :nodoc:
|
|
69
112
|
def convert_input_datetime_other(v, input_timezone)
|
|
70
113
|
local_offset = Rational(input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset, 86400)
|
|
71
114
|
(v - local_offset).new_offset(local_offset)
|
|
@@ -78,6 +121,7 @@ module Sequel
|
|
|
78
121
|
DateTime.jd(v.jd, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
|
|
79
122
|
end
|
|
80
123
|
# :nodoc:
|
|
124
|
+
# :nocov:
|
|
81
125
|
else
|
|
82
126
|
# Assume the given DateTime has a correct time but a wrong timezone. It is
|
|
83
127
|
# currently in UTC timezone, but it should be converted to the input_timezone.
|
|
@@ -340,14 +340,18 @@ module Sequel
|
|
|
340
340
|
raise Sequel::Error, "invalid array, empty string" if eos?
|
|
341
341
|
raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
|
|
342
342
|
|
|
343
|
+
# :nocov:
|
|
343
344
|
while !eos?
|
|
345
|
+
# :nocov:
|
|
344
346
|
char = scan(/[{}",]|[^{}",]+/)
|
|
345
347
|
if char == ','
|
|
346
348
|
# Comma outside quoted string indicates end of current entry
|
|
347
349
|
new_entry
|
|
348
350
|
elsif char == '"'
|
|
349
351
|
raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
|
|
352
|
+
# :nocov:
|
|
350
353
|
while true
|
|
354
|
+
# :nocov:
|
|
351
355
|
char = scan(/["\\]|[^"\\]+/)
|
|
352
356
|
if char == '\\'
|
|
353
357
|
@recorded << getch
|