diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 10af61fa2b08f..89db4a106e861 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -659,8 +659,6 @@ PHP_METHOD(PDO, prepare) /* give it a reference to me */ GC_ADDREF(&dbh_obj->std); stmt->database_object_handle = &dbh_obj->std; - /* we haven't created a lazy object yet */ - ZVAL_UNDEF(&stmt->lazy_object_ref); if (dbh->methods->preparer(dbh, statement, stmt, options)) { if (Z_TYPE(ctor_args) == IS_ARRAY) { @@ -1225,8 +1223,6 @@ PHP_METHOD(PDO, query) /* give it a reference to me */ GC_ADDREF(&dbh_obj->std); stmt->database_object_handle = &dbh_obj->std; - /* we haven't created a lazy object yet */ - ZVAL_UNDEF(&stmt->lazy_object_ref); if (dbh->methods->preparer(dbh, statement, stmt, NULL)) { PDO_STMT_CLEAR_ERR(); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 0b779f6562781..b2b4984ef8177 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -206,17 +206,17 @@ PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count) stmt->column_count = new_count; } -static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */ +static void pdo_get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */ { - if (Z_ISUNDEF(stmt->lazy_object_ref)) { + if (stmt->lazy_object_ref == NULL) { pdo_row_t *row = zend_object_alloc(sizeof(pdo_row_t), pdo_row_ce); row->stmt = stmt; zend_object_std_init(&row->std, pdo_row_ce); - ZVAL_OBJ(&stmt->lazy_object_ref, &row->std); + stmt->lazy_object_ref = &row->std; GC_ADDREF(&stmt->std); GC_DELREF(&row->std); } - ZVAL_COPY(return_value, &stmt->lazy_object_ref); + ZVAL_OBJ_COPY(return_value, stmt->lazy_object_ref); } /* }}} */ @@ -685,7 +685,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h } if (how == PDO_FETCH_LAZY) { - get_lazy_object(stmt, return_value); + pdo_get_lazy_object(stmt, return_value); return true; } @@ -2369,16 +2369,16 @@ static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name return NULL; } -void pdo_row_free_storage(zend_object *std) +static void pdo_row_free_storage(zend_object *std) { pdo_row_t *row = php_pdo_row_fetch_object(std); if (row->stmt) { - ZVAL_UNDEF(&row->stmt->lazy_object_ref); + row->stmt->lazy_object_ref = NULL; OBJ_RELEASE(&row->stmt->std); } } -zend_object *pdo_row_new(zend_class_entry *ce) +static zend_object *pdo_row_new(zend_class_entry *ce) { pdo_row_t *row = zend_object_alloc(sizeof(pdo_row_t), ce); zend_object_std_init(&row->std, ce); diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index a57a17bb81a4a..afaae4023d3ea 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -609,7 +609,7 @@ struct _pdo_stmt_t { /* for lazy fetches, we always return the same lazy object handle. * Let's keep it here. */ - zval lazy_object_ref; + zend_object *lazy_object_ref; pdo_dbh_t *dbh; /* we want to keep the dbh alive while we live, so we own a reference */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index 13c2e3d9431b4..e8befe9f819a3 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -42,10 +42,8 @@ bool pdo_stmt_describe_columns(pdo_stmt_t *stmt); bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num, zval *args, uint32_t variadic_num_args); -extern zend_object *pdo_row_new(zend_class_entry *ce); extern const zend_function_entry pdo_row_functions[]; extern zend_class_entry *pdo_row_ce; -void pdo_row_free_storage(zend_object *std); extern zend_object_handlers pdo_row_object_handlers; zend_object_iterator *php_pdo_dbstmt_iter_get(zend_class_entry *ce, zval *object); diff --git a/ext/pdo/tests/pdo_027.phpt b/ext/pdo/tests/pdo_027.phpt index a18f9b83c6ac5..6bb350f0c133b 100644 --- a/ext/pdo/tests/pdo_027.phpt +++ b/ext/pdo/tests/pdo_027.phpt @@ -19,7 +19,13 @@ $db->exec('create table test027 (id int, name varchar(10))'); $db->exec("INSERT INTO test027 (id,name) VALUES(1,'test1')"); $db->exec("INSERT INTO test027 (id,name) VALUES(2,'test2')"); -foreach ($db->query("SELECT * FROM test027", PDO::FETCH_LAZY) as $v) { +$alias = null; +$r = $db->query("SELECT * FROM test027", PDO::FETCH_LAZY); +var_dump($r); +foreach ($r as $v) { + var_dump($alias === $v); + var_dump($v); + $alias = $v; echo "lazy: " . $v->id.$v->name."\n"; } echo "End\n"; @@ -31,6 +37,28 @@ $db = PDOTest::factory(); PDOTest::dropTableIfExists($db, "test027"); ?> --EXPECT-- +object(PDOStatement)#2 (1) { + ["queryString"]=> + string(21) "SELECT * FROM test027" +} +bool(false) +object(PDORow)#4 (3) { + ["queryString"]=> + string(21) "SELECT * FROM test027" + ["id"]=> + string(1) "1" + ["name"]=> + string(5) "test1" +} lazy: 1test1 +bool(true) +object(PDORow)#4 (3) { + ["queryString"]=> + string(21) "SELECT * FROM test027" + ["id"]=> + string(1) "2" + ["name"]=> + string(5) "test2" +} lazy: 2test2 End diff --git a/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt b/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt new file mode 100644 index 0000000000000..578d758bfb34c --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_lazy_as_default.phpt @@ -0,0 +1,90 @@ +--TEST-- +PDO Common: PDO::FETCH_LAZY set via PDOStatement::setFetchMode() +--DESCRIPTION-- +This test uses an EXPECT section because we want to know the object handlers +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_lazy_as_default_changed(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10), grp VARCHAR(10))'); +// Firebird does not allow inserting multiple rows with INSERT INTO +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (1, 'A', 'alpha', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (2, 'B', 'beta', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (3, 'C', 'gamma', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (4, 'D', 'delta', 'Group2')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed'); +$pdoQuery->execute(); +$pdoQuery->setFetchMode(PDO::FETCH_LAZY); + +class Test { + public $val1; + public $val2; +} +$o = new Test(); + +var_dump($pdoQuery->fetch()); +var_dump($pdoQuery->fetchObject(Test::class)); +var_dump($pdoQuery->fetch()); +$pdoQuery->setFetchMode(PDO::FETCH_INTO, $o); +var_dump($pdoQuery->fetch()); +var_dump($o); + +?> +--CLEAN-- + +--EXPECT-- +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "alpha" + ["val1"]=> + string(1) "A" +} +object(Test)#4 (2) { + ["val1"]=> + string(1) "B" + ["val2"]=> + string(4) "beta" +} +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "gamma" + ["val1"]=> + string(1) "C" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "D" + ["val2"]=> + string(5) "delta" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "D" + ["val2"]=> + string(5) "delta" +} diff --git a/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt b/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt new file mode 100644 index 0000000000000..ca11119c636e5 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_lazy_as_not_default.phpt @@ -0,0 +1,89 @@ +--TEST-- +PDO Common: PDO::FETCH_LAZY when FETCH_INTO set via PDOStatement::setFetchMode() +--DESCRIPTION-- +This test uses an EXPECT section because we want to know the object handlers +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_lazy_as_default_changed(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10), grp VARCHAR(10))'); +// Firebird does not allow inserting multiple rows with INSERT INTO +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (1, 'A', 'alpha', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (2, 'B', 'beta', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (3, 'C', 'gamma', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_lazy_as_default_changed VALUES (4, 'D', 'delta', 'Group2')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed'); +$pdoQuery->execute(); + +class Test { + public $val1; + public $val2; +} +$o = new Test(); + +$pdoQuery->setFetchMode(PDO::FETCH_INTO, $o); + +var_dump($pdoQuery->fetch()); +var_dump($pdoQuery->fetch(PDO::FETCH_LAZY)); +var_dump($pdoQuery->fetch()); + +$another_obj = new stdClass(); +var_dump($another_obj); + +var_dump($pdoQuery->fetch(PDO::FETCH_LAZY)); + +?> +--CLEAN-- + +--EXPECT-- +object(Test)#3 (2) { + ["val1"]=> + string(1) "A" + ["val2"]=> + string(5) "alpha" +} +object(PDORow)#4 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(4) "beta" + ["val1"]=> + string(1) "B" +} +object(Test)#3 (2) { + ["val1"]=> + string(1) "C" + ["val2"]=> + string(5) "gamma" +} +object(stdClass)#4 (0) { +} +object(PDORow)#5 (3) { + ["queryString"]=> + string(56) "SELECT val2, val1 FROM pdo_fetch_lazy_as_default_changed" + ["val2"]=> + string(5) "delta" + ["val1"]=> + string(1) "D" +} diff --git a/ext/pdo/tests/pdo_query_fetch_lazy001.phpt b/ext/pdo/tests/pdo_query_fetch_lazy001.phpt new file mode 100644 index 0000000000000..cec5e9f6d6c93 --- /dev/null +++ b/ext/pdo/tests/pdo_query_fetch_lazy001.phpt @@ -0,0 +1,37 @@ +--TEST-- +PDO Common: PDO::query() with PDO::FETCH_LAZY unused result +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('create table pdo_query_fetch_lazy_001 (id int, name varchar(10))'); +$db->exec("INSERT INTO pdo_query_fetch_lazy_001 (id,name) VALUES(1,'test1')"); +$db->exec("INSERT INTO pdo_query_fetch_lazy_001 (id,name) VALUES(2,'test2')"); + +$r = $db->query("SELECT * FROM pdo_query_fetch_lazy_001", PDO::FETCH_LAZY); +var_dump($r); +echo "End\n"; +?> +--CLEAN-- + +--EXPECT-- +object(PDOStatement)#2 (1) { + ["queryString"]=> + string(38) "SELECT * FROM pdo_query_fetch_lazy_001" +} +End