Skip to content

Commit

Permalink
5.6.1 hotfix (#959)
Browse files Browse the repository at this point in the history
  • Loading branch information
yitam authored Mar 19, 2019
1 parent cdc107b commit 3ba2693
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 15 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)

## 5.6.1 - 2019-03-19
Updated PECL release packages. Here is the list of updates:

### Fixed
- Issue [#937](https://github.com/Microsoft/msphpsql/issues/937) - fixed assumptions made when calculating field or column metadata which may have resulted in application termination
- Issue [#955](https://github.com/Microsoft/msphpsql/issues/955) - modified sqlsrv config file such that it can be compiled independently of pdo_sqlsrv
- Pull Request [#946](https://github.com/Microsoft/msphpsql/pull/946) - fixed PDOStatement::getColumnMeta() to return false when something goes wrong

### Limitations
- No support for inout / output params when using sql_variant type
- No support for inout / output params when formatting decimal values
- In Linux and macOS, setlocale() only takes effect if it is invoked before the first connection. Attempting to set the locale after connecting will not work
- Always Encrypted requires [MS ODBC Driver 17+](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server)
- Only Windows Certificate Store and Azure Key Vault are supported. Custom Keystores are not yet supported
- Issue [#716](https://github.com/Microsoft/msphpsql/issues/716) - With Always Encrypted enabled, named parameters in subqueries are not supported
- [Always Encrypted limitations](https://docs.microsoft.com/sql/connect/php/using-always-encrypted-php-drivers#limitations-of-the-php-drivers-when-using-always-encrypted)

### Known Issues
- Connection pooling on Linux or macOS is not recommended with [unixODBC](http://www.unixodbc.org/) < 2.3.7
- When pooling is enabled in Linux or macOS
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostic information, such as error messages, warnings and informative messages
- due to this unixODBC bug, fetch large data (such as xml, binary) as streams as a workaround. See the examples [here](https://github.com/Microsoft/msphpsql/wiki/Features#pooling)
- With ColumnEncryption enabled, calling stored procedures with XML parameters does not work (Issue [#674](https://github.com/Microsoft/msphpsql/issues/674))
- In SUSE 15, Azure Active Directory connections may fail if PHP is installed from packages (Issue [#934](https://github.com/Microsoft/msphpsql/issues/934))

## 5.6.0 - 2019-02-15
Updated PECL release packages. Here is the list of updates:

Expand Down
24 changes: 16 additions & 8 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
// colno - The index of the field for which to return the metadata.
// return_value - zval* consisting of the metadata.
// Return:
// 0 for failure, 1 for success.
// FAILURE for failure, SUCCESS for success.
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value TSRMLS_DC)
{
PDO_RESET_STMT_ERROR;
Expand All @@ -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 Expand Up @@ -1088,14 +1096,14 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
}
catch( core::CoreException& ) {

return 0;
return FAILURE;
}
catch(...) {

DIE( "pdo_sqlsrv_stmt_get_col_meta: Unknown exception occurred while retrieving metadata." );
}

return 1;
return SUCCESS;
}


Expand Down
2 changes: 1 addition & 1 deletion source/shared/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
// Increase Patch for backward compatible fixes.
#define SQLVERSION_MAJOR 5
#define SQLVERSION_MINOR 6
#define SQLVERSION_PATCH 0
#define SQLVERSION_PATCH 1
#define SQLVERSION_BUILD 0

// For previews, set this constant to 1. Otherwise, set it to 0
Expand Down
2 changes: 1 addition & 1 deletion source/sqlsrv/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if( PHP_SQLSRV != "no" ) {
if (CHECK_LIB("odbc32.lib", "sqlsrv") && CHECK_LIB("odbccp32.lib", "sqlsrv") &&
CHECK_LIB("version.lib", "sqlsrv") && CHECK_LIB("psapi.lib", "sqlsrv")&&
CHECK_HEADER_ADD_INCLUDE( "core_sqlsrv.h", "CFLAGS_SQLSRV", configure_module_dirname + "\\shared")) {
if (PHP_PDO_SQLSRV == "no" || PHP_SQLSRV_SHARED) {
if (PHP_SQLSRV_SHARED || PHP_PDO_SQLSRV == "no") {
ADD_SOURCES( configure_module_dirname + "\\shared", shared_src_class, "sqlsrv" );
}
CHECK_HEADER_ADD_INCLUDE("sql.h", "CFLAGS_SQLSRV_ODBC");
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);
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) {
}

0 comments on commit 3ba2693

Please sign in to comment.