--- embedaddon/php/ext/pdo_dblib/dblib_stmt.c 2012/02/21 23:47:59 1.1 +++ embedaddon/php/ext/pdo_dblib/dblib_stmt.c 2013/07/22 01:31:58 1.1.1.3 @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2012 The PHP Group | + | Copyright (c) 1997-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dblib_stmt.c,v 1.1 2012/02/21 23:47:59 misho Exp $ */ +/* $Id: dblib_stmt.c,v 1.1.1.3 2013/07/22 01:31:58 misho Exp $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -33,234 +33,180 @@ #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" -static void free_rows(pdo_dblib_stmt *S TSRMLS_DC) + +/* {{{ pdo_dblib_get_field_name + * + * Return the data type name for a given TDS number + * + */ +static char *pdo_dblib_get_field_name(int type) { - int i, j; - - for (i = 0; i < S->nrows; i++) { - for (j = 0; j < S->ncols; j++) { - pdo_dblib_colval *val = &S->rows[i*S->ncols] + j; - if (val->data) { - efree(val->data); - val->data = NULL; - } - } + /* + * I don't return dbprtype(type) because it does not fully describe the type + * (example: varchar is reported as char by dbprtype) + * + * FIX ME: Cache datatypes from server systypes table in pdo_dblib_handle_factory() + * to make this future proof. + */ + + switch (type) { + case 31: return "nvarchar"; + case 34: return "image"; + case 35: return "text"; + case 36: return "uniqueidentifier"; + case 37: return "varbinary"; /* & timestamp - Sybase AS12 */ + case 38: return "bigint"; /* & bigintn - Sybase AS12 */ + case 39: return "varchar"; /* & sysname & nvarchar - Sybase AS12 */ + case 40: return "date"; + case 41: return "time"; + case 42: return "datetime2"; + case 43: return "datetimeoffset"; + case 45: return "binary"; /* Sybase AS12 */ + case 47: return "char"; /* & nchar & uniqueidentifierstr Sybase AS12 */ + case 48: return "tinyint"; + case 50: return "bit"; /* Sybase AS12 */ + case 52: return "smallint"; + case 55: return "decimal"; /* Sybase AS12 */ + case 56: return "int"; + case 58: return "smalldatetime"; + case 59: return "real"; + case 60: return "money"; + case 61: return "datetime"; + case 62: return "float"; + case 63: return "numeric"; /* or uint, ubigint, usmallint Sybase AS12 */ + case 98: return "sql_variant"; + case 99: return "ntext"; + case 104: return "bit"; + case 106: return "decimal"; /* decimal n on sybase */ + case 108: return "numeric"; /* numeric n on sybase */ + case 122: return "smallmoney"; + case 127: return "bigint"; + case 165: return "varbinary"; + case 167: return "varchar"; + case 173: return "binary"; + case 175: return "char"; + case 189: return "timestamp"; + case 231: return "nvarchar"; + case 239: return "nchar"; + case 240: return "geometry"; + case 241: return "xml"; + default: return "unknown"; } - efree(S->rows); - S->rows = NULL; - S->nrows = 0; } +/* }}} */ +static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +{ + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; + + /* Cancel any pending results */ + dbcancel(H->link); + + efree(stmt->columns); + stmt->columns = NULL; + + return 1; +} + static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - if (S->rows) { - free_rows(S TSRMLS_CC); - } - if (S->cols) { - efree(S->cols); - } - efree(S); + efree(stmt->columns); + stmt->columns = NULL; + efree(S); + return 1; } -static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) +static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; - RETCODE resret, ret; - int i, j; - int arows; - unsigned int size; + RETCODE ret; - dbsetuserdata(H->link, &S->err); - - if (S->rows) { - /* clean them up */ - free_rows(S TSRMLS_CC); + ret = dbresults(H->link); + + if (FAIL == ret) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL" TSRMLS_CC); + return 0; } + + if(NO_MORE_RESULTS == ret) { + return 0; + } + + stmt->row_count = DBCOUNT(H->link); + stmt->column_count = dbnumcols(H->link); + + return 1; +} +static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) +{ + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; + RETCODE ret; + + dbsetuserdata(H->link, (BYTE*) &S->err); + + pdo_dblib_stmt_cursor_closer(stmt TSRMLS_CC); + if (FAIL == dbcmd(H->link, stmt->active_query_string)) { return 0; } + if (FAIL == dbsqlexec(H->link)) { return 0; } - resret = dbresults(H->link); - if (resret == FAIL) { - return 0; - } - - ret = dbnextrow(H->link); - + ret = pdo_dblib_stmt_next_rowset(stmt TSRMLS_CC); + stmt->row_count = DBCOUNT(H->link); - - if (ret == NO_MORE_ROWS) { - return 1; - } - - if (!S->cols) { - S->ncols = dbnumcols(H->link); - - if (S->ncols <= 0) { - return 1; - } - - S->cols = ecalloc(S->ncols, sizeof(pdo_dblib_col)); - stmt->column_count = S->ncols; + stmt->column_count = dbnumcols(H->link); - for (i = 0, j = 0; i < S->ncols; i++) { - char *tmp = NULL; - - S->cols[i].coltype = dbcoltype(H->link, i+1); - S->cols[i].name = (char*)dbcolname(H->link, i+1); - - if (!strlen(S->cols[i].name)) { - if (j) { - spprintf(&tmp, 0, "computed%d", j++); - strlcpy(S->cols[i].name, tmp, strlen(tmp)+1); - efree(tmp); - } else { - S->cols[i].name = "computed"; - j++; - } - } - - S->cols[i].source = (char*)dbcolsource(H->link, i+1); - tmp = estrdup(S->cols[i].source ? S->cols[i].source : ""); - S->cols[i].source = tmp; - efree(tmp); - - S->cols[i].maxlen = dbcollen(H->link, i+1); - } - } - - arows = 100; - size = S->ncols * sizeof(pdo_dblib_colval); - S->rows = safe_emalloc(arows, size, 0); - - /* let's fetch all the data */ - do { - if (S->nrows >= arows) { - arows *= 2; - S->rows = erealloc(S->rows, arows * size); - } - for (i = 0; i < S->ncols; i++) { - pdo_dblib_colval *val = &S->rows[S->nrows * S->ncols + i]; - - if (dbdatlen(H->link, i+1) == 0 && dbdata(H->link, i+1) == NULL) { - val->len = 0; - val->data = NULL; - } else { - switch (S->cols[i].coltype) { - case SQLCHAR: - case SQLTEXT: - case SQLVARBINARY: - case SQLBINARY: - case SQLIMAGE: - val->len = dbdatlen(H->link, i+1); - val->data = emalloc(val->len + 1); - memcpy(val->data, dbdata(H->link, i+1), val->len); - val->data[val->len] = '\0'; - break; - case SQLMONEY: - case SQLMONEY4: - case SQLMONEYN: { - DBFLT8 money_value; - dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLFLT8, (LPBYTE)&money_value, 8); - val->len = spprintf(&val->data, 0, "%.4f", money_value); - } - break; -#ifdef SQLUNIQUE - case SQLUNIQUE: { -#else - case 36: { /* FreeTDS hack, also used by ext/mssql */ -#endif - val->len = 36+1; - val->data = emalloc(val->len + 1); - - /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */ -#ifdef SQLUNIQUE - val->len = dbconvert(NULL, SQLUNIQUE, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); -#else - val->len = dbconvert(NULL, 36, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); -#endif - php_strtoupper(val->data, val->len); - break; - } - default: - if (dbwillconvert(S->cols[i].coltype, SQLCHAR)) { - val->len = 32 + (2 * dbdatlen(H->link, i+1)); - val->data = emalloc(val->len); - - val->len = dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1), - dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); - - if (val->len >= 0) { - val->data[val->len] = '\0'; - } - } else { - val->len = 0; - val->data = NULL; - } - } - } - } - - S->nrows++; - - ret = dbnextrow(H->link); - - if (ret == BUF_FULL) { - dbclrbuf(H->link, DBLASTROW(H->link)-1); - } - } while (ret != FAIL && ret != NO_MORE_ROWS); - - if (resret != NO_MORE_RESULTS) { - /* there are additional result sets available */ - dbresults(H->link); - /* cancel pending rows */ - dbcanquery(H->link); - - /* TODO: figure out a sane solution */ - } - - S->current = -1; - - return 1; + return 1; } static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, long offset TSRMLS_DC) { + + RETCODE ret; + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - - if (!S->rows) { + pdo_dblib_db_handle *H = S->H; + + ret = dbnextrow(H->link); + + if (FAIL == ret) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL" TSRMLS_CC); return 0; } - - if (++S->current < S->nrows) { - return 1; + + if(NO_MORE_ROWS == ret) { + return 0; } - - return 0; + + return 1; } static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - struct pdo_column_data *col = &stmt->columns[colno]; - - if (!S->rows) { - return 0; + pdo_dblib_db_handle *H = S->H; + + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; } - - col->maxlen = S->cols[colno].maxlen; - col->namelen = strlen(S->cols[colno].name); - col->name = estrdup(S->cols[colno].name); + + struct pdo_column_data *col = &stmt->columns[colno]; + + col->name = (char*)dbcolname(H->link, colno+1); + col->maxlen = dbcollen(H->link, colno+1); + col->namelen = strlen(col->name); col->param_type = PDO_PARAM_STR; return 1; @@ -269,11 +215,69 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, i static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) { + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - pdo_dblib_colval *val = &S->rows[S->current * S->ncols + colno]; + pdo_dblib_db_handle *H = S->H; + + int coltype; + unsigned int tmp_len; + char *tmp_ptr = NULL; + + coltype = dbcoltype(H->link, colno+1); + + *len = dbdatlen(H->link, colno+1); + *ptr = dbdata(H->link, colno+1); + + if (*len == 0 && *ptr == NULL) { + return 1; + } + + switch (coltype) { + case SQLVARBINARY: + case SQLBINARY: + case SQLIMAGE: + case SQLTEXT: + /* FIXME: Above types should be returned as a stream as they can be VERY large */ + case SQLCHAR: + case SQLVARCHAR: + tmp_ptr = emalloc(*len + 1); + memcpy(tmp_ptr, *ptr, *len); + tmp_ptr[*len] = '\0'; + *ptr = tmp_ptr; + break; + case SQLMONEY: + case SQLMONEY4: + case SQLMONEYN: { + DBFLT8 money_value; + dbconvert(NULL, coltype, *ptr, *len, SQLFLT8, (LPBYTE)&money_value, 8); + *len = spprintf(&tmp_ptr, 0, "%.4f", money_value); + *ptr = tmp_ptr; + break; + } + case SQLUNIQUE: { + *len = 36+1; + tmp_ptr = emalloc(*len + 1); - *ptr = val->data; - *len = val->len; + /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */ + *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len); + php_strtoupper(tmp_ptr, *len); + *ptr = tmp_ptr; + break; + } + default: + if (dbwillconvert(coltype, SQLCHAR)) { + tmp_len = 32 + (2 * (*len)); /* FIXME: We allocate more than we need here */ + tmp_ptr = emalloc(tmp_len); + *len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1); + *ptr = tmp_ptr; + } else { + *len = 0; /* FIXME: Silently fails and returns null on conversion errors */ + *ptr = NULL; + } + } + + *caller_frees = 1; + return 1; } @@ -283,18 +287,34 @@ static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, return 1; } -static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; + DBTYPEINFO* dbtypeinfo; - if (S->rows) { - free_rows(S TSRMLS_CC); - S->rows = NULL; + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; } + array_init(return_value); + + dbtypeinfo = dbcoltypeinfo(H->link, colno+1); + + if(!dbtypeinfo) return FAILURE; + + add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) ); + add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision ); + add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale ); + add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1), 1); + add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)), 1); + add_assoc_long(return_value, "native_type_id", dbcoltype(H->link, colno+1)); + add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1)); + return 1; } + struct pdo_stmt_methods dblib_stmt_methods = { pdo_dblib_stmt_dtor, pdo_dblib_stmt_execute, @@ -304,8 +324,8 @@ struct pdo_stmt_methods dblib_stmt_methods = { pdo_dblib_stmt_param_hook, NULL, /* set attr */ NULL, /* get attr */ - NULL, /* meta */ - NULL, /* nextrow */ - dblib_dblib_stmt_cursor_closer + pdo_dblib_stmt_get_column_meta, /* meta */ + pdo_dblib_stmt_next_rowset, /* nextrow */ + pdo_dblib_stmt_cursor_closer };