Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rubygem-pg for openSUSE:Factory 
checked in at 2022-08-09 15:26:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-pg (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-pg.new.1521 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-pg"

Tue Aug  9 15:26:47 2022 rev:42 rq:993509 version:1.4.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-pg/rubygem-pg.changes    2022-04-30 
22:52:53.272252667 +0200
+++ /work/SRC/openSUSE:Factory/.rubygem-pg.new.1521/rubygem-pg.changes  
2022-08-09 15:27:02.989401882 +0200
@@ -1,0 +2,47 @@
+Thu Aug  4 13:22:33 UTC 2022 - Stephan Kulow <co...@suse.com>
+
+updated to version 1.4.2
+ see installed History.rdoc
+
+  == v1.4.2 [2022-07-27] Lars Kanis <l...@greiz-reinsdorf.de>
+  
+  Bugfixes:
+  
+  - Properly handle empty host parameter when connecting. #471
+  - Update Windows fat binary gem to OpenSSL-1.1.1q.
+  
+  
+  == v1.4.1 [2022-06-24] Lars Kanis <l...@greiz-reinsdorf.de>
+  
+  Bugfixes:
+  
+  - Fix another ruby-2.7 keyword warning. #465
+  - Allow PG::Error to be created without arguments. #466
+  
+  
+  == v1.4.0 [2022-06-20] Lars Kanis <l...@greiz-reinsdorf.de>
+  
+  Added:
+  
+  - Add PG::Connection#hostaddr, present since PostgreSQL-12. #453
+  - Add PG::Connection.conninfo_parse to wrap PQconninfoParse. #453
+  
+  Bugfixes:
+  
+  - Try IPv6 and IPv4 addresses, if DNS resolves to both. #452
+  - Re-add block-call semantics to PG::Connection.new accidently removed in 
pg-1.3.0. #454
+  - Handle client error after all data consumed in #copy_data for output. #455
+  - Avoid spurious keyword argument warning on Ruby 2.7. #456
+  - Change connection setup to respect connect_timeout parameter. #459
+  - Fix indefinite hang in case of connection error on Windows #458
+  - Set connection attribute of PG::Error in various places where it was 
missing. #461
+  - Fix transaction leak on early break/return. #463
+  - Update Windows fat binary gem to OpenSSL-1.1.1o and PostgreSQL-14.4.
+  
+  Enhancements:
+  
+  - Don't flush at each put_copy_data call, but flush at get_result. #462
+  
+  
+
+-------------------------------------------------------------------

Old:
----
  pg-1.3.5.gem

New:
----
  pg-1.4.2.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-pg.spec ++++++
--- /var/tmp/diff_new_pack.RpuyAr/_old  2022-08-09 15:27:03.549403482 +0200
+++ /var/tmp/diff_new_pack.RpuyAr/_new  2022-08-09 15:27:03.553403493 +0200
@@ -24,7 +24,7 @@
 #
 
 Name:           rubygem-pg
-Version:        1.3.5
+Version:        1.4.2
 Release:        0
 %define mod_name pg
 %define mod_full_name %{mod_name}-%{version}

++++++ pg-1.3.5.gem -> pg-1.4.2.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/History.rdoc new/History.rdoc
--- old/History.rdoc    2022-03-31 14:39:47.000000000 +0200
+++ new/History.rdoc    2022-07-27 10:04:31.000000000 +0200
@@ -1,3 +1,43 @@
+== v1.4.2 [2022-07-27] Lars Kanis <l...@greiz-reinsdorf.de>
+
+Bugfixes:
+
+- Properly handle empty host parameter when connecting. #471
+- Update Windows fat binary gem to OpenSSL-1.1.1q.
+
+
+== v1.4.1 [2022-06-24] Lars Kanis <l...@greiz-reinsdorf.de>
+
+Bugfixes:
+
+- Fix another ruby-2.7 keyword warning. #465
+- Allow PG::Error to be created without arguments. #466
+
+
+== v1.4.0 [2022-06-20] Lars Kanis <l...@greiz-reinsdorf.de>
+
+Added:
+
+- Add PG::Connection#hostaddr, present since PostgreSQL-12. #453
+- Add PG::Connection.conninfo_parse to wrap PQconninfoParse. #453
+
+Bugfixes:
+
+- Try IPv6 and IPv4 addresses, if DNS resolves to both. #452
+- Re-add block-call semantics to PG::Connection.new accidently removed in 
pg-1.3.0. #454
+- Handle client error after all data consumed in #copy_data for output. #455
+- Avoid spurious keyword argument warning on Ruby 2.7. #456
+- Change connection setup to respect connect_timeout parameter. #459
+- Fix indefinite hang in case of connection error on Windows #458
+- Set connection attribute of PG::Error in various places where it was 
missing. #461
+- Fix transaction leak on early break/return. #463
+- Update Windows fat binary gem to OpenSSL-1.1.1o and PostgreSQL-14.4.
+
+Enhancements:
+
+- Don't flush at each put_copy_data call, but flush at get_result. #462
+
+
 == v1.3.5 [2022-03-31] Lars Kanis <l...@greiz-reinsdorf.de>
 
 Bugfixes:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Rakefile.cross new/Rakefile.cross
--- old/Rakefile.cross  2022-03-31 14:39:47.000000000 +0200
+++ new/Rakefile.cross  2022-07-27 10:04:31.000000000 +0200
@@ -31,8 +31,8 @@
                self.host_platform              = toolchain
 
                # Cross-compilation constants
-               self.openssl_version            = ENV['OPENSSL_VERSION'] || 
'1.1.1m'
-               self.postgresql_version         = ENV['POSTGRESQL_VERSION'] || 
'14.2'
+               self.openssl_version            = ENV['OPENSSL_VERSION'] || 
'1.1.1q'
+               self.postgresql_version         = ENV['POSTGRESQL_VERSION'] || 
'14.4'
 
                # Check if symlinks work in the current working directory.
                # This fails, if rake-compiler-dock is running on a Windows box.
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
Binary files old/checksums.yaml.gz.sig and new/checksums.yaml.gz.sig differ
Binary files old/data.tar.gz.sig and new/data.tar.gz.sig differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ext/extconf.rb new/ext/extconf.rb
--- old/ext/extconf.rb  2022-03-31 14:39:47.000000000 +0200
+++ new/ext/extconf.rb  2022-07-27 10:04:31.000000000 +0200
@@ -1,5 +1,3 @@
-# -*- encoding: utf-8 -*-
-
 require 'pp'
 require 'mkmf'
 
@@ -169,32 +167,3 @@
 create_header()
 create_makefile( "pg_ext" )
 
-
-def message!(important_message)
-       message important_message
-       if !$stdout.tty? && File.chardev?('/dev/tty')
-               File.open('/dev/tty', 'w') do |tty|
-                       tty.print important_message
-               end
-       end
-rescue
-end
-
-if "2022-04-01" == Time.now.strftime("%Y-%m-%d")
-       message! <<-EOM
-===================================================================
-HEADS UP! Prepare for pg-1.4.2022 ! ????????????
-
-Now that psycopg3 has most of the features of ruby-pg, we plan to
-switch to using it as our foundation for the next pg release.
-It will run through pycall and requires a working python setup.
-This will minimize our development and maintenance efforts, since
-it allows us to use one and the same code base for both programming
-languages - ruby and python. ????????
-
-And we follow the recent merge of the Django and Rails teams! ??????
-
-Stay up-to-date at https://github.com/ged/ruby-pg/issues/449
-===================================================================
-EOM
-end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ext/pg_connection.c new/ext/pg_connection.c
--- old/ext/pg_connection.c     2022-03-31 14:39:47.000000000 +0200
+++ new/ext/pg_connection.c     2022-07-27 10:04:31.000000000 +0200
@@ -24,12 +24,33 @@
 static VALUE pgconn_wait_for_flush( VALUE self );
 static void pgconn_set_internal_encoding_index( VALUE );
 static const rb_data_type_t pg_connection_type;
+static VALUE pgconn_async_flush(VALUE self);
 
 /*
  * Global functions
  */
 
 /*
+ * Convenience function to raise connection errors
+ */
+#ifdef __GNUC__
+__attribute__((format(printf, 3, 4)))
+#endif
+static void
+pg_raise_conn_error( VALUE klass, VALUE self, const char *format, ...)
+{
+       VALUE msg, error;
+       va_list ap;
+
+       va_start(ap, format);
+       msg = rb_vsprintf(format, ap);
+       va_end(ap);
+       error = rb_exc_new_str(klass, msg);
+       rb_iv_set(error, "@connection", self);
+       rb_exc_raise(error);
+}
+
+/*
  * Fetch the PG::Connection object data pointer.
  */
 t_pg_connection *
@@ -52,7 +73,7 @@
        TypedData_Get_Struct( self, t_pg_connection, &pg_connection_type, this);
 
        if ( !this->pgconn )
-               rb_raise( rb_eConnectionBad, "connection is closed" );
+               pg_raise_conn_error( rb_eConnectionBad, self, "connection is 
closed");
 
        return this;
 }
@@ -70,8 +91,9 @@
        t_pg_connection *this;
        TypedData_Get_Struct( self, t_pg_connection, &pg_connection_type, this);
 
-       if ( !this->pgconn )
-               rb_raise( rb_eConnectionBad, "connection is closed" );
+       if ( !this->pgconn ){
+               pg_raise_conn_error( rb_eConnectionBad, self, "connection is 
closed");
+       }
 
        return this->pgconn;
 }
@@ -89,9 +111,8 @@
 
        if ( RTEST(socket_io) ) {
 #if defined(_WIN32)
-               if( rb_w32_unwrap_io_handle(this->ruby_sd) ){
-                       rb_raise(rb_eConnectionBad, "Could not unwrap win32 
socket handle");
-               }
+               if( rb_w32_unwrap_io_handle(this->ruby_sd) )
+                       pg_raise_conn_error( rb_eConnectionBad, self, "Could 
not unwrap win32 socket handle");
 #endif
                rb_funcall( socket_io, rb_intern("close"), 0 );
        }
@@ -254,7 +275,6 @@
 {
        t_pg_connection *this;
        VALUE conninfo;
-       VALUE error;
        VALUE self = pgconn_s_allocate( klass );
 
        this = pg_get_connection( self );
@@ -262,13 +282,10 @@
        this->pgconn = gvl_PQconnectdb(StringValueCStr(conninfo));
 
        if(this->pgconn == NULL)
-               rb_raise(rb_ePGerror, "PQconnectdb() unable to allocate 
structure");
+               rb_raise(rb_ePGerror, "PQconnectdb() unable to allocate PGconn 
structure");
 
-       if (PQstatus(this->pgconn) == CONNECTION_BAD) {
-               error = rb_exc_new2(rb_eConnectionBad, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if (PQstatus(this->pgconn) == CONNECTION_BAD)
+               pg_raise_conn_error( rb_eConnectionBad, self, "%s", 
PQerrorMessage(this->pgconn));
 
        pgconn_set_default_encoding( self );
 
@@ -301,7 +318,6 @@
 {
        VALUE rb_conn;
        VALUE conninfo;
-       VALUE error;
        t_pg_connection *this;
 
        /*
@@ -314,13 +330,10 @@
        this->pgconn = gvl_PQconnectStart( StringValueCStr(conninfo) );
 
        if( this->pgconn == NULL )
-               rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate 
structure");
+               rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate 
PGconn structure");
 
-       if ( PQstatus(this->pgconn) == CONNECTION_BAD ) {
-               error = rb_exc_new2(rb_eConnectionBad, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", rb_conn);
-               rb_exc_raise(error);
-       }
+       if ( PQstatus(this->pgconn) == CONNECTION_BAD )
+               pg_raise_conn_error( rb_eConnectionBad, rb_conn, "%s", 
PQerrorMessage(this->pgconn));
 
        if ( rb_block_given_p() ) {
                return rb_ensure( rb_yield, rb_conn, pgconn_finish, rb_conn );
@@ -376,6 +389,36 @@
        return array;
 }
 
+/*
+ * Document-method: PG::Connection.conninfo_parse
+ *
+ * call-seq:
+ *    PG::Connection.conninfo_parse(conninfo_string) -> Array
+ *
+ * Returns parsed connection options from the provided connection string as an 
array of hashes.
+ * Each hash has the same keys as PG::Connection.conndefaults() .
+ * The values from the +conninfo_string+ are stored in the +:val+ key.
+ */
+static VALUE
+pgconn_s_conninfo_parse(VALUE self, VALUE conninfo)
+{
+       VALUE array;
+       char *errmsg = NULL;
+       PQconninfoOption *options = PQconninfoParse(StringValueCStr(conninfo), 
&errmsg);
+       if(errmsg){
+               VALUE error = rb_str_new_cstr(errmsg);
+               PQfreemem(errmsg);
+               rb_raise(rb_ePGerror, "%"PRIsVALUE, error);
+       }
+       array = pgconn_make_conninfo_array( options );
+
+       PQconninfoFree(options);
+
+       UNUSED( self );
+
+       return array;
+}
+
 
 #ifdef HAVE_PQENCRYPTPASSWORDCONN
 static VALUE
@@ -396,7 +439,7 @@
                rval = rb_str_new2( encrypted );
                PQfreemem( encrypted );
        } else {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
        }
 
        return rval;
@@ -537,7 +580,7 @@
 {
        pgconn_close_socket_io( self );
        if(gvl_PQresetStart(pg_get_pgconn(self)) == 0)
-               rb_raise(rb_eUnableToSend, "reset has failed");
+               pg_raise_conn_error( rb_eUnableToSend, self, "reset has 
failed");
        return Qnil;
 }
 
@@ -607,7 +650,18 @@
  * call-seq:
  *    conn.host()
  *
- * Returns the connected server name.
+ * Returns the server host name of the active connection.
+ * This can be a host name, an IP address, or a directory path if the 
connection is via Unix socket.
+ * (The path case can be distinguished because it will always be an absolute 
path, beginning with +/+ .)
+ *
+ * If the connection parameters specified both host and hostaddr, then +host+ 
will return the host information.
+ * If only hostaddr was specified, then that is returned.
+ * If multiple hosts were specified in the connection parameters, +host+ 
returns the host actually connected to.
+ *
+ * If there is an error producing the host information (perhaps if the 
connection has not been fully established or there was an error), it returns an 
empty string.
+ *
+ * If multiple hosts were specified in the connection parameters, it is not 
possible to rely on the result of +host+ until the connection is established.
+ * The status of the connection can be checked using the function 
Connection#status .
  */
 static VALUE
 pgconn_host(VALUE self)
@@ -617,6 +671,26 @@
        return rb_str_new2(host);
 }
 
+/* PQhostaddr() appeared in PostgreSQL-12 together with PQresultMemorySize() */
+#if defined(HAVE_PQRESULTMEMORYSIZE)
+/*
+ * call-seq:
+ *    conn.hostaddr()
+ *
+ * Returns the server IP address of the active connection.
+ * This can be the address that a host name resolved to, or an IP address 
provided through the hostaddr parameter.
+ * If there is an error producing the host information (perhaps if the 
connection has not been fully established or there was an error), it returns an 
empty string.
+ *
+ */
+static VALUE
+pgconn_hostaddr(VALUE self)
+{
+       char *host = PQhostaddr(pg_get_pgconn(self));
+       if (!host) return Qnil;
+       return rb_str_new2(host);
+}
+#endif
+
 /*
  * call-seq:
  *    conn.port()
@@ -687,6 +761,9 @@
  *   PG::Constants::CONNECTION_BAD
  *
  * ... and other constants of kind PG::Constants::CONNECTION_*
+ *
+ * Example:
+ *   PG.constants.grep(/CONNECTION_/).find{|c| PG.const_get(c) == conn.status} 
# => :CONNECTION_OK
  */
 static VALUE
 pgconn_status(VALUE self)
@@ -811,7 +888,8 @@
        pg_deprecated(4, ("conn.socket is deprecated and should be replaced by 
conn.socket_io"));
 
        if( (sd = PQsocket(pg_get_pgconn(self))) < 0)
-               rb_raise(rb_eConnectionBad, "PQsocket() can't get socket 
descriptor");
+               pg_raise_conn_error( rb_eConnectionBad, self, "PQsocket() can't 
get socket descriptor");
+
        return INT2NUM(sd);
 }
 
@@ -839,14 +917,15 @@
        VALUE socket_io = this->socket_io;
 
        if ( !RTEST(socket_io) ) {
-               if( (sd = PQsocket(this->pgconn)) < 0)
-                       rb_raise(rb_eConnectionBad, "PQsocket() can't get 
socket descriptor");
+               if( (sd = PQsocket(this->pgconn)) < 0){
+                       pg_raise_conn_error( rb_eConnectionBad, self, 
"PQsocket() can't get socket descriptor");
+               }
 
                #ifdef _WIN32
                        ruby_sd = rb_w32_wrap_io_handle((HANDLE)(intptr_t)sd, 
O_RDWR|O_BINARY|O_NOINHERIT);
-                       if( ruby_sd == -1 ){
-                               rb_raise(rb_eConnectionBad, "Could not wrap 
win32 socket handle");
-                       }
+                       if( ruby_sd == -1 )
+                               pg_raise_conn_error( rb_eConnectionBad, self, 
"Could not wrap win32 socket handle");
+
                        this->ruby_sd = ruby_sd;
                #else
                        ruby_sd = sd;
@@ -911,7 +990,7 @@
 
        cancel = (struct pg_cancel*)PQgetCancel(conn);
        if(cancel == NULL)
-               rb_raise(rb_ePGerror,"Invalid connection!");
+               pg_raise_conn_error( rb_ePGerror, self, "Invalid connection!");
 
        if( cancel->be_pid != PQbackendPID(conn) )
                rb_raise(rb_ePGerror,"Unexpected binary struct layout - please 
file a bug report at ruby-pg!");
@@ -1540,9 +1619,9 @@
        if( !singleton ) {
                size = PQescapeStringConn(pg_get_pgconn(self), 
RSTRING_PTR(result),
                        RSTRING_PTR(string), RSTRING_LEN(string), &error);
-               if(error) {
-                       rb_raise(rb_ePGerror, "%s", 
PQerrorMessage(pg_get_pgconn(self)));
-               }
+               if(error)
+                       pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(pg_get_pgconn(self)));
+
        } else {
                size = PQescapeString(RSTRING_PTR(result), RSTRING_PTR(string), 
RSTRING_LEN(string));
        }
@@ -1638,7 +1717,6 @@
 {
        t_pg_connection *this = pg_get_connection_safe( self );
        char *escaped = NULL;
-       VALUE error;
        VALUE result = Qnil;
        int enc_idx = this->enc_idx;
 
@@ -1649,12 +1727,8 @@
 
        escaped = PQescapeLiteral(this->pgconn, RSTRING_PTR(string), 
RSTRING_LEN(string));
        if (escaped == NULL)
-       {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-               return Qnil;
-       }
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(this->pgconn));
+
        result = rb_str_new2(escaped);
        PQfreemem(escaped);
        PG_ENCODING_SET_NOCHECK(result, enc_idx);
@@ -1677,7 +1751,6 @@
 {
        t_pg_connection *this = pg_get_connection_safe( self );
        char *escaped = NULL;
-       VALUE error;
        VALUE result = Qnil;
        int enc_idx = this->enc_idx;
 
@@ -1688,12 +1761,8 @@
 
        escaped = PQescapeIdentifier(this->pgconn, RSTRING_PTR(string), 
RSTRING_LEN(string));
        if (escaped == NULL)
-       {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-               return Qnil;
-       }
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(this->pgconn));
+
        result = rb_str_new2(escaped);
        PQfreemem(escaped);
        PG_ENCODING_SET_NOCHECK(result, enc_idx);
@@ -1741,14 +1810,9 @@
 pgconn_set_single_row_mode(VALUE self)
 {
        PGconn *conn = pg_get_pgconn(self);
-       VALUE error;
 
        if( PQsetSingleRowMode(conn) == 0 )
-       {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
 
        return self;
 }
@@ -1772,15 +1836,12 @@
 pgconn_send_query(int argc, VALUE *argv, VALUE self)
 {
        t_pg_connection *this = pg_get_connection_safe( self );
-       VALUE error;
 
        /* If called with no or nil parameters, use PQexec for compatibility */
        if ( argc == 1 || (argc >= 2 && argc <= 4 && NIL_P(argv[1]) )) {
-               if(gvl_PQsendQuery(this->pgconn, pg_cstr_enc(argv[0], 
this->enc_idx)) == 0) {
-                       error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-                       rb_iv_set(error, "@connection", self);
-                       rb_exc_raise(error);
-               }
+               if(gvl_PQsendQuery(this->pgconn, pg_cstr_enc(argv[0], 
this->enc_idx)) == 0)
+                       pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
+
                pgconn_wait_for_flush( self );
                return Qnil;
        }
@@ -1837,7 +1898,6 @@
        t_pg_connection *this = pg_get_connection_safe( self );
        int result;
        VALUE command, in_res_fmt;
-       VALUE error;
        int nParams;
        int resultFormat;
        struct query_params_data paramsData = { this->enc_idx };
@@ -1854,11 +1914,9 @@
 
        free_query_params( &paramsData );
 
-       if(result == 0) {
-               error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(result == 0)
+               pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
+
        pgconn_wait_for_flush( self );
        return Qnil;
 }
@@ -1890,7 +1948,6 @@
        int result;
        VALUE name, command, in_paramtypes;
        VALUE param;
-       VALUE error;
        int i = 0;
        int nParams = 0;
        Oid *paramTypes = NULL;
@@ -1919,9 +1976,7 @@
        xfree(paramTypes);
 
        if(result == 0) {
-               error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
+               pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
        }
        pgconn_wait_for_flush( self );
        return Qnil;
@@ -1965,7 +2020,6 @@
        t_pg_connection *this = pg_get_connection_safe( self );
        int result;
        VALUE name, in_res_fmt;
-       VALUE error;
        int nParams;
        int resultFormat;
        struct query_params_data paramsData = { this->enc_idx };
@@ -1987,11 +2041,9 @@
 
        free_query_params( &paramsData );
 
-       if(result == 0) {
-               error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(result == 0)
+               pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
+
        pgconn_wait_for_flush( self );
        return Qnil;
 }
@@ -2006,14 +2058,11 @@
 static VALUE
 pgconn_send_describe_prepared(VALUE self, VALUE stmt_name)
 {
-       VALUE error;
        t_pg_connection *this = pg_get_connection_safe( self );
        /* returns 0 on failure */
-       if(gvl_PQsendDescribePrepared(this->pgconn, pg_cstr_enc(stmt_name, 
this->enc_idx)) == 0) {
-               error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(gvl_PQsendDescribePrepared(this->pgconn, pg_cstr_enc(stmt_name, 
this->enc_idx)) == 0)
+               pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
+
        pgconn_wait_for_flush( self );
        return Qnil;
 }
@@ -2029,14 +2078,11 @@
 static VALUE
 pgconn_send_describe_portal(VALUE self, VALUE portal)
 {
-       VALUE error;
        t_pg_connection *this = pg_get_connection_safe( self );
        /* returns 0 on failure */
-       if(gvl_PQsendDescribePortal(this->pgconn, pg_cstr_enc(portal, 
this->enc_idx)) == 0) {
-               error = rb_exc_new2(rb_eUnableToSend, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(gvl_PQsendDescribePortal(this->pgconn, pg_cstr_enc(portal, 
this->enc_idx)) == 0)
+               pg_raise_conn_error( rb_eUnableToSend, self, "%s", 
PQerrorMessage(this->pgconn));
+
        pgconn_wait_for_flush( self );
        return Qnil;
 }
@@ -2069,18 +2115,15 @@
  * or *notifies* to see if the state has changed.
  */
 static VALUE
-pgconn_consume_input(self)
-       VALUE self;
+pgconn_consume_input(VALUE self)
 {
-       VALUE error;
        PGconn *conn = pg_get_pgconn(self);
        /* returns 0 on error */
        if(PQconsumeInput(conn) == 0) {
                pgconn_close_socket_io(self);
-               error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(conn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
+               pg_raise_conn_error( rb_eConnectionBad, self, "%s", 
PQerrorMessage(conn));
        }
+
        return Qnil;
 }
 
@@ -2092,18 +2135,15 @@
  * #get_result would block. Otherwise returns +false+.
  */
 static VALUE
-pgconn_is_busy(self)
-       VALUE self;
+pgconn_is_busy(VALUE self)
 {
        return gvl_PQisBusy(pg_get_pgconn(self)) ? Qtrue : Qfalse;
 }
 
 static VALUE
-pgconn_sync_setnonblocking(self, state)
-       VALUE self, state;
+pgconn_sync_setnonblocking(VALUE self, VALUE state)
 {
        int arg;
-       VALUE error;
        PGconn *conn = pg_get_pgconn(self);
        if(state == Qtrue)
                arg = 1;
@@ -2112,18 +2152,15 @@
        else
                rb_raise(rb_eArgError, "Boolean value expected");
 
-       if(PQsetnonblocking(conn, arg) == -1) {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(PQsetnonblocking(conn, arg) == -1)
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
        return Qnil;
 }
 
 
 static VALUE
-pgconn_sync_isnonblocking(self)
-       VALUE self;
+pgconn_sync_isnonblocking(VALUE self)
 {
        return PQisnonblocking(pg_get_pgconn(self)) ? Qtrue : Qfalse;
 }
@@ -2132,14 +2169,10 @@
 pgconn_sync_flush(VALUE self)
 {
        PGconn *conn = pg_get_pgconn(self);
-       int ret;
-       VALUE error;
-       ret = PQflush(conn);
-       if(ret == -1) {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(conn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       int ret = PQflush(conn);
+       if(ret == -1)
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
        return (ret) ? Qfalse : Qtrue;
 }
 
@@ -2153,7 +2186,7 @@
 
        cancel = PQgetCancel(pg_get_pgconn(self));
        if(cancel == NULL)
-               rb_raise(rb_ePGerror,"Invalid connection!");
+               pg_raise_conn_error( rb_ePGerror, self, "Invalid connection!");
 
        ret = gvl_PQcancel(cancel, errbuf, sizeof(errbuf));
        if(ret == 1)
@@ -2362,7 +2395,14 @@
 
                /* Is the given timeout valid? */
                if( !ptimeout || (waittime.tv_sec >= 0 && waittime.tv_usec >= 
0) ){
-                       VALUE socket_io = pgconn_socket_io(self);
+                       VALUE socket_io;
+
+                       /* before we wait for data, make sure everything has 
been sent */
+                       pgconn_async_flush(self);
+                       if ((retval=is_readable(conn)))
+                               return retval;
+
+                       socket_io = pgconn_socket_io(self);
                        /* Wait for the socket to become readable before 
checking again */
                        ret = pg_rb_io_wait(socket_io, 
RB_INT2NUM(PG_RUBY_IO_READABLE), wait_timeout);
                } else {
@@ -2377,7 +2417,7 @@
                /* Check for connection errors (PQisBusy is true on connection 
errors) */
                if ( PQconsumeInput(conn) == 0 ){
                        pgconn_close_socket_io(self);
-                       rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", 
PQerrorMessage(conn) );
+                       pg_raise_conn_error(rb_eConnectionBad, self, 
"PQconsumeInput() %s", PQerrorMessage(conn));
                }
        }
 
@@ -2390,8 +2430,8 @@
  *
  * Attempts to flush any queued output data to the server.
  * Returns +true+ if data is successfully flushed, +false+
- * if not (can only return +false+ if connection is
- * nonblocking.
+ * if not. It can only return +false+ if connection is
+ * in nonblocking mode.
  * Raises PG::Error if some other failure occurred.
  */
 static VALUE
@@ -2527,11 +2567,9 @@
        Check_Type(buffer, T_STRING);
 
        ret = gvl_PQputCopyData(this->pgconn, RSTRING_PTR(buffer), 
RSTRING_LENINT(buffer));
-       if(ret == -1) {
-               VALUE error = rb_exc_new2(rb_ePGerror, 
PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(ret == -1)
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(this->pgconn));
+
        RB_GC_GUARD(intermediate);
        RB_GC_GUARD(buffer);
 
@@ -2542,7 +2580,6 @@
 pgconn_sync_put_copy_end(int argc, VALUE *argv, VALUE self)
 {
        VALUE str;
-       VALUE error;
        int ret;
        const char *error_message = NULL;
        t_pg_connection *this = pg_get_connection_safe( self );
@@ -2553,11 +2590,9 @@
                error_message = pg_cstr_enc(str, this->enc_idx);
 
        ret = gvl_PQputCopyEnd(this->pgconn, error_message);
-       if(ret == -1) {
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
-       }
+       if(ret == -1)
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(this->pgconn));
+
        return (ret) ? Qtrue : Qfalse;
 }
 
@@ -2565,7 +2600,6 @@
 pgconn_sync_get_copy_data(int argc, VALUE *argv, VALUE self )
 {
        VALUE async_in;
-       VALUE error;
        VALUE result;
        int ret;
        char *buffer;
@@ -2585,10 +2619,8 @@
        }
 
        ret = gvl_PQgetCopyData(this->pgconn, &buffer, RTEST(async_in));
-       if(ret == -2) { /* error */
-               error = rb_exc_new2(rb_ePGerror, PQerrorMessage(this->pgconn));
-               rb_iv_set(error, "@connection", self);
-               rb_exc_raise(error);
+       if(ret == -2){ /* error */
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(this->pgconn));
        }
        if(ret == -1) { /* No data left */
                return Qnil;
@@ -2893,9 +2925,9 @@
 
        Check_Type(str, T_STRING);
 
-       if ( (gvl_PQsetClientEncoding(conn, StringValueCStr(str))) == -1 ) {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
-       }
+       if ( (gvl_PQsetClientEncoding(conn, StringValueCStr(str))) == -1 )
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
        pgconn_set_internal_encoding_index( self );
 
        return Qnil;
@@ -3528,11 +3560,10 @@
 {
        PGconn *conn = pg_get_pgconn(self);
        int res = PQenterPipelineMode(conn);
-       if( res == 1 ) {
-               return Qnil;
-       } else {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
-       }
+       if( res != 1 )
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
+       return Qnil;
 }
 
 /*
@@ -3551,11 +3582,10 @@
 {
        PGconn *conn = pg_get_pgconn(self);
        int res = PQexitPipelineMode(conn);
-       if( res == 1 ) {
-               return Qnil;
-       } else {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
-       }
+       if( res != 1 )
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
+       return Qnil;
 }
 
 
@@ -3575,11 +3605,10 @@
 {
        PGconn *conn = pg_get_pgconn(self);
        int res = PQpipelineSync(conn);
-       if( res == 1 ) {
-               return Qnil;
-       } else {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
-       }
+       if( res != 1 )
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
+       return Qnil;
 }
 
 /*
@@ -3599,11 +3628,10 @@
 {
        PGconn *conn = pg_get_pgconn(self);
        int res = PQsendFlushRequest(conn);
-       if( res == 1 ) {
-               return Qnil;
-       } else {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
-       }
+       if( res != 1 )
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
+
+       return Qnil;
 }
 
 #endif
@@ -3634,7 +3662,7 @@
 
        lo_oid = lo_creat(conn, mode);
        if (lo_oid == 0)
-               rb_raise(rb_ePGerror, "lo_creat failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_creat failed");
 
        return UINT2NUM(lo_oid);
 }
@@ -3655,7 +3683,7 @@
 
        ret = lo_create(conn, lo_oid);
        if (ret == InvalidOid)
-               rb_raise(rb_ePGerror, "lo_create failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_create failed");
 
        return UINT2NUM(ret);
 }
@@ -3679,7 +3707,7 @@
 
        lo_oid = lo_import(conn, StringValueCStr(filename));
        if (lo_oid == 0) {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
        }
        return UINT2NUM(lo_oid);
 }
@@ -3700,7 +3728,7 @@
        oid = NUM2UINT(lo_oid);
 
        if (lo_export(conn, oid, StringValueCStr(filename)) < 0) {
-               rb_raise(rb_ePGerror, "%s", PQerrorMessage(conn));
+               pg_raise_conn_error( rb_ePGerror, self, "%s", 
PQerrorMessage(conn));
        }
        return Qnil;
 }
@@ -3731,7 +3759,7 @@
                mode = NUM2INT(nmode);
 
        if((fd = lo_open(conn, lo_oid, mode)) < 0) {
-               rb_raise(rb_ePGerror, "can't open large object: %s", 
PQerrorMessage(conn));
+               pg_raise_conn_error( rb_ePGerror, self, "can't open large 
object: %s", PQerrorMessage(conn));
        }
        return INT2FIX(fd);
 }
@@ -3753,11 +3781,11 @@
        Check_Type(buffer, T_STRING);
 
        if( RSTRING_LEN(buffer) < 0) {
-               rb_raise(rb_ePGerror, "write buffer zero string");
+               pg_raise_conn_error( rb_ePGerror, self, "write buffer zero 
string");
        }
        if((n = lo_write(conn, fd, StringValuePtr(buffer),
                                RSTRING_LEN(buffer))) < 0) {
-               rb_raise(rb_ePGerror, "lo_write failed: %s", 
PQerrorMessage(conn));
+               pg_raise_conn_error( rb_ePGerror, self, "lo_write failed: %s", 
PQerrorMessage(conn));
        }
 
        return INT2FIX(n);
@@ -3780,16 +3808,12 @@
        VALUE str;
        char *buffer;
 
-  buffer = ALLOC_N(char, len);
-       if(buffer == NULL)
-               rb_raise(rb_eNoMemError, "ALLOC failed!");
-
-       if (len < 0){
-               rb_raise(rb_ePGerror,"nagative length %d given", len);
-       }
+       if (len < 0)
+               pg_raise_conn_error( rb_ePGerror, self, "negative length %d 
given", len);
 
+       buffer = ALLOC_N(char, len);
        if((ret = lo_read(conn, lo_desc, buffer, len)) < 0)
-               rb_raise(rb_ePGerror, "lo_read failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_read failed");
 
        if(ret == 0) {
                xfree(buffer);
@@ -3819,7 +3843,7 @@
        int ret;
 
        if((ret = lo_lseek(conn, lo_desc, NUM2INT(offset), NUM2INT(whence))) < 
0) {
-               rb_raise(rb_ePGerror, "lo_lseek failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_lseek failed");
        }
 
        return INT2FIX(ret);
@@ -3839,7 +3863,7 @@
        int lo_desc = NUM2INT(in_lo_desc);
 
        if((position = lo_tell(conn, lo_desc)) < 0)
-               rb_raise(rb_ePGerror,"lo_tell failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_tell failed");
 
        return INT2FIX(position);
 }
@@ -3858,7 +3882,7 @@
        size_t len = NUM2INT(in_len);
 
        if(lo_truncate(conn,lo_desc,len) < 0)
-               rb_raise(rb_ePGerror,"lo_truncate failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_truncate failed");
 
        return Qnil;
 }
@@ -3876,7 +3900,7 @@
        int lo_desc = NUM2INT(in_lo_desc);
 
        if(lo_close(conn,lo_desc) < 0)
-               rb_raise(rb_ePGerror,"lo_close failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_close failed");
 
        return Qnil;
 }
@@ -3894,7 +3918,7 @@
        Oid oid = NUM2UINT(in_oid);
 
        if(lo_unlink(conn,oid) < 0)
-               rb_raise(rb_ePGerror,"lo_unlink failed");
+               pg_raise_conn_error( rb_ePGerror, self, "lo_unlink failed");
 
        return Qnil;
 }
@@ -4328,6 +4352,7 @@
        rb_define_singleton_method(rb_cPGconn, "quote_ident", 
pgconn_s_quote_ident, 1);
        rb_define_singleton_method(rb_cPGconn, "connect_start", 
pgconn_s_connect_start, -1);
        rb_define_singleton_method(rb_cPGconn, "conndefaults", 
pgconn_s_conndefaults, 0);
+       rb_define_singleton_method(rb_cPGconn, "conninfo_parse", 
pgconn_s_conninfo_parse, 1);
        rb_define_singleton_method(rb_cPGconn, "sync_ping", pgconn_s_sync_ping, 
-1);
        rb_define_singleton_method(rb_cPGconn, "sync_connect", 
pgconn_s_sync_connect, -1);
 
@@ -4345,6 +4370,9 @@
        rb_define_method(rb_cPGconn, "user", pgconn_user, 0);
        rb_define_method(rb_cPGconn, "pass", pgconn_pass, 0);
        rb_define_method(rb_cPGconn, "host", pgconn_host, 0);
+#if defined(HAVE_PQRESULTMEMORYSIZE)
+       rb_define_method(rb_cPGconn, "hostaddr", pgconn_hostaddr, 0);
+#endif
        rb_define_method(rb_cPGconn, "port", pgconn_port, 0);
        rb_define_method(rb_cPGconn, "tty", pgconn_tty, 0);
        rb_define_method(rb_cPGconn, "conninfo", pgconn_conninfo, 0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ext/pg_result.c new/ext/pg_result.c
--- old/ext/pg_result.c 2022-03-31 14:39:47.000000000 +0200
+++ new/ext/pg_result.c 2022-07-27 10:04:31.000000000 +0200
@@ -1476,10 +1476,10 @@
 
                pgresult = gvl_PQgetResult(pgconn);
                if( pgresult == NULL )
-                       rb_raise( rb_eNoResultError, "no result received - 
possibly an intersection with another result retrieval");
+                       rb_raise( rb_eNoResultError, "no result received - 
possibly an intersection with another query");
 
                if( nfields != PQnfields(pgresult) )
-                       rb_raise( rb_eInvalidChangeOfResultFields, "number of 
fields must not change in single row mode");
+                       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));
 
                this->pgresult = pgresult;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/pg/connection.rb new/lib/pg/connection.rb
--- old/lib/pg/connection.rb    2022-03-31 14:39:47.000000000 +0200
+++ new/lib/pg/connection.rb    2022-07-27 10:04:31.000000000 +0200
@@ -46,37 +46,6 @@
                hash.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
        end
 
-       # Decode a connection string to Hash options
-       #
-       # Value are properly unquoted and unescaped.
-       def self.connect_string_to_hash( str )
-               options = {}
-               key = nil
-               value = String.new
-               
str.scan(/\G\s*(?>([^\s\\\']+)\s*=\s*|([^\s\\\']+)|'((?:[^\'\\]|\\.)*)'|(\\.?)|(\S))(\s|\z)?/m)
 do
-                                       |k, word, sq, esc, garbage, sep|
-                       raise ArgumentError, "unterminated quoted string in 
connection info string: #{str.inspect}" if garbage
-                       if k
-                               key = k
-                       else
-                               value << (word || (sq || esc).gsub(/\\(.)/, 
'\\1'))
-                       end
-                       if sep
-                               raise ArgumentError, "missing = after 
#{value.inspect}" unless key
-                               options[key.to_sym] = value
-                               key = nil
-                               value = String.new
-                       end
-               end
-               options
-       end
-
-       # URI defined in RFC3986
-       # This regexp is modified to allow host to specify multiple comma 
separated components captured as <hostports> and to disallow comma in hostnames.
-       # Taken from: 
https://github.com/ruby/ruby/blob/be04006c7d2f9aeb7e9d8d09d945b3a9c7850202/lib/uri/rfc3986_parser.rb#L6
-       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*))?)/
-       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/
-
        # Parse the connection +args+ into a connection-parameter string.
        # See PG::Connection.new for valid arguments.
        #
@@ -87,91 +56,43 @@
        # * URI object
        # * positional arguments
        #
-       # The method adds the option "hostaddr" and "fallback_application_name" 
if they aren't already set.
-       # The URI and the options string is passed through and "hostaddr" as 
well as "fallback_application_name"
-       # are added to the end.
-       def self::parse_connect_args( *args )
+       # The method adds the option "fallback_application_name" if it isn't 
already set.
+       # It returns a connection string with "key=value" pairs.
+       def self.parse_connect_args( *args )
                hash_arg = args.last.is_a?( Hash ) ? 
args.pop.transform_keys(&:to_sym) : {}
-               option_string = ""
                iopts = {}
 
                if args.length == 1
                        case args.first
-                       when URI, POSTGRESQL_URI
-                               uri = args.first.to_s
-                               uri_match = POSTGRESQL_URI.match(uri)
-                               if uri_match['query']
-                                       iopts = 
URI.decode_www_form(uri_match['query']).to_h.transform_keys(&:to_sym)
-                               end
-                               # extract "host1,host2" from 
"host1:5432,host2:5432"
-                               iopts[:host] = 
uri_match['hostports'].split(',', -1).map do |hostport|
-                                       hostmatch = 
/\A#{HOST_AND_PORT}\z/.match(hostport)
-                                       hostmatch['IPv6address'] || 
hostmatch['IPv4address'] || hostmatch['reg-name']&.gsub(/%(\h\h)/){ $1.hex.chr }
-                               end.join(',')
-                               oopts = {}
-                       when /=/
-                               # Option string style
-                               option_string = args.first.to_s
-                               iopts = connect_string_to_hash(option_string)
-                               oopts = {}
+                       when URI, /=/, /:\/\//
+                               # Option or URL string style
+                               conn_string = args.first.to_s
+                               iopts = 
PG::Connection.conninfo_parse(conn_string).each_with_object({}){|h, o| 
o[h[:keyword].to_sym] = h[:val] if h[:val] }
                        else
                                # Positional parameters (only host given)
                                iopts[CONNECT_ARGUMENT_ORDER.first.to_sym] = 
args.first
-                               oopts = iopts.dup
                        end
                else
-                       # Positional parameters
+                       # Positional parameters with host and more
                        max = CONNECT_ARGUMENT_ORDER.length
                        raise ArgumentError,
-                               "Extra positional parameter %d: %p" % [ max + 
1, args[max] ] if args.length > max
+                                       "Extra positional parameter %d: %p" % [ 
max + 1, args[max] ] if args.length > max
 
                        CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
                                iopts[ k.to_sym ] = v if v
                        end
                        iopts.delete(:tty) # ignore obsolete tty parameter
-                       oopts = iopts.dup
                end
 
                iopts.merge!( hash_arg )
-               oopts.merge!( hash_arg )
-
-               # Resolve DNS in Ruby to avoid blocking state while connecting, 
when it ...
-               if (host=iopts[:host]) && !iopts[:hostaddr]
-                       hostaddrs = host.split(",", -1).map do |mhost|
-                               if !mhost.empty? && !mhost.start_with?("/") &&  
# isn't UnixSocket
-                                               # isn't a path on Windows
-                                               (RUBY_PLATFORM !~ /mingw|mswin/ 
|| mhost !~ /\A\w:[\/\\]/)
-
-                                       if Fiber.respond_to?(:scheduler) &&
-                                                       Fiber.scheduler &&
-                                                       RUBY_VERSION < '3.1.'
-
-                                               # Use a second thread to avoid 
blocking of the scheduler.
-                                               # `IPSocket.getaddress` isn't 
fiber aware before ruby-3.1.
-                                               Thread.new{ 
IPSocket.getaddress(mhost) rescue '' }.value
-                                       else
-                                               IPSocket.getaddress(mhost) 
rescue ''
-                                       end
-                               end
-                       end
-                       oopts[:hostaddr] = hostaddrs.join(",") if hostaddrs.any?
-               end
 
                if !iopts[:fallback_application_name]
-                       oopts[:fallback_application_name] = $0.sub( 
/^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
+                       iopts[:fallback_application_name] = $0.sub( 
/^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
                end
 
-               if uri
-                       uri += uri_match['query'] ? "&" : "?"
-                       uri += URI.encode_www_form( oopts )
-                       return uri
-               else
-                       option_string += ' ' unless option_string.empty? && 
oopts.empty?
-                       return option_string + connect_hash_to_string(oopts)
-               end
+               return connect_hash_to_string(iopts)
        end
 
-
        #  call-seq:
        #     conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
        #
@@ -241,7 +162,7 @@
        #   ["more", "data", "to", "copy"]
 
        def copy_data( sql, coder=nil )
-               raise PG::NotInBlockingMode, "copy_data can not be used in 
nonblocking mode" if nonblocking?
+               raise PG::NotInBlockingMode.new("copy_data can not be used in 
nonblocking mode", connection: self) if nonblocking?
                res = exec( sql )
 
                case res.result_status
@@ -273,11 +194,15 @@
                                yield res
                        rescue Exception => err
                                cancel
-                               while get_copy_data
+                               begin
+                                       while get_copy_data
+                                       end
+                               rescue PG::Error
+                                       # Ignore error in cleanup to avoid 
losing original exception
                                end
                                while get_result
                                end
-                               raise
+                               raise err
                        else
                                res = get_last_result
                                if !res || res.result_status != PGRES_COMMAND_OK
@@ -285,7 +210,7 @@
                                        end
                                        while get_result
                                        end
-                                       raise PG::NotAllCopyDataRetrieved, "Not 
all COPY data retrieved"
+                                       raise 
PG::NotAllCopyDataRetrieved.new("Not all COPY data retrieved", connection: self)
                                end
                                res
                        ensure
@@ -310,16 +235,17 @@
        # and a +COMMIT+ at the end of the block, or
        # +ROLLBACK+ if any exception occurs.
        def transaction
+               rollback = false
                exec "BEGIN"
-               res = yield(self)
+               yield(self)
        rescue Exception
+               rollback = true
                cancel if transaction_status == PG::PQTRANS_ACTIVE
                block
                exec "ROLLBACK"
                raise
-       else
-               exec "COMMIT"
-               res
+       ensure
+               exec "COMMIT" unless rollback
        end
 
        ### Returns an array of Hashes with connection defaults. See 
::conndefaults
@@ -482,10 +408,10 @@
        # See also #copy_data.
        #
        def put_copy_data(buffer, encoder=nil)
-               until sync_put_copy_data(buffer, encoder)
-                       flush
+               until res=sync_put_copy_data(buffer, encoder)
+                       res = flush
                end
-               flush
+               res
        end
        alias async_put_copy_data put_copy_data
 
@@ -545,6 +471,7 @@
        def reset
                reset_start
                async_connect_or_reset(:reset_poll)
+               self
        end
        alias async_reset reset
 
@@ -613,28 +540,62 @@
 
        private def async_connect_or_reset(poll_meth)
                # Track the progress of the connection, waiting for the socket 
to become readable/writable before polling it
+
+               if (timeo = conninfo_hash[:connect_timeout].to_i) && timeo > 0
+                       # Lowest timeout is 2 seconds - like in libpq
+                       timeo = [timeo, 2].max
+                       stop_time = timeo + 
Process.clock_gettime(Process::CLOCK_MONOTONIC)
+               end
+
                poll_status = PG::PGRES_POLLING_WRITING
                until poll_status == PG::PGRES_POLLING_OK ||
                                poll_status == PG::PGRES_POLLING_FAILED
 
-                       # If the socket needs to read, wait 'til it becomes 
readable to poll again
-                       case poll_status
-                       when PG::PGRES_POLLING_READING
-                               socket_io.wait_readable
+                       timeout = 
stop_time&.-(Process.clock_gettime(Process::CLOCK_MONOTONIC))
+                       event = if !timeout || timeout >= 0
+                               # If the socket needs to read, wait 'til it 
becomes readable to poll again
+                               case poll_status
+                               when PG::PGRES_POLLING_READING
+                                       if defined?(IO::READABLE) # ruby-3.0+
+                                               socket_io.wait(IO::READABLE | 
IO::PRIORITY, timeout)
+                                       else
+                                               IO.select([socket_io], nil, 
[socket_io], timeout)
+                                       end
 
-                       # ...and the same for when the socket needs to write
-                       when PG::PGRES_POLLING_WRITING
-                               socket_io.wait_writable
+                               # ...and the same for when the socket needs to 
write
+                               when PG::PGRES_POLLING_WRITING
+                                       if defined?(IO::WRITABLE) # ruby-3.0+
+                                               # Use wait instead of 
wait_readable, since connection errors are delivered as
+                                               # exceptional/priority events 
on Windows.
+                                               socket_io.wait(IO::WRITABLE | 
IO::PRIORITY, timeout)
+                                       else
+                                               # io#wait on ruby-2.x doesn't 
wait for priority, so fallback to IO.select
+                                               IO.select(nil, [socket_io], 
[socket_io], timeout)
+                                       end
+                               end
+                       end
+                       # connection to server at "localhost" (127.0.0.1), port 
5433 failed: timeout expired (PG::ConnectionBad)
+                       # connection to server on socket 
"/var/run/postgresql/.s.PGSQL.5433" failed: No such file or directory
+                       unless event
+                               if self.class.send(:host_is_named_pipe?, host)
+                                       connhost = "on socket \"#{host}\""
+                               elsif respond_to?(:hostaddr)
+                                       connhost = "at \"#{host}\" 
(#{hostaddr}), port #{port}"
+                               else
+                                       connhost = "at \"#{host}\", port 
#{port}"
+                               end
+                               raise PG::ConnectionBad.new("connection to 
server #{connhost} failed: timeout expired", connection: self)
                        end
 
                        # Check to see if it's finished or failed yet
                        poll_status = send( poll_meth )
+                       @last_status = status unless [PG::CONNECTION_BAD, 
PG::CONNECTION_OK].include?(status)
                end
 
                unless status == PG::CONNECTION_OK
                        msg = error_message
                        finish
-                       raise PG::ConnectionBad, msg
+                       raise PG::ConnectionBad.new(msg, connection: self)
                end
 
                # Set connection to nonblocking to handle all blocking states 
in ruby.
@@ -642,8 +603,6 @@
                sync_setnonblocking(true)
                self.flush_data = true
                set_default_encoding
-
-               self
        end
 
        class << self
@@ -698,13 +657,17 @@
                # connection will have its +client_encoding+ set accordingly.
                #
                # Raises a PG::Error if the connection fails.
-               def new(*args, **kwargs)
-                       conn = self.connect_start(*args, **kwargs ) or
-                               raise(PG::Error, "Unable to create a new 
connection")
-
-                       raise(PG::ConnectionBad, conn.error_message) if 
conn.status == PG::CONNECTION_BAD
+               def new(*args)
+                       conn = connect_to_hosts(*args)
 
-                       conn.send(:async_connect_or_reset, :connect_poll)
+                       if block_given?
+                               begin
+                                       return yield conn
+                               ensure
+                                       conn.finish
+                               end
+                       end
+                       conn
                end
                alias async_connect new
                alias connect new
@@ -712,6 +675,99 @@
                alias setdb new
                alias setdblogin new
 
+               private def connect_to_hosts(*args)
+                       option_string = parse_connect_args(*args)
+                       iopts = 
PG::Connection.conninfo_parse(option_string).each_with_object({}){|h, o| 
o[h[:keyword].to_sym] = h[:val] if h[:val] }
+                       iopts = 
PG::Connection.conndefaults.each_with_object({}){|h, o| o[h[:keyword].to_sym] = 
h[:val] if h[:val] }.merge(iopts)
+
+                       errors = []
+                       if iopts[:hostaddr]
+                               # hostaddr is provided -> no need to resolve 
hostnames
+                               ihostaddrs = iopts[:hostaddr].split(",", -1)
+
+                               ihosts = iopts[:host].split(",", -1) if 
iopts[:host]
+                               raise PG::ConnectionBad, "could not match 
#{ihosts.size} host names to #{ihostaddrs.size} hostaddr values" if ihosts && 
ihosts.size != ihostaddrs.size
+
+                               iports = iopts[:port].split(",", -1)
+                               iports = iports * ihostaddrs.size if 
iports.size == 1
+                               raise PG::ConnectionBad, "could not match 
#{iports.size} port numbers to #{ihostaddrs.size} hosts" if iports.size != 
ihostaddrs.size
+
+                               # Try to connect to each hostaddr with separate 
timeout
+                               ihostaddrs.each_with_index do |ihostaddr, idx|
+                                       oopts = iopts.merge(hostaddr: 
ihostaddr, port: iports[idx])
+                                       oopts[:host] = ihosts[idx] if ihosts
+                                       c = connect_internal(oopts, errors)
+                                       return c if c
+                               end
+                       elsif iopts[:host] && !iopts[:host].empty?
+                               # Resolve DNS in Ruby to avoid blocking state 
while connecting, when it ...
+                               ihosts = iopts[:host].split(",", -1)
+
+                               iports = iopts[:port].split(",", -1)
+                               iports = iports * ihosts.size if iports.size == 
1
+                               raise PG::ConnectionBad, "could not match 
#{iports.size} port numbers to #{ihosts.size} hosts" if iports.size != 
ihosts.size
+
+                               ihosts.each_with_index do |mhost, idx|
+                                       unless host_is_named_pipe?(mhost)
+                                               addrs = if 
Fiber.respond_to?(:scheduler) &&
+                                                                       
Fiber.scheduler &&
+                                                                       
RUBY_VERSION < '3.1.'
+
+                                                       # Use a second thread 
to avoid blocking of the scheduler.
+                                                       # 
`TCPSocket.gethostbyname` isn't fiber aware before ruby-3.1.
+                                                       Thread.new{ 
Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue [''] 
}.value
+                                               else
+                                                       
Addrinfo.getaddrinfo(mhost, nil, nil, :STREAM).map(&:ip_address) rescue ['']
+                                               end
+
+                                               # Try to connect to each host 
with separate timeout
+                                               addrs.each do |addr|
+                                                       oopts = 
iopts.merge(hostaddr: addr, host: mhost, port: iports[idx])
+                                                       c = 
connect_internal(oopts, errors)
+                                                       return c if c
+                                               end
+                                       else
+                                               # No hostname to resolve 
(UnixSocket)
+                                               oopts = iopts.merge(host: 
mhost, port: iports[idx])
+                                               c = connect_internal(oopts, 
errors)
+                                               return c if c
+                                       end
+                               end
+                       else
+                               # No host given
+                               return connect_internal(iopts)
+                       end
+                       raise PG::ConnectionBad, errors.join("\n")
+               end
+
+               private def connect_internal(opts, errors=nil)
+                       begin
+                               conn = self.connect_start(opts) or
+                                                                               
        raise(PG::Error, "Unable to create a new connection")
+
+                               raise PG::ConnectionBad.new(conn.error_message, 
connection: self) if conn.status == PG::CONNECTION_BAD
+
+                               conn.send(:async_connect_or_reset, 
:connect_poll)
+                       rescue PG::ConnectionBad => err
+                               if errors && !(conn && 
[PG::CONNECTION_AWAITING_RESPONSE].include?(conn.instance_variable_get(:@last_status)))
+                                       # Seems to be no authentication error 
-> try next host
+                                       errors << err
+                                       return nil
+                               else
+                                       # Probably an authentication error
+                                       raise
+                               end
+                       end
+                       conn
+               end
+
+               private def host_is_named_pipe?(host_string)
+                       host_string.empty? || host_string.start_with?("/") ||  
# it's UnixSocket?
+                                                       
host_string.start_with?("@") ||  # it's UnixSocket in the abstract namespace?
+                                                       # it's a path on 
Windows?
+                                                       (RUBY_PLATFORM =~ 
/mingw|mswin/ && host_string =~ /\A([\/\\]|\w:[\/\\])/)
+               end
+
                # call-seq:
                #    PG::Connection.ping(connection_hash)       -> Integer
                #    PG::Connection.ping(connection_string)     -> Integer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/pg/exceptions.rb new/lib/pg/exceptions.rb
--- old/lib/pg/exceptions.rb    2022-03-31 14:39:47.000000000 +0200
+++ new/lib/pg/exceptions.rb    2022-07-27 10:04:31.000000000 +0200
@@ -6,7 +6,13 @@
 
 module PG
 
-       class Error < StandardError; end
+       class Error < StandardError
+               def initialize(msg=nil, connection: nil, result: nil)
+                       @connection = connection
+                       @result = result
+                       super(msg)
+               end
+       end
 
 end # module PG
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/pg/version.rb new/lib/pg/version.rb
--- old/lib/pg/version.rb       2022-03-31 14:39:47.000000000 +0200
+++ new/lib/pg/version.rb       2022-07-27 10:04:31.000000000 +0200
@@ -1,4 +1,4 @@
 module PG
        # Library version
-       VERSION = '1.3.5'
+       VERSION = '1.4.2'
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/pg.rb new/lib/pg.rb
--- old/lib/pg.rb       2022-03-31 14:39:47.000000000 +0200
+++ new/lib/pg.rb       2022-07-27 10:04:31.000000000 +0200
@@ -59,14 +59,14 @@
        # Get the PG library version.
        #
        # +include_buildnum+ is no longer used and any value passed will be 
ignored.
-       def self::version_string( include_buildnum=nil )
-               return "%s %s" % [ self.name, VERSION ]
+       def self.version_string( include_buildnum=nil )
+               "%s %s" % [ self.name, VERSION ]
        end
 
 
        ### Convenience alias for PG::Connection.new.
-       def self::connect( *args, **kwargs )
-               return PG::Connection.new( *args, **kwargs )
+       def self.connect( *args, &block )
+               Connection.new( *args, &block )
        end
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2022-03-31 14:39:47.000000000 +0200
+++ new/metadata        2022-07-27 10:04:31.000000000 +0200
@@ -1,7 +1,7 @@
 --- !ruby/object:Gem::Specification
 name: pg
 version: !ruby/object:Gem::Version
-  version: 1.3.5
+  version: 1.4.2
 platform: ruby
 authors:
 - Michael Granger
@@ -36,7 +36,7 @@
   oL1mUdzB8KrZL4/WbG5YNX6UTtJbIOu9qEFbBAy4/jtIkJX+dlNoFwd4GXQW1YNO
   nA==
   -----END CERTIFICATE-----
-date: 2022-03-31 00:00:00.000000000 Z
+date: 2022-07-27 00:00:00.000000000 Z
 dependencies: []
 description: Pg is the Ruby interface to the PostgreSQL RDBMS. It works with 
PostgreSQL
   9.3 and later.
@@ -179,7 +179,7 @@
     - !ruby/object:Gem::Version
       version: '0'
 requirements: []
-rubygems_version: 3.2.15
+rubygems_version: 3.3.7
 signing_key: 
 specification_version: 4
 summary: Pg is the Ruby interface to the PostgreSQL RDBMS
Binary files old/metadata.gz.sig and new/metadata.gz.sig differ

Reply via email to