From b49cb5106f653e073a95f6ff42a8d3d3e8849291 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Thu, 27 May 2021 13:15:40 -0700 Subject: [PATCH] Simplified parse_param_array in sqlsrv (#1262) --- source/sqlsrv/stmt.cpp | 274 +++++++----------- .../pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt | 41 ++- .../pdo_sqlsrv/pdo_test_TVP_error_cases.phpt | 38 ++- .../sqlsrv/sqlsrv_test_TVP_double_tvps.phpt | 40 ++- .../sqlsrv/sqlsrv_test_TVP_error_cases.phpt | 39 ++- 5 files changed, 214 insertions(+), 218 deletions(-) diff --git a/source/sqlsrv/stmt.cpp b/source/sqlsrv/stmt.cpp index 594e4782..e51137b6 100644 --- a/source/sqlsrv/stmt.cpp +++ b/source/sqlsrv/stmt.cpp @@ -96,13 +96,14 @@ sqlsrv_phptype determine_sqlsrv_php_type( sqlsrv_stmt const* stmt, SQLINTEGER sq void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt ); bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type ); bool is_valid_sqlsrv_sqltype( _In_ sqlsrv_sqltype type ); -void parse_param_array( _Inout_ ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction, - _Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type, - _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits ); void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, _In_ int type ); void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type ); void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type ); bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding ); +zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_ht, zend_ulong index, + _Out_ SQLSMALLINT& direction, _Out_ SQLSRV_PHPTYPE& php_out_type, + _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type, + _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits); } @@ -1194,44 +1195,50 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt ) HashTable* params_ht = Z_ARRVAL_P( params_z ); - zend_ulong index = -1; - zend_string *key = NULL; - zval* param_z = NULL; + zend_ulong index = -1; + zend_string *key = NULL; + zval* param_z = NULL; - ZEND_HASH_FOREACH_KEY_VAL( params_ht, index, key, param_z ) { - zval* value_z = NULL; - SQLSMALLINT direction = SQL_PARAM_INPUT; - SQLSRV_ENCODING encoding = stmt->encoding(); - if( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) { - encoding = stmt->conn->encoding(); - } - SQLSMALLINT sql_type = SQL_UNKNOWN_TYPE; - SQLULEN column_size = SQLSRV_UNKNOWN_SIZE; - SQLSMALLINT decimal_digits = 0; - SQLSRV_PHPTYPE php_out_type = SQLSRV_PHPTYPE_INVALID; + ZEND_HASH_FOREACH_KEY_VAL( params_ht, index, key, param_z ) { + // make sure it's an integer index + int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; + CHECK_CUSTOM_ERROR(type != HASH_KEY_IS_LONG, stmt, SS_SQLSRV_ERROR_PARAM_INVALID_INDEX) { + throw ss::SSException(); + } - // make sure it's an integer index - int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - CHECK_CUSTOM_ERROR( type != HASH_KEY_IS_LONG, stmt, SS_SQLSRV_ERROR_PARAM_INVALID_INDEX ) { - throw ss::SSException(); - } + zval* value_z = NULL; + SQLSMALLINT direction = SQL_PARAM_INPUT; + SQLSRV_ENCODING encoding = stmt->encoding(); + if( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) { + encoding = stmt->conn->encoding(); + } + SQLSMALLINT sql_type = SQL_UNKNOWN_TYPE; + SQLULEN column_size = SQLSRV_UNKNOWN_SIZE; + SQLSMALLINT decimal_digits = 0; + SQLSRV_PHPTYPE php_out_type = SQLSRV_PHPTYPE_INVALID; // if it's a parameter array - if( Z_TYPE_P( param_z ) == IS_ARRAY ) { - - zval* var = NULL; - int zr = ( NULL != ( var = zend_hash_index_find( Z_ARRVAL_P( param_z ), 0 ))) ? SUCCESS : FAILURE; - CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 ) { - throw ss::SSException(); + if (Z_TYPE_P(param_z) == IS_ARRAY) { + try { + HashTable* param_ht = Z_ARRVAL_P(param_z); + // Check the number of elements in the array + int num_elems = zend_hash_num_elements(param_ht); + if (num_elems > 1) { + value_z = parse_param_array(stmt, param_ht, index, direction, php_out_type, encoding, sql_type, column_size, decimal_digits); + } else { + // Simply get the first variable and use the defaults + value_z = zend_hash_index_find(param_ht, 0); + if (value_z == NULL) { + THROW_SS_ERROR(stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1); + } + } + } catch (core::CoreException&) { + SQLFreeStmt(stmt->handle(), SQL_RESET_PARAMS); + throw; } - - // parse the parameter array that the user gave - parse_param_array( stmt, param_z, index, direction, php_out_type, encoding, sql_type, column_size, - decimal_digits ); - value_z = var; } else { - CHECK_CUSTOM_ERROR( !stmt->prepared && stmt->conn->ce_option.enabled, stmt, SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED ) { + CHECK_CUSTOM_ERROR(!stmt->prepared && stmt->conn->ce_option.enabled, stmt, SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED) { throw ss::SSException(); } value_z = param_z; @@ -1252,7 +1259,6 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt ) } // bind the parameter - SQLSRV_ASSERT( value_z != NULL, "bind_params: value_z is null." ); core_sqlsrv_bind_param( stmt, static_cast( index ), direction, value_z, php_out_type, encoding, sql_type, column_size, decimal_digits ); @@ -1724,6 +1730,7 @@ sqlsrv_phptype determine_sqlsrv_php_type( _In_ ss_sqlsrv_stmt const* stmt, _In_ break; default: sqlsrv_phptype.typeinfo.type = PHPTYPE_INVALID; + SQLSRV_ASSERT(false, "An invalid php type was returned with (supposedly) validated sql type and column_size"); break; } @@ -1905,179 +1912,106 @@ void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_typ } -void parse_param_array( _Inout_ ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction, +zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_ht, zend_ulong index, _Out_ SQLSMALLINT& direction, _Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type, - _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits ) + _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits) { - zval* var_or_val = NULL; - zval* temp = NULL; - HashTable* param_ht = Z_ARRVAL_P( param_array ); - sqlsrv_sqltype sqlsrv_sql_type; - HashPosition pos; + zval* var_or_val = zend_hash_index_find(param_ht, 0); + bool php_type_param_is_null = true; + bool sql_type_param_is_null = true; - try { + // Assumption: there are more than only the variable, parse the rest of the array + zval* dir = zend_hash_index_find(param_ht, 1); + if (Z_TYPE_P(dir) != IS_NULL) { + // if param direction is specified, make sure it's valid + CHECK_CUSTOM_ERROR(Z_TYPE_P(dir) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1) { + throw ss::SSException(); + } + direction = static_cast(Z_LVAL_P(dir)); - bool php_type_param_was_null = true; - bool sql_type_param_was_null = true; - - php_out_type = SQLSRV_PHPTYPE_INVALID; - encoding = SQLSRV_ENCODING_INVALID; - - // handle the array parameters that contain the value/var, direction, php_type, sql_type - zend_hash_internal_pointer_reset_ex( param_ht, &pos ); - if( zend_hash_has_more_elements_ex( param_ht, &pos ) == FAILURE || - (var_or_val = zend_hash_get_current_data_ex(param_ht, &pos)) == NULL) { - - THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 ); + CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && direction != SQL_PARAM_OUTPUT && direction != SQL_PARAM_INPUT_OUTPUT, + stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1) { + throw ss::SSException(); + } + CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && !Z_ISREF_P(var_or_val), stmt, SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF, index + 1) { + throw ss::SSException(); + } } - // if the direction is included, then use what they gave, otherwise INPUT is assumed - if ( zend_hash_move_forward_ex( param_ht, &pos ) == SUCCESS && ( temp = zend_hash_get_current_data_ex( param_ht, &pos )) != NULL && - Z_TYPE_P( temp ) != IS_NULL ) { + // Check if the user provides php type or sql type or both + zval* phptype_z = zend_hash_index_find(param_ht, 2); + zval* sqltype_z = zend_hash_index_find(param_ht, 3); - CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1 ) { + php_type_param_is_null = (phptype_z == NULL || Z_TYPE_P(phptype_z) == IS_NULL); + sql_type_param_is_null = (sqltype_z == NULL || Z_TYPE_P(sqltype_z) == IS_NULL); - throw ss::SSException(); + if (php_type_param_is_null) { + // so set default for php type based on the variable + if (Z_ISREF_P(var_or_val)) { + php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(Z_REFVAL_P(var_or_val))]; + } else { + php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(var_or_val)]; } - direction = static_cast( Z_LVAL_P( temp )); - CHECK_CUSTOM_ERROR( direction != SQL_PARAM_INPUT && direction != SQL_PARAM_OUTPUT && direction != SQL_PARAM_INPUT_OUTPUT, - stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1 ) { + } else { + CHECK_CUSTOM_ERROR(Z_TYPE_P(phptype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1) { throw ss::SSException(); } - CHECK_CUSTOM_ERROR( !Z_ISREF_P( var_or_val ) && ( direction == SQL_PARAM_OUTPUT || direction == SQL_PARAM_INPUT_OUTPUT ), stmt, SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF, index + 1 ) { + sqlsrv_phptype srv_phptype; + srv_phptype.value = Z_LVAL_P(phptype_z); + CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_phptype(srv_phptype), stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1) { throw ss::SSException(); } - } - else { - direction = SQL_PARAM_INPUT; - } - - // extract the php type and encoding from the 3rd parameter - if ( zend_hash_move_forward_ex( param_ht, &pos ) == SUCCESS && ( temp = zend_hash_get_current_data_ex( param_ht, &pos )) != NULL && - Z_TYPE_P( temp ) != IS_NULL ) { - - php_type_param_was_null = false; - sqlsrv_phptype sqlsrv_phptype; - - CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1 ) { - - throw ss::SSException(); - } - - sqlsrv_phptype.value = Z_LVAL_P( temp ); - - CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_phptype ), stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, - index + 1 ) { - - throw ss::SSException(); - } - - php_out_type = static_cast( sqlsrv_phptype.typeinfo.type ); - encoding = ( SQLSRV_ENCODING ) sqlsrv_phptype.typeinfo.encoding; + php_out_type = static_cast(srv_phptype.typeinfo.type); + encoding = (SQLSRV_ENCODING)srv_phptype.typeinfo.encoding; // if the call has a SQLSRV_PHPTYPE_STRING/STREAM('default'), then the stream is in the encoding established // by the connection - if( encoding == SQLSRV_ENCODING_DEFAULT ) { - encoding = stmt->conn->encoding(); - } - } - // set default for php type and encoding if not supplied - else { - - php_type_param_was_null = true; - - if ( Z_ISREF_P( var_or_val )){ - php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P( Z_REFVAL_P( var_or_val ))]; - } - else{ - php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P( var_or_val )]; - } - encoding = stmt->encoding(); - if( encoding == SQLSRV_ENCODING_DEFAULT ) { + if (encoding == SQLSRV_ENCODING_DEFAULT) { encoding = stmt->conn->encoding(); } } - // get the server type, column size/precision and the decimal digits if provided - if ( zend_hash_move_forward_ex( param_ht, &pos ) == SUCCESS && ( temp = zend_hash_get_current_data_ex( param_ht, &pos )) != NULL && - Z_TYPE_P( temp ) != IS_NULL ) { - - sql_type_param_was_null = false; - - CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1 ) { - + if (sql_type_param_is_null) { + // the sql type is not specified, which is required for always encrypted for non-prepared statements + CHECK_CUSTOM_ERROR(stmt->conn->ce_option.enabled && !stmt->prepared, stmt, SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED) { + throw ss::SSException(); + } + } else { + CHECK_CUSTOM_ERROR(Z_TYPE_P(sqltype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1) { throw ss::SSException(); } - - sqlsrv_sql_type.value = Z_LVAL_P( temp ); // since the user supplied this type, make sure it's valid - CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_sqltype( sqlsrv_sql_type ), stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, - index + 1 ) { - + sqlsrv_sqltype sqlsrv_sql_type; + sqlsrv_sql_type.value = Z_LVAL_P(sqltype_z); + CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_sqltype(sqlsrv_sql_type), stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1) { throw ss::SSException(); } - bool size_okay = determine_column_size_or_precision( stmt, sqlsrv_sql_type, &column_size, &decimal_digits ); - - CHECK_CUSTOM_ERROR( !size_okay, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, index + 1 ) { - + bool size_okay = determine_column_size_or_precision(stmt, sqlsrv_sql_type, &column_size, &decimal_digits); + CHECK_CUSTOM_ERROR(!size_okay, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, index + 1) { throw ss::SSException(); } sql_type = sqlsrv_sql_type.typeinfo.type; - } - // else the sql type and size are unknown, so tell the core layer to use its defaults - else { - CHECK_CUSTOM_ERROR( !stmt->prepared && stmt->conn->ce_option.enabled, stmt, SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED ) { - throw ss::SSException(); + + if (direction != SQL_PARAM_INPUT && php_type_param_is_null) { + sqlsrv_phptype srv_phptype; + srv_phptype = determine_sqlsrv_php_type(stmt, sql_type, (SQLUINTEGER)column_size, true); + + php_out_type = static_cast(srv_phptype.typeinfo.type); + encoding = static_cast(srv_phptype.typeinfo.encoding); } - sql_type_param_was_null = true; - - sql_type = SQL_UNKNOWN_TYPE; - column_size = SQLSRV_UNKNOWN_SIZE; - decimal_digits = 0; } - // if the user for some reason provides an inout / output parameter with a null phptype and a specified - // sql server type, infer the php type from the sql server type. - if( direction != SQL_PARAM_INPUT && php_type_param_was_null && !sql_type_param_was_null ) { - - sqlsrv_phptype sqlsrv_phptype; - - sqlsrv_phptype = determine_sqlsrv_php_type( stmt, sql_type, (SQLUINTEGER)column_size, true ); - - // we DIE here since everything should have been validated already and to return the user an error - // for our own logic error would be confusing/misleading. - SQLSRV_ASSERT( sqlsrv_phptype.typeinfo.type != PHPTYPE_INVALID, "An invalid php type was returned with (supposed) " - "validated sql type and column_size" ); - - php_out_type = static_cast( sqlsrv_phptype.typeinfo.type ); - encoding = static_cast( sqlsrv_phptype.typeinfo.encoding ); - } - - // verify that the parameter is a valid output param type - if( direction == SQL_PARAM_OUTPUT ) { - - switch( php_out_type ) { - case SQLSRV_PHPTYPE_NULL: - case SQLSRV_PHPTYPE_DATETIME: - case SQLSRV_PHPTYPE_STREAM: - THROW_CORE_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE ); - break; - default: - break; + if (direction == SQL_PARAM_OUTPUT) { + if (php_out_type == SQLSRV_PHPTYPE_NULL || php_out_type == SQLSRV_PHPTYPE_DATETIME || php_out_type == SQLSRV_PHPTYPE_STREAM) { + THROW_CORE_ERROR(stmt, SS_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE); } - } - } - catch( core::CoreException& ) { - - SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS ); - throw; - } + return var_or_val; } bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type ) diff --git a/test/functional/pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt b/test/functional/pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt index df9773f7..b08fa1b8 100644 --- a/test/functional/pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt +++ b/test/functional/pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt @@ -9,28 +9,45 @@ PHPT_EXEC=true require_once('MsSetup.inc'); require_once('MsCommon_mid-refactor.inc'); -function cleanup($conn, $schema) +function cleanup($conn, $schema, $pre2016) { - global $dropSchema; - - $dropProcedure = dropProcSQL($conn, "[$schema].[AddReview]"); - $conn->exec($dropProcedure); + if ($pre2016) { + // ignore the errors dropping all these + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $conn->exec("DROP PROCEDURE [$schema].[AddReview]"); + $conn->exec("DROP TYPE [$schema].[TestTVP3]"); + $conn->exec("DROP TYPE [$schema].[SupplierType]"); + $conn->exec("DROP SCHEMA [$schema]"); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } else { + global $dropSchema; + + $dropProcedure = dropProcSQL($conn, "[$schema].[AddReview]"); + $conn->exec($dropProcedure); - $dropTableType = dropTableTypeSQL($conn, "TestTVP3", $schema); - $conn->exec($dropTableType); - $dropTableType = dropTableTypeSQL($conn, "SupplierType", $schema); - $conn->exec($dropTableType); + $dropTableType = dropTableTypeSQL($conn, "TestTVP3", $schema); + $conn->exec($dropTableType); + $dropTableType = dropTableTypeSQL($conn, "SupplierType", $schema); + $conn->exec($dropTableType); - $conn->exec($dropSchema); + $conn->exec($dropSchema); + } } try { $conn = new PDO("sqlsrv:server = $server; database=$databaseName;", $uid, $pwd); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $stmt = $conn->query("SELECT @@VERSION"); + $result = $stmt->fetch(PDO::FETCH_NUM)[0]; + $version = explode(' ', $result); + $pre2016 = ($version[3] < '2016'); + // Use a different schema instead of dbo $schema = 'Sales DB'; - cleanup($conn, $schema); + cleanup($conn, $schema, $pre2016); // Create the table type and stored procedure $conn->exec($createSchema); @@ -85,7 +102,7 @@ try { fclose($image); unset($stmt); - cleanup($conn, $schema); + cleanup($conn, $schema, $pre2016); unset($conn); echo "Done" . PHP_EOL; diff --git a/test/functional/pdo_sqlsrv/pdo_test_TVP_error_cases.phpt b/test/functional/pdo_sqlsrv/pdo_test_TVP_error_cases.phpt index cc2ac9b7..bb27cb2e 100644 --- a/test/functional/pdo_sqlsrv/pdo_test_TVP_error_cases.phpt +++ b/test/functional/pdo_sqlsrv/pdo_test_TVP_error_cases.phpt @@ -28,29 +28,45 @@ function invokeProc($conn, $proc, $tvpInput, $caseNo, $inputParam = true) } } -function cleanup($conn, $schema, $tvpType, $procName) +function cleanup($conn, $schema, $tvpType, $procName, $pre2016) { - global $dropSchema; - - $dropProcedure = dropProcSQL($conn, "[$schema].[$procName]"); - $conn->exec($dropProcedure); + if ($pre2016) { + // ignore the errors dropping all these + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + + $conn->exec("DROP PROCEDURE [$schema].[$procName]"); + $conn->exec("DROP TYPE [$schema].[$tvpType]"); + $conn->exec("DROP SCHEMA [$schema]"); + + $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } else { + global $dropSchema; + + $dropProcedure = dropProcSQL($conn, "[$schema].[$procName]"); + $conn->exec($dropProcedure); - $dropTableType = dropTableTypeSQL($conn, $tvpType, $schema); - $conn->exec($dropTableType); - - $conn->exec($dropSchema); + $dropTableType = dropTableTypeSQL($conn, $tvpType, $schema); + $conn->exec($dropTableType); + + $conn->exec($dropSchema); + } } try { $conn = new PDO("sqlsrv:server = $server; database=$databaseName;", $uid, $pwd); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $stmt = $conn->query("SELECT @@VERSION"); + $result = $stmt->fetch(PDO::FETCH_NUM)[0]; + $version = explode(' ', $result); + $pre2016 = ($version[3] < '2016'); + // Use a different schema instead of dbo $schema = 'Sales DB'; $tvpType = 'TestTVP3'; $procName = 'SelectTVP3'; - cleanup($conn, $schema, $tvpType, $procName); + cleanup($conn, $schema, $tvpType, $procName, $pre2016); // Create the table type and stored procedure $conn->exec($createSchema); @@ -162,7 +178,7 @@ try { $tvpInput = array($tvpTypeName => $inputs); invokeProc($conn, $callSelectTVP3, $tvpInput, 14); - cleanup($conn, $schema, $tvpType, $procName); + cleanup($conn, $schema, $tvpType, $procName, $pre2016); unset($conn); echo "Done" . PHP_EOL; diff --git a/test/functional/sqlsrv/sqlsrv_test_TVP_double_tvps.phpt b/test/functional/sqlsrv/sqlsrv_test_TVP_double_tvps.phpt index c3d40b65..67602c93 100644 --- a/test/functional/sqlsrv/sqlsrv_test_TVP_double_tvps.phpt +++ b/test/functional/sqlsrv/sqlsrv_test_TVP_double_tvps.phpt @@ -10,28 +10,42 @@ require_once('MsCommon.inc'); date_default_timezone_set('America/Los_Angeles'); -function cleanup($conn, $schema) +function cleanup($conn, $schema, $pre2016) { - global $dropSchema; - - $dropProcedure = dropProcSQL($conn, "[$schema].[AddReview]"); - sqlsrv_query($conn, $dropProcedure); + if ($pre2016) { + sqlsrv_query($conn, "DROP PROCEDURE [$schema].[AddReview]"); + sqlsrv_query($conn, "DROP TYPE [$schema].[TestTVP3]"); + sqlsrv_query($conn, "DROP TYPE [$schema].[SupplierType]"); + sqlsrv_query($conn, "DROP SCHEMA [$schema]"); + } else { + global $dropSchema; + + $dropProcedure = dropProcSQL($conn, "[$schema].[AddReview]"); + sqlsrv_query($conn, $dropProcedure); - $dropTableType = dropTableTypeSQL($conn, "TestTVP3", $schema); - sqlsrv_query($conn, $dropTableType); - $dropTableType = dropTableTypeSQL($conn, "SupplierType", $schema); - sqlsrv_query($conn, $dropTableType); - - sqlsrv_query($conn, $dropSchema); + $dropTableType = dropTableTypeSQL($conn, "TestTVP3", $schema); + sqlsrv_query($conn, $dropTableType); + $dropTableType = dropTableTypeSQL($conn, "SupplierType", $schema); + sqlsrv_query($conn, $dropTableType); + + sqlsrv_query($conn, $dropSchema); + } } sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL); $conn = connect(array('CharacterSet'=>'UTF-8', 'ReturnDatesAsStrings' => true)); +$stmt = sqlsrv_query($conn, "SELECT @@VERSION"); +if (sqlsrv_fetch($stmt)) { + $result = sqlsrv_get_field($stmt, 0); +} +$version = explode(' ', $result); +$pre2016 = ($version[3] < '2016'); + // Use a different schema instead of dbo $schema = 'Sales DB'; -cleanup($conn, $schema); +cleanup($conn, $schema, $pre2016); // Create table types and stored procedures sqlsrv_query($conn, $createSchema); @@ -89,7 +103,7 @@ if (sqlsrv_fetch($stmt)) { } fclose($image); -cleanup($conn, $schema); +cleanup($conn, $schema, $pre2016); sqlsrv_free_stmt($stmt); sqlsrv_close($conn); diff --git a/test/functional/sqlsrv/sqlsrv_test_TVP_error_cases.phpt b/test/functional/sqlsrv/sqlsrv_test_TVP_error_cases.phpt index 57ad3990..1fb1df68 100644 --- a/test/functional/sqlsrv/sqlsrv_test_TVP_error_cases.phpt +++ b/test/functional/sqlsrv/sqlsrv_test_TVP_error_cases.phpt @@ -18,10 +18,12 @@ function invokeProc($conn, $proc, $tvpInput, $caseNo, $dir = SQLSRV_PARAM_IN) $stmt = sqlsrv_query($conn, $proc, $params); if (!$stmt) { - $errors = sqlsrv_errors(SQLSRV_ERR_ALL); + // $errors = sqlsrv_errors(SQLSRV_ERR_ALL); + $errors = sqlsrv_errors(); if (!empty($errors)) { $count = count($errors); } + $count = 1; for ($i = 0; $i < $count; $i++) { echo "Error $caseNo: "; echo $errors[$i]['message'] . PHP_EOL; @@ -29,17 +31,23 @@ function invokeProc($conn, $proc, $tvpInput, $caseNo, $dir = SQLSRV_PARAM_IN) } } -function cleanup($conn, $schema, $tvpType, $procName) +function cleanup($conn, $schema, $tvpType, $procName, $pre2016) { - global $dropSchema; - - $dropProcedure = dropProcSQL($conn, "[$schema].[$procName]"); - sqlsrv_query($conn, $dropProcedure); + if ($pre2016) { + sqlsrv_query($conn, "DROP PROCEDURE [$schema].[$procName]"); + sqlsrv_query($conn, "DROP TYPE [$schema].[$tvpType]"); + sqlsrv_query($conn, "DROP SCHEMA [$schema]"); + } else { + global $dropSchema; + + $dropProcedure = dropProcSQL($conn, "[$schema].[$procName]"); + sqlsrv_query($conn, $dropProcedure); - $dropTableType = dropTableTypeSQL($conn, $tvpType, $schema); - sqlsrv_query($conn, $dropTableType); - - sqlsrv_query($conn, $dropSchema); + $dropTableType = dropTableTypeSQL($conn, $tvpType, $schema); + sqlsrv_query($conn, $dropTableType); + + sqlsrv_query($conn, $dropSchema); + } } sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL); @@ -51,7 +59,14 @@ $schema = 'Sales DB'; $tvpType = 'TestTVP3'; $procName = 'SelectTVP3'; -cleanup($conn, $schema, $tvpType, $procName); +$stmt = sqlsrv_query($conn, "SELECT @@VERSION"); +if (sqlsrv_fetch($stmt)) { + $result = sqlsrv_get_field($stmt, 0); +} +$version = explode(' ', $result); +$pre2016 = ($version[3] < '2016'); + +cleanup($conn, $schema, $tvpType, $procName, $pre2016); // Create table type and a stored procedure sqlsrv_query($conn, $createSchema); @@ -163,7 +178,7 @@ $inputs = [ $tvpInput = array($tvpTypeName => $inputs); invokeProc($conn, $callSelectTVP3, $tvpInput, 14); -cleanup($conn, $schema, $tvpType, $procName); +cleanup($conn, $schema, $tvpType, $procName, $pre2016); sqlsrv_close($conn);