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 diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 8be27adb4b70..d84e0f8ba070 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)PDO_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/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); diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 15ecd8ce6f56..7843902a9a3a 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -705,6 +705,32 @@ 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 PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE: + if(stmt->executed) { + ZVAL_LONG(val, PQresultMemorySize(S->result)); + } else { + char *tmp; + 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; +#endif + + default: + return 0; + } +} + const struct pdo_stmt_methods pgsql_stmt_methods = { pgsql_stmt_dtor, pgsql_stmt_execute, @@ -713,7 +739,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..ad48de384a3d 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, + PDO_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..ce73e4af3266 --- /dev/null +++ b/ext/pdo_pgsql/tests/result_memory_size.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO PgSQL PDOStatement::getAttribute(PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE) +--EXTENSIONS-- +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); + +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 '%s' has not been executed yet