sequel 5.11.0 → 5.12.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 +32 -0
- data/doc/advanced_associations.rdoc +132 -14
- data/doc/postgresql.rdoc +14 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/lib/sequel/adapters/ado/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +5 -6
- data/lib/sequel/adapters/postgres.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +5 -5
- data/lib/sequel/adapters/sqlite.rb +0 -5
- data/lib/sequel/adapters/tinytds.rb +0 -5
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
- data/lib/sequel/core.rb +6 -1
- data/lib/sequel/dataset/graph.rb +25 -9
- data/lib/sequel/dataset/placeholder_literalizer.rb +47 -17
- data/lib/sequel/dataset/prepared_statements.rb +86 -18
- data/lib/sequel/dataset/sql.rb +5 -1
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
- data/lib/sequel/model/associations.rb +56 -23
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/static_cache.rb +9 -8
- data/lib/sequel/plugins/tactical_eager_loading.rb +63 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/oracle_spec.rb +44 -0
- data/spec/adapters/postgres_spec.rb +39 -0
- data/spec/core/dataset_spec.rb +23 -9
- data/spec/core/object_graph_spec.rb +314 -284
- data/spec/extensions/caller_logging_spec.rb +52 -0
- data/spec/extensions/eager_graph_eager_spec.rb +100 -0
- data/spec/extensions/finder_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +7 -12
- data/spec/extensions/static_cache_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +262 -1
- data/spec/integration/associations_test.rb +72 -0
- data/spec/integration/dataset_test.rb +3 -3
- data/spec/model/eager_loading_spec.rb +90 -0
- metadata +8 -2
@@ -441,7 +441,7 @@ module Sequel
|
|
441
441
|
/cannot be null/ => NotNullConstraintViolation,
|
442
442
|
/Deadlock found when trying to get lock; try restarting transaction/ => SerializationFailure,
|
443
443
|
/CONSTRAINT .+ failed for/ => CheckConstraintViolation,
|
444
|
-
/\
|
444
|
+
/\A(Statement aborted because lock\(s\) could not be acquired immediately and NOWAIT is set\.|Lock wait timeout exceeded; try restarting transaction)/ => DatabaseLockTimeout,
|
445
445
|
}.freeze
|
446
446
|
def database_error_regexps
|
447
447
|
DATABASE_ERROR_REGEXPS
|
@@ -804,9 +804,9 @@ module Sequel
|
|
804
804
|
true
|
805
805
|
end
|
806
806
|
|
807
|
-
#
|
807
|
+
# MariaDB 10.3+ supports INTERSECT or EXCEPT
|
808
808
|
def supports_intersect_except?
|
809
|
-
|
809
|
+
db.mariadb? && db.server_version >= 100300
|
810
810
|
end
|
811
811
|
|
812
812
|
# MySQL does not support limits in correlated subqueries (or any subqueries that use IN).
|
@@ -819,9 +819,9 @@ module Sequel
|
|
819
819
|
true
|
820
820
|
end
|
821
821
|
|
822
|
-
# MySQL 8+
|
822
|
+
# MySQL 8+ and MariaDB 10.3+ support NOWAIT.
|
823
823
|
def supports_nowait?
|
824
|
-
|
824
|
+
db.server_version >= (db.mariadb? ? 100300 : 80000)
|
825
825
|
end
|
826
826
|
|
827
827
|
# MySQL's DISTINCT ON emulation using GROUP BY does not respect the
|
@@ -306,11 +306,6 @@ module Sequel
|
|
306
306
|
def prepared_arg(k)
|
307
307
|
LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
|
308
308
|
end
|
309
|
-
|
310
|
-
# Always assume a prepared argument.
|
311
|
-
def prepared_arg?(k)
|
312
|
-
true
|
313
|
-
end
|
314
309
|
end
|
315
310
|
|
316
311
|
BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
|
@@ -194,11 +194,6 @@ module Sequel
|
|
194
194
|
def prepared_arg(k)
|
195
195
|
LiteralString.new("@#{k.to_s.gsub('.', '__')}")
|
196
196
|
end
|
197
|
-
|
198
|
-
# Always assume a prepared argument.
|
199
|
-
def prepared_arg?(k)
|
200
|
-
true
|
201
|
-
end
|
202
197
|
end
|
203
198
|
|
204
199
|
PreparedStatementMethods = prepared_statements_module("sql = prepared_sql; opts = Hash[opts]; opts[:arguments] = bind_arguments", ArgumentMapper)
|
@@ -32,14 +32,11 @@ module Sequel
|
|
32
32
|
row_count = @opts[:offset_total_count] || ds.clone(:append_sql=>String.new, :placeholder_literal_null=>true).count
|
33
33
|
dsa1 = dataset_alias(1)
|
34
34
|
|
35
|
-
if o.is_a?(Symbol) && @opts[:bind_vars] &&
|
35
|
+
if o.is_a?(Symbol) && @opts[:bind_vars] && /\A\$(.*)\z/ =~ o
|
36
36
|
# Handle use of bound variable offsets. Unfortunately, prepared statement
|
37
37
|
# bound variable offsets cannot be handled, since the bound variable value
|
38
38
|
# isn't available until later.
|
39
|
-
|
40
|
-
if prepared_arg?(s)
|
41
|
-
o = prepared_arg(s)
|
42
|
-
end
|
39
|
+
o = prepared_arg($1.to_sym)
|
43
40
|
end
|
44
41
|
|
45
42
|
reverse_offset = row_count - o
|
data/lib/sequel/core.rb
CHANGED
@@ -58,6 +58,11 @@ module Sequel
|
|
58
58
|
#
|
59
59
|
# Sequel.single_threaded = true
|
60
60
|
attr_accessor :single_threaded
|
61
|
+
|
62
|
+
# Alias of original require method, as Sequel.require is does a relative
|
63
|
+
# require for backwards compatibility.
|
64
|
+
alias orig_require require
|
65
|
+
private :orig_require
|
61
66
|
end
|
62
67
|
|
63
68
|
# Returns true if the passed object could be a specifier of conditions, false otherwise.
|
@@ -142,7 +147,7 @@ module Sequel
|
|
142
147
|
# Sequel.extension(:blank)
|
143
148
|
# Sequel.extension(:core_extensions, :named_timezones)
|
144
149
|
def self.extension(*extensions)
|
145
|
-
extensions.each{|e|
|
150
|
+
extensions.each{|e| orig_require("sequel/extensions/#{e}")}
|
146
151
|
end
|
147
152
|
|
148
153
|
# The exception classed raised if there is an error parsing JSON.
|
data/lib/sequel/dataset/graph.rb
CHANGED
@@ -96,10 +96,30 @@ module Sequel
|
|
96
96
|
|
97
97
|
table_alias_qualifier = qualifier_from_alias_symbol(table_alias, table)
|
98
98
|
implicit_qualifier = options[:implicit_qualifier]
|
99
|
+
joined_dataset = joined_dataset?
|
99
100
|
ds = self
|
101
|
+
graph = opts[:graph]
|
102
|
+
|
103
|
+
if !graph && (select = @opts[:select]) && !select.empty?
|
104
|
+
select_columns = nil
|
105
|
+
|
106
|
+
unless !joined_dataset && select.length == 1 && (select[0].is_a?(SQL::ColumnAll))
|
107
|
+
force_from_self = false
|
108
|
+
select_columns = select.map do |sel|
|
109
|
+
unless col = _hash_key_symbol(sel)
|
110
|
+
force_from_self = true
|
111
|
+
break
|
112
|
+
end
|
113
|
+
|
114
|
+
[sel, col]
|
115
|
+
end
|
116
|
+
|
117
|
+
select_columns = nil if force_from_self
|
118
|
+
end
|
119
|
+
end
|
100
120
|
|
101
121
|
# Use a from_self if this is already a joined table (or from_self specifically disabled for graphs)
|
102
|
-
if (@opts[:graph_from_self] != false &&
|
122
|
+
if (@opts[:graph_from_self] != false && !graph && (joined_dataset || force_from_self))
|
103
123
|
from_selfed = true
|
104
124
|
implicit_qualifier = options[:from_self_alias] || first_source
|
105
125
|
ds = ds.from_self(:alias=>implicit_qualifier)
|
@@ -115,7 +135,7 @@ module Sequel
|
|
115
135
|
# Whether to include the table in the result set
|
116
136
|
add_table = options[:select] == false ? false : true
|
117
137
|
|
118
|
-
if graph
|
138
|
+
if graph
|
119
139
|
graph = graph.dup
|
120
140
|
select = opts[:select].dup
|
121
141
|
[:column_aliases, :table_aliases, :column_alias_num].each{|k| graph[k] = graph[k].dup}
|
@@ -137,12 +157,8 @@ module Sequel
|
|
137
157
|
# Keep track of the alias numbers used
|
138
158
|
ca_num = graph[:column_alias_num] = Hash.new(0)
|
139
159
|
|
140
|
-
|
141
|
-
|
142
|
-
# has been used.
|
143
|
-
if (select = @opts[:select]) && !select.empty? && !(select.length == 1 && (select.first.is_a?(SQL::ColumnAll)))
|
144
|
-
select = select.map do |sel|
|
145
|
-
raise Error, "can't figure out alias to use for graphing for #{sel.inspect}" unless column = _hash_key_symbol(sel)
|
160
|
+
select = if select_columns
|
161
|
+
select_columns.map do |sel, column|
|
146
162
|
column_aliases[column] = [master, column]
|
147
163
|
if from_selfed
|
148
164
|
# Initial dataset was wrapped in subselect, selected all
|
@@ -155,7 +171,7 @@ module Sequel
|
|
155
171
|
end
|
156
172
|
end
|
157
173
|
else
|
158
|
-
|
174
|
+
columns.map do |column|
|
159
175
|
column_aliases[column] = [master, column]
|
160
176
|
SQL::QualifiedIdentifier.new(qualifier, column)
|
161
177
|
end
|
@@ -77,23 +77,8 @@ module Sequel
|
|
77
77
|
# Yields the receiver and the dataset to the block, which should
|
78
78
|
# call #arg on the receiver for each placeholder argument, and
|
79
79
|
# return the dataset that you want to load.
|
80
|
-
def loader(dataset)
|
81
|
-
|
82
|
-
@args = []
|
83
|
-
ds = yield self, dataset
|
84
|
-
sql = ds.clone(:placeholder_literalizer=>self).sql
|
85
|
-
|
86
|
-
last_offset = 0
|
87
|
-
fragments = @args.map do |used_sql, offset, arg, t|
|
88
|
-
raise Error, "placeholder literalizer argument literalized into different string than dataset returned" unless used_sql.equal?(sql)
|
89
|
-
a = [sql[last_offset...offset], arg, t]
|
90
|
-
last_offset = offset
|
91
|
-
a
|
92
|
-
end
|
93
|
-
final_sql = sql[last_offset..-1]
|
94
|
-
|
95
|
-
arity = @argn+1
|
96
|
-
PlaceholderLiteralizer.new(ds.clone, fragments, final_sql, arity)
|
80
|
+
def loader(dataset, &block)
|
81
|
+
PlaceholderLiteralizer.new(*process(dataset, &block))
|
97
82
|
end
|
98
83
|
|
99
84
|
# Return an Argument with the specified position, or the next position. In
|
@@ -111,6 +96,51 @@ module Sequel
|
|
111
96
|
def use(sql, arg, transformer)
|
112
97
|
@args << [sql, sql.length, arg, transformer]
|
113
98
|
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Return an array with two elements, the first being an
|
103
|
+
# SQL string with interpolated prepared argument placeholders
|
104
|
+
# (suitable for inspect), the the second being an array of
|
105
|
+
# SQL fragments suitable for using for creating a
|
106
|
+
# Sequel::SQL::PlaceholderLiteralString. Designed for use with
|
107
|
+
# emulated prepared statements.
|
108
|
+
def prepared_sql_and_frags(dataset, prepared_args, &block)
|
109
|
+
_, frags, final_sql, _ = process(dataset, &block)
|
110
|
+
|
111
|
+
frags = frags.map(&:first)
|
112
|
+
prepared_sql = String.new
|
113
|
+
frags.each_with_index do |sql, i|
|
114
|
+
prepared_sql << sql
|
115
|
+
prepared_sql << "$#{prepared_args[i]}"
|
116
|
+
end
|
117
|
+
if final_sql
|
118
|
+
frags << final_sql
|
119
|
+
prepared_sql << final_sql
|
120
|
+
end
|
121
|
+
|
122
|
+
[prepared_sql, frags]
|
123
|
+
end
|
124
|
+
|
125
|
+
# Internals of #loader and #prepared_sql_and_frags.
|
126
|
+
def process(dataset)
|
127
|
+
@argn = -1
|
128
|
+
@args = []
|
129
|
+
ds = yield self, dataset
|
130
|
+
sql = ds.clone(:placeholder_literalizer=>self).sql
|
131
|
+
|
132
|
+
last_offset = 0
|
133
|
+
fragments = @args.map do |used_sql, offset, arg, t|
|
134
|
+
raise Error, "placeholder literalizer argument literalized into different string than dataset returned" unless used_sql.equal?(sql)
|
135
|
+
a = [sql[last_offset...offset], arg, t]
|
136
|
+
last_offset = offset
|
137
|
+
a
|
138
|
+
end
|
139
|
+
final_sql = sql[last_offset..-1]
|
140
|
+
|
141
|
+
arity = @argn+1
|
142
|
+
[ds, fragments, final_sql, arity]
|
143
|
+
end
|
114
144
|
end
|
115
145
|
|
116
146
|
# Create a PlaceholderLiteralizer by yielding a Recorder and dataset to the
|
@@ -67,6 +67,14 @@ module Sequel
|
|
67
67
|
end
|
68
68
|
cache_set(:_prepared_sql, super)
|
69
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Report that prepared statements are not emulated, since
|
74
|
+
# all adapters that use this use native prepared statements.
|
75
|
+
def emulate_prepared_statements?
|
76
|
+
false
|
77
|
+
end
|
70
78
|
end
|
71
79
|
|
72
80
|
# Backbone of the prepared statement support. Grafts bind variable
|
@@ -155,13 +163,8 @@ module Sequel
|
|
155
163
|
# prepared_args is present. If so, they are considered placeholders,
|
156
164
|
# and they are substituted using prepared_arg.
|
157
165
|
def literal_symbol_append(sql, v)
|
158
|
-
if @opts[:bind_vars]
|
159
|
-
|
160
|
-
if prepared_arg?(s)
|
161
|
-
literal_append(sql, prepared_arg(s))
|
162
|
-
else
|
163
|
-
sql << v.to_s
|
164
|
-
end
|
166
|
+
if @opts[:bind_vars] && /\A\$(.*)\z/ =~ v
|
167
|
+
literal_append(sql, prepared_arg($1.to_sym))
|
165
168
|
else
|
166
169
|
super
|
167
170
|
end
|
@@ -214,11 +217,6 @@ module Sequel
|
|
214
217
|
@opts[:bind_vars][k]
|
215
218
|
end
|
216
219
|
|
217
|
-
# Whether there is a bound value for the given key.
|
218
|
-
def prepared_arg?(k)
|
219
|
-
@opts[:bind_vars].has_key?(k)
|
220
|
-
end
|
221
|
-
|
222
220
|
# The symbol cache should always be skipped, since placeholders are symbols.
|
223
221
|
def skip_symbol_cache?
|
224
222
|
true
|
@@ -228,9 +226,12 @@ module Sequel
|
|
228
226
|
# support and using the same argument hash so that you can use
|
229
227
|
# bind variables/prepared arguments in subselects.
|
230
228
|
def subselect_sql_append(sql, ds)
|
231
|
-
|
232
|
-
|
233
|
-
|
229
|
+
subselect_sql_dataset(sql, ds).prepared_sql
|
230
|
+
end
|
231
|
+
|
232
|
+
def subselect_sql_dataset(sql, ds)
|
233
|
+
super.clone(:prepared_args=>prepared_args, :bind_vars=>@opts[:bind_vars]).
|
234
|
+
send(:to_prepared_statement, :select, nil, :extend=>prepared_statement_modules)
|
234
235
|
end
|
235
236
|
end
|
236
237
|
|
@@ -258,11 +259,63 @@ module Sequel
|
|
258
259
|
prepared_args << k
|
259
260
|
prepared_arg_placeholder
|
260
261
|
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Prepared statements emulation support for adapters that don't
|
265
|
+
# support native prepared statements. Uses a placeholder
|
266
|
+
# literalizer to hold the prepared sql with the ability to
|
267
|
+
# interpolate arguments to prepare the final SQL string.
|
268
|
+
module EmulatePreparedStatementMethods
|
269
|
+
include UnnumberedArgumentMapper
|
270
|
+
|
271
|
+
def run(&block)
|
272
|
+
if @opts[:prepared_sql_frags]
|
273
|
+
sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
|
274
|
+
clone(:prepared_sql_frags=>nil, :sql=>sql, :prepared_sql=>sql).run(&block)
|
275
|
+
else
|
276
|
+
super
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
private
|
261
281
|
|
262
|
-
#
|
263
|
-
|
282
|
+
# Turn emulation of prepared statements back on, since ArgumentMapper
|
283
|
+
# turns it off.
|
284
|
+
def emulate_prepared_statements?
|
264
285
|
true
|
265
286
|
end
|
287
|
+
|
288
|
+
def emulated_prepared_statement(type, name, values)
|
289
|
+
prepared_sql, frags = Sequel::Dataset::PlaceholderLiteralizer::Recorder.new.send(:prepared_sql_and_frags, self, prepared_args) do |pl, ds|
|
290
|
+
ds = ds.clone(:recorder=>pl)
|
291
|
+
|
292
|
+
case type
|
293
|
+
when :first
|
294
|
+
ds.limit(1)
|
295
|
+
when :update, :insert, :insert_select, :delete
|
296
|
+
ds.with_sql(:"#{type}_sql", *values)
|
297
|
+
when :insert_pk
|
298
|
+
ds.with_sql(:insert_sql, *values)
|
299
|
+
else
|
300
|
+
ds
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
prepared_args.freeze
|
305
|
+
clone(:prepared_sql_frags=>frags, :prepared_sql=>prepared_sql, :sql=>prepared_sql)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Associates the argument with name k with the next position in
|
309
|
+
# the output array.
|
310
|
+
def prepared_arg(k)
|
311
|
+
prepared_args << k
|
312
|
+
@opts[:recorder].arg
|
313
|
+
end
|
314
|
+
|
315
|
+
def subselect_sql_dataset(sql, ds)
|
316
|
+
super.clone(:recorder=>@opts[:recorder]).
|
317
|
+
with_extend(EmulatePreparedStatementMethods)
|
318
|
+
end
|
266
319
|
end
|
267
320
|
|
268
321
|
# Set the bind variables to use for the call. If bind variables have
|
@@ -315,7 +368,16 @@ module Sequel
|
|
315
368
|
# DB.call(:select_by_name, name: 'Blah') # Same thing
|
316
369
|
def prepare(type, name, *values)
|
317
370
|
ps = to_prepared_statement(type, values, :name=>name, :extend=>prepared_statement_modules, :no_delayed_evaluations=>true)
|
318
|
-
|
371
|
+
|
372
|
+
ps = if ps.send(:emulate_prepared_statements?)
|
373
|
+
ps = ps.with_extend(EmulatePreparedStatementMethods)
|
374
|
+
ps.send(:emulated_prepared_statement, type, name, values)
|
375
|
+
else
|
376
|
+
sql = ps.prepared_sql
|
377
|
+
ps.prepared_args.freeze
|
378
|
+
ps.clone(:prepared_sql=>sql, :sql=>sql)
|
379
|
+
end
|
380
|
+
|
319
381
|
db.set_prepared_statement(name, ps)
|
320
382
|
ps
|
321
383
|
end
|
@@ -344,6 +406,12 @@ module Sequel
|
|
344
406
|
prepared_statement_modules
|
345
407
|
end
|
346
408
|
|
409
|
+
# Whether prepared statements should be emulated. True by
|
410
|
+
# default so that adapters have to opt in.
|
411
|
+
def emulate_prepared_statements?
|
412
|
+
true
|
413
|
+
end
|
414
|
+
|
347
415
|
def prepared_statement_modules
|
348
416
|
[]
|
349
417
|
end
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -1553,7 +1553,11 @@ module Sequel
|
|
1553
1553
|
|
1554
1554
|
# Append literalization of the subselect to SQL string.
|
1555
1555
|
def subselect_sql_append(sql, ds)
|
1556
|
-
|
1556
|
+
subselect_sql_dataset(sql, ds).sql
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
def subselect_sql_dataset(sql, ds)
|
1560
|
+
ds.clone(:append_sql=>sql)
|
1557
1561
|
end
|
1558
1562
|
|
1559
1563
|
# The number of decimal digits of precision to use in timestamps.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The caller_logging extension includes caller information before
|
4
|
+
# query logging, showing which code caused the query. It skips
|
5
|
+
# internal Sequel code, showing the first non-Sequel caller line.
|
6
|
+
#
|
7
|
+
# DB.extension :caller_logging
|
8
|
+
# DB[:table].first
|
9
|
+
# # Logger:
|
10
|
+
# # (0.000041s) (source: /path/to/app/foo/t.rb:12 in `get_first`) SELECT * FROM table LIMIT 1
|
11
|
+
#
|
12
|
+
# You can further filter the caller lines by setting
|
13
|
+
# <tt>Database#caller_logging_ignore</tt> to a regexp of additional
|
14
|
+
# caller lines to ignore. This is useful if you have specific
|
15
|
+
# methods or internal extensions/plugins that you would also
|
16
|
+
# like to ignore as they obscure the code actually making the
|
17
|
+
# request.
|
18
|
+
#
|
19
|
+
# DB.caller_logging_ignore = %r{/path/to/app/lib/plugins}
|
20
|
+
#
|
21
|
+
# You can also format the caller before it is placed in the logger,
|
22
|
+
# using +caller_logging_formatter+:
|
23
|
+
#
|
24
|
+
# DB.caller_logging_formatter = lambda do |caller|
|
25
|
+
# "(#{caller.sub(/\A\/path\/to\/app\//, '')})"
|
26
|
+
# end
|
27
|
+
# DB[:table].first
|
28
|
+
# # Logger:
|
29
|
+
# # (0.000041s) (foo/t.rb:12 in `get_first`) SELECT * FROM table LIMIT 1
|
30
|
+
#
|
31
|
+
# Related module: Sequel::CallerLogging
|
32
|
+
|
33
|
+
require 'rbconfig'
|
34
|
+
|
35
|
+
#
|
36
|
+
module Sequel
|
37
|
+
module CallerLogging
|
38
|
+
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
39
|
+
|
40
|
+
# A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
|
41
|
+
attr_accessor :caller_logging_ignore
|
42
|
+
|
43
|
+
# A callable to format the external caller
|
44
|
+
attr_accessor :caller_logging_formatter
|
45
|
+
|
46
|
+
# Include caller information when logging query.
|
47
|
+
def log_connection_yield(sql, conn, args=nil)
|
48
|
+
if !@loggers.empty? && (external_caller = external_caller_for_log)
|
49
|
+
sql = "#{external_caller} #{sql}"
|
50
|
+
end
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# The caller to log, ignoring internal Sequel and Ruby code, and user specified
|
57
|
+
# lines to ignore.
|
58
|
+
def external_caller_for_log
|
59
|
+
ignore = caller_logging_ignore
|
60
|
+
c = caller.find do |line|
|
61
|
+
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
62
|
+
line.start_with?(RbConfig::CONFIG["rubylibdir"]) ||
|
63
|
+
(ignore && line =~ ignore))
|
64
|
+
end
|
65
|
+
|
66
|
+
if c
|
67
|
+
c = if formatter = caller_logging_formatter
|
68
|
+
formatter.call(c)
|
69
|
+
else
|
70
|
+
"(source: #{c})"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
c
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Database.register_extension(:caller_logging, CallerLogging)
|
79
|
+
end
|