pg 1.5.9 → 1.6.0.rc2
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/{History.md → CHANGELOG.md} +54 -0
- data/Gemfile +7 -4
- data/README-Windows.rdoc +1 -1
- data/README.ja.md +4 -4
- data/README.md +6 -5
- data/Rakefile +80 -13
- data/ext/extconf.rb +158 -14
- data/ext/gvl_wrappers.c +13 -2
- data/ext/gvl_wrappers.h +33 -0
- data/ext/pg.c +16 -5
- data/ext/pg.h +9 -9
- data/ext/pg_binary_decoder.c +150 -0
- data/ext/pg_binary_encoder.c +210 -7
- data/ext/pg_cancel_connection.c +360 -0
- data/ext/pg_coder.c +52 -5
- data/ext/pg_connection.c +368 -158
- data/ext/pg_copy_coder.c +2 -2
- data/ext/pg_record_coder.c +1 -1
- data/ext/pg_result.c +9 -11
- data/ext/pg_text_encoder.c +20 -7
- data/ext/pg_tuple.c +2 -2
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +1 -1
- data/ext/pg_type_map_by_class.c +1 -1
- data/ext/pg_type_map_by_column.c +2 -1
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -1
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/lib/pg/basic_type_map_for_queries.rb +7 -3
- data/lib/pg/basic_type_registry.rb +2 -2
- data/lib/pg/cancel_connection.rb +53 -0
- data/lib/pg/coder.rb +2 -1
- data/lib/pg/connection.rb +252 -131
- data/lib/pg/version.rb +1 -1
- data/lib/pg.rb +13 -8
- data/misc/yugabyte/Dockerfile +9 -0
- data/misc/yugabyte/docker-compose.yml +28 -0
- data/misc/yugabyte/pg-test.rb +45 -0
- data/pg.gemspec +5 -3
- data/ports/patches/krb5/1.21.3/0001-Allow-static-linking-krb5-library.patch +30 -0
- data/ports/patches/openssl/3.5.1/0001-aarch64-mingw.patch +21 -0
- data/ports/patches/postgresql/17.5/0001-Use-workaround-of-__builtin_setjmp-only-on-MINGW-on-.patch +42 -0
- data/ports/patches/postgresql/17.5/0001-libpq-Process-buffered-SSL-read-bytes-to-support-rec.patch +52 -0
- data/rakelib/pg_gem_helper.rb +64 -0
- data.tar.gz.sig +0 -0
- metadata +32 -21
- metadata.gz.sig +0 -0
- data/Manifest.txt +0 -72
- 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
|
-
#
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
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
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
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
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
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
|
-
|
653
|
-
|
633
|
+
else
|
634
|
+
|
635
|
+
# PostgreSQL < 17
|
654
636
|
|
655
|
-
|
656
|
-
|
657
|
-
|
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
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
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
|
-
#
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
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
|
-
|
685
|
-
|
739
|
+
finish
|
740
|
+
raise PG::ConnectionBad.new(connection_errors.join("\n").b, connection: self)
|
686
741
|
end
|
687
742
|
end
|
688
|
-
|
689
|
-
|
690
|
-
|
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
|
-
|
703
|
-
|
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
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
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
|
-
#
|
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
|
-
#
|
768
|
-
#
|
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 =
|
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
|
-
:
|
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
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
|
-
|
10
|
-
|
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
|
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
|
-
#
|
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
|
-
|
44
|
-
#
|
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
|
-
|
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
|
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.
|
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/
|
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
|
+
|