Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 937 - fixed ASSERT and added new tests #940

Merged
merged 3 commits into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<field_meta_data> core_meta_data;

sqlsrv_stmt* driver_stmt = static_cast<sqlsrv_stmt*>( 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<field_meta_data> 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 );

Expand Down
2 changes: 1 addition & 1 deletion source/sqlsrv/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
122 changes: 122 additions & 0 deletions test/functional/pdo_sqlsrv/pdo_937_metadata.phpt
Original file line number Diff line number Diff line change
@@ -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--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsSetup.inc");
require_once("MsCommon_mid-refactor.inc");

$tableName = 'pdoTestTable_938';
$procName = 'pdoTestProc_938';

function checkMetaData($stmt)
{
$metadata = $stmt->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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only place that getColumnMeta is called? In the sqlsrv test sqlsrv_field_metadata is called several times for both non-empty and empty result sets. Is it preferable to call columnCount first in PDO?

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
3 changes: 1 addition & 2 deletions test/functional/pdo_sqlsrv/pdostatement_getColumnMeta.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
125 changes: 125 additions & 0 deletions test/functional/sqlsrv/srv_937_metadata.phpt
Original file line number Diff line number Diff line change
@@ -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--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require_once('MsCommon.inc');

$conn = connect();
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}

$tableName = 'srvTestTable_938';
$procName = 'srvTestProc_938';

dropTable($conn, $tableName);
dropProc($conn, $procName);

// Create the test table
$tsql = "CREATE TABLE $tableName([id] [int] NOT NULL,
[dummyColumn] [varchar](10) NOT NULL
)";
$stmt = sqlsrv_query($conn, $tsql);
if (!$stmt) {
fatalError("Failed to create table $tableName\n");
}

$id = 5;
$tsql = "INSERT INTO $tableName VALUES ($id, 'dummy')";
$stmt = sqlsrv_query($conn, $tsql);
if (!$stmt) {
fatalError("Failed to insert a row into table $tableName\n");
}

$tsql = "SELECT * FROM $tableName";
$stmt = sqlsrv_query($conn, $tsql);
$fieldmeta = sqlsrv_field_metadata($stmt);
var_dump($fieldmeta);

$tsql = "UPDATE $tableName SET dummyColumn = 'updated' WHERE id = $id";
$stmt = sqlsrv_prepare($conn, $tsql);
sqlsrv_execute($stmt);
$fieldmeta = sqlsrv_field_metadata($stmt);
var_dump($fieldmeta);

createProc($conn, $procName, "@id int, @val varchar(10) OUTPUT", "SELECT @val = dummyColumn FROM $tableName WHERE id = @id");

$value = '';
$tsql = "{CALL [$procName] (?, ?)}";
$stmt = sqlsrv_prepare(
$conn,
$tsql,
array(array($id, SQLSRV_PARAM_IN),
array(&$value, SQLSRV_PARAM_OUT)
)
);
$result = sqlsrv_execute($stmt);
if (!$result) {
fatalError("Failed to invoke stored procedure $procName\n");
}

echo "The value returned: $value\n";

$fieldmeta = sqlsrv_field_metadata($stmt);
var_dump($fieldmeta);

$options = array("Scrollable" => "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) {
}