From: Wez Furlong Date: Sun, 20 Apr 2003 13:08:22 +0000 (+0000) Subject: Implement sqlite_create_aggregate() which can be used to create aggregation X-Git-Tag: SPL_ALPHA~140 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4a468826484894fc56e0e475f3447e261e5a8692;p=php Implement sqlite_create_aggregate() which can be used to create aggregation functions for use in SQL statements. --- diff --git a/ext/sqlite/php_sqlite.h b/ext/sqlite/php_sqlite.h index 3b0dac9e57..86fb3084d5 100644 --- a/ext/sqlite/php_sqlite.h +++ b/ext/sqlite/php_sqlite.h @@ -65,6 +65,8 @@ PHP_FUNCTION(sqlite_busy_timeout); PHP_FUNCTION(sqlite_last_error); PHP_FUNCTION(sqlite_error_string); +PHP_FUNCTION(sqlite_create_aggregate); + #ifdef ZTS #define SQLITE_G(v) TSRMG(sqlite_globals_id, zend_sqlite_globals *, v) #else diff --git a/ext/sqlite/sqlite.c b/ext/sqlite/sqlite.c index 5ca2b78f08..d00ca3a47e 100644 --- a/ext/sqlite/sqlite.c +++ b/ext/sqlite/sqlite.c @@ -64,6 +64,12 @@ struct php_sqlite_db { int rsrc_id; }; +struct php_sqlite_agg_functions { + zval *step; + zval *final; +}; + + enum { PHPSQLITE_ASSOC = 1, PHPSQLITE_NUM = 2, PHPSQLITE_BOTH = PHPSQLITE_ASSOC|PHPSQLITE_NUM }; function_entry sqlite_functions[] = { @@ -85,6 +91,7 @@ function_entry sqlite_functions[] = { PHP_FE(sqlite_last_error, NULL) PHP_FE(sqlite_error_string, NULL) PHP_FE(sqlite_unbuffered_query, NULL) + PHP_FE(sqlite_create_aggregate, NULL) {NULL, NULL, NULL} }; @@ -279,6 +286,118 @@ static void php_sqlite_function_callback(sqlite_func *func, int argc, const char } /* }}} */ +static void php_sqlite_agg_step_function_callback(sqlite_func *func, int argc, const char **argv) +{ + zval *retval = NULL; + zval ***zargs; + zval **context_p; + int i, res, zargc; + struct php_sqlite_agg_functions *funcs = sqlite_user_data(func); + TSRMLS_FETCH(); + + /* sanity check the args */ + if (argc < 1) { + return; + } + + zargc = argc + 1; + zargs = (zval ***)emalloc(zargc * sizeof(zval **)); + + /* first arg is always the context zval */ + context_p = (zval **)sqlite_aggregate_context(func, sizeof(*context_p)); + if (*context_p == NULL) { + MAKE_STD_ZVAL(*context_p); + } + + zargs[0] = context_p; + + /* copy the other args */ + for (i = 0; i < argc; i++) { + zargs[i+1] = emalloc(sizeof(zval *)); + MAKE_STD_ZVAL(*zargs[i+1]); + ZVAL_STRING(*zargs[i+1], (char*)argv[i], 1); + } + + res = call_user_function_ex(EG(function_table), + NULL, + funcs->step, + &retval, + zargc, + zargs, + 0, NULL TSRMLS_CC); + + if (res != SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "call_user_function_ex failed"); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (zargs) { + for (i = 1; i < zargc; i++) { + zval_ptr_dtor(zargs[i]); + efree(zargs[i]); + } + efree(zargs); + } +} + +static void php_sqlite_agg_fini_function_callback(sqlite_func *func) +{ + zval *retval = NULL; + zval ***zargs; + zval funcname; + int i, res; + char *callable = NULL, *errbuf=NULL; + struct php_sqlite_agg_functions *funcs = sqlite_user_data(func); + zval **context_p; + TSRMLS_FETCH(); + + context_p = (zval **)sqlite_aggregate_context(func, sizeof(*context_p)); + + res = call_user_function_ex(EG(function_table), + NULL, + funcs->final, + &retval, + 1, + &context_p, + 0, NULL TSRMLS_CC); + + if (res == SUCCESS) { + if (retval == NULL) { + sqlite_set_result_string(func, NULL, 0); + } else { + switch (Z_TYPE_P(retval)) { + case IS_STRING: + /* TODO: for binary results, need to encode the string */ + sqlite_set_result_string(func, Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + break; + case IS_LONG: + case IS_BOOL: + sqlite_set_result_int(func, Z_LVAL_P(retval)); + break; + case IS_DOUBLE: + sqlite_set_result_double(func, Z_DVAL_P(retval)); + break; + case IS_NULL: + default: + sqlite_set_result_string(func, NULL, 0); + } + } + } else { + sqlite_set_result_error(func, "call_user_function_ex failed", -1); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(context_p); +} + + + /* {{{ Authorization Callback */ static int php_sqlite_authorizer(void *autharg, int access_type, const char *arg3, const char *arg4) { @@ -1020,3 +1139,50 @@ PHP_FUNCTION(sqlite_error_string) } } /* }}} */ + +/* {{{ proto bool sqlite_create_aggregate(string funcname, string step_f, string finalize_f[, long num_args]) + Registers an aggregated function for queries*/ +PHP_FUNCTION(sqlite_create_aggregate) +{ + char *funcname = NULL; + long funcname_len; + zval *zstep, *zfinal, *zdb; + struct php_sqlite_db *db; + struct php_sqlite_agg_functions *funcs; + char *callable = NULL; + long num_args = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rszz|l", &zdb, &funcname, &funcname_len, &zstep, &zfinal, &num_args) == FAILURE) { + return; + } + + if (!zend_is_callable(zstep, 0, &callable)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "step function `%s' is not callable", callable); + efree(callable); + return; + } + efree(callable); + + if (!zend_is_callable(zfinal, 0, &callable)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "finalize function `%s' is not callable", callable); + efree(callable); + return; + } + efree(callable); + + + DB_FROM_ZVAL(db, &zdb); + + /* TODO: this needs to be cleaned up */ + funcs = (struct php_sqlite_agg_functions *)emalloc(sizeof(*funcs)); + + MAKE_STD_ZVAL(funcs->step); + MAKE_STD_ZVAL(funcs->final); + *(funcs->step) = *zstep; + *(funcs->final) = *zfinal; + zval_copy_ctor(funcs->step); + zval_copy_ctor(funcs->final); + + sqlite_create_aggregate(db->db, funcname, num_args, php_sqlite_agg_step_function_callback, php_sqlite_agg_fini_function_callback, funcs); +} +/* }}} */