From 066fb72fdd263147aa6b691fa7d68451f66b3225 Mon Sep 17 00:00:00 2001 From: Antony Dovgal Date: Tue, 22 Nov 2005 16:30:36 +0000 Subject: [PATCH] Initial implementation of oci_bind_array_by_name() At the moment we support only these types: SQLT_NUM, SQLT_INT, SQLT_LNG - integer/long SQLT_FLT - float SQLT_AFC, SQLT_CHR, SQLT_VCS, SQLT_AVC, SQLT_STR, SQLT_LVC - char/varchar SQLT_ODT - date More supported types may follow. --- ext/oci8/oci8.c | 28 +++ ext/oci8/oci8_interface.c | 35 ++++ ext/oci8/oci8_statement.c | 351 +++++++++++++++++++++++++++++++++++++- ext/oci8/php_oci8_int.h | 16 ++ 4 files changed, 429 insertions(+), 1 deletion(-) diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index 952d5af172..941e97d17d 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -117,6 +117,7 @@ static unsigned char oci_third_arg_force_ref[] = { 3, BYREF_NONE, BYREF_NONE, BY /* {{{ extension function prototypes */ PHP_FUNCTION(oci_bind_by_name); +PHP_FUNCTION(oci_bind_array_by_name); PHP_FUNCTION(oci_define_by_name); PHP_FUNCTION(oci_field_is_null); PHP_FUNCTION(oci_field_name); @@ -196,6 +197,7 @@ PHP_FUNCTION(oci_collection_trim); static zend_function_entry php_oci_functions[] = { PHP_FE(oci_define_by_name, oci_third_arg_force_ref) PHP_FE(oci_bind_by_name, oci_third_arg_force_ref) + PHP_FE(oci_bind_array_by_name, oci_third_arg_force_ref) PHP_FE(oci_field_is_null, NULL) PHP_FE(oci_field_name, NULL) PHP_FE(oci_field_size, NULL) @@ -527,6 +529,18 @@ PHP_MINIT_FUNCTION(oci) REGISTER_LONG_CONSTANT("SQLT_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SQLT_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SQLT_RSET",SQLT_RSET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_AFC",SQLT_AFC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_CHR",SQLT_CHR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_VCS",SQLT_VCS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_AVC",SQLT_AVC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_STR",SQLT_STR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_LVC",SQLT_LVC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_FLT",SQLT_FLT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_UIN",SQLT_UIN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_LNG",SQLT_LNG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_ODT",SQLT_ODT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_BDOUBLE",SQLT_BDOUBLE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLT_BFLOAT",SQLT_BFLOAT, CONST_CS | CONST_PERSISTENT); #ifdef PHP_OCI8_HAVE_COLLECTIONS REGISTER_LONG_CONSTANT("OCI_B_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT); @@ -719,6 +733,20 @@ void php_oci_bind_hash_dtor(void *data) { php_oci_bind *bind = (php_oci_bind *) data; + if (bind->array.elements) { + efree(bind->array.elements); + } +/* if (bind->array.element_lengths) { + efree(bind->array.element_lengths); + } + if (bind->array.indicators) { + efree(bind->array.indicators); + } + if (bind->array.retcodes) { + efree(bind->array.retcodes); + } +*/ + zval_ptr_dtor(&bind->zval); } /* }}} */ diff --git a/ext/oci8/oci8_interface.c b/ext/oci8/oci8_interface.c index 71191c1f1a..1c460877cf 100644 --- a/ext/oci8/oci8_interface.c +++ b/ext/oci8/oci8_interface.c @@ -118,6 +118,41 @@ PHP_FUNCTION(oci_bind_by_name) } /* }}} */ +/* {{{ proto bool oci_bind_array_by_name(resource stmt, string name, array &var, int max_table_length [, int max_item_length [, int type ]]) + Bind a PHP array to an Oracle PL/SQL type by name */ +PHP_FUNCTION(oci_bind_array_by_name) +{ + int name_len; + long max_item_len = -1; + long max_array_len = 0; + long type = SQLT_AFC; + char *name; + zval *z_statement; + zval *bind_var = NULL; + php_oci_statement *statement; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsz/l|ll", &z_statement, &name, &name_len, &bind_var, &max_array_len, &max_item_len, &type) == FAILURE) { + return; + } + + PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement); + + if (ZEND_NUM_ARGS() == 5 && max_item_len <= 0) { + max_item_len = -1; + } + + if (max_array_len <= 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Maximum array length must be greater than zero"); + RETURN_FALSE; + } + + if (php_oci_bind_array_by_name(statement, name, name_len, bind_var, max_array_len, max_item_len, type TSRMLS_CC)) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto bool oci_free_descriptor() Deletes large object description */ PHP_FUNCTION(oci_free_descriptor) diff --git a/ext/oci8/oci8_statement.c b/ext/oci8/oci8_statement.c index 6d9eb1240a..3927392e30 100644 --- a/ext/oci8/oci8_statement.c +++ b/ext/oci8/oci8_statement.c @@ -637,6 +637,7 @@ int php_oci_bind_pre_exec(void *data TSRMLS_DC) int php_oci_bind_post_exec(void *data TSRMLS_DC) { php_oci_bind *bind = (php_oci_bind *) data; + php_oci_connection *connection = bind->parent_statement->connection; if (bind->indicator == -1) { /* NULL */ zval *val = bind->zval; @@ -649,7 +650,87 @@ int php_oci_bind_post_exec(void *data TSRMLS_DC) Z_STRVAL_P(bind->zval) = erealloc(Z_STRVAL_P(bind->zval), Z_STRLEN_P(bind->zval)+1); Z_STRVAL_P(bind->zval)[ Z_STRLEN_P(bind->zval) ] = '\0'; } - + else if (Z_TYPE_P(bind->zval) == IS_ARRAY) { + int i; + zval **entry; + HashTable *hash = HASH_OF(bind->zval); + + switch (bind->array.type) { + case SQLT_NUM: + case SQLT_INT: + case SQLT_LNG: + for (i = 0; i < bind->array.current_length; i++) { + if ((i < bind->array.old_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + zval_dtor(*entry); + ZVAL_LONG(*entry, ((ub4 *)(bind->array.elements))[i]); + zend_hash_move_forward(hash); + } + else { + add_next_index_long(bind->zval, ((ub4 *)(bind->array.elements))[i]); + } + } + break; + case SQLT_FLT: + for (i = 0; i < bind->array.current_length; i++) { + if ((i < bind->array.old_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + zval_dtor(*entry); + ZVAL_DOUBLE(*entry, ((double *)(bind->array.elements))[i]); + zend_hash_move_forward(hash); + } + else { + add_next_index_double(bind->zval, ((double *)(bind->array.elements))[i]); + } + } + break; + case SQLT_ODT: + for (i = 0; i < bind->array.current_length; i++) { + char buff[1024]; + int buff_len = 1024; + + memset((void*)buff,0,sizeof(buff)); + + if ((i < bind->array.old_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + connection->errcode = PHP_OCI_CALL(OCIDateToText, (connection->err, &(((OCIDate *)(bind->array.elements))[i]), 0, 0, 0, 0, &buff_len, buff)); + zval_dtor(*entry); + + if (connection->errcode != OCI_SUCCESS) { + php_oci_error(connection->err, connection->errcode TSRMLS_CC); + ZVAL_NULL(*entry); + } + ZVAL_STRINGL(*entry, buff, buff_len, 1); + zend_hash_move_forward(hash); + } + else { + connection->errcode = PHP_OCI_CALL(OCIDateToText, (connection->err, &(((OCIDate *)(bind->array.elements))[i]), 0, 0, 0, 0, &buff_len, buff)); + if (connection->errcode != OCI_SUCCESS) { + php_oci_error(connection->err, connection->errcode TSRMLS_CC); + add_next_index_null(bind->zval); + } + add_next_index_stringl(bind->zval, buff, buff_len, 1); + } + } + break; + + case SQLT_AFC: + case SQLT_CHR: + case SQLT_VCS: + case SQLT_AVC: + case SQLT_STR: + case SQLT_LVC: + for (i = 0; i < bind->array.current_length; i++) { + if ((i < bind->array.old_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + zval_dtor(*entry); + ZVAL_STRINGL(*entry, ((text *)bind->array.elements)+i*bind->array.max_length, bind->array.max_length, 1); + Z_STRVAL_PP(entry)[ bind->array.max_length ] = '\0'; + zend_hash_move_forward(hash); + } + else { + add_next_index_stringl(bind->zval, ((text *)bind->array.elements)+i*bind->array.max_length, bind->array.max_length, 1); + } + } + break; + } + } return 0; } @@ -766,6 +847,7 @@ int php_oci_bind_by_name(php_oci_statement *statement, char *name, int name_len, bindp->descriptor = oci_desc; bindp->statement = oci_stmt; + bindp->parent_statement = statement; bindp->zval = var; zval_add_ref(&var); @@ -995,4 +1077,271 @@ int php_oci_statement_get_numrows(php_oci_statement *statement, ub4 *numrows TSR return 0; } /* }}} */ +/* {{{ php_oci_bind_array_by_name() + Bind arrays to PL/SQL types */ +int php_oci_bind_array_by_name(php_oci_statement *statement, char *name, int name_len, zval* var, long max_table_length, long maxlength, long type TSRMLS_DC) +{ + php_oci_bind *bind, *bindp; + + convert_to_array(var); + + if (maxlength < -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid max length value (%ld)", maxlength); + return 1; + } + + switch(type) { + case SQLT_NUM: + case SQLT_INT: + case SQLT_LNG: + bind = php_oci_bind_array_helper_number(var, max_table_length TSRMLS_CC); + break; + + case SQLT_FLT: + bind = php_oci_bind_array_helper_double(var, max_table_length TSRMLS_CC); + break; + + case SQLT_AFC: + case SQLT_CHR: + case SQLT_VCS: + case SQLT_AVC: + case SQLT_STR: + case SQLT_LVC: + if (maxlength == -1 && zend_hash_num_elements(Z_ARRVAL_P(var)) == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must provide max length value for empty arrays"); + return 1; + } + bind = php_oci_bind_array_helper_string(var, max_table_length, maxlength TSRMLS_CC); + break; + case SQLT_ODT: + bind = php_oci_bind_array_helper_date(var, max_table_length, statement->connection TSRMLS_CC); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not supported bind type (%ld)", type); + return 1; + break; + } + + if (bind == NULL) { + /* failed to generate bind struct */ + return 1; + } + + if (!statement->binds) { + ALLOC_HASHTABLE(statement->binds); + zend_hash_init(statement->binds, 13, NULL, php_oci_bind_hash_dtor, 0); + } + + zend_hash_update(statement->binds, name, name_len + 1, bind, sizeof(php_oci_bind), (void **)&bindp); + + bindp->descriptor = NULL; + bindp->statement = NULL; + bindp->parent_statement = statement; + bindp->bind = NULL; + bindp->zval = var; + bindp->array.type = type; + zval_add_ref(&var); + + statement->errcode = PHP_OCI_CALL( + OCIBindByName, + ( + statement->stmt, + (OCIBind **)&bindp->bind, + statement->err, + (text *)name, + name_len, + (dvoid *) bindp->array.elements, + (sb4) bind->array.max_length, + type, + (dvoid *)0, /* bindp->array.indicators, */ + (ub2 *)0, /* bindp->array.element_lengths, */ + (ub2 *)0, /* bindp->array.retcodes, */ + (ub4) max_table_length, + (ub4 *) &(bindp->array.current_length), + (ub4) OCI_DEFAULT + ) + ); + + + if (statement->errcode != OCI_SUCCESS) { + efree(bind); + php_oci_error(statement->err, statement->errcode TSRMLS_CC); + PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode); + return 1; + } + efree(bind); + return 0; +} /* }}} */ + +/* {{{ php_oci_bind_array_helper_string() + Bind arrays to PL/SQL types */ +php_oci_bind *php_oci_bind_array_helper_string(zval* var, long max_table_length, long maxlength TSRMLS_DC) +{ + php_oci_bind *bind; + ub4 i; + HashTable *hash; + zval **entry; + + hash = HASH_OF(var); + + if (maxlength == -1) { + zend_hash_internal_pointer_reset(hash); + while (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE) { + convert_to_string_ex(entry); + if (Z_STRLEN_PP(entry) > maxlength) { + maxlength = Z_STRLEN_PP(entry); + } + zend_hash_move_forward(hash); + } + } + + bind = emalloc(sizeof(php_oci_bind)); + bind->array.elements = (text *)emalloc(max_table_length * sizeof(text) * (maxlength + 1)); + bind->array.current_length = zend_hash_num_elements(Z_ARRVAL_P(var)); + bind->array.old_length = bind->array.current_length; + bind->array.max_length = maxlength; + + zend_hash_internal_pointer_reset(hash); + for (i = 0; i < max_table_length; i++) { + if ((i < bind->array.current_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + int element_length; + + convert_to_string_ex(entry); + element_length = (maxlength > Z_STRLEN_PP(entry)) ? Z_STRLEN_PP(entry) : maxlength; + + memcpy(bind->array.elements + i*maxlength, Z_STRVAL_PP(entry), element_length); + ((text *)bind->array.elements)[i*maxlength + element_length] = '\0'; + + zend_hash_move_forward(hash); + } + else { + ((text *)bind->array.elements)[i*maxlength] = '\0'; + } + } + zend_hash_internal_pointer_reset(hash); + + return bind; +} /* }}} */ + +/* {{{ php_oci_bind_array_helper_number() + Bind arrays to PL/SQL types */ +php_oci_bind *php_oci_bind_array_helper_number(zval* var, long max_table_length TSRMLS_DC) +{ + php_oci_bind *bind; + ub4 i; + HashTable *hash; + zval **entry; + + hash = HASH_OF(var); + + bind = emalloc(sizeof(php_oci_bind)); + bind->array.elements = (ub4 *)emalloc(max_table_length * sizeof(ub4)); + bind->array.current_length = zend_hash_num_elements(Z_ARRVAL_P(var)); + bind->array.old_length = bind->array.current_length; + bind->array.max_length = sizeof(ub4); + + zend_hash_internal_pointer_reset(hash); + for (i = 0; i < max_table_length; i++) { + if ((i < bind->array.current_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + convert_to_long_ex(entry); + ((ub4 *)bind->array.elements)[i] = (ub4) Z_LVAL_PP(entry); + zend_hash_move_forward(hash); + } + else { + ((ub4 *)bind->array.elements)[i] = 0; + } + } + zend_hash_internal_pointer_reset(hash); + + return bind; +} /* }}} */ + +/* {{{ php_oci_bind_array_helper_double() + Bind arrays to PL/SQL types */ +php_oci_bind *php_oci_bind_array_helper_double(zval* var, long max_table_length TSRMLS_DC) +{ + php_oci_bind *bind; + ub4 i; + HashTable *hash; + zval **entry; + + hash = HASH_OF(var); + + bind = emalloc(sizeof(php_oci_bind)); + bind->array.elements = (double *)emalloc(max_table_length * sizeof(double)); + bind->array.current_length = zend_hash_num_elements(Z_ARRVAL_P(var)); + bind->array.old_length = bind->array.current_length; + bind->array.max_length = sizeof(double); + + zend_hash_internal_pointer_reset(hash); + for (i = 0; i < max_table_length; i++) { + if ((i < bind->array.current_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + convert_to_double_ex(entry); + ((double *)bind->array.elements)[i] = (double) Z_DVAL_PP(entry); + zend_hash_move_forward(hash); + } + else { + ((double *)bind->array.elements)[i] = 0; + } + } + zend_hash_internal_pointer_reset(hash); + + return bind; +} /* }}} */ + +/* {{{ php_oci_bind_array_helper_date() + Bind arrays to PL/SQL types */ +php_oci_bind *php_oci_bind_array_helper_date(zval* var, long max_table_length, php_oci_connection *connection TSRMLS_DC) +{ + php_oci_bind *bind; + ub4 i; + HashTable *hash; + zval **entry; + + hash = HASH_OF(var); + + bind = emalloc(sizeof(php_oci_bind)); + bind->array.elements = (OCIDate *)emalloc(max_table_length * sizeof(OCIDate)); + bind->array.current_length = zend_hash_num_elements(Z_ARRVAL_P(var)); + bind->array.old_length = bind->array.current_length; + bind->array.max_length = sizeof(OCIDate); + + zend_hash_internal_pointer_reset(hash); + for (i = 0; i < max_table_length; i++) { + OCIDate oci_date; + if ((i < bind->array.current_length) && (zend_hash_get_current_data(hash, (void **) &entry) != FAILURE)) { + + convert_to_string_ex(entry); + connection->errcode = PHP_OCI_CALL(OCIDateFromText, (connection->err, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), NULL, 0, NULL, 0, &oci_date)); + + if (connection->errcode != OCI_SUCCESS) { + /* failed to convert string to date */ + efree(bind->array.elements); + efree(bind); + php_oci_error(connection->err, connection->errcode TSRMLS_CC); + return NULL; + } + + ((OCIDate *)bind->array.elements)[i] = oci_date; + zend_hash_move_forward(hash); + } + else { + connection->errcode = PHP_OCI_CALL(OCIDateFromText, (connection->err, "01-JAN-00", sizeof("01-JAN-00")-1, NULL, 0, NULL, 0, &oci_date)); + + if (connection->errcode != OCI_SUCCESS) { + /* failed to convert string to date */ + efree(bind->array.elements); + efree(bind); + php_oci_error(connection->err, connection->errcode TSRMLS_CC); + return NULL; + } + + ((OCIDate *)bind->array.elements)[i] = oci_date; + } + } + zend_hash_internal_pointer_reset(hash); + + return bind; +} /* }}} */ + #endif /* HAVE_OCI8 */ diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index 29810aefc8..0b42e09498 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -168,6 +168,17 @@ typedef struct { /* php_oci_bind {{{ */ zval *zval; /* value */ dvoid *descriptor; /* used for binding of LOBS etc */ OCIStmt *statement; /* used for binding REFCURSORs */ + php_oci_statement *parent_statement; /* pointer to the parent statement */ + struct { + void *elements; +/* ub2 *indicators; + ub2 *element_lengths; + ub2 *retcodes; */ + long current_length; + long old_length; + long max_length; + long type; + } array; sb2 indicator; /* */ ub2 retcode; /* */ } php_oci_bind; /* }}} */ @@ -350,6 +361,11 @@ php_oci_out_column *php_oci_statement_get_column_helper(INTERNAL_FUNCTION_PARAME int php_oci_statement_get_type(php_oci_statement *, ub2 * TSRMLS_DC); int php_oci_statement_get_numrows(php_oci_statement *, ub4 * TSRMLS_DC); +int php_oci_bind_array_by_name(php_oci_statement *statement, char *name, int name_len, zval* var, long max_table_length, long maxlength, long type TSRMLS_DC); +php_oci_bind *php_oci_bind_array_helper_number(zval* var, long max_table_length TSRMLS_DC); +php_oci_bind *php_oci_bind_array_helper_double(zval* var, long max_table_length TSRMLS_DC); +php_oci_bind *php_oci_bind_array_helper_string(zval* var, long max_table_length, long maxlength TSRMLS_DC); +php_oci_bind *php_oci_bind_array_helper_date(zval* var, long max_table_length, php_oci_connection *connection TSRMLS_DC); /* }}} */ -- 2.40.0