Skip to content

Commit a78e8de

Browse files
authored
Fix: PHP 8.1 pgsql change of return values (#43)
* 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. * 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 * PR change as per comments received * Errant space removed as per comment * 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. * Semicolon added as per comment
1 parent 14b42b4 commit a78e8de

File tree

1 file changed

+63
-19
lines changed

1 file changed

+63
-19
lines changed

DB/pgsql.php

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ class DB_pgsql extends DB_common
143143
var $_num_rows = array();
144144

145145

146+
/**
147+
* PostgreSQL server version, required for accomodating changes
148+
* related to metadata column deprecations (i.e. pg_attrdef.adsrc)
149+
* @var integer
150+
* @access private
151+
*/
152+
var $pg_major_server_version = 0;
153+
146154
// }}}
147155
// {{{ constructor
148156

@@ -186,12 +194,12 @@ function __construct()
186194
* Example of connecting to a new link via a socket:
187195
* <code>
188196
* require_once 'DB.php';
189-
*
197+
*
190198
* $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
191199
* $options = array(
192200
* 'portability' => DB_PORTABILITY_ALL,
193201
* );
194-
*
202+
*
195203
* $db = DB::connect($dsn, $options);
196204
* if (PEAR::isError($db)) {
197205
* die($db->getMessage());
@@ -286,7 +294,13 @@ function connect($dsn, $persistent = false)
286294
return $this->raiseError(DB_ERROR_CONNECT_FAILED,
287295
null, null, null,
288296
$php_errormsg);
297+
}
298+
299+
if (function_exists('pg_version')) {
300+
$pg_ver = pg_version($this->connection);
301+
$this->pg_major_server_version = intval(array_key_exists('server', $pg_ver) ? $pg_ver['server'] : 0);
289302
}
303+
290304
return DB_OK;
291305
}
292306

@@ -355,12 +369,12 @@ function simpleQuery($query)
355369
} elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW|WITH)\s/si',
356370
$query))
357371
{
358-
$this->row[(int)$result] = 0; // reset the row counter.
372+
$this->row[$this->_resultId($result)] = 0; // reset the row counter.
359373
$numrows = $this->numRows($result);
360374
if (is_object($numrows)) {
361375
return $numrows;
362376
}
363-
$this->_num_rows[(int)$result] = $numrows;
377+
$this->_num_rows[$this->_resultId($result)] = $numrows;
364378
$this->affected = 0;
365379
return $result;
366380
} else {
@@ -399,7 +413,7 @@ function nextResult($result)
399413
* DB_result::fetchInto() instead. It can't be declared "protected"
400414
* because DB_result is a separate object.
401415
*
402-
* @param resource $result the query result resource
416+
* @param mixed $result the query result resource or PgSql\Result
403417
* @param array $arr the referenced array to put the data in
404418
* @param int $fetchmode how the resulting array should be indexed
405419
* @param int $rownum the row number to fetch (0 = first row)
@@ -411,7 +425,7 @@ function nextResult($result)
411425
*/
412426
function fetchInto($result, &$arr, $fetchmode, $rownum = null)
413427
{
414-
$result_int = (int)$result;
428+
$result_int = $this->_resultId($result);
415429
$rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
416430
if ($rownum >= $this->_num_rows[$result_int]) {
417431
return null;
@@ -447,17 +461,17 @@ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
447461
* DB_result::free() instead. It can't be declared "protected"
448462
* because DB_result is a separate object.
449463
*
450-
* @param resource $result PHP's query result resource
464+
* @param mixed $result PHP's query result resource or PgSql\Result
451465
*
452466
* @return bool TRUE on success, FALSE if $result is invalid
453467
*
454468
* @see DB_result::free()
455469
*/
456470
function freeResult($result)
457471
{
458-
if (is_resource($result)) {
459-
unset($this->row[(int)$result]);
460-
unset($this->_num_rows[(int)$result]);
472+
if (is_resource($result) || is_a($result, 'PgSql\Result')) {
473+
unset($this->row[$this->_resultId($result)]);
474+
unset($this->_num_rows[$this->_resultId($result)]);
461475
$this->affected = 0;
462476
return @pg_free_result($result);
463477
}
@@ -479,7 +493,7 @@ function freeResult($result)
479493
function quoteBoolean($boolean) {
480494
return $boolean ? 'TRUE' : 'FALSE';
481495
}
482-
496+
483497
// }}}
484498
// {{{ escapeSimple()
485499

@@ -525,7 +539,7 @@ function escapeSimple($str)
525539
* DB_result::numCols() instead. It can't be declared "protected"
526540
* because DB_result is a separate object.
527541
*
528-
* @param resource $result PHP's query result resource
542+
* @param mixed $result PHP's query result resource or PgSql\Result
529543
*
530544
* @return int the number of columns. A DB_Error object on failure.
531545
*
@@ -550,7 +564,7 @@ function numCols($result)
550564
* DB_result::numRows() instead. It can't be declared "protected"
551565
* because DB_result is a separate object.
552566
*
553-
* @param resource $result PHP's query result resource
567+
* @param mixed $result PHP's query result resource or PgSql\Result
554568
*
555569
* @return int the number of rows. A DB_Error object on failure.
556570
*
@@ -783,7 +797,7 @@ function pgsqlRaiseError($errno = null)
783797
/**
784798
* Gets the DBMS' native error message produced by the last query
785799
*
786-
* {@internal Error messages are used instead of error codes
800+
* {@internal Error messages are used instead of error codes
787801
* in order to support older versions of PostgreSQL.}}
788802
*
789803
* @return string the DBMS' error message
@@ -905,7 +919,7 @@ function tableInfo($result, $mode = null)
905919
$got_string = false;
906920
}
907921

908-
if (!is_resource($id)) {
922+
if (!is_resource($id) && !is_a($id, 'PgSql\Result')) {
909923
return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
910924
}
911925

@@ -957,16 +971,16 @@ function tableInfo($result, $mode = null)
957971
* and "multiple_key". The default value is passed through
958972
* rawurlencode() in case there are spaces in it.
959973
*
960-
* @param int $resource the PostgreSQL result identifier
974+
* @param int $id the PostgreSQL result identifier
961975
* @param int $num_field the field number
962976
*
963977
* @return string the flags
964978
*
965979
* @access private
966980
*/
967-
function _pgFieldFlags($resource, $num_field, $table_name)
981+
function _pgFieldFlags($id, $num_field, $table_name)
968982
{
969-
$field_name = @pg_field_name($resource, $num_field);
983+
$field_name = @pg_field_name($id, $num_field);
970984

971985
// Check if there's a schema in $table_name and update things
972986
// accordingly.
@@ -990,7 +1004,10 @@ function _pgFieldFlags($resource, $num_field, $table_name)
9901004
$flags = ($row[0] == 't') ? 'not_null ' : '';
9911005

9921006
if ($row[1] == 't') {
993-
$result = @pg_query($this->connection, "SELECT a.adsrc
1007+
$select_field = $this->pg_major_server_version >= 12
1008+
? 'pg_get_expr(a.adbin, a.adrelid)'
1009+
: 'a.adsrc';
1010+
$result = @pg_query($this->connection, "SELECT $select_field
9941011
FROM $from, pg_attrdef a
9951012
WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
9961013
AND f.attrelid = a.adrelid AND f.attname = '$field_name'
@@ -1110,6 +1127,33 @@ function _checkManip($query)
11101127
|| parent::_checkManip($query));
11111128
}
11121129

1130+
// }}}
1131+
// {{{ _resultId()
1132+
1133+
/**
1134+
* Returns a numeric value identifying the result, used for maintaining
1135+
* indexes. Required by migration from resources to 'PgSql\Result'
1136+
* types with PHP 8.1.
1137+
*
1138+
* @param mixed $result the query result resource or PgSql\Result
1139+
*
1140+
* @return int
1141+
*
1142+
* @access protected
1143+
*/
1144+
function _resultId($result)
1145+
{
1146+
return
1147+
is_resource($result)
1148+
? ( function_exists('get_resource_id')
1149+
? get_resource_id($result)
1150+
: (int)$result
1151+
) : ( function_exists('spl_object_id')
1152+
? spl_object_id($result)
1153+
// catch-all statement
1154+
: (int)$result
1155+
);
1156+
}
11131157
}
11141158

11151159
/*

0 commit comments

Comments
 (0)