From 0aaeae6346f7b676adaeea9a4ff5ef6ee55e24db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Wed, 15 May 2024 11:15:33 +0000 Subject: [PATCH 1/8] ext/pdo_pgsql: Retrieve the memory usage of the query result resource --- ext/pdo_pgsql/pdo_pgsql.c | 3 ++ ext/pdo_pgsql/pgsql_statement.c | 22 ++++++++++- ext/pdo_pgsql/php_pdo_pgsql_int.h | 1 + ext/pdo_pgsql/tests/result_memory_size.phpt | 42 +++++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_pgsql/tests/result_memory_size.phpt diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 8be27adb4b70..e9b24b817e95 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -156,6 +156,9 @@ PHP_MINIT_FUNCTION(pdo_pgsql) REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (zend_long)PGSQL_TRANSACTION_INTRANS); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); +#ifdef HAVE_PG_RESULT_MEMORY_SIZE + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_RESULT_MEMORY_SIZE", (zend_long)PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE); +#endif PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); PdoPgsql_ce->create_object = pdo_dbh_new; diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 15ecd8ce6f56..5c9610deb12a 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -705,6 +705,26 @@ static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) return 1; } +static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) +{ + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + switch (attr) { +#ifdef HAVE_PG_RESULT_MEMORY_SIZE + case PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE: + if(stmt->executed) { + ZVAL_LONG(val, PQresultMemorySize(S->result)); + } else { + ZVAL_NULL(val); + } + return 1; +#endif + + default: + return 0; + } +} + const struct pdo_stmt_methods pgsql_stmt_methods = { pgsql_stmt_dtor, pgsql_stmt_execute, @@ -713,7 +733,7 @@ const struct pdo_stmt_methods pgsql_stmt_methods = { pgsql_stmt_get_col, pgsql_stmt_param_hook, NULL, /* set_attr */ - NULL, /* get_attr */ + pgsql_stmt_get_attr, pgsql_stmt_get_column_meta, NULL, /* next_rowset */ pdo_pgsql_stmt_cursor_closer diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 303aff600605..be90aeb780c8 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -86,6 +86,7 @@ extern const struct pdo_stmt_methods pgsql_stmt_methods; enum { PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, + PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE, }; struct pdo_pgsql_lob_self { diff --git a/ext/pdo_pgsql/tests/result_memory_size.phpt b/ext/pdo_pgsql/tests/result_memory_size.phpt new file mode 100644 index 000000000000..5b1bc86fec87 --- /dev/null +++ b/ext/pdo_pgsql/tests/result_memory_size.phpt @@ -0,0 +1,42 @@ +--TEST-- +PDO PgSQL PDOStatement::getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE) +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- +query('select 1'); +$result_1 = $statement->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE); +var_dump($result_1); + +echo 'Result set with many rows: '; +$result = $db->query('select generate_series(1, 10000)'); +$result_2 = $result->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE); +var_dump($result_2); + +echo 'Large result sets should require more memory than small ones: '; +var_dump($result_2 > $result_1); + +echo 'Statements that are not executed should not consume memory: '; +$statement = $db->prepare('select 1'); +$result_3 = $statement->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE); +var_dump($result_3); + +--EXPECTF-- +Result set with only 1 row: int(%d) +Result set with many rows: int(%d) +Large result sets should require more memory than small ones: bool(true) +Statements that are not executed should not consume memory: NULL From a420d3afda36fc0789dfd7e56deb72d37ae3854e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Fri, 17 May 2024 23:28:06 +0000 Subject: [PATCH 2/8] fix: typo --- ext/pdo_pgsql/pdo_pgsql.c | 2 +- ext/pdo_pgsql/pgsql_statement.c | 2 +- ext/pdo_pgsql/php_pdo_pgsql_int.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index e9b24b817e95..d84e0f8ba070 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -157,7 +157,7 @@ PHP_MINIT_FUNCTION(pdo_pgsql) REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); #ifdef HAVE_PG_RESULT_MEMORY_SIZE - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_RESULT_MEMORY_SIZE", (zend_long)PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_RESULT_MEMORY_SIZE", (zend_long)PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE); #endif PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 5c9610deb12a..727dba8701a0 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -711,7 +711,7 @@ static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) switch (attr) { #ifdef HAVE_PG_RESULT_MEMORY_SIZE - case PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE: + case PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE: if(stmt->executed) { ZVAL_LONG(val, PQresultMemorySize(S->result)); } else { diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index be90aeb780c8..ad48de384a3d 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -86,7 +86,7 @@ extern const struct pdo_stmt_methods pgsql_stmt_methods; enum { PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC, - PRO_PGSQL_ATTR_RESULT_MEMORY_SIZE, + PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE, }; struct pdo_pgsql_lob_self { From b5769e64ab965d582df3e2e760186534068dc90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sat, 18 May 2024 00:14:25 +0000 Subject: [PATCH 3/8] impl: subclass stub and their arginfo --- ext/pdo_pgsql/pdo_pgsql.stub.php | 5 +++++ ext/pdo_pgsql/pdo_pgsql_arginfo.h | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php index eb7707147a19..eb0e3d6efd68 100644 --- a/ext/pdo_pgsql/pdo_pgsql.stub.php +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -11,6 +11,11 @@ class PdoPgsql extends PDO /** @cvalue PDO_PGSQL_ATTR_DISABLE_PREPARES */ public const int ATTR_DISABLE_PREPARES = UNKNOWN; +#ifdef HAVE_PG_RESULT_MEMORY_SIZE + /** @cvalue PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE */ + public const int ATTR_RESULT_MEMORY_SIZE = UNKNOWN; +#endif + /** @cvalue PGSQL_TRANSACTION_IDLE */ public const int TRANSACTION_IDLE = UNKNOWN; diff --git a/ext/pdo_pgsql/pdo_pgsql_arginfo.h b/ext/pdo_pgsql/pdo_pgsql_arginfo.h index 41f18486ad53..04e1441b4b6d 100644 --- a/ext/pdo_pgsql/pdo_pgsql_arginfo.h +++ b/ext/pdo_pgsql/pdo_pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2be4e9679d29fcbc3330b78f9e63c4c6e1284f19 */ + * Stub hash: 57eb44ac4546230b33165b7226b05d006b651e46 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PdoPgsql_escapeIdentifier, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) @@ -88,6 +88,14 @@ static zend_class_entry *register_class_PdoPgsql(zend_class_entry *class_entry_P zend_string *const_ATTR_DISABLE_PREPARES_name = zend_string_init_interned("ATTR_DISABLE_PREPARES", sizeof("ATTR_DISABLE_PREPARES") - 1, 1); zend_declare_typed_class_constant(class_entry, const_ATTR_DISABLE_PREPARES_name, &const_ATTR_DISABLE_PREPARES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_DISABLE_PREPARES_name); +#if defined(HAVE_PG_RESULT_MEMORY_SIZE) + + zval const_ATTR_RESULT_MEMORY_SIZE_value; + ZVAL_LONG(&const_ATTR_RESULT_MEMORY_SIZE_value, PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE); + zend_string *const_ATTR_RESULT_MEMORY_SIZE_name = zend_string_init_interned("ATTR_RESULT_MEMORY_SIZE", sizeof("ATTR_RESULT_MEMORY_SIZE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_RESULT_MEMORY_SIZE_name, &const_ATTR_RESULT_MEMORY_SIZE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_RESULT_MEMORY_SIZE_name); +#endif zval const_TRANSACTION_IDLE_value; ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); From da6b54b5d297e0f0fd7f603ab37d882881ca12b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sun, 19 May 2024 02:38:06 +0000 Subject: [PATCH 4/8] impl: Notify statements that have not been executed using `errorInfo()`. --- ext/pdo_pgsql/pgsql_statement.c | 1 + ext/pdo_pgsql/tests/result_memory_size.phpt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 727dba8701a0..cc1fc542134f 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -715,6 +715,7 @@ static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) if(stmt->executed) { ZVAL_LONG(val, PQresultMemorySize(S->result)); } else { + pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", "statement has not been executed yet"); ZVAL_NULL(val); } return 1; diff --git a/ext/pdo_pgsql/tests/result_memory_size.phpt b/ext/pdo_pgsql/tests/result_memory_size.phpt index 5b1bc86fec87..168716174a77 100644 --- a/ext/pdo_pgsql/tests/result_memory_size.phpt +++ b/ext/pdo_pgsql/tests/result_memory_size.phpt @@ -35,8 +35,12 @@ $statement = $db->prepare('select 1'); $result_3 = $statement->getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE); var_dump($result_3); +echo 'and should emit Error: '; +printf("%s: %d: %s\n", ...$statement->errorInfo()); + --EXPECTF-- Result set with only 1 row: int(%d) Result set with many rows: int(%d) Large result sets should require more memory than small ones: bool(true) Statements that are not executed should not consume memory: NULL +and should emit Error: HY000: 0: statement has not been executed yet From c58707d576739cd741d105b8cbc9c559713a3430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sun, 19 May 2024 07:15:23 +0000 Subject: [PATCH 5/8] impl: add a statement name to `errorInfo()` --- ext/pdo_pgsql/pgsql_statement.c | 5 ++++- ext/pdo_pgsql/tests/result_memory_size.phpt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index cc1fc542134f..f824ac17acec 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -715,7 +715,10 @@ static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) if(stmt->executed) { ZVAL_LONG(val, PQresultMemorySize(S->result)); } else { - pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", "statement has not been executed yet"); + char *tmp; + spprintf(&tmp, 0, "statement '%s' has not been executed yet", S->stmt_name); + + pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", tmp); ZVAL_NULL(val); } return 1; diff --git a/ext/pdo_pgsql/tests/result_memory_size.phpt b/ext/pdo_pgsql/tests/result_memory_size.phpt index 168716174a77..929502295327 100644 --- a/ext/pdo_pgsql/tests/result_memory_size.phpt +++ b/ext/pdo_pgsql/tests/result_memory_size.phpt @@ -43,4 +43,4 @@ Result set with only 1 row: int(%d) Result set with many rows: int(%d) Large result sets should require more memory than small ones: bool(true) Statements that are not executed should not consume memory: NULL -and should emit Error: HY000: 0: statement has not been executed yet +and should emit Error: HY000: 0: statement '%s' has not been executed yet From 37e203e91d35dbcfedc20c67b40933c2482f6f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sun, 19 May 2024 07:40:01 +0000 Subject: [PATCH 6/8] fix: constant declaration location --- ext/pdo_pgsql/config.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 index 27482a59f0e2..a21b11106bab 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -67,6 +67,7 @@ if test "$PHP_PDO_PGSQL" != "no"; then LDFLAGS="-L$PGSQL_LIBDIR $LDFLAGS" AC_CHECK_LIB(pq, PQlibVersion,, AC_MSG_ERROR([Unable to build the PDO PostgreSQL driver: at least libpq 9.1 is required])) + AC_CHECK_LIB(pq, PQresultMemorySize, AC_DEFINE(HAVE_PG_RESULT_MEMORY_SIZE,1,[PostgreSQL 12 or later])) LIBS=$old_LIBS LDFLAGS=$old_LDFLAGS From 218ff1e7447f7df9a69ea3472c6d2264fa334389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sun, 19 May 2024 08:19:17 +0000 Subject: [PATCH 7/8] fix: freeing the heap after use --- ext/pdo_pgsql/pgsql_statement.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index f824ac17acec..7843902a9a3a 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -719,6 +719,8 @@ static int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) spprintf(&tmp, 0, "statement '%s' has not been executed yet", S->stmt_name); pdo_pgsql_error_stmt_msg(stmt, 0, "HY000", tmp); + efree(tmp); + ZVAL_NULL(val); } return 1; From 8701022ad1b113937bb3e58c6b3f5dc358bb24c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Sun, 19 May 2024 10:02:14 +0000 Subject: [PATCH 8/8] fix: unnecessary extension declaration --- ext/pdo_pgsql/tests/result_memory_size.phpt | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/pdo_pgsql/tests/result_memory_size.phpt b/ext/pdo_pgsql/tests/result_memory_size.phpt index 929502295327..ce73e4af3266 100644 --- a/ext/pdo_pgsql/tests/result_memory_size.phpt +++ b/ext/pdo_pgsql/tests/result_memory_size.phpt @@ -1,7 +1,6 @@ --TEST-- PDO PgSQL PDOStatement::getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE) --EXTENSIONS-- -pdo pdo_pgsql --SKIPIF--