sequel 5.97.0 → 5.98.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/lib/sequel/adapters/amalgalite.rb +16 -1
- data/lib/sequel/adapters/postgres.rb +1 -0
- data/lib/sequel/adapters/sqlite.rb +15 -1
- data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
- data/lib/sequel/model/associations.rb +5 -5
- data/lib/sequel/model/base.rb +18 -2
- data/lib/sequel/plugins/insert_returning_select.rb +10 -1
- data/lib/sequel/plugins/rcte_tree.rb +5 -5
- data/lib/sequel/plugins/split_values.rb +10 -0
- data/lib/sequel/plugins/table_select.rb +7 -0
- data/lib/sequel/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28641a0240d39f16a5b6f2d30986d44a8744c26e24de5e78b8e3823a409c48d2
|
|
4
|
+
data.tar.gz: 25435173509b5058e781f4b06b1ebf8005244e86864c5a72ac35bc739656a6b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11c7f62e3e296c43bef8af4de71ecaaf01e2e4d7e034910403991b91bc2075778dde0d16e899bc3ddacc1351e38da0f61f43476272e3b90c9a2aee5b09d82599
|
|
7
|
+
data.tar.gz: 173dfc5d3bbc19846a2d27b077d2241e6f64473f8a8f16ba052d9fd021d514036af1cf42cade4ea8974f2d920e126d4c23e824ab0e2d49a9062857ecd7e93342
|
|
@@ -62,11 +62,26 @@ module Sequel
|
|
|
62
62
|
|
|
63
63
|
# Mimic the file:// uri, by having 2 preceding slashes specify a relative
|
|
64
64
|
# path, and 3 preceding slashes specify an absolute path.
|
|
65
|
+
# Also support no preceding slashes to specify a relative path.
|
|
65
66
|
def self.uri_to_options(uri) # :nodoc:
|
|
66
|
-
|
|
67
|
+
database = if uri.host.nil?
|
|
68
|
+
case uri.path
|
|
69
|
+
when '/'
|
|
70
|
+
nil
|
|
71
|
+
when nil
|
|
72
|
+
uri.opaque
|
|
73
|
+
else
|
|
74
|
+
uri.path
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
"#{uri.host}#{uri.path}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
{ :database => database }
|
|
67
81
|
end
|
|
68
82
|
private_class_method :uri_to_options
|
|
69
83
|
|
|
84
|
+
|
|
70
85
|
# Connect to the database. Since SQLite is a file based database,
|
|
71
86
|
# the only options available are :database (to specify the database
|
|
72
87
|
# name), and :timeout, to specify how long to wait for the database to
|
|
@@ -89,8 +89,22 @@ module Sequel
|
|
|
89
89
|
|
|
90
90
|
# Mimic the file:// uri, by having 2 preceding slashes specify a relative
|
|
91
91
|
# path, and 3 preceding slashes specify an absolute path.
|
|
92
|
+
# Also support no preceding slashes to specify a relative path.
|
|
92
93
|
def self.uri_to_options(uri) # :nodoc:
|
|
93
|
-
|
|
94
|
+
database = if uri.host.nil?
|
|
95
|
+
case uri.path
|
|
96
|
+
when '/'
|
|
97
|
+
nil
|
|
98
|
+
when nil
|
|
99
|
+
uri.opaque
|
|
100
|
+
else
|
|
101
|
+
uri.path
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
"#{uri.host}#{uri.path}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
{ :database => database }
|
|
94
108
|
end
|
|
95
109
|
|
|
96
110
|
private_class_method :uri_to_options
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The pg_auto_parameterize_duplicate_query_detection extension builds on the
|
|
4
|
+
# pg_auto_parameterize extension, adding support for detecting duplicate
|
|
5
|
+
# queries inside a block that occur at the same location. This is designed
|
|
6
|
+
# mostly to catch duplicate query issues (e.g. N+1 queries) during testing.
|
|
7
|
+
#
|
|
8
|
+
# To detect duplicate queries inside a block of code, wrap the code with
|
|
9
|
+
# +detect_duplicate_queries+:
|
|
10
|
+
#
|
|
11
|
+
# DB.detect_duplicate_queries{your_code}
|
|
12
|
+
#
|
|
13
|
+
# With this approach, if the test runs code where the same query is executed
|
|
14
|
+
# more than once with the same call stack, a
|
|
15
|
+
# Sequel::Postgres::AutoParameterizeDuplicateQueryDetection::DuplicateQueries
|
|
16
|
+
# exception will be raised.
|
|
17
|
+
#
|
|
18
|
+
# You can pass the +:warn+ option to +detect_duplicate_queries+ to warn
|
|
19
|
+
# instead of raising. Note that if the block passed to +detect_duplicate_queries+
|
|
20
|
+
# raises, this extension will warn, and raise the original exception.
|
|
21
|
+
#
|
|
22
|
+
# For more control, you can pass the +:handler+ option to
|
|
23
|
+
# +detect_duplicate_queries+. If the +:handler+ option is provided, this
|
|
24
|
+
# extension will call the +:handler+ option with the hash of duplicate
|
|
25
|
+
# query information, and will not raise or warn. This can be useful in
|
|
26
|
+
# production environments, to record duplicate queries for later analysis.
|
|
27
|
+
#
|
|
28
|
+
# For accuracy, the entire call stack is always used as part of the hash key
|
|
29
|
+
# to determine whether a query is duplicate. However, you can filter the
|
|
30
|
+
# displayed backtrace by using the +:backtrace_filter+ option.
|
|
31
|
+
#
|
|
32
|
+
# +detect_duplicate_queries+ is concurrency aware, it uses the same approach
|
|
33
|
+
# that Sequel's default connection pools use. So if you are running multiple
|
|
34
|
+
# threads, +detect_duplicate_queries+ will only report duplicate queries for
|
|
35
|
+
# the current thread (or fiber if the fiber_concurrency extension is used).
|
|
36
|
+
#
|
|
37
|
+
# When testing applications, it's probably best to use this to wrap the
|
|
38
|
+
# application being tested. For example, testing with rack-test, if your app
|
|
39
|
+
# is +App+, you would want to wrap it:
|
|
40
|
+
#
|
|
41
|
+
# include Rack::Test::Methods
|
|
42
|
+
#
|
|
43
|
+
# WrappedApp = lambda do |env|
|
|
44
|
+
# DB.detect_duplicate_queries{App.call(env)}
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# def app
|
|
48
|
+
# WrappedApp
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# It is possible to use this to wrap each separate spec using an around hook,
|
|
52
|
+
# but that can result in false positives when using libraries that have
|
|
53
|
+
# implicit retrying (such as Capybara), as you can have the same call stack
|
|
54
|
+
# for multiple requests.
|
|
55
|
+
#
|
|
56
|
+
# Related module: Sequel::Postgres::AutoParameterizeDuplicateQueryDetection
|
|
57
|
+
|
|
58
|
+
module Sequel
|
|
59
|
+
module Postgres
|
|
60
|
+
# Enable detecting duplicate queries inside a block
|
|
61
|
+
module AutoParameterizeDuplicateQueryDetection
|
|
62
|
+
def self.extended(db)
|
|
63
|
+
db.instance_exec do
|
|
64
|
+
@duplicate_query_detection_contexts = {}
|
|
65
|
+
@duplicate_query_detection_mutex = Mutex.new
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Exception class raised when duplicate queries are detected.
|
|
70
|
+
class DuplicateQueries < Sequel::Error
|
|
71
|
+
# A hash of queries that were duplicate. Keys are arrays
|
|
72
|
+
# with 2 entries, the first being the query SQL, and the
|
|
73
|
+
# second being the related call stack (backtrace).
|
|
74
|
+
# The values are the number of query executions.
|
|
75
|
+
attr_reader :queries
|
|
76
|
+
|
|
77
|
+
def initialize(message, queries)
|
|
78
|
+
@queries = queries
|
|
79
|
+
super(message)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Record each query executed so duplicates can be detected,
|
|
84
|
+
# if queries are being recorded.
|
|
85
|
+
def execute(sql, opts=OPTS, &block)
|
|
86
|
+
record, queries = duplicate_query_recorder_state
|
|
87
|
+
|
|
88
|
+
if record
|
|
89
|
+
queries[[sql.is_a?(Symbol) ? sql : sql.to_s, caller].freeze] += 1
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
super
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Ignore (do not record) queries inside given block. This can
|
|
96
|
+
# be useful in situations where you want to run your entire test suite
|
|
97
|
+
# with duplicate query detection, but you have duplicate queries in
|
|
98
|
+
# some parts of your application where it is not trivial to use a
|
|
99
|
+
# different approach. You can mark those specific sections with
|
|
100
|
+
# +ignore_duplicate_queries+, and still get duplicate query detection
|
|
101
|
+
# for the rest of the application.
|
|
102
|
+
def ignore_duplicate_queries(&block)
|
|
103
|
+
if state = duplicate_query_recorder_state
|
|
104
|
+
change_duplicate_query_recorder_state(state, false, &block)
|
|
105
|
+
else
|
|
106
|
+
# If we are not inside a detect_duplicate_queries block, there is
|
|
107
|
+
# no need to do anything, since we are not recording queries.
|
|
108
|
+
yield
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Run the duplicate query detector during the block.
|
|
113
|
+
# Options:
|
|
114
|
+
#
|
|
115
|
+
# :backtrace_filter :: Regexp used to filter the displayed backtrace.
|
|
116
|
+
# :handler :: If present, called with hash of duplicate query information,
|
|
117
|
+
# instead of raising or warning.
|
|
118
|
+
# :warn :: Always warn instead of raising for duplicate queries.
|
|
119
|
+
#
|
|
120
|
+
# Note that if you nest calls to this method, only the top
|
|
121
|
+
# level call will respect the passed options.
|
|
122
|
+
def detect_duplicate_queries(opts=OPTS, &block)
|
|
123
|
+
current = Sequel.current
|
|
124
|
+
if state = duplicate_query_recorder_state(current)
|
|
125
|
+
return change_duplicate_query_recorder_state(state, true, &block)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@duplicate_query_detection_mutex.synchronize do
|
|
129
|
+
@duplicate_query_detection_contexts[current] = [true, Hash.new(0)]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
begin
|
|
133
|
+
yield
|
|
134
|
+
rescue Exception => e
|
|
135
|
+
raise
|
|
136
|
+
ensure
|
|
137
|
+
_, queries = @duplicate_query_detection_mutex.synchronize do
|
|
138
|
+
@duplicate_query_detection_contexts.delete(current)
|
|
139
|
+
end
|
|
140
|
+
queries.delete_if{|_,v| v < 2}
|
|
141
|
+
|
|
142
|
+
unless queries.empty?
|
|
143
|
+
if handler = opts[:handler]
|
|
144
|
+
handler.call(queries)
|
|
145
|
+
else
|
|
146
|
+
backtrace_filter = opts[:backtrace_filter]
|
|
147
|
+
backtrace_filter_note = backtrace_filter ? " (filtered)" : ""
|
|
148
|
+
query_info = queries.map do |k,v|
|
|
149
|
+
backtrace = k[1]
|
|
150
|
+
backtrace = backtrace.grep(backtrace_filter) if backtrace_filter
|
|
151
|
+
"times:#{v}\nsql:#{k[0]}\nbacktrace#{backtrace_filter_note}:\n#{backtrace.join("\n")}\n"
|
|
152
|
+
end
|
|
153
|
+
message = "duplicate queries detected:\n\n#{query_info.join("\n")}"
|
|
154
|
+
|
|
155
|
+
if e || opts[:warn]
|
|
156
|
+
warn(message)
|
|
157
|
+
else
|
|
158
|
+
raise DuplicateQueries.new(message, queries)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
# Get the query record state for the given context.
|
|
168
|
+
def duplicate_query_recorder_state(current=Sequel.current)
|
|
169
|
+
@duplicate_query_detection_mutex.synchronize{@duplicate_query_detection_contexts[current]}
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Temporarily change whether to record queries for the block, resetting the
|
|
173
|
+
# previous setting after the block exits.
|
|
174
|
+
def change_duplicate_query_recorder_state(state, setting)
|
|
175
|
+
prev = state[0]
|
|
176
|
+
state[0] = setting
|
|
177
|
+
|
|
178
|
+
begin
|
|
179
|
+
yield
|
|
180
|
+
ensure
|
|
181
|
+
state[0] = prev
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
Database.register_extension(:pg_auto_parameterize_duplicate_query_detection) do |db|
|
|
188
|
+
db.extension(:pg_auto_parameterize)
|
|
189
|
+
db.extend(Postgres::AutoParameterizeDuplicateQueryDetection)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -302,7 +302,7 @@ module Sequel
|
|
|
302
302
|
|
|
303
303
|
if strategy == :window_function
|
|
304
304
|
delete_rn = ds.row_number_column
|
|
305
|
-
objects.each{|obj| obj.
|
|
305
|
+
objects.each{|obj| obj.remove_key!(delete_rn)}
|
|
306
306
|
end
|
|
307
307
|
elsif strategy == :union
|
|
308
308
|
objects = []
|
|
@@ -1291,11 +1291,11 @@ module Sequel
|
|
|
1291
1291
|
name = self[:name]
|
|
1292
1292
|
|
|
1293
1293
|
self[:model].eager_load_results(self, eo) do |assoc_record|
|
|
1294
|
-
assoc_record.
|
|
1294
|
+
assoc_record.remove_key!(delete_rn) if delete_rn
|
|
1295
1295
|
hash_key = if uses_lcks
|
|
1296
|
-
left_key_alias.map{|k| assoc_record.
|
|
1296
|
+
left_key_alias.map{|k| assoc_record.remove_key!(k)}
|
|
1297
1297
|
else
|
|
1298
|
-
assoc_record.
|
|
1298
|
+
assoc_record.remove_key!(left_key_alias)
|
|
1299
1299
|
end
|
|
1300
1300
|
|
|
1301
1301
|
objects = h[hash_key]
|
|
@@ -2387,7 +2387,7 @@ module Sequel
|
|
|
2387
2387
|
delete_rn = opts.delete_row_number_column
|
|
2388
2388
|
|
|
2389
2389
|
eager_load_results(opts, eo) do |assoc_record|
|
|
2390
|
-
assoc_record.
|
|
2390
|
+
assoc_record.remove_key!(delete_rn) if delete_rn
|
|
2391
2391
|
hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
|
|
2392
2392
|
objects = h[hash_key]
|
|
2393
2393
|
if assign_singular
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Sequel
|
|
4
4
|
class Model
|
|
5
|
+
# SEQUEL6: Remove Enumerable here, and send all Enumerable methods to dataset
|
|
6
|
+
# by default, with a plugin for the current behavior.
|
|
5
7
|
extend Enumerable
|
|
6
8
|
extend Inflections
|
|
7
9
|
|
|
8
10
|
# Class methods for Sequel::Model that implement basic model functionality.
|
|
9
11
|
#
|
|
10
12
|
# * All of the following methods have class methods created that send the method
|
|
11
|
-
# to the model's dataset: all, as_hash, avg, count, cross_join, distinct, each,
|
|
13
|
+
# to the model's dataset: all, any?, as_hash, avg, count, cross_join, distinct, each,
|
|
12
14
|
# each_server, empty?, except, exclude, exclude_having, fetch_rows,
|
|
13
15
|
# filter, first, first!, for_update, from, from_self, full_join, full_outer_join,
|
|
14
16
|
# get, graph, grep, group, group_and_count, group_append, group_by, having, import,
|
|
@@ -695,7 +697,7 @@ module Sequel
|
|
|
695
697
|
end
|
|
696
698
|
|
|
697
699
|
# Add model methods that call dataset methods
|
|
698
|
-
Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
|
|
700
|
+
Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:any?, :each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
|
|
699
701
|
|
|
700
702
|
private
|
|
701
703
|
|
|
@@ -1499,6 +1501,20 @@ END
|
|
|
1499
1501
|
refresh
|
|
1500
1502
|
end
|
|
1501
1503
|
|
|
1504
|
+
# Remove a key from the instances values, and return the value
|
|
1505
|
+
# of the key.
|
|
1506
|
+
#
|
|
1507
|
+
# a = Album[1]
|
|
1508
|
+
# a.values
|
|
1509
|
+
# # => {id: 1, artist_id: 2}
|
|
1510
|
+
# a.remove_key!(:artist_id)
|
|
1511
|
+
# # => 2
|
|
1512
|
+
# a.values
|
|
1513
|
+
# # => {id: 1}
|
|
1514
|
+
def remove_key!(key)
|
|
1515
|
+
@values.delete(key)
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1502
1518
|
# Creates or updates the record, after making sure the record
|
|
1503
1519
|
# is valid and before hooks execute successfully. Fails if:
|
|
1504
1520
|
#
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Sequel
|
|
4
4
|
module Plugins
|
|
5
|
-
# If the model's dataset selects explicit columns and the
|
|
5
|
+
# If the model's dataset selects explicit columns (or table.*) and the
|
|
6
6
|
# database supports it, the insert_returning_select plugin will
|
|
7
7
|
# automatically set the RETURNING clause on the dataset used to
|
|
8
8
|
# insert rows to the columns selected, which allows the default model
|
|
@@ -45,6 +45,9 @@ module Sequel
|
|
|
45
45
|
ret
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
RETURN_ALL = [Sequel::Dataset::WILDCARD].freeze
|
|
49
|
+
private_constant :RETURN_ALL
|
|
50
|
+
|
|
48
51
|
# Determine the columns to use for the returning clause, or return nil
|
|
49
52
|
# if they can't be determined and a returning clause should not be
|
|
50
53
|
# added automatically.
|
|
@@ -52,6 +55,12 @@ module Sequel
|
|
|
52
55
|
return unless ds.supports_returning?(:insert)
|
|
53
56
|
return unless values = ds.opts[:select]
|
|
54
57
|
|
|
58
|
+
# SELECT table.* -> RETURNING *
|
|
59
|
+
if values.length == 1 && values[0].is_a?(Sequel::SQL::ColumnAll)
|
|
60
|
+
return RETURN_ALL
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# SELECT column1, table.column2, ... -> RETURNING column1, column2, ...
|
|
55
64
|
values = values.map{|v| ds.unqualified_column_for(v)}
|
|
56
65
|
if values.all?
|
|
57
66
|
values
|
|
@@ -111,7 +111,7 @@ module Sequel
|
|
|
111
111
|
ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
|
112
112
|
descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
|
|
113
113
|
recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
|
|
114
|
-
extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.
|
|
114
|
+
extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.remove_key!(ka_)]}}
|
|
115
115
|
else
|
|
116
116
|
key_present = key_conv = lambda{|m| m[key]}
|
|
117
117
|
prkey_conv = lambda{|m| m[prkey]}
|
|
@@ -119,7 +119,7 @@ module Sequel
|
|
|
119
119
|
ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
|
|
120
120
|
descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
|
|
121
121
|
recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
|
|
122
|
-
extract_key_alias = lambda{|m| bd_conv[m.
|
|
122
|
+
extract_key_alias = lambda{|m| bd_conv[m.remove_key!(ka)]}
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
parent = opts.merge(opts.fetch(:parent, OPTS)).fetch(:name, :parent)
|
|
@@ -200,7 +200,7 @@ module Sequel
|
|
|
200
200
|
model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
|
|
201
201
|
opk = prkey_conv[obj]
|
|
202
202
|
if idm_obj = parent_map[opk]
|
|
203
|
-
key_aliases.each{|ka_| idm_obj
|
|
203
|
+
key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
|
|
204
204
|
obj = idm_obj
|
|
205
205
|
else
|
|
206
206
|
obj.associations[parent] = nil
|
|
@@ -307,12 +307,12 @@ module Sequel
|
|
|
307
307
|
ds = ds.select_append(ka) unless ds.opts[:select] == nil
|
|
308
308
|
model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
|
|
309
309
|
if level
|
|
310
|
-
no_cache = no_cache_level == obj.
|
|
310
|
+
no_cache = no_cache_level == obj.remove_key!(la)
|
|
311
311
|
end
|
|
312
312
|
|
|
313
313
|
opk = prkey_conv[obj]
|
|
314
314
|
if idm_obj = parent_map[opk]
|
|
315
|
-
key_aliases.each{|ka_| idm_obj
|
|
315
|
+
key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
|
|
316
316
|
obj = idm_obj
|
|
317
317
|
else
|
|
318
318
|
obj.associations[childrena] = [] unless no_cache
|
|
@@ -54,6 +54,16 @@ module Sequel
|
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
# Remove the key from noncolumn values if it is present there. If it is not
|
|
58
|
+
# present there, then use the default behavior of removing it from values.
|
|
59
|
+
def remove_key!(key)
|
|
60
|
+
if @noncolumn_values && @noncolumn_values.key?(key)
|
|
61
|
+
@noncolumn_values.delete(key)
|
|
62
|
+
else
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
57
67
|
# Check all entries in the values hash. If any of the keys are not columns,
|
|
58
68
|
# move the entry into the noncolumn_values hash.
|
|
59
69
|
def split_noncolumn_values
|
|
@@ -9,6 +9,13 @@ module Sequel
|
|
|
9
9
|
# in the result sets (and possibly overwrite columns in the
|
|
10
10
|
# current model with the same name).
|
|
11
11
|
#
|
|
12
|
+
# Note that by default on databases that supporting RETURNING,
|
|
13
|
+
# using this plugin will cause instance creations
|
|
14
|
+
# to use two queries (insert and refresh) instead of a single
|
|
15
|
+
# query using RETURNING. You can use the insert_returning_select
|
|
16
|
+
# plugin to automatically use RETURNING for instance creations
|
|
17
|
+
# for models using this plugin.
|
|
18
|
+
#
|
|
12
19
|
# Usage:
|
|
13
20
|
#
|
|
14
21
|
# # Make all model subclasses select table.*
|
data/lib/sequel/version.rb
CHANGED
|
@@ -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 =
|
|
9
|
+
MINOR = 98
|
|
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,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.98.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
@@ -250,6 +250,7 @@ files:
|
|
|
250
250
|
- lib/sequel/extensions/pg_array.rb
|
|
251
251
|
- lib/sequel/extensions/pg_array_ops.rb
|
|
252
252
|
- lib/sequel/extensions/pg_auto_parameterize.rb
|
|
253
|
+
- lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb
|
|
253
254
|
- lib/sequel/extensions/pg_auto_parameterize_in_array.rb
|
|
254
255
|
- lib/sequel/extensions/pg_enum.rb
|
|
255
256
|
- lib/sequel/extensions/pg_extended_date_support.rb
|