pgx 1.0.5 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pgx.rb +51 -96
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d32046f29c429a8e7c9e45ec0d603fd96a2588a18bbe14d773a355e2e37e061
|
4
|
+
data.tar.gz: 28e35690b03970729e9ea98080473f90ada24435089237775d3e3c8275515aa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b5bc92e8d2b851dd43b668ab278d7129dac7e7dd6fa9d18800d4f644d376b345a2dae37392629cc10f0bda979adb0deaff40cfee43d920ebf17dc650725b3cc
|
7
|
+
data.tar.gz: e43a561b2ec0dc3e5202d7774a52ad145a80ae543b0d7e52fb83273eb250c1ca08040c49b42be1f94e7249fb209b97530ad25deefc9a56594a97ef7814979544
|
data/lib/pgx.rb
CHANGED
@@ -37,15 +37,13 @@ module PGx
|
|
37
37
|
)
|
38
38
|
|
39
39
|
# @author Kiriakos Georgiou, http://www.mockbites.com/about
|
40
|
-
# Extends the
|
40
|
+
# Extends the PG::Connection methods by adding logging and
|
41
41
|
# retrying capabilities (synchronous methods only.)
|
42
42
|
# The arguments and method behaviors are
|
43
43
|
# identical, except for the 'new' method.
|
44
44
|
# @see http://www.mockbites.com/articles/tech/pgx pgx documentation
|
45
45
|
# @see http://deveiate.org/code/pg/PG/Connection.html Base class methods
|
46
|
-
class PGx::Connection
|
47
|
-
alias PG_close close # PG's close(), for use when closing bad connections
|
48
|
-
|
46
|
+
class PGx::Connection
|
49
47
|
# @param [Logger] logger: a logger object, or a lambda returning the
|
50
48
|
# logger object. Note that a lambda returning the logger is
|
51
49
|
# preferable because it makes logging of the log object itself
|
@@ -87,7 +85,7 @@ module PGx
|
|
87
85
|
# )
|
88
86
|
def initialize(*args)
|
89
87
|
connect_args = PGx.const_get('CONNECT_ARGS')
|
90
|
-
(our_args,
|
88
|
+
(our_args, pg_args) = extract_our_args(connect_args, args)
|
91
89
|
|
92
90
|
log = arg_with_default(connect_args, our_args, :logger)
|
93
91
|
@logger = [ log.is_a?(Proc) ? log.call : log ].flatten
|
@@ -112,47 +110,29 @@ module PGx
|
|
112
110
|
@connect_proc = lambda {
|
113
111
|
@prepared_live = {} # reset on connect
|
114
112
|
connect_log_and_try(
|
115
|
-
lambda {
|
113
|
+
lambda { PG::Connection.new(*pg_args) },
|
116
114
|
our_args,
|
117
|
-
|
115
|
+
pg_args
|
118
116
|
)
|
119
117
|
}
|
120
118
|
connect_loop()
|
121
119
|
end
|
122
120
|
|
121
|
+
def method_missing(m, *args, &block)
|
122
|
+
sql_log_and_try(lambda { @connection.send(m, *args, &block) }, m, args)
|
123
|
+
end
|
124
|
+
|
123
125
|
# @example
|
124
126
|
# con.transaction do
|
125
127
|
# con.exec "create temp table mytable(x text)"
|
126
128
|
# con.exec "insert into mytable(x) values('a')"
|
127
129
|
# con.exec "insert into mytable(x) values('b')"
|
128
130
|
# end
|
129
|
-
def transaction(*args)
|
131
|
+
def transaction(*args, &block)
|
130
132
|
@in_transaction_block = true
|
131
|
-
sql_log_and_try(lambda {
|
133
|
+
r = sql_log_and_try(lambda { @connection.transaction(*args, &block) }, __method__, args)
|
132
134
|
@in_transaction_block = false
|
133
|
-
|
134
|
-
|
135
|
-
# @example
|
136
|
-
# con.exec "insert into mytable(x) values('test value')"
|
137
|
-
# con.exec 'insert into mytable(x) values($1)', ['test value']
|
138
|
-
def exec(*args)
|
139
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Alias for exec
|
143
|
-
def query(*args)
|
144
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
145
|
-
end
|
146
|
-
|
147
|
-
# @example
|
148
|
-
# con.exec_params 'select * from mytable where x = $1 limit $2', [1, 100] do |rs|
|
149
|
-
# puts rs.fields.collect { |fname| "%-15s" % [fname] }.join('')
|
150
|
-
# rs.values.collect { |row|
|
151
|
-
# puts row.collect { |col| "%-15s" % [col] }.join('')
|
152
|
-
# }
|
153
|
-
# end
|
154
|
-
def exec_params(*args)
|
155
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
135
|
+
r
|
156
136
|
end
|
157
137
|
|
158
138
|
# @example
|
@@ -163,52 +143,27 @@ module PGx
|
|
163
143
|
# puts row.collect { |col| "%-15s" % [col] }.join('')
|
164
144
|
# }
|
165
145
|
# end
|
166
|
-
def prepare(*args)
|
146
|
+
def prepare(*args, &block)
|
167
147
|
# it's possible to call prepare() on an statement that is already prepared
|
168
148
|
# if you have a prepare() inside a transaction, and the transaction is retried
|
169
149
|
# thus we check if it's already live
|
170
150
|
s = args[0]
|
151
|
+
r = nil
|
171
152
|
if @prepared_live.has_key?(s)
|
172
153
|
log_debug %Q{prepared statement #{s} is already live, skipping re-preparing it}
|
173
154
|
else
|
174
|
-
sql_log_and_try(lambda {
|
155
|
+
r = sql_log_and_try(lambda { @connection.prepare(*args, &block) }, __method__, args)
|
175
156
|
@prepared_statements[s] = args[1]
|
176
157
|
@prepared_live[s] = true
|
177
158
|
end
|
159
|
+
r
|
178
160
|
end
|
179
161
|
|
180
|
-
|
181
|
-
|
182
|
-
# con.exec_prepared 'sql1', [100] do |rs|
|
183
|
-
# puts rs.fields.collect { |fname| "%-15s" % [fname] }.join('')
|
184
|
-
# rs.values.collect { |row|
|
185
|
-
# puts row.collect { |col| "%-15s" % [col] }.join('')
|
186
|
-
# }
|
187
|
-
# end
|
188
|
-
def exec_prepared(*args)
|
189
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
190
|
-
end
|
191
|
-
|
192
|
-
def cancel(*args)
|
193
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
194
|
-
end
|
195
|
-
|
196
|
-
def close(*args)
|
197
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
198
|
-
end
|
199
|
-
|
200
|
-
def finish(*args)
|
201
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
202
|
-
end
|
203
|
-
|
204
|
-
def reset(*args)
|
205
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
162
|
+
def reset(*args, &block)
|
163
|
+
r = sql_log_and_try(lambda { @connection.reset(*args, &block) }, __method__, args)
|
206
164
|
@prepared_live = {} # reset on connect
|
207
165
|
@connect_init.call(@connection)
|
208
|
-
|
209
|
-
|
210
|
-
def wait_for_notify(*args)
|
211
|
-
sql_log_and_try(lambda { super }, __method__, args)
|
166
|
+
r
|
212
167
|
end
|
213
168
|
|
214
169
|
private
|
@@ -221,7 +176,7 @@ module PGx
|
|
221
176
|
|
222
177
|
# extract our arguments defined in all_our_possible_args from args
|
223
178
|
# returns an array containing our arguments (hash) and the aguments
|
224
|
-
# for the
|
179
|
+
# for the pg method
|
225
180
|
def extract_our_args(all_our_possible_args, args)
|
226
181
|
our_args = {}
|
227
182
|
if args[0].is_a?(Hash)
|
@@ -280,45 +235,45 @@ module PGx
|
|
280
235
|
end
|
281
236
|
end
|
282
237
|
|
283
|
-
# sanitize
|
284
|
-
def
|
285
|
-
if
|
286
|
-
return
|
287
|
-
elsif
|
288
|
-
if
|
289
|
-
return [
|
238
|
+
# sanitize pg arguments, eg: blank the password so it's not in the logs
|
239
|
+
def sanitize_pg_connect_args(pg_args)
|
240
|
+
if pg_args.empty?
|
241
|
+
return pg_args
|
242
|
+
elsif pg_args.length == 1
|
243
|
+
if pg_args[0].is_a?(Hash) # hash method
|
244
|
+
return [ pg_args[0].merge(pg_args[0]) { |k, ov| k == :password ? '...' : ov } ]
|
290
245
|
end
|
291
|
-
if
|
292
|
-
return [
|
246
|
+
if pg_args[0].is_a?(String) # string method
|
247
|
+
return [ pg_args[0].gsub(/(password\s*=\s*)\S+/, '\1...') ]
|
293
248
|
end
|
294
249
|
else # positional arguments method
|
295
|
-
return [
|
250
|
+
return [ pg_args.map.with_index { |x, i| i == 6 ? '...' : x } ]
|
296
251
|
end
|
297
252
|
end
|
298
253
|
|
299
254
|
# merge as follows:
|
300
255
|
# hash and hash -> hash
|
301
256
|
# hash and array -> array
|
302
|
-
def merge_connect_args(our_args,
|
257
|
+
def merge_connect_args(our_args, pg_args)
|
303
258
|
if our_args.empty?
|
304
|
-
return
|
305
|
-
elsif
|
259
|
+
return pg_args
|
260
|
+
elsif pg_args.empty?
|
306
261
|
return our_args
|
307
|
-
elsif
|
308
|
-
return our_args.merge(
|
262
|
+
elsif pg_args.length == 1 and pg_args[0].is_a?(Hash)
|
263
|
+
return our_args.merge(pg_args[0]) # connection hash style
|
309
264
|
else
|
310
|
-
return [our_args].concat(
|
265
|
+
return [our_args].concat(pg_args)
|
311
266
|
end
|
312
267
|
end
|
313
268
|
|
314
269
|
# log and retry the connect/new method
|
315
|
-
def connect_log_and_try(
|
270
|
+
def connect_log_and_try(pg_lambda, our_args, pg_args)
|
316
271
|
try_count = 0
|
317
272
|
all_args = merge_connect_args( sanitize_our_connect_args(our_args),
|
318
|
-
|
273
|
+
sanitize_pg_connect_args(pg_args) )
|
319
274
|
begin
|
320
275
|
log_debug function_call_string('connect', all_args)
|
321
|
-
@connection =
|
276
|
+
@connection = pg_lambda.call # run pg new() mathod
|
322
277
|
log_debug 'calling connection initialization proc'
|
323
278
|
@connect_init.call(@connection)
|
324
279
|
rescue PG::Error => e
|
@@ -336,25 +291,25 @@ module PGx
|
|
336
291
|
end
|
337
292
|
|
338
293
|
# log and retry statement related methods
|
339
|
-
def sql_log_and_try(
|
294
|
+
def sql_log_and_try(pg_lambda, method, args)
|
340
295
|
try_count = 0
|
341
296
|
begin # re-prepare prepared statements after a failed transaction
|
342
|
-
transaction_reprepare(
|
343
|
-
log_debug function_call_string(
|
344
|
-
|
297
|
+
transaction_reprepare(method) # just prior to retrying the transaction
|
298
|
+
log_debug function_call_string(method, args)
|
299
|
+
pg_lambda.call # run the pg method
|
345
300
|
rescue PG::Error => e
|
346
301
|
if connected? # if we are connected, it's an SQL related error
|
347
|
-
if not method_in_transaction_block(
|
302
|
+
if not method_in_transaction_block(method) # do not log errors and retry
|
348
303
|
try_count = try_count + 1 # methods within a transaction
|
349
304
|
log_error(get_error_fields(e) + "\n" + get_error_message(e)) # block because the transaction
|
350
|
-
error_handlers(e,
|
305
|
+
error_handlers(e, method, args) # itself will log the error and
|
351
306
|
retry if sql_retriable?(e, try_count) # retry the transaction block
|
352
|
-
elsif
|
307
|
+
elsif method.to_s == 'exec_prepared' # exec_prepared failed within a transaction
|
353
308
|
@transaction_reprepare = args[0] # make a note to re-prepare before retrying
|
354
309
|
end # the transaction
|
355
310
|
else # not connected
|
356
311
|
log_warn 'bad database connection'
|
357
|
-
|
312
|
+
@connection.close # just in case so we don't leak connections
|
358
313
|
connect_loop()
|
359
314
|
retry
|
360
315
|
end
|
@@ -363,7 +318,7 @@ module PGx
|
|
363
318
|
end
|
364
319
|
|
365
320
|
# special handling of certain error states (not in a transaction)
|
366
|
-
def error_handlers(pgerr,
|
321
|
+
def error_handlers(pgerr, method, args)
|
367
322
|
begin
|
368
323
|
state = pgerr.result.error_field(PG::Result::PG_DIAG_SQLSTATE)
|
369
324
|
rescue PG::Error => e
|
@@ -372,12 +327,12 @@ module PGx
|
|
372
327
|
|
373
328
|
case state
|
374
329
|
when '26000'
|
375
|
-
reprepare_statement(args[0]) if
|
330
|
+
reprepare_statement(args[0]) if method.to_s == 'exec_prepared'
|
376
331
|
end # case
|
377
332
|
end
|
378
333
|
|
379
|
-
def transaction_reprepare(
|
380
|
-
if
|
334
|
+
def transaction_reprepare(method)
|
335
|
+
if method.to_s == 'transaction' and not @transaction_reprepare.nil?
|
381
336
|
reprepare_statement(@transaction_reprepare)
|
382
337
|
@transaction_reprepare = nil
|
383
338
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kiriakos Georgiou
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2023-04-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
13
27
|
description: |2
|
14
28
|
A pure ruby "thin" extension of pg that provides logging,
|
15
29
|
transaction retrying, and database reconnecting functionality.
|
@@ -23,7 +37,7 @@ homepage: http://www.mockbites.com/articles/tech/pgx
|
|
23
37
|
licenses:
|
24
38
|
- MIT
|
25
39
|
metadata: {}
|
26
|
-
post_install_message:
|
40
|
+
post_install_message:
|
27
41
|
rdoc_options: []
|
28
42
|
require_paths:
|
29
43
|
- lib
|
@@ -38,8 +52,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
52
|
- !ruby/object:Gem::Version
|
39
53
|
version: '0'
|
40
54
|
requirements: []
|
41
|
-
rubygems_version: 3.
|
42
|
-
signing_key:
|
55
|
+
rubygems_version: 3.3.26
|
56
|
+
signing_key:
|
43
57
|
specification_version: 4
|
44
58
|
summary: A thin extension of pg
|
45
59
|
test_files: []
|