pg_conn 0.33.0 → 0.34.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.
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 +134 -24
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d008acfbbae8f338d5f83bcdacc35b40b67b986b04068dc82b9ae7fd8053e8d4
4
- data.tar.gz: 617b133d7c15c9e358b23331883b3ec7228227d3d70db59b5e7a74b01f111bf5
3
+ metadata.gz: 6d18d4244a00a767589287122f298966ccdd9e8d53a6467f84097df698be34a6
4
+ data.tar.gz: 6ad5c4c5cf19459996d744e25627cd852d03fa1fa25688572c40de71a5a61100
5
5
  SHA512:
6
- metadata.gz: 9c742a5c22f61d1add1b77b0c50047c596d901416b32d202e16eddc38f6718fe5357550e029fe863406c0243d157632dbe61bea85dc9428d9b862f8ec512fe8f
7
- data.tar.gz: adae387af6cf778c870bdae60beb94d12634c079048748d146bab24b6035ce4295968f72467a3283be5afe79360306e22279ce94d1e36d5c893e90918cd5f952
6
+ metadata.gz: 9a476b27de6f211d02957c543cf2ee0742011e3d06a468a006fee143827c3cceb8ad32610ca5dee5692cf0230a4c188dd6a4a57335e1eb51d48bc23a9a52e7d3
7
+ data.tar.gz: 89dcdcec1bdd52d6b2a6ce56ee688e77661f7d438187e289d26e9344d9d030450caea180e260348050c9abcfd34261465fa1d82cefbded641e16ffde708250b5
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.33.0"
2
+ VERSION = "0.34.0"
3
3
  end
data/lib/pg_conn.rb CHANGED
@@ -147,13 +147,32 @@ module PgConn
147
147
  attr_reader :session
148
148
 
149
149
  # The transaction timestamp of the most recent SQL statement executed by
150
- # #exec or #transaction block. The timestamp is without time zone
150
+ # #exec or #transaction block. The timestamp is without time zone and WRONG
151
+ # in most cases
151
152
  attr_reader :timestamp
152
153
 
153
154
  # The transaction timestamp of the most recent SQL statement executed by
154
155
  # #exec or #transaction block. The timestamp includes the current time zone
155
156
  attr_reader :timestamptz
156
157
 
158
+ # Controls notices. Valid values are true, false, nil, or a Proc
159
+ # 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
163
+ def notice() @options[:notice] end
164
+
165
+ # Controls warnings. Valid values are true, false, nil, or a Proc
166
+ # object that recieves the message. True causes the message to be printed
167
+ # 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
171
+
172
+ # TODO: Move error message handling into the same framework as notice and
173
+ # warning but we have a name collision just below that would need to be
174
+ # resolved somehow
175
+
157
176
  # PG::Error object of the first failed statement in the transaction;
158
177
  # otherwise nil. It is cleared at the beginning of a transaction so be sure
159
178
  # to save it before you run any cleanup code that may initiate new
@@ -163,7 +182,7 @@ module PgConn
163
182
  # True if the transaction is in a error state
164
183
  def error?() !@error.nil? end
165
184
 
166
- # Tuple of error message, lineno, and charno of the error object where each
185
+ # Tuple of error message, lineno, and charno of the error object. Each
167
186
  # element defaults to nil if not found
168
187
  def err
169
188
  @err ||=
@@ -187,13 +206,17 @@ module PgConn
187
206
  # if absent in the Postgres error message
188
207
  def errchar = err[2]
189
208
 
209
+ DEFAULT_OPTIONS = { notice: false, warning: false }
210
+
190
211
  # :call-seq:
191
- # initialize(dbname = nil, user = nil, field_name_class: Symbol)
192
- # initialize(connection_hash, field_name_class: Symbol)
193
- # initialize(connection_string, field_name_class: Symbol)
194
- # initialize(host, port, dbname, user, password, field_name_class: Symbol)
195
- # initialize(array, field_name_class: Symbol)
196
- # initialize(pg_connection_object)
212
+ # initialize(dbname = nil, user = nil, **options)
213
+ # initialize(connection_hash, **options)
214
+ # initialize(connection_string, **options)
215
+ # initialize(host, port, dbname, user, password, **options)
216
+ # initialize(array, **options)
217
+ # initialize(pg_connection_object, **options)
218
+ #
219
+ # options can be :notice, :warning, :field_name_class, :timestamp, :timestamptz
197
220
  #
198
221
  # Initialize a connection object and connect to the database
199
222
  #
@@ -216,6 +239,9 @@ module PgConn
216
239
  # Symbol (the default) or String. The :timestamp option is used
217
240
  # internally to set the timestamp for transactions
218
241
  #
242
+ # The :notice and :warning options sets the default output handling this
243
+ # connection (FIXME fails on copied connections)
244
+ #
219
245
  # Note that the connection hash and the connection string may support more
220
246
  # parameters than documented here. Consult
221
247
  # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
@@ -223,13 +249,22 @@ module PgConn
223
249
  #
224
250
  # TODO: Change to 'initialize(*args, **opts)'
225
251
  def initialize(*args)
252
+ # Extract connection level options and leave other options (that should
253
+ # be the postgres authentication hash - postgres will emit an error if
254
+ # not)
226
255
  if args.last.is_a?(Hash)
227
- @field_name_class = args.last.delete(:field_name_class) || Symbol
228
- @timestamp = args.last.delete(:timestamp)
229
- @timestamptz = args.last.delete(:timestamptz)
230
- args.pop if args.last.empty?
256
+ opts = args.last
257
+ @field_name_class = opts.delete(:field_name_class) || Symbol
258
+ options = DEFAULT_OPTIONS.transform_values! { |k,v| opts.key?(k) ? opts.delete(k) : v }
259
+
260
+ # FIXME: Is this used?
261
+ @timestamp = opts.delete(:timestamp)
262
+ @timestamptz = opts.delete(:timestamptz)
263
+
264
+ args.pop if opts.empty?
231
265
  else
232
266
  @field_name_class = Symbol
267
+ options = DEFAULT_OPTIONS
233
268
  end
234
269
 
235
270
  # else # We assume that the current user is a postgres superuser
@@ -265,12 +300,12 @@ module PgConn
265
300
  elsif args.size == 5
266
301
  make_connection args[0], args[1], nil, nil, args[2], args[3], args[4]
267
302
  else
268
- raise Error, "Illegal number of parameters: #{args.size}"
303
+ raise Error, "Illegal number of arguments: #{args.size}"
269
304
  end
270
305
 
271
- if @pg_connection && !using_existing_connection
272
- # Set a dummy notice processor to avoid warnings on stderr
273
- @pg_connection.set_notice_processor { |message| ; } # Intentionally a nop
306
+ if @pg_connection #&& !using_existing_connection
307
+ # Set a notice processor that separates notices and warnings
308
+ @pg_connection.set_notice_processor { |message| message_processor(message) }
274
309
 
275
310
  # Auto-convert to ruby types
276
311
  type_map = PG::BasicTypeMapForResults.new(@pg_connection)
@@ -279,21 +314,25 @@ module PgConn
279
314
  # type "uuid" with oid 2950..' warnings
280
315
  type_map.default_type_map = PG::TypeMapAllStrings.new
281
316
 
282
- # Timestamp decoder
317
+ # Timestamp decoder. FIXME What is this?
283
318
  type_map.add_coder PG::TextDecoder::Timestamp.new( # Timestamp without time zone
284
319
  oid: 1114,
285
320
  flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC)
286
321
 
287
- # Decode anonymous records but note that this is only useful to convert the
288
- # outermost structure into an array, the elements are not decoded and are
289
- # returned as strings. It is best to avoid anonymous records if possible
322
+ # Decode anonymous records but note that this is only useful to convert
323
+ # the outermost structure into an array, the elements are not decoded
324
+ # and are returned as strings. It is best to avoid anonymous records if
325
+ # possible
290
326
  type_map.add_coder PG::TextDecoder::Record.new(
291
327
  oid: 2249
292
328
  )
293
329
 
294
330
  @pg_connection.type_map_for_results = type_map
295
331
  @pg_connection.field_name_type = @field_name_class.to_s.downcase.to_sym # Use symbol field names
296
- @pg_connection.exec "set client_min_messages to warning;" # Silence warnings
332
+
333
+ @options, @default_options = {}, DEFAULT_OPTIONS
334
+ set_options(options) # require @default_options
335
+ @default_options = @options.dup
297
336
  end
298
337
 
299
338
  @schema = SchemaMethods.new(self)
@@ -307,6 +346,8 @@ module PgConn
307
346
  # per-session settings in #initialize? Are they cleared by #reset too?)
308
347
  def reset
309
348
  @pg_connection.reset
349
+ self.warning = false
350
+ self.notice = false
310
351
  @pg_connection.exec "set client_min_messages to warning;" # Silence warnings
311
352
  end
312
353
 
@@ -776,6 +817,21 @@ module PgConn
776
817
  exec %(delete from #{table} where #{constraint})
777
818
  end
778
819
 
820
+ # Execute block with global options and resets afterwards. Currently only
821
+ # :notice and :warning is supported. Very useful in RSpec tests
822
+ #
823
+ # TODO: :error, :fail, :symbol, :schema, :search_path
824
+ #
825
+ def with(**options, &block)
826
+ begin
827
+ saved_options = @options.dup
828
+ set_options(options)
829
+ yield
830
+ ensure
831
+ set_options(saved_options)
832
+ end
833
+ end
834
+
779
835
  # Execute SQL statement(s) in a transaction and return the number of
780
836
  # affected records (if any). Also sets #timestamp unless a transaction is
781
837
  # already in progress. The +sql+ argument can be a command (String) or an
@@ -783,8 +839,8 @@ module PgConn
783
839
  # that span multiple lines. The empty array is a NOP but the empty string
784
840
  # is not.
785
841
  #
786
- # #exec pass Postgres exceptions to the caller unless :fail is false in which case
787
- # it returns nil.
842
+ # #exec pass Postgres exceptions to the caller unless :fail is false in
843
+ # which case it returns nil if an error occurred
788
844
  #
789
845
  # Note that postgres crashes the whole transaction stack if any error is
790
846
  # met so if you're inside a transaction, the transaction will be in an
@@ -889,7 +945,7 @@ module PgConn
889
945
  # and log as a side-effect)
890
946
  if @pg_connection
891
947
  @timestamp, @timestamptz = @pg_connection.exec(
892
- 'select current_timestamp, current_timestamp::timestamp without time zone'
948
+ 'select current_timestamp::timestamp without time zone, current_timestamp'
893
949
  ).tuple_values(0)
894
950
  end
895
951
  end
@@ -1102,6 +1158,60 @@ module PgConn
1102
1158
  "select #{field_list} from #{table} where #{where_clause}"
1103
1159
  end
1104
1160
 
1161
+ STDOUT_PRODUCER = lambda { |msg| $stdout.puts msg }
1162
+ STDERR_PRODUCER = lambda { |msg| $stderr.puts msg }
1163
+
1164
+ # FIXME Fails if connection is copied - requires a common options object
1165
+ # that is shared between all connection copies
1166
+ def set_option(option, value)
1167
+ case option
1168
+ when :notice, :warning
1169
+ @options[option] =
1170
+ case value
1171
+ when true; option == :notice ? STDOUT_PRODUCER : STDERR_PRODUCER
1172
+ when false; nil
1173
+ when nil; @default_options[option]
1174
+ when Proc; value
1175
+ else
1176
+ raise ArgumentError, "Illegal value #{value.inspect}"
1177
+ 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
1193
+ end
1194
+ else
1195
+ raise ArgumentError, "Illegal option: #{option.inspect}"
1196
+ end
1197
+ end
1198
+
1199
+ def set_options(opts)
1200
+ opts.each { |k,v| set_option(k,v) if !@options.key?(k) || @options[k] != v }
1201
+ end
1202
+
1203
+ def get_options() @options.dup end
1204
+
1205
+ # Called from postgres. Installed in #initialize
1206
+ def message_processor(message)
1207
+ case message
1208
+ when /^NOTICE:\s*(.*)$/; @options[:notice]&.call($1)
1209
+ when /^WARNING:\s*(.*)$/; @options[:warning]&.call($1)
1210
+ else
1211
+ raise "Oops"
1212
+ end
1213
+ end
1214
+
1105
1215
  # :call-seq:
1106
1216
  # pg_exec(string)
1107
1217
  # pg_exec(array)
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.33.0
4
+ version: 0.34.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-02-15 00:00:00.000000000 Z
11
+ date: 2025-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg