pg 1.5.9-x64-mingw-ucrt → 1.6.0.rc2-x64-mingw-ucrt

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 (56) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{History.md → CHANGELOG.md} +54 -0
  4. data/Gemfile +7 -4
  5. data/README-Windows.rdoc +1 -1
  6. data/README.ja.md +4 -4
  7. data/README.md +6 -5
  8. data/Rakefile +80 -13
  9. data/ext/extconf.rb +158 -14
  10. data/ext/gvl_wrappers.c +13 -2
  11. data/ext/gvl_wrappers.h +33 -0
  12. data/ext/pg.c +16 -5
  13. data/ext/pg.h +9 -9
  14. data/ext/pg_binary_decoder.c +150 -0
  15. data/ext/pg_binary_encoder.c +210 -7
  16. data/ext/pg_cancel_connection.c +360 -0
  17. data/ext/pg_coder.c +52 -5
  18. data/ext/pg_connection.c +368 -158
  19. data/ext/pg_copy_coder.c +2 -2
  20. data/ext/pg_record_coder.c +1 -1
  21. data/ext/pg_result.c +9 -11
  22. data/ext/pg_text_encoder.c +20 -7
  23. data/ext/pg_tuple.c +2 -2
  24. data/ext/pg_type_map.c +1 -1
  25. data/ext/pg_type_map_all_strings.c +1 -1
  26. data/ext/pg_type_map_by_class.c +1 -1
  27. data/ext/pg_type_map_by_column.c +2 -1
  28. data/ext/pg_type_map_by_mri_type.c +1 -1
  29. data/ext/pg_type_map_by_oid.c +3 -1
  30. data/ext/pg_type_map_in_ruby.c +1 -1
  31. data/lib/3.1/pg_ext.so +0 -0
  32. data/lib/3.2/pg_ext.so +0 -0
  33. data/lib/3.3/pg_ext.so +0 -0
  34. data/lib/3.4/pg_ext.so +0 -0
  35. data/lib/pg/basic_type_map_for_queries.rb +7 -3
  36. data/lib/pg/basic_type_registry.rb +2 -2
  37. data/lib/pg/cancel_connection.rb +53 -0
  38. data/lib/pg/coder.rb +2 -1
  39. data/lib/pg/connection.rb +252 -131
  40. data/lib/pg/version.rb +1 -1
  41. data/lib/pg.rb +13 -8
  42. data/misc/yugabyte/Dockerfile +9 -0
  43. data/misc/yugabyte/docker-compose.yml +28 -0
  44. data/misc/yugabyte/pg-test.rb +45 -0
  45. data/pg.gemspec +5 -3
  46. data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
  47. data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
  48. data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
  49. data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
  50. data/{lib/x64-mingw-ucrt → ports/x64-mingw-ucrt/lib}/libpq.dll +0 -0
  51. data/rakelib/pg_gem_helper.rb +64 -0
  52. data.tar.gz.sig +0 -0
  53. metadata +34 -25
  54. metadata.gz.sig +0 -0
  55. data/Manifest.txt +0 -72
  56. data/Rakefile.cross +0 -303
data/lib/pg/connection.rb CHANGED
@@ -356,21 +356,18 @@ class PG::Connection
356
356
  end
357
357
  end
358
358
 
359
- # Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
360
- if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
361
- # call-seq:
362
- # conn.ssl_attributes -> Hash<String,String>
363
- #
364
- # Returns SSL-related information about the connection as key/value pairs
365
- #
366
- # The available attributes varies depending on the SSL library being used,
367
- # and the type of connection.
368
- #
369
- # See also #ssl_attribute
370
- def ssl_attributes
371
- ssl_attribute_names.each.with_object({}) do |n,h|
372
- h[n] = ssl_attribute(n)
373
- end
359
+ # call-seq:
360
+ # conn.ssl_attributes -> Hash<String,String>
361
+ #
362
+ # Returns SSL-related information about the connection as key/value pairs
363
+ #
364
+ # The available attributes varies depending on the SSL library being used,
365
+ # and the type of connection.
366
+ #
367
+ # See also #ssl_attribute
368
+ def ssl_attributes
369
+ ssl_attribute_names.each.with_object({}) do |n,h|
370
+ h[n] = ssl_attribute(n)
374
371
  end
375
372
  end
376
373
 
@@ -539,6 +536,25 @@ class PG::Connection
539
536
  end
540
537
  alias async_put_copy_end put_copy_end
541
538
 
539
+ if method_defined? :send_pipeline_sync
540
+ # call-seq:
541
+ # conn.pipeline_sync
542
+ #
543
+ # Marks a synchronization point in a pipeline by sending a sync message and flushing the send buffer.
544
+ # This serves as the delimiter of an implicit transaction and an error recovery point.
545
+ #
546
+ # See enter_pipeline_mode
547
+ #
548
+ # Raises PG::Error if the connection is not in pipeline mode or sending a sync message failed.
549
+ #
550
+ # Available since PostgreSQL-14
551
+ def pipeline_sync(*args)
552
+ send_pipeline_sync(*args)
553
+ flush
554
+ end
555
+ alias async_pipeline_sync pipeline_sync
556
+ end
557
+
542
558
  if method_defined? :sync_encrypt_password
543
559
  # call-seq:
544
560
  # conn.encrypt_password( password, username, algorithm=nil ) -> String
@@ -586,128 +602,197 @@ class PG::Connection
586
602
  end
587
603
  alias async_reset reset
588
604
 
589
- # call-seq:
590
- # conn.cancel() -> String
591
- #
592
- # Requests cancellation of the command currently being
593
- # processed.
594
- #
595
- # Returns +nil+ on success, or a string containing the
596
- # error message if a failure occurs.
597
- def cancel
598
- be_pid = backend_pid
599
- be_key = backend_key
600
- cancel_request = [0x10, 1234, 5678, be_pid, be_key].pack("NnnNN")
601
-
602
- if Fiber.respond_to?(:scheduler) && Fiber.scheduler && RUBY_PLATFORM =~ /mingw|mswin/
603
- # Ruby's nonblocking IO is not really supported on Windows.
604
- # We work around by using threads and explicit calls to wait_readable/wait_writable.
605
- cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
606
- begin
607
- cl.write_nonblock(cancel_request)
608
- rescue IO::WaitReadable, Errno::EINTR
609
- cl.wait_writable
610
- retry
611
- end
612
- begin
613
- cl.read_nonblock(1)
614
- rescue IO::WaitReadable, Errno::EINTR
615
- cl.wait_readable
616
- retry
617
- rescue EOFError
618
- end
619
- elsif RUBY_ENGINE == 'truffleruby'
620
- begin
621
- cl = socket_io.remote_address.connect
622
- rescue NotImplementedError
623
- # Workaround for truffleruby < 21.3.0
624
- cl2 = Socket.for_fd(socket_io.fileno)
625
- cl2.autoclose = false
626
- adr = cl2.remote_address
627
- if adr.ip?
628
- cl = TCPSocket.new(adr.ip_address, adr.ip_port)
629
- cl.autoclose = false
630
- else
631
- cl = UNIXSocket.new(adr.unix_path)
632
- cl.autoclose = false
633
- end
634
- end
635
- cl.write(cancel_request)
636
- cl.read(1)
637
- else
638
- cl = socket_io.remote_address.connect
639
- # Send CANCEL_REQUEST_CODE and parameters
640
- cl.write(cancel_request)
641
- # Wait for the postmaster to close the connection, which indicates that it's processed the request.
642
- cl.read(1)
605
+ if defined?(PG::CancelConnection)
606
+ # PostgreSQL-17+
607
+
608
+ def sync_cancel
609
+ cancon = PG::CancelConnection.new(self)
610
+ cancon.sync_cancel
611
+ rescue PG::Error => err
612
+ err.to_s
643
613
  end
644
614
 
645
- cl.close
646
- nil
647
- rescue SystemCallError => err
648
- err.to_s
649
- end
650
- alias async_cancel cancel
615
+ # call-seq:
616
+ # conn.cancel() -> String
617
+ #
618
+ # Requests cancellation of the command currently being
619
+ # processed.
620
+ #
621
+ # Returns +nil+ on success, or a string containing the
622
+ # error message if a failure occurs.
623
+ #
624
+ # On PostgreSQL-17+ client libaray the class PG::CancelConnection is used.
625
+ # On older client library a pure ruby implementation is used.
626
+ def cancel
627
+ cancon = PG::CancelConnection.new(self)
628
+ cancon.async_cancel
629
+ rescue PG::Error => err
630
+ err.to_s
631
+ end
651
632
 
652
- private def async_connect_or_reset(poll_meth)
653
- # Track the progress of the connection, waiting for the socket to become readable/writable before polling it
633
+ else
634
+
635
+ # PostgreSQL < 17
654
636
 
655
- if (timeo = conninfo_hash[:connect_timeout].to_i) && timeo > 0
656
- host_count = conninfo_hash[:host].to_s.count(",") + 1
657
- stop_time = timeo * host_count + Process.clock_gettime(Process::CLOCK_MONOTONIC)
637
+ def cancel
638
+ be_pid = backend_pid
639
+ be_key = backend_key
640
+ cancel_request = [0x10, 1234, 5678, be_pid, be_key].pack("NnnNN")
641
+
642
+ if Fiber.respond_to?(:scheduler) && Fiber.scheduler && RUBY_PLATFORM =~ /mingw|mswin/
643
+ # Ruby's nonblocking IO is not really supported on Windows.
644
+ # We work around by using threads and explicit calls to wait_readable/wait_writable.
645
+ cl = Thread.new(socket_io.remote_address) { |ra| ra.connect }.value
646
+ begin
647
+ cl.write_nonblock(cancel_request)
648
+ rescue IO::WaitReadable, Errno::EINTR
649
+ cl.wait_writable
650
+ retry
651
+ end
652
+ begin
653
+ cl.read_nonblock(1)
654
+ rescue IO::WaitReadable, Errno::EINTR
655
+ cl.wait_readable
656
+ retry
657
+ rescue EOFError
658
+ end
659
+ else
660
+ cl = socket_io.remote_address.connect
661
+ # Send CANCEL_REQUEST_CODE and parameters
662
+ cl.write(cancel_request)
663
+ # Wait for the postmaster to close the connection, which indicates that it's processed the request.
664
+ cl.read(1)
665
+ end
666
+
667
+ cl.close
668
+ nil
669
+ rescue SystemCallError => err
670
+ err.to_s
658
671
  end
672
+ end
673
+ alias async_cancel cancel
659
674
 
660
- poll_status = PG::PGRES_POLLING_WRITING
661
- until poll_status == PG::PGRES_POLLING_OK ||
662
- poll_status == PG::PGRES_POLLING_FAILED
663
-
664
- # Set single timeout to parameter "connect_timeout" but
665
- # don't exceed total connection time of number-of-hosts * connect_timeout.
666
- timeout = [timeo, stop_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)].min if stop_time
667
- event = if !timeout || timeout >= 0
668
- # If the socket needs to read, wait 'til it becomes readable to poll again
669
- case poll_status
670
- when PG::PGRES_POLLING_READING
671
- if defined?(IO::READABLE) # ruby-3.0+
672
- socket_io.wait(IO::READABLE | IO::PRIORITY, timeout)
673
- else
674
- IO.select([socket_io], nil, [socket_io], timeout)
675
+ module Pollable
676
+ # Track the progress of the connection, waiting for the socket to become readable/writable before polling it.
677
+ #
678
+ # Connecting to multiple hosts is done like so:
679
+ # - All hosts are passed to PG::Connection.connect_start
680
+ # - As soon as the host is tried to connect the related host is removed from the hosts list
681
+ # - When the polling status changes to `PG::PGRES_POLLING_OK` the connection is returned and ready to use.
682
+ # - When the polling status changes to `PG::PGRES_POLLING_FAILED` connecting is aborted and a PG::ConnectionBad is raised with details to all connection attepts.
683
+ # - When a timeout occurs, connecting is restarted with the remaining hosts.
684
+ #
685
+ # The downside is that this connects only once to hosts which are listed twice when they timeout.
686
+ private def polling_loop(poll_meth)
687
+ connect_timeout = conninfo_hash[:connect_timeout]
688
+ if (timeo = connect_timeout.to_i) && timeo > 0
689
+ host_count = (conninfo_hash[:hostaddr].to_s.empty? ? conninfo_hash[:host] : conninfo_hash[:hostaddr]).to_s.count(",") + 1
690
+ stop_time = timeo * host_count + Process.clock_gettime(Process::CLOCK_MONOTONIC)
691
+ end
692
+ iopts = conninfo_hash.compact
693
+ connection_errors = []
694
+ poll_status = PG::PGRES_POLLING_WRITING
695
+
696
+ until poll_status == PG::PGRES_POLLING_OK ||
697
+ poll_status == PG::PGRES_POLLING_FAILED
698
+
699
+ # Set single timeout to parameter "connect_timeout" but
700
+ # don't exceed total connection time of number-of-hosts * connect_timeout.
701
+ timeout = [timeo, stop_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)].min if stop_time
702
+
703
+ hostcnt = remove_current_host(iopts)
704
+
705
+ event = if !timeout || timeout >= 0
706
+ # If the socket needs to read, wait 'til it becomes readable to poll again
707
+ case poll_status
708
+ when PG::PGRES_POLLING_READING
709
+ if defined?(IO::READABLE) # ruby-3.0+
710
+ socket_io.wait(IO::READABLE | IO::PRIORITY, timeout)
711
+ else
712
+ IO.select([socket_io], nil, [socket_io], timeout)
713
+ end
714
+
715
+ # ...and the same for when the socket needs to write
716
+ when PG::PGRES_POLLING_WRITING
717
+ if defined?(IO::WRITABLE) # ruby-3.0+
718
+ # Use wait instead of wait_readable, since connection errors are delivered as
719
+ # exceptional/priority events on Windows.
720
+ socket_io.wait(IO::WRITABLE | IO::PRIORITY, timeout)
721
+ else
722
+ # io#wait on ruby-2.x doesn't wait for priority, so fallback to IO.select
723
+ IO.select(nil, [socket_io], [socket_io], timeout)
724
+ end
675
725
  end
726
+ end
676
727
 
677
- # ...and the same for when the socket needs to write
678
- when PG::PGRES_POLLING_WRITING
679
- if defined?(IO::WRITABLE) # ruby-3.0+
680
- # Use wait instead of wait_readable, since connection errors are delivered as
681
- # exceptional/priority events on Windows.
682
- socket_io.wait(IO::WRITABLE | IO::PRIORITY, timeout)
728
+ # connection to server at "localhost" (127.0.0.1), port 5433 failed: timeout expired (PG::ConnectionBad)
729
+ # connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: No such file or directory
730
+ unless event
731
+ connection_errors << (error_message + "timeout expired")
732
+ if hostcnt > 0
733
+ reset_start2(self.class.parse_connect_args(iopts))
734
+ # Restart polling with waiting for writable.
735
+ # Otherwise "not connected" error is raised on Windows.
736
+ poll_status = PG::PGRES_POLLING_WRITING
737
+ next
683
738
  else
684
- # io#wait on ruby-2.x doesn't wait for priority, so fallback to IO.select
685
- IO.select(nil, [socket_io], [socket_io], timeout)
739
+ finish
740
+ raise PG::ConnectionBad.new(connection_errors.join("\n").b, connection: self)
686
741
  end
687
742
  end
688
- end
689
- # connection to server at "localhost" (127.0.0.1), port 5433 failed: timeout expired (PG::ConnectionBad)
690
- # connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: No such file or directory
691
- unless event
692
- if self.class.send(:host_is_named_pipe?, host)
693
- connhost = "on socket \"#{host}\""
694
- elsif respond_to?(:hostaddr)
695
- connhost = "at \"#{host}\" (#{hostaddr}), port #{port}"
696
- else
697
- connhost = "at \"#{host}\", port #{port}"
698
- end
699
- raise PG::ConnectionBad.new("connection to server #{connhost} failed: timeout expired", connection: self)
743
+
744
+ # Check to see if it's finished or failed yet
745
+ poll_status = send( poll_meth )
700
746
  end
701
747
 
702
- # Check to see if it's finished or failed yet
703
- poll_status = send( poll_meth )
748
+ unless status == PG::CONNECTION_OK
749
+ msg = error_message
750
+ finish
751
+ raise PG::ConnectionBad.new(connection_errors.map{|e| e + "\n" }.join.b + msg, connection: self)
752
+ end
704
753
  end
705
754
 
706
- unless status == PG::CONNECTION_OK
707
- msg = error_message
708
- finish
709
- raise PG::ConnectionBad.new(msg, connection: self)
755
+ # Remove the host to which the connection is currently established from the option hash.
756
+ # Affected options are:
757
+ # - :host
758
+ # - :hostaddr
759
+ # - :port
760
+ #
761
+ # Return the number of remaining hosts.
762
+ private def remove_current_host(iopts)
763
+ ihosts = iopts[:host]&.split(",", -1)
764
+ ihostaddrs = iopts[:hostaddr]&.split(",", -1)
765
+ iports = iopts[:port]&.split(",", -1)
766
+ iports = iports * (ihosts || ihostaddrs).size if iports&.size == 1
767
+
768
+ idx = (ihosts || ihostaddrs || iports).index.with_index do |_, i|
769
+ (ihosts ? ihosts[i] == host : true) &&
770
+ (ihostaddrs && respond_to?(:hostaddr, true) ? ihostaddrs[i] == hostaddr : true) &&
771
+ (iports ? iports[i].to_i == port : true)
772
+ end
773
+
774
+ if idx
775
+ ihosts&.delete_at(idx)
776
+ ihostaddrs&.delete_at(idx)
777
+ iports&.delete_at(idx)
778
+
779
+ iopts.merge!(
780
+ host: ihosts.join(",")) if ihosts
781
+ iopts.merge!(
782
+ hostaddr: ihostaddrs.join(",")) if ihostaddrs
783
+ iopts.merge!(
784
+ port: iports.join(",")) if iports
785
+ end
786
+
787
+ (ihosts || ihostaddrs || iports).size
710
788
  end
789
+ end
790
+
791
+ include Pollable
792
+
793
+ private def async_connect_or_reset(poll_meth)
794
+ # Track the progress of the connection, waiting for the socket to become readable/writable before polling it
795
+ polling_loop(poll_meth)
711
796
 
712
797
  # Set connection to nonblocking to handle all blocking states in ruby.
713
798
  # That way a fiber scheduler is able to handle IO requests.
@@ -723,7 +808,7 @@ class PG::Connection
723
808
  # PG::Connection.new(connection_string) -> conn
724
809
  # PG::Connection.new(host, port, options, tty, dbname, user, password) -> conn
725
810
  #
726
- # Create a connection to the specified server.
811
+ # === Create a connection to the specified server.
727
812
  #
728
813
  # +connection_hash+ must be a ruby Hash with connection parameters.
729
814
  # See the {list of valid parameters}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS] in the PostgreSQL documentation.
@@ -747,7 +832,13 @@ class PG::Connection
747
832
  # [+password+]
748
833
  # login password
749
834
  #
750
- # Examples:
835
+ #
836
+ # If the Ruby default internal encoding is set (i.e., <code>Encoding.default_internal != nil</code>), the
837
+ # connection will have its +client_encoding+ set accordingly.
838
+ #
839
+ # Raises a PG::Error if the connection fails.
840
+ #
841
+ # === Examples:
751
842
  #
752
843
  # # Connect using all defaults
753
844
  # PG::Connection.new
@@ -764,10 +855,16 @@ class PG::Connection
764
855
  # # As an URI
765
856
  # PG::Connection.new( "postgresql://user:pass@pgsql.example.com:5432/testdb?sslmode=require" )
766
857
  #
767
- # If the Ruby default internal encoding is set (i.e., <code>Encoding.default_internal != nil</code>), the
768
- # connection will have its +client_encoding+ set accordingly.
858
+ # === Specifying Multiple Hosts
859
+ #
860
+ # It is possible to specify multiple hosts to connect to, so that they are tried in the given order or optionally in random order.
861
+ # In the Keyword/Value format, the host, hostaddr, and port options accept comma-separated lists of values.
862
+ # The {details to libpq}[https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS] describe how it works, but there are two small differences how ruby-pg handles multiple hosts:
863
+ # - All hosts are resolved before the first connection is tried.
864
+ # This means that when +load_balance_hosts+ is set to +random+, then all resolved addresses are tried randomly in one level.
865
+ # When a host resolves to more than one address, it is therefore tried more often than a host that has only one address.
866
+ # - When a timeout occurs due to the value of +connect_timeout+, then the given +host+, +hostaddr+ and +port+ combination is not tried a second time, even if it is specified several times.
769
867
  #
770
- # Raises a PG::Error if the connection fails.
771
868
  def new(*args)
772
869
  conn = connect_to_hosts(*args)
773
870
 
@@ -825,6 +922,14 @@ class PG::Connection
825
922
  iopts = PG::Connection.conninfo_parse(option_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
826
923
  iopts = PG::Connection.conndefaults.each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }.merge(iopts)
827
924
 
925
+ if PG::BUNDLED_LIBPQ_WITH_UNIXSOCKET && iopts[:host].to_s.empty? && iopts[:hostaddr].to_s.empty?
926
+ # Many distors patch the hardcoded default UnixSocket path in libpq to /var/run/postgresql instead of /tmp .
927
+ # We simply try them all.
928
+ iopts[:host] = "/var/run/postgresql" + # Ubuntu, Debian, Fedora, Opensuse
929
+ ",/run/postgresql" + # Alpine, Archlinux, Gentoo
930
+ ",/tmp" # Stock PostgreSQL
931
+ end
932
+
828
933
  iopts_for_reset = iopts
829
934
  if iopts[:hostaddr]
830
935
  # hostaddr is provided -> no need to resolve hostnames
@@ -897,14 +1002,29 @@ class PG::Connection
897
1002
  private_constant :REDIRECT_CLASS_METHODS
898
1003
 
899
1004
  # These methods are affected by PQsetnonblocking
900
- REDIRECT_SEND_METHODS = PG.make_shareable({
1005
+ REDIRECT_SEND_METHODS = {
901
1006
  :isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
902
1007
  :nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
903
1008
  :put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
904
1009
  :put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
905
1010
  :flush => [:async_flush, :sync_flush],
906
- })
1011
+ }
907
1012
  private_constant :REDIRECT_SEND_METHODS
1013
+ if PG::Connection.instance_methods.include? :sync_pipeline_sync
1014
+ if PG::Connection.instance_methods.include? :send_pipeline_sync
1015
+ # PostgreSQL-17+
1016
+ REDIRECT_SEND_METHODS.merge!({
1017
+ :pipeline_sync => [:async_pipeline_sync, :sync_pipeline_sync],
1018
+ })
1019
+ else
1020
+ # PostgreSQL-14+
1021
+ REDIRECT_SEND_METHODS.merge!({
1022
+ :pipeline_sync => [:sync_pipeline_sync, :sync_pipeline_sync],
1023
+ })
1024
+ end
1025
+ end
1026
+ PG.make_shareable(REDIRECT_SEND_METHODS)
1027
+
908
1028
  REDIRECT_METHODS = {
909
1029
  :exec => [:async_exec, :sync_exec],
910
1030
  :query => [:async_exec, :sync_exec],
@@ -921,12 +1041,13 @@ class PG::Connection
921
1041
  :set_client_encoding => [:async_set_client_encoding, :sync_set_client_encoding],
922
1042
  :client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
923
1043
  :cancel => [:async_cancel, :sync_cancel],
1044
+ :encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
924
1045
  }
925
1046
  private_constant :REDIRECT_METHODS
926
-
927
- if PG::Connection.instance_methods.include? :async_encrypt_password
1047
+ if PG::Connection.instance_methods.include? :async_close_prepared
928
1048
  REDIRECT_METHODS.merge!({
929
- :encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
1049
+ :close_prepared => [:async_close_prepared, :sync_close_prepared],
1050
+ :close_portal => [:async_close_portal, :sync_close_portal],
930
1051
  })
931
1052
  end
932
1053
  PG.make_shareable(REDIRECT_METHODS)
data/lib/pg/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module PG
2
2
  # Library version
3
- VERSION = '1.5.9'
3
+ VERSION = '1.6.0.rc2'
4
4
  end
data/lib/pg.rb CHANGED
@@ -6,11 +6,12 @@
6
6
  module PG
7
7
 
8
8
  # Is this file part of a fat binary gem with bundled libpq?
9
- bundled_libpq_path = File.join(__dir__, RUBY_PLATFORM.gsub(/^i386-/, "x86-"))
10
- if File.exist?(bundled_libpq_path)
9
+ # This path must be enabled by add_dll_directory on Windows.
10
+ gplat = Gem::Platform.local
11
+ bundled_libpq_path = Dir[File.expand_path("../ports/#{gplat.cpu}-#{gplat.os}*/lib", __dir__)].first
12
+ if bundled_libpq_path
11
13
  POSTGRESQL_LIB_PATH = bundled_libpq_path
12
14
  else
13
- bundled_libpq_path = nil
14
15
  # Try to load libpq path as found by extconf.rb
15
16
  begin
16
17
  require "pg/postgresql_lib_path"
@@ -22,7 +23,8 @@ module PG
22
23
  end
23
24
 
24
25
  add_dll_path = proc do |path, &block|
25
- if RUBY_PLATFORM =~/(mswin|mingw)/i && path && File.exist?(path)
26
+ if RUBY_PLATFORM =~/(mswin|mingw)/i && path
27
+ BUNDLED_LIBPQ_WITH_UNIXSOCKET = false
26
28
  begin
27
29
  require 'ruby_installer/runtime'
28
30
  RubyInstaller::Runtime.add_dll_directory(path, &block)
@@ -33,19 +35,21 @@ module PG
33
35
  ENV['PATH'] = old_path
34
36
  end
35
37
  else
36
- # No need to set a load path manually - it's set as library rpath.
38
+ # libpq is found by a relative rpath in the cross compiled extension dll
39
+ # or by the system library loader
37
40
  block.call
41
+ BUNDLED_LIBPQ_WITH_UNIXSOCKET = RUBY_PLATFORM=~/linux/i && PG::IS_BINARY_GEM
38
42
  end
39
43
  end
40
44
 
41
45
  # Add a load path to the one retrieved from pg_config
42
46
  add_dll_path.call(POSTGRESQL_LIB_PATH) do
43
- if bundled_libpq_path
44
- # It's a Windows binary gem, try the <major>.<minor> subdirectory
47
+ begin
48
+ # Try the <major>.<minor> subdirectory for fat binary gems
45
49
  major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
46
50
  raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
47
51
  require "#{major_minor}/pg_ext"
48
- else
52
+ rescue LoadError
49
53
  require 'pg_ext'
50
54
  end
51
55
  end
@@ -111,6 +115,7 @@ module PG
111
115
  require 'pg/coder'
112
116
  require 'pg/type_map_by_column'
113
117
  require 'pg/connection'
118
+ require 'pg/cancel_connection'
114
119
  require 'pg/result'
115
120
  require 'pg/tuple'
116
121
  autoload :VERSION, 'pg/version'
@@ -0,0 +1,9 @@
1
+ FROM yugabytedb/yugabyte:2024.1.0.0-b129
2
+
3
+ WORKDIR /app
4
+
5
+ RUN yugabyted cert generate_server_certs --hostnames=127.0.0.1 --base_dir=.
6
+
7
+ ENTRYPOINT ["yugabyted"]
8
+ CMD ["start", "--background", "false", "--ui", "false", "--tserver_flags", "use_client_to_server_encryption=true,cert_node_filename=127.0.0.1,certs_dir=/app/generated_certs/127.0.0.1"]
9
+ VOLUME /app
@@ -0,0 +1,28 @@
1
+ services:
2
+ yb:
3
+ build: .
4
+ container_name: yb
5
+ ports:
6
+ - "127.0.0.1:5433:5433"
7
+ volumes:
8
+ - certs:/app/generated_certs
9
+ healthcheck:
10
+ test: 'ysqlsh -h $$(hostname) -c \\conninfo || exit 1;'
11
+ interval: 2s
12
+ timeout: 30s
13
+ retries: 20
14
+ start_period: 10s
15
+
16
+ pg:
17
+ image: ruby:3.0
18
+ working_dir: /app
19
+ volumes:
20
+ - .:/app
21
+ - certs:/generated_certs
22
+ command: bash -c "gem inst pg-*.gem && ruby pg-test.rb"
23
+ depends_on:
24
+ yb:
25
+ condition: service_healthy
26
+
27
+ volumes:
28
+ certs:
@@ -0,0 +1,45 @@
1
+ require 'pg'
2
+
3
+ conn = PG.connect(
4
+ host: 'yb',
5
+ port: 5433,
6
+ user: 'yugabyte',
7
+ dbname: 'yugabyte',
8
+ sslmode: 'require',
9
+ sslrootcert: 'app/generated_certs/127.0.0.1/ca.crt',
10
+ sslcert: 'app/generated_certs/127.0.0.1/node.127.0.0.1.crt',
11
+ sslkey: 'app/generated_certs/127.0.0.1/node.127.0.0.1.key'
12
+ )
13
+
14
+ $stdout.sync = true
15
+ # fd = File.open("pg_trace.log", "a+")
16
+ # conn.trace(fd)
17
+
18
+ begin
19
+ # Validate connection is working
20
+ res = conn.exec("SELECT version();")
21
+ res.each_row do |row|
22
+ puts "You are connected to: #{row[0]}"
23
+ end
24
+ # 53*511
25
+ # 53*767
26
+ # 53*1023
27
+ # 53*1279
28
+ # 7*1817
29
+ # 11*1487
30
+ # 13*1363
31
+ # 16*1211
32
+ # 18*1128
33
+ # 22*1984
34
+ # 27*1723
35
+
36
+ (22..53).each do |m|
37
+ (1..2048).each do |l|
38
+ hanging_query = "SELECT lpad(''::text, #{m}, '0') FROM generate_series(1, #{l});"
39
+ puts "Executing hanging query: #{hanging_query}"
40
+ conn.exec(hanging_query)
41
+ end
42
+ end
43
+ ensure
44
+ conn.close if conn
45
+ end
data/pg.gemspec CHANGED
@@ -10,15 +10,17 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["ged@FaerieMUD.org", "lars@greiz-reinsdorf.de"]
11
11
 
12
12
  spec.summary = "Pg is the Ruby interface to the PostgreSQL RDBMS"
13
- spec.description = "Pg is the Ruby interface to the PostgreSQL RDBMS. It works with PostgreSQL 9.3 and later."
13
+ spec.description = "Pg is the Ruby interface to the PostgreSQL RDBMS. It works with PostgreSQL 10 and later."
14
14
  spec.homepage = "https://github.com/ged/ruby-pg"
15
15
  spec.license = "BSD-2-Clause"
16
- spec.required_ruby_version = ">= 2.5"
16
+ spec.required_ruby_version = ">= 2.7"
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = "https://github.com/ged/ruby-pg"
20
- spec.metadata["changelog_uri"] = "https://github.com/ged/ruby-pg/blob/master/History.md"
20
+ spec.metadata["changelog_uri"] = "https://github.com/ged/ruby-pg/blob/master/CHANGELOG.md"
21
21
  spec.metadata["documentation_uri"] = "http://deveiate.org/code/pg"
22
+ # https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers#msys2-library-dependency
23
+ spec.metadata["msys2_mingw_dependencies"] = "postgresql"
22
24
 
23
25
  # Specify which files should be added to the gem when it is released.
24
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -0,0 +1,30 @@
1
+ From e82c1b395162ea71279ea2170259383082e41ab0 Mon Sep 17 00:00:00 2001
2
+ From: Lars Kanis <lars@greiz-reinsdorf.de>
3
+ Date: Sat, 12 Jul 2025 10:55:17 +0200
4
+ Subject: [PATCH] Allow static linking krb5 library
5
+
6
+ Otherwise it fails with:
7
+ Undefined symbols for architecture arm64:
8
+ "_krb5int_c_mit_des_zeroblock", referenced from:
9
+ _krb5int_des3_cbc_encrypt in libk5crypto.a(d3_aead.o)
10
+ _krb5int_des3_cbc_decrypt in libk5crypto.a(d3_aead.o)
11
+ ---
12
+ src/lib/crypto/builtin/des/des_int.h | 2 +-
13
+ 1 file changed, 1 insertion(+), 1 deletion(-)
14
+
15
+ diff --git a/src/lib/crypto/builtin/des/des_int.h b/src/lib/crypto/builtin/des/des_int.h
16
+ index 46fed7dbd..114e48ebd 100644
17
+ --- a/lib/crypto/builtin/des/des_int.h
18
+ +++ b/lib/crypto/builtin/des/des_int.h
19
+ @@ -159,7 +159,7 @@ mit_des_cbc_encrypt(const mit_des_cblock *in, mit_des_cblock *out,
20
+ const mit_des_cblock ivec, int enc);
21
+
22
+ #define mit_des_zeroblock krb5int_c_mit_des_zeroblock
23
+ -extern const mit_des_cblock mit_des_zeroblock;
24
+ +const mit_des_cblock mit_des_zeroblock;
25
+
26
+ /* fin_rndkey.c */
27
+ krb5_error_code mit_des_finish_random_key(const krb5_encrypt_block *,
28
+ --
29
+ 2.43.0
30
+