pg_conn 0.35.0 → 0.36.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/pg_conn/version.rb +1 -1
- data/lib/pg_conn.rb +209 -130
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d6940809a3f62bfad5cd2a94566a76bcd1944ad5b6ded6df2be159a010b2169
|
4
|
+
data.tar.gz: 6f213cb288ba5a85205fd5054fb498e6200b0319eee8c2bbc8c60515705410f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a90dab6c75eb2cfd7a99b39676a4aa0dc167462e37e9229d96ed30517b4ff0c691f6b4389ceb6d26c3f2562d7ce4e7dd2be17d054f1e61369989fb9f8c3db8e1
|
7
|
+
data.tar.gz: f91893227ef9697eb236051728d509727b2d20b5d9efd7c6f848cf2e887b1abc0e5c8a48da291914372049ab25aee6580b9b74fd422d269c3e7813928032dd23
|
data/lib/pg_conn/version.rb
CHANGED
data/lib/pg_conn.rb
CHANGED
@@ -40,22 +40,22 @@ module PgConn
|
|
40
40
|
#
|
41
41
|
def self.quote_identifier(s)
|
42
42
|
s = s.to_s if s.is_a?(Symbol)
|
43
|
-
escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
|
43
|
+
Literal.new escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
|
44
44
|
end
|
45
45
|
|
46
46
|
# Quote identifiers and concatenate them using ',' as separator
|
47
|
-
def self.quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
|
47
|
+
def self.quote_identifiers(idents) = Literal.new idents.map { |ident| quote_identifier(ident) }.join(", ")
|
48
48
|
|
49
|
-
# Quote the value as a string.
|
49
|
+
# Quote the value as a string. Returns a Literal object
|
50
50
|
#
|
51
51
|
# The value can be of any type but is converted to a string using #to_s
|
52
52
|
# before quoting. This works by default for the regular types Integer,
|
53
53
|
# true/false, Time/Date/DateTime, and arrays. Other types may require
|
54
54
|
# special handling
|
55
55
|
#
|
56
|
-
# Hashes are quoted as a literal JSON expression
|
57
|
-
#
|
58
|
-
# or 'jsonb'
|
56
|
+
# Hashes are quoted as a literal JSON expression converted into the given
|
57
|
+
# :json_type. If :json_type is nil, it is the application's responsibility to
|
58
|
+
# cast the value to either 'json' or 'jsonb'
|
59
59
|
#
|
60
60
|
# Note that a tuple value (an array) must be quoted using #quote_tuple
|
61
61
|
# because #quote_value would quote the tuple as an array value instead of a
|
@@ -66,48 +66,51 @@ module PgConn
|
|
66
66
|
# type when the argument is an empty array. It is not needed if the array
|
67
67
|
# is guaranteed to be non-empty. Nested arrays are not supported
|
68
68
|
#
|
69
|
-
def self.quote_value(value, elem_type: nil)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
69
|
+
def self.quote_value(value, elem_type: nil, json_type: nil)
|
70
|
+
Literal.new \
|
71
|
+
case value
|
72
|
+
when Literal; value
|
73
|
+
when String; escape_literal(value)
|
74
|
+
when Integer, Float; value.to_s
|
75
|
+
when true, false; value.to_s
|
76
|
+
when nil; 'null'
|
77
|
+
when Date, DateTime; "'#{value}'"
|
78
|
+
when Time; "'#{value.strftime("%FT%T%:z")}'"
|
79
|
+
when Array
|
80
|
+
if value.empty?
|
81
|
+
elem_type or raise Error, "Empty array without elem_type"
|
82
|
+
"array[]::#{elem_type}[]"
|
83
|
+
else
|
84
|
+
"array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
|
85
|
+
end
|
86
|
+
when Hash; ["'#{value.to_json}'", json_type].compact.join('::')
|
82
87
|
else
|
83
|
-
|
88
|
+
escape_literal(value.to_s)
|
84
89
|
end
|
85
|
-
when Hash; "'#{value.to_json}'"
|
86
|
-
else
|
87
|
-
escape_literal(value.to_s)
|
88
|
-
end
|
89
90
|
end
|
90
91
|
|
91
92
|
# Quote values and concatenate them using ',' as separator
|
92
|
-
def self.quote_values(values,
|
93
|
-
values.map { |value| quote_value(value,
|
93
|
+
def self.quote_values(values, **opts)
|
94
|
+
Literal.new values.map { |value| quote_value(value, **opts) }.join(", ")
|
94
95
|
end
|
95
96
|
|
96
97
|
# Quote an array of values as a tuple. The element types should be in the
|
97
98
|
# same order as the array arguments. #quote_tuples is same as #quote_values
|
98
99
|
# except the values may have different types (this makes no difference
|
99
100
|
# except in the case when the tuple may contain empty array(s))
|
100
|
-
|
101
|
+
#
|
102
|
+
# Note that it is :elem_types (plural) and not :elem_type
|
103
|
+
def self.quote_tuple(tuple, elem_types: nil, **opts)
|
101
104
|
elem_types = Array(elem_types)
|
102
|
-
tuple.map { |value|
|
105
|
+
Literal.new tuple.map { |value|
|
103
106
|
elem_type = value.is_a?(Array) ? elem_types&.shift : nil
|
104
|
-
quote_value(value, elem_type: elem_type)
|
107
|
+
quote_value(value, **opts, elem_type: elem_type)
|
105
108
|
}.join(", ")
|
106
109
|
end
|
107
110
|
|
108
111
|
# Quote an array of tuples
|
109
|
-
def self.quote_tuples(tuples,
|
110
|
-
tuples.map { |tuple| "(#{quote_tuple(tuple,
|
112
|
+
def self.quote_tuples(tuples, **opts)
|
113
|
+
Literal.new tuples.map { |tuple| "(#{quote_tuple(tuple, **opts)})" }.join(", ")
|
111
114
|
end
|
112
115
|
|
113
116
|
# Used to mark strings as literals that should not be quoted. This is the
|
@@ -125,6 +128,9 @@ module PgConn
|
|
125
128
|
# The class of column names (Symbol or String). Default is Symbol
|
126
129
|
attr_reader :field_name_class
|
127
130
|
|
131
|
+
# Default postgres JSON type (either 'json' or 'jsonb'). Default is 'jsonb'
|
132
|
+
attr_reader :default_json_type
|
133
|
+
|
128
134
|
# Name of user
|
129
135
|
def user() @pg_connection.user end
|
130
136
|
alias_method :username, :user # Obsolete FIXME Is it?
|
@@ -155,28 +161,56 @@ module PgConn
|
|
155
161
|
# #exec or #transaction block. The timestamp includes the current time zone
|
156
162
|
attr_reader :timestamptz
|
157
163
|
|
158
|
-
# Controls error messages. It can be assigned true, false, nil
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
164
|
+
# Controls error messages. It can be assigned true, false, nil, or a Proc
|
165
|
+
# object that recieves the message. True causes the message to be printed
|
166
|
+
# to standard error, false ignores it, and nil resets the state to the
|
167
|
+
# default given when the connection was initialized. #silent? returns true
|
168
|
+
# if #silent is false or a Proc object and should be used instead #silent
|
169
|
+
# to check the state because #silent returns truish when output is
|
170
|
+
# redirected to a Proc
|
171
|
+
#
|
172
|
+
# Note that #silent=, #notice=, and warning= only controls the error
|
173
|
+
# message, the exception is passed through unaltered
|
174
|
+
#
|
163
175
|
def silent() @options[:silent] end
|
176
|
+
def silent?() @producers[:silent].nil? end # silent == false/Proc is true
|
164
177
|
def silent=(value) set_option(:silent, value) end
|
165
178
|
|
166
|
-
# Controls notices. It can be assigned true, false, nil, or a Proc object
|
167
|
-
# that recieves the message. True causes the message to be printed to
|
168
|
-
# standard output, false ignores it, and nil resets the state to the
|
169
|
-
# default given when the connection was initialized or false if absent
|
170
|
-
def notice() @options[:notice] end
|
171
|
-
def notice=(value) set_option(:notice, value) end
|
172
|
-
|
173
179
|
# Controls warnings. It can be assigned true, false, nil, or a Proc object
|
174
180
|
# that recieves the message. True causes the message to be printed to
|
175
181
|
# standard error, false ignores it, and nil resets the state to the default
|
176
|
-
# given when the connection was initialized
|
182
|
+
# given when the connection was initialized
|
177
183
|
def warning() @options[:warning] end
|
184
|
+
def warning?() !warning.nil? end
|
178
185
|
def warning=(value) set_option(:warning, value) end
|
179
186
|
|
187
|
+
# Controls notice messages. It can be assigned true, false, nil, or a Proc
|
188
|
+
# object that recieves the message. True causes the message to be printed
|
189
|
+
# to standard error, false ignores it, and nil resets the state to the
|
190
|
+
# default given when the connection was initialized. Default false
|
191
|
+
def notice() @options[:notice] end
|
192
|
+
def notice?() !notice.nil? end
|
193
|
+
def notice=(value) set_option(:notice, value) end
|
194
|
+
|
195
|
+
# Controls info messages. It can be assigned true, false, nil, or a Proc
|
196
|
+
# object that recieves the message. True causes the message to be printed
|
197
|
+
# to standard output, false ignores it, and nil resets the state to the
|
198
|
+
# default given when the connection was initialized. Default false. Note
|
199
|
+
# that #info is the only level that outputs to standard output
|
200
|
+
def info() @options[:info] end
|
201
|
+
def info?() !info.nil? end
|
202
|
+
def info=(value) set_option(:info, value) end
|
203
|
+
|
204
|
+
# Controls debug messages. It can be assigned true, false, nil, or a Proc
|
205
|
+
# object that recieves the message. True causes the message to be printed
|
206
|
+
# to standard error, false ignores it, and nil resets the state to the
|
207
|
+
# default given when the connection was initialized. Default false
|
208
|
+
def debug() @options[:debug] end
|
209
|
+
def debug?() !debug.nil? end
|
210
|
+
def debug=(value) set_option(:debug, value) end
|
211
|
+
|
212
|
+
DEFAULT_OPTIONS = { silent: false, warning: true, notice: false, info: false, debug: false }
|
213
|
+
|
180
214
|
# TODO: Move error message handling into the same framework as notice and
|
181
215
|
# warning but we have a name collision just below that would need to be
|
182
216
|
# resolved somehow
|
@@ -214,8 +248,6 @@ module PgConn
|
|
214
248
|
# if absent in the Postgres error message
|
215
249
|
def errchar = err[2]
|
216
250
|
|
217
|
-
DEFAULT_OPTIONS = { silent: false, notice: false, warning: false }
|
218
|
-
|
219
251
|
# :call-seq:
|
220
252
|
# initialize(dbname = nil, user = nil, **options)
|
221
253
|
# initialize(connection_hash, **options)
|
@@ -247,8 +279,10 @@ module PgConn
|
|
247
279
|
# Symbol (the default) or String. The :timestamp option is used
|
248
280
|
# internally to set the timestamp for transactions
|
249
281
|
#
|
250
|
-
# The :notice and :warning options sets the default output handling this
|
251
|
-
# connection (FIXME fails on copied connections)
|
282
|
+
# The :notice and :warning options sets the default output handling for this
|
283
|
+
# connection (FIXME fails on copied connections). Default is to suppress
|
284
|
+
# notices and lower - this is diffent from postgres that by default include
|
285
|
+
# notices
|
252
286
|
#
|
253
287
|
# Note that the connection hash and the connection string may support more
|
254
288
|
# parameters than documented here. Consult
|
@@ -263,7 +297,17 @@ module PgConn
|
|
263
297
|
if args.last.is_a?(Hash)
|
264
298
|
opts = args.last
|
265
299
|
@field_name_class = opts.delete(:field_name_class) || Symbol
|
266
|
-
|
300
|
+
|
301
|
+
# Extract options from arguments
|
302
|
+
options = DEFAULT_OPTIONS.to_h { |k,v|
|
303
|
+
r = if opts.key?(k)
|
304
|
+
value = opts.delete(k)
|
305
|
+
value.nil? ? v : value
|
306
|
+
else
|
307
|
+
v
|
308
|
+
end
|
309
|
+
[k, r]
|
310
|
+
}
|
267
311
|
|
268
312
|
# FIXME: Is this used?
|
269
313
|
@timestamp = opts.delete(:timestamp)
|
@@ -272,7 +316,7 @@ module PgConn
|
|
272
316
|
args.pop if opts.empty?
|
273
317
|
else
|
274
318
|
@field_name_class = Symbol
|
275
|
-
options = DEFAULT_OPTIONS
|
319
|
+
options = DEFAULT_OPTIONS.dup
|
276
320
|
end
|
277
321
|
|
278
322
|
# else # We assume that the current user is a postgres superuser
|
@@ -340,11 +384,17 @@ module PgConn
|
|
340
384
|
@pg_connection.field_name_type = @field_name_class.to_s.downcase.to_sym # Use symbol field names
|
341
385
|
end
|
342
386
|
|
343
|
-
# Set options
|
344
|
-
#
|
345
|
-
|
346
|
-
|
347
|
-
|
387
|
+
# Set options. The initial options also serves as default values and are
|
388
|
+
# themselves initialized using DEFAULT_VALUES
|
389
|
+
#
|
390
|
+
# Note that options is initialized even if there is no connection to
|
391
|
+
# avoid special casing
|
392
|
+
@default_options = options
|
393
|
+
@options = {}
|
394
|
+
@producers = {} # Map from message level to Proc or nil
|
395
|
+
set_options(@default_options) if @pg_connection
|
396
|
+
|
397
|
+
@default_json_type = :jsonb
|
348
398
|
@schema = SchemaMethods.new(self)
|
349
399
|
@role = RoleMethods.new(self)
|
350
400
|
@rdbms = RdbmsMethods.new(self)
|
@@ -380,17 +430,17 @@ module PgConn
|
|
380
430
|
end
|
381
431
|
end
|
382
432
|
|
383
|
-
# Mark argument as already being quoted
|
384
|
-
# quote methods
|
433
|
+
# Mark string argument as already being quoted
|
385
434
|
def literal(arg) Literal.new(arg) end
|
386
435
|
|
387
436
|
# Connection member method variations of the PgConn quote class methods
|
437
|
+
# with at least a default value for :json_type
|
388
438
|
def quote_identifier(s) = PgConn.quote_identifier(s)
|
389
439
|
def quote_identifiers(idents) = PgConn.quote_identifiers(idents)
|
390
|
-
def quote_value(value, **opts) = PgConn.quote_value(value, **opts)
|
391
|
-
def quote_values(values, **opts) = PgConn.quote_values(values, **opts)
|
392
|
-
def quote_tuple(tuple, **opts) = PgConn.quote_tuple(tuple, **opts)
|
393
|
-
def quote_tuples(tuples, **opts) = PgConn.quote_tuples(tuples, **opts)
|
440
|
+
def quote_value(value, **opts) = PgConn.quote_value(value, json_type: self.default_json_type, **opts)
|
441
|
+
def quote_values(values, **opts) = PgConn.quote_values(values, json_type: self.default_json_type, **opts)
|
442
|
+
def quote_tuple(tuple, **opts) = PgConn.quote_tuple(tuple, json_type: self.default_json_type, **opts)
|
443
|
+
def quote_tuples(tuples, **opts) = PgConn.quote_tuples(tuples, json_type: self.default_json_type, **opts)
|
394
444
|
|
395
445
|
# Quote a record and cast it into the given type, the type can also be a
|
396
446
|
# table or view. 'data' is an array, hash, or struct representation of the
|
@@ -403,14 +453,14 @@ module PgConn
|
|
403
453
|
#
|
404
454
|
# Also note that there is not class-method variant of this method because
|
405
455
|
# it requires a connection
|
406
|
-
def quote_record(data, schema_name = nil, type,
|
407
|
-
quote_record_impl(data, schema_name, type,
|
456
|
+
def quote_record(data, schema_name = nil, type, **opts)
|
457
|
+
quote_record_impl(data, schema_name, type, array: false, **opts)
|
408
458
|
end
|
409
459
|
|
410
460
|
# Quote an array of records. The type is the record type, not the type of
|
411
461
|
# the enclosing array
|
412
|
-
def quote_records(data, schema_name = nil, type,
|
413
|
-
quote_record_impl(data, schema_name, type,
|
462
|
+
def quote_records(data, schema_name = nil, type, **opts)
|
463
|
+
quote_record_impl(data, schema_name, type, array: true, **opts)
|
414
464
|
end
|
415
465
|
|
416
466
|
# :call-seq:
|
@@ -692,11 +742,11 @@ module PgConn
|
|
692
742
|
# columns (like #tuples).
|
693
743
|
#
|
694
744
|
# The name argument can be a String or a Symbol that may contain the schema
|
695
|
-
# of the function.
|
745
|
+
# of the function. If the :proc option is true the "function" is assumed
|
696
746
|
# to be a procedure
|
697
747
|
#
|
698
|
-
def call(name, *args,
|
699
|
-
args_seq = quote_values(args,
|
748
|
+
def call(name, *args, silent: self.silent, proc: false, **opts) # :proc may interfere with hashes
|
749
|
+
args_seq = quote_values(args, **opts)
|
700
750
|
if proc
|
701
751
|
pg_exec "call #{name}(#{args_seq})", silent: silent
|
702
752
|
return nil
|
@@ -719,8 +769,8 @@ module PgConn
|
|
719
769
|
end
|
720
770
|
|
721
771
|
# Like #call with :proc set to true
|
722
|
-
def proc(name, *args, silent: self.silent)
|
723
|
-
call(name, *args, silent: silent, proc: true)
|
772
|
+
def proc(name, *args, json_type: self.default_json_type, silent: self.silent)
|
773
|
+
call(name, *args, silent: silent, proc: true, json_type: json_type)
|
724
774
|
end
|
725
775
|
|
726
776
|
# :call-seq:
|
@@ -859,7 +909,14 @@ module PgConn
|
|
859
909
|
#
|
860
910
|
# TODO: Make sure the transaction stack is emptied on postgres errors
|
861
911
|
def exec(sql, commit: true, fail: true, silent: self.silent)
|
862
|
-
transaction(commit: commit) {
|
912
|
+
transaction(commit: commit) {
|
913
|
+
begin
|
914
|
+
execute(sql, fail: fail, silent: silent)
|
915
|
+
rescue PG::Error
|
916
|
+
cancel_transaction
|
917
|
+
raise
|
918
|
+
end
|
919
|
+
}
|
863
920
|
end
|
864
921
|
|
865
922
|
# Like #exec but returns true/false depending on if the command succeeded,
|
@@ -888,7 +945,6 @@ module PgConn
|
|
888
945
|
begin
|
889
946
|
pg_exec(sql, silent: silent)&.cmd_tuples
|
890
947
|
rescue PG::Error
|
891
|
-
cancel_transaction
|
892
948
|
raise if fail
|
893
949
|
return nil
|
894
950
|
end
|
@@ -954,9 +1010,9 @@ module PgConn
|
|
954
1010
|
# file instead of being executed. Maybe remove logging (or execute always
|
955
1011
|
# and log as a side-effect)
|
956
1012
|
if @pg_connection
|
957
|
-
|
958
|
-
|
959
|
-
|
1013
|
+
@timestamp, @timestamptz = @pg_connection.exec(
|
1014
|
+
'select current_timestamp::timestamp without time zone, current_timestamp'
|
1015
|
+
).tuple_values(0)
|
960
1016
|
end
|
961
1017
|
end
|
962
1018
|
end
|
@@ -987,9 +1043,16 @@ module PgConn
|
|
987
1043
|
# progress, the method always succeeds
|
988
1044
|
def cancel_transaction
|
989
1045
|
begin
|
990
|
-
|
1046
|
+
# The transaction may be invalid to we can't use #set_option to silence
|
1047
|
+
# warnings when the transaction is rolled back. Instead we manipulate the
|
1048
|
+
# procudure method
|
1049
|
+
saved_producer = @producers[:warning]
|
1050
|
+
@producers[:warning] = nil
|
1051
|
+
pg_exec("rollback", silent: true)
|
991
1052
|
rescue PG::Error
|
992
1053
|
;
|
1054
|
+
ensure
|
1055
|
+
@producers[:warning] = saved_producer
|
993
1056
|
end
|
994
1057
|
@savepoints = nil
|
995
1058
|
true
|
@@ -1018,7 +1081,6 @@ module PgConn
|
|
1018
1081
|
return nil
|
1019
1082
|
rescue PG::Error
|
1020
1083
|
cancel_transaction
|
1021
|
-
@savepoints = nil
|
1022
1084
|
raise
|
1023
1085
|
end
|
1024
1086
|
pop_transaction(commit: commit, fail: false)
|
@@ -1093,16 +1155,18 @@ module PgConn
|
|
1093
1155
|
end
|
1094
1156
|
|
1095
1157
|
# Common implementation for #quote_record and #quote_records that avoids
|
1096
|
-
# querying the database multiple times or duplication the code
|
1097
|
-
# flag is true when called via #quote_records
|
1158
|
+
# querying the database multiple times or duplication the code
|
1098
1159
|
#
|
1099
1160
|
# @data can be a Hash, Array, or OpenStruct. Hash keys must be symbols or
|
1100
1161
|
# strings, they belong to the same namespace so :k and "k" refer to the
|
1101
|
-
# same value
|
1162
|
+
# same value. The :array flag is true when called via #quote_records
|
1102
1163
|
#
|
1103
1164
|
# Note that #quote_record_impl queries the database for information about
|
1104
1165
|
# the type. TODO Cache this information?
|
1105
|
-
|
1166
|
+
#
|
1167
|
+
def quote_record_impl(datas, schema_name = nil, type, elem_types: nil, array: nil, **opts)
|
1168
|
+
datas = [datas] if !array
|
1169
|
+
|
1106
1170
|
pg_type = [schema_name, type].compact.join('.')
|
1107
1171
|
fields = self.values(%(
|
1108
1172
|
select attname
|
@@ -1114,8 +1178,6 @@ module PgConn
|
|
1114
1178
|
order by attnum
|
1115
1179
|
)).map(&:to_sym)
|
1116
1180
|
|
1117
|
-
datas = [datas] if !array
|
1118
|
-
|
1119
1181
|
literals = datas.map { |data|
|
1120
1182
|
values =
|
1121
1183
|
case data
|
@@ -1125,20 +1187,19 @@ module PgConn
|
|
1125
1187
|
else
|
1126
1188
|
raise Error, "Illegal value #{data.inspect}"
|
1127
1189
|
end
|
1128
|
-
"(#{quote_tuple(values, elem_types: elem_types)})::#{pg_type}"
|
1190
|
+
"(#{quote_tuple(values, elem_types: elem_types, **opts)})::#{pg_type}"
|
1129
1191
|
}
|
1130
1192
|
|
1131
1193
|
if array
|
1132
|
-
"array[#{literals.join(', ')}]::#{pg_type}[]"
|
1194
|
+
Literal.new "array[#{literals.join(', ')}]::#{pg_type}[]"
|
1133
1195
|
else
|
1134
|
-
literals.first
|
1196
|
+
Literal.new literals.first
|
1135
1197
|
end
|
1136
1198
|
end
|
1137
1199
|
|
1138
1200
|
# :call-seq
|
1139
1201
|
# parse_query(query)
|
1140
1202
|
# parse_query(table, id, fields...)
|
1141
|
-
# parse_query(table, where_clause, fields...)
|
1142
1203
|
# parse_query(table, hash, fields...)
|
1143
1204
|
# parse_query(table, fields...)
|
1144
1205
|
#
|
@@ -1172,45 +1233,58 @@ module PgConn
|
|
1172
1233
|
end
|
1173
1234
|
|
1174
1235
|
STDOUT_PRODUCER = lambda { |msg| $stdout.puts msg }
|
1175
|
-
STDERR_PRODUCER = lambda { |msg| $stderr.puts msg }
|
1236
|
+
STDERR_PRODUCER = lambda { |msg| $stderr.puts msg; $stderr.flush }
|
1237
|
+
ERROR_PRODUCER = lambda { |msg, stmt| $stderr.puts stmt, nil, msg; $stderr.flush }
|
1238
|
+
|
1239
|
+
# Map from message level to default producer. Note that we rely on the key
|
1240
|
+
# order to determine the minimum message level in #set_option below
|
1241
|
+
DEFAULT_PRODUCER = {
|
1242
|
+
debug: STDERR_PRODUCER,
|
1243
|
+
info: STDOUT_PRODUCER,
|
1244
|
+
notice: STDERR_PRODUCER,
|
1245
|
+
warning: STDERR_PRODUCER,
|
1246
|
+
error: STDERR_PRODUCER
|
1247
|
+
}
|
1176
1248
|
|
1177
1249
|
# FIXME Fails if connection is copied - requires a common options object
|
1178
1250
|
# that is shared between all connection copies
|
1179
1251
|
def set_option(option, value)
|
1252
|
+
# Assign default
|
1253
|
+
value = @default_options[option] if value.nil?
|
1254
|
+
|
1255
|
+
# Find current message level
|
1256
|
+
old_level = DEFAULT_PRODUCER.keys.find { |level| @producers[level] } || :error
|
1257
|
+
|
1258
|
+
# Set new value. Can be true, false, or a Proc object
|
1259
|
+
@options[option] = value
|
1260
|
+
|
1261
|
+
# Set producer
|
1180
1262
|
case option
|
1181
1263
|
when :silent
|
1182
|
-
@
|
1264
|
+
@producers[:silent] =
|
1183
1265
|
case value
|
1184
|
-
when true
|
1185
|
-
when
|
1266
|
+
when true; nil
|
1267
|
+
when false; ERROR_PRODUCER
|
1268
|
+
when Proc; value
|
1186
1269
|
else
|
1187
|
-
raise ArgumentError, "Illegal value #{value.inspect}"
|
1270
|
+
raise ArgumentError, "Illegal value: #{value.inspect}"
|
1188
1271
|
end
|
1189
|
-
when :notice, :warning
|
1190
|
-
@
|
1272
|
+
when :debug, :info, :notice, :warning
|
1273
|
+
@producers[option] =
|
1191
1274
|
case value
|
1192
|
-
when true; option
|
1275
|
+
when true; DEFAULT_PRODUCER[option]
|
1193
1276
|
when false; nil
|
1194
|
-
when nil; @default_options[option]
|
1195
1277
|
when Proc; value
|
1196
1278
|
else
|
1197
1279
|
raise ArgumentError, "Illegal value #{value.inspect}"
|
1198
1280
|
end
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
@pg_connection.exec "set client_min_messages to error"
|
1207
|
-
end
|
1208
|
-
elsif !notice
|
1209
|
-
if enabled
|
1210
|
-
@pg_connection.exec "set client_min_messages to notice"
|
1211
|
-
else
|
1212
|
-
@pg_connection.exec "set client_min_messages to error"
|
1213
|
-
end
|
1281
|
+
|
1282
|
+
# Find new message level
|
1283
|
+
new_level = DEFAULT_PRODUCER.keys.find { |level| @producers[level] } || :error
|
1284
|
+
|
1285
|
+
# Adjust postgres message level if changed
|
1286
|
+
if old_level != new_level
|
1287
|
+
@pg_connection.exec "set client_min_messages to #{new_level}"
|
1214
1288
|
end
|
1215
1289
|
else
|
1216
1290
|
raise ArgumentError, "Illegal option: #{option.inspect}"
|
@@ -1224,10 +1298,13 @@ module PgConn
|
|
1224
1298
|
def get_options() @options.dup end
|
1225
1299
|
|
1226
1300
|
# Called from postgres. Installed in #initialize
|
1227
|
-
def message_processor(message)
|
1301
|
+
def message_processor(message, stmt = nil)
|
1228
1302
|
case message
|
1229
|
-
when /^
|
1230
|
-
when /^
|
1303
|
+
when /^DEBUG:\s*(.*)$/; @producers[:debug]&.call($1)
|
1304
|
+
when /^INFO:\s*(.*)$/; @producers[:info]&.call($1)
|
1305
|
+
when /^NOTICE:\s*(.*)$/; @producers[:notice]&.call($1)
|
1306
|
+
when /^WARNING:\s*(.*)$/; @producers[:warning]&.call($1)
|
1307
|
+
when /^ERROR:\s*(.*)$/m; @producers[:silent]&.call($1, stmt)
|
1231
1308
|
else
|
1232
1309
|
raise "Oops"
|
1233
1310
|
end
|
@@ -1255,6 +1332,10 @@ module PgConn
|
|
1255
1332
|
def pg_exec(arg, silent: self.silent)
|
1256
1333
|
if @pg_connection
|
1257
1334
|
begin
|
1335
|
+
# Set silent state
|
1336
|
+
saved_silent = self.silent
|
1337
|
+
self.silent = silent
|
1338
|
+
|
1258
1339
|
last_stmt = nil # To make the current SQL statement visible to the rescue clause. FIXME Not used?
|
1259
1340
|
if arg.is_a?(String)
|
1260
1341
|
return nil if arg == ""
|
@@ -1272,12 +1353,11 @@ module PgConn
|
|
1272
1353
|
@error = ex
|
1273
1354
|
@err = nil
|
1274
1355
|
end
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
end
|
1356
|
+
|
1357
|
+
# Process message before resetting silent state
|
1358
|
+
message_processor(@error.message, arg)
|
1359
|
+
self.silent = saved_silent
|
1360
|
+
|
1281
1361
|
raise
|
1282
1362
|
end
|
1283
1363
|
|
@@ -1313,16 +1393,15 @@ module PgConn
|
|
1313
1393
|
def self.sql_values(values) "'" + values.join("', '") + "'" end
|
1314
1394
|
def self.sql_idents(values) '"' + values.join('", "') + '"' end
|
1315
1395
|
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
# Same as postgres PG#escape_identifier but do not need a connection
|
1320
|
-
def self.escape_identifier(s)
|
1321
|
-
!s.nil? or raise TypeError, "Identifier can't be nil"
|
1322
|
-
s.is_a?(String) or raise TypeError, "Identifier can't be a #{s.class}"
|
1323
|
-
s !~ /\A\s*\z/ or raise TypeError, "Identifier be blank"
|
1324
|
-
%("#{s.gsub('"', '""')}")
|
1325
|
-
end
|
1396
|
+
# Same as postgres PG#escape_literal but do not need a connection
|
1397
|
+
def self.escape_literal(s) = s.nil? ? 'NULL' : "'#{s.gsub("'", "''")}'"
|
1326
1398
|
|
1399
|
+
# Same as postgres PG#escape_identifier but do not need a connection
|
1400
|
+
def self.escape_identifier(s)
|
1401
|
+
!s.nil? or raise TypeError, "Identifier can't be nil"
|
1402
|
+
s.is_a?(String) or raise TypeError, "Identifier can't be a #{s.class}"
|
1403
|
+
s !~ /\A\s*\z/ or raise TypeError, "Identifier be blank"
|
1404
|
+
%("#{s.gsub('"', '""')}")
|
1405
|
+
end
|
1327
1406
|
end
|
1328
1407
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_conn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.36.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|