sequel 5.101.0 → 5.104.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/MIT-LICENSE +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +2 -0
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +3 -3
- data/lib/sequel/adapters/shared/mysql.rb +5 -4
- data/lib/sequel/adapters/shared/postgres.rb +16 -16
- data/lib/sequel/adapters/shared/sqlite.rb +3 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +36 -11
- data/lib/sequel/connection_pool/timed_queue.rb +27 -9
- data/lib/sequel/connection_pool.rb +6 -0
- data/lib/sequel/database/schema_generator.rb +36 -5
- data/lib/sequel/database/schema_methods.rb +1 -1
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
- data/lib/sequel/dataset/prepared_statements.rb +7 -4
- data/lib/sequel/dataset/query.rb +6 -3
- data/lib/sequel/dataset/sql.rb +6 -1
- data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
- data/lib/sequel/extensions/connection_expiration.rb +1 -1
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/dataset_run.rb +2 -2
- data/lib/sequel/extensions/date_arithmetic.rb +6 -6
- data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
- data/lib/sequel/extensions/migration.rb +14 -17
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/model/associations.rb +180 -6
- data/lib/sequel/plugins/dataset_associations.rb +20 -1
- data/lib/sequel/plugins/dirty.rb +5 -2
- data/lib/sequel/plugins/many_through_many.rb +21 -0
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +10 -1
- data/lib/sequel/version.rb +1 -1
- metadata +3 -1
|
@@ -54,7 +54,7 @@ module Sequel
|
|
|
54
54
|
|
|
55
55
|
# Set the bind arguments based on the hash and call super.
|
|
56
56
|
def call(bind_vars=OPTS, &block)
|
|
57
|
-
sql = prepared_sql
|
|
57
|
+
sql = prepared_sql.freeze
|
|
58
58
|
prepared_args.freeze
|
|
59
59
|
ps = bind(bind_vars)
|
|
60
60
|
ps.clone(:bind_arguments=>ps.map_to_prepared_args(ps.opts[:bind_vars]), :sql=>sql, :prepared_sql=>sql).run(&block)
|
|
@@ -223,7 +223,7 @@ module Sequel
|
|
|
223
223
|
# with the prepared SQL, to ensure the prepared_sql_type is respected.
|
|
224
224
|
def force_prepared_sql
|
|
225
225
|
if prepared_sql_type != prepared_type
|
|
226
|
-
with_sql(prepared_sql)
|
|
226
|
+
with_sql(prepared_sql.freeze)
|
|
227
227
|
else
|
|
228
228
|
self
|
|
229
229
|
end
|
|
@@ -287,7 +287,7 @@ module Sequel
|
|
|
287
287
|
|
|
288
288
|
def run(&block)
|
|
289
289
|
if @opts[:prepared_sql_frags]
|
|
290
|
-
sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
|
|
290
|
+
sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
|
|
291
291
|
clone(:prepared_sql_frags=>nil, :sql=>sql, :prepared_sql=>sql).run(&block)
|
|
292
292
|
else
|
|
293
293
|
super
|
|
@@ -320,6 +320,9 @@ module Sequel
|
|
|
320
320
|
end
|
|
321
321
|
|
|
322
322
|
prepared_args.freeze
|
|
323
|
+
frags.freeze
|
|
324
|
+
frags.each(&:freeze)
|
|
325
|
+
prepared_sql.freeze
|
|
323
326
|
clone(:prepared_sql_frags=>frags, :prepared_sql=>prepared_sql, :sql=>prepared_sql)
|
|
324
327
|
end
|
|
325
328
|
|
|
@@ -409,7 +412,7 @@ module Sequel
|
|
|
409
412
|
ps = ps.with_extend(EmulatePreparedStatementMethods)
|
|
410
413
|
ps.send(:emulated_prepared_statement, type, name, values)
|
|
411
414
|
else
|
|
412
|
-
sql = ps.prepared_sql
|
|
415
|
+
sql = ps.prepared_sql.freeze
|
|
413
416
|
ps.prepared_args.freeze
|
|
414
417
|
ps.clone(:prepared_sql=>sql, :sql=>sql)
|
|
415
418
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -1301,7 +1301,7 @@ module Sequel
|
|
|
1301
1301
|
# * truncate (if a TRUNCATE statement, with no arguments)
|
|
1302
1302
|
def with_sql(sql, *args)
|
|
1303
1303
|
if sql.is_a?(Symbol)
|
|
1304
|
-
sql = public_send(sql, *args)
|
|
1304
|
+
sql = public_send(sql, *args).freeze
|
|
1305
1305
|
else
|
|
1306
1306
|
sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
|
|
1307
1307
|
end
|
|
@@ -1474,7 +1474,10 @@ module Sequel
|
|
|
1474
1474
|
def default_join_table_qualification
|
|
1475
1475
|
:symbol
|
|
1476
1476
|
end
|
|
1477
|
-
|
|
1477
|
+
|
|
1478
|
+
PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
|
|
1479
|
+
private_constant :PAREN_WRAPPER
|
|
1480
|
+
|
|
1478
1481
|
# SQL expression object based on the expr type. See +where+.
|
|
1479
1482
|
def filter_expr(expr = nil, &block)
|
|
1480
1483
|
expr = nil if expr == EMPTY_ARRAY
|
|
@@ -1495,7 +1498,7 @@ module Sequel
|
|
|
1495
1498
|
raise Error, "Invalid filter expression: #{expr.inspect}"
|
|
1496
1499
|
end
|
|
1497
1500
|
when LiteralString
|
|
1498
|
-
|
|
1501
|
+
SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
|
|
1499
1502
|
when Numeric, SQL::NumericExpression, SQL::StringExpression, Proc, String, Set
|
|
1500
1503
|
raise Error, "Invalid filter expression: #{expr.inspect}"
|
|
1501
1504
|
when TrueClass, FalseClass
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Sequel
|
|
|
53
53
|
when String
|
|
54
54
|
case v
|
|
55
55
|
when LiteralString
|
|
56
|
-
sql
|
|
56
|
+
literal_literal_string_append(sql, v)
|
|
57
57
|
when SQL::Blob
|
|
58
58
|
literal_blob_append(sql, v)
|
|
59
59
|
else
|
|
@@ -1424,6 +1424,11 @@ module Sequel
|
|
|
1424
1424
|
v.to_s
|
|
1425
1425
|
end
|
|
1426
1426
|
|
|
1427
|
+
# Append string to SQL string.
|
|
1428
|
+
def literal_literal_string_append(sql, v)
|
|
1429
|
+
sql << v
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1427
1432
|
# SQL fragment for nil
|
|
1428
1433
|
def literal_nil
|
|
1429
1434
|
"NULL"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The connection_checkout_event_callback extension modifies a database's
|
|
4
|
+
# connection pool to allow for a checkout event callback. This callback is
|
|
5
|
+
# called with the following arguments:
|
|
6
|
+
#
|
|
7
|
+
# :immediately_available :: Connection immediately available and returned
|
|
8
|
+
# :not_immediately_available :: Connection not immediately available
|
|
9
|
+
# :new_connection :: New connection created and returned
|
|
10
|
+
# Float :: Number of seconds waiting to acquire a connection
|
|
11
|
+
#
|
|
12
|
+
# This is a low-level extension that allows for building telemetry
|
|
13
|
+
# information. It doesn't implement any telemetry reporting itself. The
|
|
14
|
+
# main reason for recording this information is to use it to determine the
|
|
15
|
+
# appropriate size for the connection pool. Having too large a connection
|
|
16
|
+
# pool can waste resources, while having too small a connection pool can
|
|
17
|
+
# result in substantial time to check out a connection. In general, you
|
|
18
|
+
# want to use as small a pool as possible while keeping the time to
|
|
19
|
+
# checkout a connection low.
|
|
20
|
+
#
|
|
21
|
+
# To use the connection checkout event callback, you must first load the
|
|
22
|
+
# extension:
|
|
23
|
+
#
|
|
24
|
+
# DB.extension(:connection_checkout_event_callback)
|
|
25
|
+
#
|
|
26
|
+
# By default, an empty proc is used as the callback so that loading the
|
|
27
|
+
# support doesn't break anything. If you are using the extension, you
|
|
28
|
+
# should set the callback at some point during application startup:
|
|
29
|
+
#
|
|
30
|
+
# DB.pool.on_checkout_event = proc do |event|
|
|
31
|
+
# # ...
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# When using the sharded connection pool, the callback is also
|
|
35
|
+
# passed a second argument, the requested server shard (generally a
|
|
36
|
+
# symbol), allowing for collection of per-shard telemetry:
|
|
37
|
+
#
|
|
38
|
+
# DB.pool.on_checkout_event = proc do |event, server|
|
|
39
|
+
# # ...
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# Note that the callback may be called currently by multiple threads.
|
|
43
|
+
# You should use some form of concurrency control inside the callback,
|
|
44
|
+
# such as a mutex or queue.
|
|
45
|
+
#
|
|
46
|
+
# Below is a brief example of usage to determine the percentage of
|
|
47
|
+
# connection requests where a connection was immediately available:
|
|
48
|
+
#
|
|
49
|
+
# mutex = Mutex.new
|
|
50
|
+
# total = immediates = 0
|
|
51
|
+
#
|
|
52
|
+
# DB.pool.on_checkout_event = proc do |event|
|
|
53
|
+
# case event
|
|
54
|
+
# when :immediately_available
|
|
55
|
+
# mutex.synchronize do
|
|
56
|
+
# total += 1
|
|
57
|
+
# immediates += 1
|
|
58
|
+
# end
|
|
59
|
+
# when :not_immediately_available
|
|
60
|
+
# mutex.synchronize do
|
|
61
|
+
# total += 1
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# immediate_percentage = lambda do
|
|
67
|
+
# mutex.synchronize do
|
|
68
|
+
# 100.0 * immediates / total
|
|
69
|
+
# end
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# Note that this extension only works with the timed_queue
|
|
73
|
+
# and sharded_timed_queue connection pools (the default
|
|
74
|
+
# connection pools when using Ruby 3.2+).
|
|
75
|
+
#
|
|
76
|
+
# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
|
|
77
|
+
# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
module Sequel
|
|
81
|
+
module ConnectionCheckoutEventCallbacks
|
|
82
|
+
module TimedQueue
|
|
83
|
+
# The callback that is called with connection checkout events.
|
|
84
|
+
attr_accessor :on_checkout_event
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def available
|
|
89
|
+
conn = super
|
|
90
|
+
@on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
|
|
91
|
+
conn
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def try_make_new
|
|
95
|
+
conn = super
|
|
96
|
+
@on_checkout_event.call(:new_connection) if conn
|
|
97
|
+
conn
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def wait_until_available
|
|
101
|
+
timer = Sequel.start_timer
|
|
102
|
+
conn = super
|
|
103
|
+
@on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
|
|
104
|
+
conn
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
module ShardedTimedQueue
|
|
109
|
+
# The callback that is called with connection checkout events.
|
|
110
|
+
attr_accessor :on_checkout_event
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def available(queue, server)
|
|
115
|
+
conn = super
|
|
116
|
+
@on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
|
|
117
|
+
conn
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def try_make_new(server)
|
|
121
|
+
conn = super
|
|
122
|
+
@on_checkout_event.call(:new_connection, server) if conn
|
|
123
|
+
conn
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def wait_until_available(queue, server)
|
|
127
|
+
timer = Sequel.start_timer
|
|
128
|
+
conn = super
|
|
129
|
+
@on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
|
|
130
|
+
conn
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
default_callback = proc{}
|
|
136
|
+
|
|
137
|
+
Database.register_extension(:connection_checkout_event_callback) do |db|
|
|
138
|
+
pool = db.pool
|
|
139
|
+
|
|
140
|
+
case pool.pool_type
|
|
141
|
+
when :timed_queue
|
|
142
|
+
db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
|
|
143
|
+
when :sharded_timed_queue
|
|
144
|
+
db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
|
|
145
|
+
else
|
|
146
|
+
raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
pool.on_checkout_event ||= default_callback
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -82,12 +82,12 @@ module Sequel
|
|
|
82
82
|
DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
|
|
83
83
|
DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
|
|
84
84
|
POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
|
|
85
|
-
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
|
|
86
|
-
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
|
|
85
|
+
MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
|
|
86
|
+
MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
|
|
87
87
|
H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
|
|
88
|
-
DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
|
|
88
|
+
DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
|
|
89
89
|
ACCESS_DURATION_UNITS = DURATION_UNITS.zip(%w'yyyy m d h n s'.map(&:freeze)).freeze
|
|
90
|
-
DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
|
|
90
|
+
DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
|
|
91
91
|
|
|
92
92
|
# Append the SQL fragment for the DateAdd expression to the SQL query.
|
|
93
93
|
def date_add_sql_append(sql, da)
|
|
@@ -107,7 +107,7 @@ module Sequel
|
|
|
107
107
|
placeholder = []
|
|
108
108
|
vals = []
|
|
109
109
|
each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
|
|
110
|
-
placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
|
|
110
|
+
placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
|
|
111
111
|
vals << value
|
|
112
112
|
end
|
|
113
113
|
interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
|
|
@@ -156,7 +156,7 @@ module Sequel
|
|
|
156
156
|
expr = Sequel.cast_string(expr) + ' 00:00:00'
|
|
157
157
|
end
|
|
158
158
|
each_valid_interval_unit(h, DERBY_DURATION_UNITS) do |value, sql_unit|
|
|
159
|
-
expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
|
|
159
|
+
expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
|
|
160
160
|
end
|
|
161
161
|
when :oracle
|
|
162
162
|
each_valid_interval_unit(h, MYSQL_DURATION_UNITS) do |value, sql_unit|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The lit_require_frozen extension disallows the use of unfrozen strings
|
|
4
|
+
# as literal strings in database and dataset methods. If you try to use an
|
|
5
|
+
# unfrozen string as a literal string for a dataset using this extension,
|
|
6
|
+
# an exception will be raised.
|
|
7
|
+
#
|
|
8
|
+
# While this works for all Ruby versions, it is designed for use on Ruby 3+
|
|
9
|
+
# where all files are using the frozen-string-literal magic comment. In this
|
|
10
|
+
# case, uninterpolated literal strings are frozen, but interpolated strings
|
|
11
|
+
# are not frozen. This allows you to catch potentially dangerous code:
|
|
12
|
+
#
|
|
13
|
+
# # Probably safe, no exception raised
|
|
14
|
+
# DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
|
|
15
|
+
#
|
|
16
|
+
# # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
|
|
17
|
+
# DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
|
|
18
|
+
#
|
|
19
|
+
# The assumption made is that a frozen string is unlikely to contain unsafe
|
|
20
|
+
# input, while an unfrozen string has potentially been interpolated and may
|
|
21
|
+
# contain unsafe input.
|
|
22
|
+
#
|
|
23
|
+
# This disallows the the following cases:
|
|
24
|
+
#
|
|
25
|
+
# * Sequel::LiteralString instances that are unfrozen and are not based on a
|
|
26
|
+
# frozen string
|
|
27
|
+
# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
|
|
28
|
+
# is not frozen
|
|
29
|
+
# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
|
|
30
|
+
#
|
|
31
|
+
# To use this extension, load it into the database:
|
|
32
|
+
#
|
|
33
|
+
# DB.extension :lit_require_frozen
|
|
34
|
+
#
|
|
35
|
+
# It can also be loaded into individual datasets:
|
|
36
|
+
#
|
|
37
|
+
# ds = DB[:t].extension(:lit_require_frozen)
|
|
38
|
+
#
|
|
39
|
+
# Assuming you have good test coverage, it is recommended to only load
|
|
40
|
+
# this extension when testing.
|
|
41
|
+
#
|
|
42
|
+
# Related module: Sequel::LitRequireFrozen
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
module Sequel
|
|
46
|
+
class LiteralString
|
|
47
|
+
# The string used when creating the literal string (first argument to
|
|
48
|
+
# Sequel::LiteralString.new). This may be nil if no string was provided,
|
|
49
|
+
# or if the litral string was created before this extension was required.
|
|
50
|
+
attr_reader :source
|
|
51
|
+
|
|
52
|
+
def initialize(*a)
|
|
53
|
+
@source = a.first
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
# :nocov:
|
|
57
|
+
ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
|
|
58
|
+
# :nocov:
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module LitRequireFrozen
|
|
62
|
+
# Error class raised for using unfrozen literal string.
|
|
63
|
+
class Error < Sequel::Error
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
module DatabaseMethods
|
|
67
|
+
def self.extended(db)
|
|
68
|
+
db.extend_datasets(DatasetMethods)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Check given SQL is frozen before running it.
|
|
72
|
+
def run(sql, opts=OPTS)
|
|
73
|
+
@default_dataset.with_sql(sql)
|
|
74
|
+
super
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module DatasetMethods
|
|
79
|
+
# Check given SQL is not an unfrozen string.
|
|
80
|
+
def with_sql(sql, *args)
|
|
81
|
+
_check_unfrozen_literal_string(sql)
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Check that placeholder string is frozen (or all entries
|
|
86
|
+
# in placeholder array are frozen).
|
|
87
|
+
def placeholder_literal_string_sql_append(sql, pls)
|
|
88
|
+
str = pls.str
|
|
89
|
+
|
|
90
|
+
if str.is_a?(Array)
|
|
91
|
+
str.each do |s|
|
|
92
|
+
_check_unfrozen_literal_string(s)
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
_check_unfrozen_literal_string(str)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
super
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
# Base method that other methods used to check for whether a string should be allowed
|
|
104
|
+
# as literal SQL. Allows non-strings as well as frozen strings.
|
|
105
|
+
def _check_unfrozen_literal_string(str)
|
|
106
|
+
return if !str.is_a?(String) || str.frozen?
|
|
107
|
+
|
|
108
|
+
if str.is_a?(LiteralString)
|
|
109
|
+
_check_unfrozen_literal_string(str.source)
|
|
110
|
+
else
|
|
111
|
+
raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Check literal strings appended to SQL.
|
|
116
|
+
def literal_literal_string_append(sql, v)
|
|
117
|
+
_check_unfrozen_literal_string(v)
|
|
118
|
+
super
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Check static SQL is not frozen.
|
|
122
|
+
def static_sql(sql)
|
|
123
|
+
_check_unfrozen_literal_string(sql)
|
|
124
|
+
super
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
|
|
130
|
+
Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
|
|
131
|
+
end
|
|
@@ -186,22 +186,19 @@ module Sequel
|
|
|
186
186
|
# and returns a new block that reverses the actions taken by
|
|
187
187
|
# the given block.
|
|
188
188
|
def reverse(&block)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Proc.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
send(*a, &pr)
|
|
203
|
-
end
|
|
204
|
-
end
|
|
189
|
+
instance_exec(&block)
|
|
190
|
+
rescue NoMethodError => e
|
|
191
|
+
Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
|
|
192
|
+
rescue => e
|
|
193
|
+
Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
|
|
194
|
+
else
|
|
195
|
+
actions = @actions.reverse
|
|
196
|
+
Proc.new do
|
|
197
|
+
actions.each do |a|
|
|
198
|
+
pr = a.last.is_a?(Proc) ? a.pop : nil
|
|
199
|
+
# Allow calling private methods as the reversing methods are private
|
|
200
|
+
send(*a, &pr)
|
|
201
|
+
end
|
|
205
202
|
end
|
|
206
203
|
end
|
|
207
204
|
|
|
@@ -270,7 +267,7 @@ module Sequel
|
|
|
270
267
|
end
|
|
271
268
|
|
|
272
269
|
def add_primary_key(*args)
|
|
273
|
-
|
|
270
|
+
super if args.first.is_a?(Array)
|
|
274
271
|
@actions << [:drop_column, args.first]
|
|
275
272
|
end
|
|
276
273
|
|