Skip to content

Commit 16459fa

Browse files
authored
Merge pull request #1390 from microsoft/dev
Dev - 5.10.1
2 parents 88a9c0e + 109b8bc commit 16459fa

5 files changed

Lines changed: 137 additions & 56 deletions

File tree

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file.
33

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

6+
## 5.10.1 - 2022-05-12
7+
Updated PECL release packages. Here is the list of updates:
8+
9+
### Added
10+
- Pull request [#1382](https://github.com/microsoft/msphpsql/pull/1382) - Support for ActiveDirectoryIntegrated authentication
11+
12+
### Fixed
13+
- Pull request [#1374](https://github.com/microsoft/msphpsql/pull/1374) - Fixed ActiveDirectoryMsi Authentication behavior when specified UID by laclefyoshi
14+
15+
### Limitations
16+
- No support for inout / output params when using sql_variant type
17+
- No support for inout / output params when formatting decimal values
18+
- 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
19+
- Always Encrypted requires [MS ODBC Driver 17+](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server)
20+
- Only Windows Certificate Store and Azure Key Vault are supported. Custom Keystores are not yet supported
21+
- Issue [#716](https://github.com/Microsoft/msphpsql/issues/716) - With Always Encrypted enabled, named parameters in subqueries are not supported
22+
- Issue [#1050](https://github.com/microsoft/msphpsql/issues/1050) - With Always Encrypted enabled, insertion requires the column list for any tables with identity columns
23+
- [Always Encrypted limitations](https://docs.microsoft.com/sql/connect/php/using-always-encrypted-php-drivers#limitations-of-the-php-drivers-when-using-always-encrypted)
24+
25+
### Known Issues
26+
- 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.
27+
- Connection pooling on Linux or macOS is not recommended with [unixODBC](http://www.unixodbc.org/) < 2.3.7
28+
- When pooling is enabled in Linux or macOS
29+
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostic information, such as error messages, warnings and informative messages
30+
- 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)
31+
32+
633
## 5.10.0 - 2022-01-31
734
Updated PECL release packages. Here is the list of updates:
835

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ install:
7373
$client.Headers.Add("user-agent", "appveyor-ci-build2");
7474
$client.DownloadFile("http://windows.php.net/downloads/releases/sha256sum.txt", "c:\projects\sha256sum.txt");
7575
If ($env:PHP_MINOR_VER -Match "latest") {
76-
$env:PHP_VERSION=type c:\projects\sha256sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } ;
76+
$env:PHP_VERSION=type c:\projects\sha256sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } | Select -First 1 ;
7777
} Else {
7878
$env:PHP_VERSION=$env:PHP_MAJOR_VER + '.' + $env:PHP_MINOR_VER;
7979
}

source/shared/core_conn.cpp

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD
7474
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size);
7575
std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver);
7676
#ifndef _WIN32
77-
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver);
77+
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver);
7878
#endif
7979

8080
}
@@ -184,29 +184,29 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
184184
else {
185185
// ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13
186186
// If column encryption is enabled, check up to ODBC 18
187-
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
188-
ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13;
189-
187+
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
188+
ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13;
189+
190190
ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN;
191-
for (auto &d : drivers) {
191+
for (auto &d : drivers) {
192192
std::string driver_name = get_ODBC_driver_name(d);
193193
#ifndef _WIN32
194-
if (core_search_odbc_driver_unix(d)) {
194+
if (core_search_odbc_driver_unix(d)) {
195195
// now append the driver name to the connection string
196196
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str);
197-
r = core_odbc_connect(conn, conn_str, is_pooled);
198-
break;
199-
}
200-
#else
197+
r = core_odbc_connect(conn, conn_str, is_pooled);
198+
break;
199+
}
200+
#else
201201
std::string conn_str_driver = conn_str; // use a copy of conn_str instead
202-
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver);
202+
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver);
203203
r = core_odbc_connect(conn, conn_str_driver, is_pooled);
204204
if (SQL_SUCCEEDED(r) || !core_compare_error_state(conn, r, "IM002")) {
205205
// something else went wrong, exit the loop now other than ODBC driver not found
206206
break;
207-
}
208-
#endif
209-
else if (d == last_version) {
207+
}
208+
#endif
209+
else if (d == last_version) {
210210
// if column encryption is enabled, throw the exception related to column encryption
211211
CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
212212
throw core::CoreException();
@@ -216,7 +216,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
216216
CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
217217
throw core::CoreException();
218218
}
219-
}
219+
}
220220
}
221221
}
222222

@@ -676,8 +676,8 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
676676

677677
try {
678678
// Since connection options access token and authentication cannot coexist, check if both of them are used.
679-
// If access token is specified, check UID and PWD as well.
680-
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
679+
// If access token is specified, check UID and PWD as well.
680+
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
681681
if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) {
682682
bool invalidOptions = false;
683683

@@ -697,8 +697,10 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
697697
// Check if Authentication is ActiveDirectoryMSI because we have to handle this case differently
698698
// https://docs.microsoft.com/en-ca/azure/active-directory/managed-identities-azure-resources/overview
699699
bool activeDirectoryMSI = false;
700+
bool activeDirectoryIntegrated = false;
700701
if (authentication_option_used) {
701702
const char aadMSIoption[] = "ActiveDirectoryMSI";
703+
const char addIntegratedOption[] = "ActiveDirectoryIntegrated";
702704
zval* auth_option = NULL;
703705
auth_option = zend_hash_index_find(options, SQLSRV_CONN_OPTION_AUTHENTICATION);
704706

@@ -707,17 +709,37 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
707709
option = Z_STRVAL_P(auth_option);
708710
}
709711

710-
if (option != NULL && !stricmp(option, aadMSIoption)) {
711-
activeDirectoryMSI = true;
712+
if (option != NULL) {
713+
// Check if the user is using ActiveDirectoryMSI or ActiveDirectoryIntegrated
714+
if (!stricmp(option, aadMSIoption)) {
715+
activeDirectoryMSI = true;
716+
}
717+
else if (!stricmp(option, addIntegratedOption)) {
718+
activeDirectoryIntegrated = true;
719+
}
712720
}
713721
}
714722

715723
// Add the server name
716724
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
717725

726+
// Check uid when Authentication is ActiveDirectoryMSI
727+
// uid can be specified when using user-assigned identity
728+
if (activeDirectoryMSI) {
729+
if (uid != NULL && strnlen_s(uid) > 0) {
730+
bool escaped = core_is_conn_opt_value_escaped(uid, strnlen_s(uid));
731+
CHECK_CUSTOM_ERROR(!escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED) {
732+
throw core::CoreException();
733+
}
734+
735+
common_conn_str_append_func(ODBCConnOptions::UID, uid, strnlen_s(uid), connection_string);
736+
}
737+
}
738+
718739
// If uid is not present then we use trusted connection -- but not when connecting
719740
// using the access token or Authentication is ActiveDirectoryMSI
720-
if (!access_token_used && !activeDirectoryMSI) {
741+
// ActiveDirectoryIntegrated does not need UID or PWD
742+
if (!access_token_used && !activeDirectoryMSI && !activeDirectoryIntegrated) {
721743
if (uid == NULL || strnlen_s(uid) == 0) {
722744
connection_string += CONNECTION_OPTION_NO_CREDENTIALS; // "Trusted_Connection={Yes};"
723745
}
@@ -963,29 +985,29 @@ std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver)
963985
// Parameters:
964986
// driver - a valid value in enum ODBC_DRIVER
965987
// Return - a boolean flag that indicates if the specified driver version is found or not
966-
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver)
967-
{
968-
char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size
969-
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
970-
WORD cbBufOut;
971-
char *pszBuf = szBuf;
972-
973-
// get all the names of the installed drivers delimited by null characters
974-
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut))
975-
return false;
976-
977-
// search for the derived ODBC driver name based on the given version
978-
std::string driver_name = get_ODBC_driver_name(driver);
979-
do
980-
{
981-
if (strstr(pszBuf, driver_name.c_str()) != 0)
982-
return true;
983-
984-
// get the next driver
985-
pszBuf = strchr(pszBuf, '\0') + 1;
986-
} while (pszBuf[1] != '\0'); // end when there are two consecutive null characters
987-
988-
return false;
988+
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver)
989+
{
990+
char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size
991+
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
992+
WORD cbBufOut;
993+
char *pszBuf = szBuf;
994+
995+
// get all the names of the installed drivers delimited by null characters
996+
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut))
997+
return false;
998+
999+
// search for the derived ODBC driver name based on the given version
1000+
std::string driver_name = get_ODBC_driver_name(driver);
1001+
do
1002+
{
1003+
if (strstr(pszBuf, driver_name.c_str()) != 0)
1004+
return true;
1005+
1006+
// get the next driver
1007+
pszBuf = strchr(pszBuf, '\0') + 1;
1008+
} while (pszBuf[1] != '\0'); // end when there are two consecutive null characters
1009+
1010+
return false;
9891011
}
9901012
#endif // !_WIN32
9911013

@@ -1018,15 +1040,15 @@ void driver_set_func::func(_In_ connection_option const* option, _In_ zval* valu
10181040

10191041
// Check if the user provided driver_option matches any of the acceptable driver names
10201042
std::string driver_option(val_str, val_len);
1021-
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
1022-
1023-
conn->driver_version = ODBC_DRIVER::VER_UNKNOWN;
1024-
for (auto &d : drivers) {
1043+
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
1044+
1045+
conn->driver_version = ODBC_DRIVER::VER_UNKNOWN;
1046+
for (auto &d : drivers) {
10251047
std::string name = get_ODBC_driver_name(d);
1026-
if (!driver_option.compare(name)) {
1027-
conn->driver_version = d;
1028-
break;
1029-
}
1048+
if (!driver_option.compare(name)) {
1049+
conn->driver_version = d;
1050+
break;
1051+
}
10301052
}
10311053

10321054
CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) {

source/shared/version.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
// Increase Patch for backward compatible fixes.
2828
#define SQLVERSION_MAJOR 5
2929
#define SQLVERSION_MINOR 10
30-
#define SQLVERSION_PATCH 0
30+
#define SQLVERSION_PATCH 1
3131
#define SQLVERSION_BUILD 0
3232

3333
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
@@ -59,7 +59,7 @@
5959
#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
6060

6161
// PECL package version ('-' or '+' is not allowed) - to support Pickle do not use macros below
62-
#define PHP_SQLSRV_VERSION "5.10.0"
63-
#define PHP_PDO_SQLSRV_VERSION "5.10.0"
62+
#define PHP_SQLSRV_VERSION "5.10.1"
63+
#define PHP_PDO_SQLSRV_VERSION "5.10.1"
6464

6565
#endif // VERSION_H

test/functional/pdo_sqlsrv/pdo_azure_ad_managed_identity.phpt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,44 @@ function connectInvalidServer()
4848
}
4949
}
5050

51+
function connectInvalidServerWithUser()
52+
{
53+
global $server, $driver, $uid, $pwd;
54+
55+
try {
56+
$conn = new PDO("sqlsrv:server = $server; driver=$driver;", $uid, $pwd);
57+
58+
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
59+
$version = explode(".", $msodbcsqlVer);
60+
61+
if ($version[0] < 17 || $version[1] < 3) {
62+
//skip the rest of this test, which requires ODBC driver 17.3 or above
63+
return;
64+
}
65+
unset($conn);
66+
67+
// Try connecting to an invalid server, should get an exception from ODBC
68+
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
69+
$user = "user";
70+
$testCase = 'invalidServer';
71+
try {
72+
$conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", $user, null);
73+
echo $message . $testCase . PHP_EOL;
74+
} catch(PDOException $e) {
75+
// TODO: check the exception message here
76+
}
77+
} catch(PDOException $e) {
78+
print_r($e->getMessage());
79+
}
80+
}
81+
5182
require_once('MsSetup.inc');
5283

5384
// Make a connection to an invalid server
5485
connectInvalidServer();
86+
connectInvalidServerWithUser();
5587

5688
echo "Done\n";
5789
?>
5890
--EXPECT--
59-
Done
91+
Done

0 commit comments

Comments
 (0)