From c7693a7707efa6c083a266b367c60d5f8adbce0e Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 1 Feb 2013 16:44:14 +0100 Subject: [PATCH] bound vars --- src/php_pq.c | 193 +++++++++++++++++++++++++++++++++++--------- tests/bound001.phpt | 45 +++++++++++ 2 files changed, 198 insertions(+), 40 deletions(-) create mode 100644 tests/bound001.phpt diff --git a/src/php_pq.c b/src/php_pq.c index 0a0dea6..ac68ca2 100644 --- a/src/php_pq.c +++ b/src/php_pq.c @@ -157,6 +157,7 @@ typedef struct php_pqres_iterator { typedef struct php_pqres { PGresult *res; php_pqres_iterator_t *iter; + HashTable bound; } php_pqres_t; typedef struct php_pqres_object { @@ -569,6 +570,8 @@ static void php_pqres_object_free(void *o TSRMLS_DC) obj->intern->iter = NULL; } + zend_hash_destroy(&obj->intern->bound); + efree(obj->intern); obj->intern = NULL; } @@ -1639,6 +1642,7 @@ static zval *result_instance_zval(PGresult *res TSRMLS_DC) MAKE_STD_ZVAL(rid); r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); rid->type = IS_OBJECT; rid->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); @@ -1956,6 +1960,7 @@ static PHP_METHOD(pqconn, exec) { php_pqres_t *r = ecalloc(1, sizeof(*r)); r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); return_value->type = IS_OBJECT; return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); } @@ -1982,6 +1987,7 @@ static PHP_METHOD(pqconn, getResult) { php_pqres_t *r = ecalloc(1, sizeof(*r)); r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); return_value->type = IS_OBJECT; return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); } else { @@ -2161,6 +2167,7 @@ static PHP_METHOD(pqconn, execParams) { php_pqres_t *r = ecalloc(1, sizeof(*r)); r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); return_value->type = IS_OBJECT; return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); } @@ -2772,8 +2779,9 @@ static zend_function_entry php_pqtypes_methods[] = { {0} }; -static zval *php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC) +static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC) { + STATUS rv; php_pqres_fetch_t orig_fetch; if (!obj) { @@ -2786,13 +2794,146 @@ static zval *php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pq } orig_fetch = obj->intern->iter->fetch_type; obj->intern->iter->fetch_type = fetch_type; - if (SUCCESS == obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC)) { + if (SUCCESS == (rv = obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC))) { obj->intern->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->intern->iter, row TSRMLS_CC); obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC); } obj->intern->iter->fetch_type = orig_fetch; - return *row ? **row : NULL; + return rv; +} + +typedef struct php_pqres_col { + char *name; + int num; +} php_pqres_col_t; + +static STATUS column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres_col_t *col TSRMLS_DC) +{ + long index = -1; + char *name = NULL; + + switch (Z_TYPE_P(zcol)) { + default: + convert_to_string(zcol); + /* no break */ + + case IS_STRING: + if (!is_numeric_string(Z_STRVAL_P(zcol), Z_STRLEN_P(zcol), &index, NULL, 0)) { + name = Z_STRVAL_P(zcol); + } + break; + + case IS_LONG: + index = Z_LVAL_P(zcol); + break; + } + + if (name) { + col->name = name; + col->num = PQfnumber(obj->intern->res, name); + } else { + col->name = PQfname(obj->intern->res, index); + col->num = index; + } + + if (!col->name) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index); + return FAILURE; + } + if (col->num == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name); + return FAILURE; + } + return SUCCESS; +} + +static int compare_index(const void *lptr, const void *rptr TSRMLS_DC) +{ + const Bucket *l = *(const Bucket **) lptr; + const Bucket *r = *(const Bucket **) rptr; + + if (l->h < r->h) { + return -1; + } + if (l->h > r->h) { + return 1; + } + return 0; +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_bind, 0, 0, 2) + ZEND_ARG_INFO(0, col) + ZEND_ARG_INFO(1, ref) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, bind) { + zval *zcol, *zref; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref)) { + php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (obj->intern) { + php_pqres_col_t col; + + if (SUCCESS == column_nn(obj, zcol, &col TSRMLS_CC)) { + Z_ADDREF_P(zref); + if (SUCCESS == zend_hash_index_update(&obj->intern->bound, col.num, (void *) &zref, sizeof(zval *), NULL)) { + zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC); + RETVAL_TRUE; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to bind column %s@%d", col.name, col.num); + RETVAL_FALSE; + } + } else { + RETVAL_FALSE; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Result not initialized"); + RETVAL_FALSE; + } + } +} + +static int apply_bound(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +{ + zval **zvalue, **zbound = p; + zval **zrow = va_arg(argv, zval **); + + if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(zrow), key->h, (void *) &zvalue)) { + zval_dtor(*zbound); + ZVAL_COPY_VALUE(*zbound, *zvalue); + ZVAL_NULL(*zvalue); + zval_ptr_dtor(zvalue); + Z_ADDREF_P(*zbound); + *zvalue = *zbound; + return ZEND_HASH_APPLY_KEEP; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column ad index %lu", key->h); + return ZEND_HASH_APPLY_STOP; + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_bound, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, fetchBound) { + zend_error_handling zeh; + + zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); + if (SUCCESS == zend_parse_parameters_none()) { + php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (obj->intern) { + zval **row = NULL; + + if (SUCCESS == php_pqres_iteration(getThis(), obj, PHP_PQRES_FETCH_ARRAY, &row TSRMLS_CC)) { + if (row) { + zend_hash_apply_with_arguments(&obj->intern->bound TSRMLS_CC, apply_bound, 1, row); + RETVAL_ZVAL(*row, 1, 0); + } + } + } + } + zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_row, 0, 0, 0) @@ -2871,55 +3012,22 @@ static PHP_METHOD(pqres, fetchCol) { zend_restore_error_handling(&zeh TSRMLS_CC); } -typedef struct php_pqres_col { - char *name; - int num; -} php_pqres_col_t; - static int apply_to_col(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zval **c = p; php_pqres_object_t *obj = va_arg(argv, php_pqres_object_t *); php_pqres_col_t *col, **cols = va_arg(argv, php_pqres_col_t **); STATUS *rv = va_arg(argv, STATUS *); - long index = -1; - char *name = NULL; - - switch (Z_TYPE_PP(c)) { - default: - convert_to_string(*c); - /* no break */ - case IS_STRING: - if (!is_numeric_string(Z_STRVAL_PP(c), Z_STRLEN_PP(c), &index, NULL, 0)) { - name = Z_STRVAL_PP(c); - } - break; - case IS_LONG: - index = Z_LVAL_PP(c); - break; - } col = *cols; - if (name) { - col->name = name; - col->num = PQfnumber(obj->intern->res, name); + if (SUCCESS == column_nn(obj, *c, col TSRMLS_CC)) { + *rv = SUCCESS; } else { - col->name = PQfname(obj->intern->res, index); - col->num = index; - } - - if (!col->name) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index); - *rv = FAILURE; - return ZEND_HASH_APPLY_STOP; - } - if (col->num == -1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name); *rv = FAILURE; return ZEND_HASH_APPLY_STOP; } - ++*cols; + return ZEND_HASH_APPLY_KEEP; } @@ -3075,6 +3183,8 @@ static PHP_METHOD(pqres, count) { } static zend_function_entry php_pqres_methods[] = { + PHP_ME(pqres, bind, ai_pqres_bind, ZEND_ACC_PUBLIC) + PHP_ME(pqres, fetchBound, ai_pqres_fetch_bound, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchRow, ai_pqres_fetch_row, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchCol, ai_pqres_fetch_col, ZEND_ACC_PUBLIC) PHP_ME(pqres, count, ai_pqres_count, ZEND_ACC_PUBLIC) @@ -3126,7 +3236,6 @@ static PHP_METHOD(pqstm, __construct) { } zend_restore_error_handling(&zeh TSRMLS_CC); } - ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind, 0, 0, 2) ZEND_ARG_INFO(0, param_no) ZEND_ARG_INFO(1, param_ref) @@ -3141,6 +3250,7 @@ static PHP_METHOD(pqstm, bind) { if (obj->intern) { Z_ADDREF_P(param_ref); zend_hash_index_update(&obj->intern->bound, param_no, (void *) ¶m_ref, sizeof(zval *), NULL); + zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Statement not initialized"); } @@ -3187,6 +3297,7 @@ static PHP_METHOD(pqstm, exec) { php_pqres_t *r = ecalloc(1, sizeof(*r)); r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); return_value->type = IS_OBJECT; return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); } @@ -4717,7 +4828,9 @@ static PHP_MSHUTDOWN_FUNCTION(pq) */ static PHP_MINFO_FUNCTION(pq) { +#ifdef HAVE_PQLIBVERSION int libpq_v; +#endif char libpq_version[10] = "pre-9.1"; php_info_print_table_start(); diff --git a/tests/bound001.phpt b/tests/bound001.phpt new file mode 100644 index 0000000..74cf339 --- /dev/null +++ b/tests/bound001.phpt @@ -0,0 +1,45 @@ +--TEST-- +fetch bound +--SKIPIF-- + +--FILE-- +exec("select 1*a,2*a,3*a from generate_series(2,3) a"); +$r->bind(0, $a); +$r->bind(1, $b); +$r->bind(2, $c); +while ($s = $r->fetchBound()) { + var_dump($s,$a,$b,$c); +} +?> +DONE +--EXPECT-- +Test +array(3) { + [0]=> + &string(1) "2" + [1]=> + &string(1) "4" + [2]=> + &string(1) "6" +} +string(1) "2" +string(1) "4" +string(1) "6" +array(3) { + [0]=> + &string(1) "3" + [1]=> + &string(1) "6" + [2]=> + &string(1) "9" +} +string(1) "3" +string(1) "6" +string(1) "9" +DONE -- 2.30.2