commit
36250ea551
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -3,6 +3,39 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
|
||||
## 5.10.0-beta2 - 2021-12-02
|
||||
Updated PECL release packages. Here is the list of updates:
|
||||
|
||||
### Added
|
||||
- Support for PHP 8.1
|
||||
- Support for Ubuntu 21.10
|
||||
- Feature Request [#1320](https://github.com/microsoft/msphpsql/issues/1320) - allow PDO::ATTR_EMULATE_PREPARES to be set at the connection level
|
||||
|
||||
### Fixed
|
||||
- Issue [#1307](https://github.com/microsoft/msphpsql/issues/1307) - added TVP support to non-procedure statements
|
||||
- Issue [#1310](https://github.com/microsoft/msphpsql/issues/1310) - adjusted sql_data_type and column size for NULL parameters - pull request [#1311](https://github.com/microsoft/msphpsql/pull/1311) by gjcarrette
|
||||
- Pull request [#1326](https://github.com/microsoft/msphpsql/pull/1326) - php drivers simply pass Azure AD Authentication to ODBC driver, which will verify the settings
|
||||
- Issue [#1329](https://github.com/microsoft/msphpsql/issues/1329) - reset sql type and column size for input params
|
||||
- Issue [#1331](https://github.com/microsoft/msphpsql/issues/1331) - restore PDO::ATTR_ERRMODE if calling PDO::lastInsertId() call fails - pull request [#1330](https://github.com/microsoft/msphpsql/pull/1330) by mpyw and pull request [#1332](https://github.com/microsoft/msphpsql/pull/1332)
|
||||
|
||||
### 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
|
||||
- Issue [#1050](https://github.com/microsoft/msphpsql/issues/1050) - With Always Encrypted enabled, insertion requires the column list for any tables with identity columns
|
||||
- [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
|
||||
- This release requires ODBC Driver 17.4.2 or above. Otherwise, a warning about failing to set an attribute may be suppressed when using an older ODBC driver.
|
||||
- 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)
|
||||
|
||||
|
||||
## 5.10.0-beta1 - 2021-09-08
|
||||
Updated PECL release packages. Here is the list of updates:
|
||||
|
||||
|
|
|
@ -206,10 +206,12 @@ jobs:
|
|||
coveralls -i ./source/ -e ./source/shared/ -e ./test/ -e ./source/pdo_sqlsrv/shared/core_stream.cpp \
|
||||
-E r'.*localization*' -E r'.*globalization*' --gcov-options '\-lp'
|
||||
displayName: 'Invoke coveralls using repo token'
|
||||
condition: false
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: $(repo_token)
|
||||
TRAVIS_JOB_ID: $(Build.BuildId)
|
||||
TRAVIS_BRANCH: $(Build.SourceBranchName)
|
||||
PYTHONWARNINGS: ignore::yaml.YAMLLoadWarning
|
||||
|
||||
- script: |
|
||||
cd $(Build.SourcesDirectory)/test/functional/
|
||||
|
|
|
@ -25,7 +25,7 @@ def write_index(index_filename, tag_version):
|
|||
PATH=%var2%
|
||||
SRCSRVTRG=%TARG%\%PDBVERSION%\%fnbksl%(%var2%)
|
||||
SRCURL=https://raw.githubusercontent.com/Microsoft/msphpsql/%SRCVERSION%/source/%PATH%
|
||||
SRCSRVCMD=powershell -Command "$r=New-Object -ComObject Msxml2.XMLHTTP; $r.open('GET', '%SRCURL%', $false); $r.send(); [io.file]::WriteAllBytes('%SRCSRVTRG%', $r.responseBody)"
|
||||
SRCSRVCMD=powershell -Command "Invoke-WebRequest -Uri '%SRCURL%' -OutFile '%SRCSRVTRG%'"
|
||||
SRCVERSION=v5.6.0
|
||||
PDBVERSION=v5.6.0
|
||||
For example
|
||||
|
@ -38,9 +38,7 @@ def write_index(index_filename, tag_version):
|
|||
f.write('SRCSRVTRG=%TARG%\%PDBVERSION%\%fnbksl%(%var2%)' + os.linesep)
|
||||
f.write('SRCURL=https://raw.githubusercontent.com/Microsoft/msphpsql/%SRCVERSION%/source/%PATH%' + os.linesep)
|
||||
f.write('SRCSRVCMD=powershell -Command ')
|
||||
f.write('\"$r=New-Object -ComObject Msxml2.XMLHTTP; ')
|
||||
f.write('$r.open(\'GET\', \'%SRCURL%\', $false); ')
|
||||
f.write('$r.send(); [io.file]::WriteAllBytes(\'%SRCSRVTRG%\', $r.responseBody)\"' + os.linesep)
|
||||
f.write('\"Invoke-WebRequest -Uri \'%SRCURL%\' -OutFile \'%SRCSRVTRG%\'\"' + os.linesep)
|
||||
f.write('SRCVERSION=' + tag_version + os.linesep)
|
||||
f.write('PDBVERSION=' + tag_version + os.linesep)
|
||||
|
||||
|
|
|
@ -542,7 +542,8 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
|
|||
fetch_datetime( false ),
|
||||
format_decimals( false ),
|
||||
decimal_places( NO_CHANGE_DECIMAL_PLACES ),
|
||||
use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED)
|
||||
use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED),
|
||||
emulate_prepare(false)
|
||||
{
|
||||
if( client_buffer_max_size < 0 ) {
|
||||
client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT;
|
||||
|
@ -734,7 +735,8 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
|
|||
// assign the methods for the statement object. This is necessary even if the
|
||||
// statement fails so the user can retrieve the error information.
|
||||
stmt->methods = &pdo_sqlsrv_stmt_methods;
|
||||
stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names
|
||||
// if not emulate_prepare, we support parameterized queries with ?, not names
|
||||
stmt->supports_placeholders = (driver_dbh->emulate_prepare) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL; // the statement options may override this later
|
||||
|
||||
// Initialize the options array to be passed to the core layer
|
||||
ALLOC_HASHTABLE( pdo_stmt_options_ht );
|
||||
|
@ -1288,8 +1290,15 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
|
|||
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR );
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_EMULATE_PREPARES:
|
||||
{
|
||||
driver_dbh->emulate_prepare = zend_is_true(val);
|
||||
if (driver_dbh->emulate_prepare && driver_dbh->ce_option.enabled) {
|
||||
THROW_PDO_ERROR(driver_dbh, PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Statement level only
|
||||
case PDO_ATTR_CURSOR:
|
||||
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
||||
case SQLSRV_ATTR_DATA_CLASSIFICATION:
|
||||
|
@ -1362,8 +1371,13 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
|
|||
#endif
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_EMULATE_PREPARES:
|
||||
{
|
||||
ZVAL_BOOL(return_value, driver_dbh->emulate_prepare);
|
||||
break;
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_CURSOR:
|
||||
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
||||
case SQLSRV_ATTR_DATA_CLASSIFICATION:
|
||||
|
@ -1584,6 +1598,9 @@ zend_string * pdo_sqlsrv_dbh_last_id(_Inout_ pdo_dbh_t *dbh, _In_ const zend_str
|
|||
|
||||
driver_stmt->~sqlsrv_stmt();
|
||||
} catch( core::CoreException& ) {
|
||||
// restore error handling to its previous mode
|
||||
dbh->error_mode = prev_err_mode;
|
||||
|
||||
// copy any errors on the statement to the connection so that the user sees them, since the statement is released
|
||||
// before this method returns
|
||||
strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
|
||||
|
@ -1599,7 +1616,7 @@ zend_string * pdo_sqlsrv_dbh_last_id(_Inout_ pdo_dbh_t *dbh, _In_ const zend_str
|
|||
str[0] = '\0';
|
||||
return str;
|
||||
#else
|
||||
return NULL;
|
||||
return ZSTR_EMPTY_ALLOC();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -173,31 +173,6 @@ void conn_string_parser::validate_key( _In_reads_(key_len) const char *key, _Ino
|
|||
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ) );
|
||||
}
|
||||
|
||||
void conn_string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_ int len )
|
||||
{
|
||||
// if the keyword is 'Authentication', check whether the user specified option is supported
|
||||
bool valid = true;
|
||||
if ( stricmp( this->current_key_name, ODBCConnOptions::Authentication ) == 0 ) {
|
||||
if (len <= 0)
|
||||
valid = false;
|
||||
else {
|
||||
// extract option from the value by len
|
||||
sqlsrv_malloc_auto_ptr<char> option;
|
||||
option = static_cast<char*>( sqlsrv_malloc( len + 1 ) );
|
||||
memcpy_s( option, len + 1, value, len );
|
||||
option[len] = '\0';
|
||||
|
||||
valid = AzureADOptions::isAuthValid(option, len);
|
||||
}
|
||||
}
|
||||
if( !valid ) {
|
||||
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, this->current_key_name );
|
||||
}
|
||||
|
||||
string_parser::add_key_value_pair( value, len );
|
||||
}
|
||||
|
||||
|
||||
inline bool sql_string_parser::is_placeholder_char( char c )
|
||||
{
|
||||
// placeholder only accepts numbers, upper and lower case alphabets and underscore
|
||||
|
|
|
@ -377,10 +377,6 @@ pdo_error PDO_ERRORS[] = {
|
|||
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
|
||||
{ IMSSP, (SQLCHAR*) "Statement with emulate prepare on does not support output or input_output parameters.", -72, false }
|
||||
},
|
||||
{
|
||||
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectoryServicePrincipal is supported.", -73, false }
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_CE_DRIVER_REQUIRED,
|
||||
{ IMSSP, (SQLCHAR*) "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server (or above) for %1!s!.", -78, true }
|
||||
|
@ -441,10 +437,6 @@ pdo_error PDO_ERRORS[] = {
|
|||
SQLSRV_ERROR_INVALID_DECIMAL_PLACES,
|
||||
{ IMSSP, (SQLCHAR*) "Expected an integer to specify number of decimals to format the output values of decimal data types.", -92, false}
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_AAD_MSI_UID_PWD_NOT_NULL,
|
||||
{ IMSSP, (SQLCHAR*) "When using ActiveDirectoryMsi Authentication, PWD must be NULL. UID can be NULL, but if not, an empty string is not accepted.", -93, false}
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_DATA_CLASSIFICATION_PRE_EXECUTION,
|
||||
{ IMSSP, (SQLCHAR*) "The statement must be executed to retrieve Data Classification Sensitivity Metadata.", -94, false}
|
||||
|
|
|
@ -149,9 +149,6 @@ class conn_string_parser : private string_parser
|
|||
int discard_trailing_white_spaces( _In_reads_(len) const char* str, _Inout_ int len );
|
||||
void validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len);
|
||||
|
||||
protected:
|
||||
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len);
|
||||
|
||||
public:
|
||||
conn_string_parser( _In_ sqlsrv_context& ctx, _In_ const char* dsn, _In_ int len, _In_ HashTable* conn_options_ht );
|
||||
void parse_conn_string( void );
|
||||
|
@ -194,6 +191,7 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
|
|||
bool format_decimals;
|
||||
short decimal_places;
|
||||
short use_national_characters;
|
||||
bool emulate_prepare;
|
||||
|
||||
pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver );
|
||||
};
|
||||
|
@ -390,7 +388,6 @@ enum PDO_ERROR_CODES {
|
|||
PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
|
||||
PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE,
|
||||
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
|
||||
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
|
||||
PDO_SQLSRV_ERROR_CE_DIRECT_QUERY_UNSUPPORTED,
|
||||
PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED,
|
||||
PDO_SQLSRV_ERROR_EXTENDED_STRING_TYPE_INVALID
|
||||
|
|
|
@ -704,41 +704,6 @@ bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t v
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace AzureADOptions {
|
||||
enum AAD_AUTH_TYPE {
|
||||
MIN_AAD_AUTH_TYPE = 0,
|
||||
SQL_PASSWORD = 0,
|
||||
AAD_PASSWORD,
|
||||
AAD_MSI,
|
||||
AAD_SPA,
|
||||
MAX_AAD_AUTH_TYPE
|
||||
};
|
||||
|
||||
const char *AADAuths[] = { "SqlPassword", "ActiveDirectoryPassword", "ActiveDirectoryMsi", "ActiveDirectoryServicePrincipal" };
|
||||
|
||||
bool isAuthValid(_In_z_ const char* value, _In_ size_t value_len)
|
||||
{
|
||||
if (value_len <= 0)
|
||||
return false;
|
||||
|
||||
bool isValid = false;
|
||||
for (short i = MIN_AAD_AUTH_TYPE; i < MAX_AAD_AUTH_TYPE && !isValid; i++)
|
||||
{
|
||||
if (!stricmp(value, AADAuths[i])) {
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool isAADMsi(_In_z_ const char* value)
|
||||
{
|
||||
return (value != NULL && !stricmp(value, AADAuths[AAD_MSI]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// *** internal connection functions and classes ***
|
||||
|
||||
namespace {
|
||||
|
@ -792,10 +757,11 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
access_token_used = true;
|
||||
}
|
||||
|
||||
// Check if Authentication is ActiveDirectoryMSI
|
||||
// Check if Authentication is ActiveDirectoryMSI because we have to handle this case differently
|
||||
// https://docs.microsoft.com/en-ca/azure/active-directory/managed-identities-azure-resources/overview
|
||||
bool activeDirectoryMSI = false;
|
||||
if (authentication_option_used) {
|
||||
const char aadMSIoption[] = "ActiveDirectoryMSI";
|
||||
zval* auth_option = NULL;
|
||||
auth_option = zend_hash_index_find(options, SQLSRV_CONN_OPTION_AUTHENTICATION);
|
||||
|
||||
|
@ -804,34 +770,16 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
option = Z_STRVAL_P(auth_option);
|
||||
}
|
||||
|
||||
//if (option != NULL && !stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
|
||||
activeDirectoryMSI = AzureADOptions::isAADMsi(option);
|
||||
if (activeDirectoryMSI) {
|
||||
// There are two types of managed identities:
|
||||
// (1) A system-assigned managed identity: UID must be NULL
|
||||
// (2) A user-assigned managed identity: UID defined but must not be an empty string
|
||||
// In both cases, PWD must be NULL
|
||||
|
||||
bool invalid = false;
|
||||
if (pwd != NULL) {
|
||||
invalid = true;
|
||||
} else {
|
||||
if (uid != NULL && strnlen_s(uid) == 0) {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_CUSTOM_ERROR(invalid, conn, SQLSRV_ERROR_AAD_MSI_UID_PWD_NOT_NULL ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
if (option != NULL && !stricmp(option, aadMSIoption)) {
|
||||
activeDirectoryMSI = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the server name
|
||||
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
|
||||
|
||||
// If uid is not present then we use trusted connection -- but not when access token or ActiveDirectoryMSI is used,
|
||||
// because they are incompatible
|
||||
// If uid is not present then we use trusted connection -- but not when connecting
|
||||
// using the access token or Authentication is ActiveDirectoryMSI
|
||||
if (!access_token_used && !activeDirectoryMSI) {
|
||||
if (uid == NULL || strnlen_s(uid) == 0) {
|
||||
connection_string += CONNECTION_OPTION_NO_CREDENTIALS; // "Trusted_Connection={Yes};"
|
||||
|
|
|
@ -190,11 +190,6 @@ const int SQL_SERVER_2005_DEFAULT_DATETIME_SCALE = 3;
|
|||
const int SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION = 34;
|
||||
const int SQL_SERVER_2008_DEFAULT_DATETIME_SCALE = 7;
|
||||
|
||||
namespace AzureADOptions {
|
||||
bool isAuthValid(_In_z_ const char* value, _In_ size_t value_len);
|
||||
bool isAADMsi(_In_z_ const char* value);
|
||||
}
|
||||
|
||||
// the message returned by ODBC Driver for SQL Server
|
||||
const char ODBC_CONNECTION_BUSY_ERROR[] = "Connection is busy with results for another command";
|
||||
|
||||
|
@ -2011,7 +2006,6 @@ enum SQLSRV_ERROR_CODES {
|
|||
SQLSRV_ERROR_INVALID_OPTION_WITH_ACCESS_TOKEN,
|
||||
SQLSRV_ERROR_EMPTY_ACCESS_TOKEN,
|
||||
SQLSRV_ERROR_INVALID_DECIMAL_PLACES,
|
||||
SQLSRV_ERROR_AAD_MSI_UID_PWD_NOT_NULL,
|
||||
SQLSRV_ERROR_DATA_CLASSIFICATION_PRE_EXECUTION,
|
||||
SQLSRV_ERROR_DATA_CLASSIFICATION_NOT_AVAILABLE,
|
||||
SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED,
|
||||
|
|
|
@ -395,6 +395,13 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
|
|||
stmt->params_container.insert_param(param_num, new_param);
|
||||
param_ptr = new_param;
|
||||
new_param.transferred();
|
||||
} else if (direction == SQL_PARAM_INPUT
|
||||
&& param_ptr->sql_data_type != SQL_SS_TABLE
|
||||
&& param_ptr->strlen_or_indptr == SQL_NULL_DATA) {
|
||||
// reset the followings for regular input parameters if it was bound as a null param before
|
||||
param_ptr->sql_data_type = sql_type;
|
||||
param_ptr->column_size = column_size;
|
||||
param_ptr->strlen_or_indptr = 0;
|
||||
}
|
||||
|
||||
SQLSRV_ASSERT(param_ptr != NULL, "core_sqlsrv_bind_param: param_ptr is null. Something went wrong.");
|
||||
|
@ -2148,16 +2155,17 @@ void sqlsrv_param::process_null_param(_Inout_ zval* param_z)
|
|||
// Derive the param SQL type only if it is unknown
|
||||
if (sql_data_type == SQL_UNKNOWN_TYPE) {
|
||||
// Use the encoding to guess whether the sql_type is binary type or char type. For NULL cases,
|
||||
// if the server type is a binary type, than the server expects the sql_type to be binary type
|
||||
// if the server type is a binary type, then the server expects the sql_type to be binary type
|
||||
// as well, otherwise an error stating "Implicit conversion not allowed.." is thrown by the
|
||||
// server. For all other server types, setting the sql_type to sql_char works fine.
|
||||
sql_data_type = (encoding == SQLSRV_ENCODING_BINARY) ? SQL_BINARY : SQL_CHAR;
|
||||
// server. For all other server types, setting the sql_type to sql_varchar works fine.
|
||||
// It must be varchar with column size 0 for ISNULL to work properly.
|
||||
sql_data_type = (encoding == SQLSRV_ENCODING_BINARY) ? SQL_BINARY : SQL_VARCHAR;
|
||||
}
|
||||
|
||||
c_data_type = (encoding == SQLSRV_ENCODING_BINARY) ? SQL_C_BINARY : SQL_C_CHAR;
|
||||
|
||||
if (column_size == SQLSRV_UNKNOWN_SIZE) {
|
||||
column_size = 1;
|
||||
column_size = (encoding == SQLSRV_ENCODING_BINARY) ? 1 : 0;
|
||||
decimal_digits = 0;
|
||||
}
|
||||
buffer = NULL;
|
||||
|
@ -3107,8 +3115,6 @@ void sqlsrv_param_tvp::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* pa
|
|||
int num_columns = parse_tv_param_arrays(stmt, param_z);
|
||||
column_size = num_rows;
|
||||
|
||||
buffer = NULL;
|
||||
buffer_length = 0;
|
||||
strlen_or_indptr = (num_columns == 0)? SQL_DEFAULT_PARAM : SQL_DATA_AT_EXEC;
|
||||
} else {
|
||||
// This is one of the constituent columns of the table-valued parameter
|
||||
|
@ -3154,11 +3160,16 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
|
|||
throw core::CoreException();
|
||||
}
|
||||
|
||||
// Save the TVP type name for SQLSetDescField later
|
||||
buffer = ZSTR_VAL(tvp_name);
|
||||
buffer_length = SQL_NTS;
|
||||
|
||||
// Check if schema is provided by the user
|
||||
if (zend_hash_move_forward_ex(inputs_ht, &pos) == SUCCESS) {
|
||||
zval *schema_z = zend_hash_get_current_data_ex(inputs_ht, &pos);
|
||||
if (schema_z != NULL && Z_TYPE_P(schema_z) == IS_STRING) {
|
||||
schema_name = Z_STR_P(schema_z);
|
||||
ZVAL_NEW_STR(&placeholder_z, schema_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3308,6 +3319,34 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
|
|||
return;
|
||||
}
|
||||
|
||||
// Set Table-Valued parameter type name (and the schema where it is defined)
|
||||
SQLHDESC hIpd = NULL;
|
||||
core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
|
||||
|
||||
if (buffer != NULL) {
|
||||
// SQL_CA_SS_TYPE_NAME is optional for stored procedure calls, but it must be
|
||||
// specified for SQL statements that are not procedure calls to enable the
|
||||
// server to determine the type of the table-valued parameter.
|
||||
char *tvp_name = reinterpret_cast<char *>(buffer);
|
||||
SQLRETURN r = ::SQLSetDescField(hIpd, param_pos + 1, SQL_CA_SS_TYPE_NAME, reinterpret_cast<SQLCHAR*>(tvp_name), SQL_NTS);
|
||||
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
}
|
||||
if (Z_TYPE(placeholder_z) == IS_STRING) {
|
||||
// If the table type for the table-valued parameter is defined in a different
|
||||
// schema than the default, SQL_CA_SS_SCHEMA_NAME must be specified. If not,
|
||||
// the server will not be able to determine the type of the table-valued parameter.
|
||||
char * schema_name = Z_STRVAL(placeholder_z);
|
||||
SQLRETURN r = ::SQLSetDescField(hIpd, param_pos + 1, SQL_CA_SS_SCHEMA_NAME, reinterpret_cast<SQLCHAR*>(schema_name), SQL_NTS);
|
||||
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
// Free and reset the placeholder_z
|
||||
zend_string_release(Z_STR(placeholder_z));
|
||||
ZVAL_UNDEF(&placeholder_z);
|
||||
}
|
||||
|
||||
// Bind the TVP columns one by one
|
||||
// Register this object first using SQLSetDescField() for sending TVP data post execution
|
||||
SQLHDESC desc;
|
||||
|
@ -3606,12 +3645,17 @@ bool sqlsrv_params_container::get_next_parameter(_Inout_ sqlsrv_stmt* stmt)
|
|||
// Done now, reset current_param
|
||||
current_param = NULL;
|
||||
return false;
|
||||
} else if (r == SQL_NEED_DATA) {
|
||||
if (param != NULL) {
|
||||
current_param = reinterpret_cast<sqlsrv_param*>(param);
|
||||
SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!");
|
||||
current_param->init_data_from_zval(stmt);
|
||||
} else {
|
||||
// Do not reset current_param when param is NULL, because
|
||||
// it means that data is expected from the existing current_param
|
||||
}
|
||||
}
|
||||
|
||||
current_param = reinterpret_cast<sqlsrv_param*>(param);
|
||||
SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!");
|
||||
current_param->init_data_from_zval(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#define SQLVERSION_BUILD 0
|
||||
|
||||
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
|
||||
#define PREVIEW 1
|
||||
#define PREVIEW 2
|
||||
#define SEMVER_PRERELEASE
|
||||
|
||||
// Semantic versioning build metadata, build meta data is not counted in precedence order.
|
||||
|
@ -59,7 +59,7 @@
|
|||
#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
|
||||
|
||||
// PECL package version ('-' or '+' is not allowed) - to support Pickle do not use macros below
|
||||
#define PHP_SQLSRV_VERSION "5.10.0beta1"
|
||||
#define PHP_PDO_SQLSRV_VERSION "5.10.0beta1"
|
||||
#define PHP_SQLSRV_VERSION "5.10.0beta2"
|
||||
#define PHP_PDO_SQLSRV_VERSION "5.10.0beta2"
|
||||
|
||||
#endif // VERSION_H
|
||||
|
|
|
@ -1362,16 +1362,6 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
|
|||
throw ss::SSException();
|
||||
}
|
||||
|
||||
bool valid = true;
|
||||
if( stricmp( SS_CONN_OPTS[i].sqlsrv_name, SSConnOptionNames::Authentication ) == 0 ) {
|
||||
valid = AzureADOptions::isAuthValid(value, value_len);
|
||||
}
|
||||
|
||||
CHECK_CUSTOM_ERROR( !valid, ctx, SS_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, SS_CONN_OPTS[i].sqlsrv_name ) {
|
||||
|
||||
throw ss::SSException();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CONN_ATTR_INVALID:
|
||||
|
|
|
@ -206,7 +206,6 @@ enum SS_ERROR_CODES {
|
|||
SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED,
|
||||
SS_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
|
||||
SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF,
|
||||
SS_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
|
||||
SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED
|
||||
};
|
||||
|
||||
|
|
|
@ -1187,8 +1187,6 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt )
|
|||
|
||||
try {
|
||||
|
||||
stmt->free_param_data();
|
||||
|
||||
stmt->executed = false;
|
||||
|
||||
zval* params_z = stmt->params_z;
|
||||
|
@ -1265,6 +1263,8 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt )
|
|||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
catch( core::CoreException& ) {
|
||||
stmt->free_param_data();
|
||||
|
||||
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
|
||||
zval_ptr_dtor( stmt->params_z );
|
||||
sqlsrv_free( stmt->params_z );
|
||||
|
|
|
@ -363,10 +363,6 @@ ss_error SS_ERRORS[] = {
|
|||
"Output or bidirectional variable parameters (SQLSRV_PARAM_OUT and SQLSRV_PARAM_INOUT) passed to sqlsrv_prepare or sqlsrv_query should be passed by reference, not by value."
|
||||
, -61, true }
|
||||
},
|
||||
{
|
||||
SS_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
|
||||
{ IMSSP, (SQLCHAR*)"Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectoryServicePrincipal is supported.", -62, false }
|
||||
},
|
||||
{
|
||||
SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED,
|
||||
{ IMSSP, (SQLCHAR*)"Must specify the SQL type for each parameter in a parameterized query when using sqlsrv_query in a column encryption enabled connection.", -63, false }
|
||||
|
@ -429,10 +425,6 @@ ss_error SS_ERRORS[] = {
|
|||
SQLSRV_ERROR_INVALID_DECIMAL_PLACES,
|
||||
{ IMSSP, (SQLCHAR*) "Expected an integer to specify number of decimals to format the output values of decimal data types.", -117, false}
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_AAD_MSI_UID_PWD_NOT_NULL,
|
||||
{ IMSSP, (SQLCHAR*) "When using ActiveDirectoryMsi Authentication, PWD must be NULL. UID can be NULL, but if not, an empty string is not accepted.", -118, false}
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_DATA_CLASSIFICATION_PRE_EXECUTION,
|
||||
{ IMSSP, (SQLCHAR*) "The statement must be executed to retrieve Data Classification Sensitivity Metadata.", -119, false}
|
||||
|
|
|
@ -636,6 +636,25 @@ function IsDaasMode()
|
|||
return ($daasMode ? true : false);
|
||||
}
|
||||
|
||||
function isServerHGSEnabled()
|
||||
{
|
||||
$enabled = false;
|
||||
try {
|
||||
$conn = connect();
|
||||
$tsql = "SELECT @@SERVERNAME";
|
||||
$stmt = $conn->query($tsql);
|
||||
|
||||
$result = $stmt->fetch(PDO::FETCH_NUM);
|
||||
$name = $result[0];
|
||||
|
||||
$enabled = (strpos($result[0], 'HGS') != false);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage();
|
||||
die("Could not fetch server property.");
|
||||
}
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
function isSQLAzure()
|
||||
{
|
||||
// 'SQL Azure' indicates SQL Database or SQL Data Warehouse
|
||||
|
|
|
@ -1,49 +1,44 @@
|
|||
--TEST--
|
||||
Tests error returned when binding output parameter with emulate prepare
|
||||
--DESCRIPTION--
|
||||
The test shows that the option sets in prepared statements overrides the
|
||||
connection setting of PDO::ATTR_EMULATE_PREPARES
|
||||
--SKIPIF--
|
||||
<?php require_once('skipif_mid-refactor.inc'); ?>
|
||||
<?php require('skipif_azure_dw.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
try {
|
||||
$conn = connect();
|
||||
// Do not connect with AE enabled because otherwise this would have thrown a different exception
|
||||
$conn = new PDO("sqlsrv:server=$server; Database = $databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
|
||||
$count = 0;
|
||||
|
||||
$query = "select ? = count(* ) from cd_info";
|
||||
$stmt = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
$stmt->bindParam(1, $count, PDO::PARAM_STR, 10);
|
||||
$stmt->execute();
|
||||
echo "Result: ".$count."\n";
|
||||
|
||||
$query = "select bigint_type, int_type, money_type from [test_types] where int_type < 0";
|
||||
$stmt1 = $conn->prepare($query);
|
||||
$stmt1->execute();
|
||||
$row = $stmt1->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
|
||||
$int = 0;
|
||||
$bigint = 100;
|
||||
$query = "select ? = bigint_type, ? = int_type, ? = money_type from [test_types] where int_type < 0";
|
||||
$stmt2 = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
$stmt2->bindparam(1, $bigint, PDO::PARAM_STR, 256);
|
||||
$stmt2->bindParam(2, $int, PDO::PARAM_INT, 4);
|
||||
$stmt2->bindParam(3, $money, PDO::PARAM_STR, 1024);
|
||||
$stmt2->execute();
|
||||
echo "Big integer: ".$bigint."\n";
|
||||
echo "Integer: ".$int."\n";
|
||||
echo "Money: ".$money."\n";
|
||||
|
||||
//free the statement and connection
|
||||
unset($stmt);
|
||||
unset($stmt1);
|
||||
unset($stmt2);
|
||||
unset($conn);
|
||||
$stmt = $conn->prepare($query);
|
||||
} catch (PDOException $e) {
|
||||
print("Error: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
try {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
|
||||
$int = 0;
|
||||
$bigint = 100;
|
||||
$query = "select ? = bigint_type, ? = int_type, ? = money_type from [test_types] where int_type < 0";
|
||||
$stmt = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
} catch (PDOException $e) {
|
||||
print("Error: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
// free the statement and connection
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.
|
||||
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.
|
||||
|
|
97
test/functional/pdo_sqlsrv/pdo_1307_tvp_non_procedure.phpt
Normal file
97
test/functional/pdo_sqlsrv/pdo_1307_tvp_non_procedure.phpt
Normal file
|
@ -0,0 +1,97 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed.
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable)
|
||||
{
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname);
|
||||
$conn->exec($dropTableType);
|
||||
$conn->exec("DROP TABLE IF EXISTS [$testTable]");
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
function readData($conn, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM $testTable ORDER BY id";
|
||||
$stmt = $conn->query($tsql);
|
||||
$stmt->bindColumn('id', $ID);
|
||||
while ($row = $stmt->fetch( PDO::FETCH_BOUND ) ){
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$tvpname = 'pdo_id_table';
|
||||
$testTable = 'pdo_test_table';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE $tvpname AS TABLE(id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
$tsql = "CREATE TABLE $testTable (id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO $testTable SELECT * FROM ?";
|
||||
$tvpinput = array($tvpname => [[1], [2], [3]]);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO $testTable t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
unset($tvpinput);
|
||||
$tvpinput = array($tvpname => [[5], [4], [3], [2]]);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
Done
|
|
@ -0,0 +1,105 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed but TVP and table are defined in a different schema
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable, $schema)
|
||||
{
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname, $schema);
|
||||
$conn->exec($dropTableType);
|
||||
$conn->exec("DROP TABLE IF EXISTS [$schema].[$testTable]");
|
||||
$conn->exec("DROP SCHEMA IF EXISTS [$schema]");
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
function readData($conn, $schema, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM [$schema].[$testTable] ORDER BY id";
|
||||
$stmt = $conn->query($tsql);
|
||||
$stmt->bindColumn('id', $ID);
|
||||
while ($row = $stmt->fetch( PDO::FETCH_BOUND ) ){
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$tvpname = 'pdo_id_table2';
|
||||
$testTable = 'pdo_test_table2';
|
||||
$schema = 'pdo schema';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
// Create the schema
|
||||
$tsql = "CREATE SCHEMA [$schema]";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE [$schema].[$tvpname] AS TABLE(id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
$tsql = "CREATE TABLE [$schema].[$testTable] (id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO [$schema].[$testTable] SELECT * FROM ?";
|
||||
$tvpinput = array($tvpname => [[5], [3], [1]], $schema);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO [$schema].[$testTable] t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
unset($tvpinput);
|
||||
$tvpinput = array($tvpname => [[2], [4], [6], [7]], $schema);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
3
|
||||
5
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
Done
|
88
test/functional/pdo_sqlsrv/pdo_1310_null_varchar.phpt
Normal file
88
test/functional/pdo_sqlsrv/pdo_1310_null_varchar.phpt
Normal file
|
@ -0,0 +1,88 @@
|
|||
--TEST--
|
||||
GitHub issue 1310 - bind null field as varchar(max) if not binary
|
||||
--DESCRIPTION--
|
||||
The test shows null fields are no longer bound as char(1) if not binary such that it solves both issues 1310 and 1102.
|
||||
Note that this test does not connect with AE enabled because SQLDescribeParam() does not work with these queries.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server=$server; Database = $databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// Issue 1310
|
||||
$query = "SELECT CAST(ISNULL(:K, -1) AS INT) AS K";
|
||||
$k = null;
|
||||
|
||||
$stmt = $conn->prepare($query);
|
||||
$stmt->bindParam(':K', $k, PDO::PARAM_NULL);
|
||||
$stmt->execute();
|
||||
|
||||
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
var_dump($row);
|
||||
|
||||
$stmt->bindParam(':K', $k, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
$row = $stmt->fetchAll(PDO::FETCH_NUM);
|
||||
var_dump($row);
|
||||
|
||||
// Issue 1102
|
||||
$query = "DECLARE @d DATETIME = ISNULL(:K, GETDATE()); SELECT @d AS D;";
|
||||
$k = null;
|
||||
|
||||
$stmt = $conn->prepare($query);
|
||||
$stmt->bindParam(':K', $k, PDO::PARAM_NULL);
|
||||
$stmt->execute();
|
||||
|
||||
$row = $stmt->fetchAll(PDO::FETCH_NUM);
|
||||
var_dump($row);
|
||||
|
||||
$stmt->bindParam(':K', $k, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
var_dump($row);
|
||||
|
||||
echo "Done\n";
|
||||
} catch (PdoException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
array\(1\) {
|
||||
\["K"\]=>
|
||||
string\(2\) "-1"
|
||||
}
|
||||
}
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
string\(2\) "-1"
|
||||
}
|
||||
}
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
string\(23\) "20[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:00.000"
|
||||
}
|
||||
}
|
||||
array\(1\) {
|
||||
\[0\]=>
|
||||
array\(1\) {
|
||||
\["D"\]=>
|
||||
string\(23\) "20[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:00.000"
|
||||
}
|
||||
}
|
||||
Done
|
||||
|
62
test/functional/pdo_sqlsrv/pdo_1320_pdo_emulate_prepare.phpt
Normal file
62
test/functional/pdo_sqlsrv/pdo_1320_pdo_emulate_prepare.phpt
Normal file
|
@ -0,0 +1,62 @@
|
|||
--TEST--
|
||||
GitHub issue 1320 - support PDO::ATTR_EMULATE_PREPARES at the connection level
|
||||
--DESCRIPTION--
|
||||
Supports PDO::ATTR_EMULATE_PREPARES at the connection level but setting it to true with column
|
||||
encryption enabled will fail with an exception. Also, the options in the prepared statement will
|
||||
override the connection setting.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
echo "setAttribute should have failed because column encryption is enabled.\n\n";
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
unset($conn);
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled
|
||||
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true);
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd, $options);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
unset($conn);
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled - PDO::ATTR_EMULATE_PREPARES is false by default
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
echo "Connected successfully with column encryption enabled.\n";
|
||||
$enabled = $conn->getAttribute(PDO::ATTR_EMULATE_PREPARES);
|
||||
echo "By default, the emulation of prepared statements is:\n";
|
||||
var_dump($enabled);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
--EXPECT--
|
||||
SQLSTATE[IMSSP]: Parameterized statement with attribute PDO::ATTR_EMULATE_PREPARES is not supported in a Column Encryption enabled Connection.
|
||||
SQLSTATE[IMSSP]: Parameterized statement with attribute PDO::ATTR_EMULATE_PREPARES is not supported in a Column Encryption enabled Connection.
|
||||
Connected successfully with column encryption enabled.
|
||||
By default, the emulation of prepared statements is:
|
||||
bool(false)
|
118
test/functional/pdo_sqlsrv/pdo_1329_string_truncation.phpt
Normal file
118
test/functional/pdo_sqlsrv/pdo_1329_string_truncation.phpt
Normal file
|
@ -0,0 +1,118 @@
|
|||
--TEST--
|
||||
GitHub issue 1329 - string truncation error when binding some parameters as non-nulls the second time
|
||||
--DESCRIPTION--
|
||||
The test shows the same parameters, though bound as nulls in the first insertion, can be bound as non-nulls in the subsequent insertions.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
function dropTable($conn, $tableName)
|
||||
{
|
||||
$drop = "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" . $tableName . "') AND type in (N'U')) DROP TABLE $tableName";
|
||||
$conn->exec($drop);
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server=$server; Database = $databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
dropTable($conn, 'domains');
|
||||
|
||||
$tsql = <<<CREATESQL
|
||||
CREATE TABLE domains (
|
||||
id bigint IDENTITY(1,1) NOT NULL,
|
||||
authority nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
base_url_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
regular_not_found_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
invalid_short_url_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
CONSTRAINT PK__domains__3213E83F512B36BA PRIMARY KEY (id))
|
||||
CREATESQL;
|
||||
|
||||
$conn->exec($tsql);
|
||||
|
||||
$tsql = <<<INSERTSQL
|
||||
INSERT INTO domains (authority, base_url_redirect, regular_not_found_redirect, invalid_short_url_redirect) VALUES (?, ?, ?, ?)
|
||||
INSERTSQL;
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$authority = 'foo.com';
|
||||
$base = null;
|
||||
$notFound = null;
|
||||
$invalid = null;
|
||||
$stmt->bindParam(1, $authority);
|
||||
$stmt->bindParam(2, $base);
|
||||
$stmt->bindParam(3, $notFound);
|
||||
$stmt->bindParam(4, $invalid);
|
||||
$stmt->execute();
|
||||
|
||||
$authority = 'detached-with-ředirects.com';
|
||||
$base = 'fŏő.com';
|
||||
$notFound = 'baŗ.com';
|
||||
$invalid = null;
|
||||
$stmt->bindParam(1, $authority);
|
||||
$stmt->bindParam(2, $base);
|
||||
$stmt->bindParam(3, $notFound);
|
||||
$stmt->bindParam(4, $invalid);
|
||||
$stmt->execute();
|
||||
|
||||
$authority = 'Őther-redirects.com';
|
||||
$base = 'fooš.com';
|
||||
$notFound = null;
|
||||
$invalid = 'ŷëå';
|
||||
$stmt->bindParam(1, $authority);
|
||||
$stmt->bindParam(2, $base);
|
||||
$stmt->bindParam(3, $notFound);
|
||||
$stmt->bindParam(4, $invalid);
|
||||
$stmt->execute();
|
||||
|
||||
// fetch the data
|
||||
$stmt = $conn->prepare("SELECT * FROM domains");
|
||||
$stmt->execute();
|
||||
$row = $stmt->fetchAll(PDO::FETCH_NUM);
|
||||
print_r($row);
|
||||
|
||||
dropTable($conn, 'domains');
|
||||
|
||||
echo "Done\n";
|
||||
} catch (PdoException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[0] => 1
|
||||
[1] => foo.com
|
||||
[2] =>
|
||||
[3] =>
|
||||
[4] =>
|
||||
)
|
||||
|
||||
[1] => Array
|
||||
(
|
||||
[0] => 2
|
||||
[1] => detached-with-ředirects.com
|
||||
[2] => fŏő.com
|
||||
[3] => baŗ.com
|
||||
[4] =>
|
||||
)
|
||||
|
||||
[2] => Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => Őther-redirects.com
|
||||
[2] => fooš.com
|
||||
[3] =>
|
||||
[4] => ŷëå
|
||||
)
|
||||
|
||||
)
|
||||
Done
|
|
@ -41,8 +41,36 @@ EOF;
|
|||
|
||||
$st->execute();
|
||||
|
||||
$data = selectAll($cnn, $tbname);
|
||||
$system_param2 = 'another string';
|
||||
$utf8_param = 'Привет';
|
||||
$binary_param = fopen('php://memory', 'a');
|
||||
fwrite($binary_param, hex2bin('80838790a9')); // testing some extended characters
|
||||
rewind($binary_param);
|
||||
|
||||
$st->bindParam(1, $system_param2, PDO::PARAM_STR);
|
||||
$st->bindParam(2, $utf8_param, PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
|
||||
$st->bindParam(3, $binary_param, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
|
||||
|
||||
$st->execute();
|
||||
|
||||
$select = "SELECT * FROM $tbname WHERE system_encoding = ?";
|
||||
$st = $cnn->prepare($select);
|
||||
$st->bindParam(1, $system_param);
|
||||
$st->execute();
|
||||
|
||||
$data = $st->fetchAll(PDO::FETCH_BOTH);
|
||||
var_dump($data);
|
||||
|
||||
$st->bindParam(1, $system_param2);
|
||||
$st->execute();
|
||||
|
||||
$st->bindColumn('utf8_encoding', $param2);
|
||||
$st->bindColumn('binary_encoding', $param3);
|
||||
$row = $st->fetch(PDO::FETCH_BOUND);
|
||||
if ($param2 != $utf8_param)
|
||||
echo "$param2\n";
|
||||
if (bin2hex($param3) != '80838790a9')
|
||||
echo "$param3\n";
|
||||
|
||||
dropTable($cnn, $tbname);
|
||||
unset($st);
|
||||
|
|
|
@ -3,7 +3,7 @@ Test some basics of Azure AD Access Token support
|
|||
--DESCRIPTION--
|
||||
This test also expects certain exceptions to be thrown under some conditions.
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc');
|
||||
<?php require('skipif_azure.inc');
|
||||
require('skipif_azure_ad_acess_token.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
|
|
@ -34,22 +34,6 @@ if ($stmt === false) {
|
|||
|
||||
unset($conn);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Test Azure AD with integrated authentication. This should fail because
|
||||
// we don't support it.
|
||||
//
|
||||
$connectionInfo = "Authentication = ActiveDirectoryIntegrated; TrustServerCertificate = true;";
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server ; $connectionInfo");
|
||||
echo "Connected successfully with Authentication=ActiveDirectoryIntegrated.\n";
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
echo "Could not connect with Authentication=ActiveDirectoryIntegrated.\n";
|
||||
print_r($e->getMessage());
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Test Azure AD on an Azure database instance. Replace $azureServer, etc with
|
||||
// your credentials to test, or this part is skipped.
|
||||
|
@ -95,6 +79,4 @@ if ($azureServer != 'TARGET_AD_SERVER') {
|
|||
--EXPECTF--
|
||||
Connected successfully with Authentication=SqlPassword.
|
||||
string(1) "%d"
|
||||
Could not connect with Authentication=ActiveDirectoryIntegrated.
|
||||
SQLSTATE[IMSSP]: Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectoryServicePrincipal is supported.
|
||||
%s with Authentication=ActiveDirectoryPassword.
|
||||
|
|
|
@ -18,58 +18,6 @@ function verifyErrorMessage($exception, $expectedError, $msg)
|
|||
}
|
||||
}
|
||||
|
||||
function connectWithInvalidOptions()
|
||||
{
|
||||
global $server;
|
||||
|
||||
$message = 'AzureAD Managed Identity test: expected to fail with ';
|
||||
$expectedError = 'When using ActiveDirectoryMsi Authentication, PWD must be NULL. UID can be NULL, but if not, an empty string is not accepted';
|
||||
|
||||
$uid = '';
|
||||
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
|
||||
$testCase = 'empty UID provided';
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server; $connectionInfo", $uid);
|
||||
echo $message . $testCase . PHP_EOL;
|
||||
} catch(PDOException $e) {
|
||||
verifyErrorMessage($e, $expectedError, $testCase);
|
||||
}
|
||||
unset($connectionInfo);
|
||||
|
||||
$pwd = '';
|
||||
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
|
||||
$testCase = 'empty PWD provided';
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server; $connectionInfo", null, $pwd);
|
||||
echo $message . $testCase . PHP_EOL;
|
||||
} catch(PDOException $e) {
|
||||
verifyErrorMessage($e, $expectedError, $testCase);
|
||||
}
|
||||
unset($connectionInfo);
|
||||
|
||||
$pwd = 'dummy';
|
||||
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
|
||||
$testCase = 'PWD provided';
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server; $connectionInfo", null, $pwd);
|
||||
echo $message . $testCase . PHP_EOL;
|
||||
} catch(PDOException $e) {
|
||||
verifyErrorMessage($e, $expectedError, $testCase);
|
||||
}
|
||||
unset($connectionInfo);
|
||||
|
||||
$expectedError = 'When using Azure AD Access Token, the connection string must not contain UID, PWD, or Authentication keywords.';
|
||||
$connectionInfo = "Authentication = ActiveDirectoryMsi; AccessToken = '123';";
|
||||
$testCase = 'AccessToken option';
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server; $connectionInfo");
|
||||
echo $message . $testCase . PHP_EOL;
|
||||
} catch(PDOException $e) {
|
||||
verifyErrorMessage($e, $expectedError, $testCase);
|
||||
}
|
||||
unset($connectionInfo);
|
||||
}
|
||||
|
||||
function connectInvalidServer()
|
||||
{
|
||||
global $server, $driver, $uid, $pwd;
|
||||
|
@ -102,9 +50,6 @@ function connectInvalidServer()
|
|||
|
||||
require_once('MsSetup.inc');
|
||||
|
||||
// Test some error conditions
|
||||
connectWithInvalidOptions();
|
||||
|
||||
// Make a connection to an invalid server
|
||||
connectInvalidServer();
|
||||
|
||||
|
|
|
@ -8,21 +8,17 @@ the error message of one test case is not the same.
|
|||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once('MsSetup.inc');
|
||||
require_once('MsCommon_mid-refactor.inc');
|
||||
|
||||
$msodbcsqlMaj = "";
|
||||
$hgsEnabled = true;
|
||||
$hgsEnabled = isServerHGSEnabled();
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server", $uid, $pwd);
|
||||
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)['DriverVer'];
|
||||
$version = explode(".", $msodbcsqlVer);
|
||||
$msodbcsqlMaj = $version[0];
|
||||
|
||||
// Next, check if the server is HGS enabled
|
||||
$serverInfo = $conn->getAttribute(PDO::ATTR_SERVER_INFO);
|
||||
if (strpos($serverInfo['SQLServerName'], 'PHPHGS') === false) {
|
||||
$hgsEnabled = false;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo "Failed to connect\n";
|
||||
print_r($e->getMessage());
|
||||
|
@ -32,7 +28,6 @@ try {
|
|||
testColumnEncryption($server, $uid, $pwd, $msodbcsqlMaj);
|
||||
echo "Done";
|
||||
|
||||
|
||||
function verifyOutput($PDOerror, $expected, $caseNum)
|
||||
{
|
||||
if (strpos($PDOerror->getMessage(), $expected) === false) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
--TEST--
|
||||
Test PDO::__Construct by passing connection options and attributes.
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
<?php require('skipif_azure.inc');
|
||||
require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
--TEST--
|
||||
Test PDO::__Construct by passing connection options
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
<?php require('skipif_azure.inc');
|
||||
require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
|
|
@ -40,7 +40,7 @@ try {
|
|||
|
||||
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
string\(3\) "200"
|
||||
string\(3\) "102"
|
||||
(string\(0\) ""|bool\(false\))
|
||||
--EXPECT--
|
||||
string(3) "200"
|
||||
string(3) "102"
|
||||
string(0) ""
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
--TEST--
|
||||
Confirm that PDO::ATTR_ERRMODE value should be restored whether PDO::lastInsertId() call succeeded or not.
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
try {
|
||||
$conn = connect();
|
||||
|
||||
// create temporary tables
|
||||
createTable($conn, "table1", array(new columnMeta("int", "id", "IDENTITY(100,2)"), "val" => "int"));
|
||||
createTable($conn, "table2", array(new columnMeta("int", "id", "IDENTITY(200,2)"), "val" => "int"));
|
||||
createTable($conn, "table3", array("id" => "int", "val" => "int"));
|
||||
|
||||
insertRow($conn, "table1", array("val" => 1), "exec");
|
||||
insertRow($conn, "table2", array("val" => 2), "exec");
|
||||
$conn->lastInsertId();
|
||||
var_dump($conn->getAttribute(PDO::ATTR_ERRMODE));
|
||||
|
||||
insertRow($conn, "table2", array("val" => 3), "exec");
|
||||
insertRow($conn, "table1", array("val" => 4), "exec");
|
||||
$conn->lastInsertId();
|
||||
var_dump($conn->getAttribute(PDO::ATTR_ERRMODE));
|
||||
|
||||
// Should restore original value even if PDO::lastInsertId() failed.
|
||||
insertRow($conn, "table3", array("id" => 1, "val" => 1), "exec");
|
||||
$conn->lastInsertId();
|
||||
var_dump($conn->getAttribute(PDO::ATTR_ERRMODE));
|
||||
|
||||
dropTable($conn, "table1");
|
||||
dropTable($conn, "table2");
|
||||
dropTable($conn, "table3");
|
||||
|
||||
// Should trigger exception
|
||||
$tsql = "SELECT * FROM dummy";
|
||||
$conn->exec($tsql);
|
||||
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
print_r($e->getMessage());
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
int\(2\)
|
||||
int\(2\)
|
||||
int\(2\)
|
||||
.*Invalid object name \'dummy\'\.
|
|
@ -47,17 +47,21 @@ try {
|
|||
print_r($row);
|
||||
|
||||
//with emulate prepare and no bind param options
|
||||
print_r("Prepare with emulate prepare and no bindParam options:\n");
|
||||
if (!isAEConnected()) {
|
||||
$options = array(PDO::ATTR_EMULATE_PREPARES => true);
|
||||
} else {
|
||||
$options = array(PDO::ATTR_EMULATE_PREPARES => false);
|
||||
}
|
||||
$stmt = prepareStmt($conn, $query, $options);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
if ($stmt->rowCount() == 0) {
|
||||
print_r("No results for this query\n");
|
||||
|
||||
print_r("Prepare with emulate prepare and no bindParam options:\n");
|
||||
// This test only makes sense without AE because the default encoding is PDO::SQLSRV_ENCODING_UTF8
|
||||
if (!isAEConnected()) {
|
||||
$stmt = prepareStmt($conn, $query, $options);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($stmt->rowCount() != 0) {
|
||||
print_r("Do not expect results for this query!\n");
|
||||
print_r($row);
|
||||
}
|
||||
}
|
||||
|
||||
//with emulate prepare and SQLSRV_ENCODING_UTF8
|
||||
|
@ -117,7 +121,6 @@ Array
|
|||
[age] => 30
|
||||
)
|
||||
Prepare with emulate prepare and no bindParam options:
|
||||
No results for this query
|
||||
Prepare with emulate prepare and SQLSRV_ENCODING_UTF8:
|
||||
Array
|
||||
(
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
--TEST--
|
||||
Prepare with emulate prepare and binding uft8 characters
|
||||
--DESCRIPTION--
|
||||
This is the same as pdo_prepare_emulatePrepare_unicode.phpt except that
|
||||
PDO::ATTR_EMULATE_PREPARES at the connection level
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon_mid-refactor.inc');
|
||||
|
||||
function prepareStmt($conn, $query, $prepareOptions = array(), $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = null)
|
||||
{
|
||||
$name = "가각";
|
||||
if (!isColEncrypted()) {
|
||||
$stmt = $conn->prepare($query, $prepareOptions);
|
||||
$stmt->bindParam(':name', $name, $dataType, $length, $driverOptions);
|
||||
} else {
|
||||
$status = 1;
|
||||
$stmt = $conn->prepare($query, $prepareOptions);
|
||||
$stmt->bindParam(':name', $name, $dataType, $length, $driverOptions);
|
||||
$stmt->bindParam(':status', $status);
|
||||
}
|
||||
$stmt->execute();
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = connect("", array(), PDO::ERRMODE_SILENT);
|
||||
|
||||
$tableName = "users";
|
||||
createTable($conn, $tableName, array("name" => "nvarchar(max)", "status" => "int", "age" => "int"));
|
||||
|
||||
if (!isColEncrypted()) {
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'Belle', 1, 34)");
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'Абрам', 1, 40)");
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'가각', 1, 30)");
|
||||
$query = "SELECT * FROM [$tableName] WHERE name = :name AND status = 1";
|
||||
} else {
|
||||
insertRow($conn, $tableName, array("name" => "Belle", "status" => 1, "age" => 34));
|
||||
insertRow($conn, $tableName, array("name" => "Абрам", "status" => 1, "age" => 40));
|
||||
insertRow($conn, $tableName, array("name" => "가각", "status" => 1, "age" => 30));
|
||||
$query = "SELECT * FROM [$tableName] WHERE name = :name AND status = :status";
|
||||
}
|
||||
|
||||
//without emulate prepare
|
||||
print_r("Prepare without emulate prepare:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
|
||||
if (!isAEConnected()) {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
} else {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
}
|
||||
|
||||
//with emulate prepare and no bind param options
|
||||
print_r("Prepare with emulate prepare and no bindParam options:\n");
|
||||
// This test only makes sense without AE because the default encoding is PDO::SQLSRV_ENCODING_UTF8
|
||||
if (!isAEConnected()) {
|
||||
$stmt = prepareStmt($conn, $query, array());
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($stmt->rowCount() != 0) {
|
||||
print_r("Do not expect results for this query!\n");
|
||||
print_r($row);
|
||||
}
|
||||
}
|
||||
|
||||
//with emulate prepare and SQLSRV_ENCODING_UTF8
|
||||
print_r("Prepare with emulate prepare and SQLSRV_ENCODING_UTF8:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
|
||||
//with emulate prepare and SQLSRV_ENCODING_SYSTEM
|
||||
print_r("Prepare with emulate prepare and and SQLSRV_ENCODING_SYSTEM:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_SYSTEM);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// The combination of Column Encryption and Unix platforms support SQLSRV_ENCODING_SYSTEM because:
|
||||
// With Column Encryption enabled, binding parameters uses exact datatypes as the column definition
|
||||
// the default encoding in Linux and Mac is UTF8
|
||||
$success = true;
|
||||
if (!(strtoupper( substr( php_uname( 's' ),0,3 ) ) === 'WIN') && isAEConnected()) {
|
||||
if ($row['name'] != "가각" || $row['status'] != 1 || $row['age'] != 30) {
|
||||
print_r("Incorrect results retrieved.\n");
|
||||
$success = false;
|
||||
}
|
||||
} else {
|
||||
// the default encoding in Windows is non-UTF8, thus binding UTF8 parameters does not work
|
||||
if ($stmt->rowCount() != 0) {
|
||||
print_r("Binding UTF8 data when encoding is SQLSRV_ENCODING_SYSTEM should not work.\n");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
if ($success) {
|
||||
print_r("Binding UTF8 data with SQLSRV_ENCODING_SYSTEM is tested successfully.\n");
|
||||
}
|
||||
|
||||
//with emulate prepare and encoding SQLSRV_ENCODING_BINARY
|
||||
print_r("Prepare with emulate prepare and encoding SQLSRV_ENCODING_BINARY:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_BINARY);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
if ($stmt->rowCount() == 0) {
|
||||
print_r("No results for this query\n");
|
||||
}
|
||||
|
||||
dropTable($conn, $tableName);
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e->errorInfo);
|
||||
}
|
||||
?>
|
||||
|
||||
--EXPECT--
|
||||
Prepare without emulate prepare:
|
||||
Array
|
||||
(
|
||||
[name] => 가각
|
||||
[status] => 1
|
||||
[age] => 30
|
||||
)
|
||||
Prepare with emulate prepare and no bindParam options:
|
||||
Prepare with emulate prepare and SQLSRV_ENCODING_UTF8:
|
||||
Array
|
||||
(
|
||||
[name] => 가각
|
||||
[status] => 1
|
||||
[age] => 30
|
||||
)
|
||||
Prepare with emulate prepare and and SQLSRV_ENCODING_SYSTEM:
|
||||
Binding UTF8 data with SQLSRV_ENCODING_SYSTEM is tested successfully.
|
||||
Prepare with emulate prepare and encoding SQLSRV_ENCODING_BINARY:
|
||||
No results for this query
|
|
@ -17,15 +17,6 @@ try {
|
|||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "ID", "NOT NULL PRIMARY KEY"), "Policy" => "varchar(2)", "Label" => "varchar(10)", "Budget" => "money"));
|
||||
|
||||
try {
|
||||
$res = $conn1->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
if ($res) {
|
||||
echo "setAttribute should have failed.\n\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$query = "SELECT * FROM [$tableName]";
|
||||
$stmt = $conn1->query($query);
|
||||
|
@ -186,7 +177,6 @@ try {
|
|||
|
||||
?>
|
||||
--EXPECTF--
|
||||
SQLSTATE[IMSSP]: The given attribute is only supported on the PDOStatement object.
|
||||
SQLSTATE[IMSSP]: An invalid attribute was designated on the PDOStatement object.
|
||||
Start inserting data...
|
||||
....Done....
|
||||
|
|
|
@ -6,3 +6,9 @@ require 'MsSetup.inc';
|
|||
if ($daasMode) {
|
||||
die("skip test not applicable in Azure\n");
|
||||
}
|
||||
|
||||
require_once('MsCommon_mid-refactor.inc');
|
||||
if (isServerHGSEnabled()) {
|
||||
die("skip test not applicable in Azure VM\n");
|
||||
}
|
||||
?>
|
|
@ -34,7 +34,7 @@ if ($msodbcsqlMin < 4 and $msodbcsqlMaj == 17) {
|
|||
|
||||
// Get SQL Server
|
||||
$serverInfo = $conn->getAttribute(PDO::ATTR_SERVER_INFO);
|
||||
if (strpos($serverInfo['SQLServerName'], 'PHPHGS') === false) {
|
||||
if (strpos($serverInfo['SQLServerName'], 'HGS') === false) {
|
||||
die("skip Server is not HGS enabled");
|
||||
}
|
||||
?>
|
|
@ -3,31 +3,31 @@ if (!extension_loaded("pdo_sqlsrv")) {
|
|||
die("skip Extension not loaded");
|
||||
}
|
||||
|
||||
require_once( "MsSetup.inc" );
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
$conn = new PDO( "sqlsrv:server = $server ;", $uid, $pwd );
|
||||
if( $conn === false )
|
||||
{
|
||||
die( "skip Could not connect during SKIPIF." );
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:server = $server", $uid, $pwd);
|
||||
} catch (PDOException $e) {
|
||||
die("skip Could not connect during SKIPIF.");
|
||||
}
|
||||
|
||||
// Get process ID. Not the same as the one during the actual test, but
|
||||
// we only need to know the protocol for a particular connection.
|
||||
$stmt = $conn->query( "SELECT @@SPID" );
|
||||
if ( $stmt )
|
||||
{
|
||||
$spid = $stmt->fetch(PDO::FETCH_NUM)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
die( "skip Could not fetch SPID during SKIPIF.");
|
||||
}
|
||||
$tsql = <<<SQL
|
||||
SELECT c.session_id, c.net_transport
|
||||
FROM sys.dm_exec_connections AS c
|
||||
JOIN sys.dm_exec_sessions AS s
|
||||
ON c.session_id = s.session_id
|
||||
WHERE c.session_id = @@SPID;
|
||||
SQL;
|
||||
|
||||
$stmt = $conn->query( "SELECT * FROM sys.dm_exec_connections WHERE session_id = ".$spid);
|
||||
$prot = $stmt->fetchColumn(3);
|
||||
try {
|
||||
// Check the transport protocol for the current connection
|
||||
$stmt = $conn->query($tsql);
|
||||
$prot = $stmt->fetchColumn(1);
|
||||
|
||||
if ($prot != 'TCP')
|
||||
{
|
||||
die("skip Not using a TCP protocol." );
|
||||
if ($prot != 'TCP') {
|
||||
die("skip Not using a TCP protocol.");
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
die("skip Failed to fetch SPID and transport protocol.");
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -94,6 +94,21 @@ function isLocaleDisabled()
|
|||
return ($daasMode || $localeDisabled);
|
||||
}
|
||||
|
||||
function isServerHGSEnabled()
|
||||
{
|
||||
$conn = connect();
|
||||
$tsql = "SELECT @@SERVERNAME";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
|
||||
if (sqlsrv_fetch($stmt)) {
|
||||
$name = sqlsrv_get_field($stmt, 0);
|
||||
if (strpos($name, 'HGS') != false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSQLAzure()
|
||||
{
|
||||
// 'SQL Azure' indicates SQL Database or SQL Data Warehouse
|
||||
|
|
|
@ -7,4 +7,8 @@ if (!extension_loaded("sqlsrv")) {
|
|||
require 'MsSetup.inc';
|
||||
if ($daasMode) die("skip test not applicable in Azure\n");
|
||||
|
||||
require 'MsCommon.inc';
|
||||
if (isServerHGSEnabled()) {
|
||||
die("skip test not applicable in Azure VM\n");
|
||||
}
|
||||
?>
|
|
@ -34,7 +34,7 @@ if ($msodbcsql_min < 4 and $msodbcsql_maj == 17) {
|
|||
|
||||
// Get SQL Server
|
||||
$server_info = sqlsrv_server_info($conn);
|
||||
if (strpos($server_info['SQLServerName'], 'PHPHGS') === false) {
|
||||
if (strpos($server_info['SQLServerName'], 'HGS') === false) {
|
||||
die("skip Server is not HGS enabled");
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -3,35 +3,32 @@ if (!extension_loaded("sqlsrv")) {
|
|||
die("skip Extension not loaded");
|
||||
}
|
||||
|
||||
require_once( "MsSetup.inc" );
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
$connectionInfo = array( "UID"=>$userName, "PWD"=>$userPassword );
|
||||
$connectionInfo = array("UID"=>$userName, "PWD"=>$userPassword);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( $conn === false )
|
||||
{
|
||||
die( "skip Could not connect during SKIPIF." );
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
if ($conn === false) {
|
||||
die("skip Could not connect during SKIPIF.");
|
||||
}
|
||||
|
||||
// Get process ID. Not the same as the one during the actual test, but
|
||||
// we only need to know the protocol for a particular connection.
|
||||
$stmt = sqlsrv_query( $conn, "SELECT @@SPID" );
|
||||
if ( sqlsrv_fetch( $stmt ) )
|
||||
{
|
||||
$spid = sqlsrv_get_field( $stmt, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
die("skip Could not fetch SPID.");
|
||||
}
|
||||
$tsql = <<<SQL
|
||||
SELECT c.session_id, c.net_transport
|
||||
FROM sys.dm_exec_connections AS c
|
||||
JOIN sys.dm_exec_sessions AS s
|
||||
ON c.session_id = s.session_id
|
||||
WHERE c.session_id = @@SPID;
|
||||
SQL;
|
||||
|
||||
$stmt = sqlsrv_query( $conn, "SELECT * FROM sys.dm_exec_connections WHERE session_id = $spid");
|
||||
if ( sqlsrv_fetch( $stmt ) )
|
||||
{
|
||||
$prot = sqlsrv_get_field( $stmt, 3 );
|
||||
if ($prot != 'TCP')
|
||||
{
|
||||
die( "skip Not using a TCP protocol." );
|
||||
// Check the transport protocol for the current connection
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (sqlsrv_fetch($stmt)) {
|
||||
$prot = sqlsrv_get_field($stmt, 1);
|
||||
if ($prot != 'TCP'){
|
||||
die("skip Not using a TCP protocol.");
|
||||
}
|
||||
} else {
|
||||
die("skip Failed to fetch SPID and transport protocol.");
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -3,7 +3,7 @@ Test some basics of Azure AD Access Token support
|
|||
--DESCRIPTION--
|
||||
This test also expects certain exceptions to be thrown under some conditions.
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc');
|
||||
<?php require('skipif_azure.inc');
|
||||
require('skipif_azure_ad_acess_token.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
|
|
@ -34,22 +34,6 @@ if (sqlsrv_fetch($stmt)) {
|
|||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Test Azure AD with integrated authentication. This should fail because
|
||||
// we don't support it.
|
||||
//
|
||||
$connectionInfo = array( "Authentication"=>"ActiveDirectoryIntegrated", "TrustServerCertificate"=>true );
|
||||
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
if ($conn === false) {
|
||||
echo "Could not connect with Authentication=ActiveDirectoryIntegrated.\n";
|
||||
$errors = sqlsrv_errors();
|
||||
print_r($errors[0]);
|
||||
} else {
|
||||
echo "Connected successfully with Authentication=ActiveDirectoryIntegrated.\n";
|
||||
sqlsrv_close($conn);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Test Azure AD on an Azure database instance. Replace $azureServer, etc with
|
||||
// your credentials to test, or this part is skipped.
|
||||
|
@ -99,14 +83,4 @@ if ($azureServer != 'TARGET_AD_SERVER') {
|
|||
--EXPECTF--
|
||||
Connected successfully with Authentication=SqlPassword.
|
||||
string(1) "%d"
|
||||
Could not connect with Authentication=ActiveDirectoryIntegrated.
|
||||
Array
|
||||
(
|
||||
[0] => IMSSP
|
||||
[SQLSTATE] => IMSSP
|
||||
[1] => -62
|
||||
[code] => -62
|
||||
[2] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectoryServicePrincipal is supported.
|
||||
[message] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectoryServicePrincipal is supported.
|
||||
)
|
||||
%s with Authentication=ActiveDirectoryPassword.
|
||||
|
|
|
@ -19,34 +19,6 @@ function verifyErrorMessage($conn, $expectedError, $msg)
|
|||
}
|
||||
}
|
||||
|
||||
function connectWithInvalidOptions()
|
||||
{
|
||||
global $server;
|
||||
|
||||
$expectedError = 'When using ActiveDirectoryMsi Authentication, PWD must be NULL. UID can be NULL, but if not, an empty string is not accepted';
|
||||
|
||||
$connectionInfo = array("UID"=>"", "Authentication" => "ActiveDirectoryMsi");
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
verifyErrorMessage($conn, $expectedError, 'empty UID provided');
|
||||
unset($connectionInfo);
|
||||
|
||||
$connectionInfo = array("PWD"=>"", "Authentication" => "ActiveDirectoryMsi");
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
verifyErrorMessage($conn, $expectedError, 'empty PWD provided');
|
||||
unset($connectionInfo);
|
||||
|
||||
$connectionInfo = array("PWD"=>"pwd", "Authentication" => "ActiveDirectoryMsi");
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
verifyErrorMessage($conn, $expectedError, 'PWD provided');
|
||||
unset($connectionInfo);
|
||||
|
||||
$expectedError = 'When using Azure AD Access Token, the connection string must not contain UID, PWD, or Authentication keywords.';
|
||||
$connectionInfo = array("Authentication"=>"ActiveDirectoryMsi", "AccessToken" => "123");
|
||||
$conn = sqlsrv_connect($server, $connectionInfo);
|
||||
verifyErrorMessage($conn, $expectedError, 'AccessToken option');
|
||||
unset($connectionInfo);
|
||||
}
|
||||
|
||||
function connectInvalidServer()
|
||||
{
|
||||
global $server, $driver, $userName, $userPassword;
|
||||
|
@ -76,9 +48,6 @@ function connectInvalidServer()
|
|||
}
|
||||
}
|
||||
|
||||
// Test some error conditions
|
||||
connectWithInvalidOptions($server);
|
||||
|
||||
// Make a connection to an invalid server
|
||||
connectInvalidServer();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ the error message of one test case is not the same.
|
|||
--FILE--
|
||||
<?php
|
||||
sqlsrv_configure('WarningsReturnAsErrors', 0);
|
||||
require('MsSetup.inc');
|
||||
require('MsCommon.inc');
|
||||
|
||||
$connectionOptions = array("Database"=>$database,"UID"=>$userName, "PWD"=>$userPassword);
|
||||
testColumnEncryption($server, $connectionOptions);
|
||||
|
@ -25,11 +25,7 @@ function testColumnEncryption($server, $connectionOptions)
|
|||
$msodbcsqlMaj = explode(".", $msodbcsql_ver)[0];
|
||||
|
||||
// Next, check if the server is HGS enabled
|
||||
$hgsEnabled = true;
|
||||
$serverInfo = sqlsrv_server_info($conn);
|
||||
if (strpos($serverInfo['SQLServerName'], 'PHPHGS') === false) {
|
||||
$hgsEnabled = false;
|
||||
}
|
||||
$hgsEnabled = isServerHGSEnabled();
|
||||
|
||||
// Only works for ODBC 17
|
||||
$connectionOptions['ColumnEncryption'] = 'Enabled';
|
||||
|
|
111
test/functional/sqlsrv/srv_1307_tvp_non_procedure.phpt
Normal file
111
test/functional/sqlsrv/srv_1307_tvp_non_procedure.phpt
Normal file
|
@ -0,0 +1,111 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed.
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable)
|
||||
{
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname);
|
||||
sqlsrv_query($conn, $dropTableType);
|
||||
sqlsrv_query($conn, "DROP TABLE IF EXISTS [$testTable]");
|
||||
}
|
||||
|
||||
function readData($conn, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM $testTable ORDER BY id";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
while ($result = sqlsrv_fetch($stmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
$ID = sqlsrv_get_field($stmt, 0);
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
$conn = connect();
|
||||
|
||||
$tvpname = 'srv_id_table';
|
||||
$testTable = 'srv_test_table';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE $tvpname AS TABLE(id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
$tsql = "CREATE TABLE $testTable (id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO $testTable SELECT * FROM ?";
|
||||
$params = [
|
||||
[[$tvpname => [[5], [7], [9]]]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO $testTable t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
$params = [
|
||||
[[$tvpname => [[2], [6], [4], [8], [3]]]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_prepare($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
5
|
||||
7
|
||||
9
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
Done
|
119
test/functional/sqlsrv/srv_1307_tvp_non_procedure_schema.phpt
Normal file
119
test/functional/sqlsrv/srv_1307_tvp_non_procedure_schema.phpt
Normal file
|
@ -0,0 +1,119 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed but TVP and table are defined in a different schema
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable, $schema)
|
||||
{
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname, $schema);
|
||||
sqlsrv_query($conn, $dropTableType);
|
||||
sqlsrv_query($conn, "DROP TABLE IF EXISTS [$schema].[$testTable]");
|
||||
sqlsrv_query($conn, "DROP SCHEMA IF EXISTS [$schema]");
|
||||
}
|
||||
|
||||
function readData($conn, $schema, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM [$schema].[$testTable] ORDER BY id";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
while ($result = sqlsrv_fetch($stmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
$ID = sqlsrv_get_field($stmt, 0);
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
$conn = connect();
|
||||
|
||||
$tvpname = 'srv_id_table2';
|
||||
$testTable = 'srv_test_table2';
|
||||
$schema = 'srv schema';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
// Create the schema
|
||||
$tsql = "CREATE SCHEMA [$schema]";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE [$schema].[$tvpname] AS TABLE(id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
$tsql = "CREATE TABLE [$schema].[$testTable] (id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO [$schema].[$testTable] SELECT * FROM ?";
|
||||
$params = [
|
||||
[[$tvpname => [[15], [13], [11]], $schema]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO [$schema].[$testTable] t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
$params = [
|
||||
[[$tvpname => [[10], [16], [14], [12]], $schema]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_prepare($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
11
|
||||
13
|
||||
15
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
Done
|
65
test/functional/sqlsrv/srv_1310_null_varchar.phpt
Normal file
65
test/functional/sqlsrv/srv_1310_null_varchar.phpt
Normal file
|
@ -0,0 +1,65 @@
|
|||
--TEST--
|
||||
GitHub issue 1310 - bind null field as varchar(max) if not binary
|
||||
--DESCRIPTION--
|
||||
The test shows null fields are no longer bound as char(1) if not binary such that it solves both issues 1310 and 1102.
|
||||
Note that this test does not connect with AE enabled because SQLDescribeParam() does not work with these queries.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_versions_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
|
||||
$conn = connect();
|
||||
|
||||
// Issue 1310
|
||||
$query = "SELECT CAST(ISNULL(?, -1) AS INT) AS K";
|
||||
$k = null;
|
||||
$params = array($k);
|
||||
|
||||
$stmt = sqlsrv_prepare($conn, $query, $params);
|
||||
if (!$stmt) {
|
||||
fatalError("Failed to prepare statement (1).");
|
||||
}
|
||||
if (!sqlsrv_execute($stmt)) {
|
||||
fatalError("Failed to execute query (1).");
|
||||
}
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
var_dump($row);
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Issue 1102
|
||||
$query = "DECLARE @d DATETIME = ISNULL(?, GETDATE()); SELECT @d AS D;";
|
||||
$k = null;
|
||||
$params = array($k, null, null, null);
|
||||
|
||||
$options = array('ReturnDatesAsStrings'=> true);
|
||||
$stmt = sqlsrv_query($conn, $query, $params, $options);
|
||||
if (!$stmt) {
|
||||
fatalError("Failed to query statement (2).");
|
||||
}
|
||||
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
var_dump($row);
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
array\(1\) {
|
||||
\["K"\]=>
|
||||
int\(-1\)
|
||||
}
|
||||
array\(1\) {
|
||||
\["D"\]=>
|
||||
string\(23\) "20[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:00.000"
|
||||
}
|
||||
Done
|
112
test/functional/sqlsrv/srv_1329_string_truncation.phpt
Normal file
112
test/functional/sqlsrv/srv_1329_string_truncation.phpt
Normal file
|
@ -0,0 +1,112 @@
|
|||
--TEST--
|
||||
GitHub issue 1329 - string truncation error when binding some parameters as non-nulls the second time
|
||||
--DESCRIPTION--
|
||||
The test shows the same parameters, though bound as nulls in the first insertion, can be bound as non-nulls in the subsequent insertions.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_versions_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
|
||||
$connectionInfo = array('CharacterSet'=>'UTF-8');
|
||||
$conn = AE\connect($connectionInfo);
|
||||
|
||||
dropTable($conn, 'srv_domains');
|
||||
|
||||
$tsql = <<<CREATESQL
|
||||
CREATE TABLE srv_domains (
|
||||
id bigint IDENTITY(1,1) NOT NULL,
|
||||
authority nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
|
||||
base_url_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
regular_not_found_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
invalid_short_url_redirect nvarchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
|
||||
CONSTRAINT PK__srv_domains__3213E83F512B36BA PRIMARY KEY (id))
|
||||
CREATESQL;
|
||||
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
fatalError("failed to create test table");
|
||||
}
|
||||
|
||||
$tsql = <<<INSERTSQL
|
||||
INSERT INTO srv_domains (authority, base_url_redirect, regular_not_found_redirect, invalid_short_url_redirect) VALUES (?, ?, ?, ?)
|
||||
INSERTSQL;
|
||||
|
||||
$authority = 'foo.com';
|
||||
$base = null;
|
||||
$notFound = null;
|
||||
$invalid = null;
|
||||
$params = [&$authority, &$base, &$notFound, &$invalid];
|
||||
$stmt = sqlsrv_prepare($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
fatalError("failed to prepare the insert statement");
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
fatalError("failed to execute the insert statement (1)");
|
||||
}
|
||||
|
||||
$authority = 'detached-with-ředirects.com';
|
||||
$base = 'fŏő.com';
|
||||
$notFound = 'baŗ.com';
|
||||
$invalid = null;
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
fatalError("failed to execute the insert statement (2)");
|
||||
}
|
||||
|
||||
$authority = 'Őther-redirects.com';
|
||||
$base = 'fooš.com';
|
||||
$notFound = null;
|
||||
$invalid = 'ŷëå';
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
fatalError("failed to execute the insert statement (3)");
|
||||
}
|
||||
|
||||
// fetch the data
|
||||
$tsql = "SELECT * FROM srv_domains";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
fatalError("failed to run select query");
|
||||
}
|
||||
while ($row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
print_r($row);
|
||||
}
|
||||
|
||||
dropTable($conn, 'srv_domains');
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Array
|
||||
(
|
||||
[0] => 1
|
||||
[1] => foo.com
|
||||
[2] =>
|
||||
[3] =>
|
||||
[4] =>
|
||||
)
|
||||
Array
|
||||
(
|
||||
[0] => 2
|
||||
[1] => detached-with-ředirects.com
|
||||
[2] => fŏő.com
|
||||
[3] => baŗ.com
|
||||
[4] =>
|
||||
)
|
||||
Array
|
||||
(
|
||||
[0] => 3
|
||||
[1] => Őther-redirects.com
|
||||
[2] => fooš.com
|
||||
[3] =>
|
||||
[4] => ŷëå
|
||||
)
|
||||
Done
|
Loading…
Reference in a new issue