diff --git a/source/pdo_sqlsrv/pdo_stmt.cpp b/source/pdo_sqlsrv/pdo_stmt.cpp index cd8c697f1..507fc94ca 100644 --- a/source/pdo_sqlsrv/pdo_stmt.cpp +++ b/source/pdo_sqlsrv/pdo_stmt.cpp @@ -1029,20 +1029,28 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno try { SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: pdo_stmt object was null" ); - SQLSRV_ASSERT( stmt->columns != NULL, "pdo_sqlsrv_stmt_get_col_meta: columns are not available." ); SQLSRV_ASSERT( Z_TYPE_P( return_value ) == IS_NULL, "Metadata already has value. Must be NULL." ); - sqlsrv_malloc_auto_ptr core_meta_data; - sqlsrv_stmt* driver_stmt = static_cast( stmt->driver_data ); SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: stmt->driver_data was null"); - SQLSRV_ASSERT( colno >= 0 && colno < stmt->column_count, "pdo_sqlsrv_stmt_get_col_meta: invalid column number." ); + // Based on PDOStatement::getColumnMeta API, this should return FALSE + // if the requested column does not exist in the result set, or if + // no result set exists. Thus, do not use SQLSRV_ASSERT, which causes + // the script to fail right away. Instead, log this warning if logging + // is enabled + if (colno < 0 || colno >= stmt->column_count || stmt->columns == NULL) { + LOG( SEV_WARNING, "Invalid column number %1!d!", colno ); + return FAILURE; + } - core_meta_data = core_sqlsrv_field_metadata( driver_stmt, (SQLSMALLINT) colno TSRMLS_CC ); // initialize the array to nothing, as PDO requires us to create it core::sqlsrv_array_init( *driver_stmt, return_value TSRMLS_CC ); + sqlsrv_malloc_auto_ptr core_meta_data; + + core_meta_data = core_sqlsrv_field_metadata( driver_stmt, (SQLSMALLINT) colno TSRMLS_CC ); + // add the following fields: flags, native_type, driver:decl_type, table add_assoc_long( return_value, "flags", 0 ); diff --git a/source/sqlsrv/stmt.cpp b/source/sqlsrv/stmt.cpp index c366e0305..9d60de25f 100644 --- a/source/sqlsrv/stmt.cpp +++ b/source/sqlsrv/stmt.cpp @@ -1807,7 +1807,7 @@ SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt * stmt) throw; } - SQLSRV_ASSERT(num_cols > 0 && stmt->current_meta_data.size() == num_cols, "Meta data vector out of sync" ); + SQLSRV_ASSERT(stmt->current_meta_data.size() == num_cols, "Meta data vector out of sync" ); return num_cols; } diff --git a/test/functional/pdo_sqlsrv/pdo_937_metadata.phpt b/test/functional/pdo_sqlsrv/pdo_937_metadata.phpt new file mode 100644 index 000000000..d0cad7e75 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_937_metadata.phpt @@ -0,0 +1,122 @@ +--TEST-- +GitHub issue 937 - getting metadata will not fail after an UPDATE / DELETE statement +--DESCRIPTION-- +Verifies that getColumnMeta will not fail after processing an UPDATE / DELETE query that returns no fields. Instead, it should simply return FALSE because no result set exists. +--ENV-- +PHPT_EXEC=true +--SKIPIF-- + +--FILE-- +getColumnMeta(0); + if ($metadata !== FALSE) { + echo "Expects FALSE because no result set exists!\n"; + } +} + +try { + $conn = connect(); + + dropTable($conn, $tableName); + dropProc($conn, $procName); + + $tsql = "CREATE TABLE $tableName([id] [int] NOT NULL, [name] [varchar](10) NOT NULL)"; + $conn->query($tsql); + + $id = 3; + $tsql = "INSERT INTO $tableName VALUES ($id, 'abcde')"; + $conn->query($tsql); + + $tsql = "UPDATE $tableName SET name = 'updated' WHERE id = $id"; + $stmt = $conn->prepare($tsql); + $stmt->execute(); + $numCol = $metadata = $stmt->columnCount(); + echo "Number of columns after UPDATE: $numCol\n"; + checkMetaData($stmt); + + $tsql = "SELECT * FROM $tableName"; + $stmt = $conn->query($tsql); + $numCol = $metadata = $stmt->columnCount(); + for ($i = 0; $i < $numCol; $i++) { + $metadata = $stmt->getColumnMeta($i); + var_dump($metadata); + } + + createProc($conn, $procName, "@id int, @val varchar(10) OUTPUT", "SELECT @val = name FROM $tableName WHERE id = @id"); + + $value = ''; + $tsql = "{CALL [$procName] (?, ?)}"; + $stmt = $conn->prepare($tsql); + $stmt->bindParam(1, $id, PDO::PARAM_INT); + $stmt->bindParam(2, $value, PDO::PARAM_STR, 10); + $stmt->execute(); + $numCol = $metadata = $stmt->columnCount(); + echo "Number of columns after PROCEDURE: $numCol\n"; + echo "Value returned: $value\n"; + checkMetaData($stmt); + + $query = "DELETE FROM $tableName WHERE name = 'updated'"; + $stmt = $conn->query($query); + $numCol = $metadata = $stmt->columnCount(); + echo "Number of columns after DELETE: $numCol\n"; + checkMetaData($stmt); +} catch (PDOException $e) { + echo $e->getMessage() . PHP_EOL; +} + +dropTable($conn, $tableName); +dropProc($conn, $procName); + +unset($stmt); +unset($conn); + +?> +--EXPECT-- +Number of columns after UPDATE: 0 +array(8) { + ["flags"]=> + int(0) + ["sqlsrv:decl_type"]=> + string(3) "int" + ["native_type"]=> + string(6) "string" + ["table"]=> + string(0) "" + ["pdo_type"]=> + int(2) + ["name"]=> + string(2) "id" + ["len"]=> + int(10) + ["precision"]=> + int(0) +} +array(8) { + ["flags"]=> + int(0) + ["sqlsrv:decl_type"]=> + string(7) "varchar" + ["native_type"]=> + string(6) "string" + ["table"]=> + string(0) "" + ["pdo_type"]=> + int(2) + ["name"]=> + string(4) "name" + ["len"]=> + int(10) + ["precision"]=> + int(0) +} +Number of columns after PROCEDURE: 0 +Value returned: updated +Number of columns after DELETE: 0 diff --git a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt index f42d1d3d3..40805dc23 100644 --- a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt +++ b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt @@ -220,5 +220,4 @@ array(7) { Warning: PDOStatement::getColumnMeta(): SQLSTATE[42P10]: Invalid column reference: column number must be non-negative in %s on line %x bool(false) - -Fatal error: pdo_sqlsrv_stmt_get_col_meta: invalid column number. in %s on line %x +bool(false) diff --git a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt index bb01be10a..e7075ee46 100644 --- a/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt +++ b/test/functional/pdo_sqlsrv/pdostatement_getColumnMeta_unicode_col_name.phpt @@ -259,5 +259,4 @@ array(7) { Warning: PDOStatement::getColumnMeta(): SQLSTATE[42P10]: Invalid column reference: column number must be non-negative in %s on line %x bool(false) - -Fatal error: pdo_sqlsrv_stmt_get_col_meta: invalid column number. in %s on line %x +bool(false) \ No newline at end of file diff --git a/test/functional/sqlsrv/srv_937_metadata.phpt b/test/functional/sqlsrv/srv_937_metadata.phpt new file mode 100644 index 000000000..47c9452fe --- /dev/null +++ b/test/functional/sqlsrv/srv_937_metadata.phpt @@ -0,0 +1,125 @@ +--TEST-- +GitHub issue #937 - getting metadata will not fail after an UPDATE / DELETE statement +--DESCRIPTION-- +Verifies that sqlsrv_field_metadata will return an empty array after processing an +UPDATE / DELETE query that returns no fields. +--ENV-- +PHPT_EXEC=true +--SKIPIF-- + +--FILE-- + "buffered"); +$tsql = "DELETE FROM $tableName WHERE dummyColumn = 'updated'"; +$stmt = sqlsrv_query($conn, $tsql, array(), $options); +$fieldmeta = sqlsrv_field_metadata($stmt); +var_dump($fieldmeta); + +dropTable($conn, $tableName); +dropProc($conn, $procName); + +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +?> +--EXPECT-- +array(2) { + [0]=> + array(6) { + ["Name"]=> + string(2) "id" + ["Type"]=> + int(4) + ["Size"]=> + NULL + ["Precision"]=> + int(10) + ["Scale"]=> + NULL + ["Nullable"]=> + int(0) + } + [1]=> + array(6) { + ["Name"]=> + string(11) "dummyColumn" + ["Type"]=> + int(12) + ["Size"]=> + int(10) + ["Precision"]=> + NULL + ["Scale"]=> + NULL + ["Nullable"]=> + int(0) + } +} +array(0) { +} +The value returned: updated +array(0) { +} +array(0) { +} \ No newline at end of file