[php-src] master: ext/pgsql: stop advertising PGSQL_DML_ASYNC where it is refused

lacatoire via David Carlier <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: lacatoire (lacatoire)
Committer: David Carlier (devnexen)
Date: 2026-08-27T21:05:19+01:00

Commit: https://github.com/php/php-src/commit/65ae1ad84719ac8343f4137f3138b80152d85be0
Raw diff: https://github.com/php/php-src/commit/65ae1ad84719ac8343f4137f3138b80152d85be0.diff

ext/pgsql: stop advertising PGSQL_DML_ASYNC where it is refused

pg_update() and pg_delete() list PGSQL_DML_ASYNC among the valid flags in
the error they raise, although their masks reject it and their helpers
assert it is never set. Conversely pg_insert() and pg_update() accept the
whole PGSQL_CONV_OPTS set but only named PGSQL_CONV_FORCE_NULL, so
PGSQL_CONV_IGNORE_DEFAULT and PGSQL_CONV_IGNORE_NOT_NULL are added.

While there, widen php_pgsql_delete()'s assert to PGSQL_DML_NO_CONV,
which pg_delete() accepts and the helper itself consults.

Close GH-23480

Changed paths:
  A  ext/pgsql/tests/pg_dml_option_flags.phpt
  M  NEWS
  M  ext/pgsql/pgsql.c


Diff:

diff --git a/NEWS b/NEWS
index 6ac1e8e329ac..871c1c9adde7 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ PHP                                                                        NEWS
   . Added Pdo\Pgsql::ATTR_CHUNK_SIZE to fetch a result set in chunks of the
     given number of rows. (KentarouTakeda)
 
+- PGSQL:
+  . Fixed the pg_insert(), pg_update() and pg_delete() flag error messages,
+    which did not name the set of flags actually accepted. (lacatoire)
+
 - Phar:
   . Fixed bug GH-23418 (Use-after-free when looking up mounted directories).
     (Weilin Du)
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 9f0ca2c1a5e6..791cbfd2c8fe 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -5740,8 +5740,9 @@ PHP_FUNCTION(pg_insert)
 	}
 
 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
-		zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
-			"PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
+		zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_IGNORE_DEFAULT, PGSQL_CONV_FORCE_NULL, "
+			"PGSQL_CONV_IGNORE_NOT_NULL, PGSQL_DML_NO_CONV, PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, "
+			"and PGSQL_DML_STRING");
 		RETURN_THROWS();
 	}
 
@@ -5972,8 +5973,9 @@ PHP_FUNCTION(pg_update)
 	}
 
 	if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
-		zend_argument_value_error(5, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
-			"PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
+		zend_argument_value_error(5, "must be a valid bit mask of PGSQL_CONV_IGNORE_DEFAULT, PGSQL_CONV_FORCE_NULL, "
+			"PGSQL_CONV_IGNORE_NOT_NULL, PGSQL_DML_NO_CONV, PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, "
+			"and PGSQL_DML_STRING");
 		RETURN_THROWS();
 	}
 
@@ -6004,7 +6006,7 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t
 	ZEND_ASSERT(pg_link != NULL);
 	ZEND_ASSERT(table != NULL);
 	ZEND_ASSERT(Z_TYPE_P(ids_array) == IS_ARRAY);
-	ZEND_ASSERT(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
+	ZEND_ASSERT(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
 
 	if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
 		return FAILURE;
@@ -6074,7 +6076,7 @@ PHP_FUNCTION(pg_delete)
 
 	if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
 		zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
-			"PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
+			"PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, and PGSQL_DML_STRING");
 		RETURN_THROWS();
 	}
 
diff --git a/ext/pgsql/tests/pg_dml_option_flags.phpt b/ext/pgsql/tests/pg_dml_option_flags.phpt
new file mode 100644
index 000000000000..3f4b73671aa1
--- /dev/null
+++ b/ext/pgsql/tests/pg_dml_option_flags.phpt
@@ -0,0 +1,64 @@
+--TEST--
+pg_insert()/pg_update()/pg_delete(): the flags refused are the flags the message names
+--EXTENSIONS--
+pgsql
+--SKIPIF--
+<?php include("inc/skipif.inc"); ?>
+--FILE--
+<?php
+
+include('inc/config.inc');
+$table_name = 'table_pg_dml_option_flags';
+
+$conn = pg_connect($conn_str);
+pg_query($conn, "CREATE TABLE {$table_name} (id INT, id2 INT)");
+
+/* PGSQL_DML_ASYNC is not part of the accepted mask of these two */
+try {
+    pg_update($conn, $table_name, ['id2' => 2], ['id' => 1], PGSQL_DML_ASYNC | PGSQL_DML_STRING);
+} catch (ValueError $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    pg_delete($conn, $table_name, ['id' => 1], PGSQL_DML_ASYNC | PGSQL_DML_STRING);
+} catch (ValueError $e) {
+    echo $e->getMessage(), "\n";
+}
+
+/* 1 << 13 is not one of the flags at all */
+try {
+    pg_insert($conn, $table_name, ['id' => 1, 'id2' => 1], 1 << 13);
+} catch (ValueError $e) {
+    echo $e->getMessage(), "\n";
+}
+
+/* but PGSQL_DML_ASYNC is accepted by pg_insert() and pg_select() */
+var_dump(is_string(pg_insert($conn, $table_name, ['id' => 1, 'id2' => 1], PGSQL_DML_ASYNC | PGSQL_DML_STRING)));
+var_dump(is_string(pg_select($conn, $table_name, ['id' => 1], PGSQL_DML_ASYNC | PGSQL_DML_STRING)));
+
+/* every PGSQL_CONV_* flag the messages name is genuinely accepted */
+var_dump(is_string(pg_insert($conn, $table_name, ['id' => 1, 'id2' => 1], PGSQL_CONV_IGNORE_DEFAULT | PGSQL_DML_STRING)));
+var_dump(is_string(pg_update($conn, $table_name, ['id2' => 2], ['id' => 1], PGSQL_CONV_IGNORE_NOT_NULL | PGSQL_DML_STRING)));
+
+/* PGSQL_DML_NO_CONV is accepted by pg_delete() and reaches its helper */
+var_dump(is_string(pg_delete($conn, $table_name, ['id' => 1], PGSQL_DML_NO_CONV | PGSQL_DML_STRING)));
+
+?>
+--CLEAN--
+<?php
+include('inc/config.inc');
+$table_name = 'table_pg_dml_option_flags';
+
+$conn = pg_connect($conn_str);
+pg_query($conn, "DROP TABLE IF EXISTS {$table_name}");
+?>
+--EXPECT--
+pg_update(): Argument #5 ($flags) must be a valid bit mask of PGSQL_CONV_IGNORE_DEFAULT, PGSQL_CONV_FORCE_NULL, PGSQL_CONV_IGNORE_NOT_NULL, PGSQL_DML_NO_CONV, PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, and PGSQL_DML_STRING
+pg_delete(): Argument #4 ($flags) must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, and PGSQL_DML_STRING
+pg_insert(): Argument #4 ($flags) must be a valid bit mask of PGSQL_CONV_IGNORE_DEFAULT, PGSQL_CONV_FORCE_NULL, PGSQL_CONV_IGNORE_NOT_NULL, PGSQL_DML_NO_CONV, PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.