pg 1.3.5 → 1.4.3

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.
data/ext/pg_result.c CHANGED
@@ -1476,10 +1476,10 @@ pgresult_stream_any(VALUE self, void (*yielder)(VALUE, int, int, void*), void* d
1476
1476
 
1477
1477
  pgresult = gvl_PQgetResult(pgconn);
1478
1478
  if( pgresult == NULL )
1479
- rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another result retrieval");
1479
+ rb_raise( rb_eNoResultError, "no result received - possibly an intersection with another query");
1480
1480
 
1481
1481
  if( nfields != PQnfields(pgresult) )
1482
- rb_raise( rb_eInvalidChangeOfResultFields, "number of fields must not change in single row mode");
1482
+ rb_raise( rb_eInvalidChangeOfResultFields, "number of fields changed in single row mode from %d to %d - this is a sign for intersection with another query", nfields, PQnfields(pgresult));
1483
1483
 
1484
1484
  this->pgresult = pgresult;
1485
1485
  }
data/lib/pg/connection.rb CHANGED
@@ -46,37 +46,6 @@ class PG::Connection
46
46
  hash.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
47
47
  end
48
48
 
49
- # Decode a connection string to Hash options
50
- #
51
- # Value are properly unquoted and unescaped.
52
- def self.connect_string_to_hash( str )
53
- options = {}
54
- key = nil
55
- value = String.new
56
- str.scan(/\G\s*(?>([^\s\\\']+)\s*=\s*|([^\s\\\']+)|'((?:[^\'\\]|\\.)*)'|(\\.?)|(\S))(\s|\z)?/m) do
57
- |k, word, sq, esc, garbage, sep|
58
- raise ArgumentError, "unterminated quoted string in connection info string: #{str.inspect}" if garbage
59
- if k
60
- key = k
61
- else
62
- value << (word || (sq || esc).gsub(/\\(.)/, '\\1'))
63
- end
64
- if sep
65
- raise ArgumentError, "missing = after #{value.inspect}" unless key
66
- options[key.to_sym] = value
67
- key = nil
68
- value = String.new
69
- end
70
- end
71
- options
72
- end
73
-
74
- # URI defined in RFC3986
75
- # This regexp is modified to allow host to specify multiple comma separated components captured as <hostports> and to disallow comma in hostnames.
76
- # Taken from: https://github.com/ruby/ruby/blob/be04006c7d2f9aeb7e9d8d09d945b3a9c7850202/lib/uri/rfc3986_parser.rb#L6
77
- HOST_AND_PORT = /(?<hostport>(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[-\.!$&-+0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)/
78
- POSTGRESQL_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<hostports>#{HOST_AND_PORT}(?:,\g<hostport>)*))(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
79
-
80
49
  # Parse the connection +args+ into a connection-parameter string.
81
50
  # See PG::Connection.new for valid arguments.
82
51
  #
@@ -87,91 +56,43 @@ class PG::Connection
87
56
  # * URI object
88
57
  # * positional arguments
89
58
  #
90
- # The method adds the option "hostaddr" and "fallback_application_name" if they aren't already set.
91
- # The URI and the options string is passed through and "hostaddr" as well as "fallback_application_name"
92
- # are added to the end.
93
- def self::parse_connect_args( *args )
59
+ # The method adds the option "fallback_application_name" if it isn't already set.
60
+ # It returns a connection string with "key=value" pairs.
61
+ def self.parse_connect_args( *args )
94
62
  hash_arg = args.last.is_a?( Hash ) ? args.pop.transform_keys(&:to_sym) : {}
95
- option_string = ""
96
63
  iopts = {}
97
64
 
98
65
  if args.length == 1
99
66
  case args.first
100
- when URI, POSTGRESQL_URI
101
- uri = args.first.to_s
102
- uri_match = POSTGRESQL_URI.match(uri)
103
- if uri_match['query']
104
- iopts = URI.decode_www_form(uri_match['query']).to_h.transform_keys(&:to_sym)
105
- end
106
- # extract "host1,host2" from "host1:5432,host2:5432"
107
- iopts[:host] = uri_match['hostports'].split(',', -1).map do |hostport|
108
- hostmatch = /\A#{HOST_AND_PORT}\z/.match(hostport)
109
- hostmatch['IPv6address'] || hostmatch['IPv4address'] || hostmatch['reg-name']&.gsub(/%(\h\h)/){ $1.hex.chr }
110
- end.join(',')
111
- oopts = {}
112
- when /=/
113
- # Option string style
114
- option_string = args.first.to_s
115
- iopts = connect_string_to_hash(option_string)
116
- oopts = {}
67
+ when URI, /=/, /:\/\//
68
+ # Option or URL string style
69
+ conn_string = args.first.to_s
70
+ iopts = PG::Connection.conninfo_parse(conn_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
117
71
  else
118
72
  # Positional parameters (only host given)
119
73
  iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
120
- oopts = iopts.dup
121
74
  end
122
75
  else
123
- # Positional parameters
76
+ # Positional parameters with host and more
124
77
  max = CONNECT_ARGUMENT_ORDER.length
125
78
  raise ArgumentError,
126
- "Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
79
+ "Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
127
80
 
128
81
  CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
129
82
  iopts[ k.to_sym ] = v if v
130
83
  end
131
84
  iopts.delete(:tty) # ignore obsolete tty parameter
132
- oopts = iopts.dup
133
85
  end
134
86
 
135
87
  iopts.merge!( hash_arg )
136
- oopts.merge!( hash_arg )
137
-
138
- # Resolve DNS in Ruby to avoid blocking state while connecting, when it ...
139
- if (host=iopts[:host]) && !iopts[:hostaddr]
140
- hostaddrs = host.split(",", -1).map do |mhost|
141
- if !mhost.empty? && !mhost.start_with?("/") && # isn't UnixSocket
142
- # isn't a path on Windows
143
- (RUBY_PLATFORM !~ /mingw|mswin/ || mhost !~ /\A\w:[\/\\]/)
144
-
145
- if Fiber.respond_to?(:scheduler) &&
146
- Fiber.scheduler &&
147
- RUBY_VERSION < '3.1.'
148
-
149
- # Use a second thread to avoid blocking of the scheduler.
150
- # `IPSocket.getaddress` isn't fiber aware before ruby-3.1.
151
- Thread.new{ IPSocket.getaddress(mhost) rescue '' }.value
152
- else
153
- IPSocket.getaddress(mhost) rescue ''
154
- end
155
- end
156
- end
157
- oopts[:hostaddr] = hostaddrs.join(",") if hostaddrs.any?
158
- end
159
88
 
160
89
  if !iopts[:fallback_application_name]
161
- oopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
90
+ iopts[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
162
91
  end
163
92
 
164
- if uri
165
- uri += uri_match['query'] ? "&" : "?"
166
- uri += URI.encode_www_form( oopts )
167
- return uri
168
- else
169
- option_string += ' ' unless option_string.empty? && oopts.empty?
170
- return option_string + connect_hash_to_string(oopts)
171
- end
93
+ return connect_hash_to_string(iopts)
172
94
  end
173
95
 
174
-
175
96
  # call-seq:
176
97
  # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
177
98
  #
@@ -241,7 +162,7 @@ class PG::Connection
241
162
  # ["more", "data", "to", "copy"]
242
163
 
243
164
  def copy_data( sql, coder=nil )
244
- raise PG::NotInBlockingMode, "copy_data can not be used in nonblocking mode" if nonblocking?
165
+ raise PG::NotInBlockingMode.new("copy_data can not be used in nonblocking mode", connection: self) if nonblocking?
245
166
  res = exec( sql )
246
167
 
247
168
  case res.result_status
@@ -273,11 +194,15 @@ class PG::Connection
273
194
  yield res
274
195
  rescue Exception => err
275
196
  cancel
276
- while get_copy_data
197
+ begin
198
+ while get_copy_data
199
+ end
200
+ rescue PG::Error
201
+ # Ignore error in cleanup to avoid losing original exception
277
202
  end
278
203
  while get_result
279
204
  end
280
- raise
205
+ raise err
281
206
  else
282
207
  res = get_last_result
283
208
  if !res || res.result_status != PGRES_COMMAND_OK
@@ -285,7 +210,7 @@ class PG::Connection
285
210
  end
286
211
  while get_result
287
212
  end
288
- raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
213
+ raise PG::NotAllCopyDataRetrieved.new("Not all COPY data retrieved", connection: self)
289
214
  end
290
215
  res
291
216
  ensure
@@ -310,16 +235,17 @@ class PG::Connection
310
235
  # and a +COMMIT+ at the end of the block, or
311
236
  # +ROLLBACK+ if any exception occurs.
312
237
  def transaction
238
+ rollback = false
313
239
  exec "BEGIN"
314
- res = yield(self)
240
+ yield(self)
315
241
  rescue Exception
242
+ rollback = true
316
243
  cancel if transaction_status == PG::PQTRANS_ACTIVE
317
244
  block
318
245
  exec "ROLLBACK"
319
246
  raise
320
- else
321
- exec "COMMIT"
322
- res
247
+ ensure
248
+ exec "COMMIT" unless rollback
323
249
  end
324
250
 
325
251
  ### Returns an array of Hashes with connection defaults. See ::conndefaults
@@ -482,10 +408,20 @@ class PG::Connection
482
408
  # See also #copy_data.
483
409
  #
484
410
  def put_copy_data(buffer, encoder=nil)
485
- until sync_put_copy_data(buffer, encoder)
486
- flush
411
+ # sync_put_copy_data does a non-blocking attept to flush data.
412
+ until res=sync_put_copy_data(buffer, encoder)
413
+ # It didn't flush immediately and allocation of more buffering memory failed.
414
+ # Wait for all data sent by doing a blocking flush.
415
+ res = flush
487
416
  end
488
- flush
417
+
418
+ # And do a blocking flush every 100 calls.
419
+ # This is to avoid memory bloat, when sending the data is slower than calls to put_copy_data happen.
420
+ if (@calls_to_put_copy_data += 1) > 100
421
+ @calls_to_put_copy_data = 0
422
+ res = flush
423
+ end
424
+ res
489
425
  end
490
426
  alias async_put_copy_data put_copy_data
491
427
 
@@ -505,6 +441,7 @@ class PG::Connection
505
441
  until sync_put_copy_end(*args)
506
442
  flush
507
443
  end
444
+ @calls_to_put_copy_data = 0
508
445
  flush
509
446
  end
510
447
  alias async_put_copy_end put_copy_end
@@ -545,6 +482,7 @@ class PG::Connection
545
482
  def reset
546
483
  reset_start
547
484
  async_connect_or_reset(:reset_poll)
485
+ self
548
486
  end
549
487
  alias async_reset reset
550
488
 
@@ -613,28 +551,62 @@ class PG::Connection
613
551
 
614
552
  private def async_connect_or_reset(poll_meth)
615
553
  # Track the progress of the connection, waiting for the socket to become readable/writable before polling it
554
+
555
+ if (timeo = conninfo_hash[:connect_timeout].to_i) && timeo > 0
556
+ # Lowest timeout is 2 seconds - like in libpq
557
+ timeo = [timeo, 2].max
558
+ stop_time = timeo + Process.clock_gettime(Process::CLOCK_MONOTONIC)
559
+ end
560
+
616
561
  poll_status = PG::PGRES_POLLING_WRITING
617
562
  until poll_status == PG::PGRES_POLLING_OK ||
618
563
  poll_status == PG::PGRES_POLLING_FAILED
619
564
 
620
- # If the socket needs to read, wait 'til it becomes readable to poll again
621
- case poll_status
622
- when PG::PGRES_POLLING_READING
623
- socket_io.wait_readable
565
+ timeout = stop_time&.-(Process.clock_gettime(Process::CLOCK_MONOTONIC))
566
+ event = if !timeout || timeout >= 0
567
+ # If the socket needs to read, wait 'til it becomes readable to poll again
568
+ case poll_status
569
+ when PG::PGRES_POLLING_READING
570
+ if defined?(IO::READABLE) # ruby-3.0+
571
+ socket_io.wait(IO::READABLE | IO::PRIORITY, timeout)
572
+ else
573
+ IO.select([socket_io], nil, [socket_io], timeout)
574
+ end
624
575
 
625
- # ...and the same for when the socket needs to write
626
- when PG::PGRES_POLLING_WRITING
627
- socket_io.wait_writable
576
+ # ...and the same for when the socket needs to write
577
+ when PG::PGRES_POLLING_WRITING
578
+ if defined?(IO::WRITABLE) # ruby-3.0+
579
+ # Use wait instead of wait_readable, since connection errors are delivered as
580
+ # exceptional/priority events on Windows.
581
+ socket_io.wait(IO::WRITABLE | IO::PRIORITY, timeout)
582
+ else
583
+ # io#wait on ruby-2.x doesn't wait for priority, so fallback to IO.select
584
+ IO.select(nil, [socket_io], [socket_io], timeout)
585
+ end
586
+ end
587
+ end
588
+ # connection to server at "localhost" (127.0.0.1), port 5433 failed: timeout expired (PG::ConnectionBad)
589
+ # connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: No such file or directory
590
+ unless event
591
+ if self.class.send(:host_is_named_pipe?, host)
592
+ connhost = "on socket \"#{host}\""
593
+ elsif respond_to?(:hostaddr)
594
+ connhost = "at \"#{host}\" (#{hostaddr}), port #{port}"
595
+ else
596
+ connhost = "at \"#{host}\", port #{port}"
597
+ end
598
+ raise PG::ConnectionBad.new("connection to server #{connhost} failed: timeout expired", connection: self)
628
599
  end
629
600
 
630
601
  # Check to see if it's finished or failed yet
631
602
  poll_status = send( poll_meth )
603
+ @last_status = status unless [PG::CONNECTION_BAD, PG::CONNECTION_OK].include?(status)
632
604
  end
633
605
 
634
606
  unless status == PG::CONNECTION_OK
635
607
  msg = error_message
636
608
  finish
637
- raise PG::ConnectionBad, msg
609
+ raise PG::ConnectionBad.new(msg, connection: self)
638
610
  end
639
611
 
640
612
  # Set connection to nonblocking to handle all blocking states in ruby.
@@ -642,8 +614,6 @@ class PG::Connection
642
614
  sync_setnonblocking(true)
643
615
  self.flush_data = true
644
616
  set_default_encoding
645
-
646
- self
647
617
  end
648
618
 
649
619
  class << self
@@ -698,13 +668,17 @@ class PG::Connection
698
668
  # connection will have its +client_encoding+ set accordingly.
699
669
  #
700
670
  # Raises a PG::Error if the connection fails.
701
- def new(*args, **kwargs)
702
- conn = self.connect_start(*args, **kwargs ) or
703
- raise(PG::Error, "Unable to create a new connection")
704
-
705
- raise(PG::ConnectionBad, conn.error_message) if conn.status == PG::CONNECTION_BAD
706
-
707
- conn.send(:async_connect_or_reset, :connect_poll)
671
+ def new(*args)
672
+ conn = connect_to_hosts(*args)
673
+
674
+ if block_given?
675
+ begin
676
+ return yield conn
677
+ ensure
678
+ conn.finish
679
+ end
680
+ end
681
+ conn
708
682
  end
709
683
  alias async_connect new
710
684
  alias connect new
@@ -712,6 +686,99 @@ class PG::Connection
712
686
  alias setdb new
713
687
  alias setdblogin new
714
688
 
689
+ private def connect_to_hosts(*args)
690
+ option_string = parse_connect_args(*args)
691
+ iopts = PG::Connection.conninfo_parse(option_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
692
+ iopts = PG::Connection.conndefaults.each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }.merge(iopts)
693
+
694
+ errors = []
695
+ if iopts[:hostaddr]
696
+ # hostaddr is provided -> no need to resolve hostnames
697
+ ihostaddrs = iopts[:hostaddr].split(",", -1)
698
+
699
+ ihosts = iopts[:host].split(",", -1) if iopts[:host]
700
+ raise PG::ConnectionBad, "could not match #{ihosts.size} host names to #{ihostaddrs.size} hostaddr values" if ihosts && ihosts.size != ihostaddrs.size
701
+
702
+ iports = iopts[:port].split(",", -1)
703
+ iports = iports * ihostaddrs.size if iports.size == 1
704
+ raise PG::ConnectionBad, "could not match #{iports.size} port numbers to #{ihostaddrs.size} hosts" if iports.size != ihostaddrs.size
705
+
706
+ # Try to connect to each hostaddr with separate timeout
707
+ ihostaddrs.each_with_index do |ihostaddr, idx|
708
+ oopts = iopts.merge(hostaddr: ihostaddr, port: iports[idx])
709
+ oopts[:host] = ihosts[idx] if ihosts
710
+ c = connect_internal(oopts, errors)
711
+ return c if c
712
+ end
713
+ elsif iopts[:host] && !iopts[:host].empty?
714
+ # Resolve DNS in Ruby to avoid blocking state while connecting, when it ...
715
+ ihosts = iopts[:host].split(",", -1)
716
+
717
+ iports = iopts[:port].split(",", -1)
718
+ iports = iports * ihosts.size if iports.size == 1
719
+ raise PG::ConnectionBad, "could not match #{iports.size} port numbers to #{ihosts.size} hosts" if iports.size != ihosts.size
720
+
721
+ ihosts.each_with_index do |mhost, idx|
722
+ unless host_is_named_pipe?(mhost)
723
+ addrs = if Fiber.respond_to?(:scheduler) &&
724
+ Fiber.scheduler &&
725
+ RUBY_VERSION < '3.1.'
726
+
727
+ # Use a second thread to avoid blocking of the scheduler.
728
+ # `TCPSocket.gethostbyname` isn't fiber aware before ruby-3.1.
729
+ Thread.new{ Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [''] }.value
730
+ else
731
+ Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue ['']
732
+ end
733
+
734
+ # Try to connect to each host with separate timeout
735
+ addrs.each do |addr|
736
+ oopts = iopts.merge(hostaddr: addr, host: mhost, port: iports[idx])
737
+ c = connect_internal(oopts, errors)
738
+ return c if c
739
+ end
740
+ else
741
+ # No hostname to resolve (UnixSocket)
742
+ oopts = iopts.merge(host: mhost, port: iports[idx])
743
+ c = connect_internal(oopts, errors)
744
+ return c if c
745
+ end
746
+ end
747
+ else
748
+ # No host given
749
+ return connect_internal(iopts)
750
+ end
751
+ raise PG::ConnectionBad, errors.join("\n")
752
+ end
753
+
754
+ private def connect_internal(opts, errors=nil)
755
+ begin
756
+ conn = self.connect_start(opts) or
757
+ raise(PG::Error, "Unable to create a new connection")
758
+
759
+ raise PG::ConnectionBad.new(conn.error_message, connection: self) if conn.status == PG::CONNECTION_BAD
760
+
761
+ conn.send(:async_connect_or_reset, :connect_poll)
762
+ rescue PG::ConnectionBad => err
763
+ if errors && !(conn && [PG::CONNECTION_AWAITING_RESPONSE].include?(conn.instance_variable_get(:@last_status)))
764
+ # Seems to be no authentication error -> try next host
765
+ errors << err
766
+ return nil
767
+ else
768
+ # Probably an authentication error
769
+ raise
770
+ end
771
+ end
772
+ conn
773
+ end
774
+
775
+ private def host_is_named_pipe?(host_string)
776
+ host_string.empty? || host_string.start_with?("/") || # it's UnixSocket?
777
+ host_string.start_with?("@") || # it's UnixSocket in the abstract namespace?
778
+ # it's a path on Windows?
779
+ (RUBY_PLATFORM =~ /mingw|mswin/ && host_string =~ /\A([\/\\]|\w:[\/\\])/)
780
+ end
781
+
715
782
  # call-seq:
716
783
  # PG::Connection.ping(connection_hash) -> Integer
717
784
  # PG::Connection.ping(connection_string) -> Integer
data/lib/pg/exceptions.rb CHANGED
@@ -6,7 +6,13 @@ require 'pg' unless defined?( PG )
6
6
 
7
7
  module PG
8
8
 
9
- class Error < StandardError; end
9
+ class Error < StandardError
10
+ def initialize(msg=nil, connection: nil, result: nil)
11
+ @connection = connection
12
+ @result = result
13
+ super(msg)
14
+ end
15
+ end
10
16
 
11
17
  end # module PG
12
18
 
data/lib/pg/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module PG
2
2
  # Library version
3
- VERSION = '1.3.5'
3
+ VERSION = '1.4.3'
4
4
  end
data/lib/pg.rb CHANGED
@@ -59,14 +59,14 @@ module PG
59
59
  # Get the PG library version.
60
60
  #
61
61
  # +include_buildnum+ is no longer used and any value passed will be ignored.
62
- def self::version_string( include_buildnum=nil )
63
- return "%s %s" % [ self.name, VERSION ]
62
+ def self.version_string( include_buildnum=nil )
63
+ "%s %s" % [ self.name, VERSION ]
64
64
  end
65
65
 
66
66
 
67
67
  ### Convenience alias for PG::Connection.new.
68
- def self::connect( *args, **kwargs )
69
- return PG::Connection.new( *args, **kwargs )
68
+ def self.connect( *args, &block )
69
+ Connection.new( *args, &block )
70
70
  end
71
71
 
72
72
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.5
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Granger
@@ -36,7 +36,7 @@ cert_chain:
36
36
  oL1mUdzB8KrZL4/WbG5YNX6UTtJbIOu9qEFbBAy4/jtIkJX+dlNoFwd4GXQW1YNO
37
37
  nA==
38
38
  -----END CERTIFICATE-----
39
- date: 2022-03-31 00:00:00.000000000 Z
39
+ date: 2022-08-09 00:00:00.000000000 Z
40
40
  dependencies: []
41
41
  description: Pg is the Ruby interface to the PostgreSQL RDBMS. It works with PostgreSQL
42
42
  9.3 and later.
@@ -179,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
181
  requirements: []
182
- rubygems_version: 3.2.15
182
+ rubygems_version: 3.3.7
183
183
  signing_key:
184
184
  specification_version: 4
185
185
  summary: Pg is the Ruby interface to the PostgreSQL RDBMS
metadata.gz.sig CHANGED
Binary file