diff options
author | Stanley Sufficool <ssufficool@php.net> | 2013-06-03 20:02:08 -0700 |
---|---|---|
committer | Stanley Sufficool <ssufficool@php.net> | 2013-06-03 20:02:08 -0700 |
commit | 5a04ab9a54f529f4197bed6f17599604266cdb35 (patch) | |
tree | f8ecd80c92159255e84616b56d0399b0da4dd6c7 /ext/pdo_dblib | |
parent | 7360f0f1e6c4b49e89bd3c2cf1f524e9cf7f9dcc (diff) | |
download | php-git-5a04ab9a54f529f4197bed6f17599604266cdb35.tar.gz |
Fix PDO_DBLIB bugs: #64338, #64808, #63638
Synchronize with master
Diffstat (limited to 'ext/pdo_dblib')
-rw-r--r-- | ext/pdo_dblib/dblib_driver.c | 103 | ||||
-rw-r--r-- | ext/pdo_dblib/dblib_stmt.c | 99 | ||||
-rw-r--r-- | ext/pdo_dblib/pdo_dblib.c | 8 | ||||
-rw-r--r-- | ext/pdo_dblib/php_pdo_dblib_int.h | 8 |
4 files changed, 160 insertions, 58 deletions
diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index ffc910177f..9ed5087369 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -32,6 +32,9 @@ #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" +/* Cache of the server supported datatypes, initialized in handle_factory */ +zval* pdo_dblib_datatypes; + static int dblib_fetch_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) { pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; @@ -262,17 +265,37 @@ static struct pdo_dbh_methods dblib_methods = { static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) { pdo_dblib_db_handle *H; - int i, ret = 0; + int i, nvars, nvers, ret = 0; + int *val; + + const pdo_dblib_keyval tdsver[] = { + {"4.2",DBVERSION_42} + ,{"4.6",DBVERSION_46} + ,{"5.0",DBVERSION_70} /* FIXME: This does not work with Sybase, but environ will */ + ,{"6.0",DBVERSION_70} + ,{"7.0",DBVERSION_70} + ,{"7.1",DBVERSION_71} + ,{"7.2",DBVERSION_72} + ,{"8.0",DBVERSION_72} + ,{"10.0",DBVERSION_100} + ,{"auto",0} /* Only works with FreeTDS. Other drivers will bork */ + + }; + + nvers = sizeof(tdsver)/sizeof(tdsver[0]); + struct pdo_data_src_parser vars[] = { - { "charset", NULL, 0 }, - { "appname", "PHP " PDO_DBLIB_FLAVOUR, 0 }, - { "host", "127.0.0.1", 0 }, - { "dbname", NULL, 0 }, - { "secure", NULL, 0 }, /* DBSETLSECURE */ - /* TODO: DBSETLVERSION ? */ + { "charset", NULL, 0 } + ,{ "appname", "PHP " PDO_DBLIB_FLAVOUR, 0 } + ,{ "host", "127.0.0.1", 0 } + ,{ "dbname", NULL, 0 } + ,{ "secure", NULL, 0 } /* DBSETLSECURE */ + ,{ "version", NULL, 0 } /* DBSETLVERSION */ }; - - php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 5); + + nvars = sizeof(vars)/sizeof(vars[0]); + + php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, nvars); H = pecalloc(1, sizeof(*H), dbh->is_persistent); H->login = dblogin(); @@ -282,11 +305,37 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ goto cleanup; } + DBERRHANDLE(H->login, (EHANDLEFUNC) error_handler); + DBMSGHANDLE(H->login, (MHANDLEFUNC) msg_handler); + + if(vars[5].optval) { + for(i=0;i<nvers;i++) { + if(strcmp(vars[5].optval,tdsver[i].key) == 0) { + if(FAIL==dbsetlversion(H->login, tdsver[i].value)) { + pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Failed to set version specified in connection string." TSRMLS_CC); + goto cleanup; + } + break; + } + } + + if (i==nvers) { + printf("Invalid version '%s'\n", vars[5].optval); + pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Invalid version specified in connection string." TSRMLS_CC); + goto cleanup; /* unknown version specified */ + } + } + if (dbh->username) { - DBSETLUSER(H->login, dbh->username); + if(FAIL == DBSETLUSER(H->login, dbh->username)) { + goto cleanup; + } } + if (dbh->password) { - DBSETLPWD(H->login, dbh->password); + if(FAIL == DBSETLPWD(H->login, dbh->password)) { + goto cleanup; + } } #if !PHP_DBLIB_IS_MSSQL @@ -297,14 +346,9 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ DBSETLAPP(H->login, vars[1].optval); -#if PHP_DBLIB_IS_MSSQL - dbprocerrhandle(H->login, (EHANDLEFUNC) error_handler); - dbprocmsghandle(H->login, (MHANDLEFUNC) msg_handler); -#endif - H->link = dbopen(H->login, vars[2].optval); - if (H->link == NULL) { + if (!H->link) { goto cleanup; } @@ -315,18 +359,35 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ DBSETOPT(H->link, DBTEXTSIZE, "2147483647"); /* allow double quoted indentifiers */ - DBSETOPT(H->link, DBQUOTEDIDENT, NULL); + DBSETOPT(H->link, DBQUOTEDIDENT, "1"); - if (vars[3].optval && FAIL == dbuse(H->link, vars[3].optval)) { - goto cleanup; + if (vars[3].optval) { + DBSETLDBNAME(H->login, vars[3].optval); } ret = 1; dbh->max_escaped_char_length = 2; dbh->alloc_own_columns = 1; +#if 0 + /* Cache the supported data types from the servers systypes table */ + if(dbcmd(H->link, "select usertype, name from systypes order by usertype") != FAIL) { + if(dbsqlexec(H->link) != FAIL) { + dbresults(H->link); + while (dbnextrow(H->link) == SUCCESS) { + val = dbdata(H->link, 1); + add_index_string(pdo_dblib_datatypes, *val, dbdata(H->link, 2), 1); + } + } + /* Throw out any remaining resultsets */ + dbcancel(H-link); + } +#endif + + + cleanup: - for (i = 0; i < sizeof(vars)/sizeof(vars[0]); i++) { + for (i = 0; i < nvars; i++) { if (vars[i].freeme) { efree(vars[i].optval); } diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 1a2fefd47a..51cebc48b0 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -36,35 +36,51 @@ /* {{{ pdo_dblib_get_field_name * - * Updated for MSSQL 2008 SR2 extended types + * Return the data type name for a given TDS number * */ static char *pdo_dblib_get_field_name(int type) { + /* + * 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"; - case 108: return "numeric"; + 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 240: return "geometry"; case 165: return "varbinary"; case 167: return "varchar"; case 173: return "binary"; @@ -72,23 +88,22 @@ static char *pdo_dblib_get_field_name(int type) case 189: return "timestamp"; case 231: return "nvarchar"; case 239: return "nchar"; + case 240: return "geometry"; case 241: return "xml"; - default: - return "unknown"; - break; + default: return "unknown"; } } /* }}} */ -static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) +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); + + efree(stmt->columns); stmt->columns = NULL; return 1; @@ -98,7 +113,8 @@ static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; - dblib_dblib_stmt_cursor_closer(stmt TSRMLS_CC); + efree(stmt->columns); + stmt->columns = NULL; efree(S); @@ -113,7 +129,12 @@ static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) ret = dbresults(H->link); - if (ret == FAIL || ret == NO_MORE_RESULTS) { + 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; } @@ -131,6 +152,8 @@ static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) 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; } @@ -141,10 +164,6 @@ static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) ret = pdo_dblib_stmt_next_rowset(stmt TSRMLS_CC); - if (ret == 0) { - return 0; - } - stmt->row_count = DBCOUNT(H->link); stmt->column_count = dbnumcols(H->link); @@ -162,7 +181,12 @@ static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt, ret = dbnextrow(H->link); - if (ret == FAIL || ret == NO_MORE_ROWS) { + if (FAIL == ret) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL" TSRMLS_CC); + return 0; + } + + if(NO_MORE_ROWS == ret) { return 0; } @@ -174,6 +198,10 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; + } + struct pdo_column_data *col = &stmt->columns[colno]; col->name = (char*)dbcolname(H->link, colno+1); @@ -205,11 +233,12 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, } switch (coltype) { - case SQLCHAR: - case SQLTEXT: 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); @@ -225,34 +254,26 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, *ptr = tmp_ptr; break; } -#ifdef SQLUNIQUE case SQLUNIQUE: { -#else - case 36: { /* FreeTDS hack, also used by ext/mssql */ -#endif *len = 36+1; tmp_ptr = emalloc(*len + 1); /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */ -#ifdef SQLUNIQUE *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len); -#else - *len = dbconvert(NULL, 36, *ptr, *len, SQLCHAR, tmp_ptr, *len); -#endif php_strtoupper(tmp_ptr, *len); *ptr = tmp_ptr; break; } default: if (dbwillconvert(coltype, SQLCHAR)) { - tmp_len = 32 + (2 * (*len)); + 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; - *ptr = NULL; - } + } else { + *len = 0; /* FIXME: Silently fails and returns null on conversion errors */ + *ptr = NULL; + } } *caller_frees = 1; @@ -270,17 +291,25 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *re { pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; - + DBTYPEINFO* dbtypeinfo; + + if(colno >= stmt->column_count || colno < 0) { + return FAILURE; + } + array_init(return_value); - DBTYPEINFO* dbtypeinfo; 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; } @@ -297,6 +326,6 @@ struct pdo_stmt_methods dblib_stmt_methods = { NULL, /* get attr */ pdo_dblib_stmt_get_column_meta, /* meta */ pdo_dblib_stmt_next_rowset, /* nextrow */ - dblib_dblib_stmt_cursor_closer + pdo_dblib_stmt_cursor_closer }; diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index ed79aea20d..bc5d364ed3 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -93,8 +93,12 @@ int error_handler(DBPROCESS *dbproc, int severity, int dberr, char *state = "HY000"; TSRMLS_FETCH(); - einfo = (pdo_dblib_err*)dbgetuserdata(dbproc); - if (!einfo) einfo = &DBLIB_G(err); + if(dbproc) { + einfo = (pdo_dblib_err*)dbgetuserdata(dbproc); + if (!einfo) einfo = &DBLIB_G(err); + } else { + einfo = &DBLIB_G(err); + } einfo->severity = severity; einfo->oserr = oserr; diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h index dd06a1d94f..2bdb83c326 100644 --- a/ext/pdo_dblib/php_pdo_dblib_int.h +++ b/ext/pdo_dblib/php_pdo_dblib_int.h @@ -71,6 +71,8 @@ # define SQLVARBINARY SYBVARBINARY # ifdef SYBUNIQUE # define SQLUNIQUE SYBUNIQUE +#else +# define SQLUNIQUE 36 /* FreeTDS Hack */ # endif # define DBERRHANDLE(a, b) dberrhandle(b) @@ -118,6 +120,12 @@ typedef struct { pdo_dblib_err err; } pdo_dblib_stmt; +typedef struct { + const char* key; + int value; +} pdo_dblib_keyval; + + ZEND_BEGIN_MODULE_GLOBALS(dblib) pdo_dblib_err err; char sqlstate[6]; |