Skip to content

Commit 0d3829e

Browse files
committed
GH-15750 Pdo\Pgsql with ATTR_PREFETCH = 0: fix getColumnMeta()
doing an (internal) query to fetch metadata from the server broke the currently-running query; additionnally, fix the condition to interrupt the previous request (we shall test if *it* was lazy, not if the *new* one will)
1 parent 552823f commit 0d3829e

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

ext/pdo_pgsql/pgsql_statement.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
190190
* and returns a PGRES_FATAL_ERROR when PQgetResult gets called for stmt 2 if DEALLOCATE
191191
* was called for stmt 1 inbetween
192192
* (maybe it will change with pipeline mode in libpq 14?) */
193-
if (S->is_unbuffered && H->running_stmt) {
193+
if (H->running_stmt && H->running_stmt->is_unbuffered) {
194194
pgsql_stmt_finish(H->running_stmt, FIN_CLOSE);
195195
H->running_stmt = NULL;
196196
}
@@ -713,6 +713,12 @@ static zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, pdo_p
713713
return H->cached_table_name;
714714
}
715715

716+
if (H->running_stmt && H->running_stmt->is_unbuffered) {
717+
/* in single-row mode, libpq forbids passing a new query
718+
* while we're still flushing the current one's result */
719+
return NULL;
720+
}
721+
716722
if (H->cached_table_name) {
717723
efree(H->cached_table_name);
718724
H->cached_table_oid = InvalidOid;
@@ -805,6 +811,10 @@ static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *r
805811
break;
806812
default:
807813
/* Fetch metadata from Postgres system catalogue */
814+
if (S->H->running_stmt && S->H->running_stmt->is_unbuffered) {
815+
/* libpq forbids calling a query while we're still reading the preceding one's */
816+
break;
817+
}
808818
spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type);
809819
res = PQexec(S->H->server, q);
810820
efree(q);

ext/pdo_pgsql/tests/gh15287.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ $res = []; while (($re = $stmt->fetch())) $res[] = $re; display($res);
132132
$stmt->execute([ 0 ]);
133133
$res = []; for ($i = -1; ++$i < 2;) $res[] = $stmt->fetch(); display($res);
134134
display($pdo->query("select * from t2")->fetchAll());
135+
136+
// Metadata calls the server for some operations (notably table oid-to-name conversion).
137+
// This will break libpq (that forbids a second PQexec before we consumed the first one).
138+
// Instead of either letting libpq return an error, or blindly forbid this call, we expect
139+
// being transparently provided at least attributes which do not require a server roundtrip.
140+
// And good news: column name is one of those "local" attributes.
141+
echo "=== meta ===\n";
142+
$stmt = $pdo->query("select * from t limit 2");
143+
echo "Starting with column " . $stmt->getColumnMeta(0)['name'] . ":\n";
144+
display($stmt->fetchAll());
145+
135146
?>
136147
--EXPECTF--
137148
=== non regression ===
@@ -181,3 +192,7 @@ multiple calls to the same prepared statement, some interrupted before having re
181192
0
182193
1
183194
678 ok
195+
=== meta ===
196+
Starting with column n:
197+
0 original
198+
1 non original

0 commit comments

Comments
 (0)