commit
16459fab38
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
|
||||||
|
## 5.10.1 - 2022-05-12
|
||||||
|
Updated PECL release packages. Here is the list of updates:
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Pull request [#1382](https://github.com/microsoft/msphpsql/pull/1382) - Support for ActiveDirectoryIntegrated authentication
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Pull request [#1374](https://github.com/microsoft/msphpsql/pull/1374) - Fixed ActiveDirectoryMsi Authentication behavior when specified UID by laclefyoshi
|
||||||
|
|
||||||
|
### 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 - 2022-01-31
|
## 5.10.0 - 2022-01-31
|
||||||
Updated PECL release packages. Here is the list of updates:
|
Updated PECL release packages. Here is the list of updates:
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ install:
|
||||||
$client.Headers.Add("user-agent", "appveyor-ci-build2");
|
$client.Headers.Add("user-agent", "appveyor-ci-build2");
|
||||||
$client.DownloadFile("http://windows.php.net/downloads/releases/sha256sum.txt", "c:\projects\sha256sum.txt");
|
$client.DownloadFile("http://windows.php.net/downloads/releases/sha256sum.txt", "c:\projects\sha256sum.txt");
|
||||||
If ($env:PHP_MINOR_VER -Match "latest") {
|
If ($env:PHP_MINOR_VER -Match "latest") {
|
||||||
$env:PHP_VERSION=type c:\projects\sha256sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } ;
|
$env:PHP_VERSION=type c:\projects\sha256sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } | Select -First 1 ;
|
||||||
} Else {
|
} Else {
|
||||||
$env:PHP_VERSION=$env:PHP_MAJOR_VER + '.' + $env:PHP_MINOR_VER;
|
$env:PHP_VERSION=$env:PHP_MAJOR_VER + '.' + $env:PHP_MINOR_VER;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD
|
||||||
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size);
|
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size);
|
||||||
std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver);
|
std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver);
|
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -184,29 +184,29 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
else {
|
else {
|
||||||
// ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13
|
// ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13
|
||||||
// If column encryption is enabled, check up to ODBC 18
|
// If column encryption is enabled, check up to ODBC 18
|
||||||
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
|
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
|
||||||
ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13;
|
ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13;
|
||||||
|
|
||||||
ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN;
|
ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN;
|
||||||
for (auto &d : drivers) {
|
for (auto &d : drivers) {
|
||||||
std::string driver_name = get_ODBC_driver_name(d);
|
std::string driver_name = get_ODBC_driver_name(d);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if (core_search_odbc_driver_unix(d)) {
|
if (core_search_odbc_driver_unix(d)) {
|
||||||
// now append the driver name to the connection string
|
// now append the driver name to the connection string
|
||||||
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str);
|
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str);
|
||||||
r = core_odbc_connect(conn, conn_str, is_pooled);
|
r = core_odbc_connect(conn, conn_str, is_pooled);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
std::string conn_str_driver = conn_str; // use a copy of conn_str instead
|
std::string conn_str_driver = conn_str; // use a copy of conn_str instead
|
||||||
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver);
|
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver);
|
||||||
r = core_odbc_connect(conn, conn_str_driver, is_pooled);
|
r = core_odbc_connect(conn, conn_str_driver, is_pooled);
|
||||||
if (SQL_SUCCEEDED(r) || !core_compare_error_state(conn, r, "IM002")) {
|
if (SQL_SUCCEEDED(r) || !core_compare_error_state(conn, r, "IM002")) {
|
||||||
// something else went wrong, exit the loop now other than ODBC driver not found
|
// something else went wrong, exit the loop now other than ODBC driver not found
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (d == last_version) {
|
else if (d == last_version) {
|
||||||
// if column encryption is enabled, throw the exception related to column encryption
|
// if column encryption is enabled, throw the exception related to column encryption
|
||||||
CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
|
CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
|
@ -216,7 +216,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,8 +676,8 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Since connection options access token and authentication cannot coexist, check if both of them are used.
|
// Since connection options access token and authentication cannot coexist, check if both of them are used.
|
||||||
// If access token is specified, check UID and PWD as well.
|
// If access token is specified, check UID and PWD as well.
|
||||||
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
|
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
|
||||||
if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) {
|
if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) {
|
||||||
bool invalidOptions = false;
|
bool invalidOptions = false;
|
||||||
|
|
||||||
|
@ -697,8 +697,10 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
// Check if Authentication is ActiveDirectoryMSI because we have to handle this case differently
|
// 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
|
// https://docs.microsoft.com/en-ca/azure/active-directory/managed-identities-azure-resources/overview
|
||||||
bool activeDirectoryMSI = false;
|
bool activeDirectoryMSI = false;
|
||||||
|
bool activeDirectoryIntegrated = false;
|
||||||
if (authentication_option_used) {
|
if (authentication_option_used) {
|
||||||
const char aadMSIoption[] = "ActiveDirectoryMSI";
|
const char aadMSIoption[] = "ActiveDirectoryMSI";
|
||||||
|
const char addIntegratedOption[] = "ActiveDirectoryIntegrated";
|
||||||
zval* auth_option = NULL;
|
zval* auth_option = NULL;
|
||||||
auth_option = zend_hash_index_find(options, SQLSRV_CONN_OPTION_AUTHENTICATION);
|
auth_option = zend_hash_index_find(options, SQLSRV_CONN_OPTION_AUTHENTICATION);
|
||||||
|
|
||||||
|
@ -707,17 +709,37 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
option = Z_STRVAL_P(auth_option);
|
option = Z_STRVAL_P(auth_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option != NULL && !stricmp(option, aadMSIoption)) {
|
if (option != NULL) {
|
||||||
activeDirectoryMSI = true;
|
// Check if the user is using ActiveDirectoryMSI or ActiveDirectoryIntegrated
|
||||||
|
if (!stricmp(option, aadMSIoption)) {
|
||||||
|
activeDirectoryMSI = true;
|
||||||
|
}
|
||||||
|
else if (!stricmp(option, addIntegratedOption)) {
|
||||||
|
activeDirectoryIntegrated = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the server name
|
// Add the server name
|
||||||
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
|
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
|
||||||
|
|
||||||
|
// Check uid when Authentication is ActiveDirectoryMSI
|
||||||
|
// uid can be specified when using user-assigned identity
|
||||||
|
if (activeDirectoryMSI) {
|
||||||
|
if (uid != NULL && strnlen_s(uid) > 0) {
|
||||||
|
bool escaped = core_is_conn_opt_value_escaped(uid, strnlen_s(uid));
|
||||||
|
CHECK_CUSTOM_ERROR(!escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
common_conn_str_append_func(ODBCConnOptions::UID, uid, strnlen_s(uid), connection_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If uid is not present then we use trusted connection -- but not when connecting
|
// If uid is not present then we use trusted connection -- but not when connecting
|
||||||
// using the access token or Authentication is ActiveDirectoryMSI
|
// using the access token or Authentication is ActiveDirectoryMSI
|
||||||
if (!access_token_used && !activeDirectoryMSI) {
|
// ActiveDirectoryIntegrated does not need UID or PWD
|
||||||
|
if (!access_token_used && !activeDirectoryMSI && !activeDirectoryIntegrated) {
|
||||||
if (uid == NULL || strnlen_s(uid) == 0) {
|
if (uid == NULL || strnlen_s(uid) == 0) {
|
||||||
connection_string += CONNECTION_OPTION_NO_CREDENTIALS; // "Trusted_Connection={Yes};"
|
connection_string += CONNECTION_OPTION_NO_CREDENTIALS; // "Trusted_Connection={Yes};"
|
||||||
}
|
}
|
||||||
|
@ -963,29 +985,29 @@ std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver)
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// driver - a valid value in enum ODBC_DRIVER
|
// driver - a valid value in enum ODBC_DRIVER
|
||||||
// Return - a boolean flag that indicates if the specified driver version is found or not
|
// Return - a boolean flag that indicates if the specified driver version is found or not
|
||||||
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver)
|
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver)
|
||||||
{
|
{
|
||||||
char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size
|
char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size
|
||||||
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
|
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
|
||||||
WORD cbBufOut;
|
WORD cbBufOut;
|
||||||
char *pszBuf = szBuf;
|
char *pszBuf = szBuf;
|
||||||
|
|
||||||
// get all the names of the installed drivers delimited by null characters
|
// get all the names of the installed drivers delimited by null characters
|
||||||
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut))
|
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// search for the derived ODBC driver name based on the given version
|
// search for the derived ODBC driver name based on the given version
|
||||||
std::string driver_name = get_ODBC_driver_name(driver);
|
std::string driver_name = get_ODBC_driver_name(driver);
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (strstr(pszBuf, driver_name.c_str()) != 0)
|
if (strstr(pszBuf, driver_name.c_str()) != 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// get the next driver
|
// get the next driver
|
||||||
pszBuf = strchr(pszBuf, '\0') + 1;
|
pszBuf = strchr(pszBuf, '\0') + 1;
|
||||||
} while (pszBuf[1] != '\0'); // end when there are two consecutive null characters
|
} while (pszBuf[1] != '\0'); // end when there are two consecutive null characters
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
|
|
||||||
|
@ -1018,15 +1040,15 @@ void driver_set_func::func(_In_ connection_option const* option, _In_ zval* valu
|
||||||
|
|
||||||
// Check if the user provided driver_option matches any of the acceptable driver names
|
// Check if the user provided driver_option matches any of the acceptable driver names
|
||||||
std::string driver_option(val_str, val_len);
|
std::string driver_option(val_str, val_len);
|
||||||
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
|
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
|
||||||
|
|
||||||
conn->driver_version = ODBC_DRIVER::VER_UNKNOWN;
|
conn->driver_version = ODBC_DRIVER::VER_UNKNOWN;
|
||||||
for (auto &d : drivers) {
|
for (auto &d : drivers) {
|
||||||
std::string name = get_ODBC_driver_name(d);
|
std::string name = get_ODBC_driver_name(d);
|
||||||
if (!driver_option.compare(name)) {
|
if (!driver_option.compare(name)) {
|
||||||
conn->driver_version = d;
|
conn->driver_version = d;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) {
|
CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
// Increase Patch for backward compatible fixes.
|
// Increase Patch for backward compatible fixes.
|
||||||
#define SQLVERSION_MAJOR 5
|
#define SQLVERSION_MAJOR 5
|
||||||
#define SQLVERSION_MINOR 10
|
#define SQLVERSION_MINOR 10
|
||||||
#define SQLVERSION_PATCH 0
|
#define SQLVERSION_PATCH 1
|
||||||
#define SQLVERSION_BUILD 0
|
#define SQLVERSION_BUILD 0
|
||||||
|
|
||||||
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
|
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
|
#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
|
// PECL package version ('-' or '+' is not allowed) - to support Pickle do not use macros below
|
||||||
#define PHP_SQLSRV_VERSION "5.10.0"
|
#define PHP_SQLSRV_VERSION "5.10.1"
|
||||||
#define PHP_PDO_SQLSRV_VERSION "5.10.0"
|
#define PHP_PDO_SQLSRV_VERSION "5.10.1"
|
||||||
|
|
||||||
#endif // VERSION_H
|
#endif // VERSION_H
|
||||||
|
|
|
@ -48,12 +48,44 @@ function connectInvalidServer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectInvalidServerWithUser()
|
||||||
|
{
|
||||||
|
global $server, $driver, $uid, $pwd;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conn = new PDO("sqlsrv:server = $server; driver=$driver;", $uid, $pwd);
|
||||||
|
|
||||||
|
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
|
||||||
|
$version = explode(".", $msodbcsqlVer);
|
||||||
|
|
||||||
|
if ($version[0] < 17 || $version[1] < 3) {
|
||||||
|
//skip the rest of this test, which requires ODBC driver 17.3 or above
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unset($conn);
|
||||||
|
|
||||||
|
// Try connecting to an invalid server, should get an exception from ODBC
|
||||||
|
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
|
||||||
|
$user = "user";
|
||||||
|
$testCase = 'invalidServer';
|
||||||
|
try {
|
||||||
|
$conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", $user, null);
|
||||||
|
echo $message . $testCase . PHP_EOL;
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
// TODO: check the exception message here
|
||||||
|
}
|
||||||
|
} catch(PDOException $e) {
|
||||||
|
print_r($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
require_once('MsSetup.inc');
|
require_once('MsSetup.inc');
|
||||||
|
|
||||||
// Make a connection to an invalid server
|
// Make a connection to an invalid server
|
||||||
connectInvalidServer();
|
connectInvalidServer();
|
||||||
|
connectInvalidServerWithUser();
|
||||||
|
|
||||||
echo "Done\n";
|
echo "Done\n";
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Done
|
Done
|
||||||
|
|
Loading…
Reference in a new issue