From 624ed491f82b7c921b145082883046a539ed1955 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 04:35:52 +0000 Subject: [PATCH 1/8] Move test and expand and have it failing --- ext/pdo/tests/pdo_035.phpt | 159 ++++++++++++++++++++++++++++++ ext/pdo_sqlite/tests/pdo_035.phpt | 46 --------- 2 files changed, 159 insertions(+), 46 deletions(-) create mode 100644 ext/pdo/tests/pdo_035.phpt delete mode 100644 ext/pdo_sqlite/tests/pdo_035.phpt diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt new file mode 100644 index 0000000000000..3bad2ead5ee16 --- /dev/null +++ b/ext/pdo/tests/pdo_035.phpt @@ -0,0 +1,159 @@ +--TEST-- +PDO Common: PDORow + get_parent_class() +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE ' . TABLE_NAME .' (id int)'); +$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23)'); + +$stmt = $db->prepare('SELECT id FROM ' . TABLE_NAME); +$stmt->execute(); +$result = $stmt->fetch(PDO::FETCH_LAZY); + +var_dump($result); +var_dump(get_parent_class($result)); + +foreach ([0, "0", "id"] as $offset) { + echo 'Offset: ', var_export($offset), PHP_EOL; + $offsetRef = &$offset; + + echo 'Isset:', PHP_EOL; + var_dump(isset($result[$offset])); + var_dump(isset($result[$offsetRef])); + echo 'Empty:', PHP_EOL; + var_dump(empty($result[$offset])); + var_dump(empty($result[$offsetRef])); + echo 'Null coalesce:', PHP_EOL; + var_dump($result[$offset] ?? "default"); + var_dump($result[$offsetRef] ?? "default"); + echo 'Read:', PHP_EOL; + var_dump($result[$offset]); + var_dump($result[$offsetRef]); +} + +echo 'Errors:', PHP_EOL; +try { + $result[0] = 1; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + $result[] = 1; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + $refResult = &$result[0]; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + $refResult = &$result[]; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + unset($result[0]); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + $result->foo = 1; +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + unset($result->foo); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +object(PDORow)#3 (2) { + ["queryString"]=> + string(34) "SELECT id FROM test_pdo_35_pdo_row" + ["id"]=> + string(2) "23" +} +bool(false) +Offset: 0 +Isset: +bool(false) +bool(false) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" +Offset: '0' +Isset: +bool(false) +bool(false) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" +Offset: 'id' +Isset: +bool(true) +bool(true) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" +Errors: +Cannot write to PDORow offset +Cannot write to PDORow offset + +Notice: Indirect modification of overloaded element of PDORow has no effect in /home/girgias/Dev/PHP-8.2/ext/pdo_sqlite/tests/pdo_035.php on line 43 +AddressSanitizer:DEADLYSIGNAL +================================================================= +==3997945==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x000000dfad5b bp 0x7ffc2d5967e0 sp 0x7ffc2d5967d0 T0) +==3997945==The signal is caused by a READ memory access. +==3997945==Hint: address points to the zero page. + #0 0xdfad5b in zval_get_type /home/girgias/Dev/PHP-8.2/Zend/zend_types.h:582 + #1 0xe10769 in row_dim_read /home/girgias/Dev/PHP-8.2/ext/pdo/pdo_stmt.c:2299 + #2 0x16f0c7e in zend_fetch_dimension_address /home/girgias/Dev/PHP-8.2/Zend/zend_execute.c:2534 + #3 0x16f156b in zend_fetch_dimension_address_W /home/girgias/Dev/PHP-8.2/Zend/zend_execute.c:2609 + #4 0x182c7a6 in ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HANDLER /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:47361 + #5 0x1859bbc in execute_ex /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:60042 + #6 0x185b4d4 in zend_execute /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:60439 + #7 0x167029b in zend_execute_scripts /home/girgias/Dev/PHP-8.2/Zend/zend.c:1840 + #8 0x150533f in php_execute_script /home/girgias/Dev/PHP-8.2/main/main.c:2557 + #9 0x1a2b6cc in do_cli /home/girgias/Dev/PHP-8.2/sapi/cli/php_cli.c:964 + #10 0x1a2d2aa in main /home/girgias/Dev/PHP-8.2/sapi/cli/php_cli.c:1333 + #11 0x7fd7f6c46149 in __libc_start_call_main (/usr/lib64/../lib64/libc.so.6+0x28149) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756) + #12 0x7fd7f6c4620a in __libc_start_main_impl (/usr/lib64/../lib64/libc.so.6+0x2820a) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756) + #13 0x609b64 in _start (/home/girgias/Dev/PHP-8.2/sapi/cli/php+0x609b64) (BuildId: a4605c8d283f19dad7a72000fb964237c2b0f7a2) + +AddressSanitizer can not provide additional info. +SUMMARY: AddressSanitizer: SEGV /home/girgias/Dev/PHP-8.2/Zend/zend_types.h:582 in zval_get_type +==3997945==ABORTING diff --git a/ext/pdo_sqlite/tests/pdo_035.phpt b/ext/pdo_sqlite/tests/pdo_035.phpt deleted file mode 100644 index ec723842f03bd..0000000000000 --- a/ext/pdo_sqlite/tests/pdo_035.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -PDO Common: PDORow + get_parent_class() ---EXTENSIONS-- -pdo_sqlite ---FILE-- -exec('CREATE TABLE test (id int)'); -$db->exec('INSERT INTO test VALUES (23)'); - -$stmt = $db->prepare('SELECT id FROM test'); -$stmt->execute(); -$result = $stmt->fetch(PDO::FETCH_LAZY); - -echo get_class($result), "\n"; -var_dump(get_parent_class($result)); - -try { - $result->foo = 1; -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -try { - $result[0] = 1; -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -try { - unset($result->foo); -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} -try { - unset($result[0]); -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} - -?> ---EXPECT-- -PDORow -bool(false) -Cannot write to PDORow property -Cannot write to PDORow offset -Cannot unset PDORow property -Cannot unset PDORow offset From 27c74a15e6370a288ca4651cf4c6fb7a7e3e30c4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 04:47:24 +0000 Subject: [PATCH 2/8] ext/pdo: Fix NULL pointer dereferencing in read_dimension handler --- ext/pdo/pdo_stmt.c | 10 +++++++++- ext/pdo/tests/pdo_035.phpt | 33 +++++++-------------------------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 453933607f38e..8b98417d52e11 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2295,6 +2295,10 @@ static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv) zend_long lval; ZEND_ASSERT(stmt); + if (UNEXPECTED(!member)) { + zend_throw_error(NULL, "Cannot append to PDORow offset"); + return NULL; + } ZVAL_NULL(rv); if (Z_TYPE_P(member) == IS_LONG) { if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) { @@ -2335,7 +2339,11 @@ static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, static void row_dim_write(zend_object *object, zval *member, zval *value) { - zend_throw_error(NULL, "Cannot write to PDORow offset"); + if (!member) { + zend_throw_error(NULL, "Cannot append to PDORow offset"); + } else { + zend_throw_error(NULL, "Cannot write to PDORow offset"); + } } static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index 3bad2ead5ee16..ffdf9215eed7c 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -82,7 +82,7 @@ try { } ?> ---EXPECT-- +--EXPECTF-- object(PDORow)#3 (2) { ["queryString"]=> string(34) "SELECT id FROM test_pdo_35_pdo_row" @@ -131,29 +131,10 @@ string(2) "23" string(2) "23" Errors: Cannot write to PDORow offset -Cannot write to PDORow offset - -Notice: Indirect modification of overloaded element of PDORow has no effect in /home/girgias/Dev/PHP-8.2/ext/pdo_sqlite/tests/pdo_035.php on line 43 -AddressSanitizer:DEADLYSIGNAL -================================================================= -==3997945==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x000000dfad5b bp 0x7ffc2d5967e0 sp 0x7ffc2d5967d0 T0) -==3997945==The signal is caused by a READ memory access. -==3997945==Hint: address points to the zero page. - #0 0xdfad5b in zval_get_type /home/girgias/Dev/PHP-8.2/Zend/zend_types.h:582 - #1 0xe10769 in row_dim_read /home/girgias/Dev/PHP-8.2/ext/pdo/pdo_stmt.c:2299 - #2 0x16f0c7e in zend_fetch_dimension_address /home/girgias/Dev/PHP-8.2/Zend/zend_execute.c:2534 - #3 0x16f156b in zend_fetch_dimension_address_W /home/girgias/Dev/PHP-8.2/Zend/zend_execute.c:2609 - #4 0x182c7a6 in ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HANDLER /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:47361 - #5 0x1859bbc in execute_ex /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:60042 - #6 0x185b4d4 in zend_execute /home/girgias/Dev/PHP-8.2/Zend/zend_vm_execute.h:60439 - #7 0x167029b in zend_execute_scripts /home/girgias/Dev/PHP-8.2/Zend/zend.c:1840 - #8 0x150533f in php_execute_script /home/girgias/Dev/PHP-8.2/main/main.c:2557 - #9 0x1a2b6cc in do_cli /home/girgias/Dev/PHP-8.2/sapi/cli/php_cli.c:964 - #10 0x1a2d2aa in main /home/girgias/Dev/PHP-8.2/sapi/cli/php_cli.c:1333 - #11 0x7fd7f6c46149 in __libc_start_call_main (/usr/lib64/../lib64/libc.so.6+0x28149) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756) - #12 0x7fd7f6c4620a in __libc_start_main_impl (/usr/lib64/../lib64/libc.so.6+0x2820a) (BuildId: 7ea8d85df0e89b90c63ac7ed2b3578b2e7728756) - #13 0x609b64 in _start (/home/girgias/Dev/PHP-8.2/sapi/cli/php+0x609b64) (BuildId: a4605c8d283f19dad7a72000fb964237c2b0f7a2) +Cannot append to PDORow offset -AddressSanitizer can not provide additional info. -SUMMARY: AddressSanitizer: SEGV /home/girgias/Dev/PHP-8.2/Zend/zend_types.h:582 in zval_get_type -==3997945==ABORTING +Notice: Indirect modification of overloaded element of PDORow has no effect in %s on line %d +Cannot append to PDORow offset +Cannot unset PDORow offset +Cannot write to PDORow property +Cannot unset PDORow property From ff9ebd159d92cdf797961b391daff5553a38fe53 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 04:58:00 +0000 Subject: [PATCH 3/8] Add property tests --- ext/pdo/tests/pdo_035.phpt | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index ffdf9215eed7c..a44797b7a3bfe 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -30,6 +30,7 @@ foreach ([0, "0", "id"] as $offset) { echo 'Offset: ', var_export($offset), PHP_EOL; $offsetRef = &$offset; + echo 'Dimension:', PHP_EOL; echo 'Isset:', PHP_EOL; var_dump(isset($result[$offset])); var_dump(isset($result[$offsetRef])); @@ -42,6 +43,19 @@ foreach ([0, "0", "id"] as $offset) { echo 'Read:', PHP_EOL; var_dump($result[$offset]); var_dump($result[$offsetRef]); + echo 'Property:', PHP_EOL; + echo 'Isset:', PHP_EOL; + var_dump(isset($result->{$offset})); + var_dump(isset($result->{$offsetRef})); + echo 'Empty:', PHP_EOL; + var_dump(empty($result->{$offset})); + var_dump(empty($result->{$offsetRef})); + echo 'Null coalesce:', PHP_EOL; + var_dump($result->{$offset} ?? "default"); + var_dump($result->{$offsetRef} ?? "default"); + echo 'Read:', PHP_EOL; + var_dump($result->{$offset}); + var_dump($result->{$offsetRef}); } echo 'Errors:', PHP_EOL; @@ -91,6 +105,7 @@ object(PDORow)#3 (2) { } bool(false) Offset: 0 +Dimension: Isset: bool(false) bool(false) @@ -103,7 +118,21 @@ string(2) "23" Read: string(2) "23" string(2) "23" +Property: +Isset: +bool(true) +bool(true) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" Offset: '0' +Dimension: Isset: bool(false) bool(false) @@ -116,7 +145,34 @@ string(2) "23" Read: string(2) "23" string(2) "23" +Property: +Isset: +bool(true) +bool(true) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" Offset: 'id' +Dimension: +Isset: +bool(true) +bool(true) +Empty: +bool(false) +bool(false) +Null coalesce: +string(2) "23" +string(2) "23" +Read: +string(2) "23" +string(2) "23" +Property: Isset: bool(true) bool(true) From c1cc1cea634810da06fad486913e3660239e101f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 05:24:21 +0000 Subject: [PATCH 4/8] ext/pdo: Fix existence checks with PDORow dimension handlers --- ext/pdo/pdo_stmt.c | 105 ++++++++++++------------------------- ext/pdo/tests/pdo_035.phpt | 8 +-- 2 files changed, 37 insertions(+), 76 deletions(-) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 8b98417d52e11..7554c4bca09b6 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2256,27 +2256,25 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int /* }}} */ -/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */ - static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { pdo_row_t *row = (pdo_row_t *)object; pdo_stmt_t *stmt = row->stmt; - int colno = -1; zend_long lval; ZEND_ASSERT(stmt); ZVAL_NULL(rv); if (zend_string_equals_literal(name, "queryString")) { return zend_std_read_property(&stmt->std, name, type, cache_slot, rv); - } else if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) { + } else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) { if (lval >= 0 && lval < stmt->column_count) { fetch_value(stmt, rv, lval, NULL); } + return rv; } else { /* TODO: replace this with a hash of available column names to column * numbers */ - for (colno = 0; colno < stmt->column_count; colno++) { + for (int colno = 0; colno < stmt->column_count; colno++) { if (zend_string_equals(stmt->columns[colno].name, name)) { fetch_value(stmt, rv, colno, NULL); return rv; @@ -2287,48 +2285,31 @@ static zval *row_prop_read(zend_object *object, zend_string *name, int type, voi return rv; } -static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv) +static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv) { - pdo_row_t *row = (pdo_row_t *)object; - pdo_stmt_t *stmt = row->stmt; - int colno = -1; - zend_long lval; - ZEND_ASSERT(stmt); - - if (UNEXPECTED(!member)) { + if (UNEXPECTED(!offset)) { zend_throw_error(NULL, "Cannot append to PDORow offset"); return NULL; } - ZVAL_NULL(rv); - if (Z_TYPE_P(member) == IS_LONG) { - if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) { - fetch_value(stmt, rv, Z_LVAL_P(member), NULL); - } - } else if (Z_TYPE_P(member) == IS_STRING - && is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) { - if (lval >= 0 && lval < stmt->column_count) { - fetch_value(stmt, rv, lval, NULL); - } - } else { - if (!try_convert_to_string(member)) { - return &EG(uninitialized_zval); - } + if (Z_TYPE_P(offset) == IS_LONG) { + pdo_row_t *row = (pdo_row_t *)object; + pdo_stmt_t *stmt = row->stmt; + ZEND_ASSERT(stmt); - if (zend_string_equals_literal(Z_STR_P(member), "queryString")) { - return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv); + ZVAL_NULL(rv); + if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) { + fetch_value(stmt, rv, Z_LVAL_P(offset), NULL); } - - /* TODO: replace this with a hash of available column names to column - * numbers */ - for (colno = 0; colno < stmt->column_count; colno++) { - if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) { - fetch_value(stmt, rv, colno, NULL); - return rv; - } + return rv; + } else { + zend_string *member = zval_try_get_string(offset); + if (!member) { + return NULL; } + zval *result = row_prop_read(object, member, type, NULL, rv); + zend_string_release_ex(member, false); + return result; } - - return rv; } static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot) @@ -2350,17 +2331,16 @@ static int row_prop_exists(zend_object *object, zend_string *name, int check_emp { pdo_row_t *row = (pdo_row_t *)object; pdo_stmt_t *stmt = row->stmt; - int colno = -1; zend_long lval; ZEND_ASSERT(stmt); - if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) { + if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) { return lval >=0 && lval < stmt->column_count; } /* TODO: replace this with a hash of available column names to column * numbers */ - for (colno = 0; colno < stmt->column_count; colno++) { + for (int colno = 0; colno < stmt->column_count; colno++) { if (zend_string_equals(stmt->columns[colno].name, name)) { int res; zval val; @@ -2376,42 +2356,23 @@ static int row_prop_exists(zend_object *object, zend_string *name, int check_emp return 0; } -static int row_dim_exists(zend_object *object, zval *member, int check_empty) +static int row_dim_exists(zend_object *object, zval *offset, int check_empty) { - pdo_row_t *row = (pdo_row_t *)object; - pdo_stmt_t *stmt = row->stmt; - int colno = -1; - zend_long lval; - ZEND_ASSERT(stmt); + if (Z_TYPE_P(offset) == IS_LONG) { + pdo_row_t *row = (pdo_row_t *)object; + pdo_stmt_t *stmt = row->stmt; + ZEND_ASSERT(stmt); - if (Z_TYPE_P(member) == IS_LONG) { - return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count; - } else if (Z_TYPE_P(member) == IS_STRING) { - if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) { - return lval >=0 && lval < stmt->column_count; - } + return Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count; } else { - if (!try_convert_to_string(member)) { + zend_string *member = zval_try_get_string(offset); + if (!member) { return 0; } + int result = row_prop_exists(object, member, check_empty, NULL); + zend_string_release_ex(member, false); + return result; } - - /* TODO: replace this with a hash of available column names to column - * numbers */ - for (colno = 0; colno < stmt->column_count; colno++) { - if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) { - int res; - zval val; - - fetch_value(stmt, &val, colno, NULL); - res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL; - zval_ptr_dtor_nogc(&val); - - return res; - } - } - - return 0; } static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index a44797b7a3bfe..9f5dcafcc3e3a 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -107,8 +107,8 @@ bool(false) Offset: 0 Dimension: Isset: -bool(false) -bool(false) +bool(true) +bool(true) Empty: bool(false) bool(false) @@ -134,8 +134,8 @@ string(2) "23" Offset: '0' Dimension: Isset: -bool(false) -bool(false) +bool(true) +bool(true) Empty: bool(false) bool(false) From be9d521fb393b18cb812ac8979e7d2b592ca8a41 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 06:02:11 +0000 Subject: [PATCH 5/8] Add test showing empty is broken with column numbers --- ext/pdo/tests/pdo_035.phpt | 68 ++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index 9f5dcafcc3e3a..55b1c5575b623 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -16,17 +16,17 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); const TABLE_NAME = 'test_pdo_35_pdo_row'; -$db->exec('CREATE TABLE ' . TABLE_NAME .' (id int)'); -$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23)'); +$db->exec('CREATE TABLE ' . TABLE_NAME .' (id int, name varchar(10))'); +$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23, \'\')'); -$stmt = $db->prepare('SELECT id FROM ' . TABLE_NAME); +$stmt = $db->prepare('SELECT id, name FROM ' . TABLE_NAME); $stmt->execute(); $result = $stmt->fetch(PDO::FETCH_LAZY); var_dump($result); var_dump(get_parent_class($result)); -foreach ([0, "0", "id"] as $offset) { +foreach ([0, "0", "id", "name", 1] as $offset) { echo 'Offset: ', var_export($offset), PHP_EOL; $offsetRef = &$offset; @@ -97,11 +97,13 @@ try { ?> --EXPECTF-- -object(PDORow)#3 (2) { +object(PDORow)#3 (3) { ["queryString"]=> - string(34) "SELECT id FROM test_pdo_35_pdo_row" + string(40) "SELECT id, name FROM test_pdo_35_pdo_row" ["id"]=> string(2) "23" + ["name"]=> + string(0) "" } bool(false) Offset: 0 @@ -185,6 +187,60 @@ string(2) "23" Read: string(2) "23" string(2) "23" +Offset: 'name' +Dimension: +Isset: +bool(true) +bool(true) +Empty: +bool(true) +bool(true) +Null coalesce: +string(0) "" +string(0) "" +Read: +string(0) "" +string(0) "" +Property: +Isset: +bool(true) +bool(true) +Empty: +bool(true) +bool(true) +Null coalesce: +string(0) "" +string(0) "" +Read: +string(0) "" +string(0) "" +Offset: 1 +Dimension: +Isset: +bool(true) +bool(true) +Empty: +bool(true) +bool(true) +Null coalesce: +string(0) "" +string(0) "" +Read: +string(0) "" +string(0) "" +Property: +Isset: +bool(true) +bool(true) +Empty: +bool(true) +bool(true) +Null coalesce: +string(0) "" +string(0) "" +Read: +string(0) "" +string(0) "" Errors: Cannot write to PDORow offset Cannot append to PDORow offset From c2442ef7740f269b3749f54142bbad0c3c85431d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 05:55:51 +0000 Subject: [PATCH 6/8] ext/pdo: refactoring + fix handling of empty for rows --- ext/pdo/pdo_stmt.c | 95 +++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 7554c4bca09b6..e11d0f4288cfb 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2256,33 +2256,58 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int /* }}} */ +/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */ +static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv) +{ + /* TODO: replace this with a hash of available column names to column numbers */ + for (int colno = 0; colno < stmt->column_count; colno++) { + if (zend_string_equals(stmt->columns[colno].name, name)) { + fetch_value(stmt, rv, colno, NULL); + return rv; + } + } + return NULL; +} + +static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv) +{ + if (column >= 0 && column < stmt->column_count) { + fetch_value(stmt, rv, column, NULL); + return rv; + } + return NULL; +} + static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { pdo_row_t *row = (pdo_row_t *)object; pdo_stmt_t *stmt = row->stmt; zend_long lval; + zval *retval; ZEND_ASSERT(stmt); ZVAL_NULL(rv); if (zend_string_equals_literal(name, "queryString")) { return zend_std_read_property(&stmt->std, name, type, cache_slot, rv); } else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) { - if (lval >= 0 && lval < stmt->column_count) { - fetch_value(stmt, rv, lval, NULL); - } - return rv; + retval = row_read_column_number(stmt, lval, rv); } else { - /* TODO: replace this with a hash of available column names to column - * numbers */ - for (int colno = 0; colno < stmt->column_count; colno++) { - if (zend_string_equals(stmt->columns[colno].name, name)) { - fetch_value(stmt, rv, colno, NULL); - return rv; - } - } + retval = row_read_column_name(stmt, name, rv); + } + if (UNEXPECTED(!retval)) { + // TODO throw an error on master + //if (type != BP_VAR_IS) { + // if (is_numeric) { + // zend_value_error("Invalid column index"); + // } else { + // zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name)); + // } + //} + //return &EG(uninitialized_zval); + ZVAL_NULL(rv); + return rv; } - - return rv; + return retval; } static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv) @@ -2332,28 +2357,23 @@ static int row_prop_exists(zend_object *object, zend_string *name, int check_emp pdo_row_t *row = (pdo_row_t *)object; pdo_stmt_t *stmt = row->stmt; zend_long lval; + zval tmp_val; + zval *retval = NULL; ZEND_ASSERT(stmt); if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) { - return lval >=0 && lval < stmt->column_count; + retval = row_read_column_number(stmt, lval, &tmp_val); + } else { + retval = row_read_column_name(stmt, name, &tmp_val); } - /* TODO: replace this with a hash of available column names to column - * numbers */ - for (int colno = 0; colno < stmt->column_count; colno++) { - if (zend_string_equals(stmt->columns[colno].name, name)) { - int res; - zval val; - - fetch_value(stmt, &val, colno, NULL); - res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL; - zval_ptr_dtor_nogc(&val); - - return res; - } + if (!retval) { + return false; } - - return 0; + ZEND_ASSERT(retval == &tmp_val); + int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL; + zval_ptr_dtor_nogc(retval); + return res; } static int row_dim_exists(zend_object *object, zval *offset, int check_empty) @@ -2362,8 +2382,21 @@ static int row_dim_exists(zend_object *object, zval *offset, int check_empty) pdo_row_t *row = (pdo_row_t *)object; pdo_stmt_t *stmt = row->stmt; ZEND_ASSERT(stmt); + zend_long column = Z_LVAL_P(offset); - return Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count; + if (!check_empty) { + return column >= 0 && column < stmt->column_count; + } + + zval tmp_val; + zval *retval = row_read_column_number(stmt, column, &tmp_val); + if (!retval) { + return false; + } + ZEND_ASSERT(retval == &tmp_val); + int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL; + zval_ptr_dtor_nogc(retval); + return res; } else { zend_string *member = zval_try_get_string(offset); if (!member) { From e1c5a9079ca123013ded2ed44ee013adf08ae908 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 14:48:31 +0000 Subject: [PATCH 7/8] Fix Oracle? --- ext/pdo/tests/pdo_035.phpt | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index 55b1c5575b623..de0781f8f4f5d 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -17,7 +17,7 @@ $db = PDOTest::factory(); const TABLE_NAME = 'test_pdo_35_pdo_row'; $db->exec('CREATE TABLE ' . TABLE_NAME .' (id int, name varchar(10))'); -$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23, \'\')'); +$db->exec('INSERT INTO ' . TABLE_NAME .' VALUES (23, \'0\')'); $stmt = $db->prepare('SELECT id, name FROM ' . TABLE_NAME); $stmt->execute(); @@ -103,7 +103,7 @@ object(PDORow)#3 (3) { ["id"]=> string(2) "23" ["name"]=> - string(0) "" + string(1) "0" } bool(false) Offset: 0 @@ -196,11 +196,11 @@ Empty: bool(true) bool(true) Null coalesce: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Read: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Property: Isset: bool(true) @@ -209,11 +209,11 @@ Empty: bool(true) bool(true) Null coalesce: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Read: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Offset: 1 Dimension: Isset: @@ -223,11 +223,11 @@ Empty: bool(true) bool(true) Null coalesce: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Read: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Property: Isset: bool(true) @@ -236,11 +236,11 @@ Empty: bool(true) bool(true) Null coalesce: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Read: -string(0) "" -string(0) "" +string(1) "0" +string(1) "0" Errors: Cannot write to PDORow offset Cannot append to PDORow offset From cc3f6c9222070a0ede0c7c143d9b4fba60d3992d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 26 Feb 2024 17:45:06 +0000 Subject: [PATCH 8/8] Clean --- ext/pdo/tests/pdo_035.phpt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/pdo/tests/pdo_035.phpt b/ext/pdo/tests/pdo_035.phpt index de0781f8f4f5d..05bf32ba9afb1 100644 --- a/ext/pdo/tests/pdo_035.phpt +++ b/ext/pdo/tests/pdo_035.phpt @@ -95,6 +95,16 @@ try { echo $e->getMessage(), "\n"; } +?> +--CLEAN-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +const TABLE_NAME = 'test_pdo_35_pdo_row'; +$db->exec("DROP TABLE " . TABLE_NAME); ?> --EXPECTF-- object(PDORow)#3 (3) {