I have tried to contact the maintainers of PDO with this, but have yet to get a response. Can I get this reviewed and if possible commited?
Fixes BUG # 50755: http://bugs.php.net/bug.php?id=50755&edit=1 Compiled and tested with PHP SVN 5.2
Index: ext/pdo_dblib/dblib_stmt.c =================================================================== --- ext/pdo_dblib/dblib_stmt.c (revision 293600) +++ ext/pdo_dblib/dblib_stmt.c (working copy) @@ -32,35 +32,24 @@ #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" -static void free_rows(pdo_dblib_stmt *S TSRMLS_DC) +static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) { - int i, j; - - for (i = 0; i < S->nrows; i++) { - for (j = 0; j < S->ncols; j++) { - pdo_dblib_colval *val = &S->rows[i] + j; - if (val->data) { - efree(val->data); - val->data = NULL; - } - } - } - efree(S->rows); - S->rows = NULL; - S->nrows = 0; -} - -static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) -{ pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; - if (S->rows) { - free_rows(S TSRMLS_CC); - } + int i; + + /* Cancel any pending results */ + dbcancel(H->link); + + for (i = 0; i < stmt->column_count; i++) + if (S->cols[i].data) + efree(S->cols[i].data); + if (S->cols) { efree(S->cols); + S->cols = NULL; } - efree(S); return 1; } @@ -72,157 +61,163 @@ pdo_dblib_db_handle *H = S->H; RETCODE resret, ret; int i, j; - int arows; unsigned int size; - - dbsetuserdata(H->link, &S->err); - if (S->rows) { - /* clean them up */ - free_rows(S TSRMLS_CC); - } + /* clean up previous columns and values */ + pdo_dblib_stmt_cursor_closer(stmt); + dbsetuserdata(H->link, (BYTE*) &S->err); + 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); - stmt->row_count = DBCOUNT(H->link); + stmt->column_count = dbnumcols(H->link); - if (ret == NO_MORE_ROWS) { - return 1; - } - - if (!S->cols) { - S->ncols = dbnumcols(H->link); + if (stmt->column_count <= 0) { + return 1; + } - if (S->ncols <= 0) { - return 1; - } + S->cols = ecalloc(stmt->column_count, sizeof(pdo_dblib_col)); - S->cols = ecalloc(S->ncols, sizeof(pdo_dblib_col)); - stmt->column_count = S->ncols; - - for (i = 0, j = 0; i < S->ncols; i++) { - char *tmp = NULL; + for (i = 0, j = 0; i < stmt->column_count; i++) { + char *tmp = NULL; - S->cols[i].coltype = dbcoltype(H->link, i+1); - S->cols[i].name = (char*)dbcolname(H->link, i+1); + 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++; - } + 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].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); - } + 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); + return 1; +} - /* 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; +static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt) { + /* + * PHP.NET: + * "Each rowset can have a different set of columns from the preceding rowset. " + * + * BUG: segfault due to not reading the new columns + * + */ - default: - if (dbwillconvert(S->cols[i].coltype, SQLCHAR)) { - val->len = 32 + (2 * dbdatlen(H->link, i+1)); - val->data = emalloc(val->len); + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; + RETCODE resret; - val->len = dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1), - dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); + /* destroy the columns and data */ + pdo_dblib_stmt_cursor_closer(stmt); - if (val->len >= 0) { - val->data[val->len] = '\0'; - } - } else { - val->len = 0; - val->data = NULL; - } - } - } - } + /* TODO: Read the new column data */ - 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 */ + /* Grab next result set */ + resret = dbresults(H->link); + if (resret == FAIL) { + return 0; } - 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; + int i; + pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; + pdo_dblib_db_handle *H = S->H; - if (!S->rows) { + /* fetch the first/next row */ + ret = dbnextrow(H->link); + /* TODO: Check dbnumrets() for stored procs*/ + + if (ret == FAIL || ret == NO_MORE_ROWS) { return 0; } - - if (++S->current < S->nrows) { - return 1; + + /* + * This check is only valid with dbsetopt(DBBUFFER) + * and can be removed + */ + if (ret == BUF_FULL) { + dbclrbuf(H->link, DBLASTROW(H->link)-1); + /* retry dbnextrow, no row was returned from server */ + ret = dbnextrow(H->link); + if (ret != REG_ROW) + return 0; } - return 0; + for (i = 0; i < stmt->column_count; i++) { + + if (S->cols[i].data) { + efree(S->cols[i].data); + } + + if (dbdatlen(H->link, i+1) == 0 && dbdata(H->link, i+1) == NULL) { + S->cols[i].len = 0; + S->cols[i].data = NULL; + } else { + switch (S->cols[i].coltype) { + case SQLCHAR: + case SQLTEXT: + case SQLVARBINARY: + case SQLBINARY: + case SQLIMAGE: + S->cols[i].len = dbdatlen(H->link, i+1); + S->cols[i].data = emalloc(S->cols[i].len + 1); + memcpy(S->cols[i].data, dbdata(H->link, i+1), S->cols[i].len); + S->cols[i].data[S->cols[i].len] = '\0'; + break; + default: + if (dbwillconvert(S->cols[i].coltype, SQLCHAR)) { + S->cols[i].len = 32 + (2 * dbdatlen(H->link, i+1)); + S->cols[i].data = emalloc(S->cols[i].len); + + S->cols[i].len = dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1), + dbdatlen(H->link, i+1), SQLCHAR, + S->cols[i].data, S->cols[i].len); + + if (S->cols[i].len >= 0) { + S->cols[i].data[S->cols[i].len] = '\0'; + } + } else { + S->cols[i].len = 0; + S->cols[i].data = NULL; + } + } + } + } + + return 1; } static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) @@ -230,23 +225,29 @@ pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; struct pdo_column_data *col = &stmt->columns[colno]; - if (!S->rows) { + if (!S->cols) { return 0; } - col->maxlen = S->cols[colno].maxlen; - col->namelen = strlen(S->cols[colno].name); + col->maxlen = S->cols[colno].maxlen; + col->namelen = strlen(S->cols[colno].name); col->name = estrdup(S->cols[colno].name); + /* TODO: We know the col type, why do we return string? */ col->param_type = PDO_PARAM_STR; - + return 1; } +/* + * per php.net: returns a single column in the next row of a result set. + * Does PDO first call fetch and then call this next? + */ 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_col *val = &S->cols[colno]; *ptr = val->data; *len = val->len; @@ -259,15 +260,14 @@ return 1; } -static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +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); - S->rows = NULL; - } + pdo_dblib_stmt_cursor_closer(stmt); + efree(S); + return 1; } @@ -280,8 +280,8 @@ pdo_dblib_stmt_param_hook, NULL, /* set attr */ NULL, /* get attr */ - NULL, /* meta */ - NULL, /* nextrow */ - dblib_dblib_stmt_cursor_closer + NULL, /* get column meta */ + NULL, /* next rowset */ + pdo_dblib_stmt_cursor_closer }; Index: ext/pdo_dblib/php_pdo_dblib_int.h =================================================================== --- ext/pdo_dblib/php_pdo_dblib_int.h (revision 293600) +++ ext/pdo_dblib/php_pdo_dblib_int.h (working copy) @@ -113,29 +113,28 @@ pdo_dblib_err err; } pdo_dblib_db_handle; +/* + * ssufficool: 2010-01-14 + * Combined pdo_dblib_col and pdo_dblib_colval + * since we no longer buffer the rowset + */ typedef struct { int coltype; char *name; int maxlen; char *source; -} pdo_dblib_col; - -typedef struct { unsigned long len; char *data; -} pdo_dblib_colval; +} pdo_dblib_col; +/* + * ssufficool: 2010-01-14 + * Removed row buffer + * Moved col/row count to stmt) + */ typedef struct { pdo_dblib_db_handle *H; - - int ncols; pdo_dblib_col *cols; - - pdo_dblib_colval *rows; - int nrows; - - int current; - pdo_dblib_err err; } pdo_dblib_stmt; Index: ext/pdo_dblib/dblib_driver.c =================================================================== --- ext/pdo_dblib/dblib_driver.c (revision 293600) +++ ext/pdo_dblib/dblib_driver.c (working copy) @@ -92,7 +92,7 @@ { pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; pdo_dblib_stmt *S = ecalloc(1, sizeof(*S)); - + S->H = H; stmt->driver_data = S; stmt->methods = &dblib_stmt_methods; @@ -116,7 +116,7 @@ if (FAIL == dbsqlexec(H->link)) { return -1; } - + resret = dbresults(H->link); if (resret == FAIL) { @@ -162,7 +162,7 @@ *q++ = '\''; *q++ = '\0'; *quotedlen = l+1; - + return 1; } @@ -171,14 +171,16 @@ dblib_handle_preparer, dblib_handle_doer, dblib_handle_quoter, - NULL, - NULL, - NULL, - NULL, - NULL, /* last insert */ + NULL, /* begin */ + NULL, /* commit */ + NULL, /* rollback */ + NULL, /* set attribute */ + NULL, /* last insert id */ dblib_fetch_error, /* fetch error */ - NULL, /* get attr */ + NULL, /* get attribute */ NULL, /* check liveness */ + NULL, /* get driver methods */ + NULL, /* persistent shutdown */ }; static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) @@ -210,7 +212,7 @@ if (dbh->password) { DBSETLPWD(H->login, dbh->password); } - + #if !PHP_DBLIB_IS_MSSQL if (vars[0].optval) { DBSETLCHARSET(H->login, vars[0].optval); @@ -231,10 +233,10 @@ } /* dblib do not return more than this length from text/image */ - DBSETOPT(H->link, DBTEXTLIMIT, "2147483647"); - + dbsetopt(H->link, DBTEXTLIMIT, "2147483647", -1); + /* limit text/image from network */ - DBSETOPT(H->link, DBTEXTSIZE, "2147483647"); + dbsetopt(H->link, DBTEXTSIZE, "2147483647", -1); if (vars[3].optval && FAIL == dbuse(H->link, vars[3].optval)) { goto cleanup;
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php