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