From afa6661c2892a821660587867ea322b7adbfac57 Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Fri, 21 Mar 2025 20:18:47 +0200 Subject: [PATCH 1/7] Fix: PHP 8.1 pgsql change of return values As per https://php.watch/versions/8.1/PgSQL-resource, PostgreSQL extension has moved away from resource objects and is now returning opaque classes (PgSQL\Connection, Result, Lob). These classes no longer provide default conversions to (int), so a different approach is required, while maintaining compatibility to older PHP versions. --- DB/pgsql.php | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index bb8fbf2..7724b3e 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -355,12 +355,12 @@ function simpleQuery($query) } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW|WITH)\s/si', $query)) { - $this->row[(int)$result] = 0; // reset the row counter. + $this->row[$this->_resultId($result)] = 0; // reset the row counter. $numrows = $this->numRows($result); if (is_object($numrows)) { return $numrows; } - $this->_num_rows[(int)$result] = $numrows; + $this->_num_rows[$this->_resultId($result)] = $numrows; $this->affected = 0; return $result; } else { @@ -399,7 +399,7 @@ function nextResult($result) * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * - * @param resource $result the query result resource + * @param mixed $result the query result resource or PgSql\Result * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) @@ -411,7 +411,7 @@ function nextResult($result) */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { - $result_int = (int)$result; + $result_int = $this->_resultId($result); $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; if ($rownum >= $this->_num_rows[$result_int]) { return null; @@ -447,7 +447,7 @@ function fetchInto($result, &$arr, $fetchmode, $rownum = null) * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * - * @param resource $result PHP's query result resource + * @param mixed $result PHP's query result resource or PgSql\Result * * @return bool TRUE on success, FALSE if $result is invalid * @@ -455,9 +455,9 @@ function fetchInto($result, &$arr, $fetchmode, $rownum = null) */ function freeResult($result) { - if (is_resource($result)) { - unset($this->row[(int)$result]); - unset($this->_num_rows[(int)$result]); + if (is_resource($result) || is_a($result, 'PgSql\Result')) { + unset($this->row[$this->_resultId($result)]); + unset($this->_num_rows[$this->_resultId($result)]); $this->affected = 0; return @pg_free_result($result); } @@ -525,7 +525,7 @@ function escapeSimple($str) * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * - * @param resource $result PHP's query result resource + * @param mixed $result PHP's query result resource or PgSql\Result * * @return int the number of columns. A DB_Error object on failure. * @@ -550,7 +550,7 @@ function numCols($result) * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * - * @param resource $result PHP's query result resource + * @param mixed $result PHP's query result resource or PgSql\Result * * @return int the number of rows. A DB_Error object on failure. * @@ -905,7 +905,7 @@ function tableInfo($result, $mode = null) $got_string = false; } - if (!is_resource($id)) { + if (!is_resource($id) || is_a($result, 'PgSql\Result')) { return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); } @@ -1110,6 +1110,29 @@ function _checkManip($query) || parent::_checkManip($query)); } + // }}} + // {{{ _resultId() + + /** + * Returns a numeric value identifying the result, used for maintaining + * indexes. Required by migration from resources to 'PgSql\Result' + * types with PHP 8.1. + * + * @param mixed $result the query result resource or PgSql\Result + * + * @return int + * + * @access protected + */ + function _resultId($result) + { + return + is_resource($result) + ? function_exists('get_resource_id') + ? get_resource_id($result) + : (int)$result + : spl_object_id($result); + } } /* @@ -1119,4 +1142,3 @@ function _checkManip($query) * End: */ -?> From 647cdabf9868becd9fdb0f358a211a5ea871e88d Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Tue, 25 Mar 2025 13:27:00 +0200 Subject: [PATCH 2/7] Fixing a bug in tableInfo() method My previous commit did not check for the right result structure before raising an exception. While looking into this, I have found a deprecation issue with Postgresql server versions >= 12 that needed fixing as well (adsrc column was removed). Context and recommended replacement: https://www.postgresql.org/docs/12/release-12.html#:~:text=obsolete%20pg_attrdef.-,adsrc,-column%20(Peter%20Eisentraut --- DB/pgsql.php | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index 7724b3e..2cd13e1 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -143,6 +143,14 @@ class DB_pgsql extends DB_common var $_num_rows = array(); + /** + * PostgreSQL server version, required for accomodating changes + * related to metadata column deprecations (i.e. pg_attrdef.adsrc) + * @var integer + * @access private + */ + var $pg_major_server_version = 0; + // }}} // {{{ constructor @@ -286,7 +294,13 @@ function connect($dsn, $persistent = false) return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); + } + + if (function_exists('pg_version')) { + $pg_ver = pg_version($this->connection); + $this->pg_major_server_version = intval($pg_ver['server'] ?? 0); } + return DB_OK; } @@ -905,7 +919,7 @@ function tableInfo($result, $mode = null) $got_string = false; } - if (!is_resource($id) || is_a($result, 'PgSql\Result')) { + if (!is_resource($id) && !is_a($id, 'PgSql\Result')) { return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); } @@ -957,16 +971,16 @@ function tableInfo($result, $mode = null) * and "multiple_key". The default value is passed through * rawurlencode() in case there are spaces in it. * - * @param int $resource the PostgreSQL result identifier + * @param int $id the PostgreSQL result identifier * @param int $num_field the field number * * @return string the flags * * @access private */ - function _pgFieldFlags($resource, $num_field, $table_name) + function _pgFieldFlags($id, $num_field, $table_name) { - $field_name = @pg_field_name($resource, $num_field); + $field_name = @pg_field_name($id, $num_field); // Check if there's a schema in $table_name and update things // accordingly. @@ -990,7 +1004,10 @@ function _pgFieldFlags($resource, $num_field, $table_name) $flags = ($row[0] == 't') ? 'not_null ' : ''; if ($row[1] == 't') { - $result = @pg_query($this->connection, "SELECT a.adsrc + $select_field = $this->pg_major_server_version >= 12 + ? 'pg_get_expr(a.adbin, a.adrelid)' + : 'a.adsrc'; + $result = @pg_query($this->connection, "SELECT $select_field FROM $from, pg_attrdef a WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid AND f.attrelid = a.adrelid AND f.attname = '$field_name' From 44e9321e285f571bb4a7cf19e95d92a7e33f74ad Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Wed, 30 Apr 2025 09:11:38 +0100 Subject: [PATCH 3/7] PR change as per comments received --- DB/pgsql.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index 2cd13e1..d07a07a 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -194,12 +194,12 @@ function __construct() * Example of connecting to a new link via a socket: * * require_once 'DB.php'; - * + * * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; * $options = array( * 'portability' => DB_PORTABILITY_ALL, * ); - * + * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); @@ -294,11 +294,11 @@ function connect($dsn, $persistent = false) return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); - } + } if (function_exists('pg_version')) { $pg_ver = pg_version($this->connection); - $this->pg_major_server_version = intval($pg_ver['server'] ?? 0); + $this->pg_major_server_version = intval(array_key_exists('server', $pg_ver) ? $pg_ver['server'] : 0); } return DB_OK; @@ -493,7 +493,7 @@ function freeResult($result) function quoteBoolean($boolean) { return $boolean ? 'TRUE' : 'FALSE'; } - + // }}} // {{{ escapeSimple() @@ -797,7 +797,7 @@ function pgsqlRaiseError($errno = null) /** * Gets the DBMS' native error message produced by the last query * - * {@internal Error messages are used instead of error codes + * {@internal Error messages are used instead of error codes * in order to support older versions of PostgreSQL.}} * * @return string the DBMS' error message @@ -1143,12 +1143,12 @@ function _checkManip($query) */ function _resultId($result) { - return + return is_resource($result) - ? function_exists('get_resource_id') - ? get_resource_id($result) + ? ( function_exists('get_resource_id') + ? get_resource_id($result) : (int)$result - : spl_object_id($result); + ) : spl_object_id($result); } } @@ -1159,3 +1159,4 @@ function _resultId($result) * End: */ + ?> \ No newline at end of file From 83497f4f40092c3b5c50c2c967d8704d060f409d Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Wed, 30 Apr 2025 11:46:20 +0100 Subject: [PATCH 4/7] Errant space removed as per comment --- DB/pgsql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index d07a07a..f9bb112 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -1159,4 +1159,4 @@ function _resultId($result) * End: */ - ?> \ No newline at end of file +?> From 36a5618f5416fcfa88e90bda55f5f4b613542168 Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Wed, 30 Apr 2025 17:27:00 +0100 Subject: [PATCH 5/7] Added a catch-all statement to _resultId(...) This completes the decision tree related to handling pg_() internal functions return types and function availability based on PHP versions. The statement is not normally expected to be executed with any current or historical PHP version, but having it seems like a good idea, at least for completeness. --- DB/pgsql.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index f9bb112..c21f3f7 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -1148,7 +1148,11 @@ function _resultId($result) ? ( function_exists('get_resource_id') ? get_resource_id($result) : (int)$result - ) : spl_object_id($result); + ) : ( function_exists('spl_object_id') + ? spl_object_id($result) + // catch-all statement + : (int)$result + ) } } From e32dc3275ad9d3ae236c53633eab66570a2d5d4b Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Thu, 1 May 2025 09:57:23 +0100 Subject: [PATCH 6/7] Semicolon added as per comment --- DB/pgsql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DB/pgsql.php b/DB/pgsql.php index c21f3f7..74f44e3 100644 --- a/DB/pgsql.php +++ b/DB/pgsql.php @@ -1152,7 +1152,7 @@ function _resultId($result) ? spl_object_id($result) // catch-all statement : (int)$result - ) + ); } } From c500dc9e9dc8f0c3cf36e34aff3588a28eb410ce Mon Sep 17 00:00:00 2001 From: Dan-Claudiu Dragos Date: Fri, 2 May 2025 17:25:47 +0100 Subject: [PATCH 7/7] possible odbc fix for php 8.4 --- DB/odbc.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DB/odbc.php b/DB/odbc.php index 3bb0de1..1789639 100644 --- a/DB/odbc.php +++ b/DB/odbc.php @@ -220,7 +220,7 @@ function connect($dsn, $persistent = false) $dsn['cursor']); } - if (!is_resource($this->connection)) { + if (!is_resource($this->connection) && !is_a($result, 'Odbc\Connection')) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $this->errorNative()); @@ -366,7 +366,7 @@ function fetchInto($result, &$arr, $fetchmode, $rownum = null) */ function freeResult($result) { - return is_resource($result) ? odbc_free_result($result) : false; + return (is_resource($result) || is_a($result, 'Odbc\Result')) ? odbc_free_result($result) : false; } // }}} @@ -691,7 +691,7 @@ function odbcRaiseError($errno = null) */ function errorNative() { - if (!is_resource($this->connection)) { + if (!is_resource($this->connection) && !is_a($result, 'Odbc\Connection')) { return @odbc_error() . ' ' . @odbc_errormsg(); } return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); @@ -745,7 +745,7 @@ function tableInfo($result, $mode = null) $got_string = false; } - if (!is_resource($id)) { + if (!is_resource($id) && !is_a($result, 'Odbc\Result')) { return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); }