pg 1.5.9 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/{History.md → CHANGELOG.md} +46 -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 +254 -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 || [1]).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,18 @@ 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's specified several times.
|
867
|
+
# It's still possible to do load balancing with +load_balance_hosts+ set to +random+ and to increase the number of connections a node gets, when the hostname is provided multiple times in the host string.
|
868
|
+
# This is because in non-timeout cases the host is tried multiple times.
|
769
869
|
#
|
770
|
-
# Raises a PG::Error if the connection fails.
|
771
870
|
def new(*args)
|
772
871
|
conn = connect_to_hosts(*args)
|
773
872
|
|
@@ -825,6 +924,14 @@ class PG::Connection
|
|
825
924
|
iopts = PG::Connection.conninfo_parse(option_string).each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }
|
826
925
|
iopts = PG::Connection.conndefaults.each_with_object({}){|h, o| o[h[:keyword].to_sym] = h[:val] if h[:val] }.merge(iopts)
|
827
926
|
|
927
|
+
if PG::BUNDLED_LIBPQ_WITH_UNIXSOCKET && iopts[:host].to_s.empty? && iopts[:hostaddr].to_s.empty?
|
928
|
+
# Many distors patch the hardcoded default UnixSocket path in libpq to /var/run/postgresql instead of /tmp .
|
929
|
+
# We simply try them all.
|
930
|
+
iopts[:host] = "/var/run/postgresql" + # Ubuntu, Debian, Fedora, Opensuse
|
931
|
+
",/run/postgresql" + # Alpine, Archlinux, Gentoo
|
932
|
+
",/tmp" # Stock PostgreSQL
|
933
|
+
end
|
934
|
+
|
828
935
|
iopts_for_reset = iopts
|
829
936
|
if iopts[:hostaddr]
|
830
937
|
# hostaddr is provided -> no need to resolve hostnames
|
@@ -897,14 +1004,29 @@ class PG::Connection
|
|
897
1004
|
private_constant :REDIRECT_CLASS_METHODS
|
898
1005
|
|
899
1006
|
# These methods are affected by PQsetnonblocking
|
900
|
-
REDIRECT_SEND_METHODS =
|
1007
|
+
REDIRECT_SEND_METHODS = {
|
901
1008
|
:isnonblocking => [:async_isnonblocking, :sync_isnonblocking],
|
902
1009
|
:nonblocking? => [:async_isnonblocking, :sync_isnonblocking],
|
903
1010
|
:put_copy_data => [:async_put_copy_data, :sync_put_copy_data],
|
904
1011
|
:put_copy_end => [:async_put_copy_end, :sync_put_copy_end],
|
905
1012
|
:flush => [:async_flush, :sync_flush],
|
906
|
-
}
|
1013
|
+
}
|
907
1014
|
private_constant :REDIRECT_SEND_METHODS
|
1015
|
+
if PG::Connection.instance_methods.include? :sync_pipeline_sync
|
1016
|
+
if PG::Connection.instance_methods.include? :send_pipeline_sync
|
1017
|
+
# PostgreSQL-17+
|
1018
|
+
REDIRECT_SEND_METHODS.merge!({
|
1019
|
+
:pipeline_sync => [:async_pipeline_sync, :sync_pipeline_sync],
|
1020
|
+
})
|
1021
|
+
else
|
1022
|
+
# PostgreSQL-14+
|
1023
|
+
REDIRECT_SEND_METHODS.merge!({
|
1024
|
+
:pipeline_sync => [:sync_pipeline_sync, :sync_pipeline_sync],
|
1025
|
+
})
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
PG.make_shareable(REDIRECT_SEND_METHODS)
|
1029
|
+
|
908
1030
|
REDIRECT_METHODS = {
|
909
1031
|
:exec => [:async_exec, :sync_exec],
|
910
1032
|
:query => [:async_exec, :sync_exec],
|
@@ -921,12 +1043,13 @@ class PG::Connection
|
|
921
1043
|
:set_client_encoding => [:async_set_client_encoding, :sync_set_client_encoding],
|
922
1044
|
:client_encoding= => [:async_set_client_encoding, :sync_set_client_encoding],
|
923
1045
|
:cancel => [:async_cancel, :sync_cancel],
|
1046
|
+
:encrypt_password => [:async_encrypt_password, :sync_encrypt_password],
|
924
1047
|
}
|
925
1048
|
private_constant :REDIRECT_METHODS
|
926
|
-
|
927
|
-
if PG::Connection.instance_methods.include? :async_encrypt_password
|
1049
|
+
if PG::Connection.instance_methods.include? :async_close_prepared
|
928
1050
|
REDIRECT_METHODS.merge!({
|
929
|
-
:
|
1051
|
+
:close_prepared => [:async_close_prepared, :sync_close_prepared],
|
1052
|
+
:close_portal => [:async_close_portal, :sync_close_portal],
|
930
1053
|
})
|
931
1054
|
end
|
932
1055
|
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.
|