Compare commits

...

39 commits

Author SHA1 Message Date
Sicong c097a06bb8
Prepare for 5.11.1 release (#1475) 2023-09-05 17:09:51 -07:00
Sicong a7156c72f1 Remove if directives for PDO_ATTR_STRINGIFY_FETCHES 2023-08-31 23:07:57 +00:00
v-makouz 91a916ed8e Bugfix for 1465 (#1471)
Adding a NULL check to prevent SEEGFAULT when parameter parsing fails
2023-08-31 23:00:14 +00:00
Sicong 62beb28932 Change connection options to case insensitive (#1460) 2023-08-31 22:59:45 +00:00
Saki Takamachi 0bc36bf84f Fixed error when using PDO::ATTR_STRINGIFY_FETCHES (#1468)
* fix accept PDO_ATTR_STRINGIFY_FETCHES to set_attr on after 8.1.22 and after 8.2.9 php version

* try to put it back

* fix accept PDO_ATTR_STRINGIFY_FETCHES to set_attr on after 8.1.22 and after 8.2.9 php version
2023-08-31 22:59:28 +00:00
Sicong b1df4089cb Reset type after bind param (#1452)
* Reset type after bind param

Keep the same behavior as 5.9
Issue #1448

* Add unit test, skip for decimal format

* Add unit test, skip for decimal format

* Update unit test
2023-08-31 22:58:31 +00:00
Sicong f6f76d4ac1
Merge pull request #1445 from microsoft/dev
5.11.0 Release
2023-03-06 16:02:42 -08:00
Sicong 3cd248fba8
Update version to 5.11.0 (#1444) 2023-02-28 13:42:04 -08:00
Sicong 4ca08e5ca2
Update azure-pipelines.yml (#1440) 2023-02-27 15:01:36 -08:00
Sicong 11509df4d3
CodeQL warnings fix (#1439) 2023-02-27 15:01:16 -08:00
Sicong e1eb5aa56b
Delete media, files uploaded to Wiki already (#1432) 2023-02-06 10:20:03 -08:00
Sicong cf661d16fb
Set preview for beta release (#1430) 2023-01-27 14:25:15 -08:00
Sicong d1980d3570
Set preview for beta release (#1429) 2023-01-27 11:31:35 -08:00
Sicong afdc9b6e8d
Merge pull request #1427 from microsoft/dev
Merge to master for 5.11.0 beta1 release
2023-01-26 16:13:26 -08:00
Sicong ee5baccde4
Update PHP version (#1428) 2023-01-26 15:12:02 -08:00
Sicong d35c2b0f1b
Update minor version (#1426) 2023-01-25 11:36:17 -08:00
Sicong 04266af58c
Update changelog and version number (#1425) 2023-01-25 10:14:40 -08:00
Sicong ef10e75db8
Merge pull request #1424 from microsoft/8.2_pipeline_test
Merge changes for PHP8.2
2023-01-24 15:20:24 -08:00
absci 86e3fbf466 Remove test that expect an invalid driver error
appveyor have ODBC 13 installed on their test agent
2023-01-24 13:22:32 -08:00
Sicong cea2e8cc69
Update README.md 2023-01-24 11:33:28 -08:00
absci ffafa43095 Slience warning to test 2023-01-23 16:24:43 -08:00
Sicong 2c606fe5d2
Update pdo_azure_ad_managed_identity.phpt 2023-01-23 12:24:11 -08:00
Sicong 35bf2a6bbd
Fix invalid precision value error 2023-01-20 14:38:37 -08:00
Sicong c72e0eb166
Check null for stripos to avoid deprecated warning 2023-01-18 17:41:39 -08:00
Sicong e120fbc422
Enable ctype in build scripts 2023-01-17 13:34:56 -08:00
v-makouz 36b48f02c1 Fix more deprecated issues 2022-12-15 15:20:30 -08:00
v-makouz a5c7069b2e Merge branch 'dev' into php_8.2_tests 2022-12-13 10:12:02 -08:00
v-makouz fd8457cd55 Fix for utf8 test 2022-12-09 13:20:31 -08:00
v-makouz f764dc80ce fix for callable deprecation 2022-12-08 13:59:34 -08:00
v-makouz 93802526c0 Some PDO test fixes 2022-12-08 10:16:14 -08:00
v-makouz efd594b8cf Some test fixes for 8.2 2022-12-05 10:48:34 -08:00
Sicong 3a630fa8b0
Add pipeline trigger (#1410) 2022-09-28 06:25:52 -07:00
Sicong 9ffcb9cb2e
Print logfile to terminal (#1411) 2022-09-17 20:53:48 -07:00
Sicong 15d25ceb76
Fix right truncation issue and add unit test (#1408)
* Reset column size

* Fix clientInfo related test cases (#1407)

* Create pdo_1391_string_truncation_after_short_string.phpt

Regresssion tests: Use the same Statement to insert a row with short strings first, and longer strings second.

- Issue also happens with VARCHAR( >=4002 ). Not just Max or NVARCHAR
- Issues 1391, 1393 (and possibly 1371 ?) might all have the same root cause.

* fetchAll() -> fetch(), we only want 1 row.

* Revert trigger

Co-authored-by: Maarten S <42799774+talkinnl@users.noreply.github.com>
2022-09-13 13:30:58 -07:00
Sicong 4974fe4334
Update bvt tests for PHP 8.2 (#1409) 2022-09-13 11:17:57 -07:00
Sicong ed96718152
Print error messages for managed identity test (#1392)
* Print error messages for managed identity test

* Skip appveyor MSI tests

* Skip msi tests for unix
2022-09-12 10:15:04 -07:00
Sicong e2c15977ef
Fix clientInfo related test cases (#1407) 2022-08-29 18:48:04 -07:00
Sicong 25c8a200a5
Merge pull request #1394 from microsoft/users/GitHubPolicyService/c0a9a24e-0bbd-41f5-8dd4-8275d313c26c
Adding Microsoft SECURITY.MD
2022-06-02 09:08:24 -07:00
microsoft-github-policy-service[bot] b5f7469b02
Microsoft mandatory file 2022-05-31 16:11:56 +00:00
92 changed files with 1335 additions and 1023 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
.vs
.vscode
__pycache__
*.diff
*.exp
*.log
*.sh
*.out
test/**/**/*.php

View file

@ -3,6 +3,87 @@ 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.11.1 - 2023-08-31
Updated PECL release packages. Here is the list of updates:
### Fixed
- Reset type after bind param (#1452)
- Fixed error when using PDO::ATTR_STRINGIFY_FETCHES (#1468) by SakiTakamachi
- Change connection options to case insensitive (#1460)
- Bugfix for 1465 Adding a NULL check (#1471)
### 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.11.0 - 2023-02-28
Updated PECL release packages. Here is the list of updates:
### Added
- Support for PHP 8.2
### Removed
- Support for PHP 7.4
### 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.11.0-beta1 - 2023-01-25
Updated PECL release packages. Here is the list of updates:
### Added
- Support for PHP 8.2
### Fixed
- Pull request [#1408](https://github.com/microsoft/msphpsql/pull/1408) - Fixed right truncation issue, unit test added by talkinnl
### 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.1 - 2022-05-12 ## 5.10.1 - 2022-05-12
Updated PECL release packages. Here is the list of updates: Updated PECL release packages. Here is the list of updates:

View file

@ -1,4 +1,4 @@
Copyright(c) 2022 Microsoft Corporation Copyright(c) 2023 Microsoft Corporation
All rights reserved. All rights reserved.
MIT License MIT License

View file

@ -4,13 +4,13 @@
The [Microsoft Drivers for PHP for Microsoft SQL Server][phpdoc] are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PHP Data Objects (PDO) for accessing data in all editions of SQL Server 2012 and later (including Azure SQL DB). These drivers rely on the [Microsoft ODBC Driver for SQL Server][odbcdoc] to handle the low-level communication with SQL Server. The [Microsoft Drivers for PHP for Microsoft SQL Server][phpdoc] are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PHP Data Objects (PDO) for accessing data in all editions of SQL Server 2012 and later (including Azure SQL DB). These drivers rely on the [Microsoft ODBC Driver for SQL Server][odbcdoc] to handle the low-level communication with SQL Server.
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7.3+ with improvements on both drivers and some limitations. Upcoming [releases][releases] will contain additional functionalities, bug fixes, and more. This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 8.0+ with improvements on both drivers and some limitations. Upcoming [releases][releases] will contain additional functionalities, bug fixes, and more.
## Take our survey ## Take our survey
Thank you for taking the time to participate in the [sentiment survey](https://github.com/microsoft/msphpsql/wiki/Survey-Results). You can continue to help us improve by letting us know how we are doing and how you use [PHP][phpweb]: Thank you for taking the time to participate in the [sentiment survey](https://github.com/microsoft/msphpsql/wiki/Survey-Results). You can continue to help us improve by letting us know how we are doing and how you use [PHP][phpweb]:
<a href="https://aka.ms/mssqlphpsurvey"><img style="float: right;" height="67" width="156" src="https://sqlchoice.blob.core.windows.net/sqlchoice/static/images/survey.png"></a> [**Click here to start the PHP survey**](https://aka.ms/mssqlphpsurvey)
### Status of Most Recent Builds ### Status of Most Recent Builds
| Azure Pipelines (Linux) | AppVeyor (Windows) | Coverage (Windows) | | Azure Pipelines (Linux) | AppVeyor (Windows) | Coverage (Windows) |
@ -37,8 +37,8 @@ Please follow the [Getting started](https://docs.microsoft.com/sql/connect/php/g
For full details on the system requirements for the drivers, see the [system requirements](https://docs.microsoft.com/sql/connect/php/system-requirements-for-the-php-sql-driver) on Microsoft Docs. For full details on the system requirements for the drivers, see the [system requirements](https://docs.microsoft.com/sql/connect/php/system-requirements-for-the-php-sql-driver) on Microsoft Docs.
On the client machine: On the client machine:
- 7.4.x, 8.0.x, 8.1.x - 8.0.x, 8.1.x, 8.2.x
- [Microsoft ODBC Driver 17 or Microsoft ODBC Driver 13][odbcdoc] - [Microsoft ODBC Driver 18, 17 or 13][odbcdoc]
- If using a Web server such as Internet Information Services (IIS) or Apache, it must be configured to run PHP - If using a Web server such as Internet Information Services (IIS) or Apache, it must be configured to run PHP
On the server side, Microsoft SQL Server 2012 and above on Windows are supported, as are Microsoft SQL Server 2016 and above on Linux. On the server side, Microsoft SQL Server 2012 and above on Windows are supported, as are Microsoft SQL Server 2016 and above on Linux.
@ -47,7 +47,7 @@ On the server side, Microsoft SQL Server 2012 and above on Windows are supported
The drivers are distributed as pre-compiled extensions for PHP found on the [releases page][releases]. They are available in thread-safe and non-thread-safe versions, and in 32-bit (Windows only) and 64-bit versions. The source code for the drivers is also available, and you can compile them as thread safe or non-thread-safe versions. The thread safety configuration of your web server will determine which version you need. The drivers are distributed as pre-compiled extensions for PHP found on the [releases page][releases]. They are available in thread-safe and non-thread-safe versions, and in 32-bit (Windows only) and 64-bit versions. The source code for the drivers is also available, and you can compile them as thread safe or non-thread-safe versions. The thread safety configuration of your web server will determine which version you need.
If you choose to build the drivers, you must be able to build PHP 7.* or 8.* without including these extensions. For help building PHP on Windows, see the [official PHP website][phpbuild]. For details on compiling the drivers, see the [documentation](https://github.com/microsoft/msphpsql/blob/master/buildscripts/README.md) -- an example buildscript is provided, but you can also compile the drivers manually. If you choose to build the drivers, you must be able to build PHP 8.* without including these extensions. For help building PHP on Windows, see the [official PHP website][phpbuild]. For details on compiling the drivers, see the [documentation](https://github.com/microsoft/msphpsql/blob/master/buildscripts/README.md) -- an example buildscript is provided, but you can also compile the drivers manually.
To load the drivers, make sure that the driver is in your PHP extension directory and enable it in your PHP installation's php.ini file by adding `extension=php_sqlsrv.dll` and/or `extension=php_pdo_sqlsrv.dll` to the ini file. If necessary, specify the extension directory using `extension_dir`, for example: `extension_dir = "C:\PHP\ext"`. Note that the precompiled binaries have different names -- substitute accordingly in php.ini. For more details on loading the drivers, see [Loading the PHP SQL Driver](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver) on Microsoft Docs. To load the drivers, make sure that the driver is in your PHP extension directory and enable it in your PHP installation's php.ini file by adding `extension=php_sqlsrv.dll` and/or `extension=php_pdo_sqlsrv.dll` to the ini file. If necessary, specify the extension directory using `extension_dir`, for example: `extension_dir = "C:\PHP\ext"`. Note that the precompiled binaries have different names -- substitute accordingly in php.ini. For more details on loading the drivers, see [Loading the PHP SQL Driver](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver) on Microsoft Docs.

41
SECURITY.md Normal file
View file

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View file

@ -12,6 +12,7 @@ environment:
SQLSRV_DBNAME: msphpsql_sqlsrv SQLSRV_DBNAME: msphpsql_sqlsrv
PDOSQLSRV_DBNAME: msphpsql_pdosqlsrv PDOSQLSRV_DBNAME: msphpsql_pdosqlsrv
PYTHON: c:\Python36 PYTHON: c:\Python36
APPVEYOR: true
# For details about Appveyor build worker images (VM template): https://www.appveyor.com/docs/build-environment/#build-worker-images # For details about Appveyor build worker images (VM template): https://www.appveyor.com/docs/build-environment/#build-worker-images
matrix: matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017

View file

@ -11,6 +11,7 @@ variables:
trigger: trigger:
- dev - dev
- fix/*
pr: pr:
- dev - dev
@ -18,7 +19,7 @@ pr:
jobs: jobs:
- job: macOS - job: macOS
pool: pool:
vmImage: 'macOS-10.15' vmImage: 'macos-latest'
steps: steps:
- checkout: self - checkout: self
clean: true clean: true

View file

@ -200,7 +200,11 @@ class BuildDriver(object):
print('Something went wrong, launching log file', logfile) print('Something went wrong, launching log file', logfile)
# display log file only when not testing # display log file only when not testing
if not self.testing: if not self.testing:
os.startfile(os.path.join(root_dir, 'php-sdk', logfile)) logfile_path = os.path.join(root_dir, 'php-sdk', logfile)
if os.path.isfile(logfile_path):
with open(logfile_path, 'r') as f:
f.seek(0)
print(f.read())
os.chdir(work_dir) os.chdir(work_dir)
exit(1) exit(1)

View file

@ -321,7 +321,7 @@ class BuildUtil(object):
else: # pdo_sqlsrv else: # pdo_sqlsrv
cmd_line = ' --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line cmd_line = ' --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line
cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-json --enable-embed' + cmd_line cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-json --enable-embed --enable-mbstring --enable-ctype' + cmd_line
if self.thread == 'nts': if self.thread == 'nts':
cmd_line = cmd_line + ' --disable-zts' cmd_line = cmd_line + ' --disable-zts'
return cmd_line return cmd_line

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View file

@ -4,7 +4,7 @@ dnl
dnl Contents: the code that will go into the configure script, indicating options, dnl Contents: the code that will go into the configure script, indicating options,
dnl external libraries and includes, and what source files are to be compiled. dnl external libraries and includes, and what source files are to be compiled.
dnl dnl
dnl Microsoft Drivers 5.10 for PHP for SQL Server dnl Microsoft Drivers 5.11 for PHP for SQL Server
dnl Copyright(c) Microsoft Corporation dnl Copyright(c) Microsoft Corporation
dnl All rights reserved. dnl All rights reserved.
dnl MIT License dnl MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: JScript build configuration used by buildconf.bat // Contents: JScript build configuration used by buildconf.bat
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,17 +3,17 @@
// //
// Contents: Implements the PDO object for PDO_SQLSRV // Contents: Implements the PDO object for PDO_SQLSRV
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -34,7 +34,7 @@ namespace {
const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;";
const size_t LAST_INSERT_ID_BUFF_LEN = 50; // size of the buffer to hold the string value of the last inserted id, which may be an int, bigint, decimal(p,0) or numeric(p,0) const size_t LAST_INSERT_ID_BUFF_LEN = 50; // size of the buffer to hold the string value of the last inserted id, which may be an int, bigint, decimal(p,0) or numeric(p,0)
const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT current_value FROM sys.sequences WHERE name=N'%s'"; const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT current_value FROM sys.sequences WHERE name=N'%s'";
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes
// List of PDO supported connection options. // List of PDO supported connection options.
@ -95,7 +95,7 @@ enum PDO_STMT_OPTIONS {
// List of all the statement options supported by this driver. // List of all the statement options supported by this driver.
const stmt_option PDO_STMT_OPTS[] = { const stmt_option PDO_STMT_OPTS[] = {
{ NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout ) }, { NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout ) },
{ NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr<stmt_option_pdo_scrollable>( new stmt_option_pdo_scrollable ) }, { NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr<stmt_option_pdo_scrollable>( new stmt_option_pdo_scrollable ) },
{ NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr<stmt_option_encoding>( new stmt_option_encoding ) }, { NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr<stmt_option_encoding>( new stmt_option_encoding ) },
@ -113,12 +113,12 @@ const stmt_option PDO_STMT_OPTS[] = {
}; };
// boolean connection string // boolean connection string
struct pdo_bool_conn_str_func struct pdo_bool_conn_str_func
{ {
static void func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str ); static void func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str );
}; };
struct pdo_txn_isolation_conn_attr_func struct pdo_txn_isolation_conn_attr_func
{ {
static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ ); static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ );
}; };
@ -130,7 +130,7 @@ struct pdo_int_conn_str_func {
SQLSRV_ASSERT(Z_TYPE_P(value) == IS_STRING, "Wrong zval type for this keyword"); SQLSRV_ASSERT(Z_TYPE_P(value) == IS_STRING, "Wrong zval type for this keyword");
std::string val_str = Z_STRVAL_P( value ); std::string val_str = Z_STRVAL_P( value );
conn_str += option->odbc_name; conn_str += option->odbc_name;
conn_str += "={"; conn_str += "={";
conn_str += val_str; conn_str += val_str;
@ -155,6 +155,7 @@ struct pdo_encrypt_set_func
const char TRUE_VALUE_2[] = "1"; const char TRUE_VALUE_2[] = "1";
const char FALSE_VALUE_1[] = "false"; const char FALSE_VALUE_1[] = "false";
const char FALSE_VALUE_2[] = "0"; const char FALSE_VALUE_2[] = "0";
transform(val_str.begin(), val_str.end(), val_str.begin(), ::tolower);
// For backward compatibility, convert true/1 to yes and false/0 to no // For backward compatibility, convert true/1 to yes and false/0 to no
std::string attr; std::string attr;
@ -180,9 +181,9 @@ struct pdo_int_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ ) static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{ {
try { try {
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." );
size_t val = static_cast<size_t>( atoi( Z_STRVAL_P( value )) ); size_t val = static_cast<size_t>( atoi( Z_STRVAL_P( value )) );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER ); core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER );
} }
@ -198,8 +199,8 @@ struct pdo_bool_conn_attr_func {
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ ) static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{ {
try { try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( core_str_zval_is_true( value )), core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( core_str_zval_is_true( value )),
SQL_IS_UINTEGER ); SQL_IS_UINTEGER );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
@ -209,7 +210,7 @@ struct pdo_bool_conn_attr_func {
}; };
// statement options related functions // statement options related functions
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval** data ); _Inout_ zval** data );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht ); void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht );
@ -218,50 +219,50 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
// List of all connection options supported by this driver. // List of all connection options supported by this driver.
const connection_option PDO_CONN_OPTS[] = { const connection_option PDO_CONN_OPTS[] = {
{ {
PDOConnOptionNames::Server, PDOConnOptionNames::Server,
sizeof( PDOConnOptionNames::Server ), sizeof( PDOConnOptionNames::Server ),
PDO_CONN_OPTION_SERVER, PDO_CONN_OPTION_SERVER,
NULL, NULL,
0, 0,
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::APP, PDOConnOptionNames::APP,
sizeof( PDOConnOptionNames::APP ), sizeof( PDOConnOptionNames::APP ),
SQLSRV_CONN_OPTION_APP, SQLSRV_CONN_OPTION_APP,
ODBCConnOptions::APP, ODBCConnOptions::APP,
sizeof( ODBCConnOptions::APP ), sizeof( ODBCConnOptions::APP ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::AccessToken, PDOConnOptionNames::AccessToken,
sizeof( PDOConnOptionNames::AccessToken ), sizeof( PDOConnOptionNames::AccessToken ),
SQLSRV_CONN_OPTION_ACCESS_TOKEN, SQLSRV_CONN_OPTION_ACCESS_TOKEN,
ODBCConnOptions::AccessToken, ODBCConnOptions::AccessToken,
sizeof( ODBCConnOptions::AccessToken), sizeof( ODBCConnOptions::AccessToken),
CONN_ATTR_STRING, CONN_ATTR_STRING,
access_token_set_func::func access_token_set_func::func
}, },
{ {
PDOConnOptionNames::ApplicationIntent, PDOConnOptionNames::ApplicationIntent,
sizeof( PDOConnOptionNames::ApplicationIntent ), sizeof( PDOConnOptionNames::ApplicationIntent ),
SQLSRV_CONN_OPTION_APPLICATION_INTENT, SQLSRV_CONN_OPTION_APPLICATION_INTENT,
ODBCConnOptions::ApplicationIntent, ODBCConnOptions::ApplicationIntent,
sizeof( ODBCConnOptions::ApplicationIntent ), sizeof( ODBCConnOptions::ApplicationIntent ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::AttachDBFileName, PDOConnOptionNames::AttachDBFileName,
sizeof( PDOConnOptionNames::AttachDBFileName ), sizeof( PDOConnOptionNames::AttachDBFileName ),
SQLSRV_CONN_OPTION_ATTACHDBFILENAME, SQLSRV_CONN_OPTION_ATTACHDBFILENAME,
ODBCConnOptions::AttachDBFileName, ODBCConnOptions::AttachDBFileName,
sizeof( ODBCConnOptions::AttachDBFileName ), sizeof( ODBCConnOptions::AttachDBFileName ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::Authentication, PDOConnOptionNames::Authentication,
@ -339,17 +340,17 @@ const connection_option PDO_CONN_OPTS[] = {
PDOConnOptionNames::Encrypt, PDOConnOptionNames::Encrypt,
sizeof( PDOConnOptionNames::Encrypt ), sizeof( PDOConnOptionNames::Encrypt ),
SQLSRV_CONN_OPTION_ENCRYPT, SQLSRV_CONN_OPTION_ENCRYPT,
ODBCConnOptions::Encrypt, ODBCConnOptions::Encrypt,
sizeof( ODBCConnOptions::Encrypt ), sizeof( ODBCConnOptions::Encrypt ),
CONN_ATTR_MIXED, CONN_ATTR_MIXED,
pdo_encrypt_set_func::func pdo_encrypt_set_func::func
}, },
{ {
PDOConnOptionNames::Failover_Partner, PDOConnOptionNames::Failover_Partner,
sizeof( PDOConnOptionNames::Failover_Partner ), sizeof( PDOConnOptionNames::Failover_Partner ),
SQLSRV_CONN_OPTION_FAILOVER_PARTNER, SQLSRV_CONN_OPTION_FAILOVER_PARTNER,
ODBCConnOptions::Failover_Partner, ODBCConnOptions::Failover_Partner,
sizeof( ODBCConnOptions::Failover_Partner ), sizeof( ODBCConnOptions::Failover_Partner ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
@ -360,7 +361,7 @@ const connection_option PDO_CONN_OPTS[] = {
ODBCConnOptions::KeyStoreAuthentication, ODBCConnOptions::KeyStoreAuthentication,
sizeof( ODBCConnOptions::KeyStoreAuthentication ), sizeof( ODBCConnOptions::KeyStoreAuthentication ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
ce_akv_str_set_func::func ce_akv_str_set_func::func
}, },
{ {
PDOConnOptionNames::KeyStorePrincipalId, PDOConnOptionNames::KeyStorePrincipalId,
@ -369,7 +370,7 @@ const connection_option PDO_CONN_OPTS[] = {
ODBCConnOptions::KeyStorePrincipalId, ODBCConnOptions::KeyStorePrincipalId,
sizeof( ODBCConnOptions::KeyStorePrincipalId ), sizeof( ODBCConnOptions::KeyStorePrincipalId ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
ce_akv_str_set_func::func ce_akv_str_set_func::func
}, },
{ {
PDOConnOptionNames::KeyStoreSecret, PDOConnOptionNames::KeyStoreSecret,
@ -387,7 +388,7 @@ const connection_option PDO_CONN_OPTS[] = {
ODBCConnOptions::LoginTimeout, ODBCConnOptions::LoginTimeout,
sizeof( ODBCConnOptions::LoginTimeout ), sizeof( ODBCConnOptions::LoginTimeout ),
CONN_ATTR_INT, CONN_ATTR_INT,
pdo_int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func pdo_int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func
}, },
{ {
PDOConnOptionNames::MARS_Option, PDOConnOptionNames::MARS_Option,
@ -421,9 +422,9 @@ const connection_option PDO_CONN_OPTS[] = {
sizeof( PDOConnOptionNames::TraceFile ), sizeof( PDOConnOptionNames::TraceFile ),
SQLSRV_CONN_OPTION_TRACE_FILE, SQLSRV_CONN_OPTION_TRACE_FILE,
ODBCConnOptions::TraceFile, ODBCConnOptions::TraceFile,
sizeof( ODBCConnOptions::TraceFile ), sizeof( ODBCConnOptions::TraceFile ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
str_conn_attr_func<SQL_ATTR_TRACEFILE>::func str_conn_attr_func<SQL_ATTR_TRACEFILE>::func
}, },
{ {
PDOConnOptionNames::TraceOn, PDOConnOptionNames::TraceOn,
@ -467,7 +468,7 @@ const connection_option PDO_CONN_OPTS[] = {
SQLSRV_CONN_OPTION_WSID, SQLSRV_CONN_OPTION_WSID,
ODBCConnOptions::WSID, ODBCConnOptions::WSID,
sizeof( ODBCConnOptions::WSID ), sizeof( ODBCConnOptions::WSID ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
@ -597,7 +598,7 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
fetch_numeric( false ), fetch_numeric( false ),
fetch_datetime( false ), fetch_datetime( false ),
format_decimals( false ), format_decimals( false ),
decimal_places( NO_CHANGE_DECIMAL_PLACES ), decimal_places( NO_CHANGE_DECIMAL_PLACES ),
use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED), use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED),
emulate_prepare(false) emulate_prepare(false)
{ {
@ -608,11 +609,11 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
} }
// pdo_sqlsrv_db_handle_factory // pdo_sqlsrv_db_handle_factory
// Maps to PDO::__construct. // Maps to PDO::__construct.
// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. // Factory method called by the PDO driver manager to create a SQLSRV PDO connection.
// Does the following things: // Does the following things:
// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. // 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION.
// (If an error occurs in this function, the PDO specification mandates that // (If an error occurs in this function, the PDO specification mandates that
// an exception be thrown, regardless of the error mode setting.) // an exception be thrown, regardless of the error mode setting.)
// 2. Processes the driver options. // 2. Processes the driver options.
// 3. Creates a core_conn object by calling core_sqlsrv_connect. // 3. Creates a core_conn object by calling core_sqlsrv_connect.
@ -623,7 +624,7 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
// driver_options - A HashTable (within the zval) of options to use when creating the connection. // driver_options - A HashTable (within the zval) of options to use when creating the connection.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options) int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options)
{ {
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
@ -640,7 +641,7 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
ZVAL_UNDEF( &server_z ); ZVAL_UNDEF( &server_z );
try { try {
// no matter what the error mode, we want exceptions thrown if the connection fails // no matter what the error mode, we want exceptions thrown if the connection fails
// to happen (per the PDO spec) // to happen (per the PDO spec)
dbh->error_mode = PDO_ERRMODE_EXCEPTION; dbh->error_mode = PDO_ERRMODE_EXCEPTION;
@ -656,23 +657,23 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
dbh->refcount--; dbh->refcount--;
throw pdo::PDOException(); throw pdo::PDOException();
} }
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_conn_options_ht ); ALLOC_HASHTABLE( pdo_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ ); ZVAL_PTR_DTOR, 0 /*persistent*/ );
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error. // Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source,
static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht ); static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht );
dsn_parser->parse_conn_string(); dsn_parser->parse_conn_string();
// Extract the server name // Extract the server name
temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );
CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) { CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
@ -682,13 +683,13 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
zval_add_ref( &server_z ); zval_add_ref( &server_z );
zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );
sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn<pdo_sqlsrv_dbh>, Z_STRVAL( server_z ), sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn<pdo_sqlsrv_dbh>, Z_STRVAL( server_z ),
dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error,
PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" ); PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" );
// Free the string in server_z after being used // Free the string in server_z after being used
zend_string_release( Z_STR( server_z )); zend_string_release( Z_STR( server_z ));
SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." );
// set the driver_data and methods to complete creation of the PDO object // set the driver_data and methods to complete creation of the PDO object
@ -699,13 +700,13 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
if ( Z_TYPE( server_z ) == IS_STRING ) { if ( Z_TYPE( server_z ) == IS_STRING ) {
zend_string_release( Z_STR( server_z )); zend_string_release( Z_STR( server_z ));
} }
dbh->error_mode = prev_err_mode; // reset the error mode dbh->error_mode = prev_err_mode; // reset the error mode
g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL
return 0; return 0;
} }
catch( ... ) { catch( ... ) {
@ -719,8 +720,8 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
// pdo_sqlsrv_dbh_close // pdo_sqlsrv_dbh_close
// Maps to PDO::__destruct. // Maps to PDO::__destruct.
// Called when a PDO object is to be destroyed. // Called when a PDO object is to be destroyed.
// By the time this function is called, PDO has already made sure that // By the time this function is called, PDO has already made sure that
// all statements are disposed and the PDO object is the last item destroyed. // all statements are disposed and the PDO object is the last item destroyed.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// Return: // Return:
@ -788,7 +789,7 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
try { try {
// assign the methods for the statement object. This is necessary even if the // assign the methods for the statement object. This is necessary even if the
// statement fails so the user can retrieve the error information. // statement fails so the user can retrieve the error information.
stmt->methods = &pdo_sqlsrv_stmt_methods; stmt->methods = &pdo_sqlsrv_stmt_methods;
// if not emulate_prepare, we support parameterized queries with ?, not names // if not emulate_prepare, we support parameterized queries with ?, not names
@ -796,20 +797,20 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_stmt_options_ht ); ALLOC_HASHTABLE( pdo_stmt_options_ht );
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ ); ZVAL_PTR_DTOR, 0 /*persistent*/ );
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error. // Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht ); validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht );
driver_stmt = static_cast<pdo_sqlsrv_stmt*>( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, driver_stmt = static_cast<pdo_sqlsrv_stmt*>( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>,
pdo_stmt_options_ht, PDO_STMT_OPTS, pdo_stmt_options_ht, PDO_STMT_OPTS,
pdo_sqlsrv_handle_stmt_error, stmt )); pdo_sqlsrv_handle_stmt_error, stmt ));
// if the user didn't set anything in the prepare options, then set the buffer limit // if the user didn't set anything in the prepare options, then set the buffer limit
// to the value set on the connection. // to the value set on the connection.
if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) {
driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size; driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size;
} }
@ -888,7 +889,7 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
} }
stmt->driver_data = driver_stmt; stmt->driver_data = driver_stmt;
driver_stmt.transferred(); driver_stmt.transferred();
} }
// everything is cleaned up by this point // everything is cleaned up by this point
// catch everything so the exception doesn't spill into the calling PDO code // catch everything so the exception doesn't spill into the calling PDO code
@ -903,7 +904,7 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
// connection with the error's SQLSTATE. // connection with the error's SQLSTATE.
if( driver_dbh->last_error() ) { if( driver_dbh->last_error() ) {
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_dbh->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_dbh->last_error()->sqlstate ));
} }
@ -930,7 +931,7 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
// pdo_sqlsrv_dbh_do // pdo_sqlsrv_dbh_do
// Maps to PDO::exec. // Maps to PDO::exec.
// Execute a SQL statement, such as an insert, update or delete, and return // Execute a SQL statement, such as an insert, update or delete, and return
// the number of rows affected. // the number of rows affected.
// Parameters: // Parameters:
// dbh - the PDO connection object, which contains the ODBC handle // dbh - the PDO connection object, which contains the ODBC handle
@ -958,7 +959,7 @@ zend_long pdo_sqlsrv_dbh_do(_Inout_ pdo_dbh_t *dbh, _In_ const zend_string *sql)
SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN ));
try { try {
SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." );
SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL.");
@ -966,7 +967,7 @@ zend_long pdo_sqlsrv_dbh_do(_Inout_ pdo_dbh_t *dbh, _In_ const zend_string *sql)
pdo_stmt_t temp_stmt; pdo_stmt_t temp_stmt;
temp_stmt.dbh = dbh; temp_stmt.dbh = dbh;
// allocate a full driver statement to take advantage of the error handling // allocate a full driver statement to take advantage of the error handling
driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, NULL /*options_ht*/, driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, NULL /*options_ht*/,
NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt ); NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt );
driver_stmt->set_func( __FUNCTION__ ); driver_stmt->set_func( __FUNCTION__ );
@ -1004,11 +1005,11 @@ zend_long pdo_sqlsrv_dbh_do(_Inout_ pdo_dbh_t *dbh, _In_ const zend_string *sql)
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate ));
driver_dbh->set_last_error( driver_stmt->last_error() ); driver_dbh->set_last_error( driver_stmt->last_error() );
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
return -1; return -1;
} }
catch( ... ) { catch( ... ) {
@ -1028,7 +1029,7 @@ zend_long pdo_sqlsrv_dbh_do(_Inout_ pdo_dbh_t *dbh, _In_ const zend_string *sql)
// pdo_sqlsrv_dbh_begin // pdo_sqlsrv_dbh_begin
// Maps to PDO::beginTransaction. // Maps to PDO::beginTransaction.
// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn // Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn
// flag is maintained by PDO so we dont have to worry about it. // flag is maintained by PDO so we dont have to worry about it.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
@ -1046,17 +1047,17 @@ bool pdo_sqlsrv_dbh_begin(_Inout_ pdo_dbh_t *dbh)
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" );
sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ); sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" );
DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" ); DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" );
core_sqlsrv_begin_transaction( driver_conn ); core_sqlsrv_begin_transaction( driver_conn );
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
return 1; return 1;
#else #else
@ -1071,7 +1072,7 @@ bool pdo_sqlsrv_dbh_begin(_Inout_ pdo_dbh_t *dbh)
#endif #endif
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred.");
} }
// Should not have reached here but adding this due to compilation warnings // Should not have reached here but adding this due to compilation warnings
@ -1088,7 +1089,7 @@ bool pdo_sqlsrv_dbh_begin(_Inout_ pdo_dbh_t *dbh)
// Maps to PDO::commit. // Maps to PDO::commit.
// Commits a transaction. Returns the connection to auto-commit mode. // Commits a transaction. Returns the connection to auto-commit mode.
// PDO throws error if PDO::commit is called on a connection that is not in an active // PDO throws error if PDO::commit is called on a connection that is not in an active
// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have // transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here. // to worry about it here.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
@ -1106,17 +1107,17 @@ bool pdo_sqlsrv_dbh_commit(_Inout_ pdo_dbh_t *dbh)
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" );
sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ); sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" );
DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" ); DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" );
core_sqlsrv_commit( driver_conn ); core_sqlsrv_commit( driver_conn );
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
return 1; return 1;
#else #else
@ -1131,7 +1132,7 @@ bool pdo_sqlsrv_dbh_commit(_Inout_ pdo_dbh_t *dbh)
#endif #endif
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred.");
} }
@ -1147,7 +1148,7 @@ bool pdo_sqlsrv_dbh_commit(_Inout_ pdo_dbh_t *dbh)
// Maps to PDO::rollback. // Maps to PDO::rollback.
// Rolls back a transaction. Returns the connection in auto-commit mode. // Rolls back a transaction. Returns the connection in auto-commit mode.
// PDO throws error if PDO::rollBack is called on a connection that is not in an active // PDO throws error if PDO::rollBack is called on a connection that is not in an active
// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have // transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here. // to worry about it here.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
@ -1166,15 +1167,15 @@ bool pdo_sqlsrv_dbh_rollback(_Inout_ pdo_dbh_t *dbh)
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" );
sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ); sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" );
DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" ); DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" );
core_sqlsrv_rollback( driver_conn ); core_sqlsrv_rollback( driver_conn );
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
return 1; return 1;
#else #else
@ -1190,7 +1191,7 @@ bool pdo_sqlsrv_dbh_rollback(_Inout_ pdo_dbh_t *dbh)
#endif #endif
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred.");
} }
// Should not have reached here but adding this due to compilation warnings // Should not have reached here but adding this due to compilation warnings
@ -1204,7 +1205,7 @@ bool pdo_sqlsrv_dbh_rollback(_Inout_ pdo_dbh_t *dbh)
// pdo_sqlsrv_dbh_set_attr // pdo_sqlsrv_dbh_set_attr
// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. // Maps to PDO::setAttribute. Sets an attribute on the PDO connection object.
// PDO driver manager calls this function directly after calling the factory // PDO driver manager calls this function directly after calling the factory
// method for PDO, for any attribute which is specified in the PDO constructor. // method for PDO, for any attribute which is specified in the PDO constructor.
// Parameters: // Parameters:
// dbh - The PDO connection object maintained by PDO. // dbh - The PDO connection object maintained by PDO.
// attr - The attribute to be set. // attr - The attribute to be set.
@ -1260,7 +1261,7 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) {
convert_to_string( val ); convert_to_string( val );
THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val )); THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val ), NULL);
} }
driver_dbh->query_timeout = static_cast<long>( Z_LVAL_P( val ) ); driver_dbh->query_timeout = static_cast<long>( Z_LVAL_P( val ) );
break; break;
@ -1268,7 +1269,7 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) {
convert_to_string( val ); convert_to_string( val );
THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val )); THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val ), NULL);
} }
driver_dbh->client_buffer_max_size = Z_LVAL_P( val ); driver_dbh->client_buffer_max_size = Z_LVAL_P( val );
break; break;
@ -1323,12 +1324,18 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
break; break;
#endif #endif
case PDO_ATTR_STRINGIFY_FETCHES:
{
// do nothing
}
break;
// Not supported // Not supported
case PDO_ATTR_FETCH_TABLE_NAMES: case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES: case PDO_ATTR_FETCH_CATALOG_NAMES:
case PDO_ATTR_PREFETCH: case PDO_ATTR_PREFETCH:
case PDO_ATTR_MAX_COLUMN_LEN: case PDO_ATTR_MAX_COLUMN_LEN:
case PDO_ATTR_CURSOR_NAME: case PDO_ATTR_CURSOR_NAME:
case PDO_ATTR_AUTOCOMMIT: case PDO_ATTR_AUTOCOMMIT:
case PDO_ATTR_PERSISTENT: case PDO_ATTR_PERSISTENT:
case PDO_ATTR_TIMEOUT: case PDO_ATTR_TIMEOUT:
@ -1338,10 +1345,10 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
// Read-only // Read-only
case PDO_ATTR_SERVER_VERSION: case PDO_ATTR_SERVER_VERSION:
case PDO_ATTR_SERVER_INFO: case PDO_ATTR_SERVER_INFO:
case PDO_ATTR_CLIENT_VERSION: case PDO_ATTR_CLIENT_VERSION:
case PDO_ATTR_DRIVER_NAME: case PDO_ATTR_DRIVER_NAME:
case PDO_ATTR_CONNECTION_STATUS: case PDO_ATTR_CONNECTION_STATUS:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR );
} }
@ -1356,7 +1363,7 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
break; break;
// Statement level only // Statement level only
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
case SQLSRV_ATTR_DATA_CLASSIFICATION: case SQLSRV_ATTR_DATA_CLASSIFICATION:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
@ -1411,8 +1418,8 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
switch( attr ) { switch( attr ) {
// Not supported // Not supported
case PDO_ATTR_FETCH_TABLE_NAMES: case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES: case PDO_ATTR_FETCH_CATALOG_NAMES:
case PDO_ATTR_PREFETCH: case PDO_ATTR_PREFETCH:
case PDO_ATTR_MAX_COLUMN_LEN: case PDO_ATTR_MAX_COLUMN_LEN:
case PDO_ATTR_CURSOR_NAME: case PDO_ATTR_CURSOR_NAME:
@ -1420,7 +1427,7 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
case PDO_ATTR_TIMEOUT: case PDO_ATTR_TIMEOUT:
{ {
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
// PDO does not throw "not supported" error message for these attributes. // PDO does not throw "not supported" error message for these attributes.
THROW_PDO_ERROR(driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR); THROW_PDO_ERROR(driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR);
#else #else
return 0; return 0;
@ -1435,12 +1442,12 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
// Statement level only // Statement level only
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
case SQLSRV_ATTR_DATA_CLASSIFICATION: case SQLSRV_ATTR_DATA_CLASSIFICATION:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
} }
case PDO_ATTR_STRINGIFY_FETCHES: case PDO_ATTR_STRINGIFY_FETCHES:
{ {
// For this attribute, if we dont set the return_value than PDO returns NULL. // For this attribute, if we dont set the return_value than PDO returns NULL.
@ -1453,13 +1460,13 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
core_sqlsrv_get_server_info( driver_dbh, return_value ); core_sqlsrv_get_server_info( driver_dbh, return_value );
break; break;
} }
case PDO_ATTR_SERVER_VERSION: case PDO_ATTR_SERVER_VERSION:
{ {
core_sqlsrv_get_server_version( driver_dbh, return_value ); core_sqlsrv_get_server_version( driver_dbh, return_value );
break; break;
} }
case PDO_ATTR_CLIENT_VERSION: case PDO_ATTR_CLIENT_VERSION:
{ {
core_sqlsrv_get_client_info( driver_dbh, return_value ); core_sqlsrv_get_client_info( driver_dbh, return_value );
@ -1473,13 +1480,13 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
} }
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
{ {
ZVAL_LONG( return_value, driver_dbh->encoding() ); ZVAL_LONG( return_value, driver_dbh->encoding() );
break; break;
} }
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
{ {
ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout ));
break; break;
} }
@ -1491,7 +1498,7 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
} }
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
{ {
ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size );
break; break;
} }
@ -1515,7 +1522,7 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
} }
case SQLSRV_ATTR_DECIMAL_PLACES: case SQLSRV_ATTR_DECIMAL_PLACES:
{ {
ZVAL_LONG( return_value, driver_dbh->decimal_places ); ZVAL_LONG( return_value, driver_dbh->decimal_places );
break; break;
} }
@ -1528,7 +1535,7 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
} }
#endif #endif
default: default:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR );
break; break;
@ -1573,7 +1580,7 @@ void pdo_sqlsrv_dbh_return_error(_In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
else { else {
ctx_error = static_cast<sqlsrv_conn*>( dbh->driver_data )->last_error(); ctx_error = static_cast<sqlsrv_conn*>( dbh->driver_data )->last_error();
} }
pdo_sqlsrv_retrieve_context_error( ctx_error, info ); pdo_sqlsrv_retrieve_context_error( ctx_error, info );
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
@ -1629,7 +1636,7 @@ zend_string * pdo_sqlsrv_dbh_last_id(_Inout_ pdo_dbh_t *dbh, _In_ const zend_str
#endif #endif
wsql_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_CHAR, buffer, sizeof(buffer), &wsql_len); wsql_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_CHAR, buffer, sizeof(buffer), &wsql_len);
} }
CHECK_CUSTOM_ERROR(wsql_string == 0, driver_stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message()) { CHECK_CUSTOM_ERROR(wsql_string == 0, driver_stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1661,7 +1668,7 @@ zend_string * pdo_sqlsrv_dbh_last_id(_Inout_ pdo_dbh_t *dbh, _In_ const zend_str
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate ));
driver_dbh->set_last_error( driver_stmt->last_error() ); driver_dbh->set_last_error( driver_stmt->last_error() );
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
@ -1755,7 +1762,7 @@ zend_string* pdo_sqlsrv_dbh_quote(_Inout_ pdo_dbh_t* dbh, _In_ const zend_string
} }
// get the placeholder at the current position in driver_stmt->placeholders ht // get the placeholder at the current position in driver_stmt->placeholders ht
// Normally it's not a good idea to alter the internal pointer in a hashed array // Normally it's not a good idea to alter the internal pointer in a hashed array
// (see pull request 634 on GitHub) but in this case this is for internal use only // (see pull request 634 on GitHub) but in this case this is for internal use only
zval* placeholder = NULL; zval* placeholder = NULL;
if ((placeholder = zend_hash_get_current_data(driver_stmt->placeholders)) != NULL && zend_hash_move_forward(driver_stmt->placeholders) == SUCCESS && stmt->bound_params != NULL) { if ((placeholder = zend_hash_get_current_data(driver_stmt->placeholders)) != NULL && zend_hash_move_forward(driver_stmt->placeholders) == SUCCESS && stmt->bound_params != NULL) {
@ -1798,7 +1805,7 @@ zend_string* pdo_sqlsrv_dbh_quote(_Inout_ pdo_dbh_t* dbh, _In_ const zend_string
for (size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index) { for (size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index) {
// On success, snprintf returns the total number of characters written // On success, snprintf returns the total number of characters written
// On failure, a negative number is returned // On failure, a negative number is returned
// The generated string has a length of at most len - 1, so // The generated string has a length of at most len - 1, so
// len is 3 (2 hex digits + 1) // len is 3 (2 hex digits + 1)
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90" // Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf((char*)(*quoted + pos), 3, "%02X", unquoted[index] & 0x000000FF); int n = snprintf((char*)(*quoted + pos), 3, "%02X", unquoted[index] & 0x000000FF);
@ -1827,7 +1834,7 @@ zend_string* pdo_sqlsrv_dbh_quote(_Inout_ pdo_dbh_t* dbh, _In_ const zend_string
for (size_t index = 0; index < unquoted_len && unquoted_str[index] != '\0'; ++index) { for (size_t index = 0; index < unquoted_len && unquoted_str[index] != '\0'; ++index) {
// On success, snprintf returns the total number of characters written // On success, snprintf returns the total number of characters written
// On failure, a negative number is returned // On failure, a negative number is returned
// The generated string has a length of at most len - 1, so // The generated string has a length of at most len - 1, so
// len is 3 (2 hex digits + 1) // len is 3 (2 hex digits + 1)
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90" // Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf((char*)(p + pos), 3, "%02X", unquoted_str[index] & 0x000000FF); int n = snprintf((char*)(p + pos), 3, "%02X", unquoted_str[index] & 0x000000FF);
@ -1907,14 +1914,14 @@ pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh
// As per documentation, simply return false if the method does not exist // As per documentation, simply return false if the method does not exist
// https://www.php.net/manual/en/function.is-callable.php // https://www.php.net/manual/en/function.is-callable.php
// But user can call PDO::errorInfo() to check the error message if necessary // But user can call PDO::errorInfo() to check the error message if necessary
CHECK_CUSTOM_WARNING_AS_ERROR(true, driver_conn, PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED); CHECK_CUSTOM_WARNING_AS_ERROR(true, driver_conn, PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED, NULL);
return NULL; // return NULL for PDO to take care of the rest return NULL; // return NULL for PDO to take care of the rest
} }
namespace { namespace {
// Maps the PDO driver specific statement option/attribute constants to the core layer // Maps the PDO driver specific statement option/attribute constants to the core layer
// statement option/attribute constants. // statement option/attribute constants.
void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval* data) _Inout_ zval* data)
@ -1990,19 +1997,19 @@ void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ H
// validate_stmt_options // validate_stmt_options
// Iterates through the list of statement options provided by the user and validates them // Iterates through the list of statement options provided by the user and validates them
// against the list of statement options provided by this driver. After validation // against the list of statement options provided by this driver. After validation
// creates a Hashtable of statement options to be sent to the core layer for processing. // creates a Hashtable of statement options to be sent to the core layer for processing.
// Parameters: // Parameters:
// ctx - The current context. // ctx - The current context.
// stmt_options - The user provided list of statement options. // stmt_options - The user provided list of statement options.
// pdo_stmt_options_ht - Output hashtable of statement options. // pdo_stmt_options_ht - Output hashtable of statement options.
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht ) void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht )
{ {
try { try {
if( stmt_options ) { if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options ); HashTable* options_ht = Z_ARRVAL_P( stmt_options );
size_t int_key = -1; size_t int_key = -1;
zend_string *key = NULL; zend_string *key = NULL;
@ -2029,71 +2036,71 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str ) void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{ {
char const* val_str = "no"; char const* val_str = "no";
if( core_str_zval_is_true( value ) ) { if( core_str_zval_is_true( value ) ) {
val_str = "yes"; val_str = "yes";
} }
conn_str += option->odbc_name; conn_str += option->odbc_name;
conn_str += "={"; conn_str += "={";
conn_str += val_str; conn_str += val_str;
conn_str += "};"; conn_str += "};";
} }
void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn,
std::string& /*conn_str*/ ) std::string& /*conn_str*/ )
{ {
try { try {
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." );
const char* val = Z_STRVAL_P( value_z ); const char* val = Z_STRVAL_P( value_z );
size_t val_len = Z_STRLEN_P( value_z ); size_t val_len = Z_STRLEN_P( value_z );
zend_long out_val = SQL_TXN_READ_COMMITTED; zend_long out_val = SQL_TXN_READ_COMMITTED;
// READ_COMMITTED // READ_COMMITTED
if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) {
out_val = SQL_TXN_READ_COMMITTED; out_val = SQL_TXN_READ_COMMITTED;
} }
// READ_UNCOMMITTED // READ_UNCOMMITTED
else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) {
out_val = SQL_TXN_READ_UNCOMMITTED; out_val = SQL_TXN_READ_UNCOMMITTED;
} }
// REPEATABLE_READ // REPEATABLE_READ
else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) {
out_val = SQL_TXN_REPEATABLE_READ; out_val = SQL_TXN_REPEATABLE_READ;
} }
// SERIALIZABLE // SERIALIZABLE
else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) {
out_val = SQL_TXN_SERIALIZABLE; out_val = SQL_TXN_SERIALIZABLE;
} }
// SNAPSHOT // SNAPSHOT
else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) {
out_val = SQL_TXN_SS_SNAPSHOT; out_val = SQL_TXN_SS_SNAPSHOT;
} }
else { else {
CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER ); core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER );
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: initialization routines for PDO_SQLSRV // Contents: initialization routines for PDO_SQLSRV
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -2,20 +2,20 @@
// File: pdo_parser.cpp // File: pdo_parser.cpp
// //
// Contents: Implements a parser to parse the PDO DSN. // Contents: Implements a parser to parse the PDO DSN.
// //
// Copyright Microsoft Corporation // Copyright Microsoft Corporation
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -34,7 +34,7 @@ conn_string_parser:: conn_string_parser( _In_ sqlsrv_context& ctx, _In_ const ch
this->pos = -1; this->pos = -1;
this->ctx = &ctx; this->ctx = &ctx;
this->current_key = 0; this->current_key = 0;
this->current_key_name = NULL; this->current_key_name = NULL;
} }
sql_string_parser:: sql_string_parser( _In_ sqlsrv_context& ctx, _In_ const char* sql_str, _In_ int len, _In_ HashTable* placeholders_ht ) sql_string_parser:: sql_string_parser( _In_ sqlsrv_context& ctx, _In_ const char* sql_str, _In_ int len, _In_ HashTable* placeholders_ht )
@ -55,16 +55,16 @@ inline bool string_parser::next( void )
return false; return false;
} }
SQLSRV_ASSERT( this->pos < len, "Unexpected cursor position in conn_string_parser::next" ); SQLSRV_ASSERT( this->pos < len, "Unexpected cursor position in conn_string_parser::next" );
this->pos++; this->pos++;
if ( this->is_eos() ) { if ( this->is_eos() ) {
return false; return false;
} }
return true; return true;
} }
@ -77,12 +77,12 @@ inline bool string_parser::is_eos( void )
} }
SQLSRV_ASSERT(this->pos < len, "Unexpected cursor position in conn_string_parser::is_eos" ); SQLSRV_ASSERT(this->pos < len, "Unexpected cursor position in conn_string_parser::is_eos" );
return false; return false;
} }
// Check for white space. // Check for white space.
inline bool string_parser::is_white_space( _In_ char c ) inline bool string_parser::is_white_space( _In_ char c )
{ {
if( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) { if( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) {
return true; return true;
@ -94,9 +94,9 @@ inline bool string_parser::is_white_space( _In_ char c )
int conn_string_parser::discard_trailing_white_spaces( _In_reads_(len) const char* str, _Inout_ int len ) int conn_string_parser::discard_trailing_white_spaces( _In_reads_(len) const char* str, _Inout_ int len )
{ {
const char* end = str + ( len - 1 ); const char* end = str + ( len - 1 );
while(( this->is_white_space( *end ) ) && (len > 0) ) { while(( this->is_white_space( *end ) ) && (len > 0) ) {
len--; len--;
end--; end--;
} }
@ -108,16 +108,16 @@ int conn_string_parser::discard_trailing_white_spaces( _In_reads_(len) const cha
bool string_parser::discard_white_spaces() bool string_parser::discard_white_spaces()
{ {
if( this->is_eos() ) { if( this->is_eos() ) {
return false; return false;
} }
while( this->is_white_space( this->orig_str[pos] )) { while( this->is_white_space( this->orig_str[pos] )) {
if( !next() ) if( !next() )
return false; return false;
} }
return true; return true;
} }
@ -128,22 +128,22 @@ void string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_
ZVAL_UNDEF( &value_z ); ZVAL_UNDEF( &value_z );
if( len == 0 ) { if( len == 0 ) {
ZVAL_STRINGL( &value_z, "", 0); ZVAL_STRINGL( &value_z, "", 0);
} }
else { else {
ZVAL_STRINGL( &value_z, const_cast<char*>( value ), len ); ZVAL_STRINGL( &value_z, const_cast<char*>( value ), len );
} }
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z ); core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z );
} }
// Add a key-value pair to the hashtable with int value // Add a key-value pair to the hashtable with int value
void sql_string_parser::add_key_int_value_pair( _In_ unsigned int value ) { void sql_string_parser::add_key_int_value_pair( _In_ unsigned int value ) {
zval value_z; zval value_z;
ZVAL_LONG( &value_z, value ); ZVAL_LONG( &value_z, value );
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z ); core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z );
} }
@ -168,9 +168,9 @@ void conn_string_parser::validate_key( _In_reads_(key_len) const char *key, _Ino
key_name = static_cast<char*>( sqlsrv_malloc( new_len + 1 )); key_name = static_cast<char*>( sqlsrv_malloc( new_len + 1 ));
memcpy_s( key_name, new_len + 1 ,key, new_len ); memcpy_s( key_name, new_len + 1 ,key, new_len );
key_name[new_len] = '\0'; key_name[new_len] = '\0';
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ) ); THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ), NULL );
} }
inline bool sql_string_parser::is_placeholder_char( char c ) inline bool sql_string_parser::is_placeholder_char( char c )
@ -183,7 +183,7 @@ inline bool sql_string_parser::is_placeholder_char( char c )
} }
// Primary function which parses the connection string/DSN. // Primary function which parses the connection string/DSN.
void conn_string_parser:: parse_conn_string( void ) void conn_string_parser:: parse_conn_string( void )
{ {
States state = FirstKeyValuePair; // starting state States state = FirstKeyValuePair; // starting state
int start_pos = -1; int start_pos = -1;
@ -191,17 +191,17 @@ void conn_string_parser:: parse_conn_string( void )
try { try {
while( !this->is_eos() ) { while( !this->is_eos() ) {
switch( state ) { switch( state ) {
case FirstKeyValuePair: case FirstKeyValuePair:
{ {
// discard leading spaces // discard leading spaces
if( !next() || !discard_white_spaces() ) { if( !next() || !discard_white_spaces() ) {
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_STRING ); //EOS THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_STRING ); //EOS
} }
state = Key; state = Key;
break; break;
} }
@ -212,15 +212,15 @@ void conn_string_parser:: parse_conn_string( void )
// read the key name // read the key name
while( this->orig_str[pos] != '=' ) { while( this->orig_str[pos] != '=' ) {
if( !next() ) {
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY ); //EOS
}
}
this->validate_key( &( this->orig_str[start_pos] ), ( pos - start_pos ) ); if( !next() ) {
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY ); //EOS
}
}
this->validate_key( &( this->orig_str[start_pos] ), ( pos - start_pos ) );
state = Value; state = Value;
break; break;
@ -239,23 +239,23 @@ void conn_string_parser:: parse_conn_string( void )
add_key_value_pair( NULL, 0 ); add_key_value_pair( NULL, 0 );
if( this->is_eos() ) { if( this->is_eos() ) {
break; // EOS break; // EOS
} }
else { else {
// this->orig_str[pos] == ';' // this->orig_str[pos] == ';'
state = NextKeyValuePair; state = NextKeyValuePair;
} }
} }
// if LCB // if LCB
else if( this->orig_str[pos] == '{' ) { else if( this->orig_str[pos] == '{' ) {
start_pos = this->pos; // starting character is LCB start_pos = this->pos; // starting character is LCB
state = ValueContent1; state = ValueContent1;
} }
// If NonSP-LCB-SC // If NonSP-LCB-SC
else { else {
@ -269,10 +269,10 @@ void conn_string_parser:: parse_conn_string( void )
case ValueContent1: case ValueContent1:
{ {
while ( this->orig_str[pos] != '}' ) { while ( this->orig_str[pos] != '}' ) {
if ( ! next() ) { if ( ! next() ) {
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE, this->current_key_name ); THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE, this->current_key_name, NULL );
} }
} }
@ -287,20 +287,20 @@ void conn_string_parser:: parse_conn_string( void )
while( this->orig_str[pos] != ';' ) { while( this->orig_str[pos] != ';' ) {
if( ! next() ) { if( ! next() ) {
break; //EOS break; //EOS
} }
} }
if( !this->is_eos() && this->orig_str[pos] == ';' ) { if( !this->is_eos() && this->orig_str[pos] == ';' ) {
// semi-colon encountered, so go to next key-value pair // semi-colon encountered, so go to next key-value pair
state = NextKeyValuePair; state = NextKeyValuePair;
} }
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos ); add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos );
SQLSRV_ASSERT((( state == NextKeyValuePair ) || ( this->is_eos() )), SQLSRV_ASSERT((( state == NextKeyValuePair ) || ( this->is_eos() )),
"conn_string_parser::parse_conn_string: Invalid state encountered " ); "conn_string_parser::parse_conn_string: Invalid state encountered " );
break; break;
@ -308,7 +308,7 @@ void conn_string_parser:: parse_conn_string( void )
case RCBEncountered: case RCBEncountered:
{ {
// Read the next character after RCB. // Read the next character after RCB.
if( !next() ) { if( !next() ) {
@ -321,11 +321,11 @@ void conn_string_parser:: parse_conn_string( void )
// if second RCB encountered than go back to ValueContent1 // if second RCB encountered than go back to ValueContent1
if( this->orig_str[pos] == '}' ) { if( this->orig_str[pos] == '}' ) {
if( !next() ) { if( !next() ) {
// EOS after a second RCB is error // EOS after a second RCB is error
THROW_PDO_ERROR( this->ctx, SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN, this->current_key_name ); THROW_PDO_ERROR( this->ctx, SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN, this->current_key_name, NULL );
} }
state = ValueContent1; state = ValueContent1;
@ -336,9 +336,9 @@ void conn_string_parser:: parse_conn_string( void )
// discard any trailing white-spaces. // discard any trailing white-spaces.
if( this->is_white_space( this->orig_str[pos] )) { if( this->is_white_space( this->orig_str[pos] )) {
if( ! this->discard_white_spaces() ) { if( ! this->discard_white_spaces() ) {
//EOS //EOS
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos ); add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos );
break; break;
@ -347,32 +347,32 @@ void conn_string_parser:: parse_conn_string( void )
// if semi-colon than go to next key-value pair // if semi-colon than go to next key-value pair
if ( this->orig_str[pos] == ';' ) { if ( this->orig_str[pos] == ';' ) {
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos ); add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos );
state = NextKeyValuePair; state = NextKeyValuePair;
break; break;
} }
// Non - (RCB, SP*, SC, EOS) character. Any other character after an RCB is an error. // Non - (RCB, SP*, SC, EOS) character. Any other character after an RCB is an error.
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, this->current_key_name ); THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, this->current_key_name, NULL );
break; break;
} }
case NextKeyValuePair: case NextKeyValuePair:
{ {
SQLSRV_ASSERT(( this->orig_str[pos] == ';' ), SQLSRV_ASSERT(( this->orig_str[pos] == ';' ),
"conn_string_parser::parse_conn_string: semi-colon was expected." ); "conn_string_parser::parse_conn_string: semi-colon was expected." );
// Call next() to skip the semi-colon. // Call next() to skip the semi-colon.
if( !next() || !this->discard_white_spaces() ) { if( !next() || !this->discard_white_spaces() ) {
// EOS // EOS
break; break;
} }
if( this->orig_str[pos] == ';' ) { if( this->orig_str[pos] == ';' ) {
// a second semi-colon is error case. // a second semi-colon is error case.
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING, this->pos ); THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING, this->pos, NULL );
} }
else { else {
@ -384,7 +384,7 @@ void conn_string_parser:: parse_conn_string( void )
} //case NextKeyValuePair } //case NextKeyValuePair
} // switch } // switch
} //while } //while
} }
catch( pdo::PDOException& ) { catch( pdo::PDOException& ) {
throw; throw;

View file

@ -3,17 +3,17 @@
// //
// Contents: Implements the PDOStatement object for the PDO_SQLSRV // Contents: Implements the PDOStatement object for the PDO_SQLSRV
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -57,7 +57,7 @@ SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _I
{ {
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>(driver_stmt); pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>(driver_stmt);
SQLSRV_ASSERT(pdo_stmt != NULL, "pdo_type_to_sqlsrv_php_type: pdo_stmt object was null"); SQLSRV_ASSERT(pdo_stmt != NULL, "pdo_type_to_sqlsrv_php_type: pdo_stmt object was null");
switch( pdo_type ) { switch( pdo_type ) {
case PDO_PARAM_BOOL: case PDO_PARAM_BOOL:
@ -66,10 +66,10 @@ SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _I
case PDO_PARAM_STR: case PDO_PARAM_STR:
return SQLSRV_PHPTYPE_STRING; return SQLSRV_PHPTYPE_STRING;
case PDO_PARAM_NULL: case PDO_PARAM_NULL:
return SQLSRV_PHPTYPE_NULL; return SQLSRV_PHPTYPE_NULL;
case PDO_PARAM_LOB: case PDO_PARAM_LOB:
if (pdo_stmt->fetch_datetime) { if (pdo_stmt->fetch_datetime) {
return SQLSRV_PHPTYPE_DATETIME; return SQLSRV_PHPTYPE_DATETIME;
@ -84,8 +84,8 @@ SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _I
default: default:
DIE( "pdo_type_to_sqlsrv_php_type: Unexpected pdo_param_type encountered" ); DIE( "pdo_type_to_sqlsrv_php_type: Unexpected pdo_param_type encountered" );
} }
return SQLSRV_PHPTYPE_INVALID; // to prevent compiler warning return SQLSRV_PHPTYPE_INVALID; // to prevent compiler warning
} }
// Returns a pdo type for a given SQL type. See pdo_param_type // Returns a pdo type for a given SQL type. See pdo_param_type
@ -104,7 +104,7 @@ inline pdo_param_type sql_type_to_pdo_type( _In_ SQLSMALLINT sql_type )
case SQL_BINARY: case SQL_BINARY:
case SQL_CHAR: case SQL_CHAR:
case SQL_DECIMAL: case SQL_DECIMAL:
case SQL_DOUBLE: case SQL_DOUBLE:
case SQL_FLOAT: case SQL_FLOAT:
case SQL_GUID: case SQL_GUID:
case SQL_LONGVARBINARY: case SQL_LONGVARBINARY:
@ -159,9 +159,9 @@ void set_stmt_cursors( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
default: default:
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE ); THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
} }
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type ); core_sqlsrv_set_scrollable( stmt, odbc_cursor_type );
} }
void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z ) void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
@ -178,12 +178,12 @@ void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z
long odbc_cursor_type = static_cast<long>( Z_LVAL_P( value_z ) ); long odbc_cursor_type = static_cast<long>( Z_LVAL_P( value_z ) );
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type ); core_sqlsrv_set_scrollable( stmt, odbc_cursor_type );
return; return;
} }
// Sets the statement encoding. Default encoding on the statement // Sets the statement encoding. Default encoding on the statement
// implies use the connection's encoding. // implies use the connection's encoding.
void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z ) void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{ {
@ -192,9 +192,9 @@ void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_ENCODING ); THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_ENCODING );
} }
zend_long attr_value = Z_LVAL_P( value_z ); zend_long attr_value = Z_LVAL_P( value_z );
switch( attr_value ) { switch( attr_value ) {
// when the default encoding is applied to a statement, it means use the creating connection's encoding // when the default encoding is applied to a statement, it means use the creating connection's encoding
@ -269,7 +269,7 @@ zval convert_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_PHPTYPE sqlsrv_php_t
return out_zval; return out_zval;
} }
} // namespace } // namespace
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt ); int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt );
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt ); int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt );
@ -309,7 +309,7 @@ struct pdo_stmt_methods pdo_sqlsrv_stmt_methods = {
void stmt_option_pdo_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z ) void stmt_option_pdo_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{ {
set_stmt_cursors( stmt, value_z ); set_stmt_cursors( stmt, value_z );
} }
void stmt_option_encoding:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z ) void stmt_option_encoding:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
@ -391,7 +391,7 @@ int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt )
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_close_cursor: driver_data object was null" ); SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_close_cursor: driver_data object was null" );
// to "close the cursor" means we make the statement ready for execution again. To do this, we // to "close the cursor" means we make the statement ready for execution again. To do this, we
// skip all the result sets on the current statement. // skip all the result sets on the current statement.
// If the statement has not been executed there are no next results to iterate over. // If the statement has not been executed there are no next results to iterate over.
if ( driver_stmt && driver_stmt->executed == true ) if ( driver_stmt && driver_stmt->executed == true )
@ -414,7 +414,7 @@ int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt )
} }
// pdo_sqlsrv_stmt_describe_col // pdo_sqlsrv_stmt_describe_col
// Gets the metadata for a column based on the column number. // Gets the metadata for a column based on the column number.
// Calls the core_sqlsrv_field_metadata function present in the core layer. // Calls the core_sqlsrv_field_metadata function present in the core layer.
// Parameters: // Parameters:
// *stmt - pointer to current statement // *stmt - pointer to current statement
@ -433,7 +433,7 @@ int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno)
sqlsrv_malloc_auto_ptr<field_meta_data> core_meta_data; sqlsrv_malloc_auto_ptr<field_meta_data> core_meta_data;
try { try {
core_meta_data = core_sqlsrv_field_metadata( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), colno ); core_meta_data = core_sqlsrv_field_metadata( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), colno );
} }
@ -448,20 +448,20 @@ int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno)
pdo_column_data* column_data = &(stmt->columns[colno]); pdo_column_data* column_data = &(stmt->columns[colno]);
SQLSRV_ASSERT( column_data != NULL, "pdo_sqsrv_stmt_describe_col: pdo_column_data was null" ); SQLSRV_ASSERT( column_data != NULL, "pdo_sqsrv_stmt_describe_col: pdo_column_data was null" );
// Set the name // Set the name
column_data->name = zend_string_init( (const char*)core_meta_data->field_name.get(), core_meta_data->field_name_len, 0 ); column_data->name = zend_string_init( (const char*)core_meta_data->field_name.get(), core_meta_data->field_name_len, 0 );
// Set the maxlen // Set the maxlen
column_data->maxlen = ( core_meta_data->field_precision > 0 ) ? core_meta_data->field_precision : core_meta_data->field_size; column_data->maxlen = ( core_meta_data->field_precision > 0 ) ? core_meta_data->field_precision : core_meta_data->field_size;
// Set the precision // Set the precision
column_data->precision = core_meta_data->field_scale; column_data->precision = core_meta_data->field_scale;
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
// Set the param_type // Set the param_type
column_data->param_type = PDO_PARAM_ZVAL; column_data->param_type = PDO_PARAM_ZVAL;
#endif #endif
// store the field data for use by pdo_sqlsrv_stmt_get_col_data // store the field data for use by pdo_sqlsrv_stmt_get_col_data
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
SQLSRV_ASSERT( driver_stmt != NULL, "Invalid driver statement in pdo_sqlsrv_stmt_describe_col" ); SQLSRV_ASSERT( driver_stmt != NULL, "Invalid driver statement in pdo_sqlsrv_stmt_describe_col" );
@ -474,7 +474,7 @@ int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno)
// pdo_sqlsrv_stmt_dtor // pdo_sqlsrv_stmt_dtor
// Maps to PDOStatement::__destruct. Destructor for the PDO Statement. // Maps to PDOStatement::__destruct. Destructor for the PDO Statement.
// Parameters: // Parameters:
// *stmt - pointer to current statement // *stmt - pointer to current statement
// Return: // Return:
@ -512,7 +512,7 @@ int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt )
} }
// pdo_sqlsrv_stmt_execute // pdo_sqlsrv_stmt_execute
// Maps to PDOStatement::Execute. Executes the prepared statement. // Maps to PDOStatement::Execute. Executes the prepared statement.
// Parameters: // Parameters:
// *stmt - pointer to the current statement. // *stmt - pointer to the current statement.
// Return: // Return:
@ -537,7 +537,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
core_sqlsrv_next_result( driver_stmt, false ); core_sqlsrv_next_result( driver_stmt, false );
} }
} }
const char* query = NULL; const char* query = NULL;
unsigned int query_len = 0; unsigned int query_len = 0;
@ -548,11 +548,11 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
query_len = static_cast<unsigned int>( driver_stmt->direct_query_subst_string_len ); query_len = static_cast<unsigned int>( driver_stmt->direct_query_subst_string_len );
} }
// if the user is using prepare emulation (PDO::ATTR_EMULATE_PREPARES), set the query to the // if the user is using prepare emulation (PDO::ATTR_EMULATE_PREPARES), set the query to the
// subtituted query provided by PDO // subtituted query provided by PDO
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
// reset the placeholders hashtable internal in case the user reexecutes a statement // reset the placeholders hashtable internal in case the user reexecutes a statement
// Normally it's not a good idea to alter the internal pointer in a hashed array // Normally it's not a good idea to alter the internal pointer in a hashed array
// (see pull request 634 on GitHub) but in this case this is for internal use only // (see pull request 634 on GitHub) but in this case this is for internal use only
zend_hash_internal_pointer_reset(driver_stmt->placeholders); zend_hash_internal_pointer_reset(driver_stmt->placeholders);
@ -567,10 +567,10 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
} }
// The query timeout setting is inherited from the corresponding connection attribute, but // The query timeout setting is inherited from the corresponding connection attribute, but
// the user may have changed the query timeout setting again before this via // the user may have changed the query timeout setting again before this via
// PDOStatement::setAttribute() // PDOStatement::setAttribute()
driver_stmt->set_query_timeout(); driver_stmt->set_query_timeout();
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt, query, query_len ); SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt, query, query_len );
if ( execReturn == SQL_NO_DATA ) { if ( execReturn == SQL_NO_DATA ) {
@ -598,13 +598,13 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
} }
} }
// workaround for a bug in the PDO driver manager. It is fairly simple to crash the PDO driver manager with // workaround for a bug in the PDO driver manager. It is fairly simple to crash the PDO driver manager with
// the following sequence: // the following sequence:
// 1) Prepare and execute a statement (that has some results with it) // 1) Prepare and execute a statement (that has some results with it)
// 2) call PDOStatement::nextRowset until there are no more results // 2) call PDOStatement::nextRowset until there are no more results
// 3) execute the statement again // 3) execute the statement again
// 4) call PDOStatement::getColumnMeta // 4) call PDOStatement::getColumnMeta
// It crashes from what I can tell because there is no metadata because there was no call to // It crashes from what I can tell because there is no metadata because there was no call to
// pdo_stmt_sqlsrv_describe_col and stmt->columns is NULL on the second call to // pdo_stmt_sqlsrv_describe_col and stmt->columns is NULL on the second call to
// PDO::execute. My guess is that because stmt->executed is true, it optimizes away a necessary call to // PDO::execute. My guess is that because stmt->executed is true, it optimizes away a necessary call to
// pdo_sqlsrv_stmt_describe_col. By setting the stmt->executed flag to 0, this call is not optimized away // pdo_sqlsrv_stmt_describe_col. By setting the stmt->executed flag to 0, this call is not optimized away
@ -632,8 +632,8 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
// pdo_sqlsrv_stmt_fetch // pdo_sqlsrv_stmt_fetch
// Maps to PDOStatement::fetch // Maps to PDOStatement::fetch
// Move the cursor to the record indicated. If the cursor is moved off the end, // Move the cursor to the record indicated. If the cursor is moved off the end,
// or before the beginning if a scrollable cursor is created, then FAILURE is returned. // or before the beginning if a scrollable cursor is created, then FAILURE is returned.
// Parameters: // Parameters:
// *stmt - pointer to current statement for which the cursor should be moved. // *stmt - pointer to current statement for which the cursor should be moved.
// ori - cursor orientation. Maps to the list of PDO::FETCH_ORI_* constants // ori - cursor orientation. Maps to the list of PDO::FETCH_ORI_* constants
@ -648,7 +648,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
PDO_LOG_STMT_ENTRY; PDO_LOG_STMT_ENTRY;
try { try {
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_fetch: pdo_stmt object was null" ); SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_fetch: pdo_stmt object was null" );
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
@ -678,7 +678,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
} }
for( long i = 0; i < stmt->column_count; ++i ) { for( long i = 0; i < stmt->column_count; ++i ) {
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
if (NULL== (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, i))) && if (NULL== (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, i))) &&
(NULL == (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_columns, stmt->columns[i].name))))) { (NULL == (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_columns, stmt->columns[i].name))))) {
@ -709,8 +709,8 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
// support for the PDO rowCount method. Since rowCount doesn't call a // support for the PDO rowCount method. Since rowCount doesn't call a
// method, PDO relies on us to fill the pdo_stmt_t::row_count member // method, PDO relies on us to fill the pdo_stmt_t::row_count member
// The if condition was changed from // The if condition was changed from
// `driver_stmt->past_fetch_end || driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY` // `driver_stmt->past_fetch_end || driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY`
// because it caused SQLRowCount to be called at each fetch if using a non-forward cursor // because it caused SQLRowCount to be called at each fetch if using a non-forward cursor
// which is unnecessary and a performance hit // which is unnecessary and a performance hit
if( driver_stmt->past_fetch_end || driver_stmt->cursor_type == SQL_CURSOR_DYNAMIC) { if( driver_stmt->past_fetch_end || driver_stmt->cursor_type == SQL_CURSOR_DYNAMIC) {
@ -738,7 +738,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
return 0; return 0;
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_stmt_fetch: Unexpected exception occurred."); DIE ("pdo_sqlsrv_stmt_fetch: Unexpected exception occurred.");
} }
// Should not have reached here but adding this due to compilation warnings // Should not have reached here but adding this due to compilation warnings
@ -747,9 +747,9 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
// pdo_sqlsrv_stmt_get_col_data // pdo_sqlsrv_stmt_get_col_data
// Called by the set of PDO Fetch functions. // Called by the set of PDO Fetch functions.
// Retrieves a single column. PDO driver manager is responsible for freeing the // Retrieves a single column. PDO driver manager is responsible for freeing the
// returned buffer. Because PDO can request fields out of order and ODBC does not // returned buffer. Because PDO can request fields out of order and ODBC does not
// support out of order field requests, this function should also cache fields. // support out of order field requests, this function should also cache fields.
// Parameters: // Parameters:
// stmt - Statement to retrive the column for. // stmt - Statement to retrive the column for.
// colno - Index of the column that needs to be retrieved. Starts with 0. // colno - Index of the column that needs to be retrieved. Starts with 0.
@ -770,7 +770,7 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
PDO_RESET_STMT_ERROR; PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT; PDO_VALIDATE_STMT;
PDO_LOG_STMT_ENTRY; PDO_LOG_STMT_ENTRY;
try { try {
SQLSRV_ASSERT(stmt != NULL, "pdo_sqlsrv_stmt_get_col_data: pdo_stmt object was null"); SQLSRV_ASSERT(stmt != NULL, "pdo_sqlsrv_stmt_get_col_data: pdo_stmt object was null");
@ -784,7 +784,7 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
} }
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
// Let PDO free the memory after use. // Let PDO free the memory after use.
* caller_frees = 1; * caller_frees = 1;
#endif #endif
@ -801,7 +801,7 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
true); true);
driver_stmt->current_meta_data[colno]->sqlsrv_php_type = sqlsrv_php_type; driver_stmt->current_meta_data[colno]->sqlsrv_php_type = sqlsrv_php_type;
// if a column is bound to a type different than the column type, figure out a way to convert it to the // if a column is bound to a type different than the column type, figure out a way to convert it to the
// type they want // type they want
#if PHP_VERSION_ID < 80100 #if PHP_VERSION_ID < 80100
if (stmt->bound_columns && driver_stmt->bound_column_param_types[colno] != PDO_PARAM_ZVAL) { if (stmt->bound_columns && driver_stmt->bound_column_param_types[colno] != PDO_PARAM_ZVAL) {
@ -822,13 +822,13 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
if (bind_data != NULL && !Z_ISUNDEF(bind_data->driver_params)) { if (bind_data != NULL && !Z_ISUNDEF(bind_data->driver_params)) {
CHECK_CUSTOM_ERROR(Z_TYPE(bind_data->driver_params) != IS_LONG, driver_stmt, CHECK_CUSTOM_ERROR(Z_TYPE(bind_data->driver_params) != IS_LONG, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA, colno + 1) { PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA, colno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
CHECK_CUSTOM_ERROR(driver_stmt->bound_column_param_types[colno] != PDO_PARAM_STR CHECK_CUSTOM_ERROR(driver_stmt->bound_column_param_types[colno] != PDO_PARAM_STR
&& driver_stmt->bound_column_param_types[colno] != PDO_PARAM_LOB, driver_stmt, && driver_stmt->bound_column_param_types[colno] != PDO_PARAM_LOB, driver_stmt,
PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING, colno + 1) { PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING, colno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
@ -841,7 +841,7 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
case SQLSRV_ENCODING_UTF8: case SQLSRV_ENCODING_UTF8:
break; break;
default: default:
THROW_PDO_ERROR(driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING, colno); THROW_PDO_ERROR(driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING, colno, NULL);
break; break;
} }
} }
@ -870,7 +870,7 @@ int pdo_sqlsrv_stmt_get_col_data(_Inout_ pdo_stmt_t *stmt, _In_ int colno, _Inou
} }
#endif #endif
return 1; return 1;
} }
catch ( core::CoreException& ) { catch ( core::CoreException& ) {
return 0; return 0;
} }
@ -909,11 +909,11 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
set_stmt_encoding( driver_stmt, val ); set_stmt_encoding( driver_stmt, val );
break; break;
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY ); THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY );
break; break;
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
core_sqlsrv_set_query_timeout( driver_stmt, val ); core_sqlsrv_set_query_timeout( driver_stmt, val );
break; break;
@ -976,7 +976,7 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
PDO_RESET_STMT_ERROR; PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT; PDO_VALIDATE_STMT;
PDO_LOG_STMT_ENTRY; PDO_LOG_STMT_ENTRY;
pdo_sqlsrv_stmt* driver_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); pdo_sqlsrv_stmt* driver_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
SQLSRV_ASSERT(( driver_stmt != NULL ), "pdo_sqlsrv_stmt_get_attr: stmt->driver_data was null" ); SQLSRV_ASSERT(( driver_stmt != NULL ), "pdo_sqlsrv_stmt_get_attr: stmt->driver_data was null" );
@ -991,27 +991,27 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
} }
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
{ {
ZVAL_LONG( return_value, driver_stmt->encoding() ); ZVAL_LONG( return_value, driver_stmt->encoding() );
break; break;
} }
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
{ {
ZVAL_LONG( return_value, ( driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY ? ZVAL_LONG( return_value, ( driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY ?
PDO_CURSOR_SCROLL : PDO_CURSOR_FWDONLY )); PDO_CURSOR_SCROLL : PDO_CURSOR_FWDONLY ));
break; break;
} }
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
{ {
ZVAL_LONG( return_value, driver_stmt->cursor_type ); ZVAL_LONG( return_value, driver_stmt->cursor_type );
break; break;
} }
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
{ {
ZVAL_LONG( return_value, driver_stmt->buffered_query_limit ); ZVAL_LONG( return_value, driver_stmt->buffered_query_limit );
break; break;
} }
@ -1041,7 +1041,7 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
case SQLSRV_ATTR_DECIMAL_PLACES: case SQLSRV_ATTR_DECIMAL_PLACES:
{ {
ZVAL_LONG( return_value, driver_stmt->decimal_places ); ZVAL_LONG( return_value, driver_stmt->decimal_places );
break; break;
} }
@ -1069,9 +1069,9 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
} }
// pdo_sqlsrv_stmt_get_col_meta // pdo_sqlsrv_stmt_get_col_meta
// Maps to PDOStatement::getColumnMeta. Return extra metadata. // Maps to PDOStatement::getColumnMeta. Return extra metadata.
// Though we don't return any extra metadata, PDO relies on us to // Though we don't return any extra metadata, PDO relies on us to
// create the associative array that holds the standard information, // create the associative array that holds the standard information,
// so we create one and return it for PDO's use. // so we create one and return it for PDO's use.
// Parameters: // Parameters:
// stmt - Current statement. // stmt - Current statement.
@ -1092,9 +1092,9 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
sqlsrv_stmt* driver_stmt = static_cast<sqlsrv_stmt*>( stmt->driver_data ); sqlsrv_stmt* driver_stmt = static_cast<sqlsrv_stmt*>( stmt->driver_data );
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: stmt->driver_data was null"); SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: stmt->driver_data was null");
// Based on PDOStatement::getColumnMeta API, this should return FALSE // Based on PDOStatement::getColumnMeta API, this should return FALSE
// if the requested column does not exist in the result set, or if // if the requested column does not exist in the result set, or if
// no result set exists. Thus, do not use SQLSRV_ASSERT, which causes // no result set exists. Thus, do not use SQLSRV_ASSERT, which causes
// the script to fail right away. Instead, log this warning if logging // the script to fail right away. Instead, log this warning if logging
// is enabled // is enabled
if (colno < 0 || colno >= stmt->column_count || stmt->columns == NULL) { if (colno < 0 || colno >= stmt->column_count || stmt->columns == NULL) {
@ -1110,12 +1110,12 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
// metadata should have been saved earlier // metadata should have been saved earlier
SQLSRV_ASSERT(colno < driver_stmt->current_meta_data.size(), "pdo_sqlsrv_stmt_get_col_meta: Metadata vector out of sync with column numbers"); SQLSRV_ASSERT(colno < driver_stmt->current_meta_data.size(), "pdo_sqlsrv_stmt_get_col_meta: Metadata vector out of sync with column numbers");
core_meta_data = driver_stmt->current_meta_data[colno]; core_meta_data = driver_stmt->current_meta_data[colno];
// add the following fields: flags, native_type, driver:decl_type, table // add the following fields: flags, native_type, driver:decl_type, table
if (driver_stmt->data_classification) { if (driver_stmt->data_classification) {
core_sqlsrv_sensitivity_metadata(driver_stmt); core_sqlsrv_sensitivity_metadata(driver_stmt);
// initialize the column data classification array // initialize the column data classification array
zval data_classification; zval data_classification;
ZVAL_UNDEF(&data_classification); ZVAL_UNDEF(&data_classification);
array_init(&data_classification); array_init(&data_classification);
@ -1136,7 +1136,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
sizeof( field_type_name ), &out_buff_len, &not_used ); sizeof( field_type_name ), &out_buff_len, &not_used );
add_assoc_string( return_value, "sqlsrv:decl_type", field_type_name ); add_assoc_string( return_value, "sqlsrv:decl_type", field_type_name );
// get the PHP type of the column. The types returned here mirror the types returned by debug_zval_dump when // get the PHP type of the column. The types returned here mirror the types returned by debug_zval_dump when
// given a variable of the same type. However, debug_zval_dump also gives the length of a string, and we only // given a variable of the same type. However, debug_zval_dump also gives the length of a string, and we only
// say string, since the length is given in another field of the metadata array. // say string, since the length is given in another field of the metadata array.
long pdo_type = sql_type_to_pdo_type( core_meta_data->field_type ); long pdo_type = sql_type_to_pdo_type( core_meta_data->field_type );
@ -1179,7 +1179,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
zval_ptr_dtor(return_value); zval_ptr_dtor(return_value);
DIE( "pdo_sqlsrv_stmt_get_col_meta: Unknown exception occurred while retrieving metadata." ); DIE( "pdo_sqlsrv_stmt_get_col_meta: Unknown exception occurred while retrieving metadata." );
} }
return SUCCESS; return SUCCESS;
} }
@ -1187,7 +1187,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
// pdo_sqlsrv_stmt_next_rowset // pdo_sqlsrv_stmt_next_rowset
// Maps to PDOStatement::nextRowset. // Maps to PDOStatement::nextRowset.
// Move the cursor to the beginning of the next rowset in a multi-rowset result. // Move the cursor to the beginning of the next rowset in a multi-rowset result.
// Clears the field cache from the last row retrieved using pdo_sqlsrv_stmt_get_col_data. // Clears the field cache from the last row retrieved using pdo_sqlsrv_stmt_get_col_data.
// Calls core_sqlsrv_next_result using the core_stmt found within stmt->driver_data. // Calls core_sqlsrv_next_result using the core_stmt found within stmt->driver_data.
// If another result set is available, this function returns 1. Otherwise it returns 0. // If another result set is available, this function returns 1. Otherwise it returns 0.
// Parameters: // Parameters:
@ -1222,7 +1222,7 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt )
// return the row count regardless if there are any rows or not // return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt ); stmt->row_count = core::SQLRowCount( driver_stmt );
driver_stmt->column_count = stmt->column_count; driver_stmt->column_count = stmt->column_count;
driver_stmt->row_count = stmt->row_count; driver_stmt->row_count = stmt->row_count;
} }
@ -1240,16 +1240,16 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt )
// pdo_sqlsrv_stmt_param_hook // pdo_sqlsrv_stmt_param_hook
// Maps to PDOStatement::bindColumn. // Maps to PDOStatement::bindColumn.
// Called by PDO driver manager to bind a parameter or column. // Called by PDO driver manager to bind a parameter or column.
// This function pulls several duties for binding parameters and columns. // This function pulls several duties for binding parameters and columns.
// It takes an event as a parameter that explains what the function should do on // It takes an event as a parameter that explains what the function should do on
// behalf of a parameter or column. We only use one of these events, // behalf of a parameter or column. We only use one of these events,
// PDO_PARAM_EVT_EXEC_PRE, the remainder simply return. // PDO_PARAM_EVT_EXEC_PRE, the remainder simply return.
// Paramters: // Paramters:
// stmt - PDO Statement object to bind a parameter. // stmt - PDO Statement object to bind a parameter.
// param - paramter to bind. // param - paramter to bind.
// event_type - Event to bind a parameter // event_type - Event to bind a parameter
// Return: // Return:
// Returns 0 for failure, 1 for success. // Returns 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt, int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
_Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type) _Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type)
@ -1310,7 +1310,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
// that means they want output, and if they include the flag, then it's input/output. // that means they want output, and if they include the flag, then it's input/output.
// It's invalid to specify the input/output flag but not specify a length // It's invalid to specify the input/output flag but not specify a length
CHECK_CUSTOM_ERROR( (param->param_type & PDO_PARAM_INPUT_OUTPUT) && (param->max_value_len == 0), CHECK_CUSTOM_ERROR( (param->param_type & PDO_PARAM_INPUT_OUTPUT) && (param->max_value_len == 0),
driver_stmt, PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION, param->paramno + 1 ) { driver_stmt, PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION, param->paramno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
// if the parameter is output or input/output, translate the type between the PDO::PARAM_* constant // if the parameter is output or input/output, translate the type between the PDO::PARAM_* constant
@ -1328,7 +1328,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
} }
} }
// check if the user has specified the character set to use, take it off but ignore // check if the user has specified the character set to use, take it off but ignore
#if PHP_VERSION_ID >= 70200 #if PHP_VERSION_ID >= 70200
if ((pdo_type & PDO_PARAM_STR_NATL) == PDO_PARAM_STR_NATL) { if ((pdo_type & PDO_PARAM_STR_NATL) == PDO_PARAM_STR_NATL) {
pdo_type = pdo_type & ~PDO_PARAM_STR_NATL; pdo_type = pdo_type & ~PDO_PARAM_STR_NATL;
@ -1357,7 +1357,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
{ {
zval null_zval; zval null_zval;
php_out_type = SQLSRV_PHPTYPE_NULL; php_out_type = SQLSRV_PHPTYPE_NULL;
ZVAL_NULL( &null_zval ); ZVAL_NULL( &null_zval );
zval_ptr_dtor( &param->parameter ); zval_ptr_dtor( &param->parameter );
param->parameter = null_zval; param->parameter = null_zval;
@ -1396,8 +1396,8 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
break; break;
case SQLSRV_PHPTYPE_STRING: case SQLSRV_PHPTYPE_STRING:
{ {
CHECK_CUSTOM_ERROR( param->max_value_len <= 0, driver_stmt, CHECK_CUSTOM_ERROR( param->max_value_len <= 0, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE, param->paramno + 1 ) { PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE, param->paramno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
column_size = param->max_value_len; column_size = param->max_value_len;
@ -1409,14 +1409,14 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
} }
} }
// block all objects from being bound as input or input/output parameters since there is a // block all objects from being bound as input or input/output parameters since there is a
// weird case: // weird case:
// $obj = date_create(); // $obj = date_create();
// $s->bindParam( n, $obj, PDO::PARAM_INT ); // anything different than PDO::PARAM_STR // $s->bindParam( n, $obj, PDO::PARAM_INT ); // anything different than PDO::PARAM_STR
// that succeeds since the core layer implements DateTime object handling for the sqlsrv // that succeeds since the core layer implements DateTime object handling for the sqlsrv
// 2.0 driver. To be consistent and avoid surprises of one object type working and others // 2.0 driver. To be consistent and avoid surprises of one object type working and others
// not, we block all objects here. // not, we block all objects here.
CHECK_CUSTOM_ERROR( direction != SQL_PARAM_OUTPUT && Z_TYPE( param->parameter ) == IS_OBJECT, CHECK_CUSTOM_ERROR( direction != SQL_PARAM_OUTPUT && Z_TYPE( param->parameter ) == IS_OBJECT,
driver_stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param->paramno + 1 ) { driver_stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param->paramno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
@ -1428,15 +1428,15 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
} }
// Beginning with PHP7.2 the user can specify whether to use PDO_PARAM_STR_CHAR or PDO_PARAM_STR_NATL // Beginning with PHP7.2 the user can specify whether to use PDO_PARAM_STR_CHAR or PDO_PARAM_STR_NATL
// But this extended type will be ignored in real prepared statements, so the encoding deliberately // But this extended type will be ignored in real prepared statements, so the encoding deliberately
// set in the statement or driver options will still take precedence // set in the statement or driver options will still take precedence
if( !Z_ISUNDEF(param->driver_params) ) { if( !Z_ISUNDEF(param->driver_params) ) {
CHECK_CUSTOM_ERROR( Z_TYPE( param->driver_params ) != IS_LONG, driver_stmt, CHECK_CUSTOM_ERROR( Z_TYPE( param->driver_params ) != IS_LONG, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM ) { PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM ) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
CHECK_CUSTOM_ERROR( pdo_type != PDO_PARAM_STR && pdo_type != PDO_PARAM_LOB, driver_stmt, CHECK_CUSTOM_ERROR( pdo_type != PDO_PARAM_STR && pdo_type != PDO_PARAM_LOB, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE, param->paramno + 1 ) { PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE, param->paramno + 1, NULL) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params )); encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
@ -1448,7 +1448,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
break; break;
default: default:
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING, THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING,
param->paramno + 1 ); param->paramno + 1, NULL );
break; break;
} }
} }
@ -1500,7 +1500,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
local_encoding = conn->encoding(); local_encoding = conn->encoding();
SQLSRV_ASSERT( conn->encoding() != SQLSRV_ENCODING_DEFAULT || conn->encoding() == SQLSRV_ENCODING_INVALID, SQLSRV_ASSERT( conn->encoding() != SQLSRV_ENCODING_DEFAULT || conn->encoding() == SQLSRV_ENCODING_INVALID,
"Invalid encoding on the connection. Must not be invalid or default." ); "Invalid encoding on the connection. Must not be invalid or default." );
} }
sqlsrv_phptype.typeinfo.encoding = local_encoding; sqlsrv_phptype.typeinfo.encoding = local_encoding;
@ -1514,7 +1514,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
} }
else { else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING; sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR; sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
} }
break; break;
case SQL_FLOAT: case SQL_FLOAT:
@ -1524,7 +1524,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
} }
else { else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING; sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR; sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
} }
break; break;
case SQL_TYPE_DATE: case SQL_TYPE_DATE:
@ -1542,7 +1542,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
case SQL_DECIMAL: case SQL_DECIMAL:
case SQL_NUMERIC: case SQL_NUMERIC:
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING; sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR; sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
break; break;
case SQL_CHAR: case SQL_CHAR:
case SQL_GUID: case SQL_GUID:
@ -1567,6 +1567,6 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_INVALID; sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_INVALID;
break; break;
} }
return sqlsrv_phptype; return sqlsrv_phptype;
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: Utility functions used by both connection or statement functions // Contents: Utility functions used by both connection or statement functions
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -6,7 +6,7 @@
// //
// Contents: Declarations for the extension // Contents: Declarations for the extension
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -6,7 +6,7 @@
// //
// Contents: Internal declarations for the extension // Contents: Internal declarations for the extension
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Version resource // Contents: Version resource
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -6,7 +6,7 @@
// Contents: Contains functions for handling Windows format strings // Contents: Contains functions for handling Windows format strings
// and UTF-16 on non-Windows platforms // and UTF-16 on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// Contents: Contains functions for handling Windows format strings // Contents: Contains functions for handling Windows format strings
// and UTF-16 on non-Windows platforms // and UTF-16 on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains functions for handling UTF-16 on non-Windows platforms // Contents: Contains functions for handling UTF-16 on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains functions for handling UTF-16 on non-Windows platforms // Contents: Contains functions for handling UTF-16 on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv // Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
@ -160,7 +160,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
// In Windows, we try to connect with ODBC driver first and rely on the returned error code to try connecting with other supported ODBC drivers // In Windows, we try to connect with ODBC driver first and rely on the returned error code to try connecting with other supported ODBC drivers
if (conn->driver_version != ODBC_DRIVER::VER_UNKNOWN) { if (conn->driver_version != ODBC_DRIVER::VER_UNKNOWN) {
// if column encryption is enabled, must use ODBC driver 17 or above // if column encryption is enabled, must use ODBC driver 17 or above
CHECK_CUSTOM_ERROR(conn->ce_option.enabled && conn->driver_version == ODBC_DRIVER::VER_13, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) { CHECK_CUSTOM_ERROR(conn->ce_option.enabled && conn->driver_version == ODBC_DRIVER::VER_13, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -182,7 +182,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
#endif #endif
} }
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;
@ -208,12 +208,12 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
#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(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
// here it means that none of the supported ODBC drivers is found // here it means that none of the supported ODBC drivers is found
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(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -230,7 +230,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
throw core::CoreException(); throw core::CoreException();
} }
CHECK_SQL_WARNING_AS_ERROR( r, conn ) { CHECK_SQL_WARNING_AS_ERROR( r, conn, NULL ) {
throw core::CoreException(); throw core::CoreException();
} }
@ -324,7 +324,7 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con
// Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( conn_str.length() ), &wconn_len, true ); wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( conn_str.length() ), &wconn_len, true );
CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message()) CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message(), NULL)
{ {
throw core::CoreException(); throw core::CoreException();
} }
@ -487,7 +487,7 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding() ); SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding() );
wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ), static_cast<int>( sql_len ), &wsql_len ); wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ), static_cast<int>( sql_len ), &wsql_len );
CHECK_CUSTOM_ERROR( wsql_string == 0, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message() ) { CHECK_CUSTOM_ERROR( wsql_string == 0, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -796,7 +796,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." ); DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." );
conn_opt = get_connection_option( conn, index, valid_conn_opts ); conn_opt = get_connection_option( conn, index, valid_conn_opts );
if (conn_opt == NULL) throw;
if( index == SQLSRV_CONN_OPTION_MARS ) { if( index == SQLSRV_CONN_OPTION_MARS ) {
mars_mentioned = true; mars_mentioned = true;
} }
@ -1051,7 +1051,7 @@ void driver_set_func::func(_In_ connection_option const* option, _In_ zval* valu
} }
} }
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), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1147,6 +1147,7 @@ size_t core_str_zval_is_true(_Inout_ zval* value_z)
const char TRUE_VALUE_1[] = "true"; const char TRUE_VALUE_1[] = "true";
const char TRUE_VALUE_2[] = "1"; const char TRUE_VALUE_2[] = "1";
transform(val_str.begin(), val_str.end(), val_str.begin(), ::tolower);
if (!val_str.compare(TRUE_VALUE_1) || !val_str.compare(TRUE_VALUE_2)) { if (!val_str.compare(TRUE_VALUE_1) || !val_str.compare(TRUE_VALUE_2)) {
return 1; // true return 1; // true
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: common initialization routines shared by PDO and sqlsrv // Contents: common initialization routines shared by PDO and sqlsrv
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,17 +3,17 @@
// //
// Contents: Result sets // Contents: Result sets
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -48,7 +48,7 @@ const int INITIAL_LOB_FIELD_LEN = 2048; // base allocation size when retrie
template <int align, typename T> template <int align, typename T>
T align_to( _In_ T number ) T align_to( _In_ T number )
{ {
DEBUG_SQLSRV_ASSERT( (number + align) > number, "Number to align overflowed" ); DEBUG_SQLSRV_ASSERT( (number + align) > number, "Number to align overflowed" );
return ((number % align) == 0) ? number : (number + align - (number % align)); return ((number % align) == 0) ? number : (number + align - (number % align));
} }
@ -92,7 +92,7 @@ size_t get_float_precision( _In_ SQLLEN buffer_length, _In_ size_t unitsize)
// use the display size to determine the sql type. And if it is a double, set the precision accordingly // use the display size to determine the sql type. And if it is a double, set the precision accordingly
// the display sizes are set by the ODBC driver based on the precision of the sql type // the display sizes are set by the ODBC driver based on the precision of the sql type
// otherwise we can just use the default precision // otherwise we can just use the default precision
size_t real_display_size = 14; size_t real_display_size = 14;
size_t float_display_size = 24; size_t float_display_size = 24;
size_t real_precision = 7; size_t real_precision = 7;
@ -206,14 +206,14 @@ SQLRETURN number_to_string( _In_ Number* number_data, _Out_writes_bytes_to_opt_(
{ {
std::basic_string<char16_t> str; std::basic_string<char16_t> str;
for (const auto &mb : str_num ) for (const auto &mb : str_num )
{ {
size_t cch = SystemLocale::NextChar( CP_ACP, &mb ) - &mb; size_t cch = SystemLocale::NextChar( CP_ACP, &mb ) - &mb;
if ( cch > 0 ) if ( cch > 0 )
{ {
WCHAR ch16; WCHAR ch16;
DWORD rc; DWORD rc;
size_t cchActual = SystemLocale::ToUtf16( CP_ACP, &mb, cch, &ch16, 1, &rc); size_t cchActual = SystemLocale::ToUtf16( CP_ACP, &mb, cch, &ch16, 1, &rc);
if (cchActual > 0) if (cchActual > 0)
{ {
@ -242,8 +242,8 @@ std::string getUTF8StringFromString( _In_z_ const SQLWCHAR* source )
SQLLEN i = 0; SQLLEN i = 0;
std::string str; std::string str;
while ( source[i] ) while ( source[i] )
{ {
memset( c_str, 0, sizeof( c_str ) ); memset( c_str, 0, sizeof( c_str ) );
int cch = 0; int cch = 0;
errno_t err = mplat_wctomb_s( &cch, c_str, sizeof( c_str ), source[i++] ); errno_t err = mplat_wctomb_s( &cch, c_str, sizeof( c_str ), source[i++] );
if ( cch > 0 && err == ERROR_SUCCESS ) if ( cch > 0 && err == ERROR_SUCCESS )
@ -271,7 +271,7 @@ struct row_dtor_closure {
row_dtor_closure( _In_ sqlsrv_buffered_result_set* st, _In_ BYTE* row ) : row_dtor_closure( _In_ sqlsrv_buffered_result_set* st, _In_ BYTE* row ) :
results( st ), row_data( row ) results( st ), row_data( row )
{ {
} }
}; };
sqlsrv_error* odbc_get_diag_rec( _In_ sqlsrv_stmt* odbc, _In_ SQLSMALLINT record_number ) sqlsrv_error* odbc_get_diag_rec( _In_ sqlsrv_stmt* odbc, _In_ SQLSMALLINT record_number )
@ -280,9 +280,9 @@ sqlsrv_error* odbc_get_diag_rec( _In_ sqlsrv_stmt* odbc, _In_ SQLSMALLINT record
SQLWCHAR wnative_message[SQL_MAX_ERROR_MESSAGE_LENGTH + 1] = {L'\0'}; SQLWCHAR wnative_message[SQL_MAX_ERROR_MESSAGE_LENGTH + 1] = {L'\0'};
SQLINTEGER native_code; SQLINTEGER native_code;
SQLSMALLINT wnative_message_len = 0; SQLSMALLINT wnative_message_len = 0;
SQLSRV_ASSERT(odbc != NULL, "odbc_get_diag_rec: sqlsrv_stmt* odbc was null."); SQLSRV_ASSERT(odbc != NULL, "odbc_get_diag_rec: sqlsrv_stmt* odbc was null.");
SQLRETURN r = SQLGetDiagRecW( SQL_HANDLE_STMT, odbc->handle(), record_number, wsql_state, &native_code, wnative_message, SQLRETURN r = SQLGetDiagRecW( SQL_HANDLE_STMT, odbc->handle(), record_number, wsql_state, &native_code, wnative_message,
SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wnative_message_len ); SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wnative_message_len );
if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) {
return NULL; return NULL;
@ -300,14 +300,14 @@ sqlsrv_error* odbc_get_diag_rec( _In_ sqlsrv_stmt* odbc, _In_ SQLSMALLINT record
if ( !convert_string_from_utf16( enc, wsql_state, SQL_SQLSTATE_BUFSIZE, (char**)&sql_state, sql_state_len )) { if ( !convert_string_from_utf16( enc, wsql_state, SQL_SQLSTATE_BUFSIZE, (char**)&sql_state, sql_state_len )) {
return NULL; return NULL;
} }
sqlsrv_malloc_auto_ptr<SQLCHAR> native_message; sqlsrv_malloc_auto_ptr<SQLCHAR> native_message;
SQLLEN native_message_len = 0; SQLLEN native_message_len = 0;
if (!convert_string_from_utf16( enc, wnative_message, wnative_message_len, (char**)&native_message, native_message_len )) { if (!convert_string_from_utf16( enc, wnative_message, wnative_message_len, (char**)&native_message, native_message_len )) {
return NULL; return NULL;
} }
return new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) sql_state, (SQLCHAR*) native_message, return new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) sql_state, (SQLCHAR*) native_message,
native_code ); native_code );
} }
@ -324,7 +324,7 @@ sqlsrv_result_set::sqlsrv_result_set( _In_ sqlsrv_stmt* stmt ) :
// ODBC result set // ODBC result set
// This object simply wraps ODBC function calls // This object simply wraps ODBC function calls
sqlsrv_odbc_result_set::sqlsrv_odbc_result_set( _In_ sqlsrv_stmt* stmt ) : sqlsrv_odbc_result_set::sqlsrv_odbc_result_set( _In_ sqlsrv_stmt* stmt ) :
sqlsrv_result_set( stmt ) sqlsrv_result_set( stmt )
{ {
} }
@ -347,12 +347,12 @@ SQLRETURN sqlsrv_odbc_result_set::get_data( _In_ SQLUSMALLINT field_index, _In_
return core::SQLGetData( odbc, field_index, target_type, buffer, buffer_length, out_buffer_length, handle_warning ); return core::SQLGetData( odbc, field_index, target_type, buffer, buffer_length, out_buffer_length, handle_warning );
} }
SQLRETURN sqlsrv_odbc_result_set::get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier, SQLRETURN sqlsrv_odbc_result_set::get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length, _Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length ) _Inout_ SQLSMALLINT* out_buffer_length )
{ {
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length, return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length,
out_buffer_length ); out_buffer_length );
} }
@ -388,7 +388,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
} }
SQLULEN null_bytes = ( col_count / 8 ) + 1; // number of bits to reserve at the beginning of each row for NULL flags SQLULEN null_bytes = ( col_count / 8 ) + 1; // number of bits to reserve at the beginning of each row for NULL flags
meta = static_cast<sqlsrv_buffered_result_set::meta_data*>( sqlsrv_malloc( col_count * meta = static_cast<sqlsrv_buffered_result_set::meta_data*>( sqlsrv_malloc( col_count *
sizeof( sqlsrv_buffered_result_set::meta_data ))); sizeof( sqlsrv_buffered_result_set::meta_data )));
SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() :
@ -397,7 +397,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
// get the meta data and calculate the size of a row buffer // get the meta data and calculate the size of a row buffer
SQLULEN offset = null_bytes; SQLULEN offset = null_bytes;
for( SQLSMALLINT i = 0; i < col_count; ++i ) { for( SQLSMALLINT i = 0; i < col_count; ++i ) {
core::SQLDescribeColW( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL ); core::SQLDescribeColW( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL );
offset = align_to<sizeof(SQLPOINTER)>( offset ); offset = align_to<sizeof(SQLPOINTER)>( offset );
@ -423,7 +423,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
} }
else { else {
// If encoding is set to UTF-8, the following types are not necessarily column size. // If encoding is set to UTF-8, the following types are not necessarily column size.
// We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly.
if ( encoding == SQLSRV_ENCODING_UTF8 ) { if ( encoding == SQLSRV_ENCODING_UTF8 ) {
meta[i].length *= sizeof( WCHAR ); meta[i].length *= sizeof( WCHAR );
meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space
@ -477,7 +477,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
case SQL_SS_TIME2: case SQL_SS_TIME2:
case SQL_SS_TIMESTAMPOFFSET: case SQL_SS_TIMESTAMPOFFSET:
case SQL_TYPE_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL,
reinterpret_cast<SQLLEN*>( &meta[i].length ) ); reinterpret_cast<SQLLEN*>( &meta[i].length ) );
meta[i].length += sizeof(char) + sizeof( SQLULEN ); // null terminator space meta[i].length += sizeof(char) + sizeof( SQLULEN ); // null terminator space
offset += meta[i].length; offset += meta[i].length;
@ -517,13 +517,13 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
case SQL_TYPE_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
meta[i].c_type = SQL_C_CHAR; meta[i].c_type = SQL_C_CHAR;
break; break;
case SQL_CHAR: case SQL_CHAR:
case SQL_VARCHAR: case SQL_VARCHAR:
case SQL_SS_VARIANT: case SQL_SS_VARIANT:
case SQL_LONGVARCHAR: case SQL_LONGVARCHAR:
// If encoding is set to UTF-8, the following types are not necessarily column size. // If encoding is set to UTF-8, the following types are not necessarily column size.
// We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly.
if ( encoding == SQLSRV_ENCODING_UTF8 ) { if ( encoding == SQLSRV_ENCODING_UTF8 ) {
meta[i].c_type = SQL_C_WCHAR; meta[i].c_type = SQL_C_WCHAR;
} }
@ -531,7 +531,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
meta[i].c_type = SQL_C_CHAR; meta[i].c_type = SQL_C_CHAR;
} }
break; break;
case SQL_SS_UDT: case SQL_SS_UDT:
case SQL_LONGVARBINARY: case SQL_LONGVARBINARY:
case SQL_BINARY: case SQL_BINARY:
@ -574,7 +574,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
try { try {
while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 ) != SQL_NO_DATA ) { while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 ) != SQL_NO_DATA ) {
// allocate the row buffer // allocate the row buffer
sqlsrv_malloc_auto_ptr<unsigned char> rowAuto; sqlsrv_malloc_auto_ptr<unsigned char> rowAuto;
rowAuto = static_cast<unsigned char*>( sqlsrv_malloc( offset )); rowAuto = static_cast<unsigned char*>( sqlsrv_malloc( offset ));
@ -610,15 +610,15 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
else { else {
mem_used += meta[i].length; mem_used += meta[i].length;
CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt, CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
buffer = row + meta[i].offset + sizeof( SQLULEN ); buffer = row + meta[i].offset + sizeof( SQLULEN );
out_buffer_length = reinterpret_cast<SQLLEN*>( row + meta[i].offset ); out_buffer_length = reinterpret_cast<SQLLEN*>( row + meta[i].offset );
core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length, core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length,
false ); false );
} }
break; break;
@ -627,17 +627,17 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
case SQL_C_DOUBLE: case SQL_C_DOUBLE:
{ {
mem_used += meta[i].length; mem_used += meta[i].length;
CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt, CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
buffer = row + meta[i].offset; buffer = row + meta[i].offset;
out_buffer_length = &out_buffer_temp; out_buffer_length = &out_buffer_temp;
core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length, core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length,
false ); false );
} }
break; break;
default: default:
SQLSRV_ASSERT( false, "Unknown C type" ); SQLSRV_ASSERT( false, "Unknown C type" );
@ -656,8 +656,8 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
row_dtor_closure cl( this, row ); row_dtor_closure cl( this, row );
sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) ); sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) );
rowAuto.transferred(); rowAuto.transferred();
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
// free the rows // free the rows
if( cache ) { if( cache ) {
@ -753,7 +753,7 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
*out_buffer_length = SQL_NULL_DATA; *out_buffer_length = SQL_NULL_DATA;
return SQL_SUCCESS; return SQL_SUCCESS;
} }
// check to make sure the conversion type is valid // check to make sure the conversion type is valid
switch (meta[field_index].c_type) { switch (meta[field_index].c_type) {
case SQL_C_CHAR: case SQL_C_CHAR:
@ -817,21 +817,21 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
return SQL_ERROR; return SQL_ERROR;
} }
SQLRETURN sqlsrv_buffered_result_set::get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier, SQLRETURN sqlsrv_buffered_result_set::get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length, _Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length ) _Inout_ SQLSMALLINT* out_buffer_length )
{ {
SQLSRV_ASSERT( record_number == 1, "Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field" ); SQLSRV_ASSERT( record_number == 1, "Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field" );
SQLSRV_ASSERT( diag_identifier == SQL_DIAG_SQLSTATE, SQLSRV_ASSERT( diag_identifier == SQL_DIAG_SQLSTATE,
"Only SQL_DIAG_SQLSTATE can be fetched by sqlsrv_buffered_result_set::get_diag_field" ); "Only SQL_DIAG_SQLSTATE can be fetched by sqlsrv_buffered_result_set::get_diag_field" );
SQLSRV_ASSERT( buffer_length >= SQL_SQLSTATE_BUFSIZE, SQLSRV_ASSERT( buffer_length >= SQL_SQLSTATE_BUFSIZE,
"Buffer not big enough to return SQLSTATE in sqlsrv_buffered_result_set::get_diag_field" ); "Buffer not big enough to return SQLSTATE in sqlsrv_buffered_result_set::get_diag_field" );
if( last_error == 0 ) { if( last_error == 0 ) {
return SQL_NO_DATA; return SQL_NO_DATA;
} }
SQLSRV_ASSERT( last_error->sqlstate != NULL, SQLSRV_ASSERT( last_error->sqlstate != NULL,
"Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field" ); "Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field" );
SQLSMALLINT bufsize = ( buffer_length < SQL_SQLSTATE_BUFSIZE ) ? buffer_length : SQL_SQLSTATE_BUFSIZE; SQLSMALLINT bufsize = ( buffer_length < SQL_SQLSTATE_BUFSIZE ) ? buffer_length : SQL_SQLSTATE_BUFSIZE;
@ -858,8 +858,8 @@ sqlsrv_error* sqlsrv_buffered_result_set::get_diag_rec( _In_ SQLSMALLINT record_
if( record_number > 1 ) { if( record_number > 1 ) {
return NULL; return NULL;
} }
return new (sqlsrv_malloc( sizeof( sqlsrv_error ))) return new (sqlsrv_malloc( sizeof( sqlsrv_error )))
sqlsrv_error( last_error->sqlstate, last_error->native_message, last_error->native_code ); sqlsrv_error( last_error->sqlstate, last_error->native_message, last_error->native_code );
} }
@ -879,7 +879,7 @@ SQLLEN sqlsrv_buffered_result_set::row_count( void )
// private functions // private functions
template <typename Char> template <typename Char>
SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so_far, _Out_writes_z_(*out_buffer_length) void* buffer, SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so_far, _Out_writes_z_(*out_buffer_length) void* buffer,
_In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
_Inout_ sqlsrv_error_auto_ptr& out_error ) _Inout_ sqlsrv_error_auto_ptr& out_error )
{ {
// The hex characters for the conversion loop below // The hex characters for the conversion loop below
@ -892,7 +892,7 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
// Set the amount of space necessary for null characters at the end of the data. // Set the amount of space necessary for null characters at the end of the data.
SQLSMALLINT extra = sizeof(Char); SQLSMALLINT extra = sizeof(Char);
// TO convert a binary to a system string or a binary to a wide string, the buffer size minus // TO convert a binary to a system string or a binary to a wide string, the buffer size minus
// 'extra' is ideally multiples of 2 or 4 (depending on Char), but calculating to_copy_hex below // 'extra' is ideally multiples of 2 or 4 (depending on Char), but calculating to_copy_hex below
// takes care of this. // takes care of this.
@ -900,7 +900,7 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
// the entire length of the string is returned the first // the entire length of the string is returned the first
// call in out_buffer_len. Successive calls return how much is // call in out_buffer_len. Successive calls return how much is
// left minus how much has already been read by previous reads // left minus how much has already been read by previous reads
// *2 is for each byte to hex conversion and * extra is for either system // *2 is for each byte to hex conversion and * extra is for either system
// or wide string allocation // or wide string allocation
*out_buffer_length = (*reinterpret_cast<SQLLEN*>( field_data - sizeof( SQLULEN )) - read_so_far) * 2 * extra; *out_buffer_length = (*reinterpret_cast<SQLLEN*>( field_data - sizeof( SQLULEN )) - read_so_far) * 2 * extra;
@ -908,7 +908,7 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
SQLLEN to_copy; SQLLEN to_copy;
if( buffer_length < *out_buffer_length + extra ) { if( buffer_length < *out_buffer_length + extra ) {
to_copy = (buffer_length - extra); to_copy = (buffer_length - extra);
out_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) out_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error )))
sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 );
r = SQL_SUCCESS_WITH_INFO; r = SQL_SUCCESS_WITH_INFO;
} }
@ -990,13 +990,13 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_long( _In_ SQLSMALLINT field_ind
LONG* long_data = reinterpret_cast<LONG*>( buffer ); LONG* long_data = reinterpret_cast<LONG*>( buffer );
if( *double_data < double( LONG_MIN ) || *double_data > double( LONG_MAX )) { if( *double_data < double( LONG_MIN ) || *double_data > double( LONG_MAX )) {
last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "22003", last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "22003",
(SQLCHAR*) "Numeric value out of range", 0 ); (SQLCHAR*) "Numeric value out of range", 0 );
return SQL_ERROR; return SQL_ERROR;
} }
if( *double_data != floor( *double_data )) { if( *double_data != floor( *double_data )) {
last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "01S07", last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "01S07",
(SQLCHAR*) "Fractional truncation", 0 ); (SQLCHAR*) "Fractional truncation", 0 );
return SQL_SUCCESS_WITH_INFO; return SQL_SUCCESS_WITH_INFO;
} }
@ -1024,7 +1024,7 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( _In_ SQLSMALLINT
return r; return r;
} }
SQLRETURN sqlsrv_buffered_result_set::long_to_double( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length, SQLRETURN sqlsrv_buffered_result_set::long_to_double( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length ) _Out_ SQLLEN* out_buffer_length )
{ {
SQLSRV_ASSERT( meta[field_index].c_type == SQL_C_LONG, "Invalid conversion to long" ); SQLSRV_ASSERT( meta[field_index].c_type == SQL_C_LONG, "Invalid conversion to long" );
@ -1148,7 +1148,7 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( _In_ SQLSMALLINT field_in
return SQL_SUCCESS; return SQL_SUCCESS;
} }
SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( _In_ SQLSMALLINT field_index, _Out_writes_z_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length, SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( _In_ SQLSMALLINT field_index, _Out_writes_z_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length ) _Out_ SQLLEN* out_buffer_length )
{ {
SQLSRV_ASSERT( last_error == 0, "Pending error for sqlsrv_buffered_results_set::system_to_wide_string" ); SQLSRV_ASSERT( last_error == 0, "Pending error for sqlsrv_buffered_results_set::system_to_wide_string" );
@ -1184,7 +1184,7 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( _In_ SQLSMALLINT fi
if( (size_t) buffer_length < (field_len - read_so_far + sizeof(char)) * sizeof(WCHAR)) { if( (size_t) buffer_length < (field_len - read_so_far + sizeof(char)) * sizeof(WCHAR)) {
to_copy = (buffer_length - sizeof(WCHAR)) / sizeof(WCHAR); // to_copy is the number of characters to_copy = (buffer_length - sizeof(WCHAR)) / sizeof(WCHAR); // to_copy is the number of characters
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error )))
sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 );
r = SQL_SUCCESS_WITH_INFO; r = SQL_SUCCESS_WITH_INFO;
} }
@ -1204,20 +1204,20 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( _In_ SQLSMALLINT fi
} }
#ifndef _WIN32 #ifndef _WIN32
int ch_space = SystemLocale::ToUtf16( CP_ACP, (LPCSTR) field_data, static_cast<int>(to_copy), int ch_space = SystemLocale::ToUtf16( CP_ACP, (LPCSTR) field_data, static_cast<int>(to_copy),
static_cast<LPWSTR>(buffer), static_cast<int>(to_copy)); static_cast<LPWSTR>(buffer), static_cast<int>(to_copy));
#else #else
int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, static_cast<int>(to_copy), int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, static_cast<int>(to_copy),
static_cast<LPWSTR>(buffer), static_cast<int>(to_copy)); static_cast<LPWSTR>(buffer), static_cast<int>(to_copy));
#endif // !_WIN32 #endif // !_WIN32
if( ch_space == 0 ) { if( ch_space == 0 ) {
switch( GetLastError() ) { switch( GetLastError() ) {
case ERROR_NO_UNICODE_TRANSLATION: case ERROR_NO_UNICODE_TRANSLATION:
// the theory here is the conversion failed because the end of the buffer we provided contained only // the theory here is the conversion failed because the end of the buffer we provided contained only
// half a character at the end // half a character at the end
if( !tried_again ) { if( !tried_again ) {
to_copy--; to_copy--;
@ -1295,7 +1295,7 @@ SQLRETURN sqlsrv_buffered_result_set::to_same_string( _In_ SQLSMALLINT field_ind
SQLLEN to_copy; SQLLEN to_copy;
if( buffer_length < *out_buffer_length + extra ) { if( buffer_length < *out_buffer_length + extra ) {
to_copy = buffer_length - extra; to_copy = buffer_length - extra;
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error )))
sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 );
r = SQL_SUCCESS_WITH_INFO; r = SQL_SUCCESS_WITH_INFO;
} }
@ -1355,17 +1355,17 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( _In_ SQLSMALLINT fi
// allocate enough to handle WC -> DBCS conversion if it happens // allocate enough to handle WC -> DBCS conversion if it happens
temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof(char), sizeof(char))); temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof(char), sizeof(char)));
#ifndef _WIN32 #ifndef _WIN32
temp_length = SystemLocale::FromUtf16( CP_ACP, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)), temp_length = SystemLocale::FromUtf16( CP_ACP, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
(LPSTR) temp_string.get(), static_cast<int>(field_len) ); (LPSTR) temp_string.get(), static_cast<int>(field_len) );
#else #else
BOOL default_char_used = FALSE; BOOL default_char_used = FALSE;
char default_char = '?'; char default_char = '?';
temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)), temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
(LPSTR) temp_string.get(), static_cast<int>(field_len), &default_char, &default_char_used ); (LPSTR) temp_string.get(), static_cast<int>(field_len), &default_char, &default_char_used );
#endif // !_WIN32 #endif // !_WIN32
if( temp_length == 0 ) { if( temp_length == 0 ) {
switch( GetLastError() ) { switch( GetLastError() ) {
@ -1390,7 +1390,7 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( _In_ SQLSMALLINT fi
if( (size_t) buffer_length < (temp_length - read_so_far + sizeof(char))) { if( (size_t) buffer_length < (temp_length - read_so_far + sizeof(char))) {
to_copy = buffer_length - sizeof(char); to_copy = buffer_length - sizeof(char);
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error )))
sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 );
r = SQL_SUCCESS_WITH_INFO; r = SQL_SUCCESS_WITH_INFO;
} }
@ -1469,7 +1469,7 @@ void cache_row_dtor( _In_ zval* data )
sqlsrv_free( cl ); sqlsrv_free( cl );
} }
SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ sqlsrv_buffered_result_set::meta_data& meta, SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ sqlsrv_buffered_result_set::meta_data& meta,
_In_ zend_long mem_used ) _In_ zend_long mem_used )
{ {
SQLSMALLINT extra = 0; SQLSMALLINT extra = 0;
@ -1515,8 +1515,8 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
// if the last read was successful, we're done // if the last read was successful, we're done
if( r == SQL_SUCCESS ) { if( r == SQL_SUCCESS ) {
// check to make sure we haven't overflown our memory limit // check to make sure we haven't overflown our memory limit
CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt, CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1525,7 +1525,7 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
// else if it wasn't the truncated warning (01004) then we're done // else if it wasn't the truncated warning (01004) then we're done
else if( r == SQL_SUCCESS_WITH_INFO ) { else if( r == SQL_SUCCESS_WITH_INFO ) {
SQLSMALLINT len; SQLSMALLINT len;
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
); );
if( !is_truncated_warning( state )) { if( !is_truncated_warning( state )) {
@ -1539,8 +1539,8 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
// if the type of the field returns the total to be read, we use that and preallocate the buffer // if the type of the field returns the total to be read, we use that and preallocate the buffer
if( last_field_len != SQL_NO_TOTAL ) { if( last_field_len != SQL_NO_TOTAL ) {
CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt, CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1554,8 +1554,8 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
// otherwise allocate another chunk of memory to read in // otherwise allocate another chunk of memory to read in
else { else {
to_read *= 2; to_read *= 2;
CHECK_CUSTOM_ERROR( mem_used + to_read > stmt->buffered_query_limit * 1024, stmt, CHECK_CUSTOM_ERROR( mem_used + to_read > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit, NULL) {
throw core::CoreException(); throw core::CoreException();
} }

View file

@ -6,7 +6,7 @@
// //
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server // Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
@ -375,7 +375,7 @@ inline void sqlsrv_free_trace( _Inout_ void* ptr, _In_ const char* file, _In_ in
#else #else
// Unlike their C standard library's counterparts the Zend Engine's memory management functions // Unlike their C standard library's counterparts the Zend Engine's memory management functions
// emalloc or erealloc won't return NULL in case of an problem while allocating the requested // emalloc or erealloc won't return NULL in case of an problem while allocating the requested
// memory but bail out and terminate the current request. // memory but bail out and terminate the current request.
// Check www.phpinternalsbook.com/php7/memory_management/zend_memory_manager.html for details // Check www.phpinternalsbook.com/php7/memory_management/zend_memory_manager.html for details
inline void* sqlsrv_malloc( _In_ size_t size ) inline void* sqlsrv_malloc( _In_ size_t size )
@ -1060,7 +1060,7 @@ enum SERVER_VERSION {
// supported driver versions. // supported driver versions.
// ODBC 17 is the default // ODBC 17 is the default
enum class ODBC_DRIVER : int enum class ODBC_DRIVER : int
{ {
VER_17 = 17, VER_17 = 17,
VER_18 = 18, VER_18 = 18,
VER_13 = 13, VER_13 = 13,
@ -1416,7 +1416,7 @@ struct sqlsrv_param
zval* param_ptr_z; // NULL by default - points to the original parameter or its reference zval* param_ptr_z; // NULL by default - points to the original parameter or its reference
std::size_t num_bytes_read; // 0 by default - number of bytes processed so far (for an empty PHP stream, an empty string is sent to the server) std::size_t num_bytes_read; // 0 by default - number of bytes processed so far (for an empty PHP stream, an empty string is sent to the server)
php_stream* param_stream; // NULL by default - used to send stream data from an input parameter to the server php_stream* param_stream; // NULL by default - used to send stream data from an input parameter to the server
sqlsrv_param(_In_ SQLUSMALLINT param_num, _In_ SQLSMALLINT dir, _In_ SQLSRV_ENCODING enc, _In_ SQLSMALLINT sql_type, _In_ SQLULEN col_size, _In_ SQLSMALLINT dec_digits) : sqlsrv_param(_In_ SQLUSMALLINT param_num, _In_ SQLSMALLINT dir, _In_ SQLSRV_ENCODING enc, _In_ SQLSMALLINT sql_type, _In_ SQLULEN col_size, _In_ SQLSMALLINT dec_digits) :
c_data_type(0), buffer(NULL), buffer_length(0), strlen_or_indptr(0), param_pos(param_num), direction(dir), encoding(enc), sql_data_type(sql_type), c_data_type(0), buffer(NULL), buffer_length(0), strlen_or_indptr(0), param_pos(param_num), direction(dir), encoding(enc), sql_data_type(sql_type),
column_size(col_size), decimal_digits(dec_digits), param_php_type(0), was_null(false), param_ptr_z(NULL), num_bytes_read(0), param_stream(NULL) column_size(col_size), decimal_digits(dec_digits), param_php_type(0), was_null(false), param_ptr_z(NULL), num_bytes_read(0), param_stream(NULL)
@ -1445,7 +1445,7 @@ struct sqlsrv_param
virtual void process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z); virtual void process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z);
virtual void process_resource_param(_Inout_ zval* param_z); virtual void process_resource_param(_Inout_ zval* param_z);
virtual void process_object_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z); virtual void process_object_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z);
virtual void bind_param(_Inout_ sqlsrv_stmt* stmt); virtual void bind_param(_Inout_ sqlsrv_stmt* stmt);
// The following methods are used to supply data to the server via SQLPutData // The following methods are used to supply data to the server via SQLPutData
@ -1479,7 +1479,7 @@ struct sqlsrv_param_inout : public sqlsrv_param
// Resize the output string buffer based on its properties and whether it is a numeric type // Resize the output string buffer based on its properties and whether it is a numeric type
void resize_output_string_buffer(_Inout_ zval* param_z, _In_ bool is_numeric_type); void resize_output_string_buffer(_Inout_ zval* param_z, _In_ bool is_numeric_type);
// A helper method called by finalize_output_value() to finalize output string parameters // A helper method called by finalize_output_value() to finalize output string parameters
void finalize_output_string(); void finalize_output_string();
}; };
@ -1493,7 +1493,7 @@ struct sqlsrv_param_inout : public sqlsrv_param
struct sqlsrv_param_tvp : public sqlsrv_param struct sqlsrv_param_tvp : public sqlsrv_param
{ {
std::map<SQLUSMALLINT, sqlsrv_param_tvp*> tvp_columns; // The constituent columns of the table-valued parameter std::map<SQLUSMALLINT, sqlsrv_param_tvp*> tvp_columns; // The constituent columns of the table-valued parameter
sqlsrv_param_tvp* parent_tvp; // For a TVP column to reference to the table-valued parameter. NULL if this is the TVP itself. sqlsrv_param_tvp* parent_tvp; // For a TVP column to reference to the table-valued parameter. NULL if this is the TVP itself.
int num_rows; // The total number of rows int num_rows; // The total number of rows
int current_row; // A counter to keep track of which row is to be processed int current_row; // A counter to keep track of which row is to be processed
@ -1514,7 +1514,7 @@ struct sqlsrv_param_tvp : public sqlsrv_param
// Change the column encoding based on the sql data type // Change the column encoding based on the sql data type
static void sql_type_to_encoding(_In_ SQLSMALLINT sql_type, _Inout_ SQLSRV_ENCODING* encoding); static void sql_type_to_encoding(_In_ SQLSMALLINT sql_type, _Inout_ SQLSRV_ENCODING* encoding);
// The following methods are only applicable to a table-valued parameter or its individual columns // The following methods are only applicable to a table-valued parameter or its individual columns
int parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z); int parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z);
void get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string* table_type_name, _In_ zend_string* schema_name); void get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string* table_type_name, _In_ zend_string* schema_name);
@ -2208,7 +2208,7 @@ namespace core {
// regular error handling // regular error handling
return; return;
} }
CHECK_SQL_ERROR_OR_WARNING( rtemp, stmt ) { CHECK_SQL_ERROR_OR_WARNING( rtemp, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2239,7 +2239,7 @@ namespace core {
SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier, SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier,
diag_info_buffer, buffer_length, out_buffer_length ); diag_info_buffer, buffer_length, out_buffer_length );
CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { CHECK_SQL_ERROR_OR_WARNING( r, ctx, NULL) {
throw CoreException(); throw CoreException();
} }
@ -2251,7 +2251,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr ); r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
CHECK_SQL_ERROR_OR_WARNING( r, InputHandle ) { CHECK_SQL_ERROR_OR_WARNING( r, InputHandle, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2272,7 +2272,7 @@ namespace core {
r = ::SQLBindParameter( stmt->handle(), ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize, r = ::SQLBindParameter( stmt->handle(), ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize,
DecimalDigits, ParameterValuePtr, BufferLength, StrLen_Or_IndPtr ); DecimalDigits, ParameterValuePtr, BufferLength, StrLen_Or_IndPtr );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2281,7 +2281,7 @@ namespace core {
{ {
SQLRETURN r = ::SQLCloseCursor( stmt->handle() ); SQLRETURN r = ::SQLCloseCursor( stmt->handle() );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2293,7 +2293,7 @@ namespace core {
SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char, SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num ); buffer_length, out_buffer_length, field_type_num );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2305,7 +2305,7 @@ namespace core {
SQLRETURN r = ::SQLColAttributeW( stmt->handle(), field_index, field_identifier, field_type_char, SQLRETURN r = ::SQLColAttributeW( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num ); buffer_length, out_buffer_length, field_type_num );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2318,7 +2318,7 @@ namespace core {
r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out, r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
data_type, col_size, decimal_digits, nullable); data_type, col_size, decimal_digits, nullable);
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2331,7 +2331,7 @@ namespace core {
r = ::SQLDescribeColW( stmt->handle(), colno, col_name, col_name_length, col_name_length_out, r = ::SQLDescribeColW( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
data_type, col_size, decimal_digits, nullable ); data_type, col_size, decimal_digits, nullable );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2342,7 +2342,7 @@ namespace core {
SQLRETURN r; SQLRETURN r;
r = ::SQLDescribeParam( stmt->handle(), paramno, data_type, col_size, decimal_digits, nullable ); r = ::SQLDescribeParam( stmt->handle(), paramno, data_type, col_size, decimal_digits, nullable );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2352,7 +2352,7 @@ namespace core {
SQLRETURN r; SQLRETURN r;
r = ::SQLNumParams( stmt->handle(), num_params ); r = ::SQLNumParams( stmt->handle(), num_params );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2361,7 +2361,7 @@ namespace core {
{ {
SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType ); SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType );
CHECK_SQL_ERROR_OR_WARNING( r, conn ) { CHECK_SQL_ERROR_OR_WARNING( r, conn, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2373,7 +2373,7 @@ namespace core {
check_for_mars_error( stmt, r ); check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2387,7 +2387,7 @@ namespace core {
check_for_mars_error( stmt, r ); check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
return r; return r;
@ -2401,7 +2401,7 @@ namespace core {
check_for_mars_error( stmt, r ); check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2412,7 +2412,7 @@ namespace core {
{ {
SQLRETURN r = ::SQLFetchScroll( stmt->handle(), fetch_orientation, fetch_offset ); SQLRETURN r = ::SQLFetchScroll( stmt->handle(), fetch_orientation, fetch_offset );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
return r; return r;
@ -2424,14 +2424,14 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLFreeHandle( ctx.handle_type(), ctx.handle() ); r = ::SQLFreeHandle( ctx.handle_type(), ctx.handle() );
CHECK_SQL_ERROR_OR_WARNING( r, ctx ) {} CHECK_SQL_ERROR_OR_WARNING( r, ctx, NULL ) {}
} }
inline void SQLGetStmtAttr( _Inout_ sqlsrv_stmt* stmt, _In_ SQLINTEGER attr, _Out_writes_opt_(buf_len) void* value_ptr, _In_ SQLINTEGER buf_len, _Out_opt_ SQLINTEGER* str_len) inline void SQLGetStmtAttr( _Inout_ sqlsrv_stmt* stmt, _In_ SQLINTEGER attr, _Out_writes_opt_(buf_len) void* value_ptr, _In_ SQLINTEGER buf_len, _Out_opt_ SQLINTEGER* str_len)
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLGetStmtAttr( stmt->handle(), attr, value_ptr, buf_len, str_len ); r = ::SQLGetStmtAttr( stmt->handle(), attr, value_ptr, buf_len, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2450,7 +2450,7 @@ namespace core {
} }
if( handle_warning ) { if( handle_warning ) {
CHECK_SQL_WARNING_AS_ERROR( r, stmt ) { CHECK_SQL_WARNING_AS_ERROR( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2465,7 +2465,7 @@ namespace core {
SQLRETURN r; SQLRETURN r;
r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len ); r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, conn ) { CHECK_SQL_ERROR_OR_WARNING( r, conn, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2476,7 +2476,7 @@ namespace core {
SQLRETURN r; SQLRETURN r;
r = ::SQLGetTypeInfo( stmt->handle(), data_type ); r = ::SQLGetTypeInfo( stmt->handle(), data_type );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2487,7 +2487,7 @@ namespace core {
{ {
SQLRETURN r = ::SQLMoreResults( stmt->handle() ); SQLRETURN r = ::SQLMoreResults( stmt->handle() );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2500,7 +2500,7 @@ namespace core {
SQLSMALLINT num_cols; SQLSMALLINT num_cols;
r = ::SQLNumResultCols( stmt->handle(), &num_cols ); r = ::SQLNumResultCols( stmt->handle(), &num_cols );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2513,7 +2513,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLParamData( stmt->handle(), value_ptr_ptr ); r = ::SQLParamData( stmt->handle(), value_ptr_ptr );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
return r; return r;
@ -2523,7 +2523,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLPrepareW( stmt->handle(), sql, sql_len ); r = ::SQLPrepareW( stmt->handle(), sql, sql_len );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2533,7 +2533,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLPutData( stmt->handle(), data_ptr, strlen_or_ind ); r = ::SQLPutData( stmt->handle(), data_ptr, strlen_or_ind );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2558,7 +2558,7 @@ namespace core {
return 0; return 0;
#endif // !_WIN32 #endif // !_WIN32
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
@ -2571,7 +2571,7 @@ namespace core {
SQLRETURN r; SQLRETURN r;
r = ::SQLSetConnectAttr( ctx.handle(), attr, value_ptr, str_len ); r = ::SQLSetConnectAttr( ctx.handle(), attr, value_ptr, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { CHECK_SQL_ERROR_OR_WARNING( r, ctx, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2583,7 +2583,7 @@ namespace core {
core::SQLGetStmtAttr( stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0 ); core::SQLGetStmtAttr( stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0 );
if( value_ptr ) { if( value_ptr ) {
r = ::SQLSetDescField( hIpd, rec_num, fld_id, value_ptr, str_len ); r = ::SQLSetDescField( hIpd, rec_num, fld_id, value_ptr, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2593,7 +2593,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLSetEnvAttr( ctx.handle(), attr, value_ptr, str_len ); r = ::SQLSetEnvAttr( ctx.handle(), attr, value_ptr, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { CHECK_SQL_ERROR_OR_WARNING( r, ctx, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2602,7 +2602,7 @@ namespace core {
{ {
SQLRETURN r = ::SQLSetConnectAttr( conn->handle(), attribute, value_ptr, value_len ); SQLRETURN r = ::SQLSetConnectAttr( conn->handle(), attribute, value_ptr, value_len );
CHECK_SQL_ERROR_OR_WARNING( r, conn ) { CHECK_SQL_ERROR_OR_WARNING( r, conn, NULL ) {
throw CoreException(); throw CoreException();
} }
} }
@ -2611,7 +2611,7 @@ namespace core {
{ {
SQLRETURN r; SQLRETURN r;
r = ::SQLSetStmtAttr( stmt->handle(), attr, value_ptr, str_len ); r = ::SQLSetStmtAttr( stmt->handle(), attr, value_ptr, str_len );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw CoreException(); throw CoreException();
} }
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv // Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
@ -162,7 +162,7 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
// delete sensivity data // delete sensivity data
clean_up_sensitivity_metadata(); clean_up_sensitivity_metadata();
// clean up metadata // clean up metadata
clean_up_results_metadata(); clean_up_results_metadata();
invalidate(); invalidate();
@ -366,7 +366,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
_Inout_ SQLSMALLINT decimal_digits) _Inout_ SQLSMALLINT decimal_digits)
{ {
// check is only < because params are 0 based // check is only < because params are 0 based
CHECK_CUSTOM_ERROR(param_num >= SQL_SERVER_MAX_PARAMS, stmt, SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, param_num + 1) { CHECK_CUSTOM_ERROR(param_num >= SQL_SERVER_MAX_PARAMS, stmt, SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, param_num + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -395,7 +395,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
stmt->params_container.insert_param(param_num, new_param); stmt->params_container.insert_param(param_num, new_param);
param_ptr = new_param; param_ptr = new_param;
new_param.transferred(); new_param.transferred();
} else if (direction == SQL_PARAM_INPUT } else if (direction == SQL_PARAM_INPUT
&& param_ptr->sql_data_type != SQL_SS_TABLE && param_ptr->sql_data_type != SQL_SS_TABLE
&& param_ptr->strlen_or_indptr == SQL_NULL_DATA) { && param_ptr->strlen_or_indptr == SQL_NULL_DATA) {
// reset the followings for regular input parameters if it was bound as a null param before // reset the followings for regular input parameters if it was bound as a null param before
@ -408,16 +408,29 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
bool result = param_ptr->prepare_param(param_ref, param_z); bool result = param_ptr->prepare_param(param_ref, param_z);
if (!result && direction == SQL_PARAM_INPUT_OUTPUT) { if (!result && direction == SQL_PARAM_INPUT_OUTPUT) {
CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, param_num + 1) { CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, param_num + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
// If Always Encrypted is enabled, transfer the known param meta data if applicable, which might alter param_z for decimal types // If Always Encrypted is enabled, transfer the known param meta data if applicable, which might alter param_z for decimal types
if (stmt->conn->ce_option.enabled) { if (stmt->conn->ce_option.enabled
if (param_ptr->sql_data_type == SQL_UNKNOWN_TYPE || param_ptr->column_size == SQLSRV_UNKNOWN_SIZE) { && (param_ptr->sql_data_type == SQL_UNKNOWN_TYPE || param_ptr->column_size == SQLSRV_UNKNOWN_SIZE)) {
// meta data parameters are always sorted based on parameter number // meta data parameters are always sorted based on parameter number
param_ptr->copy_param_meta_ae(param_z, stmt->params_container.params_meta_ae[param_num]); param_ptr->copy_param_meta_ae(param_z, stmt->params_container.params_meta_ae[param_num]);
}
else {
if (Z_TYPE_P(param_z) == IS_STRING && column_size == SQLSRV_UNKNOWN_SIZE) {
size_t char_size = (encoding == SQLSRV_ENCODING_UTF8) ? sizeof(SQLWCHAR) : sizeof(char);
SQLULEN byte_len = Z_STRLEN_P(param_z) * char_size;
if (byte_len > SQL_SERVER_MAX_FIELD_SIZE) {
param_ptr->column_size = SQL_SERVER_MAX_TYPE_SIZE;
}
else {
if (param_ptr->column_size == SQLSRV_UNKNOWN_SIZE) {
param_ptr->column_size = SQL_SERVER_MAX_FIELD_SIZE / char_size;
}
}
} }
} }
@ -429,7 +442,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
// For a parameter targeting a SmallDatetime column, the return values are SQL_TYPE_TIMESTAMP, 16, and 0. Inputting these values into SQLBindParameter() results in Operand type clash error. // For a parameter targeting a SmallDatetime column, the return values are SQL_TYPE_TIMESTAMP, 16, and 0. Inputting these values into SQLBindParameter() results in Operand type clash error.
// This is because SQL_TYPE_TIMESTAMP corresponds to Datetime2 by default, and conversion of Datetime2 to Datetime and conversion of Datetime2 to SmallDatatime is not allowed with encrypted columns. // This is because SQL_TYPE_TIMESTAMP corresponds to Datetime2 by default, and conversion of Datetime2 to Datetime and conversion of Datetime2 to SmallDatatime is not allowed with encrypted columns.
// To fix the conversion problem, set the SQL_CA_SS_SERVER_TYPE field of the parameter to SQL_SS_TYPE_DATETIME and SQL_SS_TYPE_SMALLDATETIME respectively for a Datetime and Smalldatetime column. // To fix the conversion problem, set the SQL_CA_SS_SERVER_TYPE field of the parameter to SQL_SS_TYPE_DATETIME and SQL_SS_TYPE_SMALLDATETIME respectively for a Datetime and Smalldatetime column.
// Note this must be called after SQLBindParameter() or SQLSetDescField() may fail. // Note this must be called after SQLBindParameter() or SQLSetDescField() may fail.
// VSO BUG 2693: how to correctly distinguish datetime from datetime2(3)? Both have the same decimal_digits and column_size // VSO BUG 2693: how to correctly distinguish datetime from datetime2(3)? Both have the same decimal_digits and column_size
if (stmt->conn->ce_option.enabled && param_ptr->sql_data_type == SQL_TYPE_TIMESTAMP) { if (stmt->conn->ce_option.enabled && param_ptr->sql_data_type == SQL_TYPE_TIMESTAMP) {
if (param_ptr->decimal_digits == 3) { if (param_ptr->decimal_digits == 3) {
@ -476,7 +489,7 @@ SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_l
wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ), wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ),
sql_len, &wsql_len ); sql_len, &wsql_len );
CHECK_CUSTOM_ERROR( wsql_string == 0, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, CHECK_CUSTOM_ERROR( wsql_string == 0, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE,
get_last_error_message() ) { get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -633,7 +646,7 @@ field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQL
bool converted = convert_string_from_utf16( encoding, field_name_temp, field_len_temp, ( char** ) &( meta_data->field_name ), field_name_len ); bool converted = convert_string_from_utf16( encoding, field_name_temp, field_len_temp, ( char** ) &( meta_data->field_name ), field_name_len );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ) { CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -711,7 +724,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
// To retrieve sensitivity classfication data, the first step is to retrieve the IRD(Implementation Row Descriptor) handle by // To retrieve sensitivity classfication data, the first step is to retrieve the IRD(Implementation Row Descriptor) handle by
// calling SQLGetStmtAttr with SQL_ATTR_IMP_ROW_DESC statement attribute // calling SQLGetStmtAttr with SQL_ATTR_IMP_ROW_DESC statement attribute
r = ::SQLGetStmtAttr(stmt->handle(), SQL_ATTR_IMP_ROW_DESC, reinterpret_cast<SQLPOINTER*>(&ird), SQL_IS_POINTER, 0); r = ::SQLGetStmtAttr(stmt->handle(), SQL_ATTR_IMP_ROW_DESC, reinterpret_cast<SQLPOINTER*>(&ird), SQL_IS_POINTER, 0);
CHECK_SQL_ERROR_OR_WARNING(r, stmt) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in getting Implementation Row Descriptor handle." ); LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in getting Implementation Row Descriptor handle." );
throw core::CoreException(); throw core::CoreException();
} }
@ -728,7 +741,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
SQLSMALLINT len; SQLSMALLINT len;
rc = ::SQLGetDiagField(SQL_HANDLE_DESC, ird, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len); rc = ::SQLGetDiagField(SQL_HANDLE_DESC, ird, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
CHECK_SQL_ERROR_OR_WARNING(rc, stmt) { CHECK_SQL_ERROR_OR_WARNING(rc, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -736,7 +749,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
throw core::CoreException(); throw core::CoreException();
} }
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Check if ODBC driver or the server supports the Data Classification feature.") { CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Check if ODBC driver or the server supports the Data Classification feature.", NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -748,7 +761,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
if (r != SQL_SUCCESS) { if (r != SQL_SUCCESS) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW again." ); LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW again." );
CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "SQLGetDescFieldW failed unexpectedly") { CHECK_CUSTOM_ERROR(true, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "SQLGetDescFieldW failed unexpectedly", NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -756,7 +769,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
// Start parsing the data (blob) // Start parsing the data (blob)
using namespace data_classification; using namespace data_classification;
// If make it this far, must be using ODBC 17.2 or above. Prior to ODBC 17.4, checking Data Classification version will fail. // If make it this far, must be using ODBC 17.2 or above. Prior to ODBC 17.4, checking Data Classification version will fail.
// When the function is successful and the version is right, rank info is available for retrieval // When the function is successful and the version is right, rank info is available for retrieval
bool getRankInfo = false; bool getRankInfo = false;
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION_VERSION, reinterpret_cast<SQLPOINTER>(&dcVersion), SQL_IS_INTEGER, &dcIRD); r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION_VERSION, reinterpret_cast<SQLPOINTER>(&dcVersion), SQL_IS_INTEGER, &dcIRD);
@ -780,7 +793,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
unsigned char *dcend = dcbuf; unsigned char *dcend = dcbuf;
dcend += dclen; dcend += dclen;
CHECK_CUSTOM_ERROR(dcptr != dcend, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Metadata parsing ends unexpectedly") { CHECK_CUSTOM_ERROR(dcptr != dcend, stmt, SQLSRV_ERROR_DATA_CLASSIFICATION_FAILED, "Metadata parsing ends unexpectedly", NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1071,7 +1084,7 @@ void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* val
if( Z_TYPE_P( value_z ) != IS_LONG || Z_LVAL_P( value_z ) < 0 ) { if( Z_TYPE_P( value_z ) != IS_LONG || Z_LVAL_P( value_z ) < 0 ) {
convert_to_string( value_z ); convert_to_string( value_z );
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( value_z ) ); THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( value_z ), NULL );
} }
// Save the query timeout setting for processing later // Save the query timeout setting for processing later
@ -1354,7 +1367,7 @@ size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) cons
break; break;
default: default:
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE,
get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )); get_last_error_message( ERROR_NO_UNICODE_TRANSLATION ), NULL);
break; break;
} }
@ -1379,7 +1392,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
// make sure that fields are not retrieved incorrectly. // make sure that fields are not retrieved incorrectly.
CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index,
stmt->last_field_index ) { stmt->last_field_index, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1394,11 +1407,11 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( SQLLEN ), SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( SQLLEN ),
field_len, true /*handle_warning*/ ); field_len, true /*handle_warning*/ );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw core::CoreException(); throw core::CoreException();
} }
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1421,11 +1434,11 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ),
field_len, true /*handle_warning*/ ); field_len, true /*handle_warning*/ );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw core::CoreException(); throw core::CoreException();
} }
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1464,7 +1477,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
field_len_temp = 0; field_len_temp = 0;
} }
CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index) { CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1491,7 +1504,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
throw core::CoreException(); throw core::CoreException();
} }
// For a sqlsrv stream, only REPORT_ERRORS may be used. For "mode", the 'b' option // For a sqlsrv stream, only REPORT_ERRORS may be used. For "mode", the 'b' option
// is ignored on POSIX systems, which treat text and binary files the same. Yet, the // is ignored on POSIX systems, which treat text and binary files the same. Yet, the
// 'b' option might be important in other systems. // 'b' option might be important in other systems.
// For details check https://www.php.net/manual/en/internals2.ze1.streams.php // For details check https://www.php.net/manual/en/internals2.ze1.streams.php
@ -1609,7 +1622,7 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
if (num_decimals > scale) { if (num_decimals > scale) {
last_pos = round_up_decimal_numbers(buffer, (pt - src) + offset, scale, offset, last_pos); last_pos = round_up_decimal_numbers(buffer, (pt - src) + offset, scale, offset, last_pos);
} }
} }
// Remove the extra white space if not used. For a negative number, // Remove the extra white space if not used. For a negative number,
// the first pos is always a space // the first pos is always a space
@ -1621,7 +1634,7 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
if (is_negative) { if (is_negative) {
buffer[--offset] = '-'; buffer[--offset] = '-';
} }
int len = last_pos - offset; int len = last_pos - offset;
memcpy_s(field_value, len, buffer + offset, len); memcpy_s(field_value, len, buffer + offset, len);
field_value[len] = '\0'; field_value[len] = '\0';
@ -1672,7 +1685,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
} else { } else {
c_type = SQL_C_CHAR; c_type = SQL_C_CHAR;
extra = sizeof(SQLCHAR); extra = sizeof(SQLCHAR);
// For numbers, no need to convert // For numbers, no need to convert
if (sqlsrv_php_type.typeinfo.encoding == CP_UTF8 && !is_a_numeric_type(sql_field_type)) { if (sqlsrv_php_type.typeinfo.encoding == CP_UTF8 && !is_a_numeric_type(sql_field_type)) {
c_type = SQL_C_WCHAR; c_type = SQL_C_WCHAR;
@ -1683,8 +1696,8 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
} }
// If this is a large type, then read the first chunk to get the actual length from SQLGetData // If this is a large type, then read the first chunk to get the actual length from SQLGetData
// The user may use "SET TEXTSIZE" to specify the size of varchar(max), nvarchar(max), // The user may use "SET TEXTSIZE" to specify the size of varchar(max), nvarchar(max),
// varbinary(max), text, ntext, and image data returned by a SELECT statement. // varbinary(max), text, ntext, and image data returned by a SELECT statement.
// For varbinary(max), varchar(max) and nvarchar(max), sql_display_size will be 0, regardless // For varbinary(max), varchar(max) and nvarchar(max), sql_display_size will be 0, regardless
if (sql_display_size == 0 || if (sql_display_size == 0 ||
(sql_field_type == SQL_WLONGVARCHAR || sql_field_type == SQL_LONGVARCHAR || sql_field_type == SQL_LONGVARBINARY)) { (sql_field_type == SQL_WLONGVARCHAR || sql_field_type == SQL_LONGVARCHAR || sql_field_type == SQL_LONGVARBINARY)) {
@ -1700,7 +1713,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp, sql_display_size + extra, &field_len_temp, false /*handle_warning*/); r = stmt->current_results->get_data(field_index + 1, c_type, field_value_temp, sql_display_size + extra, &field_len_temp, false /*handle_warning*/);
} }
CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index) { CHECK_CUSTOM_ERROR((r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1750,7 +1763,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
} while (r == SQL_SUCCESS_WITH_INFO && is_truncated_warning(state)); } while (r == SQL_SUCCESS_WITH_INFO && is_truncated_warning(state));
} // if (field_len_temp == SQL_NO_TOTAL) } // if (field_len_temp == SQL_NO_TOTAL)
else { else {
// The field length (or its estimate) is returned, thus no need to double the allocation size. // The field length (or its estimate) is returned, thus no need to double the allocation size.
// Allocate field_len_temp (which is the field length retrieved from the first SQLGetData) but with some padding // Allocate field_len_temp (which is the field length retrieved from the first SQLGetData) but with some padding
// because there is a chance that the estimated field_len_temp is not accurate enough // because there is a chance that the estimated field_len_temp is not accurate enough
SQLLEN buffer_len = 50; SQLLEN buffer_len = 50;
@ -1762,7 +1775,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
field_len_temp + buffer_len, &chunk_field_len, false /*handle_warning*/); field_len_temp + buffer_len, &chunk_field_len, false /*handle_warning*/);
field_len_temp = initial_field_len + chunk_field_len; field_len_temp = initial_field_len + chunk_field_len;
CHECK_SQL_ERROR_OR_WARNING(r, stmt) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1772,7 +1785,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
} // if (is_truncated_warning(state)) } // if (is_truncated_warning(state))
} // if (r == SQL_SUCCESS_WITH_INFO) } // if (r == SQL_SUCCESS_WITH_INFO)
CHECK_SQL_ERROR_OR_WARNING(r, stmt) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -1780,7 +1793,7 @@ void get_field_as_string(_Inout_ sqlsrv_stmt *stmt, _In_ SQLUSMALLINT field_inde
bool converted = convert_string_from_utf16_inplace(static_cast<SQLSRV_ENCODING>(sqlsrv_php_type.typeinfo.encoding), bool converted = convert_string_from_utf16_inplace(static_cast<SQLSRV_ENCODING>(sqlsrv_php_type.typeinfo.encoding),
&field_value_temp, field_len_temp); &field_value_temp, field_len_temp);
CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -1879,11 +1892,11 @@ bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type )
return false; return false;
} }
void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits) void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits)
{ {
char* value = Z_STRVAL_P(param_z); char* value = Z_STRVAL_P(param_z);
int value_len = Z_STRLEN_P(param_z); int value_len = Z_STRLEN_P(param_z);
// If the length is greater than maxDecimalStrLen, do not convert the string // If the length is greater than maxDecimalStrLen, do not convert the string
// 6 is derived from: 1 for the decimal point; 1 for sign of the number; 1 for 'e' or 'E' (scientific notation); // 6 is derived from: 1 for the decimal point; 1 for sign of the number; 1 for 'e' or 'E' (scientific notation);
// 1 for sign of scientific exponent; 2 for length of scientific exponent // 1 for sign of scientific exponent; 2 for length of scientific exponent
@ -1902,7 +1915,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
return; // invalid input caused the conversion to throw an exception return; // invalid input caused the conversion to throw an exception
} }
if (index < value_len) { if (index < value_len) {
return; // the input contains something else apart from the numerical value return; // the input contains something else apart from the numerical value
} }
// Navigate to the first digit or the decimal point // Navigate to the first digit or the decimal point
@ -1956,7 +1969,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
if (num_decimals > decimal_digits) { if (num_decimals > decimal_digits) {
round_up_decimal_numbers(buffer, (pt - src) + offset, decimal_digits, offset, length + offset); round_up_decimal_numbers(buffer, (pt - src) + offset, decimal_digits, offset, length + offset);
} }
} }
} else { } else {
int oldpos = 0; int oldpos = 0;
if (pt == NULL) { if (pt == NULL) {
@ -1998,7 +2011,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
} }
} }
// Start copying the content to the buffer until the exp sign or one more digit after decimal_digits // Start copying the content to the buffer until the exp sign or one more digit after decimal_digits
char *p = src; char *p = src;
int idx = offset; int idx = offset;
int lastpos = newpos + decimal_digits + 1; int lastpos = newpos + decimal_digits + 1;
@ -2018,7 +2031,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
if (num_decimals > decimal_digits) { if (num_decimals > decimal_digits) {
round_up_decimal_numbers(buffer, newpos, decimal_digits, offset, idx); round_up_decimal_numbers(buffer, newpos, decimal_digits, offset, idx);
} }
} }
} }
// Set the minus sign if negative // Set the minus sign if negative
@ -2067,7 +2080,7 @@ int round_up_decimal_numbers(_Inout_ char* buffer, _In_ int decimal_pos, _In_ in
buffer[pos] = '\0'; buffer[pos] = '\0';
return pos; return pos;
} }
} }
// Do nothing and just return // Do nothing and just return
return lastpos; return lastpos;
@ -2117,9 +2130,9 @@ bool sqlsrv_param::prepare_param(_In_ zval* param_ref, _Inout_ zval* param_z)
// If SQL type or column size is unknown, derives the appropriate values as well using the provided param zval and encoding // If SQL type or column size is unknown, derives the appropriate values as well using the provided param zval and encoding
void sqlsrv_param::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z) void sqlsrv_param::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z)
{ {
// Get param php type // Get param php type
param_php_type = Z_TYPE_P(param_z); param_php_type = Z_TYPE_P(param_z);
switch (param_php_type) { switch (param_php_type) {
case IS_NULL: case IS_NULL:
process_null_param(param_z); process_null_param(param_z);
@ -2145,7 +2158,7 @@ void sqlsrv_param::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_
break; break;
case IS_ARRAY: case IS_ARRAY:
default: default:
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1); THROW_CORE_ERROR(stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1, NULL);
break; break;
} }
} }
@ -2203,8 +2216,8 @@ void sqlsrv_param::process_long_param(_Inout_ zval* param_z)
} }
} }
// When binding any integer, the zend_long value and its length are used as the buffer // When binding any integer, the zend_long value and its length are used as the buffer
// and buffer length. When the buffer is 8 bytes use the corresponding C type for // and buffer length. When the buffer is 8 bytes use the corresponding C type for
// 8-byte integers // 8-byte integers
#ifdef ZEND_ENABLE_ZVAL_LONG64 #ifdef ZEND_ENABLE_ZVAL_LONG64
c_data_type = SQL_C_SBIGINT; c_data_type = SQL_C_SBIGINT;
@ -2272,18 +2285,6 @@ bool sqlsrv_param::derive_string_types_sizes(_In_ zval* param_z)
break; break;
} }
// Derive the column size also only if it is unknown
if (column_size == SQLSRV_UNKNOWN_SIZE) {
size_t char_size = (encoding == SQLSRV_ENCODING_UTF8) ? sizeof(SQLWCHAR) : sizeof(char);
SQLULEN byte_len = Z_STRLEN_P(param_z) * char_size;
if (byte_len > SQL_SERVER_MAX_FIELD_SIZE) {
column_size = SQL_SERVER_MAX_TYPE_SIZE;
} else {
column_size = SQL_SERVER_MAX_FIELD_SIZE / char_size;
}
}
return is_numeric; return is_numeric;
} }
@ -2316,7 +2317,7 @@ void sqlsrv_param::process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
bool is_numeric = derive_string_types_sizes(param_z); bool is_numeric = derive_string_types_sizes(param_z);
// With AE, the precision of the decimal or numeric inputs have to match exactly as defined in the columns. // With AE, the precision of the decimal or numeric inputs have to match exactly as defined in the columns.
// Without AE, the derived default sql types will not be this specific. Thus, if sql_type is SQL_DECIMAL // Without AE, the derived default sql types will not be this specific. Thus, if sql_type is SQL_DECIMAL
// or SQL_NUMERIC, the user must have clearly specified it (using the SQLSRV driver) as SQL_DECIMAL or SQL_NUMERIC. // or SQL_NUMERIC, the user must have clearly specified it (using the SQLSRV driver) as SQL_DECIMAL or SQL_NUMERIC.
// In either case, the input passed into SQLBindParam requires matching scale (i.e., number of decimal digits). // In either case, the input passed into SQLBindParam requires matching scale (i.e., number of decimal digits).
if (sql_data_type == SQL_DECIMAL || sql_data_type == SQL_NUMERIC) { if (sql_data_type == SQL_DECIMAL || sql_data_type == SQL_NUMERIC) {
@ -2331,7 +2332,7 @@ void sqlsrv_param::process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
} }
// This changes the member placeholder_z to hold the wide string // This changes the member placeholder_z to hold the wide string
bool converted = convert_input_str_to_utf16(stmt, param_z); bool converted = convert_input_str_to_utf16(stmt, param_z);
CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, param_pos + 1, get_last_error_message()) { CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, param_pos + 1, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -2491,7 +2492,7 @@ void sqlsrv_param::process_object_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
if (succeeded) { if (succeeded) {
succeeded = convert_datetime_to_string(stmt, param_z); succeeded = convert_datetime_to_string(stmt, param_z);
} }
CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1) { CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -2507,6 +2508,9 @@ void sqlsrv_param::bind_param(_Inout_ sqlsrv_stmt* stmt)
} }
core::SQLBindParameter(stmt, param_pos + 1, direction, c_data_type, sql_data_type, column_size, decimal_digits, buffer, buffer_length, &strlen_or_indptr); core::SQLBindParameter(stmt, param_pos + 1, direction, c_data_type, sql_data_type, column_size, decimal_digits, buffer, buffer_length, &strlen_or_indptr);
if (!stmt->conn->ce_option.enabled && !stmt->format_decimals) {
sql_data_type = SQL_UNKNOWN_TYPE;
}
} }
void sqlsrv_param::init_data_from_zval(_Inout_ sqlsrv_stmt* stmt) void sqlsrv_param::init_data_from_zval(_Inout_ sqlsrv_stmt* stmt)
@ -2560,7 +2564,7 @@ bool sqlsrv_param::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
SQLWCHAR wbuffer[PHP_STREAM_BUFFER_SIZE + 1] = { L'\0' }; SQLWCHAR wbuffer[PHP_STREAM_BUFFER_SIZE + 1] = { L'\0' };
int wbuffer_size = static_cast<int>(sizeof(wbuffer) / sizeof(SQLWCHAR)); int wbuffer_size = static_cast<int>(sizeof(wbuffer) / sizeof(SQLWCHAR));
DWORD last_error_code = ERROR_SUCCESS; DWORD last_error_code = ERROR_SUCCESS;
// The buffer_size is the # of wchars. Set to buffer_size / 2 // The buffer_size is the # of wchars. Set to buffer_size / 2
#ifndef _WIN32 #ifndef _WIN32
int wsize = SystemLocale::ToUtf16Strict(encoding, buffer, static_cast<int>(read), wbuffer, wbuffer_size, &last_error_code); int wsize = SystemLocale::ToUtf16Strict(encoding, buffer, static_cast<int>(read), wbuffer, wbuffer_size, &last_error_code);
@ -2576,7 +2580,7 @@ bool sqlsrv_param::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
// read the missing bytes // read the missing bytes
size_t new_read = php_stream_read(param_stream, static_cast<char*>(buffer) + read, need_to_read); size_t new_read = php_stream_read(param_stream, static_cast<char*>(buffer) + read, need_to_read);
// if the bytes couldn't be read, then we return an error // if the bytes couldn't be read, then we return an error
CHECK_CUSTOM_ERROR(new_read != need_to_read, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, get_last_error_message(ERROR_NO_UNICODE_TRANSLATION)) { CHECK_CUSTOM_ERROR(new_read != need_to_read, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, get_last_error_message(ERROR_NO_UNICODE_TRANSLATION), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -2587,7 +2591,7 @@ bool sqlsrv_param::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
wsize = MultiByteToWideChar(encoding, MB_ERR_INVALID_CHARS, buffer, static_cast<int>(read + new_read), wbuffer, static_cast<int>(sizeof(wbuffer) / sizeof(wchar_t))); wsize = MultiByteToWideChar(encoding, MB_ERR_INVALID_CHARS, buffer, static_cast<int>(read + new_read), wbuffer, static_cast<int>(sizeof(wbuffer) / sizeof(wchar_t)));
#endif //!_WIN32 #endif //!_WIN32
// something else must be wrong if it failed // something else must be wrong if it failed
CHECK_CUSTOM_ERROR(wsize == 0, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, get_last_error_message(ERROR_NO_UNICODE_TRANSLATION)) { CHECK_CUSTOM_ERROR(wsize == 0, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, get_last_error_message(ERROR_NO_UNICODE_TRANSLATION), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -2605,7 +2609,7 @@ bool sqlsrv_param_inout::prepare_param(_In_ zval* param_ref, _Inout_ zval* param
{ {
// Save the output param reference now // Save the output param reference now
param_ptr_z = param_ref; param_ptr_z = param_ref;
int type = Z_TYPE_P(param_z); int type = Z_TYPE_P(param_z);
was_null = (type == IS_NULL); was_null = (type == IS_NULL);
was_bool = (type == IS_TRUE || type == IS_FALSE); was_bool = (type == IS_TRUE || type == IS_FALSE);
@ -2676,7 +2680,7 @@ bool sqlsrv_param_inout::prepare_param(_In_ zval* param_ref, _Inout_ zval* param
// If SQL type or column size is unknown, derives the appropriate values as well using the provided param zval and encoding // If SQL type or column size is unknown, derives the appropriate values as well using the provided param zval and encoding
void sqlsrv_param_inout::process_param(_Inout_ sqlsrv_stmt* stmt, zval* param_z) void sqlsrv_param_inout::process_param(_Inout_ sqlsrv_stmt* stmt, zval* param_z)
{ {
// Get param php type NOW because the original parameter might have been converted beforehand // Get param php type NOW because the original parameter might have been converted beforehand
param_php_type = Z_TYPE_P(param_z); param_php_type = Z_TYPE_P(param_z);
switch (param_php_type) { switch (param_php_type) {
@ -2690,7 +2694,7 @@ void sqlsrv_param_inout::process_param(_Inout_ sqlsrv_stmt* stmt, zval* param_z)
process_string_param(stmt, param_z); process_string_param(stmt, param_z);
break; break;
default: default:
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1); THROW_CORE_ERROR(stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_pos + 1, NULL);
break; break;
} }
@ -2728,7 +2732,7 @@ void sqlsrv_param_inout::process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_
unsigned int wchar_size = 0; unsigned int wchar_size = 0;
wide_buffer = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, reinterpret_cast<const char*>(buffer), static_cast<int>(buffer_length), &wchar_size); wide_buffer = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, reinterpret_cast<const char*>(buffer), static_cast<int>(buffer_length), &wchar_size);
CHECK_CUSTOM_ERROR(wide_buffer == 0, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, param_pos + 1, get_last_error_message()) { CHECK_CUSTOM_ERROR(wide_buffer == 0, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, param_pos + 1, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
wide_buffer[wchar_size] = L'\0'; wide_buffer[wchar_size] = L'\0';
@ -2736,7 +2740,7 @@ void sqlsrv_param_inout::process_string_param(_Inout_ sqlsrv_stmt* stmt, _Inout_
buffer = Z_STRVAL_P(param_z); buffer = Z_STRVAL_P(param_z);
buffer_length = Z_STRLEN_P(param_z); buffer_length = Z_STRLEN_P(param_z);
} }
} }
strlen_or_indptr = buffer_length; strlen_or_indptr = buffer_length;
@ -2804,7 +2808,7 @@ void sqlsrv_param_inout::finalize_output_value()
throw core::CoreException(); throw core::CoreException();
} }
} }
// Even if the output param is a boolean, still convert to a long // Even if the output param is a boolean, still convert to a long
// integer first to take care of rounding // integer first to take care of rounding
convert_to_long(value_z); convert_to_long(value_z);
if (was_bool) { if (was_bool) {
@ -2857,7 +2861,7 @@ void sqlsrv_param_inout::finalize_output_string()
break; break;
} }
CHECK_CUSTOM_ERROR(str_len > (buffer_length - null_size), stmt, SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, param_pos + 1) { CHECK_CUSTOM_ERROR(str_len > (buffer_length - null_size), stmt, SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -2884,7 +2888,7 @@ void sqlsrv_param_inout::finalize_output_string()
SQLLEN outLen = 0; SQLLEN outLen = 0;
bool result = convert_string_from_utf16(encoding, reinterpret_cast<const SQLWCHAR*>(str), int(str_len / sizeof(SQLWCHAR)), &outString, outLen); bool result = convert_string_from_utf16(encoding, reinterpret_cast<const SQLWCHAR*>(str), int(str_len / sizeof(SQLWCHAR)), &outString, outLen);
CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) { CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -2909,20 +2913,20 @@ void sqlsrv_param_inout::finalize_output_string()
void sqlsrv_param_inout::resize_output_string_buffer(_Inout_ zval* param_z, _In_ bool is_numeric_type) void sqlsrv_param_inout::resize_output_string_buffer(_Inout_ zval* param_z, _In_ bool is_numeric_type)
{ {
// Prerequisites: buffer, buffer_length, column_size, and strlen_or_indptr have been set to a known value // Prerequisites: buffer, buffer_length, column_size, and strlen_or_indptr have been set to a known value
// Purpose: // Purpose:
// Verify there is enough space to hold the output string parameter, and allocate if necessary. The param_z // Verify there is enough space to hold the output string parameter, and allocate if necessary. The param_z
// is updated to contain the new buffer with the correct size and its reference is incremented, and all required // is updated to contain the new buffer with the correct size and its reference is incremented, and all required
// values for SQLBindParameter will also be updated. // values for SQLBindParameter will also be updated.
SQLLEN original_len = buffer_length; SQLLEN original_len = buffer_length;
SQLLEN expected_len; SQLLEN expected_len;
SQLLEN buffer_null_extra; SQLLEN buffer_null_extra;
SQLLEN elem_size; SQLLEN elem_size;
// Calculate the size of each 'element' represented by column_size. WCHAR is the size of a wide char (2), and so is // Calculate the size of each 'element' represented by column_size. WCHAR is the size of a wide char (2), and so is
// a N(VAR)CHAR/NTEXT field being returned as a binary field. // a N(VAR)CHAR/NTEXT field being returned as a binary field.
elem_size = (c_data_type == SQL_C_WCHAR || elem_size = (c_data_type == SQL_C_WCHAR ||
(c_data_type == SQL_C_BINARY && (c_data_type == SQL_C_BINARY &&
(sql_data_type == SQL_WCHAR || sql_data_type == SQL_WVARCHAR || sql_data_type == SQL_WLONGVARCHAR))) ? sizeof(SQLWCHAR) : sizeof(SQLCHAR); (sql_data_type == SQL_WCHAR || sql_data_type == SQL_WVARCHAR || sql_data_type == SQL_WLONGVARCHAR))) ? sizeof(SQLWCHAR) : sizeof(SQLCHAR);
// account for the NULL terminator returned by ODBC and needed by Zend to avoid a "String not null terminated" debug warning // account for the NULL terminator returned by ODBC and needed by Zend to avoid a "String not null terminated" debug warning
@ -3028,7 +3032,7 @@ void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string
core::SQLAllocHandle(SQL_HANDLE_STMT, *(stmt->conn), &chstmt); core::SQLAllocHandle(SQL_HANDLE_STMT, *(stmt->conn), &chstmt);
rc = SQLSetStmtAttr(chstmt, SQL_SOPT_SS_NAME_SCOPE, (SQLPOINTER)SQL_SS_NAME_SCOPE_TABLE_TYPE, SQL_IS_UINTEGER); rc = SQLSetStmtAttr(chstmt, SQL_SOPT_SS_NAME_SCOPE, (SQLPOINTER)SQL_SS_NAME_SCOPE_TABLE_TYPE, SQL_IS_UINTEGER);
CHECK_CUSTOM_ERROR(!SQL_SUCCEEDED(rc), stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1) { CHECK_CUSTOM_ERROR(!SQL_SUCCEEDED(rc), stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3040,7 +3044,7 @@ void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string
rc = SQLColumns(chstmt, NULL, 0, NULL, SQL_NTS, reinterpret_cast<SQLCHAR*>(table_type), SQL_NTS, NULL, 0); rc = SQLColumns(chstmt, NULL, 0, NULL, SQL_NTS, reinterpret_cast<SQLCHAR*>(table_type), SQL_NTS, NULL, 0);
} }
CHECK_CUSTOM_ERROR(!SQL_SUCCEEDED(rc), stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1) { CHECK_CUSTOM_ERROR(!SQL_SUCCEEDED(rc), stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3055,7 +3059,7 @@ void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string
while (SQL_SUCCESS == rc) { while (SQL_SUCCESS == rc) {
rc = SQLFetch(chstmt); rc = SQLFetch(chstmt);
if (rc == SQL_NO_DATA) { if (rc == SQL_NO_DATA) {
CHECK_CUSTOM_ERROR(tvp_columns.size() == 0, stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1) { CHECK_CUSTOM_ERROR(tvp_columns.size() == 0, stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
break; break;
@ -3076,7 +3080,7 @@ void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string
pos++; pos++;
} }
} else { } else {
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1); THROW_CORE_ERROR(stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1, NULL);
} }
SQLCloseCursor(chstmt); SQLCloseCursor(chstmt);
@ -3114,7 +3118,7 @@ void sqlsrv_param_tvp::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* pa
this->num_rows = 0; this->num_rows = 0;
int num_columns = parse_tv_param_arrays(stmt, param_z); int num_columns = parse_tv_param_arrays(stmt, param_z);
column_size = num_rows; column_size = num_rows;
strlen_or_indptr = (num_columns == 0)? SQL_DEFAULT_PARAM : SQL_DATA_AT_EXEC; strlen_or_indptr = (num_columns == 0)? SQL_DEFAULT_PARAM : SQL_DATA_AT_EXEC;
} else { } else {
// This is one of the constituent columns of the table-valued parameter // This is one of the constituent columns of the table-valued parameter
@ -3137,7 +3141,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
zend_string *schema_name = NULL; zend_string *schema_name = NULL;
zval *tvp_data_z = NULL; zval *tvp_data_z = NULL;
HashPosition pos; HashPosition pos;
zend_hash_internal_pointer_reset_ex(inputs_ht, &pos); zend_hash_internal_pointer_reset_ex(inputs_ht, &pos);
if (zend_hash_has_more_elements_ex(inputs_ht, &pos) == SUCCESS) { if (zend_hash_has_more_elements_ex(inputs_ht, &pos) == SUCCESS) {
@ -3148,22 +3152,22 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
if (key_type == HASH_KEY_IS_STRING) { if (key_type == HASH_KEY_IS_STRING) {
key_len = ZSTR_LEN(tvp_name); key_len = ZSTR_LEN(tvp_name);
tvp_data_z = zend_hash_get_current_data_ex(inputs_ht, &pos); tvp_data_z = zend_hash_get_current_data_ex(inputs_ht, &pos);
} }
CHECK_CUSTOM_ERROR((key_type == HASH_KEY_IS_LONG || key_len == 0), stmt, SQLSRV_ERROR_TVP_INVALID_TABLE_TYPE_NAME, param_pos + 1) { CHECK_CUSTOM_ERROR((key_type == HASH_KEY_IS_LONG || key_len == 0), stmt, SQLSRV_ERROR_TVP_INVALID_TABLE_TYPE_NAME, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
// TODO: Find the docs page somewhere that says a TVP can not be null but it may have null columns?? // TODO: Find the docs page somewhere that says a TVP can not be null but it may have null columns??
CHECK_CUSTOM_ERROR(tvp_data_z == NULL || Z_TYPE_P(tvp_data_z) == IS_NULL || Z_TYPE_P(tvp_data_z) != IS_ARRAY, stmt, SQLSRV_ERROR_TVP_INVALID_INPUTS, param_pos + 1) { CHECK_CUSTOM_ERROR(tvp_data_z == NULL || Z_TYPE_P(tvp_data_z) == IS_NULL || Z_TYPE_P(tvp_data_z) != IS_ARRAY, stmt, SQLSRV_ERROR_TVP_INVALID_INPUTS, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
// Save the TVP type name for SQLSetDescField later // Save the TVP type name for SQLSetDescField later
buffer = ZSTR_VAL(tvp_name); buffer = ZSTR_VAL(tvp_name);
buffer_length = SQL_NTS; buffer_length = SQL_NTS;
// Check if schema is provided by the user // Check if schema is provided by the user
if (zend_hash_move_forward_ex(inputs_ht, &pos) == SUCCESS) { if (zend_hash_move_forward_ex(inputs_ht, &pos) == SUCCESS) {
zval *schema_z = zend_hash_get_current_data_ex(inputs_ht, &pos); zval *schema_z = zend_hash_get_current_data_ex(inputs_ht, &pos);
@ -3174,7 +3178,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
} }
// Save the TVP multi-dim array data, which should be something like this // Save the TVP multi-dim array data, which should be something like this
// [ // [
// [r1c1, r1c2, r1c3], // [r1c1, r1c2, r1c3],
// [r2c1, r2c2, r2c3], // [r2c1, r2c2, r2c3],
// [r3c1, r3c2, r3c3] // [r3c1, r3c2, r3c3]
@ -3203,7 +3207,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
// Loop through the rows to check the number of columns // Loop through the rows to check the number of columns
ZEND_HASH_FOREACH_KEY_VAL(rows_ht, id, key, row_z) { ZEND_HASH_FOREACH_KEY_VAL(rows_ht, id, key, row_z) {
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR(type == HASH_KEY_IS_STRING, stmt, SQLSRV_ERROR_TVP_STRING_KEYS, param_pos + 1) { CHECK_CUSTOM_ERROR(type == HASH_KEY_IS_STRING, stmt, SQLSRV_ERROR_TVP_STRING_KEYS, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3212,13 +3216,13 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
} }
// Individual row must be an array // Individual row must be an array
CHECK_CUSTOM_ERROR(Z_TYPE_P(row_z) != IS_ARRAY, stmt, SQLSRV_ERROR_TVP_ROW_NOT_ARRAY, param_pos + 1) { CHECK_CUSTOM_ERROR(Z_TYPE_P(row_z) != IS_ARRAY, stmt, SQLSRV_ERROR_TVP_ROW_NOT_ARRAY, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
// Are all the TVP's rows the same size // Are all the TVP's rows the same size
num_columns = zend_hash_num_elements(Z_ARRVAL_P(row_z)); num_columns = zend_hash_num_elements(Z_ARRVAL_P(row_z));
CHECK_CUSTOM_ERROR(num_columns != total_num_columns, stmt, SQLSRV_ERROR_TVP_ROWS_UNEXPECTED_SIZE, param_pos + 1, total_num_columns) { CHECK_CUSTOM_ERROR(num_columns != total_num_columns, stmt, SQLSRV_ERROR_TVP_ROWS_UNEXPECTED_SIZE, param_pos + 1, total_num_columns, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
@ -3230,7 +3234,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
void sqlsrv_param_tvp::process_param_column_value(_Inout_ sqlsrv_stmt* stmt) void sqlsrv_param_tvp::process_param_column_value(_Inout_ sqlsrv_stmt* stmt)
{ {
// This is one of the constituent columns of the table-valued parameter // This is one of the constituent columns of the table-valued parameter
// The corresponding column value of the TVP's first row is already saved in // The corresponding column value of the TVP's first row is already saved in
// the member variable param_ptr_z, which may be a NULL value // the member variable param_ptr_z, which may be a NULL value
zval *data_z = param_ptr_z; zval *data_z = param_ptr_z;
param_php_type = is_a_string_type(sql_data_type) ? IS_STRING : Z_TYPE_P(data_z); param_php_type = is_a_string_type(sql_data_type) ? IS_STRING : Z_TYPE_P(data_z);
@ -3253,7 +3257,7 @@ void sqlsrv_param_tvp::process_param_column_value(_Inout_ sqlsrv_stmt* stmt)
} else { } else {
// If preprocessing a datetime object fails, throw an error of invalid php type // If preprocessing a datetime object fails, throw an error of invalid php type
bool succeeded = preprocess_datetime_object(stmt, data_z); bool succeeded = preprocess_datetime_object(stmt, data_z);
CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1) { CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -3265,7 +3269,7 @@ void sqlsrv_param_tvp::process_param_column_value(_Inout_ sqlsrv_stmt* stmt)
process_null_param_value(stmt); process_null_param_value(stmt);
break; break;
default: default:
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1); THROW_CORE_ERROR(stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1, NULL);
break; break;
} }
@ -3324,22 +3328,22 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0); core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
if (buffer != NULL) { if (buffer != NULL) {
// SQL_CA_SS_TYPE_NAME is optional for stored procedure calls, but it must be // 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 // specified for SQL statements that are not procedure calls to enable the
// server to determine the type of the table-valued parameter. // server to determine the type of the table-valued parameter.
char *tvp_name = reinterpret_cast<char *>(buffer); 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); 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) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
if (Z_TYPE(placeholder_z) == IS_STRING) { if (Z_TYPE(placeholder_z) == IS_STRING) {
// If the table type for the table-valued parameter is defined in a different // 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, // 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. // the server will not be able to determine the type of the table-valued parameter.
char * schema_name = Z_STRVAL(placeholder_z); 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); 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) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
// Free and reset the placeholder_z // Free and reset the placeholder_z
@ -3352,7 +3356,7 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
SQLHDESC desc; SQLHDESC desc;
core::SQLGetStmtAttr(stmt, SQL_ATTR_APP_PARAM_DESC, &desc, 0, 0); core::SQLGetStmtAttr(stmt, SQL_ATTR_APP_PARAM_DESC, &desc, 0, 0);
SQLRETURN r = ::SQLSetDescField(desc, param_pos + 1, SQL_DESC_DATA_PTR, reinterpret_cast<SQLPOINTER>(this), 0); SQLRETURN r = ::SQLSetDescField(desc, param_pos + 1, SQL_DESC_DATA_PTR, reinterpret_cast<SQLPOINTER>(this), 0);
CHECK_SQL_ERROR_OR_WARNING(r, stmt) { CHECK_SQL_ERROR_OR_WARNING(r, stmt, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3374,11 +3378,11 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
zval* data_z = NULL; zval* data_z = NULL;
int num_columns = 0; int num_columns = 0;
// In case there are null values in the first row, have to loop // In case there are null values in the first row, have to loop
// through the entire first row of column values using the Zend macros. // through the entire first row of column values using the Zend macros.
ZEND_HASH_FOREACH_KEY_VAL(cols_ht, id, key, data_z) { ZEND_HASH_FOREACH_KEY_VAL(cols_ht, id, key, data_z) {
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR(type == HASH_KEY_IS_STRING, stmt, SQLSRV_ERROR_TVP_STRING_KEYS, param_pos + 1) { CHECK_CUSTOM_ERROR(type == HASH_KEY_IS_STRING, stmt, SQLSRV_ERROR_TVP_STRING_KEYS, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3387,8 +3391,8 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
sqlsrv_param* column_param = tvp_columns[pos]; sqlsrv_param* column_param = tvp_columns[pos];
SQLSRV_ASSERT(column_param != NULL, "sqlsrv_param_tvp::bind_param -- column param should not be null"); SQLSRV_ASSERT(column_param != NULL, "sqlsrv_param_tvp::bind_param -- column param should not be null");
// If data_z is NULL, will need to keep looking in the subsequent rows of // If data_z is NULL, will need to keep looking in the subsequent rows of
// the same column until a non-null value is found. Since Zend macros must be // the same column until a non-null value is found. Since Zend macros must be
// used to traverse the array items, nesting Zend macros in different directions // used to traverse the array items, nesting Zend macros in different directions
// does not work. // does not work.
// Therefore, save data_z for later processing and binding. // Therefore, save data_z for later processing and binding.
@ -3467,10 +3471,10 @@ void sqlsrv_param_tvp::populate_cell_placeholder(_Inout_ sqlsrv_stmt* stmt, _In_
} }
} }
// If this is the table-valued parameter, loop through each parameter column // If this is the table-valued parameter, loop through each parameter column
// and populate the cell's placeholder_z. // and populate the cell's placeholder_z.
// If this is one of the constituent columns of the table-valued parameter, // If this is one of the constituent columns of the table-valued parameter,
// call SQLPutData() to send the cell value to the server (based on current_row // call SQLPutData() to send the cell value to the server (based on current_row
// and param_pos) // and param_pos)
bool sqlsrv_param_tvp::send_data_packet(_Inout_ sqlsrv_stmt* stmt) bool sqlsrv_param_tvp::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
{ {
@ -3514,7 +3518,7 @@ bool sqlsrv_param_tvp::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
bool succeeded = convert_datetime_to_string(stmt, value_z); bool succeeded = convert_datetime_to_string(stmt, value_z);
// Conversion failed so assume the input was an invalid PHP type // Conversion failed so assume the input was an invalid PHP type
CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1) { CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_INVALID_COLUMN_PHPTYPE, parent_tvp->param_pos + 1, param_pos + 1, NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3540,7 +3544,7 @@ bool sqlsrv_param_tvp::send_data_packet(_Inout_ sqlsrv_stmt* stmt)
} }
// This method would change the member placeholder_z // This method would change the member placeholder_z
bool succeeded = convert_input_str_to_utf16(stmt, value_z); bool succeeded = convert_input_str_to_utf16(stmt, value_z);
CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_STRING_ENCODING_TRANSLATE, parent_tvp->param_pos + 1, param_pos + 1, get_last_error_message()) { CHECK_CUSTOM_ERROR(!succeeded, stmt, SQLSRV_ERROR_TVP_STRING_ENCODING_TRANSLATE, parent_tvp->param_pos + 1, param_pos + 1, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
@ -3642,7 +3646,7 @@ bool sqlsrv_params_container::get_next_parameter(_Inout_ sqlsrv_stmt* stmt)
// If no more data, all the bound parameters have been exhausted, so return false (done) // If no more data, all the bound parameters have been exhausted, so return false (done)
if (SQL_SUCCEEDED(r) || r == SQL_NO_DATA) { if (SQL_SUCCEEDED(r) || r == SQL_NO_DATA) {
// Done now, reset current_param // Done now, reset current_param
current_param = NULL; current_param = NULL;
return false; return false;
} else if (r == SQL_NEED_DATA) { } else if (r == SQL_NEED_DATA) {
@ -3651,7 +3655,7 @@ bool sqlsrv_params_container::get_next_parameter(_Inout_ sqlsrv_stmt* stmt)
SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!"); SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!");
current_param->init_data_from_zval(stmt); current_param->init_data_from_zval(stmt);
} else { } else {
// Do not reset current_param when param is NULL, because // Do not reset current_param when param is NULL, because
// it means that data is expected from the existing current_param // it means that data is expected from the existing current_param
} }
} }
@ -3674,7 +3678,7 @@ bool sqlsrv_params_container::send_next_packet(_Inout_ sqlsrv_stmt* stmt)
// The helper method send_stream_packet() returns false when EOF is reached // The helper method send_stream_packet() returns false when EOF is reached
if (current_param && current_param->send_data_packet(stmt) == false) { if (current_param && current_param->send_data_packet(stmt) == false) {
// Now that EOF has been reached, reset current_param for next round // Now that EOF has been reached, reset current_param for next round
// Bear in mind that SQLParamData might request the same stream resource again // Bear in mind that SQLParamData might request the same stream resource again
current_param = NULL; current_param = NULL;
} }

View file

@ -3,17 +3,17 @@
// //
// Contents: Implementation of PHP streams for reading SQL Server data // Contents: Implementation of PHP streams for reading SQL Server data
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -27,7 +27,7 @@ int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ )
{ {
sqlsrv_stream* ss = static_cast<sqlsrv_stream*>( stream->abstract ); sqlsrv_stream* ss = static_cast<sqlsrv_stream*>( stream->abstract );
SQLSRV_ASSERT( ss != NULL && ss->stmt != NULL, "sqlsrv_stream_close: sqlsrv_stream* ss was null." ); SQLSRV_ASSERT( ss != NULL && ss->stmt != NULL, "sqlsrv_stream_close: sqlsrv_stream* ss was null." );
// free the stream resources in the Zend engine // free the stream resources in the Zend engine
php_stream_free( stream, PHP_STREAM_FREE_RELEASE_STREAM ); php_stream_free( stream, PHP_STREAM_FREE_RELEASE_STREAM );
@ -44,7 +44,7 @@ int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ )
// read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are // read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are
// set when sqlsrv_get_field is called by the user specifying which field type they want. // set when sqlsrv_get_field is called by the user specifying which field type they want.
#if PHP_VERSION_ID >= 70400 #if PHP_VERSION_ID >= 70400
ssize_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count) ssize_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count)
#else #else
size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count) size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count)
@ -97,7 +97,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
SQLRETURN r = ss->stmt->current_results->get_data(ss->field_index + 1, c_type, get_data_buffer, count /*BufferLength*/, &read, false /*handle_warning*/); SQLRETURN r = ss->stmt->current_results->get_data(ss->field_index + 1, c_type, get_data_buffer, count /*BufferLength*/, &read, false /*handle_warning*/);
CHECK_SQL_ERROR( r, ss->stmt ) { CHECK_SQL_ERROR( r, ss->stmt ) {
stream->eof = 1; stream->eof = 1;
throw core::CoreException(); throw core::CoreException();
} }
@ -126,7 +126,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
"did not occur." ); "did not occur." );
} }
// As per SQLGetData documentation, if the length of character data exceeds the BufferLength, // As per SQLGetData documentation, if the length of character data exceeds the BufferLength,
// SQLGetData truncates the data to BufferLength less the length of null-termination character. // SQLGetData truncates the data to BufferLength less the length of null-termination character.
// But when fetching binary fields as chars (wide chars), each byte is represented as 2 hex characters, // But when fetching binary fields as chars (wide chars), each byte is represented as 2 hex characters,
// each takes the size of a char (wide char). Note that BufferLength may not be multiples of 2 or 4. // each takes the size of a char (wide char). Note that BufferLength may not be multiples of 2 or 4.
@ -138,7 +138,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
if( is_truncated_warning( state ) || count < read) { if( is_truncated_warning( state ) || count < read) {
#else #else
if( is_truncated_warning( state ) ) { if( is_truncated_warning( state ) ) {
#endif // !_WIN32 #endif // !_WIN32
size_t char_size = sizeof(SQLCHAR); size_t char_size = sizeof(SQLCHAR);
switch( c_type ) { switch( c_type ) {
@ -168,13 +168,13 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
} }
} }
else { else {
CHECK_SQL_WARNING( r, ss->stmt ); CHECK_SQL_WARNING( r, ss->stmt, NULL );
} }
} }
// If the encoding is UTF-8 // If the encoding is UTF-8
if( c_type == SQL_C_WCHAR ) { if( c_type == SQL_C_WCHAR ) {
count *= 2; count *= 2;
// Undo the shift to use the full buffer // Undo the shift to use the full buffer
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing // flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP. // an error. This happens only on XP.
@ -199,18 +199,18 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
static_cast<int>(read >> 1), buf, static_cast<int>(count), NULL, NULL ); static_cast<int>(read >> 1), buf, static_cast<int>(count), NULL, NULL );
#endif // !_WIN32 #endif // !_WIN32
if( enc_len == 0 ) { if( enc_len == 0 ) {
stream->eof = 1; stream->eof = 1;
THROW_CORE_ERROR( ss->stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ); THROW_CORE_ERROR( ss->stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message(), NULL );
} }
read = enc_len; read = enc_len;
} }
return static_cast<size_t>( read ); return static_cast<size_t>( read );
} }
catch (core::CoreException&) { catch (core::CoreException&) {
#if PHP_VERSION_ID >= 70400 #if PHP_VERSION_ID >= 70400
return -1; return -1;
#else #else
return 0; return 0;
@ -218,7 +218,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
} }
catch (...) { catch (...) {
LOG(SEV_ERROR, "sqlsrv_stream_read: Unknown exception caught."); LOG(SEV_ERROR, "sqlsrv_stream_read: Unknown exception caught.");
#if PHP_VERSION_ID >= 70400 #if PHP_VERSION_ID >= 70400
return -1; return -1;
#else #else
return 0; return 0;
@ -242,7 +242,7 @@ php_stream_ops sqlsrv_stream_ops = {
// open a stream and return the sqlsrv_stream_ops function table as part of the // open a stream and return the sqlsrv_stream_ops function table as part of the
// return value. There is only one valid way to open a stream, using sqlsrv_get_field on // return value. There is only one valid way to open a stream, using sqlsrv_get_field on
// certain field types. A sqlsrv stream may only be opened in read mode. // certain field types. A sqlsrv stream may only be opened in read mode.
static php_stream* sqlsrv_stream_opener( _In_opt_ php_stream_wrapper* wrapper, _In_ const char*, _In_ const char* mode, static php_stream* sqlsrv_stream_opener( _In_opt_ php_stream_wrapper* wrapper, _In_ const char*, _In_ const char* mode,
_In_opt_ int options, _In_ zend_string **, php_stream_context* STREAMS_DC ) _In_opt_ int options, _In_ zend_string **, php_stream_context* STREAMS_DC )
{ {
@ -259,8 +259,8 @@ static php_stream* sqlsrv_stream_opener( _In_opt_ php_stream_wrapper* wrapper, _
ss = static_cast<sqlsrv_stream*>( sqlsrv_malloc( sizeof( sqlsrv_stream ))); ss = static_cast<sqlsrv_stream*>( sqlsrv_malloc( sizeof( sqlsrv_stream )));
memset( ss, 0, sizeof( sqlsrv_stream )); memset( ss, 0, sizeof( sqlsrv_stream ));
// The function core_get_field_common() is changed to pass REPORT_ERRORS for // The function core_get_field_common() is changed to pass REPORT_ERRORS for
// php_stream_open_wrapper(). Whether the error flag is toggled or cleared, // php_stream_open_wrapper(). Whether the error flag is toggled or cleared,
// the argument "options" will be zero. // the argument "options" will be zero.
// For details check this pull request: https://github.com/php/php-src/pull/6190 // For details check this pull request: https://github.com/php/php-src/pull/6190
if (options != 0) { if (options != 0) {

View file

@ -2,20 +2,20 @@
// File: core_util.cpp // File: core_util.cpp
// //
// Contents: Utility functions used by both connection or statement functions for both the PDO and sqlsrv drivers // Contents: Utility functions used by both connection or statement functions for both the PDO and sqlsrv drivers
// //
// Comments: Mostly error handling and some type handling // Comments: Mostly error handling and some type handling
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -38,7 +38,7 @@ char last_err_msg[2048] = {'\0'}; // 2k to hold the error messages
// routine used by utf16_string_from_mbcs_string // routine used by utf16_string_from_mbcs_string
unsigned int convert_string_from_default_encoding( _In_ unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string, unsigned int convert_string_from_default_encoding( _In_ unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string,
_In_ unsigned int mbcs_len, _In_ unsigned int mbcs_len,
_Out_writes_(utf16_len) __transfer( mbcs_in_string ) SQLWCHAR* utf16_out_string, _Out_writes_(utf16_len) __transfer( mbcs_in_string ) SQLWCHAR* utf16_out_string,
_In_ unsigned int utf16_len, bool use_strict_conversion = false ); _In_ unsigned int utf16_len, bool use_strict_conversion = false );
@ -62,13 +62,13 @@ void log_activity(_In_opt_ const char* msg, _In_opt_ va_list* print_args)
} }
// SQLSTATE for all internal errors // SQLSTATE for all internal errors
SQLCHAR IMSSP[] = "IMSSP"; SQLCHAR IMSSP[] = "IMSSP";
// SQLSTATE for all internal warnings // SQLSTATE for all internal warnings
SQLCHAR SSPWARN[] = "01SSP"; SQLCHAR SSPWARN[] = "01SSP";
// write to the php log if the severity and subsystem match the filters currently set in the INI or // write to the php log if the severity and subsystem match the filters currently set in the INI or
// the script (sqlsrv_configure). // the script (sqlsrv_configure).
void write_to_log( _In_ unsigned int severity, _In_ const char* msg, ...) void write_to_log( _In_ unsigned int severity, _In_ const char* msg, ...)
{ {
@ -150,7 +150,7 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_(
// Allocate enough space to hold the largest possible number of bytes for UTF-8 conversion // Allocate enough space to hold the largest possible number of bytes for UTF-8 conversion
// instead of calling FromUtf16, for performance reasons // instead of calling FromUtf16, for performance reasons
cchOutLen = 4 * cchInLen; cchOutLen = 4 * cchInLen;
#else #else
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing // flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP. // an error. This happens only on XP.
DWORD flags = 0; DWORD flags = 0;
@ -162,10 +162,10 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_(
// Calculate the number of output bytes required - no performance hit here because // Calculate the number of output bytes required - no performance hit here because
// WideCharToMultiByte is highly optimised // WideCharToMultiByte is highly optimised
cchOutLen = WideCharToMultiByte( encoding, flags, cchOutLen = WideCharToMultiByte( encoding, flags,
inString, cchInLen, inString, cchInLen,
NULL, 0, NULL, NULL ); NULL, 0, NULL, NULL );
#endif // !_WIN32 #endif // !_WIN32
if( cchOutLen == 0 ) { if( cchOutLen == 0 ) {
return false; return false;
} }
@ -173,7 +173,7 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_(
// Create a buffer to fit the encoded string // Create a buffer to fit the encoded string
char* newString = reinterpret_cast<char*>( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ )); char* newString = reinterpret_cast<char*>( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ ));
memset(newString, '\0', cchOutLen+1); memset(newString, '\0', cchOutLen+1);
#ifndef _WIN32 #ifndef _WIN32
int rc = SystemLocale::FromUtf16Strict( encoding, inString, cchInLen, newString, static_cast<int>(cchOutLen)); int rc = SystemLocale::FromUtf16Strict( encoding, inString, cchInLen, newString, static_cast<int>(cchOutLen));
#else #else
@ -216,7 +216,7 @@ SQLWCHAR* utf16_string_from_mbcs_string( _In_ SQLSRV_ENCODING php_encoding, _In_
return utf16_string; return utf16_string;
} }
// Converts an input (assuming a datetime string) to a zval containing a PHP DateTime object. // Converts an input (assuming a datetime string) to a zval containing a PHP DateTime object.
// If the input is null, this simply returns a NULL zval. If anything wrong occurs during conversion, // If the input is null, this simply returns a NULL zval. If anything wrong occurs during conversion,
// an exception will be thrown. // an exception will be thrown.
void convert_datetime_string_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_opt_ char* input, _In_ SQLLEN length, _Inout_ zval& out_zval) void convert_datetime_string_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_opt_ char* input, _In_ SQLLEN length, _Inout_ zval& out_zval)
@ -308,10 +308,10 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
SQLLEN sqlstate_len = 0; SQLLEN sqlstate_len = 0;
convert_string_from_utf16(enc, wsqlstate, wsqlstate_len, (char**)&error->sqlstate, sqlstate_len); convert_string_from_utf16(enc, wsqlstate, wsqlstate_len, (char**)&error->sqlstate, sqlstate_len);
SQLLEN message_len = 0; SQLLEN message_len = 0;
if (r == SQL_SUCCESS_WITH_INFO && wmessage_len > SQL_MAX_ERROR_MESSAGE_LENGTH) { if (r == SQL_SUCCESS_WITH_INFO && wmessage_len > SQL_MAX_ERROR_MESSAGE_LENGTH) {
// note that wmessage_len is the number of characters required for the error message -- // note that wmessage_len is the number of characters required for the error message --
// create a new buffer big enough for this lengthy error message // create a new buffer big enough for this lengthy error message
sqlsrv_malloc_auto_ptr<SQLWCHAR> wnative_message_str; sqlsrv_malloc_auto_ptr<SQLWCHAR> wnative_message_str;
@ -319,7 +319,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
SQLSMALLINT returned_len = 0; SQLSMALLINT returned_len = 0;
wnative_message_str = reinterpret_cast<SQLWCHAR*>(sqlsrv_malloc(expected_len)); wnative_message_str = reinterpret_cast<SQLWCHAR*>(sqlsrv_malloc(expected_len));
memset(wnative_message_str, '\0', expected_len); memset(wnative_message_str, '\0', expected_len);
SQLRETURN rtemp = ::SQLGetDiagFieldW(h_type, h, record_number, SQL_DIAG_MESSAGE_TEXT, wnative_message_str, wmessage_len, &returned_len); SQLRETURN rtemp = ::SQLGetDiagFieldW(h_type, h, record_number, SQL_DIAG_MESSAGE_TEXT, wnative_message_str, wmessage_len, &returned_len);
if (!SQL_SUCCEEDED(rtemp) || returned_len != expected_len) { if (!SQL_SUCCEEDED(rtemp) || returned_len != expected_len) {
@ -341,7 +341,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
// Only overrides 'severity' if 'check_warning' is true (false by default) // Only overrides 'severity' if 'check_warning' is true (false by default)
if (check_warning) { if (check_warning) {
// The character string value returned for an SQLSTATE consists of a two-character class value // The character string value returned for an SQLSTATE consists of a two-character class value
// followed by a three-character subclass value. A class value of "01" indicates a warning. // followed by a three-character subclass value. A class value of "01" indicates a warning.
// https://docs.microsoft.com/sql/odbc/reference/appendixes/appendix-a-odbc-error-codes?view=sql-server-ver15 // https://docs.microsoft.com/sql/odbc/reference/appendixes/appendix-a-odbc-error-codes?view=sql-server-ver15
if (error->sqlstate[0] == '0' && error->sqlstate[1] == '1') { if (error->sqlstate[0] == '0' && error->sqlstate[1] == '1') {
@ -360,7 +360,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
} }
// format and return a driver specfic error // format and return a driver specfic error
void core_sqlsrv_format_driver_error( _In_ sqlsrv_context& ctx, _In_ sqlsrv_error_const const* custom_error, void core_sqlsrv_format_driver_error( _In_ sqlsrv_context& ctx, _In_ sqlsrv_error_const const* custom_error,
_Out_ sqlsrv_error_auto_ptr& formatted_error, _In_ logging_severity severity, _In_opt_ va_list* args ) _Out_ sqlsrv_error_auto_ptr& formatted_error, _In_ logging_severity severity, _In_opt_ va_list* args )
{ {
// allocate space for the formatted message // allocate space for the formatted message
@ -368,13 +368,13 @@ void core_sqlsrv_format_driver_error( _In_ sqlsrv_context& ctx, _In_ sqlsrv_erro
formatted_error->sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); formatted_error->sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE ));
formatted_error->native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 )); formatted_error->native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ));
DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast<LPSTR>( custom_error->native_message ), 0, 0, DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast<LPSTR>( custom_error->native_message ), 0, 0,
reinterpret_cast<LPSTR>( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH, args ); reinterpret_cast<LPSTR>( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH, args );
if( rc == 0 ) { if( rc == 0 ) {
strcpy_s( reinterpret_cast<char*>( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH, strcpy_s( reinterpret_cast<char*>( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH,
reinterpret_cast<char*>( INTERNAL_FORMAT_ERROR )); reinterpret_cast<char*>( INTERNAL_FORMAT_ERROR ));
} }
strcpy_s( reinterpret_cast<char*>( formatted_error->sqlstate ), SQL_SQLSTATE_BUFSIZE, strcpy_s( reinterpret_cast<char*>( formatted_error->sqlstate ), SQL_SQLSTATE_BUFSIZE,
reinterpret_cast<char*>( custom_error->sqlstate )); reinterpret_cast<char*>( custom_error->sqlstate ));
formatted_error->native_code = custom_error->native_code; formatted_error->native_code = custom_error->native_code;
@ -471,7 +471,7 @@ unsigned int convert_string_from_default_encoding( _In_ unsigned int php_encodin
#else #else
unsigned int required_len = MultiByteToWideChar( win_encoding, MB_ERR_INVALID_CHARS, mbcs_in_string, mbcs_len, utf16_out_string, utf16_len ); unsigned int required_len = MultiByteToWideChar( win_encoding, MB_ERR_INVALID_CHARS, mbcs_in_string, mbcs_len, utf16_out_string, utf16_len );
#endif // !_Win32 #endif // !_Win32
if( required_len == 0 ) { if( required_len == 0 ) {
return 0; return 0;
} }
@ -509,7 +509,7 @@ namespace data_classification {
bool converted = convert_string_from_utf16(encoding, temp_field_name, len, field_name, field_name_len); bool converted = convert_string_from_utf16(encoding, temp_field_name, len, field_name, field_name_len);
CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { CHECK_CUSTOM_ERROR(!converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -524,16 +524,16 @@ namespace data_classification {
} }
sqlsrv_free(pair); sqlsrv_free(pair);
} }
void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>* pairs, _Inout_ unsigned char **pptr) void parse_sensitivity_name_id_pairs(_Inout_ sqlsrv_stmt* stmt, _Inout_ USHORT& numpairs, _Inout_ std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>>* pairs, _Inout_ unsigned char **pptr)
{ {
unsigned char *ptr = *pptr; unsigned char *ptr = *pptr;
unsigned short npairs; unsigned short npairs;
numpairs = npairs = *(reinterpret_cast<unsigned short*>(ptr)); numpairs = npairs = *(reinterpret_cast<unsigned short*>(ptr));
SQLSRV_ENCODING encoding = ((stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding()); SQLSRV_ENCODING encoding = ((stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : stmt->encoding());
pairs->reserve(numpairs); pairs->reserve(numpairs);
ptr += sizeof(unsigned short); ptr += sizeof(unsigned short);
while (npairs--) { while (npairs--) {
int namelen, idlen; int namelen, idlen;
@ -549,7 +549,7 @@ namespace data_classification {
namelen = *ptr++; namelen = *ptr++;
nameptr = ptr; nameptr = ptr;
pair->name_len = namelen; pair->name_len = namelen;
convert_sensivity_field(stmt, encoding, nameptr, namelen, (char**)&name, field_len); convert_sensivity_field(stmt, encoding, nameptr, namelen, (char**)&name, field_len);
pair->name = name; pair->name = name;
@ -566,7 +566,7 @@ namespace data_classification {
pair.transferred(); pair.transferred();
} }
*pptr = ptr; *pptr = ptr;
} }
void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr, _In_ bool getRankInfo) void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr, _In_ bool getRankInfo)
{ {
@ -628,7 +628,7 @@ namespace data_classification {
if (meta == NULL) { if (meta == NULL) {
return 0; return 0;
} }
SQLSRV_ASSERT(colno >= 0 && colno < meta->num_columns, "fill_column_sensitivity_array: column number out of bounds"); SQLSRV_ASSERT(colno >= 0 && colno < meta->num_columns, "fill_column_sensitivity_array: column number out of bounds");
zval data_classification; zval data_classification;

View file

@ -4,7 +4,7 @@
// Contents: Contains functions for handling Windows format strings // Contents: Contains functions for handling Windows format strings
// and UTF-16 on non-Windows platforms // and UTF-16 on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// Contents: Contains a portable abstraction for interlocked, atomic // Contents: Contains a portable abstraction for interlocked, atomic
// operations on int32_t and pointer types. // operations on int32_t and pointer types.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// Contents: Contains a portable abstraction for interlocked, atomic // Contents: Contains a portable abstraction for interlocked, atomic
// operations on int32_t and pointer types. // operations on int32_t and pointer types.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// Contents: Contains a portable abstraction for interlocked, singly // Contents: Contains a portable abstraction for interlocked, singly
// linked list. // linked list.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains portable classes for localization // Contents: Contains portable classes for localization
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -5,7 +5,7 @@
// Must be included in one c/cpp file per binary // Must be included in one c/cpp file per binary
// A build error will occur if this inclusion policy is not followed // A build error will occur if this inclusion policy is not followed
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -20,7 +20,7 @@
// pecuniary loss) arising out of the use of or inability to use // pecuniary loss) arising out of the use of or inability to use
// this SDK, even if Microsoft has been advised of the possibility // this SDK, even if Microsoft has been advised of the possibility
// of such damages. // of such damages.
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains the minimal definitions to build on non-Windows platforms // Contents: Contains the minimal definitions to build on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -1,7 +1,7 @@
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
// File: typedefs_for_linux.h // File: typedefs_for_linux.h
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// File: version.h // File: version.h
// Contents: Version number constants // Contents: Version number constants
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
@ -26,7 +26,7 @@
// Increase Minor with backward compatible new functionalities and API changes. // Increase Minor with backward compatible new functionalities and API changes.
// 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 11
#define SQLVERSION_PATCH 1 #define SQLVERSION_PATCH 1
#define SQLVERSION_BUILD 0 #define SQLVERSION_BUILD 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.1" #define PHP_SQLSRV_VERSION "5.11.1"
#define PHP_PDO_SQLSRV_VERSION "5.10.1" #define PHP_PDO_SQLSRV_VERSION "5.11.1"
#endif // VERSION_H #endif // VERSION_H

View file

@ -3,7 +3,7 @@
// //
// Contents: include for definition of Windows types for non-Windows platforms // Contents: include for definition of Windows types for non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@
// Contents: This module defines helper functions to prevent // Contents: This module defines helper functions to prevent
// integer overflow bugs. // integer overflow bugs.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains the minimal definitions to build on non-Windows platforms // Contents: Contains the minimal definitions to build on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: Contains the minimal definitions to build on non-Windows platforms // Contents: Contains the minimal definitions to build on non-Windows platforms
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -4,7 +4,7 @@ dnl
dnl Contents: the code that will go into the configure script, indicating options, dnl Contents: the code that will go into the configure script, indicating options,
dnl external libraries and includes, and what source files are to be compiled. dnl external libraries and includes, and what source files are to be compiled.
dnl dnl
dnl Microsoft Drivers 5.10 for PHP for SQL Server dnl Microsoft Drivers 5.11 for PHP for SQL Server
dnl Copyright(c) Microsoft Corporation dnl Copyright(c) Microsoft Corporation
dnl All rights reserved. dnl All rights reserved.
dnl MIT License dnl MIT License

View file

@ -3,7 +3,7 @@
// //
// Contents: JScript build configuration used by buildconf.bat // Contents: JScript build configuration used by buildconf.bat
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -3,17 +3,17 @@
// //
// Contents: Routines that use connection handles // Contents: Routines that use connection handles
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -117,7 +117,7 @@ struct conn_char_set_func {
if (!strnicmp( encoding, ss_encoding->iana, encoding_len )) { if (!strnicmp( encoding, ss_encoding->iana, encoding_len )) {
if ( ss_encoding->not_for_connection ) { if ( ss_encoding->not_for_connection ) {
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding, NULL );
} }
conn->set_encoding( static_cast<SQLSRV_ENCODING>(ss_encoding->code_page )); conn->set_encoding( static_cast<SQLSRV_ENCODING>(ss_encoding->code_page ));
@ -125,7 +125,7 @@ struct conn_char_set_func {
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding, NULL );
} }
}; };
@ -135,7 +135,16 @@ struct bool_conn_str_func {
{ {
char temp_str[MAX_CONN_VALSTRING_LEN]; char temp_str[MAX_CONN_VALSTRING_LEN];
snprintf(temp_str, MAX_CONN_VALSTRING_LEN, "%s={%s};", option->odbc_name, (zend_is_true(value) ? "yes" : "no")); if (Z_TYPE_P(value) != IS_STRING) {
convert_to_string(value);
}
const char *value_str = Z_STRVAL_P(value);
snprintf(temp_str,
MAX_CONN_VALSTRING_LEN,
"%s={%s};",
option->odbc_name,
((stricmp(value_str, "true") == 0 || stricmp(value_str, "1") == 0) ? "yes" : "no"));
conn_str += temp_str; conn_str += temp_str;
} }
}; };
@ -144,7 +153,7 @@ struct int_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str ) static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{ {
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_LONG, "An integer is expected for this keyword" ) SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_LONG, "An integer is expected for this keyword" )
char temp_str[MAX_CONN_VALSTRING_LEN]; char temp_str[MAX_CONN_VALSTRING_LEN];
@ -159,7 +168,7 @@ struct int_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ ) static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{ {
try { try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )), SQL_IS_UINTEGER ); core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )), SQL_IS_UINTEGER );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
@ -175,7 +184,7 @@ struct bool_conn_attr_func {
{ {
try { try {
core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)), SQL_IS_UINTEGER); core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)), SQL_IS_UINTEGER);
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;
@ -187,13 +196,13 @@ struct bool_conn_attr_func {
//// *** internal functions *** //// *** internal functions ***
void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn ); void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn );
void validate_conn_options( _Inout_ sqlsrv_context& ctx, _In_ zval* user_options_z, _Inout_ char** uid, _Inout_ char** pwd, void validate_conn_options( _Inout_ sqlsrv_context& ctx, _In_ zval* user_options_z, _Inout_ char** uid, _Inout_ char** pwd,
_Inout_ HashTable* ss_conn_options_ht ); _Inout_ HashTable* ss_conn_options_ht );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht ); void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht );
void add_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, void add_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len,
_Inout_ HashTable* options_ht, _Inout_ zval* data ); _Inout_ HashTable* options_ht, _Inout_ zval* data );
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ HashTable* options_ht, _Inout_ zval* data ); void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ HashTable* options_ht, _Inout_ zval* data );
int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ zval const* value_z ); int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ zval const* value_z );
int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len ); int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len );
} }
@ -264,60 +273,60 @@ const char HostNameInCertificate[] = "HostNameInCertificate";
} }
enum SS_CONN_OPTIONS { enum SS_CONN_OPTIONS {
SS_CONN_OPTION_DATE_AS_STRING = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, SS_CONN_OPTION_DATE_AS_STRING = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC,
SS_CONN_OPTION_FORMAT_DECIMALS, SS_CONN_OPTION_FORMAT_DECIMALS,
SS_CONN_OPTION_DECIMAL_PLACES, SS_CONN_OPTION_DECIMAL_PLACES,
}; };
//List of all statement options supported by this driver //List of all statement options supported by this driver
const stmt_option SS_STMT_OPTS[] = { const stmt_option SS_STMT_OPTS[] = {
{ {
SSStmtOptionNames::QUERY_TIMEOUT, SSStmtOptionNames::QUERY_TIMEOUT,
sizeof( SSStmtOptionNames::QUERY_TIMEOUT ), sizeof( SSStmtOptionNames::QUERY_TIMEOUT ),
SQLSRV_STMT_OPTION_QUERY_TIMEOUT, SQLSRV_STMT_OPTION_QUERY_TIMEOUT,
std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout ) std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout )
}, },
{ {
SSStmtOptionNames::SEND_STREAMS_AT_EXEC, SSStmtOptionNames::SEND_STREAMS_AT_EXEC,
sizeof( SSStmtOptionNames::SEND_STREAMS_AT_EXEC ), sizeof( SSStmtOptionNames::SEND_STREAMS_AT_EXEC ),
SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC, SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC,
std::unique_ptr<stmt_option_send_at_exec>( new stmt_option_send_at_exec ) std::unique_ptr<stmt_option_send_at_exec>( new stmt_option_send_at_exec )
}, },
{ {
SSStmtOptionNames::SCROLLABLE, SSStmtOptionNames::SCROLLABLE,
sizeof( SSStmtOptionNames::SCROLLABLE ), sizeof( SSStmtOptionNames::SCROLLABLE ),
SQLSRV_STMT_OPTION_SCROLLABLE, SQLSRV_STMT_OPTION_SCROLLABLE,
std::unique_ptr<stmt_option_ss_scrollable>( new stmt_option_ss_scrollable ) std::unique_ptr<stmt_option_ss_scrollable>( new stmt_option_ss_scrollable )
}, },
{ {
SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE, SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE,
sizeof( SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE ), sizeof( SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE ),
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE, SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE,
std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit ) std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit )
}, },
{ {
SSStmtOptionNames::DATE_AS_STRING, SSStmtOptionNames::DATE_AS_STRING,
sizeof( SSStmtOptionNames::DATE_AS_STRING ), sizeof( SSStmtOptionNames::DATE_AS_STRING ),
SQLSRV_STMT_OPTION_DATE_AS_STRING, SQLSRV_STMT_OPTION_DATE_AS_STRING,
std::unique_ptr<stmt_option_date_as_string>( new stmt_option_date_as_string ) std::unique_ptr<stmt_option_date_as_string>( new stmt_option_date_as_string )
}, },
{ {
SSStmtOptionNames::FORMAT_DECIMALS, SSStmtOptionNames::FORMAT_DECIMALS,
sizeof( SSStmtOptionNames::FORMAT_DECIMALS ), sizeof( SSStmtOptionNames::FORMAT_DECIMALS ),
SQLSRV_STMT_OPTION_FORMAT_DECIMALS, SQLSRV_STMT_OPTION_FORMAT_DECIMALS,
std::unique_ptr<stmt_option_format_decimals>( new stmt_option_format_decimals ) std::unique_ptr<stmt_option_format_decimals>( new stmt_option_format_decimals )
}, },
{ {
SSStmtOptionNames::DECIMAL_PLACES, SSStmtOptionNames::DECIMAL_PLACES,
sizeof( SSStmtOptionNames::DECIMAL_PLACES), sizeof( SSStmtOptionNames::DECIMAL_PLACES),
SQLSRV_STMT_OPTION_DECIMAL_PLACES, SQLSRV_STMT_OPTION_DECIMAL_PLACES,
std::unique_ptr<stmt_option_decimal_places>( new stmt_option_decimal_places ) std::unique_ptr<stmt_option_decimal_places>( new stmt_option_decimal_places )
}, },
{ {
SSStmtOptionNames::DATA_CLASSIFICATION, SSStmtOptionNames::DATA_CLASSIFICATION,
sizeof( SSStmtOptionNames::DATA_CLASSIFICATION ), sizeof( SSStmtOptionNames::DATA_CLASSIFICATION ),
SQLSRV_STMT_OPTION_DATA_CLASSIFICATION, SQLSRV_STMT_OPTION_DATA_CLASSIFICATION,
std::unique_ptr<stmt_option_data_classification>( new stmt_option_data_classification ) std::unique_ptr<stmt_option_data_classification>( new stmt_option_data_classification )
}, },
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} }, { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
@ -326,32 +335,32 @@ const stmt_option SS_STMT_OPTS[] = {
// List of all connection options supported by this driver. // List of all connection options supported by this driver.
const connection_option SS_CONN_OPTS[] = { const connection_option SS_CONN_OPTS[] = {
{ {
SSConnOptionNames::APP, SSConnOptionNames::APP,
sizeof( SSConnOptionNames::APP ), sizeof( SSConnOptionNames::APP ),
SQLSRV_CONN_OPTION_APP, SQLSRV_CONN_OPTION_APP,
ODBCConnOptions::APP, ODBCConnOptions::APP,
sizeof( ODBCConnOptions::APP ), sizeof( ODBCConnOptions::APP ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
SSConnOptionNames::AccessToken, SSConnOptionNames::AccessToken,
sizeof( SSConnOptionNames::AccessToken ), sizeof( SSConnOptionNames::AccessToken ),
SQLSRV_CONN_OPTION_ACCESS_TOKEN, SQLSRV_CONN_OPTION_ACCESS_TOKEN,
ODBCConnOptions::AccessToken, ODBCConnOptions::AccessToken,
sizeof( ODBCConnOptions::AccessToken), sizeof( ODBCConnOptions::AccessToken),
CONN_ATTR_STRING, CONN_ATTR_STRING,
access_token_set_func::func access_token_set_func::func
}, },
{ {
SSConnOptionNames::ApplicationIntent, SSConnOptionNames::ApplicationIntent,
sizeof( SSConnOptionNames::ApplicationIntent ), sizeof( SSConnOptionNames::ApplicationIntent ),
SQLSRV_CONN_OPTION_APPLICATION_INTENT, SQLSRV_CONN_OPTION_APPLICATION_INTENT,
ODBCConnOptions::ApplicationIntent, ODBCConnOptions::ApplicationIntent,
sizeof( ODBCConnOptions::ApplicationIntent ), sizeof( ODBCConnOptions::ApplicationIntent ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
SSConnOptionNames::AttachDBFileName, SSConnOptionNames::AttachDBFileName,
@ -447,17 +456,17 @@ const connection_option SS_CONN_OPTS[] = {
SSConnOptionNames::Encrypt, SSConnOptionNames::Encrypt,
sizeof( SSConnOptionNames::Encrypt ), sizeof( SSConnOptionNames::Encrypt ),
SQLSRV_CONN_OPTION_ENCRYPT, SQLSRV_CONN_OPTION_ENCRYPT,
ODBCConnOptions::Encrypt, ODBCConnOptions::Encrypt,
sizeof( ODBCConnOptions::Encrypt ), sizeof( ODBCConnOptions::Encrypt ),
CONN_ATTR_MIXED, CONN_ATTR_MIXED,
srv_encrypt_set_func::func srv_encrypt_set_func::func
}, },
{ {
SSConnOptionNames::Failover_Partner, SSConnOptionNames::Failover_Partner,
sizeof( SSConnOptionNames::Failover_Partner ), sizeof( SSConnOptionNames::Failover_Partner ),
SQLSRV_CONN_OPTION_FAILOVER_PARTNER, SQLSRV_CONN_OPTION_FAILOVER_PARTNER,
ODBCConnOptions::Failover_Partner, ODBCConnOptions::Failover_Partner,
sizeof( ODBCConnOptions::Failover_Partner ), sizeof( ODBCConnOptions::Failover_Partner ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
@ -468,7 +477,7 @@ const connection_option SS_CONN_OPTS[] = {
ODBCConnOptions::KeyStoreAuthentication, ODBCConnOptions::KeyStoreAuthentication,
sizeof( ODBCConnOptions::KeyStoreAuthentication ), sizeof( ODBCConnOptions::KeyStoreAuthentication ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
ce_akv_str_set_func::func ce_akv_str_set_func::func
}, },
{ {
SSConnOptionNames::KeyStorePrincipalId, SSConnOptionNames::KeyStorePrincipalId,
@ -477,7 +486,7 @@ const connection_option SS_CONN_OPTS[] = {
ODBCConnOptions::KeyStorePrincipalId, ODBCConnOptions::KeyStorePrincipalId,
sizeof( ODBCConnOptions::KeyStorePrincipalId ), sizeof( ODBCConnOptions::KeyStorePrincipalId ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
ce_akv_str_set_func::func ce_akv_str_set_func::func
}, },
{ {
SSConnOptionNames::KeyStoreSecret, SSConnOptionNames::KeyStoreSecret,
@ -495,7 +504,7 @@ const connection_option SS_CONN_OPTS[] = {
ODBCConnOptions::LoginTimeout, ODBCConnOptions::LoginTimeout,
sizeof( ODBCConnOptions::LoginTimeout ), sizeof( ODBCConnOptions::LoginTimeout ),
CONN_ATTR_INT, CONN_ATTR_INT,
int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func
}, },
{ {
SSConnOptionNames::MARS_Option, SSConnOptionNames::MARS_Option,
@ -529,9 +538,9 @@ const connection_option SS_CONN_OPTS[] = {
sizeof( SSConnOptionNames::TraceFile ), sizeof( SSConnOptionNames::TraceFile ),
SQLSRV_CONN_OPTION_TRACE_FILE, SQLSRV_CONN_OPTION_TRACE_FILE,
ODBCConnOptions::TraceFile, ODBCConnOptions::TraceFile,
sizeof( ODBCConnOptions::TraceFile ), sizeof( ODBCConnOptions::TraceFile ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
str_conn_attr_func<SQL_ATTR_TRACEFILE>::func str_conn_attr_func<SQL_ATTR_TRACEFILE>::func
}, },
{ {
SSConnOptionNames::TraceOn, SSConnOptionNames::TraceOn,
@ -575,7 +584,7 @@ const connection_option SS_CONN_OPTS[] = {
SQLSRV_CONN_OPTION_WSID, SQLSRV_CONN_OPTION_WSID,
ODBCConnOptions::WSID, ODBCConnOptions::WSID,
sizeof( ODBCConnOptions::WSID ), sizeof( ODBCConnOptions::WSID ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
@ -643,11 +652,11 @@ const connection_option SS_CONN_OPTS[] = {
// $connectionInfo [OPTIONAL]: An associative array that contains connection // $connectionInfo [OPTIONAL]: An associative array that contains connection
// attributes (for example, array("Database" => "AdventureWorks")). // attributes (for example, array("Database" => "AdventureWorks")).
// //
// Return Value // Return Value
// A PHP connection resource. If a connection cannot be successfully created and // A PHP connection resource. If a connection cannot be successfully created and
// opened, false is returned // opened, false is returned
PHP_FUNCTION ( sqlsrv_connect ) PHP_FUNCTION ( sqlsrv_connect )
{ {
LOG_FUNCTION( "sqlsrv_connect" ); LOG_FUNCTION( "sqlsrv_connect" );
g_ss_henv_cp->set_func(_FN_); g_ss_henv_cp->set_func(_FN_);
@ -664,11 +673,11 @@ PHP_FUNCTION ( sqlsrv_connect )
ZVAL_UNDEF(&conn_z); ZVAL_UNDEF(&conn_z);
// get the server name and connection options // get the server name and connection options
int result = zend_parse_parameters( ZEND_NUM_ARGS(), "s|a", &server, &server_len, &options_z ); int result = zend_parse_parameters( ZEND_NUM_ARGS(), "s|a", &server, &server_len, &options_z );
CHECK_CUSTOM_ERROR(( result == FAILURE ), *g_ss_henv_cp, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, "sqlsrv_connect" ) { CHECK_CUSTOM_ERROR(( result == FAILURE ), *g_ss_henv_cp, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, "sqlsrv_connect", NULL) {
RETURN_FALSE; RETURN_FALSE;
} }
hash_auto_ptr ss_conn_options_ht; hash_auto_ptr ss_conn_options_ht;
hash_auto_ptr stmts; hash_auto_ptr stmts;
ss_sqlsrv_conn* conn = NULL; ss_sqlsrv_conn* conn = NULL;
@ -677,38 +686,38 @@ PHP_FUNCTION ( sqlsrv_connect )
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_conn_options_ht ); ALLOC_HASHTABLE( ss_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, ss_conn_options_ht, 10 /* # of buckets */, core::sqlsrv_zend_hash_init( *g_ss_henv_cp, ss_conn_options_ht, 10 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ ); ZVAL_PTR_DTOR, 0 /*persistent*/ );
// Either of g_ss_henv_cp or g_ss_henv_ncp can be used to propagate the error. // Either of g_ss_henv_cp or g_ss_henv_ncp can be used to propagate the error.
::validate_conn_options( *g_ss_henv_cp, options_z, &uid, &pwd, ss_conn_options_ht ); ::validate_conn_options( *g_ss_henv_cp, options_z, &uid, &pwd, ss_conn_options_ht );
// call the core connect function // call the core connect function
conn = static_cast<ss_sqlsrv_conn*>( core_sqlsrv_connect( *g_ss_henv_cp, *g_ss_henv_ncp, &core::allocate_conn<ss_sqlsrv_conn>, conn = static_cast<ss_sqlsrv_conn*>( core_sqlsrv_connect( *g_ss_henv_cp, *g_ss_henv_ncp, &core::allocate_conn<ss_sqlsrv_conn>,
server, uid, pwd, ss_conn_options_ht, ss_error_handler, server, uid, pwd, ss_conn_options_ht, ss_error_handler,
SS_CONN_OPTS, NULL, "sqlsrv_connect" )); SS_CONN_OPTS, NULL, "sqlsrv_connect" ));
SQLSRV_ASSERT( conn != NULL, "sqlsrv_connect: Invalid connection returned. Exception should have been thrown." ); SQLSRV_ASSERT( conn != NULL, "sqlsrv_connect: Invalid connection returned. Exception should have been thrown." );
// create a bunch of statements // create a bunch of statements
ALLOC_HASHTABLE( stmts ); ALLOC_HASHTABLE( stmts );
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, stmts, 5, NULL /* dtor */, 0 /* persistent */ ); core::sqlsrv_zend_hash_init( *g_ss_henv_cp, stmts, 5, NULL /* dtor */, 0 /* persistent */ );
// register the connection with the PHP runtime // register the connection with the PHP runtime
ss::zend_register_resource(conn_z, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name); ss::zend_register_resource(conn_z, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name);
conn->stmts = stmts; conn->stmts = stmts;
stmts.transferred(); stmts.transferred();
RETURN_RES( Z_RES(conn_z) ); RETURN_RES( Z_RES(conn_z) );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
if( conn != NULL ) { if( conn != NULL ) {
conn->invalidate(); conn->invalidate();
} }
@ -732,7 +741,7 @@ PHP_FUNCTION ( sqlsrv_connect )
// queries are automatically committed upon success unless they have been // queries are automatically committed upon success unless they have been
// designated as part of an explicit transaction by using // designated as part of an explicit transaction by using
// sqlsrv_begin_transaction. // sqlsrv_begin_transaction.
// //
// If sqlsrv_begin_transaction is called after a transaction has already been // If sqlsrv_begin_transaction is called after a transaction has already been
// initiated on the connection but not completed by calling either sqlsrv_commit // initiated on the connection but not completed by calling either sqlsrv_commit
// or sqlsrv_rollback, the call returns false and an Already in Transaction // or sqlsrv_rollback, the call returns false and an Already in Transaction
@ -744,11 +753,11 @@ PHP_FUNCTION ( sqlsrv_connect )
// Return Value // Return Value
// A Boolean value: true if the transaction was successfully begun. Otherwise, false. // A Boolean value: true if the transaction was successfully begun. Otherwise, false.
PHP_FUNCTION( sqlsrv_begin_transaction ) PHP_FUNCTION( sqlsrv_begin_transaction )
{ {
LOG_FUNCTION( "sqlsrv_begin_transaction" ); LOG_FUNCTION( "sqlsrv_begin_transaction" );
ss_sqlsrv_conn* conn = NULL; ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 ); PROCESS_PARAMS( conn, "r", _FN_, 0 );
@ -766,7 +775,7 @@ PHP_FUNCTION( sqlsrv_begin_transaction )
catch( core::CoreException& ) { catch( core::CoreException& ) {
RETURN_FALSE; RETURN_FALSE;
} }
catch( ... ) { catch( ... ) {
DIE("sqlsrv_begin_transaction: Unknown exception caught."); DIE("sqlsrv_begin_transaction: Unknown exception caught.");
@ -793,33 +802,33 @@ PHP_FUNCTION( sqlsrv_begin_transaction )
PHP_FUNCTION( sqlsrv_close ) PHP_FUNCTION( sqlsrv_close )
{ {
LOG_FUNCTION( "sqlsrv_close" ); LOG_FUNCTION( "sqlsrv_close" );
zval* conn_r = NULL; zval* conn_r = NULL;
ss_sqlsrv_conn* conn = NULL; ss_sqlsrv_conn* conn = NULL;
sqlsrv_context_auto_ptr error_ctx; sqlsrv_context_auto_ptr error_ctx;
reset_errors(); reset_errors();
try { try {
// dummy context to pass to the error handler // dummy context to pass to the error handler
error_ctx = new (sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL ); error_ctx = new (sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_); error_ctx->set_func(_FN_);
if( zend_parse_parameters(ZEND_NUM_ARGS(), "r", &conn_r) == FAILURE ) { if( zend_parse_parameters(ZEND_NUM_ARGS(), "r", &conn_r) == FAILURE ) {
// Check if it was a zval // Check if it was a zval
int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "z", &conn_r ); int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "z", &conn_r );
CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ , NULL) {
throw ss::SSException(); throw ss::SSException();
} }
// if sqlsrv_close was called on a non-existent connection then we just return success. // if sqlsrv_close was called on a non-existent connection then we just return success.
if( Z_TYPE_P( conn_r ) == IS_NULL ) { if( Z_TYPE_P( conn_r ) == IS_NULL ) {
RETURN_TRUE; RETURN_TRUE;
} }
else { else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
} }
SQLSRV_ASSERT( conn_r != NULL, "sqlsrv_close: conn_r was null" ); SQLSRV_ASSERT( conn_r != NULL, "sqlsrv_close: conn_r was null" );
@ -829,16 +838,16 @@ PHP_FUNCTION( sqlsrv_close )
if ( Z_RES_TYPE_P( conn_r ) == RSRC_INVALID_TYPE) { if ( Z_RES_TYPE_P( conn_r ) == RSRC_INVALID_TYPE) {
RETURN_TRUE; RETURN_TRUE;
} }
CHECK_CUSTOM_ERROR(( conn == NULL ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( conn == NULL ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
conn->set_func(_FN_); conn->set_func(_FN_);
// cause any variables still holding a reference to this to be invalid so they cause // cause any variables still holding a reference to this to be invalid so they cause
// an error when passed to a sqlsrv function. There's nothing we can do if the // an error when passed to a sqlsrv function. There's nothing we can do if the
// removal fails, so we just log it and move on. // removal fails, so we just log it and move on.
#if PHP_VERSION_ID < 80000 #if PHP_VERSION_ID < 80000
if (zend_list_close(Z_RES_P(conn_r)) == FAILURE) { if (zend_list_close(Z_RES_P(conn_r)) == FAILURE) {
@ -857,7 +866,7 @@ PHP_FUNCTION( sqlsrv_close )
RETURN_TRUE; RETURN_TRUE;
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
RETURN_FALSE; RETURN_FALSE;
} }
catch( ... ) { catch( ... ) {
@ -868,7 +877,7 @@ PHP_FUNCTION( sqlsrv_close )
void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc ) void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc )
{ {
// Without sqlsrv_close(), this function is invoked by php during the final clean up stage. // Without sqlsrv_close(), this function is invoked by php during the final clean up stage.
// To prevent memory/resource leaks, no more logging at this point. // To prevent memory/resource leaks, no more logging at this point.
//LOG_FUNCTION( "sqlsrv_conn_dtor" ); //LOG_FUNCTION( "sqlsrv_conn_dtor" );
@ -883,17 +892,17 @@ void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc )
// close the connection itself. // close the connection itself.
core_sqlsrv_close( conn ); core_sqlsrv_close( conn );
rsrc->ptr = NULL; rsrc->ptr = NULL;
} }
// sqlsrv_commit( resource $conn ) // sqlsrv_commit( resource $conn )
// //
// Commits the current transaction on the specified connection and returns the // Commits the current transaction on the specified connection and returns the
// connection to the auto-commit mode. The current transaction includes all // connection to the auto-commit mode. The current transaction includes all
// statements on the specified connection that were executed after the call to // statements on the specified connection that were executed after the call to
// sqlsrv_begin_transaction and before any calls to sqlsrv_rollback or // sqlsrv_begin_transaction and before any calls to sqlsrv_rollback or
// sqlsrv_commit. // sqlsrv_commit.
// The SQLSRV driver is in auto-commit mode by // The SQLSRV driver is in auto-commit mode by
// default. This means that all queries are automatically committed upon success // default. This means that all queries are automatically committed upon success
@ -902,7 +911,7 @@ void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc )
// not in an active transaction and that was initiated with // not in an active transaction and that was initiated with
// sqlsrv_begin_transaction, the call returns false and a Not in Transaction // sqlsrv_begin_transaction, the call returns false and a Not in Transaction
// error is added to the error collection. // error is added to the error collection.
// //
// Parameters // Parameters
// $conn: The connection on which the transaction is active. // $conn: The connection on which the transaction is active.
// //
@ -921,7 +930,7 @@ PHP_FUNCTION( sqlsrv_commit )
CHECK_CUSTOM_ERROR(( conn->in_transaction == false ), *conn, SS_SQLSRV_ERROR_NOT_IN_TXN ) { CHECK_CUSTOM_ERROR(( conn->in_transaction == false ), *conn, SS_SQLSRV_ERROR_NOT_IN_TXN ) {
RETURN_FALSE; RETURN_FALSE;
} }
try { try {
conn->in_transaction = false; conn->in_transaction = false;
@ -945,7 +954,7 @@ PHP_FUNCTION( sqlsrv_commit )
// statements on the specified connection that were executed after the call to // statements on the specified connection that were executed after the call to
// sqlsrv_begin_transaction and before any calls to sqlsrv_rollback or // sqlsrv_begin_transaction and before any calls to sqlsrv_rollback or
// sqlsrv_commit. // sqlsrv_commit.
// //
// The SQLSRV driver is in auto-commit mode by default. This // The SQLSRV driver is in auto-commit mode by default. This
// means that all queries are automatically committed upon success unless they // means that all queries are automatically committed upon success unless they
// have been designated as part of an explicit transaction by using // have been designated as part of an explicit transaction by using
@ -955,7 +964,7 @@ PHP_FUNCTION( sqlsrv_commit )
// transaction that was initiated with sqlsrv_begin_transaction, the call // transaction that was initiated with sqlsrv_begin_transaction, the call
// returns false and a Not in Transaction error is added to the error // returns false and a Not in Transaction error is added to the error
// collection. // collection.
// //
// Parameters // Parameters
// $conn: The connection on which the transaction is active. // $conn: The connection on which the transaction is active.
// //
@ -970,14 +979,14 @@ PHP_FUNCTION( sqlsrv_rollback )
ss_sqlsrv_conn* conn = NULL; ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 ); PROCESS_PARAMS( conn, "r", _FN_, 0 );
// Return false if not in transaction // Return false if not in transaction
CHECK_CUSTOM_ERROR(( conn->in_transaction == false ), *conn, SS_SQLSRV_ERROR_NOT_IN_TXN ) { CHECK_CUSTOM_ERROR(( conn->in_transaction == false ), *conn, SS_SQLSRV_ERROR_NOT_IN_TXN ) {
RETURN_FALSE; RETURN_FALSE;
} }
try { try {
conn->in_transaction = false; conn->in_transaction = false;
core_sqlsrv_rollback( conn ); core_sqlsrv_rollback( conn );
RETURN_TRUE; RETURN_TRUE;
@ -1002,7 +1011,7 @@ PHP_FUNCTION( sqlsrv_client_info )
LOG_FUNCTION( "sqlsrv_client_info" ); LOG_FUNCTION( "sqlsrv_client_info" );
ss_sqlsrv_conn* conn = NULL; ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 ); PROCESS_PARAMS( conn, "r", _FN_, 0 );
try { try {
core_sqlsrv_get_client_info(conn, return_value); core_sqlsrv_get_client_info(conn, return_value);
@ -1019,14 +1028,14 @@ PHP_FUNCTION( sqlsrv_client_info )
} }
// sqlsrv_server_info( resource $conn ) // sqlsrv_server_info( resource $conn )
// //
// Returns information about the server. // Returns information about the server.
// //
// Parameters // Parameters
// $conn: The connection resource by which the client and server are connected. // $conn: The connection resource by which the client and server are connected.
// //
// Return Value // Return Value
// An associative array with the following keys: // An associative array with the following keys:
// CurrentDatabase // CurrentDatabase
// The database currently being targeted. // The database currently being targeted.
// SQLServerVersion // SQLServerVersion
@ -1051,7 +1060,7 @@ PHP_FUNCTION( sqlsrv_server_info )
// sqlsrv_prepare( resource $conn, string $tsql [, array $params [, array $options]]) // sqlsrv_prepare( resource $conn, string $tsql [, array $params [, array $options]])
// //
// Creates a statement resource associated with the specified connection. A statement // Creates a statement resource associated with the specified connection. A statement
// resource returned by sqlsrv_prepare may be executed multiple times by sqlsrv_execute. // resource returned by sqlsrv_prepare may be executed multiple times by sqlsrv_execute.
// In between each execution, the values may be updated by changing the value of the // In between each execution, the values may be updated by changing the value of the
@ -1097,7 +1106,7 @@ PHP_FUNCTION( sqlsrv_prepare )
zval stmt_z; zval stmt_z;
ZVAL_UNDEF(&stmt_z); ZVAL_UNDEF(&stmt_z);
PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z ); PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z, NULL );
try { try {
@ -1105,19 +1114,19 @@ PHP_FUNCTION( sqlsrv_prepare )
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht ); ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */, core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ ); ZVAL_PTR_DTOR, 0 /*persistent*/ );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht ); validate_stmt_options( *conn, options_z, ss_stmt_options_ht );
} }
if( params_z && Z_TYPE_P( params_z ) != IS_ARRAY ) { if( params_z && Z_TYPE_P( params_z ) != IS_ARRAY ) {
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
if( options_z && Z_TYPE_P( options_z ) != IS_ARRAY ) { if( options_z && Z_TYPE_P( options_z ) != IS_ARRAY ) {
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
if( sql == NULL ) { if( sql == NULL ) {
@ -1125,12 +1134,12 @@ PHP_FUNCTION( sqlsrv_prepare )
DIE( "sqlsrv_prepare: sql string was null." ); DIE( "sqlsrv_prepare: sql string was null." );
} }
stmt = static_cast<ss_sqlsrv_stmt*>( core_sqlsrv_create_stmt( conn, core::allocate_stmt<ss_sqlsrv_stmt>, stmt = static_cast<ss_sqlsrv_stmt*>( core_sqlsrv_create_stmt( conn, core::allocate_stmt<ss_sqlsrv_stmt>,
ss_stmt_options_ht, SS_STMT_OPTS, ss_stmt_options_ht, SS_STMT_OPTS,
ss_error_handler, NULL ) ); ss_error_handler, NULL ) );
core_sqlsrv_prepare( stmt, sql, sql_len ); core_sqlsrv_prepare( stmt, sql, sql_len );
if (params_z) { if (params_z) {
stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval)); stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_COPY(stmt->params_z, params_z); ZVAL_COPY(stmt->params_z, params_z);
@ -1138,27 +1147,27 @@ PHP_FUNCTION( sqlsrv_prepare )
stmt->prepared = true; stmt->prepared = true;
// register the statement with the PHP runtime // register the statement with the PHP runtime
ss::zend_register_resource( stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name ); ss::zend_register_resource( stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name );
// store the resource id with the connection so the connection // store the resource id with the connection so the connection
// can release this statement when it closes. // can release this statement when it closes.
zend_long next_index = zend_hash_next_free_element( conn->stmts ); zend_long next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z); core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z);
stmt->conn_index = next_index; stmt->conn_index = next_index;
// the statement is now registered with EG( regular_list ) // the statement is now registered with EG( regular_list )
stmt.transferred(); stmt.transferred();
RETURN_RES(Z_RES(stmt_z)); RETURN_RES(Z_RES(stmt_z));
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
if( stmt ) { if( stmt ) {
stmt->conn = NULL; stmt->conn = NULL;
stmt->~ss_sqlsrv_stmt(); stmt->~ss_sqlsrv_stmt();
} }
@ -1176,7 +1185,7 @@ PHP_FUNCTION( sqlsrv_prepare )
} }
// sqlsrv_query( resource $conn, string $tsql [, array $params [, array $options]]) // sqlsrv_query( resource $conn, string $tsql [, array $params [, array $options]])
// //
// Creates a statement resource associated with the specified connection. The statement // Creates a statement resource associated with the specified connection. The statement
// is immediately executed and may not be executed again using sqlsrv_execute. // is immediately executed and may not be executed again using sqlsrv_execute.
// //
@ -1215,11 +1224,11 @@ PHP_FUNCTION( sqlsrv_query )
hash_auto_ptr ss_stmt_options_ht; hash_auto_ptr ss_stmt_options_ht;
size_t sql_len = 0; size_t sql_len = 0;
zval* options_z = NULL; zval* options_z = NULL;
zval* params_z = NULL; zval* params_z = NULL;
zval stmt_z; zval stmt_z;
ZVAL_UNDEF(&stmt_z); ZVAL_UNDEF(&stmt_z);
PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z ); PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z, NULL );
try { try {
@ -1228,18 +1237,18 @@ PHP_FUNCTION( sqlsrv_query )
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht ); ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */, ZVAL_PTR_DTOR, core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */, ZVAL_PTR_DTOR,
0 /*persistent*/ ); 0 /*persistent*/ );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht ); validate_stmt_options( *conn, options_z, ss_stmt_options_ht );
} }
if( params_z && Z_TYPE_P( params_z ) != IS_ARRAY ) { if( params_z && Z_TYPE_P( params_z ) != IS_ARRAY ) {
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
if( options_z && Z_TYPE_P( options_z ) != IS_ARRAY ) { if( options_z && Z_TYPE_P( options_z ) != IS_ARRAY ) {
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
if( sql == NULL ) { if( sql == NULL ) {
@ -1247,8 +1256,8 @@ PHP_FUNCTION( sqlsrv_query )
DIE( "sqlsrv_query: sql string was null." ); DIE( "sqlsrv_query: sql string was null." );
} }
stmt = static_cast<ss_sqlsrv_stmt*>( core_sqlsrv_create_stmt( conn, core::allocate_stmt<ss_sqlsrv_stmt>, stmt = static_cast<ss_sqlsrv_stmt*>( core_sqlsrv_create_stmt( conn, core::allocate_stmt<ss_sqlsrv_stmt>,
ss_stmt_options_ht, SS_STMT_OPTS, ss_stmt_options_ht, SS_STMT_OPTS,
ss_error_handler, NULL ) ); ss_error_handler, NULL ) );
if( params_z ) { if( params_z ) {
@ -1262,13 +1271,13 @@ PHP_FUNCTION( sqlsrv_query )
// execute the statement // execute the statement
core_sqlsrv_execute( stmt, sql, static_cast<int>( sql_len ) ); core_sqlsrv_execute( stmt, sql, static_cast<int>( sql_len ) );
// register the statement with the PHP runtime // register the statement with the PHP runtime
ss::zend_register_resource(stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name); ss::zend_register_resource(stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name);
// store the resource id with the connection so the connection // store the resource id with the connection so the connection
// can release this statement when it closes. // can release this statement when it closes.
zend_ulong next_index = zend_hash_next_free_element( conn->stmts ); zend_ulong next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z); core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z);
stmt->conn_index = next_index; stmt->conn_index = next_index;
stmt.transferred(); stmt.transferred();
@ -1311,7 +1320,7 @@ void free_stmt_resource( _Inout_ zval* stmt_z )
// internal connection functions // internal connection functions
namespace { namespace {
// must close all statement handles opened by this connection before closing the connection // must close all statement handles opened by this connection before closing the connection
// no errors are returned, since close should always succeed // no errors are returned, since close should always succeed
@ -1322,8 +1331,8 @@ void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn )
SQLSRV_ASSERT(( conn->handle() != NULL ), "sqlsrv_conn_close_stmts: Connection handle is NULL. Trying to destroy an " SQLSRV_ASSERT(( conn->handle() != NULL ), "sqlsrv_conn_close_stmts: Connection handle is NULL. Trying to destroy an "
"already destroyed connection."); "already destroyed connection.");
SQLSRV_ASSERT(( conn->stmts ), "sqlsrv_conn_close_stmts: Connection doesn't contain a statement array." ); SQLSRV_ASSERT(( conn->stmts ), "sqlsrv_conn_close_stmts: Connection doesn't contain a statement array." );
// loop through the stmts hash table and destroy each stmt resource so we can close the // loop through the stmts hash table and destroy each stmt resource so we can close the
// ODBC connection // ODBC connection
zval* rsrc_ptr = NULL; zval* rsrc_ptr = NULL;
@ -1368,16 +1377,16 @@ void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn )
conn->stmts = NULL; conn->stmts = NULL;
} }
int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ zval const* value_z ) int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ zval const* value_z )
{ {
for( int i=0; SS_CONN_OPTS[i].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++i ) for( int i=0; SS_CONN_OPTS[i].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++i )
{ {
if( key_len == SS_CONN_OPTS[i].sqlsrv_len && !stricmp( ZSTR_VAL( key ), SS_CONN_OPTS[i].sqlsrv_name )) { if( key_len == SS_CONN_OPTS[i].sqlsrv_len && !stricmp( ZSTR_VAL( key ), SS_CONN_OPTS[i].sqlsrv_name )) {
switch( SS_CONN_OPTS[i].value_type ) { switch( SS_CONN_OPTS[i].value_type ) {
case CONN_ATTR_BOOL: case CONN_ATTR_BOOL:
// bool attributes can be either strings to be appended to the connection string // bool attributes can be either strings to be appended to the connection string
// as yes or no or integral connection attributes. This will have to be reworked // as yes or no or integral connection attributes. This will have to be reworked
// if we ever introduce a boolean connection option that maps to a string connection // if we ever introduce a boolean connection option that maps to a string connection
@ -1388,7 +1397,7 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
case CONN_ATTR_INT: case CONN_ATTR_INT:
{ {
CHECK_CUSTOM_ERROR( (Z_TYPE_P( value_z ) != IS_LONG ), ctx, SQLSRV_ERROR_INVALID_OPTION_TYPE_INT, CHECK_CUSTOM_ERROR( (Z_TYPE_P( value_z ) != IS_LONG ), ctx, SQLSRV_ERROR_INVALID_OPTION_TYPE_INT,
SS_CONN_OPTS[i].sqlsrv_name ) SS_CONN_OPTS[i].sqlsrv_name, NULL)
{ {
throw ss::SSException(); throw ss::SSException();
} }
@ -1396,8 +1405,8 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
} }
case CONN_ATTR_STRING: case CONN_ATTR_STRING:
{ {
CHECK_CUSTOM_ERROR( Z_TYPE_P( value_z ) != IS_STRING, ctx, SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING, CHECK_CUSTOM_ERROR( Z_TYPE_P( value_z ) != IS_STRING, ctx, SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING,
SS_CONN_OPTS[i].sqlsrv_name ) { SS_CONN_OPTS[i].sqlsrv_name, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1406,7 +1415,7 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
size_t value_len = Z_STRLEN_P( value_z ); size_t value_len = Z_STRLEN_P( value_z );
bool escaped = core_is_conn_opt_value_escaped( value, value_len ); bool escaped = core_is_conn_opt_value_escaped( value, value_len );
CHECK_CUSTOM_ERROR( !escaped, ctx, SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED, SS_CONN_OPTS[i].sqlsrv_name ) { CHECK_CUSTOM_ERROR( !escaped, ctx, SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED, SS_CONN_OPTS[i].sqlsrv_name, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1435,34 +1444,34 @@ int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len )
return SQLSRV_STMT_OPTION_INVALID; return SQLSRV_STMT_OPTION_INVALID;
} }
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len,
_Inout_ HashTable* options_ht, _Inout_ zval* data ) _Inout_ HashTable* options_ht, _Inout_ zval* data )
{ {
int option_key = ::get_stmt_option_key( key, key_len ); int option_key = ::get_stmt_option_key( key, key_len );
CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, ZSTR_VAL( key ) ) { CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, ZSTR_VAL( key ), NULL) {
throw ss::SSException(); throw ss::SSException();
} }
Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too. Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data ); core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data );
} }
void add_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, void add_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len,
_Inout_ HashTable* options_ht, _Inout_ zval* data ) _Inout_ HashTable* options_ht, _Inout_ zval* data )
{ {
int option_key = ::get_conn_option_key( ctx, key, key_len, data ); int option_key = ::get_conn_option_key( ctx, key, key_len, data );
CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, ZSTR_VAL( key ) ) { CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, ZSTR_VAL( key ), NULL) {
throw ss::SSException(); throw ss::SSException();
} }
Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too. Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data ); core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data );
} }
// Iterates through the list of statement options provided by the user and validates them // Iterates through the list of statement options provided by the user and validates them
// against the list of supported statement options by this driver. After validation // against the list of supported statement options by this driver. After validation
// creates a Hashtable of statement options to be sent to the core layer for processing. // creates a Hashtable of statement options to be sent to the core layer for processing.
@ -1470,7 +1479,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
{ {
try { try {
if( stmt_options ) { if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options ); HashTable* options_ht = Z_ARRVAL_P( stmt_options );
zend_ulong int_key = -1; zend_ulong int_key = -1;
zend_string *key = NULL; zend_string *key = NULL;
@ -1482,7 +1491,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
if (type != HASH_KEY_IS_STRING) { if (type != HASH_KEY_IS_STRING) {
CHECK_CUSTOM_ERROR(true, ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, std::to_string( int_key ).c_str() ) { CHECK_CUSTOM_ERROR(true, ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, std::to_string( int_key ).c_str(), NULL) {
throw core::CoreException(); throw core::CoreException();
} }
} }
@ -1502,7 +1511,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
} }
} }
// Iterates through the list of connection options provided by the user and validates them // Iterates through the list of connection options provided by the user and validates them
// against the predefined list of supported connection options by this driver. After validation // against the predefined list of supported connection options by this driver. After validation
// creates a Hashtable of connection options to be sent to the core layer for processing. // creates a Hashtable of connection options to be sent to the core layer for processing.
@ -1520,7 +1529,7 @@ void validate_conn_options( _Inout_ sqlsrv_context& ctx, _In_ zval* user_options
int type = HASH_KEY_NON_EXISTENT; int type = HASH_KEY_NON_EXISTENT;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR(( Z_TYPE_P( data ) == IS_NULL || Z_TYPE_P( data ) == IS_UNDEF ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, key) { CHECK_CUSTOM_ERROR(( Z_TYPE_P( data ) == IS_NULL || Z_TYPE_P( data ) == IS_UNDEF ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, key, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1548,7 +1557,7 @@ void validate_conn_options( _Inout_ sqlsrv_context& ctx, _In_ zval* user_options
DIE( "validate_conn_options: key was null." ); DIE( "validate_conn_options: key was null." );
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;

View file

@ -2,7 +2,7 @@
// File: init.cpp // File: init.cpp
// Contents: initialization routines for the extension // Contents: initialization routines for the extension
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -8,7 +8,7 @@
// //
// Comments: Also contains "internal" declarations shared across source files. // Comments: Also contains "internal" declarations shared across source files.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -6,19 +6,19 @@
// //
// Contents: Internal declarations for the extension // Contents: Internal declarations for the extension
// //
// Comments: Also contains "internal" declarations shared across source files. // Comments: Also contains "internal" declarations shared across source files.
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -44,9 +44,9 @@
PHP_INI_BEGIN() PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN( INI_PREFIX INI_WARNINGS_RETURN_AS_ERRORS , "1", PHP_INI_ALL, OnUpdateBool, warnings_return_as_errors, STD_PHP_INI_BOOLEAN( INI_PREFIX INI_WARNINGS_RETURN_AS_ERRORS , "1", PHP_INI_ALL, OnUpdateBool, warnings_return_as_errors,
zend_sqlsrv_globals, sqlsrv_globals ) zend_sqlsrv_globals, sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_LOG_SEVERITY, "0", PHP_INI_ALL, OnUpdateLong, log_severity, zend_sqlsrv_globals, STD_PHP_INI_ENTRY( INI_PREFIX INI_LOG_SEVERITY, "0", PHP_INI_ALL, OnUpdateLong, log_severity, zend_sqlsrv_globals,
sqlsrv_globals ) sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_LOG_SUBSYSTEMS, "0", PHP_INI_ALL, OnUpdateLong, log_subsystems, zend_sqlsrv_globals, STD_PHP_INI_ENTRY( INI_PREFIX INI_LOG_SUBSYSTEMS, "0", PHP_INI_ALL, OnUpdateLong, log_subsystems, zend_sqlsrv_globals,
sqlsrv_globals ) sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_BUFFERED_QUERY_LIMIT, INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong, buffered_query_limit, STD_PHP_INI_ENTRY( INI_PREFIX INI_BUFFERED_QUERY_LIMIT, INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong, buffered_query_limit,
zend_sqlsrv_globals, sqlsrv_globals ) zend_sqlsrv_globals, sqlsrv_globals )
@ -85,13 +85,13 @@ struct ss_sqlsrv_conn : sqlsrv_conn
bool format_decimals; // flag set to turn on formatting for values of decimal / numeric types bool format_decimals; // flag set to turn on formatting for values of decimal / numeric types
short decimal_places; // number of decimal digits to show in a result set unless format_numbers is false short decimal_places; // number of decimal digits to show in a result set unless format_numbers is false
bool in_transaction; // flag set when inside a transaction and used for checking validity of tran API calls bool in_transaction; // flag set when inside a transaction and used for checking validity of tran API calls
// static variables used in process_params // static variables used in process_params
static const char* resource_name; static const char* resource_name;
static int descriptor; static int descriptor;
// initialize with default values // initialize with default values
ss_sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv ) : ss_sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv ) :
sqlsrv_conn( h, e, drv, SQLSRV_ENCODING_SYSTEM ), sqlsrv_conn( h, e, drv, SQLSRV_ENCODING_SYSTEM ),
stmts( NULL ), stmts( NULL ),
date_as_string( false ), date_as_string( false ),
@ -125,7 +125,7 @@ struct ss_sqlsrv_stmt : public sqlsrv_stmt {
virtual ~ss_sqlsrv_stmt( void ); virtual ~ss_sqlsrv_stmt( void );
void new_result_set( void ); void new_result_set( void );
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants // driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
sqlsrv_phptype sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_to_stream ); sqlsrv_phptype sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_to_stream );
@ -164,7 +164,7 @@ void __cdecl sqlsrv_stmt_dtor( _Inout_ zend_resource *rsrc );
// "internal" statement functions shared by functions in conn.cpp and stmt.cpp // "internal" statement functions shared by functions in conn.cpp and stmt.cpp
void bind_params( _Inout_ ss_sqlsrv_stmt* stmt ); void bind_params( _Inout_ ss_sqlsrv_stmt* stmt );
bool sqlsrv_stmt_common_execute( sqlsrv_stmt* s, const SQLCHAR* sql_string, int sql_len, bool direct, const char* function bool sqlsrv_stmt_common_execute( sqlsrv_stmt* s, const SQLCHAR* sql_string, int sql_len, bool direct, const char* function
); );
void free_odbc_resources( ss_sqlsrv_stmt* stmt ); void free_odbc_resources( ss_sqlsrv_stmt* stmt );
void free_stmt_resource( _Inout_ zval* stmt_z ); void free_stmt_resource( _Inout_ zval* stmt_z );
@ -183,12 +183,12 @@ struct ss_error {
// List of all driver specific error codes. // List of all driver specific error codes.
enum SS_ERROR_CODES { enum SS_ERROR_CODES {
SS_SQLSRV_ERROR_ALREADY_IN_TXN = SQLSRV_ERROR_DRIVER_SPECIFIC, SS_SQLSRV_ERROR_ALREADY_IN_TXN = SQLSRV_ERROR_DRIVER_SPECIFIC,
SS_SQLSRV_ERROR_NOT_IN_TXN, SS_SQLSRV_ERROR_NOT_IN_TXN,
SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER,
SS_SQLSRV_ERROR_REGISTER_RESOURCE, SS_SQLSRV_ERROR_REGISTER_RESOURCE,
SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY, SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY,
SS_SQLSRV_ERROR_STATEMENT_NOT_PREPARED, SS_SQLSRV_ERROR_STATEMENT_NOT_PREPARED,
SS_SQLSRV_ERROR_INVALID_FETCH_STYLE, SS_SQLSRV_ERROR_INVALID_FETCH_STYLE,
SS_SQLSRV_ERROR_INVALID_FETCH_TYPE, SS_SQLSRV_ERROR_INVALID_FETCH_TYPE,
@ -227,9 +227,9 @@ SQLWCHAR* utf16_string_from_mbcs_string( _In_ unsigned int php_encoding, _In_rea
_In_ unsigned int mbcs_len, _Out_ unsigned int* utf16_len, bool use_strict_conversion = false ); _In_ unsigned int mbcs_len, _Out_ unsigned int* utf16_len, bool use_strict_conversion = false );
// *** internal error macros and functions *** // *** internal error macros and functions ***
bool handle_error( sqlsrv_context const* ctx, int log_subsystem, const char* function, bool handle_error( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp, ... ); sqlsrv_error const* ssphp, ... );
void handle_warning( sqlsrv_context const* ctx, int log_subsystem, const char* function, void handle_warning( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp, ... ); sqlsrv_error const* ssphp, ... );
void __cdecl sqlsrv_error_dtor( zend_resource *rsrc ); void __cdecl sqlsrv_error_dtor( zend_resource *rsrc );
@ -307,7 +307,7 @@ public:
const char* _FN_ = function_name; \ const char* _FN_ = function_name; \
SQLSRV_G( current_subsystem ) = current_log_subsystem; \ SQLSRV_G( current_subsystem ) = current_log_subsystem; \
core_sqlsrv_register_severity_checker(ss_severity_check); \ core_sqlsrv_register_severity_checker(ss_severity_check); \
LOG(SEV_NOTICE, "%1!s!: entering", _FN_); LOG(SEV_NOTICE, "%1!s!: entering", _FN_);
// check the global variables of sqlsrv severity whether the message qualifies to be logged with the LOG macro // check the global variables of sqlsrv severity whether the message qualifies to be logged with the LOG macro
bool ss_severity_check(_In_ unsigned int severity); bool ss_severity_check(_In_ unsigned int severity);
@ -323,7 +323,7 @@ enum logging_subsystems {
//********************************************************************************************************************************* //*********************************************************************************************************************************
// Common function wrappers // Common function wrappers
// have to place this namespace before the utility functions // have to place this namespace before the utility functions
// otherwise can't compile in Linux because 'ss' not defined // otherwise can't compile in Linux because 'ss' not defined
//********************************************************************************************************************************* //*********************************************************************************************************************************
@ -342,7 +342,7 @@ namespace ss {
{ {
int zr = (NULL != (Z_RES(rsrc_result) = ::zend_register_resource(rsrc_pointer, rsrc_type)) ? SUCCESS : FAILURE); int zr = (NULL != (Z_RES(rsrc_result) = ::zend_register_resource(rsrc_pointer, rsrc_type)) ? SUCCESS : FAILURE);
CHECK_CUSTOM_ERROR(( zr == FAILURE ), reinterpret_cast<sqlsrv_context*>( rsrc_pointer ), SS_SQLSRV_ERROR_REGISTER_RESOURCE, CHECK_CUSTOM_ERROR(( zr == FAILURE ), reinterpret_cast<sqlsrv_context*>( rsrc_pointer ), SS_SQLSRV_ERROR_REGISTER_RESOURCE,
rsrc_name ) { rsrc_name, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
Z_TYPE_INFO(rsrc_result) = IS_RESOURCE_EX; Z_TYPE_INFO(rsrc_result) = IS_RESOURCE_EX;
@ -363,7 +363,7 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
zval* rsrc; zval* rsrc;
H* h = NULL; H* h = NULL;
// reset the errors from the previous API call // reset the errors from the previous API call
reset_errors(); reset_errors();
@ -384,14 +384,14 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
va_start(vaList, param_count); //set the pointer to first argument va_start(vaList, param_count); //set the pointer to first argument
for(size_t i = 0; i < param_count; ++i) { for(size_t i = 0; i < param_count; ++i) {
arr[i] = va_arg(vaList, void*); arr[i] = va_arg(vaList, void*);
} }
va_end(vaList); va_end(vaList);
int result = SUCCESS; int result = SUCCESS;
// dummy context to pass to the error handler // dummy context to pass to the error handler
sqlsrv_context error_ctx( 0, ss_error_handler, NULL ); sqlsrv_context error_ctx( 0, ss_error_handler, NULL );
error_ctx.set_func( calling_func ); error_ctx.set_func( calling_func );
@ -403,50 +403,50 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
break; break;
case 1: case 1:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0] ); result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0] );
break; break;
case 2: case 2:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0], result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0],
arr[1] ); arr[1] );
break; break;
case 3: case 3:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0], result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0],
arr[1], arr[2] ); arr[1], arr[2] );
break; break;
case 4: case 4:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0], result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0],
arr[1], arr[2], arr[3] ); arr[1], arr[2], arr[3] );
break; break;
case 5: case 5:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0], result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0],
arr[1], arr[2], arr[3], arr[4] ); arr[1], arr[2], arr[3], arr[4] );
break; break;
case 6: case 6:
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0], result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0],
arr[1], arr[2], arr[3], arr[4], arr[5] ); arr[1], arr[2], arr[3], arr[4], arr[5] );
break; break;
default: default:
{ {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func, NULL );
break; break;
} }
} }
CHECK_CUSTOM_ERROR(( result == FAILURE ), &error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func ) { CHECK_CUSTOM_ERROR(( result == FAILURE ), &error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
// get the resource registered // get the resource registered
h = static_cast<H*>( zend_fetch_resource(Z_RES_P(rsrc), H::resource_name, H::descriptor )); h = static_cast<H*>( zend_fetch_resource(Z_RES_P(rsrc), H::resource_name, H::descriptor ));
CHECK_CUSTOM_ERROR(( h == NULL ), &error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func ) { CHECK_CUSTOM_ERROR(( h == NULL ), &error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -455,11 +455,11 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
return NULL; return NULL;
} }
catch ( ... ) { catch ( ... ) {
DIE( "%1!s!: Unknown exception caught in process_params.", calling_func ); DIE( "%1!s!: Unknown exception caught in process_params.", calling_func );
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: Routines that use statement handles // Contents: Routines that use statement handles
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
@ -100,8 +100,8 @@ void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type ); void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type ); void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding ); bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding );
zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_ht, zend_ulong index, zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_ht, zend_ulong index,
_Out_ SQLSMALLINT& direction, _Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSMALLINT& direction, _Out_ SQLSRV_PHPTYPE& php_out_type,
_Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits); _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits);
@ -341,7 +341,7 @@ PHP_FUNCTION( sqlsrv_fetch )
zend_long fetch_offset = 0; // default value for parameter if one isn't supplied zend_long fetch_offset = 0; // default value for parameter if one isn't supplied
// take only the statement resource // take only the statement resource
PROCESS_PARAMS( stmt, "r|ll", _FN_, 2, &fetch_style, &fetch_offset ); PROCESS_PARAMS( stmt, "r|ll", _FN_, 2, &fetch_style, &fetch_offset, NULL );
try { try {
@ -395,7 +395,7 @@ PHP_FUNCTION( sqlsrv_fetch_array )
// retrieve the statement resource and optional fetch type (see enum SQLSRV_FETCH_TYPE), // retrieve the statement resource and optional fetch type (see enum SQLSRV_FETCH_TYPE),
// fetch style (see SQLSRV_SCROLL_* constants) and fetch offset // fetch style (see SQLSRV_SCROLL_* constants) and fetch offset
PROCESS_PARAMS( stmt, "r|lll", _FN_, 3, &fetch_type, &fetch_style, &fetch_offset ); PROCESS_PARAMS( stmt, "r|lll", _FN_, 3, &fetch_type, &fetch_style, &fetch_offset, NULL );
try { try {
@ -786,7 +786,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// fetch style (see SQLSRV_SCROLL_* constants) and fetch offset // fetch style (see SQLSRV_SCROLL_* constants) and fetch offset
// we also use z! instead of s and a so that null may be passed in as valid values for // we also use z! instead of s and a so that null may be passed in as valid values for
// the class name and ctor params // the class name and ctor params
PROCESS_PARAMS( stmt, "r|z!z!ll", _FN_, 4, &class_name_z, &ctor_params_z, &fetch_style, &fetch_offset ); PROCESS_PARAMS( stmt, "r|z!z!ll", _FN_, 4, &class_name_z, &ctor_params_z, &fetch_style, &fetch_offset, NULL );
try { try {
@ -797,7 +797,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
if( class_name_z ) { if( class_name_z ) {
CHECK_CUSTOM_ERROR(( Z_TYPE_P( class_name_z ) != IS_STRING ), stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( Z_TYPE_P( class_name_z ) != IS_STRING ), stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
class_name = Z_STRVAL( *class_name_z ); class_name = Z_STRVAL( *class_name_z );
@ -805,7 +805,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
} }
if( ctor_params_z && Z_TYPE_P( ctor_params_z ) != IS_ARRAY ) { if( ctor_params_z && Z_TYPE_P( ctor_params_z ) != IS_ARRAY ) {
THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
// fetch the data // fetch the data
@ -822,14 +822,14 @@ PHP_FUNCTION( sqlsrv_fetch_object )
zend_string* class_name_str_z = zend_string_init( class_name, class_name_len, 0 ); zend_string* class_name_str_z = zend_string_init( class_name, class_name_len, 0 );
int zr = ( NULL != ( class_entry = zend_lookup_class( class_name_str_z ))) ? SUCCESS : FAILURE; int zr = ( NULL != ( class_entry = zend_lookup_class( class_name_str_z ))) ? SUCCESS : FAILURE;
zend_string_release( class_name_str_z ); zend_string_release( class_name_str_z );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_BAD_CLASS, class_name ) { CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_BAD_CLASS, class_name, NULL ) {
throw ss::SSException(); throw ss::SSException();
} }
// create an instance of the object with its default properties // create an instance of the object with its default properties
// we pass NULL for the properties so that the object will be populated by its default properties // we pass NULL for the properties so that the object will be populated by its default properties
zr = object_and_properties_init( &retval_z, class_entry, NULL /*properties*/ ); zr = object_and_properties_init( &retval_z, class_entry, NULL /*properties*/ );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) { CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name, NULL ) {
throw ss::SSException(); throw ss::SSException();
} }
@ -872,7 +872,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
zval* value_z = NULL; zval* value_z = NULL;
ZEND_HASH_FOREACH_VAL( ctor_params_ht, value_z ) { ZEND_HASH_FOREACH_VAL( ctor_params_ht, value_z ) {
zr = ( value_z ) ? SUCCESS : FAILURE; zr = ( value_z ) ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) { CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name, NULL ) {
throw ss::SSException(); throw ss::SSException();
} }
ZVAL_COPY_VALUE(&params_m[i], value_z); ZVAL_COPY_VALUE(&params_m[i], value_z);
@ -906,7 +906,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
fcic.object = Z_OBJ_P( &retval_z ); fcic.object = Z_OBJ_P( &retval_z );
zr = zend_call_function( &fci, &fcic ); zr = zend_call_function( &fci, &fcic );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) { CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name, NULL ) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1083,7 +1083,7 @@ PHP_FUNCTION( sqlsrv_get_field )
ZVAL_UNDEF(&retval_z); ZVAL_UNDEF(&retval_z);
// get the statement, the field index and the optional type // get the statement, the field index and the optional type
PROCESS_PARAMS( stmt, "rl|l", _FN_, 2, &field_index, &sqlsrv_php_type ); PROCESS_PARAMS( stmt, "rl|l", _FN_, 2, &field_index, &sqlsrv_php_type, NULL );
try { try {
@ -1091,7 +1091,7 @@ PHP_FUNCTION( sqlsrv_get_field )
SQLSMALLINT num_cols = get_resultset_meta_data(stmt); SQLSMALLINT num_cols = get_resultset_meta_data(stmt);
if( field_index < 0 || field_index >= num_cols ) { if( field_index < 0 || field_index >= num_cols ) {
THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
core_sqlsrv_get_field( stmt, static_cast<SQLUSMALLINT>( field_index ), sqlsrv_php_type, false, field_value, &field_len, false/*cache_field*/, core_sqlsrv_get_field( stmt, static_cast<SQLUSMALLINT>( field_index ), sqlsrv_php_type, false, field_value, &field_len, false/*cache_field*/,
@ -1227,7 +1227,7 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt )
// Simply get the first variable and use the defaults // Simply get the first variable and use the defaults
value_z = zend_hash_index_find(param_ht, 0); value_z = zend_hash_index_find(param_ht, 0);
if (value_z == NULL) { if (value_z == NULL) {
THROW_SS_ERROR(stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1); THROW_SS_ERROR(stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1, NULL);
} }
} }
} catch (core::CoreException&) { } catch (core::CoreException&) {
@ -1300,7 +1300,7 @@ PHP_FUNCTION( sqlsrv_cancel )
close_active_stream( stmt ); close_active_stream( stmt );
SQLRETURN r = SQLCancel( stmt->handle() ); SQLRETURN r = SQLCancel( stmt->handle() );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { CHECK_SQL_ERROR_OR_WARNING( r, stmt, NULL ) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1375,7 +1375,7 @@ PHP_FUNCTION( sqlsrv_free_stmt )
// Check if it was a zval // Check if it was a zval
int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "z", &stmt_r ); int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "z", &stmt_r );
CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1386,7 +1386,7 @@ PHP_FUNCTION( sqlsrv_free_stmt )
} }
else { else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
} }
@ -1402,7 +1402,7 @@ PHP_FUNCTION( sqlsrv_free_stmt )
if( stmt == NULL ) { if( stmt == NULL ) {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
// delete the resource from Zend's master list, which will trigger the statement's destructor // delete the resource from Zend's master list, which will trigger the statement's destructor
@ -1774,7 +1774,7 @@ void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt )
if( SQL_SUCCEEDED( r )) { if( SQL_SUCCEEDED( r )) {
stmt->has_rows = true; stmt->has_rows = true;
CHECK_SQL_WARNING( r, stmt ); CHECK_SQL_WARNING( r, stmt, NULL );
// restore the cursor to its original position. // restore the cursor to its original position.
r = stmt->current_results->fetch( SQL_FETCH_ABSOLUTE, 0 ); r = stmt->current_results->fetch( SQL_FETCH_ABSOLUTE, 0 );
SQLSRV_ASSERT(( r == SQL_NO_DATA ), "core_sqlsrv_has_rows: Should have scrolled the cursor to the beginning " SQLSRV_ASSERT(( r == SQL_NO_DATA ), "core_sqlsrv_has_rows: Should have scrolled the cursor to the beginning "
@ -1791,7 +1791,7 @@ void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt )
if( SQL_SUCCEEDED( r )) { if( SQL_SUCCEEDED( r )) {
stmt->has_rows = true; stmt->has_rows = true;
CHECK_SQL_WARNING( r, stmt ); CHECK_SQL_WARNING( r, stmt, NULL );
return; return;
} }
} }
@ -1841,7 +1841,7 @@ void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_typ
SQLSRV_PHPTYPE sqlsrv_php_type_out = SQLSRV_PHPTYPE_INVALID; SQLSRV_PHPTYPE sqlsrv_php_type_out = SQLSRV_PHPTYPE_INVALID;
// make sure that the fetch type is legal // make sure that the fetch type is legal
CHECK_CUSTOM_ERROR((fetch_type < MIN_SQLSRV_FETCH || fetch_type > MAX_SQLSRV_FETCH), stmt, SS_SQLSRV_ERROR_INVALID_FETCH_TYPE, stmt->func()) { CHECK_CUSTOM_ERROR((fetch_type < MIN_SQLSRV_FETCH || fetch_type > MAX_SQLSRV_FETCH), stmt, SS_SQLSRV_ERROR_INVALID_FETCH_TYPE, stmt->func(), NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1893,7 +1893,7 @@ void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_typ
if( fetch_type & SQLSRV_FETCH_ASSOC ) { if( fetch_type & SQLSRV_FETCH_ASSOC ) {
CHECK_CUSTOM_WARNING_AS_ERROR(( stmt->fetch_field_names[i].len == 0 && !allow_empty_field_names ), stmt, CHECK_CUSTOM_WARNING_AS_ERROR(( stmt->fetch_field_names[i].len == 0 && !allow_empty_field_names ), stmt,
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY) { SS_SQLSRV_WARNING_FIELD_NAME_EMPTY, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1922,18 +1922,19 @@ zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_h
// Assumption: there are more than only the variable, parse the rest of the array // Assumption: there are more than only the variable, parse the rest of the array
zval* dir = zend_hash_index_find(param_ht, 1); zval* dir = zend_hash_index_find(param_ht, 1);
if(!dir) { throw ss::SSException(); }
if (Z_TYPE_P(dir) != IS_NULL) { if (Z_TYPE_P(dir) != IS_NULL) {
// if param direction is specified, make sure it's valid // if param direction is specified, make sure it's valid
CHECK_CUSTOM_ERROR(Z_TYPE_P(dir) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1) { CHECK_CUSTOM_ERROR(Z_TYPE_P(dir) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
direction = static_cast<SQLSMALLINT>(Z_LVAL_P(dir)); direction = static_cast<SQLSMALLINT>(Z_LVAL_P(dir));
CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && direction != SQL_PARAM_OUTPUT && direction != SQL_PARAM_INPUT_OUTPUT, CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && direction != SQL_PARAM_OUTPUT && direction != SQL_PARAM_INPUT_OUTPUT,
stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1) { stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && !Z_ISREF_P(var_or_val), stmt, SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF, index + 1) { CHECK_CUSTOM_ERROR(direction != SQL_PARAM_INPUT && !Z_ISREF_P(var_or_val), stmt, SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
} }
@ -1953,13 +1954,13 @@ zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_h
php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(var_or_val)]; php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(var_or_val)];
} }
} else { } else {
CHECK_CUSTOM_ERROR(Z_TYPE_P(phptype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1) { CHECK_CUSTOM_ERROR(Z_TYPE_P(phptype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
sqlsrv_phptype srv_phptype; sqlsrv_phptype srv_phptype;
srv_phptype.value = Z_LVAL_P(phptype_z); srv_phptype.value = Z_LVAL_P(phptype_z);
CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_phptype(srv_phptype), stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1) { CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_phptype(srv_phptype), stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -1978,19 +1979,19 @@ zval* parse_param_array(_Inout_ ss_sqlsrv_stmt* stmt, _Inout_ HashTable* param_h
throw ss::SSException(); throw ss::SSException();
} }
} else { } else {
CHECK_CUSTOM_ERROR(Z_TYPE_P(sqltype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1) { CHECK_CUSTOM_ERROR(Z_TYPE_P(sqltype_z) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
// since the user supplied this type, make sure it's valid // since the user supplied this type, make sure it's valid
sqlsrv_sqltype sqlsrv_sql_type; sqlsrv_sqltype sqlsrv_sql_type;
sqlsrv_sql_type.value = Z_LVAL_P(sqltype_z); sqlsrv_sql_type.value = Z_LVAL_P(sqltype_z);
CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_sqltype(sqlsrv_sql_type), stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1) { CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_sqltype(sqlsrv_sql_type), stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
bool size_okay = determine_column_size_or_precision(stmt, sqlsrv_sql_type, &column_size, &decimal_digits); bool size_okay = determine_column_size_or_precision(stmt, sqlsrv_sql_type, &column_size, &decimal_digits);
CHECK_CUSTOM_ERROR(!size_okay, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, index + 1) { CHECK_CUSTOM_ERROR(!size_okay, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, index + 1, NULL) {
throw ss::SSException(); throw ss::SSException();
} }

View file

@ -3,7 +3,7 @@
// //
// Contents: Version resource // Contents: Version resource
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License

View file

@ -5,17 +5,17 @@
// //
// Comments: Mostly error handling and some type handling // Comments: Mostly error handling and some type handling
// //
// Microsoft Drivers 5.10 for PHP for SQL Server // Microsoft Drivers 5.11 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
@ -26,17 +26,17 @@ extern "C" {
#include "php_sqlsrv_int.h" #include "php_sqlsrv_int.h"
namespace { namespace {
// current subsytem. defined for the CHECK_SQL_{ERROR|WARNING} macros // current subsytem. defined for the CHECK_SQL_{ERROR|WARNING} macros
unsigned int current_log_subsystem = LOG_UTIL; unsigned int current_log_subsystem = LOG_UTIL;
// *** internal functions *** // *** internal functions ***
sqlsrv_error_const* get_error_message( _In_ unsigned int sqlsrv_error_code ); sqlsrv_error_const* get_error_message( _In_ unsigned int sqlsrv_error_code );
void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain,
_In_ bool warning ); _In_ bool warning );
bool ignore_warning( _In_ char* sql_state, _In_ int native_code ); bool ignore_warning( _In_ char* sql_state, _In_ int native_code );
bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity, bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity,
_In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args ); _In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args );
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest ); int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest );
@ -46,9 +46,9 @@ bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z );
// List of all error messages // List of all error messages
ss_error SS_ERRORS[] = { ss_error SS_ERRORS[] = {
{ {
SS_SQLSRV_ERROR_INVALID_OPTION, SS_SQLSRV_ERROR_INVALID_OPTION,
{ IMSSP, (SQLCHAR*)"Invalid option %1!s! was passed to sqlsrv_connect.", -1, true } { IMSSP, (SQLCHAR*)"Invalid option %1!s! was passed to sqlsrv_connect.", -1, true }
}, },
@ -57,23 +57,23 @@ ss_error SS_ERRORS[] = {
// these two share the same code since they are basically the same error. // these two share the same code since they are basically the same error.
{ {
SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED,
{ IMSSP, (SQLCHAR*) "An unescaped right brace (}) was found in either the user name or password. All right braces must be" { IMSSP, (SQLCHAR*) "An unescaped right brace (}) was found in either the user name or password. All right braces must be"
" escaped with another right brace (}}).", -4, false } " escaped with another right brace (}}).", -4, false }
}, },
{ {
SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED, SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED,
{ IMSSP, (SQLCHAR*)"An unescaped right brace (}) was found in option %1!s!.", -4, true } { IMSSP, (SQLCHAR*)"An unescaped right brace (}) was found in option %1!s!.", -4, true }
}, },
{
SQLSRV_ERROR_NO_DATA,
{ IMSSP, (SQLCHAR*)"Field %1!d! returned no data.", -5, true }
},
{ {
SQLSRV_ERROR_STREAMABLE_TYPES_ONLY, SQLSRV_ERROR_NO_DATA,
{ IMSSP, (SQLCHAR*)"Field %1!d! returned no data.", -5, true }
},
{
SQLSRV_ERROR_STREAMABLE_TYPES_ONLY,
{ IMSSP, (SQLCHAR*)"Only char, nchar, varchar, nvarchar, binary, varbinary, and large object types can be read by using " { IMSSP, (SQLCHAR*)"Only char, nchar, varchar, nvarchar, binary, varbinary, and large object types can be read by using "
"streams.", -6, false} "streams.", -6, false}
}, },
@ -90,12 +90,12 @@ ss_error SS_ERRORS[] = {
}, },
{ {
SS_SQLSRV_ERROR_VAR_REQUIRED, SS_SQLSRV_ERROR_VAR_REQUIRED,
{ IMSSP, (SQLCHAR*)"Parameter array %1!d! must have at least one value or variable.", -9, true } { IMSSP, (SQLCHAR*)"Parameter array %1!d! must have at least one value or variable.", -9, true }
}, },
{ {
SS_SQLSRV_ERROR_INVALID_FETCH_TYPE, SS_SQLSRV_ERROR_INVALID_FETCH_TYPE,
{ IMSSP, (SQLCHAR*)"An invalid fetch type was specified. SQLSRV_FETCH_NUMERIC, SQLSRV_FETCH_ARRAY and SQLSRV_FETCH_BOTH are acceptable values.", -10, false } { IMSSP, (SQLCHAR*)"An invalid fetch type was specified. SQLSRV_FETCH_NUMERIC, SQLSRV_FETCH_ARRAY and SQLSRV_FETCH_BOTH are acceptable values.", -10, false }
}, },
@ -103,24 +103,24 @@ ss_error SS_ERRORS[] = {
SQLSRV_ERROR_STATEMENT_NOT_EXECUTED, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED,
{ IMSSP, (SQLCHAR*)"The statement must be executed before results can be retrieved.", -11, false } { IMSSP, (SQLCHAR*)"The statement must be executed before results can be retrieved.", -11, false }
}, },
{ {
SS_SQLSRV_ERROR_ALREADY_IN_TXN, SS_SQLSRV_ERROR_ALREADY_IN_TXN,
{ IMSSP, (SQLCHAR*)"Cannot begin a transaction until the current transaction has been completed by calling either " { IMSSP, (SQLCHAR*)"Cannot begin a transaction until the current transaction has been completed by calling either "
"sqlsrv_commit or sqlsrv_rollback.", -12, false } "sqlsrv_commit or sqlsrv_rollback.", -12, false }
}, },
{ {
SS_SQLSRV_ERROR_NOT_IN_TXN, SS_SQLSRV_ERROR_NOT_IN_TXN,
{ IMSSP, (SQLCHAR*)"A transaction must be started by calling sqlsrv_begin_transaction before calling sqlsrv_commit or " { IMSSP, (SQLCHAR*)"A transaction must be started by calling sqlsrv_begin_transaction before calling sqlsrv_commit or "
"sqlsrv_rollback.", -13, false } "sqlsrv_rollback.", -13, false }
}, },
{ {
SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER,
{ IMSSP, (SQLCHAR*)"An invalid parameter was passed to %1!s!.", -14, true } { IMSSP, (SQLCHAR*)"An invalid parameter was passed to %1!s!.", -14, true }
}, },
{ {
SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION,
{ IMSSP, (SQLCHAR*)"An invalid direction for parameter %1!d! was specified. SQLSRV_PARAM_IN, SQLSRV_PARAM_OUT, and " { IMSSP, (SQLCHAR*)"An invalid direction for parameter %1!d! was specified. SQLSRV_PARAM_IN, SQLSRV_PARAM_OUT, and "
@ -140,7 +140,7 @@ ss_error SS_ERRORS[] = {
{ {
SQLSRV_ERROR_FETCH_NOT_CALLED, SQLSRV_ERROR_FETCH_NOT_CALLED,
{ IMSSP, (SQLCHAR*)"A row must be retrieved with sqlsrv_fetch before retrieving data with sqlsrv_get_field.", -18, false } { IMSSP, (SQLCHAR*)"A row must be retrieved with sqlsrv_fetch before retrieving data with sqlsrv_get_field.", -18, false }
}, },
{ {
SQLSRV_ERROR_FIELD_INDEX_ERROR, SQLSRV_ERROR_FIELD_INDEX_ERROR,
@ -159,14 +159,14 @@ ss_error SS_ERRORS[] = {
{ {
SQLSRV_ERROR_FETCH_PAST_END, SQLSRV_ERROR_FETCH_PAST_END,
{ IMSSP, (SQLCHAR*)"There are no more rows in the active result set. Since this result set is not scrollable, no more " { IMSSP, (SQLCHAR*)"There are no more rows in the active result set. Since this result set is not scrollable, no more "
"data may be retrieved.", -22, false } "data may be retrieved.", -22, false }
}, },
{ {
SS_SQLSRV_ERROR_STATEMENT_NOT_PREPARED, SS_SQLSRV_ERROR_STATEMENT_NOT_PREPARED,
{ IMSSP, (SQLCHAR*)"A statement must be prepared with sqlsrv_prepare before calling sqlsrv_execute.", -23, false } { IMSSP, (SQLCHAR*)"A statement must be prepared with sqlsrv_prepare before calling sqlsrv_execute.", -23, false }
}, },
{ {
SQLSRV_ERROR_ZEND_HASH, SQLSRV_ERROR_ZEND_HASH,
{ IMSSP, (SQLCHAR*)"An error occurred while creating or accessing a Zend hash table.", -24, false } { IMSSP, (SQLCHAR*)"An error occurred while creating or accessing a Zend hash table.", -24, false }
@ -181,37 +181,37 @@ ss_error SS_ERRORS[] = {
SQLSRV_ERROR_NEXT_RESULT_PAST_END, SQLSRV_ERROR_NEXT_RESULT_PAST_END,
{ IMSSP, (SQLCHAR*)"There are no more results returned by the query.", -26, false } { IMSSP, (SQLCHAR*)"There are no more results returned by the query.", -26, false }
}, },
{ {
SQLSRV_ERROR_STREAM_CREATE, SQLSRV_ERROR_STREAM_CREATE,
{ IMSSP, (SQLCHAR*)"An error occurred while retrieving a SQL Server field as a stream.", -27, false } { IMSSP, (SQLCHAR*)"An error occurred while retrieving a SQL Server field as a stream.", -27, false }
}, },
{ {
SQLSRV_ERROR_NO_FIELDS, SQLSRV_ERROR_NO_FIELDS,
{ IMSSP, (SQLCHAR*)"The active result for the query contains no fields.", -28, false } { IMSSP, (SQLCHAR*)"The active result for the query contains no fields.", -28, false }
}, },
{ {
SS_SQLSRV_ERROR_ZEND_BAD_CLASS, SS_SQLSRV_ERROR_ZEND_BAD_CLASS,
{ IMSSP, (SQLCHAR*)"Failed to find class %1!s!.", -29, true } { IMSSP, (SQLCHAR*)"Failed to find class %1!s!.", -29, true }
}, },
{ {
SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED,
{ IMSSP, (SQLCHAR*)"Failed to create an instance of class %1!s!.", -30, true } { IMSSP, (SQLCHAR*)"Failed to create an instance of class %1!s!.", -30, true }
}, },
{ {
SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION,
{ IMSSP, (SQLCHAR*)"An invalid size or precision for parameter %1!d! was specified.", -31, true } { IMSSP, (SQLCHAR*)"An invalid size or precision for parameter %1!d! was specified.", -31, true }
}, },
{ {
SQLSRV_ERROR_INVALID_OPTION_KEY, SQLSRV_ERROR_INVALID_OPTION_KEY,
{ IMSSP, (SQLCHAR*)"Option %1!s! is invalid.", -32, true } { IMSSP, (SQLCHAR*)"Option %1!s! is invalid.", -32, true }
}, },
// these three errors are returned for invalid options, so they are given the same number for compatibility with 1.1 // these three errors are returned for invalid options, so they are given the same number for compatibility with 1.1
{ {
SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE,
@ -219,17 +219,17 @@ ss_error SS_ERRORS[] = {
}, },
{ {
SQLSRV_ERROR_INVALID_OPTION_TYPE_INT, SQLSRV_ERROR_INVALID_OPTION_TYPE_INT,
{ IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. Integer type was expected.", -33, true } { IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. Integer type was expected.", -33, true }
}, },
{ {
SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING, SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING,
{ IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. String type was expected.", -33, true } { IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. String type was expected.", -33, true }
}, },
{ {
SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH,
{ IMSSP, (SQLCHAR*)"The type of output parameter %1!d! does not match the type specified by the SQLSRV_PHPTYPE_* constant." { IMSSP, (SQLCHAR*)"The type of output parameter %1!d! does not match the type specified by the SQLSRV_PHPTYPE_* constant."
" For output parameters, the type of the variable's current value must match the SQLSRV_PHPTYPE_* constant, or be NULL. " " For output parameters, the type of the variable's current value must match the SQLSRV_PHPTYPE_* constant, or be NULL. "
"If the type is NULL, the PHP type of the output parameter is inferred from the SQLSRV_SQLTYPE_* constant.", -34, true } "If the type is NULL, the PHP type of the output parameter is inferred from the SQLSRV_SQLTYPE_* constant.", -34, true }
@ -238,14 +238,14 @@ ss_error SS_ERRORS[] = {
{ {
SQLSRV_ERROR_INVALID_TYPE, SQLSRV_ERROR_INVALID_TYPE,
{ IMSSP, (SQLCHAR*)"Invalid type", -35, false } { IMSSP, (SQLCHAR*)"Invalid type", -35, false }
}, },
// 36-38 have no equivalent 2.0 errors // 36-38 have no equivalent 2.0 errors
{ {
SS_SQLSRV_ERROR_REGISTER_RESOURCE, SS_SQLSRV_ERROR_REGISTER_RESOURCE,
{ IMSSP, (SQLCHAR*)"Registering the %1!s! resource failed.", -39, true } { IMSSP, (SQLCHAR*)"Registering the %1!s! resource failed.", -39, true }
}, },
{ {
SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
@ -256,7 +256,7 @@ ss_error SS_ERRORS[] = {
SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE,
{ IMSSP, (SQLCHAR*)"An error occurred translating string for an output param to UTF-8: %1!s!", -41, true } { IMSSP, (SQLCHAR*)"An error occurred translating string for an output param to UTF-8: %1!s!", -41, true }
}, },
{ {
SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE,
{ IMSSP, (SQLCHAR*)"An error occurred translating string for a field to UTF-8: %1!s!", -42, true } { IMSSP, (SQLCHAR*)"An error occurred translating string for a field to UTF-8: %1!s!", -42, true }
@ -267,24 +267,24 @@ ss_error SS_ERRORS[] = {
{ IMSSP, (SQLCHAR*)"An error occurred translating a PHP stream from UTF-8 to UTF-16: %1!s!", -43, true } { IMSSP, (SQLCHAR*)"An error occurred translating a PHP stream from UTF-8 to UTF-16: %1!s!", -43, true }
}, },
{ {
SQLSRV_ERROR_MARS_OFF, SQLSRV_ERROR_MARS_OFF,
{ IMSSP, (SQLCHAR*)"The connection cannot process this operation because there is a statement with pending results. " { IMSSP, (SQLCHAR*)"The connection cannot process this operation because there is a statement with pending results. "
"To make the connection available for other queries, either fetch all results or cancel or free the statement. " "To make the connection available for other queries, either fetch all results or cancel or free the statement. "
"For more information, see the product documentation about the MultipleActiveResultSets connection option.", -44, false } "For more information, see the product documentation about the MultipleActiveResultSets connection option.", -44, false }
}, },
{ {
SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE,
{ IMSSP, (SQLCHAR*) "Expected an array of options for the connection. Connection options must be passed as an array of " { IMSSP, (SQLCHAR*) "Expected an array of options for the connection. Connection options must be passed as an array of "
"key/value pairs.", -45, false } "key/value pairs.", -45, false }
}, },
{ {
SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE,
{ IMSSP, (SQLCHAR*) "An error occurred translating the query string to UTF-16: %1!s!.", -46, true } { IMSSP, (SQLCHAR*) "An error occurred translating the query string to UTF-16: %1!s!.", -46, true }
}, },
{ {
SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE,
{ IMSSP, (SQLCHAR*) "An error occurred translating the connection string to UTF-16: %1!s!", -47, true } { IMSSP, (SQLCHAR*) "An error occurred translating the connection string to UTF-16: %1!s!", -47, true }
@ -300,13 +300,13 @@ ss_error SS_ERRORS[] = {
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server. " { IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server. "
"Access the following URL to download the ODBC Driver for SQL Server for %1!s!: " "Access the following URL to download the ODBC Driver for SQL Server for %1!s!: "
"https://go.microsoft.com/fwlink/?LinkId=163712", -49, true } "https://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
}, },
{ {
SS_SQLSRV_ERROR_STATEMENT_NOT_SCROLLABLE, SS_SQLSRV_ERROR_STATEMENT_NOT_SCROLLABLE,
{ IMSSP, (SQLCHAR*)"This function only works with statements that have static or keyset scrollable cursors.", -50, false } { IMSSP, (SQLCHAR*)"This function only works with statements that have static or keyset scrollable cursors.", -50, false }
}, },
{ {
SS_SQLSRV_ERROR_STATEMENT_SCROLLABLE, SS_SQLSRV_ERROR_STATEMENT_SCROLLABLE,
{ IMSSP, (SQLCHAR*)"This function only works with statements that are not scrollable.", -51, false } { IMSSP, (SQLCHAR*)"This function only works with statements that are not scrollable.", -51, false }
@ -314,11 +314,11 @@ ss_error SS_ERRORS[] = {
// new error for 2.0, used here since 1.1 didn't have a -52 // new error for 2.0, used here since 1.1 didn't have a -52
{ {
SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, SQLSRV_ERROR_MAX_PARAMS_EXCEEDED,
{ IMSSP, (SQLCHAR*) "Tried to bind parameter number %1!d!. SQL Server supports a maximum of 2100 parameters.", -52, true } { IMSSP, (SQLCHAR*) "Tried to bind parameter number %1!d!. SQL Server supports a maximum of 2100 parameters.", -52, true }
}, },
{ {
SS_SQLSRV_ERROR_INVALID_FETCH_STYLE, SS_SQLSRV_ERROR_INVALID_FETCH_STYLE,
{ IMSSP, (SQLCHAR*)"The scroll type passed to sqlsrv_fetch, sqlsrv_fetch_array, or sqlsrv_fetch_object was not valid. " { IMSSP, (SQLCHAR*)"The scroll type passed to sqlsrv_fetch, sqlsrv_fetch_array, or sqlsrv_fetch_object was not valid. "
"Please use one of the SQLSRV_SCROLL constants.", -53, false } "Please use one of the SQLSRV_SCROLL constants.", -53, false }
@ -329,14 +329,14 @@ ss_error SS_ERRORS[] = {
{ IMSSP, (SQLCHAR*)"The value passed for the 'Scrollable' statement option is invalid. Please use 'static', 'dynamic', " { IMSSP, (SQLCHAR*)"The value passed for the 'Scrollable' statement option is invalid. Please use 'static', 'dynamic', "
"'keyset', 'forward', or 'buffered'.", -54, false } "'keyset', 'forward', or 'buffered'.", -54, false }
}, },
{ {
SQLSRV_ERROR_UNKNOWN_SERVER_VERSION, SQLSRV_ERROR_UNKNOWN_SERVER_VERSION,
{ IMSSP, (SQLCHAR*)"Failed to retrieve the server version. Unable to continue.", -55, false } { IMSSP, (SQLCHAR*)"Failed to retrieve the server version. Unable to continue.", -55, false }
}, },
{ {
SQLSRV_ERROR_INVALID_PARAMETER_ENCODING, SQLSRV_ERROR_INVALID_PARAMETER_ENCODING,
{ IMSSP, (SQLCHAR*) "An invalid encoding was specified for parameter %1!d!.", -56, true } { IMSSP, (SQLCHAR*) "An invalid encoding was specified for parameter %1!d!.", -56, true }
}, },
@ -346,7 +346,7 @@ ss_error SS_ERRORS[] = {
}, },
{ {
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED,
{ IMSSP, (SQLCHAR*) "String data, right truncated for output parameter %1!d!.", -58, true } { IMSSP, (SQLCHAR*) "String data, right truncated for output parameter %1!d!.", -58, true }
}, },
{ {
@ -472,7 +472,7 @@ ss_error SS_ERRORS[] = {
{ {
SQLSRV_ERROR_TVP_INPUT_PARAM_ONLY, SQLSRV_ERROR_TVP_INPUT_PARAM_ONLY,
{ IMSSP, (SQLCHAR*) "You cannot return data in a table-valued parameter. Table-valued parameters are input-only.", -130, false } { IMSSP, (SQLCHAR*) "You cannot return data in a table-valued parameter. Table-valued parameters are input-only.", -130, false }
}, },
// terminate the list of errors/warnings // terminate the list of errors/warnings
{ UINT_MAX, {} } { UINT_MAX, {} }
@ -491,7 +491,7 @@ bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_err
severity = SEV_WARNING; severity = SEV_WARNING;
} }
return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning, return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
print_args ); print_args );
} }
@ -507,7 +507,7 @@ bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_err
// //
// $errorsAndOrWarnings[OPTIONAL]: A predefined constant. This parameter can // $errorsAndOrWarnings[OPTIONAL]: A predefined constant. This parameter can
// take one of the values listed: // take one of the values listed:
// //
// SQLSRV_ERR_ALL // SQLSRV_ERR_ALL
// Errors and warnings generated on the last sqlsrv function call are returned. // Errors and warnings generated on the last sqlsrv function call are returned.
// SQLSRV_ERR_ERRORS // SQLSRV_ERR_ERRORS
@ -603,10 +603,10 @@ PHP_FUNCTION( sqlsrv_configure )
// dummy context to pass onto the error handler // dummy context to pass onto the error handler
error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL ); error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_); error_ctx->set_func(_FN_);
int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "sz", &option, &option_len, &value_z ); int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "sz", &option, &option_len, &value_z );
CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -622,11 +622,11 @@ PHP_FUNCTION( sqlsrv_configure )
// LogSeverity // LogSeverity
else if( !stricmp( option, INI_LOG_SEVERITY )) { else if( !stricmp( option, INI_LOG_SEVERITY )) {
CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
zend_long severity_mask = Z_LVAL_P( value_z ); zend_long severity_mask = Z_LVAL_P( value_z );
// make sure they can't use 0 to shut off the masking in the severity // make sure they can't use 0 to shut off the masking in the severity
if( severity_mask < SEV_ALL || severity_mask == 0 || severity_mask > (SEV_NOTICE + SEV_ERROR + SEV_WARNING) ) { if( severity_mask < SEV_ALL || severity_mask == 0 || severity_mask > (SEV_NOTICE + SEV_ERROR + SEV_WARNING) ) {
@ -641,11 +641,11 @@ PHP_FUNCTION( sqlsrv_configure )
// LogSubsystems // LogSubsystems
else if( !stricmp( option, INI_LOG_SUBSYSTEMS )) { else if( !stricmp( option, INI_LOG_SUBSYSTEMS )) {
CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
zend_long subsystem_mask = Z_LVAL_P( value_z ); zend_long subsystem_mask = Z_LVAL_P( value_z );
if( subsystem_mask < LOG_ALL || subsystem_mask > (LOG_INIT + LOG_CONN + LOG_STMT + LOG_UTIL) ) { if( subsystem_mask < LOG_ALL || subsystem_mask > (LOG_INIT + LOG_CONN + LOG_STMT + LOG_UTIL) ) {
@ -658,15 +658,15 @@ PHP_FUNCTION( sqlsrv_configure )
} }
else if( !stricmp( option, INI_BUFFERED_QUERY_LIMIT )) { else if( !stricmp( option, INI_BUFFERED_QUERY_LIMIT )) {
CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_ ) { CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
zend_long buffered_query_limit = Z_LVAL_P( value_z ); zend_long buffered_query_limit = Z_LVAL_P( value_z );
CHECK_CUSTOM_ERROR( buffered_query_limit <= 0, error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_ ) { CHECK_CUSTOM_ERROR( buffered_query_limit <= 0, error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_, NULL) {
throw ss::SSException(); throw ss::SSException();
} }
@ -678,7 +678,7 @@ PHP_FUNCTION( sqlsrv_configure )
else { else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
@ -693,7 +693,7 @@ PHP_FUNCTION( sqlsrv_configure )
// sqlsrv_get_config( string $setting ) // sqlsrv_get_config( string $setting )
// //
// Returns the current value of the specified configuration setting. // Returns the current value of the specified configuration setting.
// //
// Parameters // Parameters
@ -719,15 +719,15 @@ PHP_FUNCTION( sqlsrv_get_config )
reset_errors(); reset_errors();
try { try {
// dummy context to pass onto the error handler // dummy context to pass onto the error handler
error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL ); error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_); error_ctx->set_func(_FN_);
int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "s", &option, &option_len ); int zr = zend_parse_parameters( ZEND_NUM_ARGS(), "s", &option, &option_len );
CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ , NULL) {
throw ss::SSException(); throw ss::SSException();
} }
SQLSRV_ASSERT( option != NULL, "sqlsrv_get_config: option was null." ); SQLSRV_ASSERT( option != NULL, "sqlsrv_get_config: option was null." );
if( !stricmp( option, INI_WARNINGS_RETURN_AS_ERRORS )) { if( !stricmp( option, INI_WARNINGS_RETURN_AS_ERRORS )) {
@ -751,16 +751,16 @@ PHP_FUNCTION( sqlsrv_get_config )
return; return;
} }
else { else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_, NULL );
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
RETURN_FALSE; RETURN_FALSE;
} }
catch( ... ) { catch( ... ) {
DIE( "sqlsrv_get_config: Unknown exception caught." ); DIE( "sqlsrv_get_config: Unknown exception caught." );
} }
} }
@ -782,13 +782,13 @@ sqlsrv_error_const* get_error_message( _In_ unsigned int sqlsrv_error_code ) {
return error_message; return error_message;
} }
void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain,
_In_ bool warning ) _In_ bool warning )
{ {
array_init(error_z); array_init(error_z);
// sqlstate // sqlstate
zval temp; zval temp;
ZVAL_UNDEF(&temp); ZVAL_UNDEF(&temp);
core::sqlsrv_zval_stringl( &temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE ); core::sqlsrv_zval_stringl( &temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE );
Z_TRY_ADDREF_P( &temp ); Z_TRY_ADDREF_P( &temp );
@ -819,36 +819,36 @@ void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error,
// add the error or warning to the reported_chain. // add the error or warning to the reported_chain.
if( !warning || SQLSRV_G( warnings_return_as_errors ) ) if( !warning || SQLSRV_G( warnings_return_as_errors ) )
{ {
// if the warning is part of the ignored warning list than // if the warning is part of the ignored warning list than
// add to the ignored chain if the ignored chain is not null. // add to the ignored chain if the ignored chain is not null.
if( warning && ignore_warning( reinterpret_cast<char*>(error->sqlstate), error->native_code ) && if( warning && ignore_warning( reinterpret_cast<char*>(error->sqlstate), error->native_code ) &&
ignored_chain != NULL ) { ignored_chain != NULL ) {
if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) { if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" ); DIE( "Fatal error during error processing" );
} }
} }
else { else {
// It is either an error or a warning which should not be ignored. // It is either an error or a warning which should not be ignored.
if( add_next_index_zval( reported_chain, error_z ) == FAILURE ) { if( add_next_index_zval( reported_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" ); DIE( "Fatal error during error processing" );
} }
} }
} }
else else
{ {
// It is a warning with warning_return_as_errors as false, so simply add it to the ignored_chain list // It is a warning with warning_return_as_errors as false, so simply add it to the ignored_chain list
if( ignored_chain != NULL ) { if( ignored_chain != NULL ) {
if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) { if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" ); DIE( "Fatal error during error processing" );
} }
} }
} }
} }
bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity, bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity,
_In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args ) _In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args )
{ {
bool result = true; bool result = true;
@ -872,20 +872,20 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
// array of ignored errors // array of ignored errors
if( ignored_chain != NULL ) { if( ignored_chain != NULL ) {
if( Z_TYPE_P( ignored_chain ) == IS_NULL ) { if( Z_TYPE_P( ignored_chain ) == IS_NULL ) {
ignored_chain_was_null = true; ignored_chain_was_null = true;
array_init( ignored_chain ); array_init( ignored_chain );
} }
} }
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) { if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, log_severity, print_args ); core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, log_severity, print_args );
copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning ); copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning );
} }
SQLSMALLINT record_number = 0; SQLSMALLINT record_number = 0;
do { do {
@ -894,7 +894,7 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning ); copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning );
} }
} while( result ); } while( result );
// If it were a warning, we report that warnings where ignored except if warnings_return_as_errors // If it were a warning, we report that warnings where ignored except if warnings_return_as_errors
// was true and we added some warnings to the reported_chain. // was true and we added some warnings to the reported_chain.
if( warning ) { if( warning ) {
@ -902,9 +902,9 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
errors_ignored = true; errors_ignored = true;
if( SQLSRV_G( warnings_return_as_errors ) ) { if( SQLSRV_G( warnings_return_as_errors ) ) {
if( zend_hash_num_elements( Z_ARRVAL_P( reported_chain )) > prev_reported_cnt ) { if( zend_hash_num_elements( Z_ARRVAL_P( reported_chain )) > prev_reported_cnt ) {
// We actually added some errors // We actually added some errors
errors_ignored = false; errors_ignored = false;
} }
@ -946,12 +946,12 @@ bool ignore_warning( _In_ char* sql_state, _In_ int native_code )
return true; return true;
} }
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
return false; return false;
} }
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest ) int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest )
{ {
zval_ptr_dtor( dest ); zval_ptr_dtor( dest );
return ZEND_HASH_APPLY_REMOVE; return ZEND_HASH_APPLY_REMOVE;
} }
@ -988,6 +988,6 @@ bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z )
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
return true; return true;
} }
} // namespace } // namespace

View file

@ -8,9 +8,13 @@ fetch with all fetch styles
$conn = new PDO( "sqlsrv:server=$server ; Database = $databaseName", "$uid", "$pwd"); $conn = new PDO( "sqlsrv:server=$server ; Database = $databaseName", "$uid", "$pwd");
print( "\n---------- PDO::FETCH_CLASS -------------\n" ); print( "\n---------- PDO::FETCH_CLASS -------------\n" );
$stmt = $conn->query( "select * from HumanResources.Department order by GroupName" ); $stmt = $conn->query( "select DepartmentID, Name, GroupName from HumanResources.Department order by GroupName" );
class cc { class cc {
public $DepartmentID;
public $Name;
public $GroupName;
function __construct( $arg ) { function __construct( $arg ) {
echo "$arg"; echo "$arg";
} }
@ -26,7 +30,7 @@ fetch with all fetch styles
} }
print( "\n---------- PDO::FETCH_INTO -------------\n" ); print( "\n---------- PDO::FETCH_INTO -------------\n" );
$stmt = $conn->query( "select * from HumanResources.Department order by GroupName" ); $stmt = $conn->query( "select DepartmentID, Name, GroupName from HumanResources.Department order by GroupName" );
$c_obj = new cc( '' ); $c_obj = new cc( '' );
$stmt->setFetchMode(PDO::FETCH_INTO, $c_obj); $stmt->setFetchMode(PDO::FETCH_INTO, $c_obj);

View file

@ -25,6 +25,10 @@ fetches the rows in a result set in an array
print "\n-----------\n"; print "\n-----------\n";
class cc { class cc {
public $ContactTypeID;
public $Name;
public $ModifiedDate;
function __construct( $arg ) { function __construct( $arg ) {
echo "$arg\n"; echo "$arg\n";
} }
@ -34,7 +38,7 @@ fetches the rows in a result set in an array
} }
}; };
$stmt = $conn->query( 'SELECT TOP(2) * FROM Person.ContactType' ); $stmt = $conn->query( 'SELECT TOP(2) ContactTypeID, Name, ModifiedDate FROM Person.ContactType' );
$all = $stmt->fetchAll( PDO::FETCH_CLASS, 'cc', array( 'Hi!' )); $all = $stmt->fetchAll( PDO::FETCH_CLASS, 'cc', array( 'Hi!' ));
var_dump( $all ); var_dump( $all );

View file

@ -26,9 +26,13 @@ while ( $row = $stmt->fetch() ){
} }
echo "\n........ query with a new class ............\n"; echo "\n........ query with a new class ............\n";
$query = 'select * from HumanResources.Department order by GroupName'; $query = 'select DepartmentID, Name, GroupName from HumanResources.Department order by GroupName';
// query with a class // query with a class
class cc { class cc {
public $DepartmentID;
public $Name;
public $GroupName;
function __construct( $arg ) { function __construct( $arg ) {
echo "$arg"; echo "$arg";
} }

View file

@ -25,7 +25,7 @@ $tsql1 = "UPDATE Production.ProductReview
// //
$reviewID = 3; $reviewID = 3;
$comments = utf8_encode("testing 1, 2, 3, 4. Testing."); $comments = mb_convert_encoding("testing 1, 2, 3, 4. Testing.", 'ISO-8859-1', 'UTF-8');
$params1 = array( $params1 = array(
array( $comments, null ), array( $comments, null ),
array( $reviewID, null ) array( $reviewID, null )

View file

@ -7,6 +7,8 @@ retrieves each row of a result set as an instance of the Product class defined i
/* Define the Product class. */ /* Define the Product class. */
class Product class Product
{ {
/* Constructor */ /* Constructor */
public function ProductConstruct($ID) public function ProductConstruct($ID)
{ {
@ -17,6 +19,8 @@ class Product
public $StockedQty; public $StockedQty;
public $SafetyStockLevel; public $SafetyStockLevel;
private $UnitPrice; private $UnitPrice;
public $Name;
public $Color;
function getPrice() function getPrice()
{ {
return $this->UnitPrice; return $this->UnitPrice;

View file

@ -27,7 +27,7 @@ $tsql1 = "UPDATE Production.ProductReview
// utf8_encode to simulate an application that uses UTF-8 encoded data. // utf8_encode to simulate an application that uses UTF-8 encoded data.
// //
$reviewID = 3; $reviewID = 3;
$comments = utf8_encode("testing"); $comments = mb_convert_encoding("testing", 'ISO-8859-1', 'UTF-8');
$params1 = array( $params1 = array(
array($comments, array($comments,
SQLSRV_PARAM_IN, SQLSRV_PARAM_IN,

View file

@ -7,7 +7,7 @@
Common functions (shared by all tests). Common functions (shared by all tests).
*/ */
error_reporting( ~E_DEPRECATED );
$tvpIncPath = dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'inc'.DIRECTORY_SEPARATOR; $tvpIncPath = dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'inc'.DIRECTORY_SEPARATOR;
require_once($tvpIncPath. 'test_tvp_data.php'); require_once($tvpIncPath. 'test_tvp_data.php');
@ -1840,4 +1840,4 @@ function compareResourceToInput($actual, $expected)
} }
return $matched; return $matched;
} }

View file

@ -59,6 +59,7 @@ function ExecStmt()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class Test class Test
{ {
function __construct($name = 'N/A') function __construct($name = 'N/A')

View file

@ -48,6 +48,7 @@ function FetchMode()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class Test class Test
{ {
function __construct($name = 'N/A') function __construct($name = 'N/A')

View file

@ -72,6 +72,7 @@ function FetchAll()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class Test1 class Test1
{ {
public function __construct() public function __construct()
@ -80,6 +81,7 @@ class Test1
} }
} }
#[AllowDynamicProperties]
class Test2 class Test2
{ {
public function __construct() public function __construct()
@ -88,6 +90,7 @@ class Test2
} }
} }
#[AllowDynamicProperties]
class Test3 class Test3
{ {
public function __construct() public function __construct()

View file

@ -64,6 +64,7 @@ function FetchAll()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class DerivedStatement extends PDOStatement class DerivedStatement extends PDOStatement
{ {
private function __construct($name, $conn) private function __construct($name, $conn)
@ -78,6 +79,7 @@ class DerivedStatement extends PDOStatement
} }
} }
#[AllowDynamicProperties]
class Test1 class Test1
{ {
public function __construct($id, $val) public function __construct($id, $val)

View file

@ -10,6 +10,7 @@ PHPT_EXEC=true
<?php <?php
include 'MsCommon.inc'; include 'MsCommon.inc';
#[AllowDynamicProperties]
class TestClass class TestClass
{ {
private $set_calls = 0; private $set_calls = 0;

View file

@ -48,6 +48,7 @@ function Extend()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class ExPDO extends PDO class ExPDO extends PDO
{ {
public $test1 = 1; public $test1 = 1;
@ -74,6 +75,7 @@ class ExPDO extends PDO
} }
} }
#[AllowDynamicProperties]
class ExPDOStatement extends PDOStatement class ExPDOStatement extends PDOStatement
{ {
protected function __construct() protected function __construct()

View file

@ -46,27 +46,31 @@ function Extend()
EndTest($testName); EndTest($testName);
} }
#[AllowDynamicProperties]
class ExPDO extends PDO class ExPDO extends PDO
{ {
public function __construct() public function __construct()
{ {
$this->protocol(); $this->protocol();
$args = func_get_args(); $args = func_get_args();
return (call_user_func_array(array($this, 'parent::__construct'), $args)); $callable = parent::class . '::__construct';
return (call_user_func_array($callable, $args));
} }
public function exec(string $args1) : int|false public function exec(string $args1) : int|false
{ {
$this->protocol(); $this->protocol();
$args = func_get_args(); $args = func_get_args();
return (call_user_func_array(array($this, 'parent::exec'), $args)); $callable = parent::class . '::exec';
return (call_user_func_array($callable, $args));
} }
function query(string $sql, ?int $fetchMode = null, mixed ...$fetchModeArgs): PDOStatement|false function query(string $sql, ?int $fetchMode = null, mixed ...$fetchModeArgs): PDOStatement|false
{ {
$this->protocol(); $this->protocol();
$args = func_get_args(); $args = func_get_args();
return (call_user_func_array(array($this, 'parent::query'), $args)); $callable = parent::class . '::query';
return (call_user_func_array($callable, $args));
} }
public function __call($method, $args) public function __call($method, $args)

View file

@ -0,0 +1,77 @@
--TEST--
GitHub issue 1391 - string truncation error when binding some parameters as longer strings the second time
--DESCRIPTION--
The test shows the same parameters, though bound as short strings in the first insertion, can be bound as longer strings 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, 'long_strings');
$tsql = <<<CREATESQL
CREATE TABLE long_strings (
id bigint IDENTITY(1,1) NOT NULL,
four_thousand varchar(4002) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
var_max varchar(max) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
nvar_max varchar(max) NOT NULL,
CONSTRAINT PK__long_strings__1391E83F512B1391 PRIMARY KEY (id))
CREATESQL;
$conn->exec($tsql);
$tsql = <<<INSERTSQL
INSERT INTO long_strings (four_thousand, var_max, nvar_max) VALUES (?, ?, ?)
INSERTSQL;
$stmt = $conn->prepare($tsql);
// Bind and execute short string values first
$fourThousand = '4';
$varMax = 'v';
$nvarMax = 'n';
$stmt->bindParam(1, $fourThousand);
$stmt->bindParam(2, $varMax);
$stmt->bindParam(3, $nvarMax);
$stmt->execute();
// Bind and execute long string values second, on same $stmt
$fourThousand = str_repeat('4', 4001);
$varMax = str_repeat('v', 4001);
$nvarMax = str_repeat('n', 4001);
$stmt->bindParam(1, $fourThousand);
$stmt->bindParam(2, $varMax);
$stmt->bindParam(3, $nvarMax);
$stmt->execute();
// fetch the data
$stmt = $conn->prepare("SELECT COUNT(*) FROM long_strings");
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_NUM);
echo $row[0]."\n";
dropTable($conn, 'long_strings');
echo "Done\n";
} catch (PdoException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
2
Done

View file

@ -3,7 +3,11 @@ Test some error conditions of Azure AD Managed Identity support
--DESCRIPTION-- --DESCRIPTION--
This test expects certain exceptions to be thrown under some conditions. This test expects certain exceptions to be thrown under some conditions.
--SKIPIF-- --SKIPIF--
<?php require('skipif.inc');?> <?php
require('skipif.inc');
require('skipif_Appveyor.inc');
require('skipif_unix.inc');
?>
--FILE-- --FILE--
<?php <?php
require_once("MsCommon_mid-refactor.inc"); require_once("MsCommon_mid-refactor.inc");
@ -41,7 +45,9 @@ function connectInvalidServer()
$conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", null, null); $conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", null, null);
echo $message . $testCase . PHP_EOL; echo $message . $testCase . PHP_EOL;
} catch(PDOException $e) { } catch(PDOException $e) {
// TODO: check the exception message here echo "Failed to connect\n";
print_r($e->getMessage());
echo "\n";
} }
} catch(PDOException $e) { } catch(PDOException $e) {
print_r($e->getMessage()); print_r($e->getMessage());
@ -72,7 +78,9 @@ function connectInvalidServerWithUser()
$conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", $user, null); $conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", $user, null);
echo $message . $testCase . PHP_EOL; echo $message . $testCase . PHP_EOL;
} catch(PDOException $e) { } catch(PDOException $e) {
// TODO: check the exception message here echo "Failed to connect\n";
print_r($e->getMessage());
echo "\n";
} }
} catch(PDOException $e) { } catch(PDOException $e) {
print_r($e->getMessage()); print_r($e->getMessage());
@ -82,10 +90,15 @@ function connectInvalidServerWithUser()
require_once('MsSetup.inc'); require_once('MsSetup.inc');
// Make a connection to an invalid server // Make a connection to an invalid server
// Expect to get two error messages
connectInvalidServer(); connectInvalidServer();
connectInvalidServerWithUser(); connectInvalidServerWithUser();
echo "Done\n"; echo "Done\n";
?> ?>
--EXPECT-- --EXPECT--
Failed to connect
SQLSTATE[08001]: [Microsoft][ODBC Driver 17 for SQL Server]Named Pipes Provider: Could not open a connection to SQL Server [53].
Failed to connect
SQLSTATE[08001]: [Microsoft][ODBC Driver 17 for SQL Server]Named Pipes Provider: Could not open a connection to SQL Server [53].
Done Done

View file

@ -22,7 +22,6 @@ $conn = null;
testValidValues(); testValidValues();
testInvalidValues(); testInvalidValues();
testEncryptedWithODBC(); testEncryptedWithODBC();
testWrongODBC();
echo "Done" . PHP_EOL; echo "Done" . PHP_EOL;
// end test // end test
@ -118,17 +117,6 @@ function testEncryptedWithODBC()
connectVerifyOutput($connectionOptions, "Using ODBC 13 for AE", $expected); connectVerifyOutput($connectionOptions, "Using ODBC 13 for AE", $expected);
} }
function testWrongODBC()
{
global $msodbcsqlMaj;
$value = "ODBC Driver 18 for SQL Server";
$connectionOptions = "Driver = $value;";
$expected = "The specified ODBC Driver is not found.";
connectVerifyOutput($connectionOptions, "Connect with ODBC 18", $expected);
}
?> ?>
--EXPECT-- --EXPECT--
Done Done

View file

@ -8,6 +8,8 @@ This test should not use temporary table as it might occasionally cause deadlock
<?php <?php
include 'MsCommon.inc'; include 'MsCommon.inc';
#[AllowDynamicProperties]
class PdoTestClass class PdoTestClass
{ {
function __construct () function __construct ()
@ -16,6 +18,7 @@ class PdoTestClass
} }
} }
#[AllowDynamicProperties]
class PdoTestClass2 class PdoTestClass2
{ {
function __construct ($a1, $a2) function __construct ($a1, $a2)

View file

@ -20,7 +20,7 @@ try {
--EXPECTREGEX-- --EXPECTREGEX--
Array Array
\( \(
\[(DriverDllName|DriverName)\] => (msodbcsql1[1-9].dll|(libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib)) \[(DriverDllName|DriverName)\] => (msodbcsql1[1-9].dll|(libmsodbcsql-[0-9]{2}\.[0-9]{1,2}\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib))
\[DriverODBCVer\] => [0-9]{1,2}\.[0-9]{1,2} \[DriverODBCVer\] => [0-9]{1,2}\.[0-9]{1,2}
\[DriverVer\] => [0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4} \[DriverVer\] => [0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}
\[ExtensionVer\] => [0-9]\.[0-9]+\.[0-9](-(RC[1-9]?|beta[1-9]))?(\.[0-9]+)?(\+[0-9]+)? \[ExtensionVer\] => [0-9]\.[0-9]+\.[0-9](-(RC[1-9]?|beta[1-9]))?(\.[0-9]+)?(\+[0-9]+)?

View file

@ -125,7 +125,7 @@ SQLSTATE\[IMSSP\]: A read-only attribute was designated on the PDO object.
Get Result PDO::ATTR_CLIENT_VERSION : Get Result PDO::ATTR_CLIENT_VERSION :
array\(4\) { array\(4\) {
\[\"(DriverDllName|DriverName)\"\]=> \[\"(DriverDllName|DriverName)\"\]=>
(string\([0-9]+\) \"msodbcsql1[1-9].dll\"|string\([0-9]+\) \"(libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib)\") (string\([0-9]+\) \"msodbcsql1[1-9].dll\"|string\([0-9]+\) \"(libmsodbcsql-[0-9]{2}\.[0-9]{1,2}\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib)\")
\["DriverODBCVer"\]=> \["DriverODBCVer"\]=>
string\(5\) \"[0-9]{1,2}\.[0-9]{1,2}\" string\(5\) \"[0-9]{1,2}\.[0-9]{1,2}\"
\["DriverVer"\]=> \["DriverVer"\]=>

View file

@ -8,6 +8,7 @@ ORDER BY should work with sql_variants
<?php <?php
require_once("MsCommon_mid-refactor.inc"); require_once("MsCommon_mid-refactor.inc");
#[AllowDynamicProperties]
class Food class Food
{ {
public function getFood() public function getFood()

View file

@ -24,6 +24,6 @@ if ($c !== false) {
Fatal error: Uncaught PDOException: SQLSTATE\[(28000|08001|HYT00)\]: .*\[Microsoft\]\[ODBC Driver 1[0-9] for SQL Server\](\[SQL Server\])?(Named Pipes Provider: Could not open a connection to SQL Server \[2\]\. |TCP Provider: Error code (0x2726|0x2AF9)|Login timeout expired|Login failed for user 'sa'\.) in .+(\/|\\)pdo_utf8_conn\.php:[0-9]+ Fatal error: Uncaught PDOException: SQLSTATE\[(28000|08001|HYT00)\]: .*\[Microsoft\]\[ODBC Driver 1[0-9] for SQL Server\](\[SQL Server\])?(Named Pipes Provider: Could not open a connection to SQL Server \[2\]\. |TCP Provider: Error code (0x2726|0x2AF9)|Login timeout expired|Login failed for user 'sa'\.) in .+(\/|\\)pdo_utf8_conn\.php:[0-9]+
Stack trace: Stack trace:
#0 .+(\/|\\)pdo_utf8_conn\.php\([0-9]+\): PDO->__construct(\(\)|\('sqlsrv:Server=l\.\.\.', 'sa', 'Sunshine4u'\)) #0 .+(\/|\\)pdo_utf8_conn\.php\([0-9]+\): PDO->__construct(\(\)|\('sqlsrv:Server=l\.\.\.', 'sa', ('Sunshine4u'|Object\(SensitiveParameterValue\))\))
#1 {main} #1 {main}
thrown in .+(\/|\\)pdo_utf8_conn\.php on line [0-9]+ thrown in .+(\/|\\)pdo_utf8_conn\.php on line [0-9]+

View file

@ -0,0 +1,4 @@
<?php
if (getenv('APPVEYOR')) {
die("skip Appveyor pipeline");
}

View file

@ -12,6 +12,7 @@ require('skipif_versions_old.inc');
<?php <?php
require_once('MsCommon.inc'); require_once('MsCommon.inc');
#[AllowDynamicProperties]
class TestClass class TestClass
{ {
public function __construct($a1, $a2, $a3) public function __construct($a1, $a2, $a3)

View file

@ -15,7 +15,7 @@ var_dump( $client_info );
--EXPECTREGEX-- --EXPECTREGEX--
array\(4\) { array\(4\) {
\[\"(DriverDllName|DriverName)\"\]=> \[\"(DriverDllName|DriverName)\"\]=>
(string\([0-9]+\) \"msodbcsql1[1-9].dll\"|string\([0-9]+\) \"(libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib)\") (string\([0-9]+\) \"msodbcsql1[1-9].dll\"|string\([0-9]+\) \"(libmsodbcsql-[0-9]{2}\.[0-9]{1,2}\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib)\")
\[\"DriverODBCVer\"\]=> \[\"DriverODBCVer\"\]=>
string\(5\) \"[0-9]{1,2}\.[0-9]{1,2}\" string\(5\) \"[0-9]{1,2}\.[0-9]{1,2}\"
\[\"DriverVer\"\]=> \[\"DriverVer\"\]=>

View file

@ -20,7 +20,6 @@ sqlsrv_close($conn);
testValidValues($msodbcsqlMaj, $server, $connectionOptions); testValidValues($msodbcsqlMaj, $server, $connectionOptions);
testInvalidValues($msodbcsqlMaj, $server, $connectionOptions); testInvalidValues($msodbcsqlMaj, $server, $connectionOptions);
testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions); testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions);
testWrongODBC($msodbcsqlMaj, $server, $connectionOptions);
echo "Done\n"; echo "Done\n";
// end test // end test
@ -115,15 +114,6 @@ function testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions)
connectVerifyOutput($server, $connectionOptions, "Using ODBC 13 for AE", $expected); connectVerifyOutput($server, $connectionOptions, "Using ODBC 13 for AE", $expected);
} }
function testWrongODBC($msodbcsqlMaj, $server, $connectionOptions)
{
$value = "ODBC Driver 18 for SQL Server";
$connectionOptions['Driver']=$value;
$expected = "The specified ODBC Driver is not found.";
connectVerifyOutput($server, $connectionOptions, "Connect with ODBC 18", $expected);
}
?> ?>
--EXPECT-- --EXPECT--
Done Done

View file

@ -61,7 +61,7 @@ function isDataClassSupported($conn, &$driverCapable)
// ODBC Driver must be 17.2 or above // ODBC Driver must be 17.2 or above
$driverCapable = true; $driverCapable = true;
if ($version[0] < 17 || $version[1] < 2) { if ($version[0] < 17 || ($version[0] == 17 && $version[1] < 2)) {
$driverCapable = false; $driverCapable = false;
return false; return false;
} }

View file

@ -63,7 +63,7 @@ function isDataClassSupported($conn, &$driverCapable)
// ODBC Driver must be 17.2 or above // ODBC Driver must be 17.2 or above
$driverCapable = true; $driverCapable = true;
if ($version[0] < 17 || $version[1] < 2) { if ($version[0] < 17 || ($version[0] == 17 && $version[1] < 2)) {
$driverCapable = false; $driverCapable = false;
return false; return false;
} }

View file

@ -5,6 +5,7 @@ Test for fetch_object
--FILE-- --FILE--
<?php <?php
#[AllowDynamicProperties]
class foo class foo
{ {
public $stuff = "stuff"; public $stuff = "stuff";
@ -25,6 +26,7 @@ class foo
} }
} }
#[AllowDynamicProperties]
class foo_noargs class foo_noargs
{ {
public $stuff = "stuff"; public $stuff = "stuff";

View file

@ -5,6 +5,7 @@ Test for fetch_object
--FILE-- --FILE--
<?php <?php
#[AllowDynamicProperties]
class foo class foo
{ {
public $stuff = "stuff"; public $stuff = "stuff";
@ -25,6 +26,7 @@ class foo
} }
} }
#[AllowDynamicProperties]
class foo_noargs class foo_noargs
{ {
public $stuff = "stuff"; public $stuff = "stuff";

View file

@ -4,6 +4,7 @@ Test insert various data types and fetch as strings
<?php <?php
require_once('MsCommon.inc'); require_once('MsCommon.inc');
#[AllowDynamicProperties]
class TestClass2 class TestClass2
{ {
public function __construct($a1, $a2) public function __construct($a1, $a2)
@ -12,6 +13,7 @@ class TestClass2
} }
} }
#[AllowDynamicProperties]
class TestClass3 class TestClass3
{ {
public function __construct($a1, $a2, $a3) public function __construct($a1, $a2, $a3)

View file

@ -5,6 +5,7 @@ Test for fetch_object with Unicode column name
--FILE-- --FILE--
<?php <?php
#[AllowDynamicProperties]
class foo class foo
{ {
public $stuff = "stuff"; public $stuff = "stuff";
@ -25,6 +26,7 @@ class foo
} }
} }
#[AllowDynamicProperties]
class foo_noargs class foo_noargs
{ {
public $stuff = "stuff"; public $stuff = "stuff";

View file

@ -6,6 +6,7 @@ sqlsrv_fetch_object() into a class with Unicode column name
<?php <?php
// Define the Product class // Define the Product class
#[AllowDynamicProperties]
class Product class Product
{ {
public function __construct($ID, $UID) public function __construct($ID, $UID)
@ -37,6 +38,7 @@ class Product
} }
} }
#[AllowDynamicProperties]
class Sample extends Product class Sample extends Product
{ {
public function __construct($ID) public function __construct($ID)

View file

@ -0,0 +1,34 @@
--TEST--
Test for Github Issue 1448
--DESCRIPTION--
Prepare and execute with int, then execute with string caused "Invalid character value for cast specification" error.
Repro script provided by thsmrtone1
--FILE--
<?php
sqlsrv_configure( 'WarningsReturnAsErrors', 0 );
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
require_once( 'MsCommon.inc' );
$conn = Connect();
if( !$conn ) {
var_dump( sqlsrv_errors() );
die( "sqlsrv_connect failed." );
}
sqlsrv_query($conn, "CREATE TABLE test1448 (testCol nvarchar(50) NULL)");
$v0 = 1000;
$stmt = sqlsrv_prepare($conn, 'INSERT INTO [test1448] (testCol) VALUES (?);', [&$v0]);
sqlsrv_execute($stmt);
$v0 = 'abcd';
sqlsrv_execute($stmt);
$error = sqlsrv_errors(SQLSRV_ERR_ERRORS);
var_dump($error);
dropTable($conn, "test1448");
sqlsrv_close($conn);
?>
--EXPECT--
NULL

View file

@ -4,6 +4,8 @@ Send a large amount (10MB) using encryption. In a Linux CI environment use a sma
<?php require('skipif_azure_dw.inc'); ?> <?php require('skipif_azure_dw.inc'); ?>
--FILE-- --FILE--
<?php <?php
#[AllowDynamicProperties]
class my_stream class my_stream
{ {
public $total_read = 0; public $total_read = 0;