pg_conn 0.34.0 → 0.35.1
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 +203 -101
- 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: 7c834402fcec956ffcb288fa201d7a6bade6b435002ae4758e1d86ea54f5a656
|
4
|
+
data.tar.gz: 20fafcbbe4c9f93ab426c6012e91f073e5224ab84d23948da894f4a64b5ef7c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1da57a0c7f8daffdaac879899d3bd7d9723a6512e038afda6996329717d9b0246f7b646161ee1f0a82e00d828a12b9328f212ef98b47132914e4c66dc9f1e888
|
7
|
+
data.tar.gz: 4fd13dd8b4130ebb417379dd3ec62a3476f7d721a7617d14ae2f5b88a0c1f13812dbd440931bc14c02c3db702f9ff181a8b4ab3116a08527293ae6b632c9cd0b
|
data/lib/pg_conn/version.rb
CHANGED
data/lib/pg_conn.rb
CHANGED
@@ -53,9 +53,9 @@ module PgConn
|
|
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 them 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,7 +66,7 @@ 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)
|
69
|
+
def self.quote_value(value, elem_type: nil, json_type: nil)
|
70
70
|
case value
|
71
71
|
when Literal; value
|
72
72
|
when String; escape_literal(value)
|
@@ -82,32 +82,34 @@ module PgConn
|
|
82
82
|
else
|
83
83
|
"array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
|
84
84
|
end
|
85
|
-
when Hash; "'#{value.to_json}'"
|
85
|
+
when Hash; ["'#{value.to_json}'", json_type].compact.join('::')
|
86
86
|
else
|
87
87
|
escape_literal(value.to_s)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
# Quote values and concatenate them using ',' as separator
|
92
|
-
def self.quote_values(values,
|
93
|
-
values.map { |value| quote_value(value,
|
92
|
+
def self.quote_values(values, **opts)
|
93
|
+
values.map { |value| quote_value(value, **opts) }.join(", ")
|
94
94
|
end
|
95
95
|
|
96
96
|
# Quote an array of values as a tuple. The element types should be in the
|
97
97
|
# same order as the array arguments. #quote_tuples is same as #quote_values
|
98
98
|
# except the values may have different types (this makes no difference
|
99
99
|
# except in the case when the tuple may contain empty array(s))
|
100
|
-
|
100
|
+
#
|
101
|
+
# Note that it is :elem_types (plural) and not :elem_type
|
102
|
+
def self.quote_tuple(tuple, elem_types: nil, **opts)
|
101
103
|
elem_types = Array(elem_types)
|
102
104
|
tuple.map { |value|
|
103
105
|
elem_type = value.is_a?(Array) ? elem_types&.shift : nil
|
104
|
-
quote_value(value, elem_type: elem_type)
|
106
|
+
quote_value(value, **opts, elem_type: elem_type)
|
105
107
|
}.join(", ")
|
106
108
|
end
|
107
109
|
|
108
110
|
# Quote an array of tuples
|
109
|
-
def self.quote_tuples(tuples,
|
110
|
-
tuples.map { |tuple| "(#{quote_tuple(tuple,
|
111
|
+
def self.quote_tuples(tuples, **opts)
|
112
|
+
tuples.map { |tuple| "(#{quote_tuple(tuple, **opts)})" }.join(", ")
|
111
113
|
end
|
112
114
|
|
113
115
|
# Used to mark strings as literals that should not be quoted. This is the
|
@@ -125,6 +127,9 @@ module PgConn
|
|
125
127
|
# The class of column names (Symbol or String). Default is Symbol
|
126
128
|
attr_reader :field_name_class
|
127
129
|
|
130
|
+
# Default postgres JSON type (either 'json' or 'jsonb'). Default is 'jsonb'
|
131
|
+
attr_reader :default_json_type
|
132
|
+
|
128
133
|
# Name of user
|
129
134
|
def user() @pg_connection.user end
|
130
135
|
alias_method :username, :user # Obsolete FIXME Is it?
|
@@ -155,19 +160,55 @@ module PgConn
|
|
155
160
|
# #exec or #transaction block. The timestamp includes the current time zone
|
156
161
|
attr_reader :timestamptz
|
157
162
|
|
158
|
-
# Controls
|
163
|
+
# Controls error messages. It can be assigned true, false, nil, or a Proc
|
159
164
|
# object that recieves the message. True causes the message to be printed
|
160
|
-
# to standard
|
161
|
-
# default given when the connection was initialized
|
162
|
-
|
165
|
+
# to standard error, false ignores it, and nil resets the state to the
|
166
|
+
# default given when the connection was initialized. #silent? returns true
|
167
|
+
# if #silent is false or a Proc object and should be used instead #silent
|
168
|
+
# to check the state because #silent returns truish when output is
|
169
|
+
# redirected to a Proc
|
170
|
+
#
|
171
|
+
# Note that #silent=, #notice=, and warning= only controls the error
|
172
|
+
# message, the exception is passed through unaltered
|
173
|
+
#
|
174
|
+
def silent() @options[:silent] end
|
175
|
+
def silent?() @producers[:silent].nil? end # silent == false/Proc is true
|
176
|
+
def silent=(value) set_option(:silent, value) end
|
177
|
+
|
178
|
+
# Controls warnings. It can be assigned true, false, nil, or a Proc object
|
179
|
+
# that recieves the message. True causes the message to be printed to
|
180
|
+
# standard error, false ignores it, and nil resets the state to the default
|
181
|
+
# given when the connection was initialized
|
182
|
+
def warning() @options[:warning] end
|
183
|
+
def warning?() !warning.nil? end
|
184
|
+
def warning=(value) set_option(:warning, value) end
|
185
|
+
|
186
|
+
# Controls notice messages. It can be assigned true, false, nil, or a Proc
|
187
|
+
# object that recieves the message. True causes the message to be printed
|
188
|
+
# to standard error, false ignores it, and nil resets the state to the
|
189
|
+
# default given when the connection was initialized. Default false
|
163
190
|
def notice() @options[:notice] end
|
191
|
+
def notice?() !notice.nil? end
|
192
|
+
def notice=(value) set_option(:notice, value) end
|
193
|
+
|
194
|
+
# Controls info messages. It can be assigned true, false, nil, or a Proc
|
195
|
+
# object that recieves the message. True causes the message to be printed
|
196
|
+
# to standard output, false ignores it, and nil resets the state to the
|
197
|
+
# default given when the connection was initialized. Default false. Note
|
198
|
+
# that #info is the only level that outputs to standard output
|
199
|
+
def info() @options[:info] end
|
200
|
+
def info?() !info.nil? end
|
201
|
+
def info=(value) set_option(:info, value) end
|
164
202
|
|
165
|
-
# Controls
|
203
|
+
# Controls debug messages. It can be assigned true, false, nil, or a Proc
|
166
204
|
# object that recieves the message. True causes the message to be printed
|
167
205
|
# to standard error, false ignores it, and nil resets the state to the
|
168
|
-
# default given when the connection was initialized
|
169
|
-
def
|
170
|
-
def
|
206
|
+
# default given when the connection was initialized. Default false
|
207
|
+
def debug() @options[:debug] end
|
208
|
+
def debug?() !debug.nil? end
|
209
|
+
def debug=(value) set_option(:debug, value) end
|
210
|
+
|
211
|
+
DEFAULT_OPTIONS = { silent: false, warning: true, notice: false, info: false, debug: false }
|
171
212
|
|
172
213
|
# TODO: Move error message handling into the same framework as notice and
|
173
214
|
# warning but we have a name collision just below that would need to be
|
@@ -206,8 +247,6 @@ module PgConn
|
|
206
247
|
# if absent in the Postgres error message
|
207
248
|
def errchar = err[2]
|
208
249
|
|
209
|
-
DEFAULT_OPTIONS = { notice: false, warning: false }
|
210
|
-
|
211
250
|
# :call-seq:
|
212
251
|
# initialize(dbname = nil, user = nil, **options)
|
213
252
|
# initialize(connection_hash, **options)
|
@@ -239,8 +278,10 @@ module PgConn
|
|
239
278
|
# Symbol (the default) or String. The :timestamp option is used
|
240
279
|
# internally to set the timestamp for transactions
|
241
280
|
#
|
242
|
-
# The :notice and :warning options sets the default output handling this
|
243
|
-
# connection (FIXME fails on copied connections)
|
281
|
+
# The :notice and :warning options sets the default output handling for this
|
282
|
+
# connection (FIXME fails on copied connections). Default is to suppress
|
283
|
+
# notices and lower - this is diffent from postgres that by default include
|
284
|
+
# notices
|
244
285
|
#
|
245
286
|
# Note that the connection hash and the connection string may support more
|
246
287
|
# parameters than documented here. Consult
|
@@ -255,7 +296,17 @@ module PgConn
|
|
255
296
|
if args.last.is_a?(Hash)
|
256
297
|
opts = args.last
|
257
298
|
@field_name_class = opts.delete(:field_name_class) || Symbol
|
258
|
-
|
299
|
+
|
300
|
+
# Extract options from arguments
|
301
|
+
options = DEFAULT_OPTIONS.to_h { |k,v|
|
302
|
+
r = if opts.key?(k)
|
303
|
+
value = opts.delete(k)
|
304
|
+
value.nil? ? v : value
|
305
|
+
else
|
306
|
+
v
|
307
|
+
end
|
308
|
+
[k, r]
|
309
|
+
}
|
259
310
|
|
260
311
|
# FIXME: Is this used?
|
261
312
|
@timestamp = opts.delete(:timestamp)
|
@@ -264,7 +315,7 @@ module PgConn
|
|
264
315
|
args.pop if opts.empty?
|
265
316
|
else
|
266
317
|
@field_name_class = Symbol
|
267
|
-
options = DEFAULT_OPTIONS
|
318
|
+
options = DEFAULT_OPTIONS.dup
|
268
319
|
end
|
269
320
|
|
270
321
|
# else # We assume that the current user is a postgres superuser
|
@@ -314,7 +365,8 @@ module PgConn
|
|
314
365
|
# type "uuid" with oid 2950..' warnings
|
315
366
|
type_map.default_type_map = PG::TypeMapAllStrings.new
|
316
367
|
|
317
|
-
# Timestamp decoder. FIXME What is this?
|
368
|
+
# Timestamp decoder. FIXME What is this? Why only Timestamp and not
|
369
|
+
# Timestamptz?
|
318
370
|
type_map.add_coder PG::TextDecoder::Timestamp.new( # Timestamp without time zone
|
319
371
|
oid: 1114,
|
320
372
|
flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC)
|
@@ -329,12 +381,19 @@ module PgConn
|
|
329
381
|
|
330
382
|
@pg_connection.type_map_for_results = type_map
|
331
383
|
@pg_connection.field_name_type = @field_name_class.to_s.downcase.to_sym # Use symbol field names
|
332
|
-
|
333
|
-
@options, @default_options = {}, DEFAULT_OPTIONS
|
334
|
-
set_options(options) # require @default_options
|
335
|
-
@default_options = @options.dup
|
336
384
|
end
|
337
385
|
|
386
|
+
# Set options. The initial options also serves as default values and are
|
387
|
+
# themselves initialized using DEFAULT_VALUES
|
388
|
+
#
|
389
|
+
# Note that options is initialized even if there is no connection to
|
390
|
+
# avoid special casing
|
391
|
+
@default_options = options
|
392
|
+
@options = {}
|
393
|
+
@producers = {} # Map from message level to Proc or nil
|
394
|
+
set_options(@default_options) if @pg_connection
|
395
|
+
|
396
|
+
@default_json_type = :jsonb
|
338
397
|
@schema = SchemaMethods.new(self)
|
339
398
|
@role = RoleMethods.new(self)
|
340
399
|
@rdbms = RdbmsMethods.new(self)
|
@@ -375,12 +434,13 @@ module PgConn
|
|
375
434
|
def literal(arg) Literal.new(arg) end
|
376
435
|
|
377
436
|
# Connection member method variations of the PgConn quote class methods
|
437
|
+
# with at least a default value for :json_type
|
378
438
|
def quote_identifier(s) = PgConn.quote_identifier(s)
|
379
439
|
def quote_identifiers(idents) = PgConn.quote_identifiers(idents)
|
380
|
-
def quote_value(value, **opts) = PgConn.quote_value(value, **opts)
|
381
|
-
def quote_values(values, **opts) = PgConn.quote_values(values, **opts)
|
382
|
-
def quote_tuple(tuple, **opts) = PgConn.quote_tuple(tuple, **opts)
|
383
|
-
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)
|
384
444
|
|
385
445
|
# Quote a record and cast it into the given type, the type can also be a
|
386
446
|
# table or view. 'data' is an array, hash, or struct representation of the
|
@@ -393,14 +453,14 @@ module PgConn
|
|
393
453
|
#
|
394
454
|
# Also note that there is not class-method variant of this method because
|
395
455
|
# it requires a connection
|
396
|
-
def quote_record(data, schema_name = nil, type,
|
397
|
-
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)
|
398
458
|
end
|
399
459
|
|
400
460
|
# Quote an array of records. The type is the record type, not the type of
|
401
461
|
# the enclosing array
|
402
|
-
def quote_records(data, schema_name = nil, type,
|
403
|
-
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)
|
404
464
|
end
|
405
465
|
|
406
466
|
# :call-seq:
|
@@ -682,11 +742,11 @@ module PgConn
|
|
682
742
|
# columns (like #tuples).
|
683
743
|
#
|
684
744
|
# The name argument can be a String or a Symbol that may contain the schema
|
685
|
-
# of the function.
|
745
|
+
# of the function. If the :proc option is true the "function" is assumed
|
686
746
|
# to be a procedure
|
687
747
|
#
|
688
|
-
def call(name, *args,
|
689
|
-
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)
|
690
750
|
if proc
|
691
751
|
pg_exec "call #{name}(#{args_seq})", silent: silent
|
692
752
|
return nil
|
@@ -709,8 +769,8 @@ module PgConn
|
|
709
769
|
end
|
710
770
|
|
711
771
|
# Like #call with :proc set to true
|
712
|
-
def proc(name, *args, silent:
|
713
|
-
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)
|
714
774
|
end
|
715
775
|
|
716
776
|
# :call-seq:
|
@@ -818,7 +878,7 @@ module PgConn
|
|
818
878
|
end
|
819
879
|
|
820
880
|
# Execute block with global options and resets afterwards. Currently only
|
821
|
-
# :notice and :warning is supported. Very useful in RSpec tests
|
881
|
+
# :silent, :notice and :warning is supported. Very useful in RSpec tests
|
822
882
|
#
|
823
883
|
# TODO: :error, :fail, :symbol, :schema, :search_path
|
824
884
|
#
|
@@ -848,14 +908,21 @@ module PgConn
|
|
848
908
|
# transaction stack has collapsed
|
849
909
|
#
|
850
910
|
# TODO: Make sure the transaction stack is emptied on postgres errors
|
851
|
-
def exec(sql, commit: true, fail: true, silent:
|
852
|
-
transaction(commit: commit) {
|
911
|
+
def exec(sql, commit: true, fail: true, silent: self.silent)
|
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
|
+
}
|
853
920
|
end
|
854
921
|
|
855
|
-
# Like #exec but returns true/false depending on if the command succeeded
|
856
|
-
#
|
857
|
-
#
|
858
|
-
# should be captured
|
922
|
+
# Like #exec but returns true/false depending on if the command succeeded,
|
923
|
+
# error messages are suppressed by default. There is no corresponding
|
924
|
+
# #execute? method because any failure rolls back the whole transaction
|
925
|
+
# stack. TODO: Check which exceptions that should be captured
|
859
926
|
def exec?(sql, commit: true, silent: true)
|
860
927
|
begin
|
861
928
|
exec(sql, commit: commit, fail: true, silent: silent)
|
@@ -873,12 +940,11 @@ module PgConn
|
|
873
940
|
# unless :fail is false in which case it returns nil
|
874
941
|
#
|
875
942
|
# TODO: Handle postgres exceptions wrt transaction state and stack
|
876
|
-
def execute(sql, fail: true, silent:
|
943
|
+
def execute(sql, fail: true, silent: self.silent)
|
877
944
|
if @pg_connection
|
878
945
|
begin
|
879
946
|
pg_exec(sql, silent: silent)&.cmd_tuples
|
880
947
|
rescue PG::Error
|
881
|
-
cancel_transaction
|
882
948
|
raise if fail
|
883
949
|
return nil
|
884
950
|
end
|
@@ -944,9 +1010,9 @@ module PgConn
|
|
944
1010
|
# file instead of being executed. Maybe remove logging (or execute always
|
945
1011
|
# and log as a side-effect)
|
946
1012
|
if @pg_connection
|
947
|
-
|
948
|
-
|
949
|
-
|
1013
|
+
@timestamp, @timestamptz = @pg_connection.exec(
|
1014
|
+
'select current_timestamp::timestamp without time zone, current_timestamp'
|
1015
|
+
).tuple_values(0)
|
950
1016
|
end
|
951
1017
|
end
|
952
1018
|
end
|
@@ -977,9 +1043,16 @@ module PgConn
|
|
977
1043
|
# progress, the method always succeeds
|
978
1044
|
def cancel_transaction
|
979
1045
|
begin
|
980
|
-
|
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)
|
981
1052
|
rescue PG::Error
|
982
1053
|
;
|
1054
|
+
ensure
|
1055
|
+
@producers[:warning] = saved_producer
|
983
1056
|
end
|
984
1057
|
@savepoints = nil
|
985
1058
|
true
|
@@ -994,6 +1067,9 @@ module PgConn
|
|
994
1067
|
# PgConn::Rollback exception in which case #transaction returns nil. Note
|
995
1068
|
# that the transaction timestamp is set to the start of the first
|
996
1069
|
# transaction even if transactions are nested
|
1070
|
+
#
|
1071
|
+
# FIXME: There is some strange problem in rspec where an #insert handles
|
1072
|
+
# an exception correctly while #exec, #execute, and #transaction does not
|
997
1073
|
def transaction(commit: true, &block)
|
998
1074
|
if block_given?
|
999
1075
|
result = nil
|
@@ -1005,7 +1081,6 @@ module PgConn
|
|
1005
1081
|
return nil
|
1006
1082
|
rescue PG::Error
|
1007
1083
|
cancel_transaction
|
1008
|
-
@savepoints = nil
|
1009
1084
|
raise
|
1010
1085
|
end
|
1011
1086
|
pop_transaction(commit: commit, fail: false)
|
@@ -1089,7 +1164,10 @@ module PgConn
|
|
1089
1164
|
#
|
1090
1165
|
# Note that #quote_record_impl queries the database for information about
|
1091
1166
|
# the type. TODO Cache this information?
|
1092
|
-
def quote_record_impl(
|
1167
|
+
def quote_record_impl(
|
1168
|
+
datas, schema_name = nil, type, elem_types: nil, array: nil, **opts)
|
1169
|
+
datas = [datas] if !array
|
1170
|
+
|
1093
1171
|
pg_type = [schema_name, type].compact.join('.')
|
1094
1172
|
fields = self.values(%(
|
1095
1173
|
select attname
|
@@ -1101,8 +1179,6 @@ module PgConn
|
|
1101
1179
|
order by attnum
|
1102
1180
|
)).map(&:to_sym)
|
1103
1181
|
|
1104
|
-
datas = [datas] if !array
|
1105
|
-
|
1106
1182
|
literals = datas.map { |data|
|
1107
1183
|
values =
|
1108
1184
|
case data
|
@@ -1112,10 +1188,11 @@ module PgConn
|
|
1112
1188
|
else
|
1113
1189
|
raise Error, "Illegal value #{data.inspect}"
|
1114
1190
|
end
|
1115
|
-
"(#{quote_tuple(values, elem_types: elem_types)})::#{pg_type}"
|
1191
|
+
"(#{quote_tuple(values, elem_types: elem_types, **opts)})::#{pg_type}"
|
1116
1192
|
}
|
1117
1193
|
|
1118
1194
|
if array
|
1195
|
+
# papg_type(table, where_clause, fields...)
|
1119
1196
|
"array[#{literals.join(', ')}]::#{pg_type}[]"
|
1120
1197
|
else
|
1121
1198
|
literals.first
|
@@ -1125,7 +1202,6 @@ module PgConn
|
|
1125
1202
|
# :call-seq
|
1126
1203
|
# parse_query(query)
|
1127
1204
|
# parse_query(table, id, fields...)
|
1128
|
-
# parse_query(table, where_clause, fields...)
|
1129
1205
|
# parse_query(table, hash, fields...)
|
1130
1206
|
# parse_query(table, fields...)
|
1131
1207
|
#
|
@@ -1160,36 +1236,57 @@ module PgConn
|
|
1160
1236
|
|
1161
1237
|
STDOUT_PRODUCER = lambda { |msg| $stdout.puts msg }
|
1162
1238
|
STDERR_PRODUCER = lambda { |msg| $stderr.puts msg }
|
1239
|
+
ERROR_PRODUCER = lambda { |msg, stmt| $stderr.puts stmt, nil, msg; $stderr.flush }
|
1240
|
+
|
1241
|
+
# Map from message level to default producer. Note that we rely on the key
|
1242
|
+
# order to determine the minimum message level in #set_option below
|
1243
|
+
DEFAULT_PRODUCER = {
|
1244
|
+
debug: STDERR_PRODUCER,
|
1245
|
+
info: STDOUT_PRODUCER,
|
1246
|
+
notice: STDERR_PRODUCER,
|
1247
|
+
warning: STDERR_PRODUCER,
|
1248
|
+
error: STDERR_PRODUCER
|
1249
|
+
}
|
1163
1250
|
|
1164
1251
|
# FIXME Fails if connection is copied - requires a common options object
|
1165
1252
|
# that is shared between all connection copies
|
1166
1253
|
def set_option(option, value)
|
1254
|
+
# Assign default
|
1255
|
+
value = @default_options[option] if value.nil?
|
1256
|
+
|
1257
|
+
# Find current message level
|
1258
|
+
old_level = DEFAULT_PRODUCER.keys.find { |level| @producers[level] } || :error
|
1259
|
+
|
1260
|
+
# Set new value. Can be true, false, or a Proc object
|
1261
|
+
@options[option] = value
|
1262
|
+
|
1263
|
+
# Set producer
|
1167
1264
|
case option
|
1168
|
-
when :
|
1169
|
-
@
|
1265
|
+
when :silent
|
1266
|
+
@producers[:silent] =
|
1170
1267
|
case value
|
1171
|
-
when true;
|
1268
|
+
when true; nil
|
1269
|
+
when false; ERROR_PRODUCER
|
1270
|
+
when Proc; value
|
1271
|
+
else
|
1272
|
+
raise ArgumentError, "Illegal value: #{value.inspect}"
|
1273
|
+
end
|
1274
|
+
when :debug, :info, :notice, :warning
|
1275
|
+
@producers[option] =
|
1276
|
+
case value
|
1277
|
+
when true; DEFAULT_PRODUCER[option]
|
1172
1278
|
when false; nil
|
1173
|
-
when nil; @default_options[option]
|
1174
1279
|
when Proc; value
|
1175
1280
|
else
|
1176
1281
|
raise ArgumentError, "Illegal value #{value.inspect}"
|
1177
1282
|
end
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
@pg_connection.exec "set client_min_messages to error"
|
1186
|
-
end
|
1187
|
-
elsif !notice
|
1188
|
-
if enabled
|
1189
|
-
@pg_connection.exec "set client_min_messages to notice"
|
1190
|
-
else
|
1191
|
-
@pg_connection.exec "set client_min_messages to error"
|
1192
|
-
end
|
1283
|
+
|
1284
|
+
# Find new message level
|
1285
|
+
new_level = DEFAULT_PRODUCER.keys.find { |level| @producers[level] } || :error
|
1286
|
+
|
1287
|
+
# Adjust postgres message level if changed
|
1288
|
+
if old_level != new_level
|
1289
|
+
@pg_connection.exec "set client_min_messages to #{new_level}"
|
1193
1290
|
end
|
1194
1291
|
else
|
1195
1292
|
raise ArgumentError, "Illegal option: #{option.inspect}"
|
@@ -1203,10 +1300,13 @@ module PgConn
|
|
1203
1300
|
def get_options() @options.dup end
|
1204
1301
|
|
1205
1302
|
# Called from postgres. Installed in #initialize
|
1206
|
-
def message_processor(message)
|
1303
|
+
def message_processor(message, stmt = nil)
|
1207
1304
|
case message
|
1208
|
-
when /^
|
1209
|
-
when /^
|
1305
|
+
when /^DEBUG:\s*(.*)$/; @producers[:debug]&.call($1)
|
1306
|
+
when /^INFO:\s*(.*)$/; @producers[:info]&.call($1)
|
1307
|
+
when /^NOTICE:\s*(.*)$/; @producers[:notice]&.call($1)
|
1308
|
+
when /^WARNING:\s*(.*)$/; @producers[:warning]&.call($1)
|
1309
|
+
when /^ERROR:\s*(.*)$/m; @producers[:silent]&.call($1, stmt)
|
1210
1310
|
else
|
1211
1311
|
raise "Oops"
|
1212
1312
|
end
|
@@ -1231,9 +1331,13 @@ module PgConn
|
|
1231
1331
|
# though
|
1232
1332
|
#
|
1233
1333
|
# TODO: Fix silent by not handling exceptions
|
1234
|
-
def pg_exec(arg, silent:
|
1334
|
+
def pg_exec(arg, silent: self.silent)
|
1235
1335
|
if @pg_connection
|
1236
1336
|
begin
|
1337
|
+
# Set silent state
|
1338
|
+
saved_silent = self.silent
|
1339
|
+
self.silent = silent
|
1340
|
+
|
1237
1341
|
last_stmt = nil # To make the current SQL statement visible to the rescue clause. FIXME Not used?
|
1238
1342
|
if arg.is_a?(String)
|
1239
1343
|
return nil if arg == ""
|
@@ -1251,12 +1355,11 @@ module PgConn
|
|
1251
1355
|
@error = ex
|
1252
1356
|
@err = nil
|
1253
1357
|
end
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
end
|
1358
|
+
|
1359
|
+
# Process message before resetting silent state
|
1360
|
+
message_processor(@error.message, arg)
|
1361
|
+
self.silent = saved_silent
|
1362
|
+
|
1260
1363
|
raise
|
1261
1364
|
end
|
1262
1365
|
|
@@ -1292,16 +1395,15 @@ module PgConn
|
|
1292
1395
|
def self.sql_values(values) "'" + values.join("', '") + "'" end
|
1293
1396
|
def self.sql_idents(values) '"' + values.join('", "') + '"' end
|
1294
1397
|
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
# Same as postgres PG#escape_identifier but do not need a connection
|
1299
|
-
def self.escape_identifier(s)
|
1300
|
-
!s.nil? or raise TypeError, "Identifier can't be nil"
|
1301
|
-
s.is_a?(String) or raise TypeError, "Identifier can't be a #{s.class}"
|
1302
|
-
s !~ /\A\s*\z/ or raise TypeError, "Identifier be blank"
|
1303
|
-
%("#{s.gsub('"', '""')}")
|
1304
|
-
end
|
1398
|
+
# Same as postgres PG#escape_literal but do not need a connection
|
1399
|
+
def self.escape_literal(s) = s.nil? ? 'NULL' : "'#{s.gsub("'", "''")}'"
|
1305
1400
|
|
1401
|
+
# Same as postgres PG#escape_identifier but do not need a connection
|
1402
|
+
def self.escape_identifier(s)
|
1403
|
+
!s.nil? or raise TypeError, "Identifier can't be nil"
|
1404
|
+
s.is_a?(String) or raise TypeError, "Identifier can't be a #{s.class}"
|
1405
|
+
s !~ /\A\s*\z/ or raise TypeError, "Identifier be blank"
|
1406
|
+
%("#{s.gsub('"', '""')}")
|
1407
|
+
end
|
1306
1408
|
end
|
1307
1409
|
|
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.35.1
|
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-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|