pgx 1.0.4 → 1.0.7
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/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 # 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: []
|