From f6f144c1accf2b2a7400bb366a33d6dd9e931323 Mon Sep 17 00:00:00 2001 From: Andreas Karlsson Date: Wed, 11 Feb 2026 03:40:24 +0100 Subject: [PATCH] Add method for getting the full protocol version PostgreSQL 18 added PQfullProtocolVersion() which return the full protocol version used including the minor version, e.g. 3.0 or 3.2 rather than just 3. The new methods PG::Connection#full_protocol_version returns the version as major version * 10000 + minor version, e.g. 30002 for version 3.2. --- ext/extconf.rb | 1 + ext/pg_connection.c | 28 ++++++++++++++++++++++++++++ spec/helpers.rb | 1 + spec/pg/connection_spec.rb | 12 ++++++++++++ 4 files changed, 42 insertions(+) diff --git a/ext/extconf.rb b/ext/extconf.rb index 4e293b080..b36a4c3a8 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -316,6 +316,7 @@ module PG src + " int con(){ return PGRES_PIPELINE_SYNC; }" end have_func 'PQsetChunkedRowsMode', 'libpq-fe.h' # since PostgreSQL-17 +have_func 'PQfullProtocolVersion', 'libpq-fe.h' # since PostgreSQL-18 have_func 'timegm' have_func 'rb_io_wait' # since ruby-3.0 have_func 'rb_io_descriptor' # since ruby-3.1 diff --git a/ext/pg_connection.c b/ext/pg_connection.c index 696f26f6f..6d4300be1 100644 --- a/ext/pg_connection.c +++ b/ext/pg_connection.c @@ -865,6 +865,31 @@ pgconn_protocol_version(VALUE self) return INT2NUM(protocol_version); } +#ifdef HAVE_PQFULLPROTOCOLVERSION +/* + * call-seq: + * conn.full_protocol_version -> Integer + * + * Interrogates the frontend/backend protocol being used, including minor version. + * + * Applications might wish to use this function to determine whether certain features are supported. + * The result is the major version multiplied by 10000 added to the minor version, e.g. 30002 for version 3.2. + * The protocol version will not change after connection startup is complete, but it could theoretically change during a connection reset. + * The 3.0 protocol is supported by PostgreSQL server versions 7.4 and above. + * + * PG::ConnectionBad is raised if the connection is bad. + */ +static VALUE +pgconn_full_protocol_version(VALUE self) +{ + int protocol_version = PQfullProtocolVersion(pg_get_pgconn(self)); + if (protocol_version == 0) { + pg_raise_conn_error( rb_eConnectionBad, self, "PQfullProtocolVersion() can't get protocol version"); + } + return INT2NUM(protocol_version); +} +#endif + /* * call-seq: * conn.server_version -> Integer @@ -4733,6 +4758,9 @@ init_pg_connection(void) rb_define_method(rb_cPGconn, "transaction_status", pgconn_transaction_status, 0); rb_define_method(rb_cPGconn, "parameter_status", pgconn_parameter_status, 1); rb_define_method(rb_cPGconn, "protocol_version", pgconn_protocol_version, 0); +#ifdef HAVE_PQFULLPROTOCOLVERSION + rb_define_method(rb_cPGconn, "full_protocol_version", pgconn_full_protocol_version, 0); +#endif rb_define_method(rb_cPGconn, "server_version", pgconn_server_version, 0); rb_define_method(rb_cPGconn, "error_message", pgconn_error_message, 0); rb_define_method(rb_cPGconn, "socket", pgconn_socket, 0); diff --git a/spec/helpers.rb b/spec/helpers.rb index 5935e2034..6ebb0604f 100644 --- a/spec/helpers.rb +++ b/spec/helpers.rb @@ -682,6 +682,7 @@ def set_etc_hosts(hostaddr, hostname) config.filter_run_excluding( :postgresql_12 ) if PG.library_version < 120000 config.filter_run_excluding( :postgresql_14 ) if PG.library_version < 140000 config.filter_run_excluding( :postgresql_17 ) if PG.library_version < 170000 + config.filter_run_excluding( :postgresql_18 ) if PG.library_version < 180000 config.filter_run_excluding( :unix_socket ) if RUBY_PLATFORM=~/mingw|mswin/i config.filter_run_excluding( :scheduler ) if RUBY_VERSION < "3.0" || (RUBY_PLATFORM =~ /mingw|mswin/ && RUBY_VERSION < "3.1") || !Fiber.respond_to?(:scheduler) config.filter_run_excluding( :scheduler_address_resolve ) if RUBY_VERSION < "3.1" diff --git a/spec/pg/connection_spec.rb b/spec/pg/connection_spec.rb index 9ac2a2475..7763493a2 100644 --- a/spec/pg/connection_spec.rb +++ b/spec/pg/connection_spec.rb @@ -976,6 +976,18 @@ end end + context :protocol_version, :postgresql_18 do + it "should retrieve the wrie protocol version" do + expect( @conn.full_protocol_version ).to eq 30000 + end + + it "should raise an error on a bad connection" do + conn = PG::Connection.connect_start( @conninfo ) + conn.finish + expect{ conn.full_protocol_version }.to raise_error(PG::ConnectionBad) + end + end + it "allows a query to be cancelled" do start = Time.now @conn.set_notice_processor do |notice|