diff options
-rwxr-xr-x | ext/pdo/pdo.c | 4 | ||||
-rwxr-xr-x | ext/pdo/pdo_dbh.c | 73 | ||||
-rwxr-xr-x | ext/pdo/pdo_stmt.c | 342 | ||||
-rwxr-xr-x | ext/pdo/php_pdo_driver.h | 20 | ||||
-rwxr-xr-x | ext/pdo/php_pdo_int.h | 3 |
5 files changed, 420 insertions, 22 deletions
diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index cc620d810e..ea03f61cf3 100755 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -225,6 +225,9 @@ PHP_MINIT_FUNCTION(pdo) REGISTER_LONG_CONSTANT("PDO_FETCH_BOTH", (long)PDO_FETCH_BOTH, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PDO_FETCH_OBJ", (long)PDO_FETCH_OBJ, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PDO_FETCH_BOUND",(long)PDO_FETCH_BOUND, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PDO_FETCH_COLUMN",(long)PDO_FETCH_COLUMN, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PDO_FETCH_CLASS",(long)PDO_FETCH_CLASS, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PDO_FETCH_INTO", (long)PDO_FETCH_INTO, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PDO_ATTR_AUTOCOMMIT", (long)PDO_ATTR_AUTOCOMMIT, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PDO_ATTR_SCROLL", (long)PDO_ATTR_SCROLL, CONST_CS|CONST_PERSISTENT); @@ -272,6 +275,7 @@ PHP_MINIT_FUNCTION(pdo) INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions); ce.create_object = pdo_dbstmt_new; pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC); + pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get; INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions); ce.create_object = pdo_row_new; diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index b1cda08f37..2e814ed99c 100755 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -348,6 +348,8 @@ static PHP_METHOD(PDO, prepare) /* unconditionally keep this for later reference */ stmt->query_string = estrndup(statement, statement_len); stmt->query_stringlen = statement_len; + stmt->default_fetch_type = PDO_FETCH_BOTH; + if (dbh->methods->preparer(dbh, statement, statement_len, stmt, options, driver_options TSRMLS_CC)) { /* prepared; create a statement object for PHP land to access it */ Z_TYPE_P(return_value) = IS_OBJECT; @@ -630,6 +632,76 @@ static PHP_METHOD(PDO, errorInfo) } /* }}} */ +/* {{{ proto object PDO::queryAndIterate(string sql [, PDOStatement::setFetchMode() args]) + Prepare and execute $sql; returns the statement object for iteration */ +static PHP_METHOD(PDO, queryAndIterate) +{ + pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC); + pdo_stmt_t *stmt; + char *statement; + int statement_len; + zval *driver_options = NULL; + long options = 0; + + if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &statement, + &statement_len)) { + RETURN_FALSE; + } + + PDO_DBH_CLEAR_ERR(); + stmt = ecalloc(1, sizeof(*stmt)); + /* unconditionally keep this for later reference */ + stmt->query_string = estrndup(statement, statement_len); + stmt->query_stringlen = statement_len; + stmt->default_fetch_type = PDO_FETCH_BOTH; + + if (dbh->methods->preparer(dbh, statement, statement_len, stmt, options, driver_options TSRMLS_CC)) { + /* prepared; create a statement object for PHP land to access it */ + Z_TYPE_P(return_value) = IS_OBJECT; + Z_OBJ_HANDLE_P(return_value) = zend_objects_store_put(stmt, NULL, pdo_dbstmt_free_storage, NULL TSRMLS_CC); + Z_OBJ_HT_P(return_value) = &pdo_dbstmt_object_handlers; + + /* give it a reference to me */ + stmt->database_object_handle = *getThis(); + zend_objects_store_add_ref(getThis() TSRMLS_CC); + stmt->dbh = dbh; + + /* we haven't created a lazy object yet */ + ZVAL_NULL(&stmt->lazy_object_ref); + + stmt->refcount = 1; + + if (ZEND_NUM_ARGS() == 1 || + SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, + stmt, 1)) { + PDO_STMT_CLEAR_ERR(); + + /* now execute the statement */ + PDO_STMT_CLEAR_ERR(); + if (stmt->methods->executer(stmt TSRMLS_CC)) { + int ret = 1; + if (!stmt->executed) { + if (stmt->dbh->alloc_own_columns) { + ret = pdo_stmt_describe_columns(stmt TSRMLS_CC); + } + stmt->executed = 1; + } + if (ret) { + return; + } + } + } + /* something broke */ + PDO_HANDLE_STMT_ERR(); + + /* TODO: kill the object handle for the stmt here */ + } else { + efree(stmt); + PDO_HANDLE_DBH_ERR(); + } + RETURN_FALSE; +} +/* }}} */ function_entry pdo_dbh_functions[] = { PHP_ME_MAPPING(__construct, dbh_constructor, NULL) @@ -644,6 +716,7 @@ function_entry pdo_dbh_functions[] = { PHP_ME(PDO, errorCode, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDO, errorInfo, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDO, getAttribute, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PDO, queryAndIterate,NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 3d2d183190..e4f89b16a4 100755 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -108,7 +108,7 @@ iterate: return ret; } -static int describe_columns(pdo_stmt_t *stmt TSRMLS_DC) +int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC) { int col; @@ -338,7 +338,7 @@ static PHP_METHOD(PDOStatement, execute) if (stmt->dbh->alloc_own_columns) { /* for "big boy" drivers, we need to allocate memory to fetch * the results into, so lets do that now */ - ret = describe_columns(stmt TSRMLS_CC); + ret = pdo_stmt_describe_columns(stmt TSRMLS_CC); } stmt->executed = 1; @@ -394,7 +394,7 @@ static int do_fetch_common(pdo_stmt_t *stmt, int do_bind TSRMLS_DC) } /* some drivers might need to describe the columns now */ - if (!stmt->columns && !describe_columns(stmt TSRMLS_CC)) { + if (!stmt->columns && !pdo_stmt_describe_columns(stmt TSRMLS_CC)) { return 0; } @@ -436,6 +436,10 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_ { enum pdo_fetch_type really_how = how; + if (really_how == PDO_FETCH_USE_DEFAULT) { + really_how = how = stmt->default_fetch_type; + } + if (!do_fetch_common(stmt, do_bind TSRMLS_CC)) { return 0; } @@ -453,10 +457,44 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_ return 1; } - array_init(return_value); + switch (how) { + case PDO_FETCH_ASSOC: + case PDO_FETCH_BOTH: + case PDO_FETCH_NUM: + array_init(return_value); + break; + + case PDO_FETCH_COLUMN: + if (stmt->fetch.column >= 0 && stmt->fetch.column < stmt->column_count) { + fetch_value(stmt, return_value, stmt->fetch.column TSRMLS_CC); + return 1; + } + return 0; - if (how == PDO_FETCH_OBJ) { - how = PDO_FETCH_ASSOC; + case PDO_FETCH_OBJ: + object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR); + break; + + case PDO_FETCH_CLASS: + object_init_ex(return_value, stmt->fetch.cls.ce); + + /* TODO: call ctor */ + break; + + case PDO_FETCH_INTO: + Z_TYPE_P(return_value) = IS_OBJECT; + Z_OBJ_HANDLE_P(return_value) = Z_OBJ_HANDLE_P(stmt->fetch.into); + Z_OBJ_HT_P(return_value) = Z_OBJ_HT_P(stmt->fetch.into); + zend_objects_store_add_ref(stmt->fetch.into); + + if (zend_get_class_entry(return_value) == ZEND_STANDARD_CLASS_DEF_PTR) { + how = PDO_FETCH_OBJ; + } + break; + + default: + /* shouldn't happen */ + return 0; } for (i = 0; i < stmt->column_count; i++) { @@ -464,20 +502,29 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_ MAKE_STD_ZVAL(val); fetch_value(stmt, val, i TSRMLS_CC); - if (how == PDO_FETCH_ASSOC || how == PDO_FETCH_BOTH) { - add_assoc_zval(return_value, stmt->columns[i].name, val); - } - if (how == PDO_FETCH_NUM || how == PDO_FETCH_BOTH) { - add_next_index_zval(return_value, val); - } + switch (how) { + case PDO_FETCH_ASSOC: + add_assoc_zval(return_value, stmt->columns[i].name, val); + break; - if (how == PDO_FETCH_BOTH) { - ZVAL_ADDREF(val); - } - } + case PDO_FETCH_BOTH: + add_assoc_zval(return_value, stmt->columns[i].name, val); + ZVAL_ADDREF(val); + add_next_index_zval(return_value, val); + break; + + case PDO_FETCH_NUM: + add_next_index_zval(return_value, val); + break; - if (really_how == PDO_FETCH_OBJ) { - object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value)); + case PDO_FETCH_OBJ: + case PDO_FETCH_INTO: + case PDO_FETCH_CLASS: + zend_update_property(NULL, return_value, + stmt->columns[i].name, stmt->columns[i].namelen, + val TSRMLS_CC); + break; + } } } @@ -488,7 +535,7 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_ Fetches the next row and returns it, or false if there are no more rows */ static PHP_METHOD(PDOStatement, fetch) { - long how = PDO_FETCH_BOTH; + long how = PDO_FETCH_USE_DEFAULT; pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC); if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &how)) { @@ -528,7 +575,7 @@ static PHP_METHOD(PDOStatement, fetchSingle) static PHP_METHOD(PDOStatement, fetchAll) { pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC); - long how = PDO_FETCH_BOTH; + long how = PDO_FETCH_USE_DEFAULT; zval *data; if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &how)) { @@ -750,6 +797,140 @@ static PHP_METHOD(PDOStatement, getColumnMeta) } /* }}} */ +/* {{{ proto bool PDOStatement::setFetchMode(int mode [) + Returns meta data for a numbered column */ + +int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip) +{ + long mode = PDO_FETCH_BOTH; + int argc = ZEND_NUM_ARGS() - skip; + zval ***args; + zend_class_entry **cep; + + /* TODO: clear up class stuff here */ + switch (stmt->default_fetch_type) { + case PDO_FETCH_CLASS: + if (stmt->fetch.cls.ctor_args) { + FREE_ZVAL(stmt->fetch.cls.ctor_args); + stmt->fetch.cls.ctor_args = NULL; + } + break; + + case PDO_FETCH_INTO: + if (stmt->fetch.into) { + ZVAL_DELREF(stmt->fetch.into); + stmt->fetch.into = NULL; + } + break; + default: + ; + } + + stmt->default_fetch_type = PDO_FETCH_BOTH; + + if (argc == 0) { + return SUCCESS; + } + + args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval*), 0); + + if (FAILURE == zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args)) { +fail_out: + efree(args); + return FAILURE; + } + + convert_to_long_ex(args[skip]); + mode = Z_LVAL_PP(args[skip]); + + switch (mode) { + case PDO_FETCH_LAZY: + case PDO_FETCH_ASSOC: + case PDO_FETCH_NUM: + case PDO_FETCH_BOTH: + case PDO_FETCH_OBJ: + case PDO_FETCH_BOUND: + break; + + case PDO_FETCH_COLUMN: + if (argc != 2) { + goto fail_out; + } + convert_to_long_ex(args[skip+1]); + stmt->fetch.column = Z_LVAL_PP(args[skip+1]); + break; + + case PDO_FETCH_CLASS: + if (argc < 2 || argc > 3) { + goto fail_out; + } + convert_to_string_ex(args[skip+1]); + + if (FAILURE == zend_lookup_class(Z_STRVAL_PP(args[skip+1]), + Z_STRLEN_PP(args[skip+1]), &cep)) { + goto fail_out; + } + + if (!cep || !*cep) { + goto fail_out; + } + + stmt->fetch.cls.ce = *cep; + + if (stmt->dbh->is_persistent) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement. This will be fixed in a later release"); + } + + if (argc == 3) { + convert_to_array_ex(args[skip+2]); + stmt->fetch.cls.ctor_args = *args[skip+2]; + zval_copy_ctor(stmt->fetch.cls.ctor_args); + } + break; + + case PDO_FETCH_INTO: + if (argc != 2) { + goto fail_out; + } + if (Z_TYPE_PP(args[skip+1]) != IS_OBJECT) { + goto fail_out; + } + + if (stmt->dbh->is_persistent) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP might crash if you don't call $stmt->setFetchMode() to reset to defaults on this persistent statement. This will be fixed in a later release"); + } + + MAKE_STD_ZVAL(stmt->fetch.into); + + Z_TYPE_P(stmt->fetch.into) = IS_OBJECT; + Z_OBJ_HANDLE_P(stmt->fetch.into) = Z_OBJ_HANDLE_PP(args[skip+1]); + Z_OBJ_HT_P(stmt->fetch.into) = Z_OBJ_HT_PP(args[skip+1]); + zend_objects_store_add_ref(stmt->fetch.into); + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "mode is out of range"); + return FAILURE; + } + + stmt->default_fetch_type = mode; + efree(args); + + return SUCCESS; +} + +static PHP_METHOD(PDOStatement, setFetchMode) +{ + pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC); + + RETVAL_BOOL( + pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, + stmt, 0) == SUCCESS ? 1 : 0 + ); +} +/* }}} */ + + function_entry pdo_dbstmt_functions[] = { PHP_ME(PDOStatement, execute, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, fetch, NULL, ZEND_ACC_PUBLIC) @@ -764,6 +945,7 @@ function_entry pdo_dbstmt_functions[] = { PHP_ME(PDOStatement, getAttribute, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, columnCount, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, getColumnMeta, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, setFetchMode, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -959,6 +1141,126 @@ zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC) } /* }}} */ +/* {{{ statement iterator */ + +struct php_pdo_iterator { + zend_object_iterator iter; + pdo_stmt_t *stmt; + ulong key; + zval *fetch_ahead; +}; + +static void pdo_stmt_iter_dtor(zend_object_iterator *iter TSRMLS_DC) +{ + struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; + + if (--I->stmt->refcount == 0) { + free_statement(I->stmt TSRMLS_CC); + } + + if (I->fetch_ahead) { + ZVAL_DELREF(I->fetch_ahead); + } + + efree(I); +} + +static int pdo_stmt_iter_valid(zend_object_iterator *iter TSRMLS_DC) +{ + struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; + + return I->fetch_ahead ? SUCCESS : FAILURE; +} + +static void pdo_stmt_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) +{ + struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; + zval **ptr_ptr; + zval *ptr; + + /* sanity */ + if (!I->fetch_ahead) { + *data = NULL; + return; + } + + ptr_ptr = emalloc(sizeof(*ptr_ptr)); + *ptr_ptr = I->fetch_ahead; + ZVAL_ADDREF(I->fetch_ahead); + + *data = ptr_ptr; +} + +static int pdo_stmt_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, + ulong *int_key TSRMLS_DC) +{ + struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; + + if (I->key == (ulong)-1) { + return HASH_KEY_NON_EXISTANT; + } + *int_key = I->key; + return HASH_KEY_IS_LONG; +} + +static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) +{ + struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; + + if (I->fetch_ahead) { + ZVAL_DELREF(I->fetch_ahead); + I->fetch_ahead = NULL; + } + + MAKE_STD_ZVAL(I->fetch_ahead); + + if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT TSRMLS_CC)) { + pdo_stmt_t *stmt = I->stmt; /* for PDO_HANDLE_STMT_ERR() */ + + PDO_HANDLE_STMT_ERR(); + I->key = (ulong)-1; + FREE_ZVAL(I->fetch_ahead); + I->fetch_ahead = NULL; + + return; + } + + I->key++; +} + +static zend_object_iterator_funcs pdo_stmt_iter_funcs = { + pdo_stmt_iter_dtor, + pdo_stmt_iter_valid, + pdo_stmt_iter_get_data, + pdo_stmt_iter_get_key, + pdo_stmt_iter_move_forwards, + NULL +}; + +zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC) +{ + pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(object TSRMLS_CC); + struct php_pdo_iterator *I; + + I = ecalloc(1, sizeof(*I)); + I->iter.funcs = &pdo_stmt_iter_funcs; + I->iter.data = I; + I->stmt = stmt; + stmt->refcount++; + + MAKE_STD_ZVAL(I->fetch_ahead); + if (!do_fetch(I->stmt, TRUE, I->fetch_ahead, PDO_FETCH_USE_DEFAULT TSRMLS_CC)) { + PDO_HANDLE_STMT_ERR(); + I->key = (ulong)-1; + FREE_ZVAL(I->fetch_ahead); + I->fetch_ahead = NULL; + } + + return &I->iter; +} + +/* }}} */ + /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */ function_entry pdo_row_functions[] = { diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 082154a533..4bc96d51b3 100755 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -35,7 +35,7 @@ struct pdo_bound_param_data; # define FALSE 0 #endif -#define PDO_DRIVER_API 20040925 +#define PDO_DRIVER_API 20041027 enum pdo_param_type { PDO_PARAM_NULL, @@ -43,16 +43,20 @@ enum pdo_param_type { PDO_PARAM_STR, PDO_PARAM_LOB, PDO_PARAM_STMT, /* hierarchical result set */ - }; enum pdo_fetch_type { + PDO_FETCH_USE_DEFAULT, 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 */ + PDO_FETCH_CLASS, /* create an instance of named class, call ctor and set properties */ + PDO_FETCH_INTO, /* fetch row into an existing object */ + PDO_FETCH__MAX /* must be last */ }; enum pdo_attribute_type { @@ -358,6 +362,7 @@ struct _pdo_dbh_t { const char *persistent_id; int persistent_id_len; unsigned int refcount; + }; /* describes a column */ @@ -434,6 +439,17 @@ struct _pdo_stmt_t { * Let's keep it here. */ zval lazy_object_ref; unsigned long refcount; + + /* defaults for fetches */ + enum pdo_fetch_type default_fetch_type; + union { + int column; + struct { + zend_class_entry *ce; + zval *ctor_args; + } cls; + zval *into; + } fetch; }; /* call this in MINIT to register your PDO driver */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index 8d2be68450..8e00989567 100755 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -34,7 +34,10 @@ extern zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC); extern function_entry pdo_dbstmt_functions[]; extern zend_class_entry *pdo_dbstmt_ce; void pdo_dbstmt_free_storage(zend_object *object TSRMLS_DC); +zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC); extern zend_object_handlers pdo_dbstmt_object_handlers; +int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC); +int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int skip_first_arg); extern zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC); extern function_entry pdo_row_functions[]; |