Skip to content

Commit b4e272c

Browse files
committed
ext/pdo: Fix various PDORow bugs
- Add tests - NULL derefencing in read_dimension handler - Fix isset() - Fix empty() with column numbers as offsets - Refactoring to use common functions
1 parent e1a8ebd commit b4e272c

File tree

4 files changed

+362
-141
lines changed

4 files changed

+362
-141
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.2.18
44

5+
- PDO:
6+
. Fix various PDORow bugs. (Girgias)
7+
58

69
14 Mar 2024, PHP 8.2.17
710

ext/pdo/pdo_stmt.c

+97-95
Original file line numberDiff line numberDiff line change
@@ -2257,74 +2257,84 @@ zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int
22572257
/* }}} */
22582258

22592259
/* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2260+
static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv)
2261+
{
2262+
/* TODO: replace this with a hash of available column names to column numbers */
2263+
for (int colno = 0; colno < stmt->column_count; colno++) {
2264+
if (zend_string_equals(stmt->columns[colno].name, name)) {
2265+
fetch_value(stmt, rv, colno, NULL);
2266+
return rv;
2267+
}
2268+
}
2269+
return NULL;
2270+
}
2271+
2272+
static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv)
2273+
{
2274+
if (column >= 0 && column < stmt->column_count) {
2275+
fetch_value(stmt, rv, column, NULL);
2276+
return rv;
2277+
}
2278+
return NULL;
2279+
}
22602280

22612281
static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
22622282
{
22632283
pdo_row_t *row = (pdo_row_t *)object;
22642284
pdo_stmt_t *stmt = row->stmt;
2265-
int colno = -1;
22662285
zend_long lval;
2286+
zval *retval;
22672287
ZEND_ASSERT(stmt);
22682288

22692289
ZVAL_NULL(rv);
22702290
if (zend_string_equals_literal(name, "queryString")) {
22712291
return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2272-
} else if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2273-
if (lval >= 0 && lval < stmt->column_count) {
2274-
fetch_value(stmt, rv, lval, NULL);
2275-
}
2292+
} else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2293+
retval = row_read_column_number(stmt, lval, rv);
22762294
} else {
2277-
/* TODO: replace this with a hash of available column names to column
2278-
* numbers */
2279-
for (colno = 0; colno < stmt->column_count; colno++) {
2280-
if (zend_string_equals(stmt->columns[colno].name, name)) {
2281-
fetch_value(stmt, rv, colno, NULL);
2282-
return rv;
2283-
}
2284-
}
2295+
retval = row_read_column_name(stmt, name, rv);
22852296
}
2286-
2287-
return rv;
2297+
if (UNEXPECTED(!retval)) {
2298+
// TODO throw an error on master
2299+
//if (type != BP_VAR_IS) {
2300+
// if (is_numeric) {
2301+
// zend_value_error("Invalid column index");
2302+
// } else {
2303+
// zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name));
2304+
// }
2305+
//}
2306+
//return &EG(uninitialized_zval);
2307+
ZVAL_NULL(rv);
2308+
return rv;
2309+
}
2310+
return retval;
22882311
}
22892312

2290-
static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv)
2313+
static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv)
22912314
{
2292-
pdo_row_t *row = (pdo_row_t *)object;
2293-
pdo_stmt_t *stmt = row->stmt;
2294-
int colno = -1;
2295-
zend_long lval;
2296-
ZEND_ASSERT(stmt);
2315+
if (UNEXPECTED(!offset)) {
2316+
zend_throw_error(NULL, "Cannot append to PDORow offset");
2317+
return NULL;
2318+
}
2319+
if (Z_TYPE_P(offset) == IS_LONG) {
2320+
pdo_row_t *row = (pdo_row_t *)object;
2321+
pdo_stmt_t *stmt = row->stmt;
2322+
ZEND_ASSERT(stmt);
22972323

2298-
ZVAL_NULL(rv);
2299-
if (Z_TYPE_P(member) == IS_LONG) {
2300-
if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2301-
fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2302-
}
2303-
} else if (Z_TYPE_P(member) == IS_STRING
2304-
&& is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2305-
if (lval >= 0 && lval < stmt->column_count) {
2306-
fetch_value(stmt, rv, lval, NULL);
2324+
ZVAL_NULL(rv);
2325+
if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) {
2326+
fetch_value(stmt, rv, Z_LVAL_P(offset), NULL);
23072327
}
2328+
return rv;
23082329
} else {
2309-
if (!try_convert_to_string(member)) {
2310-
return &EG(uninitialized_zval);
2311-
}
2312-
2313-
if (zend_string_equals_literal(Z_STR_P(member), "queryString")) {
2314-
return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv);
2315-
}
2316-
2317-
/* TODO: replace this with a hash of available column names to column
2318-
* numbers */
2319-
for (colno = 0; colno < stmt->column_count; colno++) {
2320-
if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2321-
fetch_value(stmt, rv, colno, NULL);
2322-
return rv;
2323-
}
2330+
zend_string *member = zval_try_get_string(offset);
2331+
if (!member) {
2332+
return NULL;
23242333
}
2334+
zval *result = row_prop_read(object, member, type, NULL, rv);
2335+
zend_string_release_ex(member, false);
2336+
return result;
23252337
}
2326-
2327-
return rv;
23282338
}
23292339

23302340
static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
@@ -2335,75 +2345,67 @@ static zval *row_prop_write(zend_object *object, zend_string *name, zval *value,
23352345

23362346
static void row_dim_write(zend_object *object, zval *member, zval *value)
23372347
{
2338-
zend_throw_error(NULL, "Cannot write to PDORow offset");
2348+
if (!member) {
2349+
zend_throw_error(NULL, "Cannot append to PDORow offset");
2350+
} else {
2351+
zend_throw_error(NULL, "Cannot write to PDORow offset");
2352+
}
23392353
}
23402354

23412355
static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
23422356
{
23432357
pdo_row_t *row = (pdo_row_t *)object;
23442358
pdo_stmt_t *stmt = row->stmt;
2345-
int colno = -1;
23462359
zend_long lval;
2360+
zval tmp_val;
2361+
zval *retval = NULL;
23472362
ZEND_ASSERT(stmt);
23482363

2349-
if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2350-
return lval >=0 && lval < stmt->column_count;
2364+
if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2365+
retval = row_read_column_number(stmt, lval, &tmp_val);
2366+
} else {
2367+
retval = row_read_column_name(stmt, name, &tmp_val);
23512368
}
23522369

2353-
/* TODO: replace this with a hash of available column names to column
2354-
* numbers */
2355-
for (colno = 0; colno < stmt->column_count; colno++) {
2356-
if (zend_string_equals(stmt->columns[colno].name, name)) {
2357-
int res;
2358-
zval val;
2359-
2360-
fetch_value(stmt, &val, colno, NULL);
2361-
res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2362-
zval_ptr_dtor_nogc(&val);
2363-
2364-
return res;
2365-
}
2370+
if (!retval) {
2371+
return false;
23662372
}
2367-
2368-
return 0;
2373+
ZEND_ASSERT(retval == &tmp_val);
2374+
int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2375+
zval_ptr_dtor_nogc(retval);
2376+
return res;
23692377
}
23702378

2371-
static int row_dim_exists(zend_object *object, zval *member, int check_empty)
2379+
static int row_dim_exists(zend_object *object, zval *offset, int check_empty)
23722380
{
2373-
pdo_row_t *row = (pdo_row_t *)object;
2374-
pdo_stmt_t *stmt = row->stmt;
2375-
int colno = -1;
2376-
zend_long lval;
2377-
ZEND_ASSERT(stmt);
2381+
if (Z_TYPE_P(offset) == IS_LONG) {
2382+
pdo_row_t *row = (pdo_row_t *)object;
2383+
pdo_stmt_t *stmt = row->stmt;
2384+
ZEND_ASSERT(stmt);
2385+
zend_long column = Z_LVAL_P(offset);
23782386

2379-
if (Z_TYPE_P(member) == IS_LONG) {
2380-
return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2381-
} else if (Z_TYPE_P(member) == IS_STRING) {
2382-
if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2383-
return lval >=0 && lval < stmt->column_count;
2387+
if (!check_empty) {
2388+
return column >= 0 && column < stmt->column_count;
23842389
}
2390+
2391+
zval tmp_val;
2392+
zval *retval = row_read_column_number(stmt, column, &tmp_val);
2393+
if (!retval) {
2394+
return false;
2395+
}
2396+
ZEND_ASSERT(retval == &tmp_val);
2397+
int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2398+
zval_ptr_dtor_nogc(retval);
2399+
return res;
23852400
} else {
2386-
if (!try_convert_to_string(member)) {
2401+
zend_string *member = zval_try_get_string(offset);
2402+
if (!member) {
23872403
return 0;
23882404
}
2405+
int result = row_prop_exists(object, member, check_empty, NULL);
2406+
zend_string_release_ex(member, false);
2407+
return result;
23892408
}
2390-
2391-
/* TODO: replace this with a hash of available column names to column
2392-
* numbers */
2393-
for (colno = 0; colno < stmt->column_count; colno++) {
2394-
if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2395-
int res;
2396-
zval val;
2397-
2398-
fetch_value(stmt, &val, colno, NULL);
2399-
res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2400-
zval_ptr_dtor_nogc(&val);
2401-
2402-
return res;
2403-
}
2404-
}
2405-
2406-
return 0;
24072409
}
24082410

24092411
static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)

0 commit comments

Comments
 (0)