pg 1.3.5 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
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