From 211a7820edcc4454efc7892b3d64228233f8a1f8 Mon Sep 17 00:00:00 2001 From: "MiRacLe.RPZ" Date: Wed, 10 Feb 2016 17:41:02 +0300 Subject: [PATCH 1/3] Fix #69592: pdo_dblib now skips empty resultsets while fetching data Some sql-statements can return empty resultsets, now pdo_dblib skips empty datasets like ext/mssql. --- ext/pdo_dblib/dblib_stmt.c | 13 +++++++++--- ext/pdo_dblib/tests/bug_69592.phpt | 33 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 ext/pdo_dblib/tests/bug_69592.phpt diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 263ae6dcb8fdc..64b94a751421c 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -126,8 +126,15 @@ 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 ret; - - ret = dbresults(H->link); + int num_fields; + + do { + ret = dbresults(H->link); + } while ((num_fields = dbnumcols(H->link)) <= 0 && ret == SUCCEED); + + if (num_fields <= 0) { + return 0; + } if (FAIL == ret) { pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL" TSRMLS_CC); @@ -139,7 +146,7 @@ static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) } stmt->row_count = DBCOUNT(H->link); - stmt->column_count = dbnumcols(H->link); + stmt->column_count = num_fields; return 1; } diff --git a/ext/pdo_dblib/tests/bug_69592.phpt b/ext/pdo_dblib/tests/bug_69592.phpt new file mode 100644 index 0000000000000..3999e15c13343 --- /dev/null +++ b/ext/pdo_dblib/tests/bug_69592.phpt @@ -0,0 +1,33 @@ +--TEST-- +PDO_DBLIB: Empty resultsets on SET NOCOUNT expression +--SKIPIF-- + +--FILE-- +query($sql); +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(result => 0), actual: array() +var_dump($stmt->nextRowset()); // expected: bool(false), actual: bool(true) +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(), actual: array(result => 0) +?> +--EXPECT-- +array(1) { + [0]=> + array(1) { + ["result"]=> + string(1) "0" + } +} +bool(false) +array(0) { +} + From 82bfd09e41f8c4c5afd06ff60aef1dc510cccfb5 Mon Sep 17 00:00:00 2001 From: "MiRacLe.RPZ" Date: Tue, 1 Mar 2016 00:26:51 +0300 Subject: [PATCH 2/3] Added attribute PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS (false by default) for skip rowsets without columns --- ext/pdo_dblib/dblib_driver.c | 31 ++++++++++++++++++++++++++++-- ext/pdo_dblib/dblib_stmt.c | 11 ++++++----- ext/pdo_dblib/pdo_dblib.c | 1 + ext/pdo_dblib/php_pdo_dblib_int.h | 5 ++++- ext/pdo_dblib/tests/bug_69592.phpt | 9 ++++++++- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 4303379adf928..06bd3ce3a60b5 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -244,6 +244,33 @@ char *dblib_handle_last_id(pdo_dbh_t *dbh, const char *name, unsigned int *len T return id; } +static int dblib_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) +{ + pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; + switch (attr) { + case PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS: + H->skip_empty_rowsets = zval_is_true(val); + return 1; + default: + return 0; + } +} + +static int pdo_dblib_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC) +{ + pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; + + switch (attr) { + case PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS: + ZVAL_BOOL(return_value, H->skip_empty_rowsets); + break; + default: + return 0; + } + + return 1; +} + static struct pdo_dbh_methods dblib_methods = { dblib_handle_closer, dblib_handle_preparer, @@ -252,10 +279,10 @@ static struct pdo_dbh_methods dblib_methods = { dblib_handle_begin, /* begin */ dblib_handle_commit, /* commit */ dblib_handle_rollback, /* rollback */ - NULL, /*set attr */ + dblib_handle_set_attr, /*set attr */ dblib_handle_last_id, /* last insert id */ dblib_fetch_error, /* fetch error */ - NULL, /* get attr */ + pdo_dblib_get_attribute, /* get attr */ NULL, /* check liveness */ NULL, /* get driver methods */ NULL, /* request shutdown */ diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 64b94a751421c..3c445051c09b3 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -127,12 +127,13 @@ static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) pdo_dblib_db_handle *H = S->H; RETCODE ret; int num_fields; - - do { - ret = dbresults(H->link); - } while ((num_fields = dbnumcols(H->link)) <= 0 && ret == SUCCEED); - if (num_fields <= 0) { + do { + ret = dbresults(H->link); + num_fields = dbnumcols(H->link); + } while (H->skip_empty_rowsets && num_fields <= 0 && ret == SUCCEED); + + if (H->skip_empty_rowsets && num_fields <= 0) { return 0; } diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 55ce016e54e1e..0a61680f1d936 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -188,6 +188,7 @@ PHP_RSHUTDOWN_FUNCTION(pdo_dblib) PHP_MINIT_FUNCTION(pdo_dblib) { + REGISTER_PDO_CLASS_CONST_LONG("DBLIB_ATTR_SKIP_EMPTY_ROWSETS", PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS); if (FAIL == dbinit()) { return FAILURE; } diff --git a/ext/pdo_dblib/php_pdo_dblib_int.h b/ext/pdo_dblib/php_pdo_dblib_int.h index 925315a14a89a..22c62f68982a1 100644 --- a/ext/pdo_dblib/php_pdo_dblib_int.h +++ b/ext/pdo_dblib/php_pdo_dblib_int.h @@ -111,7 +111,7 @@ typedef struct { typedef struct { LOGINREC *login; DBPROCESS *link; - + unsigned skip_empty_rowsets:1; pdo_dblib_err err; } pdo_dblib_db_handle; @@ -125,6 +125,9 @@ typedef struct { int value; } pdo_dblib_keyval; +enum { + PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS = PDO_ATTR_DRIVER_SPECIFIC, +}; ZEND_BEGIN_MODULE_GLOBALS(dblib) pdo_dblib_err err; diff --git a/ext/pdo_dblib/tests/bug_69592.phpt b/ext/pdo_dblib/tests/bug_69592.phpt index 3999e15c13343..3f729976be3f3 100644 --- a/ext/pdo_dblib/tests/bug_69592.phpt +++ b/ext/pdo_dblib/tests/bug_69592.phpt @@ -9,6 +9,12 @@ require dirname(__FILE__) . '/config.inc'; getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); // disabled by default + +$db->setAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS, true); + +var_dump($db->getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); + $sql = ' SET NOCOUNT ON SELECT 0 AS [result] @@ -20,6 +26,8 @@ var_dump($stmt->nextRowset()); // expected: bool(false), actual: bool(true) var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(), actual: array(result => 0) ?> --EXPECT-- +bool(false) +bool(true) array(1) { [0]=> array(1) { @@ -30,4 +38,3 @@ array(1) { bool(false) array(0) { } - From abf40f2f97239749b156e89230b0faa4f17c5111 Mon Sep 17 00:00:00 2001 From: "MiRacLe.RPZ" Date: Thu, 3 Mar 2016 00:07:00 +0300 Subject: [PATCH 3/3] actualize testcase --- ext/pdo_dblib/tests/bug_69592.phpt | 34 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/ext/pdo_dblib/tests/bug_69592.phpt b/ext/pdo_dblib/tests/bug_69592.phpt index 3f729976be3f3..ad5133f27998a 100644 --- a/ext/pdo_dblib/tests/bug_69592.phpt +++ b/ext/pdo_dblib/tests/bug_69592.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO_DBLIB: Empty resultsets on SET NOCOUNT expression +PDO_DBLIB: PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS for skip junk resultsets on SET NOCOUNT expression --SKIPIF-- getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); // disabled by default - -$db->setAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS, true); - -var_dump($db->getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); - $sql = ' SET NOCOUNT ON SELECT 0 AS [result] '; +var_dump($db->getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); // disabled by default + +$stmt = $db->query($sql); +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(result => 0), actual: array() +var_dump($stmt->nextRowset()); // expected: bool(false), actual: bool(true) +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(), actual: array(result => 0) +$stmt->closeCursor(); + + +$db->setAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS, true); +var_dump($db->getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); + $stmt = $db->query($sql); var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(result => 0), actual: array() var_dump($stmt->nextRowset()); // expected: bool(false), actual: bool(true) var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); // expected: array(), actual: array(result => 0) +$stmt->closeCursor(); +var_dump($db->getAttribute(PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS)); + ?> --EXPECT-- bool(false) +array(0) { +} +bool(true) +array(1) { + [0]=> + array(1) { + ["result"]=> + string(1) "0" + } +} bool(true) array(1) { [0]=> @@ -38,3 +57,4 @@ array(1) { bool(false) array(0) { } +bool(true)