Hi!
I "enhanced" my previous patch with the possibility of zero key rows...
HPO
Hello Hans-Peter,
can you add tests so we get a better idea?
cvs add <test> ; cvd di -N > patchfile.txt
marcus
Monday, November 26, 2007, 5:43:33 PM, you wrote:
Index: ext/pdo/pdo_dbh.c
RCS file: /repository/php-src/ext/pdo/pdo_dbh.c,v
retrieving revision 1.82.2.31.2.17.2.2
diff -u -r1.82.2.31.2.17.2.2 pdo_dbh.c
--- ext/pdo/pdo_dbh.c 7 Oct 2007 05:22:05 -0000 1.82.2.31.2.17.2.2
+++ ext/pdo/pdo_dbh.c 26 Nov 2007 16:38:16 -0000
@@ -784,6 +784,15 @@
return SUCCESS;
}
case PDO_ATTR_2D_NULLBASE:
if( dbh->nullbase ) {
efree( dbh->nullbase );
}
dbh->nullbase = (value ? estrdup( Z_STRVAL_P(value) ) : `NULL` );
return SUCCESS;
default: ; }
@@ -872,6 +881,8 @@
case PDO_ATTR_DEFAULT_FETCH_MODE:
RETURN_LONG(dbh->default_fetch_type);
case PDO_ATTR_2D_NULLBASE:
RETURN_STRING(dbh->nullbase, 1); } if (!dbh->methods->get_attribute) {
@@ -1331,10 +1342,11 @@
REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_POST",
(long)PDO_PARAM_EVT_FETCH_POST);
REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_NORMALIZE",
(long)PDO_PARAM_EVT_NORMALIZE);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_LAZY", (long)PDO_FETCH_LAZY); REGISTER_PDO_CLASS_CONST_LONG("FETCH_ASSOC",(long)PDO_FETCH_ASSOC); REGISTER_PDO_CLASS_CONST_LONG("FETCH_NUM", (long)PDO_FETCH_NUM); REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOTH", (long)PDO_FETCH_BOTH);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_2D", (long)PDO_FETCH_2D);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_LAZY", (long)PDO_FETCH_LAZY); REGISTER_PDO_CLASS_CONST_LONG("FETCH_OBJ", (long)PDO_FETCH_OBJ); REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOUND",(long)PDO_FETCH_BOUND);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_COLUMN",(long)PDO_FETCH_COLUMN);
@@ -1343,7 +1355,8 @@
REGISTER_PDO_CLASS_CONST_LONG("FETCH_FUNC", (long)PDO_FETCH_FUNC);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_GROUP",(long)PDO_FETCH_GROUP);REGISTER_PDO_CLASS_CONST_LONG("FETCH_UNIQUE",(long)PDO_FETCH_UNIQUE);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR",(long)PDO_FETCH_KEY_PAIR);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR",(long)PDO_FETCH_KEYS);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEYS",(long)PDO_FETCH_KEYS);
REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE",(long)PDO_FETCH_CLASSTYPE);
#ifPHP_MAJOR_VERSION
> 5 ||PHP_MINOR_VERSION
>= 1REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(long)PDO_FETCH_SERIALIZE);
@@ -1372,7 +1385,7 @@REGISTER_PDO_CLASS_CONST_LONG("ATTR_MAX_COLUMN_LEN",(long)PDO_ATTR_MAX_COLUMN_LEN);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_EMULATE_PREPARES",(long)PDO_ATTR_EMULATE_PREPARES);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_DEFAULT_FETCH_MODE",(long)PDO_ATTR_DEFAULT_FETCH_MODE);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_2D_NULLBASE", (long)PDO_ATTR_2D_NULLBASE); REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_SILENT", (long)PDO_ERRMODE_SILENT); REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_WARNING", (long)PDO_ERRMODE_WARNING); REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_EXCEPTION", (long)PDO_ERRMODE_EXCEPTION);
@@ -1472,13 +1485,17 @@
dbh->methods->rollback(dbh TSRMLS_CC);
dbh->in_txn = 0;
}
if (dbh->properties) { zend_hash_destroy(dbh->properties); efree(dbh->properties); dbh->properties = NULL; }
if( dbh->nullbase ) {
efree( dbh->nullbase );
}
if (!dbh->is_persistent) { dbh_free(dbh TSRMLS_CC); } else if (dbh->methods && dbh->methods->persistent_shutdown) {
@@ -1496,6 +1513,7 @@
memset(dbh, 0, sizeof(*dbh));
dbh->ce = ce;
dbh->refcount = 1;
dbh->nullbase = estrdup( "" ); ALLOC_HASHTABLE(dbh->properties); zend_hash_init(dbh->properties, 0, NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(dbh->properties, &ce->default_properties, (copy_ctor_func_t)
zval_add_ref, (void *) &tmp, sizeof(zval *));
Index: ext/pdo/pdo_stmt.cRCS file: /repository/php-src/ext/pdo/pdo_stmt.c,v
retrieving revision 1.118.2.38.2.24.2.7
diff -u -r1.118.2.38.2.24.2.7 pdo_stmt.c
--- ext/pdo/pdo_stmt.c 20 Nov 2007 23:12:17 -0000 1.118.2.38.2.24.2.7
+++ ext/pdo/pdo_stmt.c 26 Nov 2007 16:38:16 -0000
@@ -886,6 +886,68 @@
}
/* }}} */+/* iteratively create array out of a column */
+static void do_fetch_keys_iterative( pdo_stmt_t *stmt, zval *base, zval *index ) {
zval *val;
zval **valp;
int isfound;
int i=1;
while(
(i < stmt->fetch.num_keys)
|| (i <= stmt->fetch.num_keys &&
stmt->column_count-stmt->fetch.num_keys > 1)
){
isfound = (( (Z_TYPE_P(index) == IS_LONG)
? zend_hash_index_find(Z_ARRVAL_P(base), Z_LVAL_P(index),
(void**)&valp)
: zend_hash_find(Z_ARRVAL_P(base), Z_STRVAL_P(index),
Z_STRLEN_P(index)+1, (void**)&valp)
) == SUCCESS);
if( !isfound ) {
MAKE_STD_ZVAL(val);
array_init(val);
if( Z_TYPE_P(index) == IS_LONG ) {
add_index_zval( base, Z_LVAL_P(index), val );
} else {
add_assoc_zval( base, Z_STRVAL_P(index), val );
}
}
base = (isfound ? (*valp) : val);
zval_ptr_dtor(&index);
fetch_value( stmt, index, i++, `NULL` TSRMLS_CC);
}
if( stmt->column_count-stmt->fetch.num_keys > 1 ) {
do {
add_assoc_zval( base, stmt->columns[i-1].name, index );
zval_ptr_dtor(&index);
if( i < stmt->column_count ) {
fetch_value( stmt, index, i, `NULL` TSRMLS_CC);
}
} while( i++ < stmt->column_count );
}
else if( stmt->fetch.num_keys > 0 ) {
MAKE_STD_ZVAL(val);
fetch_value( stmt, val, i, `NULL` TSRMLS_CC );
if( Z_TYPE_P(index) == IS_LONG ) {
add_index_zval( base, Z_LVAL_P(index), val );
} else {
add_assoc_zval( base, Z_STRVAL_P(index), val );
}
}
else {
while( `TRUE` ) {
add_next_index_zval( base, index );
if( i < stmt->column_count ) {
fetch_value( stmt, index, i++, `NULL` TSRMLS_CC );
}
else {
break;
}
}
}
+}
/* perform a fetch. If do_bind is true, update any bound columns.
- If return_value is not null, store values into it according to HOW. */
static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
@@ -894,7 +956,8 @@
int flags = how & PDO_FETCH_FLAGS, idx, old_arg_count = 0;
zend_class_entry *ce = NULL, *old_ce = NULL;
zval grp_val, *grp, **pgrp, *retval, *old_ctor_args = NULL;
zval *nullbase = NULL;
if (how == PDO_FETCH_USE_DEFAULT) { how = stmt->default_fetch_type; }
@@ -925,6 +988,10 @@
case PDO_FETCH_BOTH:
case PDO_FETCH_NUM:
case PDO_FETCH_NAMED:
case PDO_FETCH_2D:
case PDO_FETCH_2D_NUM:
case PDO_FETCH_2D_ASSOC:
case PDO_FETCH_2D_BOTH: if (!return_all) {
ALLOC_HASHTABLE(return_value->value.ht);
zend_hash_init(return_value->value.ht,
stmt->column_count, NULL, ZVAL_PTR_DTOR, 0);
@@ -934,9 +1001,9 @@
}
break;
case PDO_FETCH_KEY_PAIR:
if (stmt->column_count != 2) {
pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
"PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns."
TSRMLS_CC);
case PDO_FETCH_KEYS:
if (stmt->column_count <= stmt->fetch.num_keys) {
pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
"PDO::FETCH_KEYS needs at least one value column." TSRMLS_CC);
return 0;
}
if (!return_all) {
@@ -1047,7 +1114,7 @@
return 0;
}
if (return_all && how != PDO_FETCH_KEY_PAIR) {
if (return_all && how != PDO_FETCH_KEYS) { INIT_PZVAL(&grp_val); fetch_value(stmt, &grp_val, i, `NULL` TSRMLS_CC); convert_to_string(&grp_val);
@@ -1060,27 +1127,66 @@
for (idx = 0; i < stmt->column_count; i++, idx++) { zval *val;
zval *base2d; MAKE_STD_ZVAL(val); fetch_value(stmt, val, i, `NULL` TSRMLS_CC); switch (how) {
case PDO_FETCH_2D:
case PDO_FETCH_2D_NUM:
case PDO_FETCH_2D_ASSOC:
case PDO_FETCH_2D_BOTH:
if( !nullbase && ((how & PDO_FETCH_BOTH) ||
!stmt->columns[i].relname) ) {
if( stmt->dbh->nullbase ) {
MAKE_STD_ZVAL(nullbase);
array_init(nullbase);
add_assoc_zval(return_value,
stmt->dbh->nullbase, nullbase);
}
else {
nullbase = return_value;
}
}
if( stmt->columns[i].relname ) {
zval **curr_val = NULL;
if
(zend_hash_find(Z_ARRVAL_P(return_value),
stmt->columns[i].relname,
stmt->columns[i].relnamelen+1,
(void**)&curr_val)
== SUCCESS) {
if( Z_TYPE_PP(curr_val) != IS_ARRAY
) {
/* TODO: ERROR OUT OR NOT? */
return 0;
}
base2d = *curr_val;
}
else {
MAKE_STD_ZVAL(base2d);
array_init(base2d);
add_assoc_zval(return_value,
stmt->columns[i].relname, base2d);
}
}
else {
base2d = nullbase;
}
add_assoc_zval(base2d, stmt->columns[i].name, val);
if( how & PDO_FETCH_ASSOC ) {
Z_ADDREF_P(val);
add_assoc_zval(nullbase,
stmt->columns[i].name, val);
}
if( how & PDO_FETCH_NUM ) {
Z_ADDREF_P(val);
add_next_index_zval(nullbase, val);
}
break;
case PDO_FETCH_ASSOC: add_assoc_zval(return_value, stmt->columns[i].name,
val);
break;
case PDO_FETCH_KEY_PAIR:
case PDO_FETCH_KEYS: {
zval *tmp;
MAKE_STD_ZVAL(tmp);
fetch_value(stmt, tmp, ++i, `NULL` TSRMLS_CC);
if (Z_TYPE_P(val) == IS_LONG) {
zend_hash_index_update((return_all ?
Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL_P(val), &tmp, sizeof(zval *),
NULL);
} else {
convert_to_string(val);
zend_symtable_update((return_all ?
Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STRVAL_P(val), Z_STRLEN_P(val) + 1,
&tmp, sizeof(zval *), NULL);
}
zval_ptr_dtor(&val);
do_fetch_keys_iterative( stmt, (return_all ?
return_all : return_value), val );
return 1;
}
break;
@@ -1539,6 +1645,15 @@
}
break;
case PDO_FETCH_KEYS:
if( ZEND_NUM_ARGS() == 2 ) {
stmt->fetch.num_keys = Z_LVAL_P(arg2);
}
else {
stmt->fetch.num_keys = 1;
}
break;
default: if (ZEND_NUM_ARGS() > 1) { pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Extraneous
additional parameters" TSRMLS_CC);
@@ -1553,8 +1668,8 @@
if (!error) {
PDO_STMT_CLEAR_ERR();
MAKE_STD_ZVAL(data);
if ( (how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type ==
PDO_FETCH_KEY_PAIR)
if ( (how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEYS ||
(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type ==
PDO_FETCH_KEYS)
) {
array_init(return_value);
return_all = return_value;
@@ -1571,7 +1686,7 @@
do {
MAKE_STD_ZVAL(data);
} while (do_fetch(stmt, TRUE, data, how, PDO_FETCH_ORI_NEXT, 0,
return_all TSRMLS_CC));
} else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT &&
stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
} else if (how == PDO_FETCH_KEYS || (how == PDO_FETCH_USE_DEFAULT &&
stmt->default_fetch_type == PDO_FETCH_KEYS)) {
while (do_fetch(stmt, TRUE, data, how, PDO_FETCH_ORI_NEXT, 0,
return_all TSRMLS_CC));
} else {
array_init(return_value);
@@ -1922,7 +2037,10 @@
case PDO_FETCH_OBJ:
case PDO_FETCH_BOUND:
case PDO_FETCH_NAMED:
case PDO_FETCH_KEY_PAIR:
case PDO_FETCH_2D:
case PDO_FETCH_2D_NUM:
case PDO_FETCH_2D_ASSOC:
case PDO_FETCH_2D_BOTH: break; case PDO_FETCH_COLUMN:
@@ -1988,6 +2106,18 @@
zend_objects_store_add_ref(stmt->fetch.into TSRMLS_CC);
break;
case PDO_FETCH_KEYS:
if( argc != 2 ) {
stmt->fetch.num_keys = 1;
}
else if( Z_TYPE_PP(args[skip+1]) == IS_LONG ) {
stmt->fetch.num_keys = Z_LVAL_PP(args[skip+1]);
}
else {
pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "num_keys
must be integer" TSRMLS_CC);
}
break;
default: pdo_raise_impl_error(stmt->dbh, stmt, "22003", "Invalid fetch mode
specified" TSRMLS_CC);
goto fail_out;
@@ -2348,6 +2478,10 @@
efree(cols[i].name);
cols[i].name = NULL;
}
if (cols[i].relname) {
efree(cols[i].relname);
cols[i].relname = NULL;
} } efree(stmt->columns); stmt->columns = NULL;
Index: ext/pdo/php_pdo_driver.h
RCS file: /repository/php-src/ext/pdo/php_pdo_driver.h,v
retrieving revision 1.66.2.11.2.6.2.1
diff -u -r1.66.2.11.2.6.2.1 php_pdo_driver.h
--- ext/pdo/php_pdo_driver.h 27 Sep 2007 18:00:42 -0000 1.66.2.11.2.6.2.1
+++ ext/pdo/php_pdo_driver.h 26 Nov 2007 16:38:16 -0000
@@ -79,10 +79,14 @@enum pdo_fetch_type {
PDO_FETCH_USE_DEFAULT,
PDO_FETCH_ASSOC, /* BEGIN */
PDO_FETCH_NUM, /* All values from BEGIN to END are specially */
PDO_FETCH_BOTH, /* arranged to be bitwise combineable. */
PDO_FETCH_2D, /* e.g. ASSOC | NUM = BOTH */
PDO_FETCH_2D_ASSOC, /* e.g. 2D | ASSOC = 2D_ASSOC */
PDO_FETCH_2D_NUM,
PDO_FETCH_2D_BOTH, /* END */ PDO_FETCH_LAZY,
PDO_FETCH_ASSOC,
PDO_FETCH_NUM,
PDO_FETCH_BOTH, PDO_FETCH_OBJ, PDO_FETCH_BOUND, /* return true/false only; rely on bound columns */ PDO_FETCH_COLUMN, /* fetch a numbered column only */
@@ -90,7 +94,7 @@
PDO_FETCH_INTO, /* fetch row into an existing object /
PDO_FETCH_FUNC, / fetch into function and return its result /
PDO_FETCH_NAMED, / like PDO_FETCH_ASSOC, but can handle duplicate names */
PDO_FETCH_KEY_PAIR, /* fetch into an array where the 1st column is a key and all
subsequent columns are values */
PDO_FETCH_KEYS, /* fetch into an array where the first column(s) are keys and
all subsequent columns are values /
PDO_FETCH__MAX / must be last */
};@@ -124,6 +128,7 @@
PDO_ATTR_CURSOR_NAME, /* name a cursor for use in "WHERE CURRENT OF
<name>" /
PDO_ATTR_CURSOR, / cursor type /
PDO_ATTR_ORACLE_NULLS, / convert empty strings toNULL
*/
PDO_ATTR_CONNECTION_LAZY, /* only connect to database upon use */ PDO_ATTR_PERSISTENT, /* pconnect style connection */ PDO_ATTR_STATEMENT_CLASS, /* array(classname, array(ctor_args)) to specify the
class of the constructed statement /
PDO_ATTR_FETCH_TABLE_NAMES, / include table names in the column names, where
available /
@@ -133,6 +138,7 @@
PDO_ATTR_MAX_COLUMN_LEN, / make database calculate maximum length of data
found in a column /
PDO_ATTR_DEFAULT_FETCH_MODE, / Set the default fetch mode /
PDO_ATTR_EMULATE_PREPARES, / use query emulation rather than native */
PDO_ATTR_2D_NULLBASE, /* array name for not further qualified columns */ /* this defines the start of the range for driver specific options. * Drivers should define their own attribute constants beginning with this
@@ -470,6 +476,9 @@
* equal 32 */
unsigned _reserved_flags:21;
/* 2d nullbase */
char *nullbase;
/* data source string used to open this handle */ const char *data_source; unsigned long data_source_len;
@@ -508,6 +517,8 @@
/* describes a column */
struct pdo_column_data {
char *relname;
int relnamelen; char *name; int namelen; unsigned long maxlen;
@@ -620,6 +631,7 @@
zend_fcall_info_cache fcc;
zval *values; / freed */
} func;
int num_keys; zval *into; } fetch;
Index: ext/pdo_firebird/firebird_statement.c
RCS file: /repository/php-src/ext/pdo_firebird/firebird_statement.c,v
retrieving revision 1.18.2.1.2.5.2.8
diff -u -r1.18.2.1.2.5.2.8 firebird_statement.c
--- ext/pdo_firebird/firebird_statement.c 19 Nov 2007 21:55:30 -0000
1.18.2.1.2.5.2.8
+++ ext/pdo_firebird/firebird_statement.c 26 Nov 2007 16:38:16 -0000
@@ -164,8 +164,10 @@
colname_len = (S->H->fetch_table_names && var->relname_length)
? (var->aliasname_length + var->relname_length + 1)
: (var->aliasname_length);
col->precision = -var->sqlscale;
col->precision = -var->sqlscale; col->maxlen = var->sqllen;
col->relname = (var->relname_length ? estrndup(var->relname, var->relname_length) :
NULL);
col->relnamelen = var->relname_length; col->namelen = colname_len; col->name = cp = emalloc(colname_len + 1); if (colname_len > var->aliasname_length) {
Index: ext/pdo_mysql/mysql_statement.c
RCS file: /repository/php-src/ext/pdo_mysql/mysql_statement.c,v
retrieving revision 1.48.2.14.2.6
diff -u -r1.48.2.14.2.6 mysql_statement.c
--- ext/pdo_mysql/mysql_statement.c 17 May 2007 15:12:23 -0000 1.48.2.14.2.6
+++ ext/pdo_mysql/mysql_statement.c 26 Nov 2007 16:38:17 -0000
@@ -454,6 +454,8 @@
cols[i].maxlen = S->fields[i].length;
cols[i].namelen = namelen;
cols[i].name = estrndup(S->fields[i].name, namelen);
cols[i].relnamelen = strlen(S->fields[i].table); /* TODO: WHERE are names
efree'd? */
cols[i].relname = (cols[i].relnamelen ? estrndup(S->fields[i].table,
cols[i].relnamelen) : NULL);
cols[i].param_type = PDO_PARAM_STR;
}
return 1;
Best regards,
Marcus
Hoi!
Marcus Boerger wrote:
can you add tests so we get a better idea?
cvs add <test> ; cvd di -N > patchfile.txt
I could... If I had a cvs account.
As I don't, please find attached an updated patch (I hope I got the zval
allocation right) as well as:
- pdo_034.phpt (updated in patch)
- pdo_035.phpt (addition, not in patch)
Please be aware that pdo_035.phpt may only pass on firebird and mysql as
it depends on an updated column information struct.
HPO