Skip to content

Commit 0366dee

Browse files
ext/pdo_pgsql: Fixed PDO::CURSOR_SCROLL statements closing a cursor that does not exist.
1 parent 35b09ea commit 0366dee

5 files changed

Lines changed: 76 additions & 3 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ PHP NEWS
5656
. Fixed a leak when a persistent connection failed a liveness check
5757
with no other live PDO handle. (iliaal)
5858

59+
- PDO_PGSQL:
60+
. Fixed PDO::CURSOR_SCROLL statements closing a cursor that does not exist.
61+
(KentarouTakeda)
62+
5963
- Phar:
6064
. Fixed bug GH-23418 (Use-after-free when looking up mounted directories).
6165
(Weilin Du)

ext/pdo_pgsql/pgsql_statement.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ static int pgsql_stmt_dtor(pdo_stmt_t *stmt)
114114
}
115115

116116
if (S->cursor_name) {
117-
if (server_obj_usable) {
117+
if (S->is_cursor_declared && server_obj_usable) {
118118
pdo_pgsql_db_handle *H = S->H;
119119
char *q = NULL;
120120
PGresult *res;
@@ -156,10 +156,11 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
156156
if (S->cursor_name) {
157157
char *q = NULL;
158158

159-
if (S->is_prepared) {
159+
if (S->is_cursor_declared) {
160160
spprintf(&q, 0, "CLOSE %s", S->cursor_name);
161161
PQclear(PQexec(H->server, q));
162162
efree(q);
163+
S->is_cursor_declared = false;
163164
}
164165

165166
spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, ZSTR_VAL(stmt->active_query_string));
@@ -175,7 +176,7 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt)
175176
PQclear(S->result);
176177

177178
/* the cursor was declared correctly */
178-
S->is_prepared = 1;
179+
S->is_cursor_declared = true;
179180

180181
/* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */
181182
spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name);

ext/pdo_pgsql/php_pdo_pgsql_int.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ typedef struct {
6868
Oid *param_types;
6969
int current_row;
7070
bool is_prepared;
71+
bool is_cursor_declared;
7172
} pdo_pgsql_stmt;
7273

7374
typedef struct {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
PDO PgSQL PDO::CURSOR_SCROLL sends no CLOSE after a failed re-declare
3+
--EXTENSIONS--
4+
pdo_pgsql
5+
--SKIPIF--
6+
<?php
7+
require __DIR__ . '/config.inc';
8+
require dirname(__DIR__, 2) . '/pdo/tests/pdo_test.inc';
9+
PDOTest::skip();
10+
?>
11+
--FILE--
12+
<?php
13+
14+
require_once __DIR__ . "/config.inc";
15+
16+
$db = Pdo::connect($config['ENV']['PDOTEST_DSN']);
17+
18+
$stmt = $db->prepare('SELECT CAST(:v AS int)', [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
19+
$stmt->execute([':v' => '1']);
20+
21+
try {
22+
$stmt->execute([':v' => 'not an int']);
23+
} catch (PDOException $e) {
24+
echo $e::class, ': ', $e->getCode(), PHP_EOL;
25+
}
26+
27+
$db->beginTransaction();
28+
unset($stmt);
29+
30+
$db->exec('SELECT 2');
31+
32+
echo 'Done', PHP_EOL;
33+
34+
?>
35+
--EXPECT--
36+
PDOException: 22P02
37+
Done
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
PDO PgSQL PDO::CURSOR_SCROLL sends no CLOSE for a cursor it never declared
3+
--EXTENSIONS--
4+
pdo_pgsql
5+
--SKIPIF--
6+
<?php
7+
require __DIR__ . '/config.inc';
8+
require dirname(__DIR__, 2) . '/pdo/tests/pdo_test.inc';
9+
PDOTest::skip();
10+
?>
11+
--FILE--
12+
<?php
13+
14+
require_once __DIR__ . "/config.inc";
15+
16+
/** @var Pdo */
17+
$db = Pdo::connect($config['ENV']['PDOTEST_DSN']);
18+
19+
$db->beginTransaction();
20+
21+
$stmt = $db->prepare('SELECT 1', [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
22+
unset($stmt);
23+
24+
$db->exec('SELECT 2');
25+
26+
echo 'Done';
27+
28+
?>
29+
--EXPECT--
30+
Done

0 commit comments

Comments
 (0)