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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pg_conn/version.rb +1 -1
  3. data/lib/pg_conn.rb +203 -101
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d18d4244a00a767589287122f298966ccdd9e8d53a6467f84097df698be34a6
4
- data.tar.gz: 6ad5c4c5cf19459996d744e25627cd852d03fa1fa25688572c40de71a5a61100
3
+ metadata.gz: 7c834402fcec956ffcb288fa201d7a6bade6b435002ae4758e1d86ea54f5a656
4
+ data.tar.gz: 20fafcbbe4c9f93ab426c6012e91f073e5224ab84d23948da894f4a64b5ef7c9
5
5
  SHA512:
6
- metadata.gz: 9a476b27de6f211d02957c543cf2ee0742011e3d06a468a006fee143827c3cceb8ad32610ca5dee5692cf0230a4c188dd6a4a57335e1eb51d48bc23a9a52e7d3
7
- data.tar.gz: 89dcdcec1bdd52d6b2a6ce56ee688e77661f7d438187e289d26e9344d9d030450caea180e260348050c9abcfd34261465fa1d82cefbded641e16ffde708250b5
6
+ metadata.gz: 1da57a0c7f8daffdaac879899d3bd7d9723a6512e038afda6996329717d9b0246f7b646161ee1f0a82e00d828a12b9328f212ef98b47132914e4c66dc9f1e888
7
+ data.tar.gz: 4fd13dd8b4130ebb417379dd3ec62a3476f7d721a7617d14ae2f5b88a0c1f13812dbd440931bc14c02c3db702f9ff181a8b4ab3116a08527293ae6b632c9cd0b
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.34.0"
2
+ VERSION = "0.35.1"
3
3
  end
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. The result is a string
57
- # and it is the application's responsibility to cast them to either 'json'
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, elem_type: nil)
93
- values.map { |value| quote_value(value, elem_type: elem_type) }.join(", ")
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
- def self.quote_tuple(tuple, elem_types: nil)
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, elem_types: nil)
110
- tuples.map { |tuple| "(#{quote_tuple(tuple, elem_types: elem_types)})" }.join(", ")
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 notices. Valid values are true, false, nil, or a Proc
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 output, false ignores it, and nil resets the state to the
161
- # default given when the connection was initialized
162
- def notice=(value) set_option(:notice, value) end
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 warnings. Valid values are true, false, nil, or a Proc
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 warning=(value) set_option(:warning, value) end
170
- def warning() @options[:warning] end
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
- options = DEFAULT_OPTIONS.transform_values! { |k,v| opts.key?(k) ? opts.delete(k) : v }
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, elem_types: nil)
397
- quote_record_impl(data, schema_name, type, elem_types: elem_types, array: false)
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, elem_types: nil)
403
- quote_record_impl(data, schema_name, type, elem_types: elem_types, array: true)
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. If the :proc option is true the "function" is assumed
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, elem_type: nil, silent: false, proc: false) # :proc may interfere with hashes
689
- args_seq = quote_values(args, elem_type: elem_type)
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: false)
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: false)
852
- transaction(commit: commit) { execute(sql, fail: fail, silent: silent) }
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
- # There is not a corresponding #execute? method because any failure rolls
857
- # back the whole transaction stack. TODO: Check which exceptions that
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: false)
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
- @timestamp, @timestamptz = @pg_connection.exec(
948
- 'select current_timestamp::timestamp without time zone, current_timestamp'
949
- ).tuple_values(0)
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
- pg_exec("rollback")
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(datas, schema_name = nil, type, elem_types: nil, array: nil)
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 :notice, :warning
1169
- @options[option] =
1265
+ when :silent
1266
+ @producers[:silent] =
1170
1267
  case value
1171
- when true; option == :notice ? STDOUT_PRODUCER : STDERR_PRODUCER
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
- enabled = !@options[option].nil?
1179
- if option == :notice
1180
- if enabled
1181
- @pg_connection.exec "set client_min_messages to notice"
1182
- elsif warning
1183
- @pg_connection.exec "set client_min_messages to warning"
1184
- else
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 /^NOTICE:\s*(.*)$/; @options[:notice]&.call($1)
1209
- when /^WARNING:\s*(.*)$/; @options[:warning]&.call($1)
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: false)
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
- if !silent # FIXME Why do we handle this?
1255
- $stderr.puts arg
1256
- $stderr.puts
1257
- $stderr.puts ex.message
1258
- $stderr.flush
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
- # Same as postgres PG#escape_literal but do not need a connection
1296
- def self.escape_literal(s) = s.nil? ? 'NULL' : "'#{s.gsub("'", "''")}'"
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.34.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-08 00:00:00.000000000 Z
11
+ date: 2025-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg