Merge pull request #1197 from microsoft/dev

5.9.0-preview1
This commit is contained in:
Jenny Tam 2020-10-06 14:34:30 -07:00 committed by GitHub
commit d92cc3d58d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
264 changed files with 6704 additions and 3491 deletions

View file

@ -3,13 +3,54 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
## 5.9.0-preview1 - 2020-10-02
Updated PECL release packages. Here is the list of updates:
### Added
- Support for PHP 8.0 RC 1
- Support for Ubuntu 20.04 and Alpine 3.12
- Support for GB18030 locale [#1115](
https://github.com/microsoft/msphpsql/pull/1115)
- Feature Request [#924](https://github.com/microsoft/msphpsql/issues/924) - extended PDO errorinfo to include [additional odbc messages if available](https://github.com/microsoft/msphpsql/wiki/Features#pdoErrorInfo) - pull request [#1133](
https://github.com/microsoft/msphpsql/pull/1133)
- [Data Classification with rank info](https://github.com/microsoft/msphpsql/wiki/Features#dataClass), which requires [MS ODBC Driver 17.4.2+](https://docs.microsoft.com/sql/connect/odbc/download-odbc-driver-for-sql-server?view=sql-server-ver15) and [SQL Server 2019](https://www.microsoft.com/sql-server/sql-server-2019) or an Azure SQL instance that supports it
### Removed
- Dropped support for Ubuntu 19.10 and Debian 8.
### Fixed
- Pull Request [#1127](https://github.com/microsoft/msphpsql/pull/1127) - removal of TSRMLS macros in preparation for PHP 8 by remicollet
- Pull Request [#1136](https://github.com/microsoft/msphpsql/pull/1136) - improved performance when handling decimal numbers as inputs or outputs and removed unncessary conversions for numeric values
- Pull Request [#1143](https://github.com/microsoft/msphpsql/pull/1143) - if an exception occurs when executing a query, will not change the output parameters
- Pull Request [#1144](https://github.com/microsoft/msphpsql/pull/1144) - use the correct C types when binding output parameters with integer values
- Pull Request [#1146](https://github.com/microsoft/msphpsql/pull/1146) - improved performance when fetching numbers using client buffers
- Pull Request [#1165](https://github.com/microsoft/msphpsql/pull/1165) - setting query timeout without using LOCK TIMEOUT, which saves an extra trip to the server
- Issue [#1170](https://github.com/microsoft/msphpsql/issues/1170) - when fetching large data types such as ntext will check more than only the display size - pull request [#1172](https://github.com/microsoft/msphpsql/pull/1172)
### 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 preview 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.8.1 - 2020-04-15
Updated PECL release packages. Here is the list of updates:
### Fixed
- Pull Request [#1094](https://github.com/microsoft/msphpsql/pull/1094) - Fixed default locale issues in Alpine Linux
- Pull Request [#1095](https://github.com/microsoft/msphpsql/pull/1095) - Removed unnecessary data structure to support Client-Side Cursors feature in Alpine Linux
- Pull Request [#1095](https://github.com/microsoft/msphpsql/pull/1107) - Fixed logging issues when both drivers are enabled in Alpine Linux
- Pull Request [#1107](https://github.com/microsoft/msphpsql/pull/1107) - Fixed logging issues when both drivers are enabled in Alpine Linux
### Limitations
- No support for inout / output params when using sql_variant type

View file

@ -42,8 +42,11 @@ ENV PATH="/opt/mssql-tools/bin:${PATH}"
# add locales for testing
RUN sed -i 's/# en_US ISO-8859-1/en_US ISO-8859-1/g' /etc/locale.gen
RUN sed -i 's/# fr_FR@euro ISO-8859-15/fr_FR@euro ISO-8859-15/g' /etc/locale.gen
RUN sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
RUN sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
RUN sed -i 's/# zh_CN GB2312/zh_CN GB2312/g' /etc/locale.gen
RUN sed -i 's/# zh_CN.GB18030 GB18030/zh_CN.GB18030 GB18030/g' /etc/locale.gen
RUN locale-gen
# set locale to utf-8

View file

@ -1,7 +1,7 @@
# Linux and macOS Installation Tutorial for the Microsoft Drivers for PHP for SQL Server
The following instructions assume a clean environment and show how to install PHP 7.x, the Microsoft ODBC driver, the Apache web server, and the Microsoft Drivers for PHP for SQL Server on Ubuntu 16.04, 18.04, and 19.10, RedHat 7 and 8, Debian 8, 9, and 10, Suse 12 and 15, Alpine 3.11, and macOS 10.13, 10.14, and 10.15. These instructions advise installing the drivers using PECL, but you can also download the prebuilt binaries from the [Microsoft Drivers for PHP for SQL Server](https://github.com/Microsoft/msphpsql/releases) Github project page and install them following the instructions in [Loading the Microsoft Drivers for PHP for SQL Server](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver). For an explanation of extension loading and why we do not add the extensions to php.ini, see the section on [loading the drivers](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver#loading-the-driver-at-php-startup).
The following instructions assume a clean environment and show how to install PHP 7.2+, the Microsoft ODBC driver, the Apache web server, and the Microsoft Drivers for PHP for SQL Server on Ubuntu 16.04, 18.04, and 20.04, RedHat 7 and 8, Debian 9 and 10, Suse 12 and 15, Alpine 3.11 and 3.12, and macOS 10.13, 10.14, and 10.15. These instructions advise installing the drivers using PECL, but you can also download the prebuilt binaries from the [Microsoft Drivers for PHP for SQL Server](https://github.com/Microsoft/msphpsql/releases) Github project page and install them following the instructions in [Loading the Microsoft Drivers for PHP for SQL Server](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver). For an explanation of extension loading and why we do not add the extensions to php.ini, see the section on [loading the drivers](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver#loading-the-driver-at-php-startup).
These instructions install PHP 7.4 by default using `pecl install`. You may need to run `pecl channel-update pecl.php.net` first. Note that some supported Linux distros default to PHP 7.1 or earlier, which is not supported for the latest version of the PHP drivers for SQL Server -- please see the notes at the beginning of each section to install PHP 7.2 or 7.3 instead.
The following instructions install PHP 7.4 by default using `pecl install`. You may need to run `pecl channel-update pecl.php.net` first. Note that some supported Linux distros default to PHP 7.1 or earlier, which is not supported for the latest version of the PHP drivers for SQL Server -- please see the notes at the beginning of each section to install PHP 7.2 or 7.3 instead.
Also included are instructions for installing the PHP FastCGI Process Manager, PHP-FPM, on Ubuntu. This is needed if using the nginx web server instead of Apache.
@ -10,9 +10,9 @@ Also included are instructions for installing the PHP FastCGI Process Manager, P
- [Installing the drivers on Ubuntu 16.04, 18.04, and 19.10](#installing-the-drivers-on-ubuntu-1604-1804-and-1910)
- [Installing the drivers with PHP-FPM on Ubuntu](#installing-the-drivers-with-php-fpm-on-ubuntu)
- [Installing the drivers on Red Hat 7 and 8](#installing-the-drivers-on-red-hat-7-and-8)
- [Installing the drivers on Debian 8, 9, and 10](#installing-the-drivers-on-debian-8-9-and-10)
- [Installing the drivers on Debian 9 and 10](#installing-the-drivers-on-debian-9-and-10)
- [Installing the drivers on Suse 12 and 15](#installing-the-drivers-on-suse-12-and-15)
- [Installing the drivers on Alpine 3.11](#installing-the-drivers-on-alpine-311)
- [Installing the drivers on Alpine 3.11 and 3.12](#installing-the-drivers-on-alpine-311-and-312)
- [Installing the drivers on macOS High Sierra, Mojave, and Catalina](#installing-the-drivers-on-macos-high-sierra-mojave-and-catalina)
## Installing the drivers on Ubuntu 16.04, 18.04, and 19.10
@ -189,7 +189,7 @@ sudo apachectl restart
```
To test your installation, see [Testing your installation](#testing-your-installation) at the end of this document.
## Installing the drivers on Debian 8, 9, and 10
## Installing the drivers on Debian 9 and 10
> [!NOTE]
> To install PHP 7.2 or 7.3, replace 7.4 in the following commands with 7.2 or 7.3.
@ -290,13 +290,13 @@ sudo systemctl restart apache2
```
To test your installation, see [Testing your installation](#testing-your-installation) at the end of this document.
## Installing the drivers on Alpine 3.11
## Installing the drivers on Alpine 3.11 and 3.12
> [!NOTE]
> The default version of PHP is 7.3. Alternate versions of PHP may be available from other repositories for Alpine 3.11. You can instead compile PHP from source.
> The default version of PHP is 7.3. Alternate versions of PHP may be available from other repositories for Alpine. You can instead compile PHP from source.
### Step 1. Install PHP
PHP packages for Alpine can be found in the `edge/community` repository. Please check [Enable Community Repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository) on their WIKI page. Add the following line to `/etc/apt/repositories`, replacing `<mirror>` with the URL of an Alpine repository mirror:
PHP packages for Alpine can be found in the `edge/community` repository. Please check [Enable Community Repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository) on their WIKI page. Add the following line to `/etc/apk/repositories`, replacing `<mirror>` with the URL of an Alpine repository mirror:
```
http://<mirror>/alpine/edge/community
```

View file

@ -35,8 +35,6 @@ Azure Pipelines | AppVeyor (Windows) | Travis CI (Linux) | Co
* [**RedHat + SQL Server + PHP 7**](https://www.microsoft.com/sql-server/developer-get-started/php/rhel)
* [**SUSE + SQL Server + PHP 7**](https://www.microsoft.com/sql-server/developer-get-started/php/sles)
* [**macOS + SQL Server + PHP 7**](https://www.microsoft.com/sql-server/developer-get-started/php/mac/)
* [**Docker**](https://hub.docker.com/r/lbosqmsft/mssql-php-msphpsql/)
## Announcements

View file

@ -1,10 +1,6 @@
version: '{branch}.{build}'
branches:
# whitelist
#only:
# blacklist
except:
- PHP-7.0-Linux
- PHP5
@ -22,19 +18,19 @@ environment:
BUILD_PLATFORM: x64
TEST_PHP_SQL_SERVER: (local)\SQL2017
SQL_INSTANCE: SQL2017
PHP_VC: 15
PHP_MAJOR_VER: 7.3
PHP_VC: vc15
PHP_MAJOR_VER: 7.4
PHP_MINOR_VER: latest
PHP_EXE_PATH: x64\Release_TS
THREAD: ts
platform: x64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
BUILD_PLATFORM: x86
TEST_PHP_SQL_SERVER: (local)\SQL2016
SQL_INSTANCE: SQL2016
PHP_VC: 15
PHP_MAJOR_VER: 7.4
PHP_MINOR_VER: latest
TEST_PHP_SQL_SERVER: (local)\SQL2019
SQL_INSTANCE: SQL2019
PHP_VC: vs16
PHP_MAJOR_VER: 8.0
PHP_MINOR_VER: 0rc1
PHP_EXE_PATH: Release
THREAD: nts
platform: x86
@ -83,7 +79,7 @@ install:
}
- echo Downloading MSODBCSQL 17
# AppVeyor build works are x64 VMs and 32-bit ODBC driver cannot be installed on it
- ps: (new-object net.webclient).DownloadFile('https://download.microsoft.com/download/E/6/B/E6BFDC7A-5BCD-4C51-9912-635646DA801E/en-US/17.5.2.1/x64/msodbcsql.msi', 'c:\projects\msodbcsql.msi')
- ps: (new-object net.webclient).DownloadFile('https://download.microsoft.com/download/6/b/3/6b3dd05c-678c-4e6b-b503-1d66e16ef23d/en-US/17.6.1.1/x64/msodbcsql.msi', 'c:\projects\msodbcsql.msi')
- cmd /c start /wait msiexec /i "c:\projects\msodbcsql.msi" /q IACCEPTMSODBCSQLLICENSETERMS=YES ADDLOCAL=ALL
- echo Checking the version of MSODBCSQL
- reg query "HKLM\SOFTWARE\ODBC\odbcinst.ini\ODBC Driver 17 for SQL Server"
@ -99,13 +95,14 @@ install:
- echo install opencppcoverage
- choco install opencppcoverage
- set path=C:\Program Files\OpenCppCoverage;%PYTHON%;%PYTHON%\Scripts;%path%
- copy %APPVEYOR_BUILD_FOLDER%\codecov.yml c:\projects
build_script:
- copy %APPVEYOR_BUILD_FOLDER%\buildscripts\*.py c:\projects
- cd c:\projects
- python -V
- python builddrivers.py --PHPVER=%PHP_VERSION% --ARCH=%BUILD_PLATFORM% --THREAD=%THREAD% --SOURCE=%APPVEYOR_BUILD_FOLDER%\source --TESTING --NO_RENAME
- cd c:\projects\php-sdk\phpdev\vc%PHP_VC%\%BUILD_PLATFORM%\php-%PHP_VERSION%-src\
- cd c:\projects\php-sdk\phpdev\%PHP_VC%\%BUILD_PLATFORM%\php-%PHP_VERSION%-src\
- set PHP_SRC_DIR=%CD%\ext
- cd %PHP_EXE_PATH%
- set PHP_EXE_PATH=%CD%
@ -122,7 +119,7 @@ test_script:
- ps: >-
If ($env:BUILD_PLATFORM -Match "x86") {
Write-Host "Running phpt tests via OpenCppCoverage..."
OpenCppCoverage.exe --sources ${env:PHP_SRC_DIR}\*sqlsrv --modules ${env:PHP_EXE_PATH}\php*sqlsrv.dll --export_type=cobertura:c:\projects\coverage.xml --quiet --cover_children --continue_after_cpp_exception --optimized_build -- .\php.exe run-tests.php -P ${env:APPVEYOR_BUILD_FOLDER}\test\functional\ | out-file -filePath ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -encoding UTF8;
OpenCppCoverage.exe --sources ${env:PHP_SRC_DIR}\*sqlsrv --modules ${env:PHP_EXE_PATH}\php*sqlsrv.dll --excluded_sources ${env:PHP_SRC_DIR}\pdo_sqlsrv\shared\core_stream.cpp --export_type=cobertura:c:\projects\coverage.xml --quiet --cover_children --continue_after_cpp_exception --optimized_build -- .\php.exe run-tests.php -P --no-color ${env:APPVEYOR_BUILD_FOLDER}\test\functional\ | out-file -filePath ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -encoding UTF8;
Write-Host "Showing the last 25 lines of the log file..."
Get-Content ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -Tail 25;
ls *.xml

View file

@ -111,8 +111,11 @@ jobs:
- script: |
sudo sed -i 's/# en_US ISO-8859-1/en_US ISO-8859-1/g' /etc/locale.gen
sudo sed -i 's/# fr_FR@euro ISO-8859-15/fr_FR@euro ISO-8859-15/g' /etc/locale.gen
sudo sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
sudo sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
sudo sed -i 's/# zh_CN GB2312/zh_CN GB2312/g' /etc/locale.gen
sudo sed -i 's/# zh_CN.GB18030 GB18030/zh_CN.GB18030 GB18030/g' /etc/locale.gen
sudo locale-gen
export LANG='en_US.UTF-8'
export LANGUAGE='en_US:en'

View file

@ -5,16 +5,16 @@
To build extensions for
1. PHP 7.0* or PHP 7.1*
* install Visual Studio 2015 and make sure C++ tools are enabled.
2. PHP 7.2*
* install Visual Studio 2017, including Visual C++ toolset and the Windows SDK components.
2. PHP 7.2* or above
* install Visual Studio 2017 (PHP 7.*) or Visual Studio 2019 (PHP 8.*), including Visual C++ toolset and the Windows SDK components.
To use the sample build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x and Git for Windows (which comes with Visual Studio 2017). If `git` is unrecognized in a regular command prompt, make sure the environment path is set up correctly.
To use the sample build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x and Git for Windows (which comes with Visual Studio 2017 or 2019). If `git` is unrecognized in a regular command prompt, make sure the environment path is set up correctly.
## Compile the drivers
You must first be able to build PHP 7.* without including our PHP extensions. For help with building PHP 7.0* or PHP 7.1* in Windows, see the [official PHP website](https://wiki.php.net/internals/windows/stepbystepbuild). For PHP 7.2 or above, visit [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for new instructions.
You must first be able to build PHP source without including our PHP extensions. For help with building PHP 7.0* or PHP 7.1* in Windows, see the [official PHP website](https://wiki.php.net/internals/windows/stepbystepbuild). For PHP 7.2 or above, visit [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for new instructions.
The Microsoft Drivers for PHP for SQL Server have been compiled and tested with PHP 7.0.* and 7.1.* using Visual C++ 2015 as well as PHP 7.2.1 using Visual C++ 2017 v15.5.
The Microsoft Drivers for PHP for SQL Server have been compiled and tested with PHP 7.2+ using Visual Studio 2017 and PHP 8.0 previews using Visual Studio 2019. The drivers for Windows that are published for each release (including previews) are digitally signed. You are recommended to sign the binaries you have compiled locally for your own development or testing purposes, using tools like Authenticode. It verifies the publisher's identity and prevents malicious actors from posing as legitimate developers.
### Manually building from source
@ -45,7 +45,7 @@ The sample build scripts, `builddrivers.py` and `buildtools.py`, can be used to
#### Overview
When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you will enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names.
When asked to provide the PHP version, you should enter values like `7.3.17`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 8.0.0 beta 3, the tag name is `php-8.0.0beta3`, so you will enter `8.0.0beta3`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names.
PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will, by default, create a `php-sdk` folder in the C:\ drive, and this `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available.
@ -57,7 +57,7 @@ PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably
3. Interactive mode:
* Type `py builddrivers.py` to start the interactive mode. Use lower cases to answer the following questions:
* PHP Version (e.g. `7.1.7` or `7.2.1`)
* PHP Version
* 64-bit?
* Thread safe?
* Driver?
@ -68,8 +68,8 @@ PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably
4. Use Command-line arguments
* Type `py builddrivers.py -h` to get a list of options and their descriptions
* For example,
* `py builddrivers.py --PHPVER=7.2.1 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --SOURCE=C:\local\source`
* `py builddrivers.py --PHPVER=7.1.13 --ARCH=x86 --THREAD=ts --DEBUG`
* `py builddrivers.py --PHPVER=7.4.10 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --SOURCE=C:\local\source`
* `py builddrivers.py --PHPVER=7.2.30 --ARCH=x86 --THREAD=ts --DEBUG`
5. Based on the given configuration, if the script detects the presence of the PHP source directory, you can choose whether to rebuild, clean or superclean:
* `rebuild` to build again using the same configuration (32 bit, thread safe, etc.)

View file

@ -26,7 +26,6 @@ import os.path
import argparse
import subprocess
from buildtools import BuildUtil
from indexsymbols import *
class BuildDriver(object):
"""Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties:
@ -40,8 +39,6 @@ class BuildDriver(object):
make_clean # a boolean flag - whether make clean is necessary
source_path # path to a local source folder
testing # whether the user has turned on testing mode
srctool_path # path to source indexing tools (empty string by default)
tag_version # tag version for source indexing (empty string by default)
"""
def __init__(self, phpver, driver, arch, thread, debug, repo, branch, source, path, testing, no_rename):
@ -53,8 +50,6 @@ class BuildDriver(object):
self.testing = testing
self.rebuild = False
self.make_clean = False
self.srctool_path = ''
self.tag_version = ''
def show_config(self):
print()
@ -118,34 +113,6 @@ class BuildDriver(object):
print("The path provided is invalid. Please re-enter.")
return source
def index_all_symbols(self, ext_dir, srctool_path, tag_version):
"""This takes care of indexing all the symbols
:param ext_dir: the directory where we can find the built extension(s)
:param srctool_path: the path to the tools for source indexing
:param tag_version: tag version for source indexing
:outcome: all symbols will be source indexed
"""
work_dir = os.path.dirname(os.path.realpath(__file__))
os.chdir(srctool_path)
if self.util.driver == 'all':
driver = 'sqlsrv'
pdbfile = os.path.join(ext_dir, self.util.driver_name(driver, '.pdb'))
print('Indexing this symbol: ', pdbfile)
run_indexing_tools(pdbfile, driver, tag_version)
driver = 'pdo_sqlsrv'
pdbfile = os.path.join(ext_dir, self.util.driver_name(driver, '.pdb'))
print('Indexing this symbol: ', pdbfile)
run_indexing_tools(pdbfile, driver, tag_version)
else:
driver = self.util.driver
pdbfile = os.path.join(ext_dir, self.util.driver_name(driver, '.pdb'))
print('Indexing this symbol: ', pdbfile)
run_indexing_tools(pdbfile, driver, tag_version)
os.chdir(work_dir)
def build_extensions(self, root_dir, logfile):
"""This takes care of getting the drivers' source files, building the drivers.
If dest_path is defined, the binaries will be copied to the designated destinations.
@ -185,12 +152,6 @@ class BuildDriver(object):
# ext_dir is the directory where we can find the built extension(s)
ext_dir = self.util.build_drivers(self.make_clean, dest, logfile)
# Do source indexing only if the tag and tools path are both specified
if self.tag_version is not '' and self.srctool_path is not '':
print('Source indexing begins...')
self.index_all_symbols(ext_dir, self.srctool_path, self.tag_version)
print('Source indexing done')
# Copy the binaries if a destination path is defined
if self.dest_path is not None:
dest_drivers = os.path.join(self.dest_path, self.util.major_version(), self.util.arch)
@ -212,12 +173,10 @@ class BuildDriver(object):
return ext_dir
def build(self, srctool_path, tag_version):
def build(self):
"""This is the main entry point of building drivers for PHP.
For development, this will loop till the user decides to quit.
:param srctool_path: the path to the tools for source indexing
:param tag_version: tag version for source indexing
"""
self.show_config()
@ -234,10 +193,6 @@ class BuildDriver(object):
logfile = self.util.get_logfile_name()
# Save source indexing details
self.srctool_path = srctool_path
self.tag_version = tag_version
try:
ext_dir = self.build_extensions(root_dir, logfile)
print('Build Completed')
@ -280,7 +235,7 @@ def validate_input(question, values):
################################### Main Function ###################################
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--PHPVER', help="PHP version, e.g. 7.1.*, 7.2.* etc.")
parser.add_argument('--PHPVER', help="PHP version, e.g. 7.4.* etc.")
parser.add_argument('--ARCH', choices=['x64', 'x86'])
parser.add_argument('--THREAD', choices=['nts', 'ts'])
parser.add_argument('--DRIVER', default='all', choices=['all', 'sqlsrv', 'pdo_sqlsrv'], help="driver to build (default: all)")
@ -291,8 +246,6 @@ if __name__ == '__main__':
parser.add_argument('--TESTING', action='store_true', help="turns on testing mode (default: False)")
parser.add_argument('--DESTPATH', default=None, help="an alternative destination for the drivers (default: None)")
parser.add_argument('--NO_RENAME', action='store_true', help="drivers will not be renamed(default: False)")
parser.add_argument('--SRCIDX_PATH', default='', help="the path to the tools for source indexing (default: '')")
parser.add_argument('--TAG_VERSION', default='', help="the tag version for source indexing (default: '')")
args = parser.parse_args()
@ -354,4 +307,5 @@ if __name__ == '__main__':
path,
testing,
no_rename)
builder.build(args.SRCIDX_PATH, args.TAG_VERSION)
builder.build()

View file

@ -50,12 +50,8 @@ class BuildUtil(object):
def version_label(self):
"""Return the version label based on the PHP version."""
major_ver = self.major_version()
if major_ver[2] == '0':
version = major_ver[0]
else:
version = major_ver[0] + major_ver[2]
major_ver = self.major_version()
version = major_ver[0] + major_ver[2]
return version
def driver_name(self, driver, suffix):
@ -100,16 +96,11 @@ class BuildUtil(object):
def compiler_version(self, sdk_dir):
"""Return the appropriate compiler version based on PHP version."""
if self.vc is '':
VC = 'vc14'
if self.vc == '':
VC = 'vc15'
version = self.version_label()
if version >= '72': # Compiler version for PHP 7.2 or above
VC = 'vc15'
if version == '74':
# Compiler version for PHP 7.4 or above
# Can be compiled using VS 2017 or VS 2019
print('Checking compiler versions...')
VC = self.determine_compiler(sdk_dir, 15)
if version == '80': # Compiler version for PHP 8.0 or above
VC = 'vs16'
self.vc = VC
print('Compiler: ' + self.vc)
return self.vc

View file

@ -4,7 +4,7 @@ dnl
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
dnl Microsoft Drivers 5.8 for PHP for SQL Server
dnl Microsoft Drivers 5.9 for PHP for SQL Server
dnl Copyright(c) Microsoft Corporation
dnl All rights reserved.
dnl MIT License

View file

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

View file

@ -3,7 +3,7 @@
//
// Contents: Implements the PDO object for PDO_SQLSRV
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -113,19 +113,18 @@ const stmt_option PDO_STMT_OPTS[] = {
// boolean connection string
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 TSRMLS_DC );
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
{
static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC );
static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ );
};
struct pdo_int_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC )
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{
TSRMLS_C;
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
std::string val_str = Z_STRVAL_P( value );
@ -140,14 +139,14 @@ struct pdo_int_conn_str_func {
template <unsigned int Attr>
struct pdo_int_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
try {
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 )) );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER );
}
catch( core::CoreException& ) {
throw;
@ -158,12 +157,12 @@ struct pdo_int_conn_attr_func {
template <unsigned int Attr>
struct pdo_bool_conn_attr_func {
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( core_str_zval_is_true( value )),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
}
catch( core::CoreException& ) {
throw;
@ -173,8 +172,8 @@ struct pdo_bool_conn_attr_func {
// statement options related functions
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval** data TSRMLS_DC );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC );
_Inout_ zval** data );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht );
} // namespace
@ -438,35 +437,35 @@ const connection_option PDO_CONN_OPTS[] = {
// close the connection
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh );
// execute queries
int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql,
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC );
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC );
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options );
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len );
// transaction support functions
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh );
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh );
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh );
// attribute functions
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC );
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC );
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val );
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value );
// return more information
int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
_Out_ zval *info TSRMLS_DC);
_Out_ zval *info);
// return the last id generated by an executed SQL statement
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC );
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len );
// additional methods are supported in this function
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC );
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind );
// quote a string, meaning put quotes around it and escape any quotes within it
int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen,
enum pdo_param_type paramtype TSRMLS_DC );
enum pdo_param_type paramtype );
struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
@ -498,8 +497,8 @@ struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
}
// constructor for the internal object for connections
pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC ) :
sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ),
pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver ) :
sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 ),
stmts( NULL ),
direct_query( false ),
query_timeout( QUERY_TIMEOUT_INVALID ),
@ -532,7 +531,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.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options TSRMLS_DC)
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options)
{
PDO_LOG_DBH_ENTRY;
@ -570,12 +569,12 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
ALLOC_HASHTABLE( pdo_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
ZVAL_PTR_DTOR, 0 /*persistent*/ );
// 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,
static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht );
dsn_parser->parse_conn_string( TSRMLS_C );
dsn_parser->parse_conn_string();
// Extract the server name
temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );
@ -593,7 +592,7 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
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,
PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC );
PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" );
// Free the string in server_z after being used
zend_string_release( Z_STR( server_z ));
@ -634,7 +633,7 @@ int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_
// dbh - The PDO managed connection object.
// Return:
// Always returns 1 for success.
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh )
{
LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" );
@ -647,7 +646,7 @@ int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
PDO_RESET_DBH_ERROR;
// call the core layer close
core_sqlsrv_close( reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ) TSRMLS_CC );
core_sqlsrv_close( reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ) );
dbh->driver_data = NULL;
// always return success that the connection is closed
@ -666,7 +665,7 @@ int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql,
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC )
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -692,14 +691,14 @@ int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const ch
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_stmt_options_ht );
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
ZVAL_PTR_DTOR, 0 /*persistent*/ );
// 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 TSRMLS_CC );
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>,
pdo_stmt_options_ht, PDO_STMT_OPTS,
pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC ));
pdo_sqlsrv_handle_stmt_error, stmt ));
// if the user didn't set anything in the prepare options, then set the buffer limit
// to the value set on the connection.
@ -714,7 +713,7 @@ int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const ch
// rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named
// parameters for consistency with the PDO MySQL and PDO ODBC drivers.
int zr = pdo_parse_params( stmt, const_cast<char*>( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC );
int zr = pdo_parse_params( stmt, const_cast<char*>( sql ), sql_len, &sql_rewrite, &sql_rewrite_len );
CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) {
throw core::CoreException();
@ -728,7 +727,7 @@ int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const ch
if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) {
core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC );
core_sqlsrv_prepare( driver_stmt, sql, sql_len );
}
else if( driver_stmt->direct_query ) {
@ -745,10 +744,10 @@ int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const ch
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
// parse placeholders in the sql query into the placeholders ht
ALLOC_HASHTABLE( placeholders );
core::sqlsrv_zend_hash_init( *driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */ );
sql_parser = new ( sqlsrv_malloc( sizeof( sql_string_parser ))) sql_string_parser( *driver_dbh, stmt->query_string,
static_cast<int>(stmt->query_stringlen), placeholders );
sql_parser->parse_sql_string( TSRMLS_C );
sql_parser->parse_sql_string();
driver_stmt->placeholders = placeholders;
placeholders.transferred();
}
@ -796,7 +795,7 @@ int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const ch
// sql_len - length of sql query
// Return
// # of rows affected, -1 for an error.
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC )
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -821,23 +820,23 @@ zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) c
temp_stmt.dbh = dbh;
// 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*/,
NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC );
NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt );
driver_stmt->set_func( __FUNCTION__ );
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast<int>( sql_len ) );
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt, sql, static_cast<int>( sql_len ) );
// since the user can give us a compound statement, we return the row count for the last set, and since the row count
// isn't guaranteed to be valid until all the results have been fetched, we fetch them all first.
if ( execReturn != SQL_NO_DATA && core_sqlsrv_has_any_result( driver_stmt TSRMLS_CC )) {
if ( execReturn != SQL_NO_DATA && core_sqlsrv_has_any_result( driver_stmt )) {
SQLRETURN r = SQL_SUCCESS;
do {
rows = core::SQLRowCount( driver_stmt TSRMLS_CC );
rows = core::SQLRowCount( driver_stmt );
r = core::SQLMoreResults( driver_stmt TSRMLS_CC );
r = core::SQLMoreResults( driver_stmt );
} while ( r != SQL_NO_DATA );
}
@ -885,7 +884,7 @@ zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) c
// dbh - The PDO managed connection object.
// Return:
// 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -901,7 +900,7 @@ int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" );
core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC );
core_sqlsrv_begin_transaction( driver_conn );
return 1;
}
@ -927,7 +926,7 @@ int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
// dbh - The PDO managed connection object.
// Return:
// 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -943,7 +942,7 @@ int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" );
core_sqlsrv_commit( driver_conn TSRMLS_CC );
core_sqlsrv_commit( driver_conn );
return 1;
}
@ -967,7 +966,7 @@ int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
// dbh - The PDO managed connection object.
// Return:
// 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -982,7 +981,7 @@ int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" );
core_sqlsrv_rollback( driver_conn TSRMLS_CC );
core_sqlsrv_rollback( driver_conn );
return 1;
}
@ -1006,7 +1005,7 @@ int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
// val - The value of the attribute to be set.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC )
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1169,7 +1168,7 @@ int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
// return_value - zval in which to return the attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC )
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1213,26 +1212,25 @@ int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
case PDO_ATTR_SERVER_INFO:
{
core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC );
core_sqlsrv_get_server_info( driver_dbh, return_value );
break;
}
case PDO_ATTR_SERVER_VERSION:
{
core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC );
core_sqlsrv_get_server_version( driver_dbh, return_value );
break;
}
case PDO_ATTR_CLIENT_VERSION:
{
core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC );
core_sqlsrv_get_client_info( driver_dbh, return_value );
//Add the PDO SQLSRV driver's file version
//Declarations below eliminate compiler warnings about string constant to char* conversions
const char* extver = "ExtensionVer";
std::string filever = VER_FILEVERSION_STR;
core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/
TSRMLS_CC );
add_assoc_string(return_value, extver, &filever[0]);
break;
}
@ -1315,7 +1313,7 @@ int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
_Out_ zval *info TSRMLS_DC)
_Out_ zval *info)
{
SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." );
@ -1341,7 +1339,7 @@ int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
// len - Length of the name.
// Return:
// Returns the last insert id as a string.
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC )
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1368,7 +1366,7 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
else {
char* quoted_table = NULL;
size_t quoted_len = 0;
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strnlen_s( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL TSRMLS_CC );
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strnlen_s( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL );
SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name.");
snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table );
sqlsrv_free( quoted_table );
@ -1379,7 +1377,7 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
temp_stmt.dbh = dbh;
// 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*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC );
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 );
driver_stmt->set_func( __FUNCTION__ );
@ -1392,11 +1390,11 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
}
// execute the last insert id query
core::SQLExecDirectW( driver_stmt, wsql_string TSRMLS_CC );
core::SQLExecDirectW( driver_stmt, wsql_string );
core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC );
core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 );
SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN,
reinterpret_cast<SQLLEN*>( len ), false TSRMLS_CC );
reinterpret_cast<SQLLEN*>( len ), false );
CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt,
PDO_SQLSRV_ERROR_LAST_INSERT_ID ) {
@ -1442,7 +1440,7 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const char* unquoted, _In_ size_t unquoted_len, _Outptr_result_buffer_(*quoted_len) char **quoted, _Out_ size_t* quoted_len,
enum pdo_param_type paramtype TSRMLS_DC )
enum pdo_param_type paramtype )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1602,7 +1600,7 @@ int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const
}
// This method is not implemented by this driver.
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC )
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1622,7 +1620,7 @@ namespace {
// Maps the PDO driver specific statement option/attribute constants to the core layer
// statement option/attribute constants.
void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval* data TSRMLS_DC)
_Inout_ zval* data)
{
zend_ulong option_key = -1;
switch (key) {
@ -1689,7 +1687,7 @@ void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ H
// if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it
if (option_key != -1) {
zval_add_ref(data);
core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC);
core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data);
}
}
@ -1702,7 +1700,7 @@ void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ H
// ctx - The current context.
// stmt_options - The user provided list 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 TSRMLS_DC )
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht )
{
try {
@ -1720,7 +1718,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
throw core::CoreException();
}
add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC );
add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data );
} ZEND_HASH_FOREACH_END();
}
}
@ -1731,9 +1729,8 @@ 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 TSRMLS_DC )
void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{
TSRMLS_C;
char const* val_str = "no";
if( core_str_zval_is_true( value ) ) {
@ -1748,7 +1745,7 @@ void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_
}
void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn,
std::string& /*conn_str*/ TSRMLS_DC )
std::string& /*conn_str*/ )
{
try {
@ -1800,7 +1797,7 @@ void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/
}
}
core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER );
}
catch( core::CoreException& ) {

View file

@ -3,7 +3,7 @@
//
// Contents: initialization routines for PDO_SQLSRV
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -53,8 +53,8 @@ pdo_driver_t pdo_sqlsrv_driver = {
// functions to register SQLSRV constants with the PDO class
// (It's in all CAPS so it looks like the Zend macros that do similar work)
void REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( _In_z_ char const* name, _In_ long value TSRMLS_DC );
void REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( _In_z_ char const* name, _In_z_ char const* value TSRMLS_DC );
void REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( _In_z_ char const* name, _In_ long value );
void REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( _In_z_ char const* name, _In_z_ char const* value );
struct sqlsrv_attr_pdo_constant {
const char *name;
@ -154,18 +154,18 @@ PHP_MINIT_FUNCTION(pdo_sqlsrv)
// register all attributes supported by this driver.
for( int i= 0; pdo_attr_constants[i].name != NULL; ++i ) {
REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( pdo_attr_constants[i].name, pdo_attr_constants[i].value TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( pdo_attr_constants[i].name, pdo_attr_constants[i].value );
}
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_READ_UNCOMMITTED", PDOTxnIsolationValues::READ_UNCOMMITTED TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_READ_COMMITTED", PDOTxnIsolationValues::READ_COMMITTED TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_REPEATABLE_READ", PDOTxnIsolationValues::REPEATABLE_READ TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_SERIALIZABLE", PDOTxnIsolationValues::SERIALIZABLE TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_SNAPSHOT", PDOTxnIsolationValues::SNAPSHOT TSRMLS_CC );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_READ_UNCOMMITTED", PDOTxnIsolationValues::READ_UNCOMMITTED );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_READ_COMMITTED", PDOTxnIsolationValues::READ_COMMITTED );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_REPEATABLE_READ", PDOTxnIsolationValues::REPEATABLE_READ );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_SERIALIZABLE", PDOTxnIsolationValues::SERIALIZABLE );
REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( "SQLSRV_TXN_SNAPSHOT", PDOTxnIsolationValues::SNAPSHOT );
// retrieve the handles for the environments
core_sqlsrv_minit( &g_pdo_henv_cp, &g_pdo_henv_ncp, pdo_sqlsrv_handle_env_error, "PHP_MINIT_FUNCTION for pdo_sqlsrv" TSRMLS_CC );
core_sqlsrv_minit( &g_pdo_henv_cp, &g_pdo_henv_ncp, pdo_sqlsrv_handle_env_error, "PHP_MINIT_FUNCTION for pdo_sqlsrv" );
}
catch( ... ) {
@ -274,27 +274,20 @@ namespace {
// mimic the functionality of the REGISTER_PDO_CLASS_CONST_LONG. We use this instead of the macro because
// we dynamically link the pdo_get_dbh_class function rather than use the static php_pdo_get_dbh_ce (see MINIT)
void REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( _In_z_ char const* name, _In_ long value TSRMLS_DC )
void REGISTER_PDO_SQLSRV_CLASS_CONST_LONG( _In_z_ char const* name, _In_ long value )
{
zend_class_entry* zend_class = php_pdo_get_dbh_ce();
SQLSRV_ASSERT( zend_class != NULL, "REGISTER_PDO_SQLSRV_CLASS_CONST_LONG: php_pdo_get_dbh_ce failed");
int zr = zend_declare_class_constant_long( zend_class, const_cast<char*>( name ), strlen( name ), value TSRMLS_CC );
if( zr == FAILURE ) {
throw core::CoreException();
}
zend_declare_class_constant_long(zend_class, const_cast<char*>(name), strlen(name), value);
}
void REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( _In_z_ char const* name, _In_z_ char const* value TSRMLS_DC )
void REGISTER_PDO_SQLSRV_CLASS_CONST_STRING( _In_z_ char const* name, _In_z_ char const* value )
{
zend_class_entry* zend_class = php_pdo_get_dbh_ce();
SQLSRV_ASSERT( zend_class != NULL, "REGISTER_PDO_SQLSRV_CLASS_CONST_STRING: php_pdo_get_dbh_ce failed");
int zr = zend_declare_class_constant_string( zend_class, const_cast<char*>( name ), strlen( name ), const_cast<char*>( value ) TSRMLS_CC );
if( zr == FAILURE ) {
throw core::CoreException();
}
zend_declare_class_constant_string(zend_class, const_cast<char*>(name), strlen(name), const_cast<char*>(value));
}
// array of pdo constants.

View file

@ -5,7 +5,7 @@
//
// Copyright Microsoft Corporation
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -122,7 +122,7 @@ bool string_parser::discard_white_spaces()
}
// Add a key-value pair to the hashtable
void string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_ int len TSRMLS_DC )
void string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_ int len )
{
zval value_z;
ZVAL_UNDEF( &value_z );
@ -136,19 +136,19 @@ void string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_
ZVAL_STRINGL( &value_z, const_cast<char*>( value ), len );
}
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z TSRMLS_CC );
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
void sql_string_parser::add_key_int_value_pair( _In_ unsigned int value TSRMLS_DC ) {
void sql_string_parser::add_key_int_value_pair( _In_ unsigned int value ) {
zval value_z;
ZVAL_LONG( &value_z, value );
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z TSRMLS_CC );
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z );
}
// Validate a given DSN keyword.
void conn_string_parser::validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len TSRMLS_DC )
void conn_string_parser::validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len )
{
int new_len = discard_trailing_white_spaces( key, key_len );
@ -173,7 +173,7 @@ void conn_string_parser::validate_key( _In_reads_(key_len) const char *key, _Ino
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ) );
}
void conn_string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_ int len TSRMLS_DC )
void conn_string_parser::add_key_value_pair( _In_reads_(len) const char* value, _In_ int len )
{
// if the keyword is 'Authentication', check whether the user specified option is supported
bool valid = true;
@ -208,7 +208,7 @@ inline bool sql_string_parser::is_placeholder_char( char c )
}
// Primary function which parses the connection string/DSN.
void conn_string_parser:: parse_conn_string( TSRMLS_D )
void conn_string_parser:: parse_conn_string( void )
{
States state = FirstKeyValuePair; // starting state
int start_pos = -1;
@ -244,7 +244,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
}
}
this->validate_key( &( this->orig_str[start_pos] ), ( pos - start_pos ) TSRMLS_CC );
this->validate_key( &( this->orig_str[start_pos] ), ( pos - start_pos ) );
state = Value;
@ -261,7 +261,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
// if EOS encountered after 0 or more spaces OR semi-colon encountered.
if( !discard_white_spaces() || this->orig_str[pos] == ';' ) {
add_key_value_pair( NULL, 0 TSRMLS_CC );
add_key_value_pair( NULL, 0 );
if( this->is_eos() ) {
@ -323,7 +323,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
state = NextKeyValuePair;
}
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos TSRMLS_CC );
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos );
SQLSRV_ASSERT((( state == NextKeyValuePair ) || ( this->is_eos() )),
"conn_string_parser::parse_conn_string: Invalid state encountered " );
@ -338,7 +338,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
if( !next() ) {
// EOS
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos TSRMLS_CC );
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos );
break;
}
@ -365,7 +365,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
if( ! this->discard_white_spaces() ) {
//EOS
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos TSRMLS_CC );
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos );
break;
}
}
@ -373,7 +373,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
// if semi-colon than go to next key-value pair
if ( this->orig_str[pos] == ';' ) {
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos TSRMLS_CC );
add_key_value_pair( &( this->orig_str[start_pos] ), end_pos - start_pos );
state = NextKeyValuePair;
break;
}
@ -417,7 +417,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
}
// Primary function which parses out the named placeholders from a sql string.
void sql_string_parser::parse_sql_string( TSRMLS_D ) {
void sql_string_parser::parse_sql_string( void ) {
try {
int start_pos = -1;
while ( !this->is_eos() ) {
@ -447,7 +447,7 @@ void sql_string_parser::parse_sql_string( TSRMLS_D ) {
while ( is_placeholder_char( this->orig_str[pos] )) {
next();
}
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos TSRMLS_CC );
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos );
discard_white_spaces();
// if an '=' is right after a placeholder, it means the placeholder is for output parameters
// and emulate prepare does not support output parameters

View file

@ -3,7 +3,7 @@
//
// Contents: Implements the PDOStatement object for the PDO_SQLSRV
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -53,7 +53,7 @@ inline SQLSMALLINT pdo_fetch_ori_to_odbc_fetch_ori ( _In_ enum pdo_fetch_orienta
// Returns SQLSRV data type for a given PDO type. See pdo_param_type
// for list of supported pdo types.
SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _In_ enum pdo_param_type pdo_type TSRMLS_DC )
SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _In_ enum pdo_param_type pdo_type )
{
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");
@ -137,7 +137,7 @@ inline pdo_param_type sql_type_to_pdo_type( _In_ SQLSMALLINT sql_type )
// Calls core_sqlsrv_set_scrollable function to set cursor.
// PDO supports two cursor types: PDO_CURSOR_FWDONLY, PDO_CURSOR_SCROLL.
void set_stmt_cursors( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
void set_stmt_cursors( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{
if( Z_TYPE_P( value_z ) != IS_LONG ) {
@ -161,10 +161,10 @@ void set_stmt_cursors( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
}
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC );
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type );
}
void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{
if( Z_TYPE_P( value_z ) != IS_LONG ) {
@ -178,14 +178,14 @@ 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 ) );
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC );
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type );
return;
}
// Sets the statement encoding. Default encoding on the statement
// implies use the connection's encoding.
void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{
// validate the value
if( Z_TYPE_P( value_z ) != IS_LONG ) {
@ -280,20 +280,20 @@ zval convert_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_PHPTYPE sqlsrv_php_t
} // namespace
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt );
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt );
int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orientation ori,
_In_ zend_long offset TSRMLS_DC );
_In_ zend_long offset );
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 TSRMLS_DC );
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRMLS_DC );
_Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type );
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno );
int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees TSRMLS_DC );
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC );
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC );
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value TSRMLS_DC );
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees );
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val );
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value );
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value );
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt );
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt );
struct pdo_stmt_methods pdo_sqlsrv_stmt_methods = {
@ -311,40 +311,40 @@ 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 TSRMLS_DC )
void stmt_option_pdo_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
set_stmt_cursors( stmt, value_z TSRMLS_CC );
set_stmt_cursors( stmt, value_z );
}
void stmt_option_encoding:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_encoding:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
set_stmt_encoding( stmt, value_z TSRMLS_CC );
set_stmt_encoding( stmt, value_z );
}
void stmt_option_direct_query:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_direct_query:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->direct_query = ( zend_is_true( value_z )) ? true : false;
}
void stmt_option_cursor_scroll_type:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_cursor_scroll_type:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
set_stmt_cursor_scroll_type( stmt, value_z TSRMLS_CC );
set_stmt_cursor_scroll_type( stmt, value_z );
}
void stmt_option_emulate_prepares:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_emulate_prepares:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_stmt_t *pdo_stmt = static_cast<pdo_stmt_t*>( stmt->driver() );
pdo_stmt->supports_placeholders = ( zend_is_true( value_z )) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL;
}
void stmt_option_fetch_numeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_fetch_numeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->fetch_numeric = ( zend_is_true( value_z )) ? true : false;
}
void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->fetch_datetime = ( zend_is_true( value_z )) ? true : false;
@ -384,7 +384,7 @@ pdo_sqlsrv_stmt::~pdo_sqlsrv_stmt( void )
// *stmt - Pointer to current statement
// Return:
// Returns 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -404,7 +404,7 @@ int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
if ( driver_stmt && driver_stmt->executed == true )
{
while( driver_stmt && driver_stmt->past_next_result_end == false ) {
core_sqlsrv_next_result( driver_stmt TSRMLS_CC );
core_sqlsrv_next_result( driver_stmt );
}
}
}
@ -428,7 +428,7 @@ int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
// colno - Index of the column which requires description.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRMLS_DC)
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -441,7 +441,7 @@ int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRML
try {
core_meta_data = core_sqlsrv_field_metadata( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), colno TSRMLS_CC );
core_meta_data = core_sqlsrv_field_metadata( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), colno );
}
catch( core::CoreException& ) {
@ -485,7 +485,7 @@ int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRML
// *stmt - pointer to current statement
// Return:
// 1 for success.
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt )
{
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
@ -523,7 +523,7 @@ int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
// *stmt - pointer to the current statement.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -540,7 +540,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
while( driver_stmt->past_next_result_end == false ) {
core_sqlsrv_next_result( driver_stmt TSRMLS_CC, false );
core_sqlsrv_next_result( driver_stmt, false );
}
}
@ -572,7 +572,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
// PDOStatement::setAttribute()
driver_stmt->set_query_timeout();
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, query, query_len );
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt, query, query_len );
if ( execReturn == SQL_NO_DATA ) {
stmt->column_count = 0;
@ -582,7 +582,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
}
else {
if (driver_stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
stmt->column_count = core::SQLNumResultCols( driver_stmt );
driver_stmt->column_count = stmt->column_count;
}
else {
@ -591,7 +591,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
if (driver_stmt->row_count == ACTIVE_NUM_ROWS_INVALID) {
// return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
stmt->row_count = core::SQLRowCount( driver_stmt );
driver_stmt->row_count = stmt->row_count;
}
else {
@ -642,7 +642,7 @@ int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orientation ori,
_In_ zend_long offset TSRMLS_DC)
_In_ zend_long offset)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -688,7 +688,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
}
SQLSMALLINT odbc_fetch_ori = pdo_fetch_ori_to_odbc_fetch_ori( ori );
bool data = core_sqlsrv_fetch( driver_stmt, odbc_fetch_ori, offset TSRMLS_CC );
bool data = core_sqlsrv_fetch( driver_stmt, odbc_fetch_ori, offset );
// 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
@ -698,7 +698,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
// which is unnecessary and a performance hit
if( driver_stmt->past_fetch_end || driver_stmt->cursor_type == SQL_CURSOR_DYNAMIC) {
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
stmt->row_count = core::SQLRowCount( driver_stmt );
driver_stmt->row_count = stmt->row_count;
// a row_count of -1 means no rows, but we change it to 0
@ -741,7 +741,7 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees TSRMLS_DC)
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -781,7 +781,7 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
sqlsrv_php_type.typeinfo.type = pdo_type_to_sqlsrv_php_type( driver_stmt,
driver_stmt->bound_column_param_types[colno]
TSRMLS_CC );
);
pdo_bound_param_data* bind_data = NULL;
bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, colno));
@ -823,7 +823,7 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
SQLSRV_PHPTYPE sqlsrv_phptype_out = SQLSRV_PHPTYPE_INVALID;
core_sqlsrv_get_field( driver_stmt, colno, sqlsrv_php_type, false, *(reinterpret_cast<void**>(ptr)),
reinterpret_cast<SQLLEN*>( len ), true, &sqlsrv_phptype_out TSRMLS_CC );
reinterpret_cast<SQLLEN*>( len ), true, &sqlsrv_phptype_out );
if (ptr) {
zval* zval_ptr = reinterpret_cast<zval*>(sqlsrv_malloc(sizeof(zval)));
@ -851,7 +851,7 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
// val - Attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC)
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -869,7 +869,7 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
case SQLSRV_ATTR_ENCODING:
set_stmt_encoding( driver_stmt, val TSRMLS_CC );
set_stmt_encoding( driver_stmt, val );
break;
case PDO_ATTR_CURSOR:
@ -877,7 +877,7 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
case SQLSRV_ATTR_QUERY_TIMEOUT:
core_sqlsrv_set_query_timeout( driver_stmt, val TSRMLS_CC );
core_sqlsrv_set_query_timeout( driver_stmt, val );
break;
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
@ -885,7 +885,7 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
core_sqlsrv_set_buffered_query_limit( driver_stmt, val TSRMLS_CC );
core_sqlsrv_set_buffered_query_limit( driver_stmt, val );
break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
@ -901,7 +901,7 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
case SQLSRV_ATTR_DECIMAL_PLACES:
core_sqlsrv_set_decimal_places(driver_stmt, val TSRMLS_CC);
core_sqlsrv_set_decimal_places(driver_stmt, val);
break;
case SQLSRV_ATTR_DATA_CLASSIFICATION:
@ -933,7 +933,7 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
// return_value - Attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC )
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -1041,7 +1041,7 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
// return_value - zval* consisting of the metadata.
// Return:
// FAILURE for failure, SUCCESS for success.
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value TSRMLS_DC)
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -1065,7 +1065,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
}
// initialize the array to nothing, as PDO requires us to create it
core::sqlsrv_array_init( *driver_stmt, return_value TSRMLS_CC );
array_init(return_value);
field_meta_data* core_meta_data;
@ -1080,7 +1080,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
// initialize the column data classification array
zval data_classification;
ZVAL_UNDEF(&data_classification);
core::sqlsrv_array_init(*driver_stmt, &data_classification TSRMLS_CC );
array_init(&data_classification);
data_classification::fill_column_sensitivity_array(driver_stmt, (SQLSMALLINT)colno, &data_classification);
@ -1095,7 +1095,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
SQLSMALLINT out_buff_len;
SQLLEN not_used;
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TYPE_NAME, field_type_name,
sizeof( field_type_name ), &out_buff_len, &not_used TSRMLS_CC );
sizeof( field_type_name ), &out_buff_len, &not_used );
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
@ -1120,7 +1120,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
char table_name[SQL_SERVER_IDENT_SIZE_MAX] = {'\0'};
SQLLEN field_type_num;
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TABLE_NAME, table_name, SQL_SERVER_IDENT_SIZE_MAX,
&out_buff_len, &field_type_num TSRMLS_CC );
&out_buff_len, &field_type_num );
add_assoc_string( return_value, "table", table_name );
if( stmt->columns && stmt->columns[colno].param_type == PDO_PARAM_ZVAL ) {
@ -1150,7 +1150,7 @@ int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno
// stmt - PDOStatement object containing the result set.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -1164,7 +1164,7 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_next_rowset: driver_data object was null" );
core_sqlsrv_next_result( static_cast<sqlsrv_stmt*>( stmt->driver_data ) TSRMLS_CC );
core_sqlsrv_next_result( static_cast<sqlsrv_stmt*>( stmt->driver_data ) );
// clear the current meta data since the new result will generate new meta data
std::for_each( driver_stmt->current_meta_data.begin(), driver_stmt->current_meta_data.end(), meta_data_free );
@ -1175,10 +1175,10 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
return 0;
}
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
stmt->column_count = core::SQLNumResultCols( driver_stmt );
// return the row count regardless if there are any rows or not
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
stmt->row_count = core::SQLRowCount( driver_stmt );
driver_stmt->column_count = stmt->column_count;
driver_stmt->row_count = stmt->row_count;
@ -1209,7 +1209,7 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
// Return:
// Returns 0 for failure, 1 for success.
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 TSRMLS_DC)
_Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type)
{
PDO_RESET_STMT_ERROR;
@ -1255,7 +1255,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
while( driver_stmt->past_next_result_end == false ) {
core_sqlsrv_next_result( driver_stmt TSRMLS_CC, false );
core_sqlsrv_next_result( driver_stmt, false );
}
}
@ -1401,7 +1401,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
// and bind the parameter
core_sqlsrv_bind_param( driver_stmt, static_cast<SQLUSMALLINT>( param->paramno ), direction, &(param->parameter) , php_out_type, encoding,
sql_type, column_size, decimal_digits TSRMLS_CC );
sql_type, column_size, decimal_digits );
}
break;
// undo any work done by the core layer after the statement is executed
@ -1416,7 +1416,7 @@ int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
}
core_sqlsrv_post_param( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), param->paramno,
&(param->parameter) TSRMLS_CC );
&(param->parameter) );
}
break;
case PDO_PARAM_EVT_FETCH_PRE:
@ -1523,12 +1523,3 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
return sqlsrv_phptype;
}
void pdo_sqlsrv_stmt::set_query_timeout()
{
if (query_timeout == QUERY_TIMEOUT_INVALID || query_timeout < 0) {
return;
}
core::SQLSetStmtAttr(this, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>((SQLLEN)query_timeout), SQL_IS_UINTEGER TSRMLS_CC);
}

View file

@ -3,7 +3,7 @@
//
// Contents: Utility functions used by both connection or statement functions
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -43,8 +43,11 @@ const int WARNING_MIN_LENGTH = static_cast<const int>( strlen( WARNING_TEMPLATE
sqlsrv_error_const* get_error_message( _In_opt_ unsigned int sqlsrv_error_code);
// build the object and throw the PDO exception
void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error TSRMLS_DC );
void pdo_sqlsrv_throw_exception(_In_ sqlsrv_error const* error);
void format_or_get_all_errors(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _Inout_ sqlsrv_error_auto_ptr& error, _Inout_ char* error_code, _In_opt_ va_list* print_args);
void add_remaining_errors_to_array (_In_ sqlsrv_error const* error, _Inout_ zval* array_z);
}
// pdo driver error messages
@ -462,146 +465,80 @@ pdo_error PDO_ERRORS[] = {
{ UINT_MAX, {} }
};
// PDO error handler for the environment context.
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args )
{
SQLSRV_ASSERT(( ctx != NULL ), "pdo_sqlsrv_handle_env_error: sqlsrv_context was null" );
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>( ctx.driver());
SQLSRV_ASSERT(( dbh != NULL ), "pdo_sqlsrv_handle_env_error: pdo_dbh_t was null" );
SQLSRV_ASSERT((ctx != NULL), "pdo_sqlsrv_handle_env_error: sqlsrv_context was null");
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>(ctx.driver());
SQLSRV_ASSERT((dbh != NULL), "pdo_sqlsrv_handle_env_error: pdo_dbh_t was null");
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, dbh->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR TSRMLS_CC, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR TSRMLS_CC );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning && dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
strcpy_s( dbh->error_code, sizeof( pdo_error_type ), reinterpret_cast<const char*>( error->sqlstate ));
ctx.set_last_error(error);
switch( dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
if( !warning ) {
pdo_sqlsrv_throw_exception( error TSRMLS_CC );
}
ctx.set_last_error( error );
break;
default:
DIE( "pdo_sqlsrv_handle_env_error: Unexpected error mode. %1!d!", dbh->error_mode );
break;
}
// we don't transfer the zval_auto_ptr since set_last_error increments the zval ref count
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// pdo error handler for the dbh context.
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args )
{
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>( ctx.driver());
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_handle_dbh_error: Null dbh passed" );
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, dbh->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR TSRMLS_CC, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR TSRMLS_CC );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning) {
if (dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
else if (dbh->error_mode == PDO_ERRMODE_WARNING) {
size_t msg_len = strnlen_s(reinterpret_cast<const char*>(error->native_message)) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + WARNING_MIN_LENGTH + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>(sqlsrv_malloc(msg_len));
core_sqlsrv_format_message(msg, static_cast<unsigned int>(msg_len), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message);
php_error(E_WARNING, "%s", msg.get());
}
}
SQLSRV_ASSERT(strnlen_s(reinterpret_cast<const char*>(error->sqlstate)) <= sizeof(dbh->error_code), "Error code overflow");
strcpy_s(dbh->error_code, sizeof(dbh->error_code), reinterpret_cast<const char*>(error->sqlstate));
switch( dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
if( !warning ) {
pdo_sqlsrv_throw_exception( error TSRMLS_CC );
}
ctx.set_last_error( error );
break;
case PDO_ERRMODE_WARNING:
if( !warning ) {
size_t msg_len = strnlen_s( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + WARNING_MIN_LENGTH + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>( sqlsrv_malloc( msg_len ) );
core_sqlsrv_format_message( msg, static_cast<unsigned int>( msg_len ), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message );
php_error(E_WARNING, "%s", msg.get());
}
ctx.set_last_error( error );
break;
case PDO_ERRMODE_SILENT:
ctx.set_last_error( error );
break;
default:
DIE( "Unknown error mode. %1!d!", dbh->error_mode );
break;
}
ctx.set_last_error(error);
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// PDO error handler for the statement context.
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
_In_opt_ va_list* print_args )
bool pdo_sqlsrv_handle_stmt_error(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args)
{
pdo_stmt_t* pdo_stmt = reinterpret_cast<pdo_stmt_t*>( ctx.driver());
SQLSRV_ASSERT( pdo_stmt != NULL && pdo_stmt->dbh != NULL, "pdo_sqlsrv_handle_stmt_error: Null statement or dbh passed" );
pdo_stmt_t* pdo_stmt = reinterpret_cast<pdo_stmt_t*>(ctx.driver());
SQLSRV_ASSERT(pdo_stmt != NULL && pdo_stmt->dbh != NULL, "pdo_sqlsrv_handle_stmt_error: Null statement or dbh passed");
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, pdo_stmt->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR TSRMLS_CC, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR TSRMLS_CC );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
}
SQLSRV_ASSERT( strnlen_s( reinterpret_cast<const char*>( error->sqlstate ) ) <= sizeof( pdo_stmt->error_code ), "Error code overflow");
strcpy_s( pdo_stmt->error_code, sizeof( pdo_stmt->error_code ), reinterpret_cast<const char*>( error->sqlstate ));
switch( pdo_stmt->dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
if( !warning ) {
pdo_sqlsrv_throw_exception( error TSRMLS_CC );
}
ctx.set_last_error( error );
break;
case PDO_ERRMODE_WARNING:
ctx.set_last_error( error );
break;
case PDO_ERRMODE_SILENT:
ctx.set_last_error( error );
break;
default:
DIE( "Unknown error mode. %1!d!", pdo_stmt->dbh->error_mode );
break;
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning && pdo_stmt->dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
ctx.set_last_error(error);
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// Transfer a sqlsrv_context's error to a PDO zval. The standard format for a zval error is 3 elements:
// 0, native code
// 1, native message
@ -613,11 +550,13 @@ void pdo_sqlsrv_retrieve_context_error( _In_ sqlsrv_error const* last_error, _Ou
// SQLSTATE is already present in the zval.
add_next_index_long( pdo_zval, last_error->native_code );
add_next_index_string( pdo_zval, reinterpret_cast<char*>( last_error->native_message ));
add_remaining_errors_to_array (last_error, pdo_zval);
}
}
// check the global variable of pdo_sqlsrv severity whether the message qualifies to be logged with the LOG macro
bool pdo_severity_check(_In_ unsigned int severity TSRMLS_DC)
bool pdo_severity_check(_In_ unsigned int severity)
{
return ((severity & PDO_SQLSRV_G(pdo_log_severity)));
}
@ -639,7 +578,7 @@ sqlsrv_error_const* get_error_message( _In_opt_ unsigned int sqlsrv_error_code)
return error_message;
}
void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error TSRMLS_DC )
void pdo_sqlsrv_throw_exception(_In_ sqlsrv_error const* error)
{
zval ex_obj;
ZVAL_UNDEF( &ex_obj );
@ -649,15 +588,23 @@ void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error TSRMLS_DC )
int zr = object_init_ex( &ex_obj, ex_class );
SQLSRV_ASSERT( zr != FAILURE, "Failed to initialize exception object" );
#if PHP_VERSION_ID >= 80000
zend_object *zendobj = Z_OBJ_P(&ex_obj);
#endif
sqlsrv_malloc_auto_ptr<char> ex_msg;
size_t ex_msg_len = strnlen_s( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE +
size_t ex_msg_len = strnlen_s(reinterpret_cast<const char*>(error->native_message)) + SQL_SQLSTATE_BUFSIZE +
12 + 1; // 12 = "SQLSTATE[]: "
ex_msg = reinterpret_cast<char*>( sqlsrv_malloc( ex_msg_len ));
snprintf( ex_msg, ex_msg_len, EXCEPTION_MSG_TEMPLATE, error->sqlstate, error->native_message );
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_MSG, sizeof( EXCEPTION_PROPERTY_MSG ) - 1,
ex_msg TSRMLS_CC );
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_CODE, sizeof( EXCEPTION_PROPERTY_CODE ) - 1,
reinterpret_cast<char*>( error->sqlstate ) TSRMLS_CC );
ex_msg = reinterpret_cast<char*>(sqlsrv_malloc(ex_msg_len));
snprintf(ex_msg, ex_msg_len, EXCEPTION_MSG_TEMPLATE, error->sqlstate, error->native_message);
#if PHP_VERSION_ID < 80000
zend_update_property_string(ex_class, &ex_obj, EXCEPTION_PROPERTY_MSG, sizeof(EXCEPTION_PROPERTY_MSG) - 1, ex_msg);
zend_update_property_string(ex_class, &ex_obj, EXCEPTION_PROPERTY_CODE, sizeof(EXCEPTION_PROPERTY_CODE) - 1, reinterpret_cast<char*>(error->sqlstate));
#else
zend_update_property_string(ex_class, zendobj, EXCEPTION_PROPERTY_MSG, sizeof(EXCEPTION_PROPERTY_MSG) - 1, ex_msg);
zend_update_property_string(ex_class, zendobj, EXCEPTION_PROPERTY_CODE, sizeof(EXCEPTION_PROPERTY_CODE) - 1, reinterpret_cast<char*>(error->sqlstate));
#endif
zval ex_error_info;
ZVAL_UNDEF( &ex_error_info );
@ -665,16 +612,77 @@ void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error TSRMLS_DC )
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->sqlstate ));
add_next_index_long( &ex_error_info, error->native_code );
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->native_message ));
add_remaining_errors_to_array (error, &ex_error_info);
//zend_update_property makes an entry in the properties_table in ex_obj point to the Z_ARRVAL( ex_error_info )
//and the refcount of the zend_array is incremented by 1
zend_update_property( ex_class, &ex_obj, EXCEPTION_PROPERTY_ERRORINFO, sizeof( EXCEPTION_PROPERTY_ERRORINFO ) - 1,
&ex_error_info TSRMLS_CC );
#if PHP_VERSION_ID < 80000
zend_update_property(ex_class, &ex_obj, EXCEPTION_PROPERTY_ERRORINFO, sizeof(EXCEPTION_PROPERTY_ERRORINFO) - 1, &ex_error_info);
#else
zend_update_property(ex_class, zendobj, EXCEPTION_PROPERTY_ERRORINFO, sizeof(EXCEPTION_PROPERTY_ERRORINFO) - 1, &ex_error_info);
#endif
//DELREF ex_error_info here to decrement the refcount of the zend_array is 1
//the global hashtable EG(exception) then points to the zend_object in ex_obj in zend_throw_exception_object;
//this ensure when EG(exception) cleans itself at php shutdown, the zend_array allocated is properly destroyed
Z_DELREF( ex_error_info );
zend_throw_exception_object( &ex_obj TSRMLS_CC );
zend_throw_exception_object( &ex_obj );
}
void add_remaining_errors_to_array (_In_ sqlsrv_error const* error, _Inout_ zval* array_z)
{
if (error->next != NULL && PDO_SQLSRV_G(report_additional_errors)) {
sqlsrv_error *p = error->next;
while (p != NULL) {
// check if sql state or native message is NULL and handle them accordingly
char *state = "";
char *msg = "";
if (p->sqlstate != NULL) {
state = reinterpret_cast<char*>(p->sqlstate);
}
if (p->native_message != NULL) {
msg = reinterpret_cast<char*>(p->native_message);
}
add_next_index_string(array_z, state);
add_next_index_long(array_z, p->native_code);
add_next_index_string(array_z, msg);
p = p-> next;
}
}
}
void format_or_get_all_errors(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _Inout_ sqlsrv_error_auto_ptr& error, _Inout_ char* error_code, _In_opt_ va_list* print_args)
{
if (sqlsrv_error_code != SQLSRV_ERROR_ODBC) {
core_sqlsrv_format_driver_error(ctx, get_error_message(sqlsrv_error_code), error, SEV_ERROR, print_args);
strcpy_s(error_code, sizeof(pdo_error_type), reinterpret_cast<const char*>(error->sqlstate));
}
else {
bool result = core_sqlsrv_get_odbc_error(ctx, 1, error, SEV_ERROR, true);
if (result) {
// Check if there exist more errors
int rec_number = 2;
sqlsrv_error_auto_ptr err;
sqlsrv_error *p = error;
do {
result = core_sqlsrv_get_odbc_error(ctx, rec_number++, err, SEV_ERROR, true);
if (result) {
p->next = err.get();
err.transferred();
p = p->next;
}
} while (result);
}
// core_sqlsrv_get_odbc_error() returns the error_code of size SQL_SQLSTATE_BUFSIZE,
// which is the same size as pdo_error_type
strcpy_s(error_code, sizeof(pdo_error_type), reinterpret_cast<const char*>(error->sqlstate));
}
}
}

View file

@ -6,7 +6,7 @@
//
// Contents: Declarations for the extension
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -31,6 +31,7 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlsrv)
unsigned int pdo_log_severity;
zend_long client_buffer_max_size;
short report_additional_errors;
#ifndef _WIN32
zend_long set_locale_info;

View file

@ -6,7 +6,7 @@
//
// Contents: Internal declarations for the extension
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -53,6 +53,7 @@ extern HMODULE g_sqlsrv_hmodule;
// (these are defined as macros to allow concatenation as we do below)
#define INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE "client_buffer_max_kb_size"
#define INI_PDO_SQLSRV_LOG "log_severity"
#define INI_PDO_SQLSRV_MORE_ERRORS "report_additional_errors"
#define INI_PREFIX "pdo_sqlsrv."
#ifndef _WIN32
@ -64,6 +65,7 @@ PHP_INI_BEGIN()
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE , INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong,
client_buffer_max_size, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SQLSRV_MORE_ERRORS, "1", PHP_INI_ALL, OnUpdateLong, report_additional_errors, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)
#ifndef _WIN32
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SET_LOCALE_INFO, "2", PHP_INI_ALL, OnUpdateLong, set_locale_info,
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)
@ -120,7 +122,7 @@ class string_parser
inline bool is_eos(void);
inline bool is_white_space( _In_ char c );
bool discard_white_spaces(void);
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len TSRMLS_DC );
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len );
};
@ -145,14 +147,14 @@ class conn_string_parser : private string_parser
private:
const char* current_key_name;
int discard_trailing_white_spaces( _In_reads_(len) const char* str, _Inout_ int len );
void validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len TSRMLS_DC);
void validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len);
protected:
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len TSRMLS_DC);
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len);
public:
conn_string_parser( _In_ sqlsrv_context& ctx, _In_ const char* dsn, _In_ int len, _In_ HashTable* conn_options_ht );
void parse_conn_string( TSRMLS_D );
void parse_conn_string( void );
};
@ -166,9 +168,9 @@ class sql_string_parser : private string_parser
private:
bool is_placeholder_char(char);
public:
void add_key_int_value_pair( _In_ unsigned int value TSRMLS_DC );
void add_key_int_value_pair( _In_ unsigned int value );
sql_string_parser(_In_ sqlsrv_context& ctx, _In_ const char* sql_str, _In_ int len, _In_ HashTable* placeholder_ht);
void parse_sql_string(TSRMLS_D);
void parse_sql_string(void);
};
@ -178,7 +180,7 @@ class sql_string_parser : private string_parser
extern const connection_option PDO_CONN_OPTS[];
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options TSRMLS_DC);
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options);
// a core layer pdo dbh object. This object inherits and overrides the statement factory
struct pdo_sqlsrv_dbh : public sqlsrv_conn {
@ -193,7 +195,7 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
short decimal_places;
short use_national_characters;
pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC );
pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver );
};
@ -202,45 +204,39 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
//*********************************************************************************************************************************
struct stmt_option_encoding : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_pdo_scrollable : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_direct_query : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_cursor_scroll_type : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_emulate_prepares : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_fetch_numeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_fetch_datetime : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
extern struct pdo_stmt_methods pdo_sqlsrv_stmt_methods;
// a core layer pdo stmt object. This object inherits and overrides the callbacks necessary
struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
pdo_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv TSRMLS_DC ) :
sqlsrv_stmt( c, handle, e, drv TSRMLS_CC ),
pdo_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv ) :
sqlsrv_stmt( c, handle, e, drv ),
direct_query( false ),
direct_query_subst_string( NULL ),
direct_query_subst_string_len( 0 ),
@ -264,9 +260,6 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
// for PDO, everything is a string, so we return SQLSRV_PHPTYPE_STRING for all SQL types
virtual sqlsrv_phptype sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_to_stream );
// driver specific way to set query timeout
virtual void set_query_timeout();
bool direct_query; // flag set if the query should be executed directly or prepared
const char* direct_query_subst_string; // if the query is direct, hold the substitution string if using named parameters
size_t direct_query_subst_string_len; // length of query string used for direct queries
@ -292,18 +285,18 @@ struct pdo_error {
// called when an error occurs in the core layer. These routines are set as the error_callback in a
// context. The context is passed to this function since it contains the function
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning TSRMLS_DC,
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args );
// common routine to transfer a sqlsrv_context's error to a PDO zval
void pdo_sqlsrv_retrieve_context_error( _In_ sqlsrv_error const* last_error, _Out_ zval* pdo_zval );
// reset the errors from the last operation
inline void pdo_reset_dbh_error( _Inout_ pdo_dbh_t* dbh TSRMLS_DC )
inline void pdo_reset_dbh_error( _Inout_ pdo_dbh_t* dbh )
{
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), "00000" ); // 00000 means no error
@ -330,7 +323,7 @@ inline void pdo_reset_dbh_error( _Inout_ pdo_dbh_t* dbh TSRMLS_DC )
core_sqlsrv_register_severity_checker(pdo_severity_check); \
LOG(SEV_NOTICE, message);
#define PDO_RESET_DBH_ERROR pdo_reset_dbh_error( dbh TSRMLS_CC );
#define PDO_RESET_DBH_ERROR pdo_reset_dbh_error( dbh );
inline void pdo_reset_stmt_error( _Inout_ pdo_stmt_t* stmt )
{
@ -406,7 +399,7 @@ enum PDO_ERROR_CODES {
extern pdo_error PDO_ERRORS[];
#define THROW_PDO_ERROR( ctx, custom, ... ) \
call_error_handler( ctx, custom TSRMLS_CC, false, ## __VA_ARGS__ ); \
call_error_handler( ctx, custom, false, ## __VA_ARGS__ ); \
throw pdo::PDOException();
namespace pdo {
@ -422,7 +415,6 @@ namespace pdo {
} // namespace pdo
// check the global variable of pdo_sqlsrv severity whether the message qualifies to be logged with the LOG macro
bool pdo_severity_check(_In_ unsigned int severity TSRMLS_DC);
bool pdo_severity_check(_In_ unsigned int severity);
#endif /* PHP_PDO_SQLSRV_INT_H */

View file

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

View file

@ -6,7 +6,7 @@
// Contents: Contains functions for handling Windows format strings
// and UTF-16 on non-Windows platforms
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -389,7 +389,6 @@ int FormattedPrintA( IFormattedPrintOutput<char> * output, const char *format, v
char sz[BUFFERSIZE];
} buffer = {'\0'};
WCHAR wchar; /* temp wchar_t */
int buffersize; /* size of text.sz (used only for the call to _cfltcvt) */
int bufferiswide=0; /* non-zero = buffer contains wide chars already */
#ifndef _SAFECRT_IMPL
@ -406,7 +405,6 @@ int FormattedPrintA( IFormattedPrintOutput<char> * output, const char *format, v
textlen = 0; /* no text yet */
state = ST_NORMAL; /* starting state */
heapbuf = NULL; /* not using heap-allocated buffer */
buffersize = 0;
/* main loop -- loop while format character exist and no I/O errors */
while ((ch = *format++) != '\0' && charsout >= 0) {
@ -631,9 +629,9 @@ int FormattedPrintA( IFormattedPrintOutput<char> * output, const char *format, v
case ('a'): {
/* floating point conversion -- we call cfltcvt routines */
/* to do the work for us. */
flags |= FL_SIGNED; /* floating point is signed conversion */
text.sz = buffer.sz; /* put result in buffer */
buffersize = BUFFERSIZE;
flags |= FL_SIGNED; /* floating point is signed conversion */
text.sz = buffer.sz; /* put result in buffer */
int buffersize = BUFFERSIZE; /* size of text.sz (used only for the call to _cfltcvt) */
/* compute the precision value */
if (precision < 0)

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@
//
// Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -68,13 +68,12 @@ const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};";
void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
_Inout_opt_ HashTable* options_ht, _In_ const connection_option valid_conn_opts[],
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
void* driver,_Inout_ std::string& connection_string );
void determine_server_version( _Inout_ sqlsrv_conn* conn );
const char* get_processor_arch( void );
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ const char* key, _In_ SQLULEN key_len TSRMLS_DC );
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC );
void load_azure_key_vault( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ const char* key, _In_ SQLULEN key_len );
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str );
void load_azure_key_vault( _Inout_ sqlsrv_conn* conn );
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD config_value, size_t key_size);
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size);
}
@ -97,7 +96,7 @@ void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char*
sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_context& henv_ncp, _In_ driver_conn_factory conn_factory,
_Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
_Inout_opt_ HashTable* options_ht, _In_ error_callback err, _In_ const connection_option valid_conn_opts[],
_In_ void* driver, _In_z_ const char* driver_func TSRMLS_DC )
_In_ void* driver, _In_z_ const char* driver_func )
{
SQLRETURN r;
@ -149,11 +148,11 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
#endif // !_WIN32
SQLHANDLE temp_conn_h;
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC );
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h );
conn = conn_factory( temp_conn_h, err, driver );
conn->set_func( driver_func );
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str TSRMLS_CC );
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str );
// If column encryption is enabled, must use ODBC driver 17
if( conn->ce_option.enabled && conn->driver_version != ODBC_DRIVER_UNKNOWN) {
@ -271,7 +270,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
#ifndef _WIN32
if ( r == SQL_SUCCESS_WITH_INFO ) {
#endif // !_WIN32
determine_server_version( conn TSRMLS_CC );
determine_server_version( conn );
#ifndef _WIN32
}
#endif // !_WIN32
@ -384,6 +383,9 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con
sqlsrv_malloc_auto_ptr<SQLWCHAR> wconn_string;
unsigned int wconn_len = static_cast<unsigned int>( conn_str.length() + 1 ) * sizeof( SQLWCHAR );
// Set the desired data classification version before connecting, but older ODBC drivers will generate a warning message 'Driver's SQLSetConnectAttr failed'
SQLSetConnectAttr(conn->handle(), SQL_COPT_SS_DATACLASSIFICATION_VERSION, reinterpret_cast<SQLPOINTER>(data_classification::VERSION_RANK_AVAILABLE), SQL_IS_POINTER);
// We only support UTF-8 encoding for connection string.
// 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 );
@ -426,14 +428,14 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con
// Parameters:
// sqlsrv_conn*: The connection with which the transaction is associated.
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn )
{
try {
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_begin_transaction: connection object was null." );
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_OFF ),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
}
catch ( core::CoreException& ) {
throw;
@ -449,16 +451,16 @@ void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
// Parameters:
// sqlsrv_conn*: The connection on which the transaction is active.
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn )
{
try {
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_commit: connection object was null." );
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_COMMIT TSRMLS_CC );
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_COMMIT );
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
}
catch ( core::CoreException& ) {
throw;
@ -474,16 +476,16 @@ void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
// Parameters:
// sqlsrv_conn*: The connection on which the transaction is active.
void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn )
{
try {
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_rollback: connection object was null." );
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK );
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
}
catch ( core::CoreException& ) {
@ -495,7 +497,7 @@ void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
// Called when a connection resource is destroyed by the Zend engine.
// Parameters:
// conn - The current active connection.
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn )
{
// if the connection wasn't successful, just return.
if( conn == NULL )
@ -504,7 +506,7 @@ void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
try {
// rollback any transaction in progress (we don't care about the return result)
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK );
}
catch( core::CoreException& ) {
LOG( SEV_ERROR, "Transaction rollback failed when closing the connection." );
@ -529,7 +531,7 @@ void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
// sql - T-SQL command to prepare
// sql_len - length of the T-SQL string
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len TSRMLS_DC )
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len )
{
try {
@ -557,7 +559,7 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
}
// prepare our wide char query string
core::SQLPrepareW( stmt, reinterpret_cast<SQLWCHAR*>( wsql_string.get() ), wsql_len TSRMLS_CC );
core::SQLPrepareW( stmt, reinterpret_cast<SQLWCHAR*>( wsql_string.get() ), wsql_len );
stmt->param_descriptions.clear();
@ -587,22 +589,14 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
// conn - The connection resource by which the client and server are connected.
// *server_version - zval for returning results.
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* server_version TSRMLS_DC )
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* server_version )
{
try {
sqlsrv_malloc_auto_ptr<char> buffer;
char buffer[INFO_BUFFER_LEN] = "";
SQLSMALLINT buffer_len = 0;
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
core::sqlsrv_zval_stringl( server_version, buffer, buffer_len );
if ( buffer != 0 ) {
sqlsrv_free( buffer );
}
buffer.transferred();
}
catch( core::CoreException& ) {
core::SQLGetInfo(conn, SQL_DBMS_VER, buffer, INFO_BUFFER_LEN, &buffer_len);
core::sqlsrv_zval_stringl(server_version, buffer, buffer_len);
} catch( core::CoreException& ) {
throw;
}
}
@ -614,36 +608,28 @@ void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* se
// conn - The connection resource by which the client and server are connected.
// *server_info - zval for returning results.
void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *server_info TSRMLS_DC )
void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *server_info )
{
try {
sqlsrv_malloc_auto_ptr<char> buffer;
char buffer[INFO_BUFFER_LEN] = "";
SQLSMALLINT buffer_len = 0;
// Get the database name
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_DATABASE_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
core::SQLGetInfo(conn, SQL_DATABASE_NAME, buffer, INFO_BUFFER_LEN, &buffer_len);
// initialize the array
core::sqlsrv_array_init( *conn, server_info TSRMLS_CC );
array_init(server_info);
core::sqlsrv_add_assoc_string( *conn, server_info, "CurrentDatabase", buffer, 0 /*duplicate*/ TSRMLS_CC );
buffer.transferred();
add_assoc_string(server_info, "CurrentDatabase", buffer);
// Get the server version
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerVersion", buffer, 0 /*duplicate*/ TSRMLS_CC );
buffer.transferred();
core::SQLGetInfo(conn, SQL_DBMS_VER, buffer, INFO_BUFFER_LEN, &buffer_len);
add_assoc_string(server_info, "SQLServerVersion", buffer);
// Get the server name
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_SERVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerName", buffer, 0 /*duplicate*/ TSRMLS_CC );
buffer.transferred();
}
catch( core::CoreException& ) {
core::SQLGetInfo(conn, SQL_SERVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len);
add_assoc_string(server_info, "SQLServerName", buffer);
} catch (core::CoreException&) {
throw;
}
}
@ -654,42 +640,32 @@ void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *server_
// conn - The connection resource by which the client and server are connected.
// *client_info - zval for returning the results.
void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC )
void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info )
{
try {
sqlsrv_malloc_auto_ptr<char> buffer;
char buffer[INFO_BUFFER_LEN] = "";
SQLSMALLINT buffer_len = 0;
// Get the ODBC driver's dll name
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len );
// initialize the array
core::sqlsrv_array_init( *conn, client_info TSRMLS_CC );
array_init(client_info);
#ifndef _WIN32
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverName", buffer, 0 /*duplicate*/ TSRMLS_CC );
add_assoc_string(client_info, "DriverName", buffer);
#else
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverDllName", buffer, 0 /*duplicate*/ TSRMLS_CC );
add_assoc_string(client_info, "DriverDllName", buffer);
#endif // !_WIN32
buffer.transferred();
// Get the ODBC driver's ODBC version
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_DRIVER_ODBC_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverODBCVer", buffer, 0 /*duplicate*/ TSRMLS_CC );
buffer.transferred();
core::SQLGetInfo( conn, SQL_DRIVER_ODBC_VER, buffer, INFO_BUFFER_LEN, &buffer_len );
add_assoc_string(client_info, "DriverODBCVer", buffer);
// Get the OBDC driver's version
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_DRIVER_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverVer", buffer, 0 /*duplicate*/ TSRMLS_CC );
buffer.transferred();
}
catch( core::CoreException& ) {
core::SQLGetInfo( conn, SQL_DRIVER_VER, buffer, INFO_BUFFER_LEN, &buffer_len );
add_assoc_string(client_info, "DriverVer", buffer);
} catch( core::CoreException& ) {
throw;
}
}
@ -753,7 +729,7 @@ bool core_is_authentication_option_valid( _In_z_ const char* value, _In_ size_t
namespace {
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ SQLULEN key,
_In_ const connection_option conn_opts[] TSRMLS_DC )
_In_ const connection_option conn_opts[] )
{
for( int opt_idx = 0; conn_opts[opt_idx].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) {
@ -774,7 +750,7 @@ connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ SQLULEN
void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
_Inout_opt_ HashTable* options, _In_ const connection_option valid_conn_opts[],
void* driver, _Inout_ std::string& connection_string TSRMLS_DC )
void* driver, _Inout_ std::string& connection_string )
{
bool mars_mentioned = false;
connection_option const* conn_opt;
@ -808,9 +784,12 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
zval* auth_option = NULL;
auth_option = zend_hash_index_find(options, SQLSRV_CONN_OPTION_AUTHENTICATION);
char* option = Z_STRVAL_P(auth_option);
char* option = NULL;
if (auth_option != NULL) {
option = Z_STRVAL_P(auth_option);
}
if (!stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
if (option != NULL && !stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
activeDirectoryMSI = true;
// There are two types of managed identities:
@ -834,7 +813,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
}
// Add the server name
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string TSRMLS_CC );
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
// If uid is not present then we use trusted connection -- but not when access token or ActiveDirectoryMSI is used,
// because they are incompatible
@ -848,7 +827,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
throw core::CoreException();
}
common_conn_str_append_func(ODBCConnOptions::UID, uid, strnlen_s(uid), connection_string TSRMLS_CC);
common_conn_str_append_func(ODBCConnOptions::UID, uid, strnlen_s(uid), connection_string);
// if no password was given, then don't add a password to the connection string. Perhaps the UID
// given doesn't have a password?
@ -858,7 +837,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
throw core::CoreException();
}
common_conn_str_append_func(ODBCConnOptions::PWD, pwd, strnlen_s(pwd), connection_string TSRMLS_CC);
common_conn_str_append_func(ODBCConnOptions::PWD, pwd, strnlen_s(pwd), connection_string);
}
}
}
@ -894,13 +873,13 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
// The driver layer should ensure a valid key.
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 TSRMLS_CC );
conn_opt = get_connection_option( conn, index, valid_conn_opts );
if( index == SQLSRV_CONN_OPTION_MARS ) {
mars_mentioned = true;
}
conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC );
conn_opt->func( conn_opt, data, conn, connection_string );
} ZEND_HASH_FOREACH_END();
// MARS on if not explicitly turned off
@ -915,30 +894,6 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
}
}
// get_server_version
// Helper function which returns the version of the SQL Server we are connected to.
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC )
{
try {
sqlsrv_malloc_auto_ptr<char> buffer;
SQLSMALLINT buffer_len = 0;
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
core::SQLGetInfo( conn, SQL_DBMS_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
*server_version = buffer;
len = buffer_len;
buffer.transferred();
}
catch( core::CoreException& ) {
throw;
}
}
// get_processor_arch
// Calls GetSystemInfo to verify the what architecture of the processor is supported
// and return the string of the processor name.
@ -989,11 +944,11 @@ const char* get_processor_arch( void )
// Exception is thrown when the server version is either undetermined
// or is invalid (< 2000).
void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
void determine_server_version( _Inout_ sqlsrv_conn* conn )
{
SQLSMALLINT info_len;
char p[INFO_BUFFER_LEN] = {'\0'};
core::SQLGetInfo( conn, SQL_DBMS_VER, p, INFO_BUFFER_LEN, &info_len TSRMLS_CC );
core::SQLGetInfo( conn, SQL_DBMS_VER, p, INFO_BUFFER_LEN, &info_len );
errno = 0;
char version_major_str[3] = {'\0'};
@ -1013,7 +968,7 @@ void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
conn->server_version = version_major;
}
void load_azure_key_vault(_Inout_ sqlsrv_conn* conn TSRMLS_DC)
void load_azure_key_vault(_Inout_ sqlsrv_conn* conn)
{
// If column encryption is not enabled simply do nothing. Otherwise, check if Azure Key Vault
// is required for encryption or decryption. Note, in order to load and configure Azure Key Vault,
@ -1092,11 +1047,10 @@ void configure_azure_key_vault(sqlsrv_conn* conn, BYTE config_attr, const char*
core::SQLSetConnectAttr(conn, SQL_COPT_SS_CEKEYSTOREDATA, reinterpret_cast<SQLPOINTER>(pData), SQL_IS_POINTER);
}
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC )
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str )
{
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
// be escaped, such as a closing }.
TSRMLS_C;
if( val_len > 0 && val[0] == '{' && val[val_len - 1] == '}' ) {
++val;
@ -1111,26 +1065,25 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l
} // namespace
// simply add the parsed value to the connection string
void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str TSRMLS_DC )
void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str )
{
const char* val_str = Z_STRVAL_P( value );
size_t val_len = Z_STRLEN_P( value );
common_conn_str_append_func( option->odbc_name, val_str, val_len, conn_str TSRMLS_CC );
common_conn_str_append_func( option->odbc_name, val_str, val_len, conn_str );
}
// do nothing for connection pooling since we handled it earlier when
// deciding which environment handle to use.
void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ TSRMLS_DC )
void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ )
{
TSRMLS_C;
}
void driver_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
void driver_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str )
{
const char* val_str = Z_STRVAL_P( value );
size_t val_len = Z_STRLEN_P( value );
std::string driver_option( "" );
common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option TSRMLS_CC );
common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option );
conn->driver_version = ODBC_DRIVER_UNKNOWN;
for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && conn->driver_version == ODBC_DRIVER_UNKNOWN; ++i ) {
@ -1148,7 +1101,7 @@ void driver_set_func::func( _In_ connection_option const* option, _In_ zval* val
conn_str += driver_option;
}
void column_encryption_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
void column_encryption_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str )
{
convert_to_string( value );
const char* value_str = Z_STRVAL_P( value );
@ -1168,7 +1121,7 @@ void column_encryption_set_func::func( _In_ connection_option const* option, _In
conn_str += ";";
}
void ce_akv_str_set_func::func(_In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC)
void ce_akv_str_set_func::func(_In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str)
{
SQLSRV_ASSERT(Z_TYPE_P(value) == IS_STRING, "Azure Key Vault keywords accept only strings.");
@ -1254,7 +1207,7 @@ size_t core_str_zval_is_true( _Inout_ zval* value_z )
return 0; // false
}
void access_token_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
void access_token_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str )
{
SQLSRV_ASSERT(Z_TYPE_P(value) == IS_STRING, "An access token must be a byte string.");

View file

@ -3,7 +3,7 @@
//
// Contents: common initialization routines shared by PDO and sqlsrv
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -34,7 +34,7 @@ bool isVistaOrGreater;
// henv_cp - Environment handle for pooled connection.
// henv_ncp - Environment handle for non-pooled connection.
// err - Driver specific error handler which handles any errors during initialization.
void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_context** henv_ncp, _In_ error_callback err, _In_z_ const char* driver_func TSRMLS_DC )
void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_context** henv_ncp, _In_ error_callback err, _In_z_ const char* driver_func )
{
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ) );
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long ));
@ -65,11 +65,11 @@ void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_contex
// set to ODBC 3
core::SQLSetEnvAttr( **henv_ncp, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>( SQL_OV_ODBC3 ), SQL_IS_INTEGER
TSRMLS_CC );
);
// disable connection pooling
core::SQLSetEnvAttr( **henv_ncp, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast<SQLPOINTER>( SQL_CP_OFF ),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
// allocate the pooled envrionment handle
// we can't use the wrapper in core_sqlsrv.h since we don't have a context on which to base errors, so
@ -83,11 +83,11 @@ void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_contex
(*henv_cp)->set_func( driver_func );
// set to ODBC 3
core::SQLSetEnvAttr( **henv_cp, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>( SQL_OV_ODBC3 ), SQL_IS_INTEGER TSRMLS_CC);
core::SQLSetEnvAttr( **henv_cp, SQL_ATTR_ODBC_VERSION, reinterpret_cast<SQLPOINTER>( SQL_OV_ODBC3 ), SQL_IS_INTEGER);
// enable connection pooling
core:: SQLSetEnvAttr( **henv_cp, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast<SQLPOINTER>( SQL_CP_ONE_PER_HENV ),
SQL_IS_UINTEGER TSRMLS_CC );
SQL_IS_UINTEGER );
}
catch( core::CoreException& e ) {

View file

@ -3,7 +3,7 @@
//
// Contents: Result sets
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -78,7 +78,7 @@ bool get_bit( _In_ void* ptr, _In_ unsigned int bit )
// read in LOB field during buffered result creation
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 TSRMLS_DC );
_In_ zend_long mem_used );
// dtor for each row in the cache
void cache_row_dtor( _In_ zval* data );
@ -262,64 +262,6 @@ std::string getUTF8StringFromString( _In_z_ const char* source )
#endif // !_WIN32
template <typename Number, typename Char>
SQLRETURN string_to_number( _In_z_ Char* string_data, SQLLEN str_len, _Out_writes_bytes_(*out_buffer_length) void* buffer, SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length, _Inout_ sqlsrv_error_auto_ptr& last_error )
{
Number* number_data = reinterpret_cast<Number*>( buffer );
#ifdef _WIN32
std::locale loc; // default locale should match system
std::basic_istringstream<Char> is;
is.str( string_data );
is.imbue( loc );
std::ios_base::iostate st = 0;
std::use_facet< std::num_get< Char > >( loc ).get( std::basic_istream<Char>::_Iter( is.rdbuf()), std::basic_istream<Char>::_Iter( 0 ), is, st, *number_data );
if ( st & std::ios_base::failbit ) {
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(( SQLCHAR* ) "22003", ( SQLCHAR* ) "Numeric value out of range", 103 );
return SQL_ERROR;
}
*out_buffer_length = sizeof( Number );
#else
std::string str = getUTF8StringFromString( string_data );
std::istringstream is( str );
std::locale loc; // default locale should match system
is.imbue( loc );
auto& facet = std::use_facet<std::num_get<char>>( is.getloc() );
std::istreambuf_iterator<char> beg( is ), end;
std::ios_base::iostate err = std::ios_base::goodbit;
if ( std::is_integral<Number>::value )
{
long number;
facet.get( beg, end, is, err, number );
*number_data = number;
}
else
{
double number;
facet.get( beg, end, is, err, number );
*number_data = number;
}
*out_buffer_length = sizeof( Number );
if ( is.fail() )
{
last_error = new ( sqlsrv_malloc(sizeof( sqlsrv_error ))) sqlsrv_error(( SQLCHAR* ) "22003", ( SQLCHAR* ) "Numeric value out of range", 103 );
return SQL_ERROR;
}
#endif // _WIN32
return SQL_SUCCESS;
}
// "closure" for the hash table destructor
struct row_dtor_closure {
@ -391,27 +333,27 @@ sqlsrv_odbc_result_set::~sqlsrv_odbc_result_set( void )
{
}
SQLRETURN sqlsrv_odbc_result_set::fetch( _In_ SQLSMALLINT orientation, _In_ SQLLEN offset TSRMLS_DC )
SQLRETURN sqlsrv_odbc_result_set::fetch( _In_ SQLSMALLINT orientation, _In_ SQLLEN offset )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLFetchScroll( odbc, orientation, offset TSRMLS_CC );
return core::SQLFetchScroll( odbc, orientation, offset );
}
SQLRETURN sqlsrv_odbc_result_set::get_data( _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_opt_(buffer_length) SQLPOINTER buffer, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
_In_ bool handle_warning TSRMLS_DC )
_In_ bool handle_warning )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLGetData( odbc, field_index, target_type, buffer, buffer_length, out_buffer_length, handle_warning TSRMLS_CC );
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,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Inout_ SQLSMALLINT* out_buffer_length )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length,
out_buffer_length TSRMLS_CC );
out_buffer_length );
}
sqlsrv_error* sqlsrv_odbc_result_set::get_diag_rec( _In_ SQLSMALLINT record_number )
@ -420,17 +362,17 @@ sqlsrv_error* sqlsrv_odbc_result_set::get_diag_rec( _In_ SQLSMALLINT record_numb
return odbc_get_diag_rec( odbc, record_number );
}
SQLLEN sqlsrv_odbc_result_set::row_count( TSRMLS_D )
SQLLEN sqlsrv_odbc_result_set::row_count( void )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLRowCount( odbc TSRMLS_CC );
return core::SQLRowCount( odbc );
}
// Buffered result set
// This class holds a result set in memory
sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ) :
sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stmt ) :
sqlsrv_result_set( stmt ),
cache(NULL),
col_count(0),
@ -439,7 +381,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
read_so_far(0),
temp_length(0)
{
col_count = core::SQLNumResultCols( stmt TSRMLS_CC );
col_count = core::SQLNumResultCols( stmt );
// there is no result set to buffer
if( col_count == 0 ) {
return;
@ -456,7 +398,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
SQLULEN offset = null_bytes;
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 TSRMLS_CC );
core::SQLDescribeColW( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL );
offset = align_to<sizeof(SQLPOINTER)>( offset );
meta[i].offset = offset;
@ -469,7 +411,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
case SQL_GUID:
case SQL_NUMERIC:
core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL,
reinterpret_cast<SQLLEN*>( &meta[i].length ) TSRMLS_CC );
reinterpret_cast<SQLLEN*>( &meta[i].length ) );
meta[i].length += sizeof( char ) + sizeof( SQLULEN ); // null terminator space
offset += meta[i].length;
break;
@ -536,7 +478,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
case SQL_SS_TIMESTAMPOFFSET:
case SQL_TYPE_TIMESTAMP:
core::SQLColAttributeW( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL,
reinterpret_cast<SQLLEN*>( &meta[i].length ) TSRMLS_CC );
reinterpret_cast<SQLLEN*>( &meta[i].length ) );
meta[i].length += sizeof(char) + sizeof( SQLULEN ); // null terminator space
offset += meta[i].length;
break;
@ -628,10 +570,10 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
size_t row_count = 0;
// 10 is an arbitrary number for now for the initial size of the cache
ALLOC_HASHTABLE( cache );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, cache_row_dtor /*dtor*/, 0 /*persistent*/ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, cache_row_dtor /*dtor*/, 0 /*persistent*/ );
try {
while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ) != SQL_NO_DATA ) {
while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 ) != SQL_NO_DATA ) {
// allocate the row buffer
sqlsrv_malloc_auto_ptr<unsigned char> rowAuto;
@ -655,7 +597,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
out_buffer_length = &out_buffer_temp;
SQLPOINTER* lob_addr = reinterpret_cast<SQLPOINTER*>( &row[meta[i].offset] );
*lob_addr = read_lob_field( stmt, i, meta[i], mem_used TSRMLS_CC );
*lob_addr = read_lob_field( stmt, i, meta[i], mem_used );
// a NULL pointer means NULL field
if( *lob_addr == NULL ) {
*out_buffer_length = SQL_NULL_DATA;
@ -677,7 +619,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
buffer = row + meta[i].offset + sizeof( SQLULEN );
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,
false TSRMLS_CC );
false );
}
break;
@ -693,7 +635,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
buffer = row + meta[i].offset;
out_buffer_length = &out_buffer_temp;
core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length,
false TSRMLS_CC );
false );
}
break;
@ -712,7 +654,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* stm
// add it to the cache
row_dtor_closure cl( this, row );
sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) TSRMLS_CC );
sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) );
rowAuto.transferred();
}
}
@ -738,7 +680,7 @@ sqlsrv_buffered_result_set::~sqlsrv_buffered_result_set( void )
}
}
SQLRETURN sqlsrv_buffered_result_set::fetch( _Inout_ SQLSMALLINT orientation, _Inout_opt_ SQLLEN offset TSRMLS_DC )
SQLRETURN sqlsrv_buffered_result_set::fetch( _Inout_ SQLSMALLINT orientation, _Inout_opt_ SQLLEN offset )
{
last_error = NULL;
last_field_index = -1;
@ -762,7 +704,7 @@ SQLRETURN sqlsrv_buffered_result_set::fetch( _Inout_ SQLSMALLINT orientation, _I
current = 1;
break;
case SQL_FETCH_LAST:
current = row_count( TSRMLS_C );
current = row_count();
break;
case SQL_FETCH_ABSOLUTE:
current = offset;
@ -783,8 +725,8 @@ SQLRETURN sqlsrv_buffered_result_set::fetch( _Inout_ SQLSMALLINT orientation, _I
}
// the cursor can never get further away than just after the last row
if( current > row_count( TSRMLS_C ) || ( current <= 0 && offset > 0 ) /*overflow condition*/ ) {
current = row_count( TSRMLS_C ) + 1;
if( current > row_count() || ( current <= 0 && offset > 0 ) /*overflow condition*/ ) {
current = row_count() + 1;
return SQL_NO_DATA;
}
@ -793,7 +735,7 @@ SQLRETURN sqlsrv_buffered_result_set::fetch( _Inout_ SQLSMALLINT orientation, _I
SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_bytes_opt_(buffer_length) SQLPOINTER buffer, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
bool handle_warning )
{
last_error = NULL;
field_index--; // convert from 1 based to 0 based
@ -851,7 +793,6 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
case SQL_C_LONG: return sqlsrv_buffered_result_set::to_long(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_BINARY: return sqlsrv_buffered_result_set::to_long(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_CHAR: return sqlsrv_buffered_result_set::long_to_system_string(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_WCHAR: return sqlsrv_buffered_result_set::long_to_wide_string(field_index, buffer, buffer_length, out_buffer_length);
default:
break;
}
@ -862,7 +803,6 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
case SQL_C_BINARY: return sqlsrv_buffered_result_set::to_double(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_CHAR: return sqlsrv_buffered_result_set::double_to_system_string(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_LONG: return sqlsrv_buffered_result_set::double_to_long(field_index, buffer, buffer_length, out_buffer_length);
case SQL_C_WCHAR: return sqlsrv_buffered_result_set::double_to_wide_string(field_index, buffer, buffer_length, out_buffer_length);
default:
break;
}
@ -879,7 +819,7 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( _In_ SQLUSMALLINT field_index, _
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_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
_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( diag_identifier == SQL_DIAG_SQLSTATE,
@ -923,7 +863,7 @@ sqlsrv_error* sqlsrv_buffered_result_set::get_diag_rec( _In_ SQLSMALLINT record_
sqlsrv_error( last_error->sqlstate, last_error->native_message, last_error->native_code );
}
SQLLEN sqlsrv_buffered_result_set::row_count( TSRMLS_D )
SQLLEN sqlsrv_buffered_result_set::row_count( void )
{
last_error = NULL;
@ -1082,23 +1022,6 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( _In_ SQLSMALLINT
return r;
}
SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( _In_ SQLSMALLINT field_index, _Out_writes_bytes_to_opt_(buffer_length, *out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[field_index].c_type == SQL_C_DOUBLE, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_wide_string" );
unsigned char* row = get_row();
double* double_data = reinterpret_cast<double*>( &row[meta[field_index].offset] );
SQLRETURN r = SQL_SUCCESS;
#ifdef _WIN32
r = number_to_string<WCHAR>( double_data, buffer, buffer_length, out_buffer_length, last_error );
#else
r = number_to_string<char16_t, double>( double_data, buffer, buffer_length, out_buffer_length, last_error );
#endif // _WIN32
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,
_Out_ SQLLEN* out_buffer_length )
{
@ -1131,23 +1054,6 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( _In_ SQLSMALLINT fi
return r;
}
SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( _In_ SQLSMALLINT field_index, _Out_writes_bytes_to_opt_(buffer_length, *out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[field_index].c_type == SQL_C_LONG, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_wide_string" );
unsigned char* row = get_row();
LONG* long_data = reinterpret_cast<LONG*>( &row[meta[field_index].offset] );
SQLRETURN r = SQL_SUCCESS;
#ifdef _WIN32
r = number_to_string<WCHAR>( long_data, buffer, buffer_length, out_buffer_length, last_error );
#else
r = number_to_string<char16_t, LONG>( long_data, buffer, buffer_length, out_buffer_length, last_error );
#endif // _WIN32
return r;
}
SQLRETURN sqlsrv_buffered_result_set::string_to_double( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
_Inout_ SQLLEN* out_buffer_length )
{
@ -1157,7 +1063,16 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_double( _In_ SQLSMALLINT field_i
unsigned char* row = get_row();
char* string_data = reinterpret_cast<char*>( &row[meta[field_index].offset] ) + sizeof( SQLULEN );
return string_to_number<double>( string_data, meta[field_index].length, buffer, buffer_length, out_buffer_length, last_error );
double* number_data = reinterpret_cast<double*>(buffer);
try {
*number_data = std::stod(std::string(string_data));
} catch (const std::logic_error& err) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
*out_buffer_length = sizeof(double);
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
@ -1169,7 +1084,20 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( _In_ SQLSMALLINT field_
unsigned char* row = get_row();
SQLWCHAR* string_data = reinterpret_cast<SQLWCHAR*>( &row[meta[field_index].offset] ) + sizeof( SQLULEN ) / sizeof( SQLWCHAR );
return string_to_number<double>( string_data, meta[field_index].length, buffer, buffer_length, out_buffer_length, last_error );
double* number_data = reinterpret_cast<double*>(buffer);
try {
#ifdef _WIN32
*number_data = std::stod(std::wstring(string_data));
#else
*number_data = std::stod(getUTF8StringFromString(string_data));
#endif // _WIN32
} catch (const std::logic_error& err) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
*out_buffer_length = sizeof(double);
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::string_to_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
@ -1181,7 +1109,16 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_long( _In_ SQLSMALLINT field_ind
unsigned char* row = get_row();
char* string_data = reinterpret_cast<char*>( &row[meta[field_index].offset] ) + sizeof( SQLULEN );
return string_to_number<LONG>( string_data, meta[field_index].length, buffer, buffer_length, out_buffer_length, last_error );
LONG* number_data = reinterpret_cast<LONG*>(buffer);
try {
*number_data = std::stol(std::string(string_data));
} catch (const std::logic_error& err) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
*out_buffer_length = sizeof(LONG);
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( _In_ SQLSMALLINT field_index, _Out_writes_bytes_(*out_buffer_length) void* buffer, _In_ SQLLEN buffer_length,
@ -1193,7 +1130,20 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( _In_ SQLSMALLINT field_in
unsigned char* row = get_row();
SQLWCHAR* string_data = reinterpret_cast<SQLWCHAR*>( &row[meta[field_index].offset] ) + sizeof( SQLULEN ) / sizeof( SQLWCHAR );
return string_to_number<LONG>( string_data, meta[field_index].length, buffer, buffer_length, out_buffer_length, last_error );
LONG* number_data = reinterpret_cast<LONG*>(buffer);
try {
#ifdef _WIN32
*number_data = std::stol(std::wstring(string_data));
#else
*number_data = std::stol(getUTF8StringFromString(string_data));
#endif // _WIN32
} catch (const std::logic_error& err) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
*out_buffer_length = sizeof(LONG);
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,
@ -1518,7 +1468,7 @@ void cache_row_dtor( _In_ zval* data )
}
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 TSRMLS_DC )
_In_ zend_long mem_used )
{
SQLSMALLINT extra = 0;
SQLULEN* output_buffer_len = NULL;
@ -1553,7 +1503,7 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
r = core::SQLGetData( stmt, field_index + 1, meta.c_type, buffer.get() + already_read + sizeof( SQLULEN ),
to_read - already_read + extra, &last_field_len, false /*handle_warning*/ TSRMLS_CC );
to_read - already_read + extra, &last_field_len, false /*handle_warning*/ );
// if the field is NULL, then return a NULL pointer
if( last_field_len == SQL_NULL_DATA ) {
@ -1574,7 +1524,7 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
else if( r == SQL_SUCCESS_WITH_INFO ) {
SQLSMALLINT len;
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
);
if( !is_truncated_warning( state )) {
break;

View file

@ -6,7 +6,7 @@
//
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -289,16 +289,16 @@ struct sqlsrv_static_assert<true> { _In_ static const int value = 1; };
// log_callback
// a driver specific callback for checking if the messages are qualified to be logged:
// severity - severity of the message: notice, warning, or error
typedef bool (*severity_callback)(_In_ unsigned int severity TSRMLS_DC);
typedef bool (*severity_callback)(_In_ unsigned int severity);
// each driver must register a severity checker callback for logging to work according to the INI settings
void core_sqlsrv_register_severity_checker(_In_ severity_callback driver_checker);
// a simple wrapper around a PHP error logging function.
void write_to_log( _In_ unsigned int severity TSRMLS_DC, _In_ const char* msg, ... );
void write_to_log( _In_ unsigned int severity, _In_ const char* msg, ... );
// a macro to make it convenient to use the function.
#define LOG( severity, msg, ...) write_to_log( severity TSRMLS_CC, msg, ## __VA_ARGS__ )
#define LOG( severity, msg, ...) write_to_log( severity, msg, ## __VA_ARGS__ )
// mask for filtering which severities are written to the log
enum logging_severity {
@ -397,7 +397,8 @@ inline void* sqlsrv_malloc( _In_ size_t element_count, _In_ size_t element_size,
DIE( "Integer overflow in sqlsrv_malloc" );
}
if( element_size * element_count + extra == 0 ) {
// safeguard against anomalous calculation or any arithmetic overflow
if( element_size * element_count + extra <= 0 ) {
DIE( "Allocation size must be more than 0" );
}
@ -776,6 +777,7 @@ struct sqlsrv_error_const {
// subclass which is used by the core layer to instantiate ODBC errors
struct sqlsrv_error : public sqlsrv_error_const {
struct sqlsrv_error *next; // Only used in pdo_sqlsrv for additional errors (as a linked list)
sqlsrv_error( void )
{
@ -783,16 +785,18 @@ struct sqlsrv_error : public sqlsrv_error_const {
native_message = NULL;
native_code = -1;
format = false;
next = NULL;
}
sqlsrv_error( _In_ SQLCHAR* sql_state, _In_ SQLCHAR* message, _In_ SQLINTEGER code, _In_ bool printf_format = false )
sqlsrv_error( _In_ SQLCHAR* sql_state, _In_ SQLCHAR* message, _In_ SQLINTEGER code, _In_ bool printf_format = false)
{
sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE ));
native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ));
strcpy_s( reinterpret_cast<char*>( sqlstate ), SQL_SQLSTATE_BUFSIZE, reinterpret_cast<const char*>( sql_state ));
strcpy_s( reinterpret_cast<char*>( native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH + 1, reinterpret_cast<const char*>( message ));
sqlstate = reinterpret_cast<SQLCHAR*>(sqlsrv_malloc(SQL_SQLSTATE_BUFSIZE));
native_message = reinterpret_cast<SQLCHAR*>(sqlsrv_malloc(SQL_MAX_ERROR_MESSAGE_LENGTH + 1));
strcpy_s(reinterpret_cast<char*>(sqlstate), SQL_SQLSTATE_BUFSIZE, reinterpret_cast<const char*>(sql_state));
strcpy_s(reinterpret_cast<char*>(native_message), SQL_MAX_ERROR_MESSAGE_LENGTH + 1, reinterpret_cast<const char*>(message));
native_code = code;
format = printf_format;
next = NULL;
}
sqlsrv_error( _In_ sqlsrv_error_const const& prototype )
@ -802,16 +806,26 @@ struct sqlsrv_error : public sqlsrv_error_const {
~sqlsrv_error( void )
{
if( sqlstate != NULL ) {
sqlsrv_free( sqlstate );
reset();
}
void reset() {
if (sqlstate != NULL) {
sqlsrv_free(sqlstate);
sqlstate = NULL;
}
if( native_message != NULL ) {
sqlsrv_free( native_message );
if (native_message != NULL) {
sqlsrv_free(native_message);
native_message = NULL;
}
if (next != NULL) {
next->reset(); // free the next sqlsrv_error, and so on
sqlsrv_free(next);
next = NULL;
}
}
};
// an auto_ptr for sqlsrv_errors. These call the destructor explicitly rather than call delete
class sqlsrv_error_auto_ptr : public sqlsrv_auto_ptr<sqlsrv_error, sqlsrv_error_auto_ptr > {
@ -852,7 +866,6 @@ public:
}
};
//*********************************************************************************************************************************
// Context
//*********************************************************************************************************************************
@ -864,7 +877,7 @@ struct sqlsrv_conn;
// a driver specific callback for processing errors.
// ctx - the context holding the handles
// sqlsrv_error_code - specific error code to return.
typedef bool (*error_callback)( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool error TSRMLS_DC, _In_opt_ va_list* print_args );
typedef bool (*error_callback)( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool error, _In_opt_ va_list* print_args );
// sqlsrv_context
// a context holds relevant information to be passed with a connection and statement objects.
@ -1019,7 +1032,7 @@ struct sqlsrv_encoding {
extern bool isVistaOrGreater; // used to determine if OS is Vista or Greater
extern HashTable* g_encodings; // encodings supported by this driver
void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_context** henv_ncp, _In_ error_callback err, _In_z_ const char* driver_func TSRMLS_DC );
void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_context** henv_ncp, _In_ error_callback err, _In_z_ const char* driver_func );
void core_sqlsrv_mshutdown( _Inout_ sqlsrv_context& henv_cp, _Inout_ sqlsrv_context& henv_ncp );
// environment context used by sqlsrv_connect for when a connection error occurs.
@ -1094,7 +1107,7 @@ struct sqlsrv_conn : public sqlsrv_context {
sqlsrv_malloc_auto_ptr<ACCESSTOKEN> azure_ad_access_token;
// initialize with default values
sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_opt_ void* drv, _In_ SQLSRV_ENCODING encoding TSRMLS_DC ) :
sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_opt_ void* drv, _In_ SQLSRV_ENCODING encoding ) :
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
{
server_version = SERVER_VERSION_UNKNOWN;
@ -1225,54 +1238,54 @@ struct connection_option {
// process the connection type
// return whether or not the function was successful in processing the connection option
void (*func)( connection_option const*, zval* value, sqlsrv_conn* conn, std::string& conn_str TSRMLS_DC );
void (*func)( connection_option const*, zval* value, sqlsrv_conn* conn, std::string& conn_str );
};
// connection attribute functions
// simply add the parsed value to the connection string
struct conn_str_append_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str TSRMLS_DC );
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str );
};
struct conn_null_func {
static void func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ TSRMLS_DC );
static void func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ );
};
struct column_encryption_set_func {
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str );
};
struct driver_set_func {
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str );
};
struct ce_akv_str_set_func {
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str );
};
struct access_token_set_func {
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str );
};
// factory to create a connection (since they are subclassed to instantiate statements)
typedef sqlsrv_conn* (*driver_conn_factory)( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv TSRMLS_DC );
typedef sqlsrv_conn* (*driver_conn_factory)( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv );
// *** connection functions ***
sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_context& henv_ncp, _In_ driver_conn_factory conn_factory,
_Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
_Inout_opt_ HashTable* options_ht, _In_ error_callback err, _In_ const connection_option valid_conn_opts[],
_In_ void* driver, _In_z_ const char* driver_func TSRMLS_DC );
_In_ void* driver, _In_z_ const char* driver_func );
SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str, _In_ bool is_pooled );
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC );
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len TSRMLS_DC );
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval* server_info TSRMLS_DC );
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval *server_version TSRMLS_DC );
void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC );
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn );
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len );
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn );
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn );
void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn );
void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval* server_info );
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval *server_version );
void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info );
bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t value_len );
size_t core_str_zval_is_true( _Inout_ zval* str_zval );
bool core_is_authentication_option_valid( _In_z_ const char* value, _In_ size_t value_len );
@ -1285,42 +1298,42 @@ bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN r, _In_ c
struct stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, _In_ zval* /*value_z*/ TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, _In_ zval* /*value_z*/ );
};
struct stmt_option_query_timeout : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_send_at_exec : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_buffered_query_limit : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_date_as_string : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_format_decimals : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_decimal_places : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
struct stmt_option_data_classification : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* opt, _In_ zval* value_z );
};
// used to hold the table for statment options
@ -1353,7 +1366,7 @@ struct sqlsrv_stream {
};
// close any active stream
void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void close_active_stream( _Inout_ sqlsrv_stmt* stmt );
extern php_stream_wrapper g_sqlsrv_stream_wrapper;
@ -1427,14 +1440,16 @@ struct sqlsrv_output_param {
};
namespace data_classification {
const int VERSION_RANK_AVAILABLE = 2; // Rank info is available when data classification version is 2+
const int RANK_NOT_DEFINED = -1;
// *** data classficiation metadata structures and helper methods -- to store and/or process the sensitivity classification data ***
struct name_id_pair;
struct sensitivity_metadata;
void name_id_pair_free(name_id_pair * 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 TSRMLS_CC);
void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr);
USHORT fill_column_sensitivity_array(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Inout_ zval *column_data TSRMLS_CC);
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_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr, _In_ bool getRankInfo);
USHORT fill_column_sensitivity_array(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Inout_ zval *column_data);
struct name_id_pair {
UCHAR name_len;
@ -1454,8 +1469,9 @@ namespace data_classification {
struct label_infotype_pair {
USHORT label_idx;
USHORT infotype_idx;
int rank; // Default value is "not defined"
label_infotype_pair() : label_idx(0), infotype_idx(0)
label_infotype_pair() : label_idx(0), infotype_idx(0), rank(RANK_NOT_DEFINED)
{
}
};
@ -1481,8 +1497,9 @@ namespace data_classification {
std::vector<name_id_pair*, sqlsrv_allocator<name_id_pair*>> infotypes;
USHORT num_columns;
std::vector<column_sensitivity> columns_sensitivity;
int rank; // Default value is "not defined"
sensitivity_metadata() : num_labels(0), num_infotypes(0), num_columns(0)
sensitivity_metadata() : num_labels(0), num_infotypes(0), num_columns(0), rank(RANK_NOT_DEFINED)
{
}
@ -1502,11 +1519,13 @@ struct field_meta_data;
// *** Statement resource structure ***
struct sqlsrv_stmt : public sqlsrv_context {
void free_param_data( TSRMLS_D );
virtual void new_result_set( TSRMLS_D );
void free_param_data( void );
virtual void new_result_set( void );
// free sensitivity classification metadata
void clean_up_sensitivity_metadata();
// set query timeout
void set_query_timeout();
sqlsrv_conn* conn; // Connection that created this statement
@ -1551,14 +1570,13 @@ struct sqlsrv_stmt : public sqlsrv_context {
// meta data for data classification
sqlsrv_malloc_auto_ptr<data_classification::sensitivity_metadata> current_sensitivity_metadata;
sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC );
sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv );
virtual ~sqlsrv_stmt( void );
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
virtual sqlsrv_phptype sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_to_stream ) = 0;
// driver specific way to set query timeout
virtual void set_query_timeout() = 0;
};
// *** field metadata struct ***
@ -1607,31 +1625,31 @@ const size_t SQLSRV_CURSOR_BUFFERED = 0xfffffffeUL; // arbitrary number that doe
#endif // !_WIN32
// factory to create a statement
typedef sqlsrv_stmt* (*driver_stmt_factory)( sqlsrv_conn* conn, SQLHANDLE h, error_callback e, void* drv TSRMLS_DC );
typedef sqlsrv_stmt* (*driver_stmt_factory)( sqlsrv_conn* conn, SQLHANDLE h, error_callback e, void* drv );
// *** statement functions ***
sqlsrv_stmt* core_sqlsrv_create_stmt( _Inout_ sqlsrv_conn* conn, _In_ driver_stmt_factory stmt_factory, _In_opt_ HashTable* options_ht,
_In_opt_ const stmt_option valid_stmt_opts[], _In_ error_callback const err, _In_opt_ void* driver TSRMLS_DC );
_In_opt_ const stmt_option valid_stmt_opts[], _In_ error_callback const err, _In_opt_ void* driver );
void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_num, _In_ SQLSMALLINT direction, _Inout_ zval* param_z,
_In_ SQLSRV_PHPTYPE php_out_type, _Inout_ SQLSRV_ENCODING encoding, _Inout_ SQLSMALLINT sql_type, _Inout_ SQLULEN column_size,
_Inout_ SQLSMALLINT decimal_digits TSRMLS_DC );
SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_reads_bytes_(sql_len) const char* sql = NULL, _In_ int sql_len = 0 );
field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno TSRMLS_DC );
bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLULEN fetch_offset TSRMLS_DC );
_Inout_ SQLSMALLINT decimal_digits );
SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql = NULL, _In_ int sql_len = 0 );
field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno );
bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLULEN fetch_offset );
void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ sqlsrv_phptype sqlsrv_phptype, _In_ bool prefer_string,
_Outref_result_bytebuffer_maybenull_(*field_length) void*& field_value, _Inout_ SQLLEN* field_length, _In_ bool cache_field,
_Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC);
bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool finalize_output_params = true, _In_ bool throw_on_errors = true );
void core_sqlsrv_post_param( _Inout_ sqlsrv_stmt* stmt, _In_ zend_ulong paramno, zval* param_z TSRMLS_DC );
void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long cursor_type TSRMLS_DC );
void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* value_z TSRMLS_DC );
void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC );
bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLEN limit TSRMLS_DC );
void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC);
void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
_Outref_result_bytebuffer_maybenull_(*field_length) void*& field_value, _Inout_ SQLLEN* field_length, _In_ bool cache_field,
_Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out);
bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt );
void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt, _In_ bool finalize_output_params = true, _In_ bool throw_on_errors = true );
void core_sqlsrv_post_param( _Inout_ sqlsrv_stmt* stmt, _In_ zend_ulong paramno, zval* param_z );
void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long cursor_type );
void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* value_z );
void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z );
bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z );
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLEN limit );
void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z);
void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt );
//*********************************************************************************************************************************
// Result Set
@ -1652,15 +1670,15 @@ struct sqlsrv_result_set {
virtual ~sqlsrv_result_set( void ) { }
virtual bool cached( int field_index ) = 0;
virtual SQLRETURN fetch( _Inout_ SQLSMALLINT fetch_orientation, _Inout_opt_ SQLLEN fetch_offset TSRMLS_DC ) = 0;
virtual SQLRETURN fetch( _Inout_ SQLSMALLINT fetch_orientation, _Inout_opt_ SQLLEN fetch_offset ) = 0;
virtual SQLRETURN get_data( _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_bytes_opt_(buffer_length) void* buffer, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )= 0;
bool handle_warning )= 0;
virtual SQLRETURN get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0;
_Inout_ SQLSMALLINT* out_buffer_length ) = 0;
virtual sqlsrv_error* get_diag_rec( _In_ SQLSMALLINT record_number ) = 0;
virtual SQLLEN row_count( TSRMLS_D ) = 0;
virtual SQLLEN row_count( void ) = 0;
};
struct sqlsrv_odbc_result_set : public sqlsrv_result_set {
@ -1669,15 +1687,15 @@ struct sqlsrv_odbc_result_set : public sqlsrv_result_set {
virtual ~sqlsrv_odbc_result_set( void );
virtual bool cached( int field_index ) { return false; }
virtual SQLRETURN fetch( _In_ SQLSMALLINT fetch_orientation, _In_ SQLLEN fetch_offset TSRMLS_DC );
virtual SQLRETURN fetch( _In_ SQLSMALLINT fetch_orientation, _In_ SQLLEN fetch_offset );
virtual SQLRETURN get_data( _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_opt_(buffer_length) void* buffer, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
_In_ bool handle_warning TSRMLS_DC );
_In_ bool handle_warning );
virtual SQLRETURN get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Inout_ SQLSMALLINT* out_buffer_length );
virtual sqlsrv_error* get_diag_rec( _In_ SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
virtual SQLLEN row_count( void );
private:
// prevent invalid instantiations and assignments
@ -1703,19 +1721,19 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
static const zend_long BUFFERED_QUERY_LIMIT_DEFAULT = 10240; // measured in KB
static const zend_long BUFFERED_QUERY_LIMIT_INVALID = 0;
explicit sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* odbc TSRMLS_DC );
explicit sqlsrv_buffered_result_set( _Inout_ sqlsrv_stmt* odbc );
virtual ~sqlsrv_buffered_result_set( void );
virtual bool cached( int field_index ) { return true; }
virtual SQLRETURN fetch( _Inout_ SQLSMALLINT fetch_orientation, _Inout_opt_ SQLLEN fetch_offset TSRMLS_DC );
virtual SQLRETURN fetch( _Inout_ SQLSMALLINT fetch_orientation, _Inout_opt_ SQLLEN fetch_offset );
virtual SQLRETURN get_data( _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_bytes_opt_(buffer_length) void* buffer, _In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC );
bool handle_warning );
virtual SQLRETURN get_diag_field( _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Inout_updates_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Inout_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Inout_ SQLSMALLINT* out_buffer_length );
virtual sqlsrv_error* get_diag_rec( _In_ SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
virtual SQLLEN row_count( void );
// buffered result set specific
SQLSMALLINT column_count( void )
@ -1900,12 +1918,11 @@ enum error_handling_flags {
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error,
_In_ logging_severity severity TSRMLS_DC );
_In_ logging_severity severity, _In_opt_ bool check_warning = false );
// format and return a driver specfic 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 TSRMLS_DC, _In_opt_ va_list* args );
_Out_ sqlsrv_error_auto_ptr& formatted_error, _In_ logging_severity severity, _In_opt_ va_list* args );
// return the message for the HRESULT returned by GetLastError. Some driver errors use this to
// return the Windows error, e.g, when a UTF-8 <-> UTF-16 conversion fails.
@ -1916,20 +1933,20 @@ DWORD core_sqlsrv_format_message( _Out_ char* output_buffer, _In_ unsigned outpu
// convenience functions that overload either a reference or a pointer so we can use
// either in the CHECK_* functions.
inline bool call_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned long sqlsrv_error_code TSRMLS_DC, _In_ bool warning, ... )
inline bool call_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned long sqlsrv_error_code, _In_ bool warning, ... )
{
va_list print_params;
va_start( print_params, warning );
bool ignored = ctx.error_handler()( ctx, sqlsrv_error_code, warning TSRMLS_CC, &print_params );
bool ignored = ctx.error_handler()( ctx, sqlsrv_error_code, warning, &print_params );
va_end( print_params );
return ignored;
}
inline bool call_error_handler( _Inout_ sqlsrv_context* ctx, _In_ unsigned long sqlsrv_error_code TSRMLS_DC, _In_ bool warning, ... )
inline bool call_error_handler( _Inout_ sqlsrv_context* ctx, _In_ unsigned long sqlsrv_error_code, _In_ bool warning, ... )
{
va_list print_params;
va_start( print_params, warning );
bool ignored = ctx->error_handler()( *ctx, sqlsrv_error_code, warning TSRMLS_CC, &print_params );
bool ignored = ctx->error_handler()( *ctx, sqlsrv_error_code, warning, &print_params );
va_end( print_params );
return ignored;
}
@ -1970,7 +1987,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
bool flag##unique = (condition); \
bool ignored##unique = true; \
if (flag##unique) { \
ignored##unique = call_error_handler( context, ssphp TSRMLS_CC, /*warning*/false, ## __VA_ARGS__ ); \
ignored##unique = call_error_handler( context, ssphp, /*warning*/false, ## __VA_ARGS__ ); \
} \
if( !ignored##unique )
@ -1990,7 +2007,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
#define CHECK_WARNING_AS_ERROR_UNIQUE( unique, condition, context, ssphp, ... ) \
bool ignored##unique = true; \
if( condition ) { \
ignored##unique = call_error_handler( context, ssphp TSRMLS_CC, /*warning*/true, ## __VA_ARGS__ ); \
ignored##unique = call_error_handler( context, ssphp, /*warning*/true, ## __VA_ARGS__ ); \
} \
if( !ignored##unique )
@ -1999,7 +2016,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
#define CHECK_SQL_WARNING( result, context, ... ) \
if( result == SQL_SUCCESS_WITH_INFO ) { \
(void)call_error_handler( context, 0 TSRMLS_CC, /*warning*/ true, ## __VA_ARGS__ ); \
(void)call_error_handler( context, 0, /*warning*/ true, ## __VA_ARGS__ ); \
}
#define CHECK_CUSTOM_WARNING_AS_ERROR( condition, context, ssphp, ... ) \
@ -2012,16 +2029,16 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
SQLSRV_ASSERT( result != SQL_INVALID_HANDLE, "Invalid handle returned." ); \
bool ignored = true; \
if( result == SQL_ERROR ) { \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC TSRMLS_CC, false, ##__VA_ARGS__ ); \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, false, ##__VA_ARGS__ ); \
} \
else if( result == SQL_SUCCESS_WITH_INFO ) { \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC TSRMLS_CC, true TSRMLS_CC, ##__VA_ARGS__ ); \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, true, ##__VA_ARGS__ ); \
} \
if( !ignored )
// throw an exception after it has been hooked into the custom error handler
#define THROW_CORE_ERROR( ctx, custom, ... ) \
(void)call_error_handler( ctx, custom TSRMLS_CC, /*warning*/ false, ## __VA_ARGS__ ); \
(void)call_error_handler( ctx, custom, /*warning*/ false, ## __VA_ARGS__ ); \
throw core::CoreException();
//*********************************************************************************************************************************
@ -2038,7 +2055,7 @@ namespace core {
}
};
inline void check_for_mars_error( _Inout_ sqlsrv_stmt* stmt, _In_ SQLRETURN r TSRMLS_DC )
inline void check_for_mars_error( _Inout_ sqlsrv_stmt* stmt, _In_ SQLRETURN r )
{
// Skip this if not SQL_ERROR -
// We check for the 'connection busy' error caused by having MultipleActiveResultSets off
@ -2083,7 +2100,7 @@ namespace core {
inline SQLRETURN SQLGetDiagField( _Inout_ sqlsrv_context* ctx, _In_ SQLSMALLINT record_number, _In_ SQLSMALLINT diag_identifier,
_Out_writes_opt_(buffer_length) SQLPOINTER diag_info_buffer, _In_ SQLSMALLINT buffer_length,
_Out_opt_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_opt_ SQLSMALLINT* out_buffer_length )
{
SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier,
diag_info_buffer, buffer_length, out_buffer_length );
@ -2096,7 +2113,7 @@ namespace core {
}
inline void SQLAllocHandle( _In_ SQLSMALLINT HandleType, _Inout_ sqlsrv_context& InputHandle,
_Out_ SQLHANDLE* OutputHandlePtr TSRMLS_DC )
_Out_ SQLHANDLE* OutputHandlePtr )
{
SQLRETURN r;
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
@ -2115,7 +2132,7 @@ namespace core {
_Inout_opt_ SQLPOINTER ParameterValuePtr,
_Inout_ SQLLEN BufferLength,
_Inout_ SQLLEN * StrLen_Or_IndPtr
TSRMLS_DC )
)
{
SQLRETURN r;
r = ::SQLBindParameter( stmt->handle(), ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize,
@ -2126,7 +2143,7 @@ namespace core {
}
}
inline void SQLCloseCursor( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
inline void SQLCloseCursor( _Inout_ sqlsrv_stmt* stmt )
{
SQLRETURN r = ::SQLCloseCursor( stmt->handle() );
@ -2137,7 +2154,7 @@ namespace core {
inline void SQLColAttribute( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLUSMALLINT field_identifier,
_Out_writes_bytes_opt_(buffer_length) SQLPOINTER field_type_char, _In_ SQLSMALLINT buffer_length,
_Out_opt_ SQLSMALLINT* out_buffer_length, _Out_opt_ SQLLEN* field_type_num TSRMLS_DC )
_Out_opt_ SQLSMALLINT* out_buffer_length, _Out_opt_ SQLLEN* field_type_num )
{
SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num );
@ -2149,7 +2166,7 @@ namespace core {
inline void SQLColAttributeW( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLUSMALLINT field_identifier,
_Out_writes_bytes_opt_(buffer_length) SQLPOINTER field_type_char, _In_ SQLSMALLINT buffer_length,
_Out_opt_ SQLSMALLINT* out_buffer_length, _Out_opt_ SQLLEN* field_type_num TSRMLS_DC )
_Out_opt_ SQLSMALLINT* out_buffer_length, _Out_opt_ SQLLEN* field_type_num )
{
SQLRETURN r = ::SQLColAttributeW( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num );
@ -2161,7 +2178,7 @@ namespace core {
inline void SQLDescribeCol( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Out_writes_opt_(col_name_length) SQLCHAR* col_name, _In_ SQLSMALLINT col_name_length,
_Out_opt_ SQLSMALLINT* col_name_length_out, _Out_opt_ SQLSMALLINT* data_type, _Out_opt_ SQLULEN* col_size,
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable TSRMLS_DC )
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable )
{
SQLRETURN r;
r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
@ -2174,7 +2191,7 @@ namespace core {
inline void SQLDescribeColW( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Out_writes_opt_(col_name_length) SQLWCHAR* col_name, _In_ SQLSMALLINT col_name_length,
_Out_opt_ SQLSMALLINT* col_name_length_out, _Out_opt_ SQLSMALLINT* data_type, _Out_opt_ SQLULEN* col_size,
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable TSRMLS_DC )
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable )
{
SQLRETURN r;
r = ::SQLDescribeColW( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
@ -2186,7 +2203,7 @@ namespace core {
}
inline void SQLDescribeParam( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT paramno, _Out_opt_ SQLSMALLINT* data_type, _Out_opt_ SQLULEN* col_size,
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable TSRMLS_DC )
_Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable )
{
SQLRETURN r;
r = ::SQLDescribeParam( stmt->handle(), paramno, data_type, col_size, decimal_digits, nullable );
@ -2206,7 +2223,7 @@ namespace core {
}
}
inline void SQLEndTran( _In_ SQLSMALLINT handleType, _Inout_ sqlsrv_conn* conn, _In_ SQLSMALLINT completionType TSRMLS_DC )
inline void SQLEndTran( _In_ SQLSMALLINT handleType, _Inout_ sqlsrv_conn* conn, _In_ SQLSMALLINT completionType )
{
SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType );
@ -2216,11 +2233,11 @@ namespace core {
}
// SQLExecDirect returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success
inline SQLRETURN SQLExecDirect( _Inout_ sqlsrv_stmt* stmt, _In_ char* sql TSRMLS_DC )
inline SQLRETURN SQLExecDirect( _Inout_ sqlsrv_stmt* stmt, _In_ char* sql )
{
SQLRETURN r = ::SQLExecDirect( stmt->handle(), reinterpret_cast<SQLCHAR*>( sql ), SQL_NTS );
check_for_mars_error( stmt, r TSRMLS_CC );
check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
@ -2229,12 +2246,12 @@ namespace core {
return r;
}
inline SQLRETURN SQLExecDirectW( _Inout_ sqlsrv_stmt* stmt, _In_ SQLWCHAR* wsql TSRMLS_DC )
inline SQLRETURN SQLExecDirectW( _Inout_ sqlsrv_stmt* stmt, _In_ SQLWCHAR* wsql )
{
SQLRETURN r;
r = ::SQLExecDirectW( stmt->handle(), reinterpret_cast<SQLWCHAR*>( wsql ), SQL_NTS );
check_for_mars_error( stmt, r TSRMLS_CC );
check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw CoreException();
@ -2243,12 +2260,12 @@ namespace core {
}
// SQLExecute returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success
inline SQLRETURN SQLExecute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
inline SQLRETURN SQLExecute( _Inout_ sqlsrv_stmt* stmt )
{
SQLRETURN r;
r = ::SQLExecute( stmt->handle() );
check_for_mars_error( stmt, r TSRMLS_CC );
check_for_mars_error( stmt, r );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw CoreException();
@ -2257,7 +2274,7 @@ namespace core {
return r;
}
inline SQLRETURN SQLFetchScroll( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLLEN fetch_offset TSRMLS_DC )
inline SQLRETURN SQLFetchScroll( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLLEN fetch_offset )
{
SQLRETURN r = ::SQLFetchScroll( stmt->handle(), fetch_orientation, fetch_offset );
@ -2269,14 +2286,14 @@ namespace core {
// wrap SQLFreeHandle and report any errors, but don't actually signal an error to the calling routine
inline void SQLFreeHandle( _Inout_ sqlsrv_context& ctx TSRMLS_DC )
inline void SQLFreeHandle( _Inout_ sqlsrv_context& ctx )
{
SQLRETURN r;
r = ::SQLFreeHandle( ctx.handle_type(), ctx.handle() );
CHECK_SQL_ERROR_OR_WARNING( r, ctx ) {}
}
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 TSRMLS_DC)
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;
r = ::SQLGetStmtAttr( stmt->handle(), attr, value_ptr, buf_len, str_len );
@ -2287,7 +2304,7 @@ namespace core {
inline SQLRETURN SQLGetData( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLSMALLINT target_type,
_Out_writes_opt_(buffer_length) void* buffer, _In_ SQLLEN buffer_length, _Out_opt_ SQLLEN* out_buffer_length,
_In_ bool handle_warning TSRMLS_DC )
_In_ bool handle_warning )
{
SQLRETURN r = ::SQLGetData( stmt->handle(), field_index, target_type, buffer, buffer_length, out_buffer_length );
@ -2309,7 +2326,7 @@ namespace core {
inline void SQLGetInfo( _Inout_ sqlsrv_conn* conn, _In_ SQLUSMALLINT info_type, _Out_writes_bytes_opt_(buffer_len) SQLPOINTER info_value, _In_ SQLSMALLINT buffer_len,
_Out_opt_ SQLSMALLINT* str_len TSRMLS_DC )
_Out_opt_ SQLSMALLINT* str_len )
{
SQLRETURN r;
r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len );
@ -2320,7 +2337,7 @@ namespace core {
}
inline void SQLGetTypeInfo( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT data_type TSRMLS_DC )
inline void SQLGetTypeInfo( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT data_type )
{
SQLRETURN r;
r = ::SQLGetTypeInfo( stmt->handle(), data_type );
@ -2332,7 +2349,7 @@ namespace core {
// SQLMoreResults returns the status code since it returns SQL_NO_DATA when there is no more data in a result set.
inline SQLRETURN SQLMoreResults( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
inline SQLRETURN SQLMoreResults( _Inout_ sqlsrv_stmt* stmt )
{
SQLRETURN r = ::SQLMoreResults( stmt->handle() );
@ -2343,7 +2360,7 @@ namespace core {
return r;
}
inline SQLSMALLINT SQLNumResultCols( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
inline SQLSMALLINT SQLNumResultCols( _Inout_ sqlsrv_stmt* stmt )
{
SQLRETURN r;
SQLSMALLINT num_cols;
@ -2358,7 +2375,7 @@ namespace core {
// SQLParamData returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA when there are more
// parameters or when the parameters are all processed.
inline SQLRETURN SQLParamData( _Inout_ sqlsrv_stmt* stmt, _Out_opt_ SQLPOINTER* value_ptr_ptr TSRMLS_DC )
inline SQLRETURN SQLParamData( _Inout_ sqlsrv_stmt* stmt, _Out_opt_ SQLPOINTER* value_ptr_ptr )
{
SQLRETURN r;
r = ::SQLParamData( stmt->handle(), value_ptr_ptr );
@ -2368,7 +2385,7 @@ namespace core {
return r;
}
inline void SQLPrepareW( _Inout_ sqlsrv_stmt* stmt, _In_reads_(sql_len) SQLWCHAR * sql, _In_ SQLINTEGER sql_len TSRMLS_DC )
inline void SQLPrepareW( _Inout_ sqlsrv_stmt* stmt, _In_reads_(sql_len) SQLWCHAR * sql, _In_ SQLINTEGER sql_len )
{
SQLRETURN r;
r = ::SQLPrepareW( stmt->handle(), sql, sql_len );
@ -2378,7 +2395,7 @@ namespace core {
}
inline void SQLPutData( _Inout_ sqlsrv_stmt* stmt, _In_reads_(strlen_or_ind) SQLPOINTER data_ptr, _In_ SQLLEN strlen_or_ind TSRMLS_DC )
inline void SQLPutData( _Inout_ sqlsrv_stmt* stmt, _In_reads_(strlen_or_ind) SQLPOINTER data_ptr, _In_ SQLLEN strlen_or_ind )
{
SQLRETURN r;
r = ::SQLPutData( stmt->handle(), data_ptr, strlen_or_ind );
@ -2388,7 +2405,7 @@ namespace core {
}
inline SQLLEN SQLRowCount( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
inline SQLLEN SQLRowCount( _Inout_ sqlsrv_stmt* stmt )
{
SQLRETURN r;
SQLLEN rows_affected;
@ -2415,7 +2432,7 @@ namespace core {
}
inline void SQLSetConnectAttr( _Inout_ sqlsrv_context& ctx, _In_ SQLINTEGER attr, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC )
inline void SQLSetConnectAttr( _Inout_ sqlsrv_context& ctx, _In_ SQLINTEGER attr, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len )
{
SQLRETURN r;
r = ::SQLSetConnectAttr( ctx.handle(), attr, value_ptr, str_len );
@ -2425,7 +2442,7 @@ namespace core {
}
}
inline void SQLSetDescField( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT rec_num, _In_ SQLSMALLINT fld_id, _In_reads_bytes_opt_( str_len ) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC )
inline void SQLSetDescField( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT rec_num, _In_ SQLSMALLINT fld_id, _In_reads_bytes_opt_( str_len ) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len )
{
SQLRETURN r;
SQLHDESC hIpd = NULL;
@ -2438,7 +2455,7 @@ namespace core {
}
}
inline void SQLSetEnvAttr( _Inout_ sqlsrv_context& ctx, _In_ SQLINTEGER attr, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC )
inline void SQLSetEnvAttr( _Inout_ sqlsrv_context& ctx, _In_ SQLINTEGER attr, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len )
{
SQLRETURN r;
r = ::SQLSetEnvAttr( ctx.handle(), attr, value_ptr, str_len );
@ -2447,7 +2464,7 @@ namespace core {
}
}
inline void SQLSetConnectAttr( _Inout_ sqlsrv_conn* conn, _In_ SQLINTEGER attribute, _In_reads_bytes_opt_(value_len) SQLPOINTER value_ptr, _In_ SQLINTEGER value_len TSRMLS_DC )
inline void SQLSetConnectAttr( _Inout_ sqlsrv_conn* conn, _In_ SQLINTEGER attribute, _In_reads_bytes_opt_(value_len) SQLPOINTER value_ptr, _In_ SQLINTEGER value_len )
{
SQLRETURN r = ::SQLSetConnectAttr( conn->handle(), attribute, value_ptr, value_len );
@ -2456,7 +2473,7 @@ namespace core {
}
}
inline void SQLSetStmtAttr( _Inout_ sqlsrv_stmt* stmt, _In_ SQLINTEGER attr, _In_reads_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC )
inline void SQLSetStmtAttr( _Inout_ sqlsrv_stmt* stmt, _In_ SQLINTEGER attr, _In_reads_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len )
{
SQLRETURN r;
r = ::SQLSetStmtAttr( stmt->handle(), attr, value_ptr, str_len );
@ -2485,78 +2502,7 @@ namespace core {
}
}
// exception thrown when a zend function wrapped here fails.
// wrappers for the zend functions called by our driver. These functions hook into the error reporting of our driver and throw
// exceptions when an error occurs. They are prefaced with sqlsrv_<zend_function_name> because many of the zend functions are
// actually macros that call other functions, so the sqlsrv_ is necessary to differentiate them from the macro system.
// If there is a zend function in the source that isn't found here, it is because it returns void and there is no error
// that can be thrown from it.
inline void sqlsrv_add_index_zval( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array, _In_ zend_ulong index, _In_ zval* value TSRMLS_DC)
{
int zr = add_index_zval( array, index, value );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_add_next_index_zval( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array, _In_ zval* value TSRMLS_DC)
{
int zr = add_next_index_zval( array, value );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_add_assoc_null( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array_z, _In_ const char* key TSRMLS_DC )
{
int zr = ::add_assoc_null( array_z, key );
CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_add_assoc_long( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array_z, _In_ const char* key, _In_ zend_long val TSRMLS_DC )
{
int zr = ::add_assoc_long( array_z, key, val );
CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_add_assoc_string( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array_z, _In_ const char* key, _Inout_z_ char* val, _In_ bool duplicate TSRMLS_DC )
{
int zr = ::add_assoc_string(array_z, key, val);
CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
if (duplicate == 0) {
sqlsrv_free(val);
}
}
inline void sqlsrv_add_assoc_zval( _Inout_ sqlsrv_context& ctx, _Inout_ zval* array_z, _In_ const char* key, _In_ zval* val TSRMLS_DC )
{
int zr = ::add_assoc_zval(array_z, key, val);
CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_array_init( _Inout_ sqlsrv_context& ctx, _Out_ zval* new_array TSRMLS_DC)
{
#if PHP_VERSION_ID < 70300
CHECK_ZEND_ERROR(::array_init(new_array), ctx, SQLSRV_ERROR_ZEND_HASH) {
throw CoreException();
}
#else
array_init(new_array);
#endif
}
inline void sqlsrv_php_stream_from_zval_no_verify( _Inout_ sqlsrv_context& ctx, _Outref_result_maybenull_ php_stream*& stream, _In_opt_ zval* stream_z TSRMLS_DC )
inline void sqlsrv_php_stream_from_zval_no_verify( _Inout_ sqlsrv_context& ctx, _Outref_result_maybenull_ php_stream*& stream, _In_opt_ zval* stream_z )
{
// this duplicates the macro php_stream_from_zval_no_verify, which we can't use because it has an assignment
php_stream_from_zval_no_verify( stream, stream_z );
@ -2565,7 +2511,7 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_get_current_data( _In_ sqlsrv_context& ctx, _In_ HashTable* ht, _Outref_result_maybenull_ zval*& output_data TSRMLS_DC)
inline void sqlsrv_zend_hash_get_current_data( _In_ sqlsrv_context& ctx, _In_ HashTable* ht, _Outref_result_maybenull_ zval*& output_data)
{
int zr = (output_data = ::zend_hash_get_current_data(ht)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
@ -2573,7 +2519,7 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_get_current_data_ptr( _Inout_ sqlsrv_context& ctx, _In_ HashTable* ht, _Outref_result_maybenull_ void*& output_data TSRMLS_DC)
inline void sqlsrv_zend_hash_get_current_data_ptr( _Inout_ sqlsrv_context& ctx, _In_ HashTable* ht, _Outref_result_maybenull_ void*& output_data)
{
int zr = (output_data = ::zend_hash_get_current_data_ptr(ht)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
@ -2581,7 +2527,7 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_index_del( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index TSRMLS_DC )
inline void sqlsrv_zend_hash_index_del( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index )
{
int zr = ::zend_hash_index_del( ht, index );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
@ -2589,7 +2535,7 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_index_update( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_ zval* data_z TSRMLS_DC )
inline void sqlsrv_zend_hash_index_update( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_ zval* data_z )
{
int zr = (data_z = ::zend_hash_index_update(ht, index, data_z)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
@ -2597,7 +2543,7 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_index_update_ptr( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_ void* pData TSRMLS_DC)
inline void sqlsrv_zend_hash_index_update_ptr( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_ void* pData)
{
int zr = (pData = ::zend_hash_index_update_ptr(ht, index, pData)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
@ -2606,15 +2552,15 @@ namespace core {
}
inline void sqlsrv_zend_hash_index_update_mem( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_reads_bytes_(size) void* pData, _In_ std::size_t size TSRMLS_DC)
{
int zr = (pData = ::zend_hash_index_update_mem(ht, index, pData, size)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_index_update_mem( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zend_ulong index, _In_reads_bytes_(size) void* pData, _In_ std::size_t size)
{
int zr = (pData = ::zend_hash_index_update_mem(ht, index, pData, size)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_next_index_insert( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zval* data TSRMLS_DC )
inline void sqlsrv_zend_hash_next_index_insert( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ zval* data )
{
int zr = (data = ::zend_hash_next_index_insert(ht, data)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
@ -2622,15 +2568,15 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_next_index_insert_mem( _Inout_ sqlsrv_context& ctx, _In_ HashTable* ht, _In_reads_bytes_(data_size) void* data, _In_ size_t data_size TSRMLS_DC)
{
int zr = (data = ::zend_hash_next_index_insert_mem(ht, data, data_size)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_next_index_insert_mem( _Inout_ sqlsrv_context& ctx, _In_ HashTable* ht, _In_reads_bytes_(data_size) void* data, _In_ size_t data_size)
{
int zr = (data = ::zend_hash_next_index_insert_mem(ht, data, data_size)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_next_index_insert_ptr( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ void* data TSRMLS_DC)
inline void sqlsrv_zend_hash_next_index_insert_ptr( _Inout_ sqlsrv_context& ctx, _Inout_ HashTable* ht, _In_ void* data)
{
int zr = (data = ::zend_hash_next_index_insert_ptr(ht, data)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) {
@ -2639,21 +2585,21 @@ namespace core {
}
inline void sqlsrv_zend_hash_init(sqlsrv_context& ctx, _Inout_ HashTable* ht, _Inout_ uint32_t initial_size,
_In_ dtor_func_t dtor_fn, _In_ zend_bool persistent TSRMLS_DC )
_In_ dtor_func_t dtor_fn, _In_ zend_bool persistent )
{
::zend_hash_init(ht, initial_size, NULL, dtor_fn, persistent);
}
template <typename Statement>
sqlsrv_stmt* allocate_stmt( _In_ sqlsrv_conn* conn, _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC )
sqlsrv_stmt* allocate_stmt( _In_ sqlsrv_conn* conn, _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver )
{
return new ( sqlsrv_malloc( sizeof( Statement ))) Statement( conn, h, e, driver TSRMLS_CC );
return new ( sqlsrv_malloc( sizeof( Statement ))) Statement( conn, h, e, driver );
}
template <typename Connection>
sqlsrv_conn* allocate_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC )
sqlsrv_conn* allocate_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver )
{
return new ( sqlsrv_malloc( sizeof( Connection ))) Connection( h, e, driver TSRMLS_CC );
return new ( sqlsrv_malloc( sizeof( Connection ))) Connection( h, e, driver );
}
} // namespace core
@ -2661,10 +2607,10 @@ sqlsrv_conn* allocate_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void*
template <unsigned int Attr>
struct str_conn_attr_func {
static void func( connection_option const* /*option*/, zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_STRVAL_P( value )), static_cast<SQLINTEGER>( Z_STRLEN_P( value )) TSRMLS_CC );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_STRVAL_P( value )), static_cast<SQLINTEGER>( Z_STRLEN_P( value )) );
}
catch ( core::CoreException& ) {
throw;

View file

@ -3,7 +3,7 @@
//
// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -66,6 +66,9 @@ struct col_cache {
const int INITIAL_FIELD_STRING_LEN = 2048; // base allocation size when retrieving a string field
const char DECIMAL_POINT = '.';
const int SQL_SERVER_DECIMAL_MAXIMUM_PRECISION = 38; // 38 is the maximum length of a stringified decimal number
// UTF-8 tags for byte length of characters, used by streams to make sure we don't clip a character in between reads
const unsigned int UTF8_MIDBYTE_MASK = 0xc0;
const unsigned int UTF8_MIDBYTE_TAG = 0x80;
@ -92,44 +95,45 @@ const size_t DATE_FORMAT_LEN = sizeof( DATE_FORMAT );
// *** internal functions ***
// Only declarations are put here. Functions contain the documentation they need at their definition sites.
void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLLEN sql_type, _Inout_ SQLLEN& size TSRMLS_DC );
size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) const char* buffer, _In_ size_t buffer_end TSRMLS_DC );
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLLEN sql_type, _Inout_ SQLLEN& size );
size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) const char* buffer, _In_ size_t buffer_end );
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt );
bool convert_input_param_to_utf16( _In_ zval* input_param_z, _Inout_ zval* convert_param_z );
void core_get_field_common(_Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype
sqlsrv_php_type, _Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len TSRMLS_DC);
sqlsrv_php_type, _Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval const* param_z, _In_ SQLSRV_ENCODING encoding TSRMLS_DC );
SQLSMALLINT default_c_type(_Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval const* param_z, _In_ SQLSMALLINT sql_type, _In_ SQLSRV_ENCODING encoding);
void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned int paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC );
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits );
// given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate)
void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding,
_Out_ SQLSMALLINT& sql_type TSRMLS_DC );
_Out_ SQLSMALLINT& sql_type );
void col_cache_dtor( _Inout_ zval* data_z );
void field_cache_dtor( _Inout_ zval* data_z );
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ short decimal_pos, _In_ short decimals_places, _In_ short offset, _In_ short lastpos);
void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT field_scale, _Inout_updates_bytes_(*field_len) char*& field_value, _Inout_ SQLLEN* field_len);
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt, _In_opt_ bool exception_thrown = false );
void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype sqlsrv_php_type,
_Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len TSRMLS_DC );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, _In_ zend_ulong key, _In_ const stmt_option stmt_opts[] TSRMLS_DC );
_Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, _In_ zend_ulong key, _In_ const stmt_option stmt_opts[] );
bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type );
// assure there is enough space for the output parameter string
void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z, _In_ SQLULEN paramno, SQLSRV_ENCODING encoding,
_In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _In_ SQLSMALLINT decimal_digits,
_Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len TSRMLS_DC );
void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits );
void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param TSRMLS_DC );
_Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len );
void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits);
void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param );
// send all the stream data
void send_param_streams( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void send_param_streams( _Inout_ sqlsrv_stmt* stmt );
// called when a bound output string parameter is to be destroyed
void sqlsrv_output_param_dtor( _Inout_ zval* data );
// called when a bound stream parameter is to be destroyed.
void sqlsrv_stream_dtor( _Inout_ zval* data );
bool is_a_numeric_type(_In_ SQLSMALLINT sql_type);
}
// constructor for sqlsrv_stmt. Here so that we can use functions declared earlier.
sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC ) :
sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv ) :
sqlsrv_context( handle, SQL_HANDLE_STMT, e, drv, SQLSRV_ENCODING_DEFAULT ),
conn( c ),
executed( false ),
@ -155,34 +159,33 @@ sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error
{
ZVAL_UNDEF( &active_stream );
// initialize the input string parameters array (which holds zvals)
core::sqlsrv_array_init( *conn, &param_input_strings TSRMLS_CC );
array_init(&param_input_strings);
// initialize the (input only) stream parameters (which holds sqlsrv_stream structures)
ZVAL_NEW_ARR( &param_streams );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( param_streams ), 5 /* # of buckets */, sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC);
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( param_streams ), 5 /* # of buckets */, sqlsrv_stream_dtor, 0 /*persistent*/);
// initialize the (input only) datetime parameters of converted date time objects to strings
array_init( &param_datetime_buffers );
// initialize the output string parameters (which holds sqlsrv_output_param structures)
ZVAL_NEW_ARR( &output_params );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( output_params ), 5 /* # of buckets */, sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC);
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( output_params ), 5 /* # of buckets */, sqlsrv_output_param_dtor, 0 /*persistent*/);
// initialize the col cache
ZVAL_NEW_ARR( &col_cache );
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL(col_cache), 5 /* # of buckets */, col_cache_dtor, 0 /*persistent*/ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL(col_cache), 5 /* # of buckets */, col_cache_dtor, 0 /*persistent*/ );
// initialize the field cache
ZVAL_NEW_ARR( &field_cache );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL(field_cache), 5 /* # of buckets */, field_cache_dtor, 0 /*persistent*/ TSRMLS_CC);
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL(field_cache), 5 /* # of buckets */, field_cache_dtor, 0 /*persistent*/);
}
// desctructor for sqlsrv statement.
sqlsrv_stmt::~sqlsrv_stmt( void )
{
if( Z_TYPE( active_stream ) != IS_UNDEF ) {
TSRMLS_FETCH();
close_active_stream( this TSRMLS_CC );
close_active_stream( this );
}
// delete any current results
@ -208,7 +211,7 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
// centralized place to release (without destroying the hash tables
// themselves) all the parameter data that accrues during the
// execution phase.
void sqlsrv_stmt::free_param_data( TSRMLS_D )
void sqlsrv_stmt::free_param_data( void )
{
SQLSRV_ASSERT(Z_TYPE( param_input_strings ) == IS_ARRAY && Z_TYPE( param_streams ) == IS_ARRAY,
"sqlsrv_stmt::free_param_data: Param zvals aren't arrays." );
@ -224,7 +227,7 @@ void sqlsrv_stmt::free_param_data( TSRMLS_D )
// to be called whenever a new result set is created, such as after an
// execute or next_result. Resets the state variables.
void sqlsrv_stmt::new_result_set( TSRMLS_D )
void sqlsrv_stmt::new_result_set( void )
{
this->fetch_called = false;
this->has_rows = false;
@ -254,7 +257,7 @@ void sqlsrv_stmt::new_result_set( TSRMLS_D )
if( cursor_type == SQLSRV_CURSOR_BUFFERED ) {
sqlsrv_malloc_auto_ptr<sqlsrv_buffered_result_set> result;
result = reinterpret_cast<sqlsrv_buffered_result_set*> ( sqlsrv_malloc( sizeof( sqlsrv_buffered_result_set ) ) );
new ( result.get() ) sqlsrv_buffered_result_set( this TSRMLS_CC );
new ( result.get() ) sqlsrv_buffered_result_set( this );
current_results = result.get();
result.transferred();
}
@ -272,6 +275,15 @@ void sqlsrv_stmt::clean_up_sensitivity_metadata()
}
}
void sqlsrv_stmt::set_query_timeout()
{
if (query_timeout == QUERY_TIMEOUT_INVALID || query_timeout < 0) {
return;
}
core::SQLSetStmtAttr(this, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>((SQLLEN)query_timeout), SQL_IS_UINTEGER);
}
// core_sqlsrv_create_stmt
// Common code to allocate a statement from either driver. Returns a valid driver statement object or
// throws an exception if an error occurs.
@ -286,7 +298,7 @@ void sqlsrv_stmt::clean_up_sensitivity_metadata()
// Returns the created statement
sqlsrv_stmt* core_sqlsrv_create_stmt( _Inout_ sqlsrv_conn* conn, _In_ driver_stmt_factory stmt_factory, _In_opt_ HashTable* options_ht,
_In_opt_ const stmt_option valid_stmt_opts[], _In_ error_callback const err, _In_opt_ void* driver TSRMLS_DC )
_In_opt_ const stmt_option valid_stmt_opts[], _In_ error_callback const err, _In_opt_ void* driver )
{
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> stmt;
SQLHANDLE stmt_h = SQL_NULL_HANDLE;
@ -294,9 +306,9 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( _Inout_ sqlsrv_conn* conn, _In_ driver_stm
try {
core::SQLAllocHandle( SQL_HANDLE_STMT, *conn, &stmt_h TSRMLS_CC );
core::SQLAllocHandle( SQL_HANDLE_STMT, *conn, &stmt_h );
stmt = stmt_factory( conn, stmt_h, err, driver TSRMLS_CC );
stmt = stmt_factory( conn, stmt_h, err, driver );
stmt->conn = conn;
@ -317,14 +329,14 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( _Inout_ sqlsrv_conn* conn, _In_ driver_stm
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." );
const stmt_option* stmt_opt = get_stmt_option( stmt->conn, index, valid_stmt_opts TSRMLS_CC );
const stmt_option* stmt_opt = get_stmt_option( stmt->conn, index, valid_stmt_opts );
// if the key didn't match, then return the error to the script.
// The driver layer should ensure that the key is valid.
DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." );
// perform the actions the statement option needs done.
(*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC );
(*stmt_opt->func)( stmt, stmt_opt, value_z );
} ZEND_HASH_FOREACH_END();
}
@ -377,7 +389,7 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( _Inout_ sqlsrv_conn* conn, _In_ driver_stm
void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_num, _In_ SQLSMALLINT direction, _Inout_ zval* param_z,
_In_ SQLSRV_PHPTYPE php_out_type, _Inout_ SQLSRV_ENCODING encoding, _Inout_ SQLSMALLINT sql_type, _Inout_ SQLULEN column_size,
_Inout_ SQLSMALLINT decimal_digits TSRMLS_DC )
_Inout_ SQLSMALLINT decimal_digits )
{
SQLSMALLINT c_type;
SQLPOINTER buffer = NULL;
@ -493,16 +505,16 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
else{
// if the sql type is unknown, then set the default based on the PHP type passed in
if( sql_type == SQL_UNKNOWN_TYPE ){
default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC );
default_sql_type( stmt, param_num, param_z, encoding, sql_type );
}
// if the size is unknown, then set the default based on the PHP type passed in
if( column_size == SQLSRV_UNKNOWN_SIZE ){
default_sql_size_and_scale( stmt, static_cast<unsigned int>(param_num), param_z, encoding, column_size, decimal_digits TSRMLS_CC );
default_sql_size_and_scale( stmt, static_cast<unsigned int>(param_num), param_z, encoding, column_size, decimal_digits );
}
}
// determine the ODBC C type
c_type = default_c_type( stmt, param_num, param_z, encoding TSRMLS_CC );
c_type = default_c_type(stmt, param_num, param_z, sql_type, encoding);
// set the buffer based on the PHP parameter type
switch( Z_TYPE_P( param_z )){
@ -527,7 +539,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
if( direction != SQL_PARAM_INPUT ){
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), zval_was_bool, php_out_type);
save_output_param_for_later( stmt, output_param TSRMLS_CC );
save_output_param_for_later( stmt, output_param );
}
}
break;
@ -539,21 +551,27 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
if( direction != SQL_PARAM_INPUT ){
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), zval_was_bool, php_out_type);
save_output_param_for_later( stmt, output_param TSRMLS_CC );
save_output_param_for_later( stmt, output_param );
}
}
break;
case IS_STRING:
{
if ( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC ) {
adjustInputPrecision( param_z, decimal_digits );
// 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
// 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).
if (sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC) {
adjustDecimalPrecision(param_z, decimal_digits);
}
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
bool is_numeric = is_a_numeric_type(sql_type);
// if the encoding is UTF-8, translate from UTF-8 to UTF-16 (the type variables should have already been adjusted)
if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ){
if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 && !is_numeric){
zval wbuffer_z;
ZVAL_NULL( &wbuffer_z );
@ -565,7 +583,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
}
buffer = Z_STRVAL_P( &wbuffer_z );
buffer_len = Z_STRLEN_P( &wbuffer_z );
core::sqlsrv_add_index_zval( *stmt, &( stmt->param_input_strings ), param_num, &wbuffer_z TSRMLS_CC );
add_index_zval(&(stmt->param_input_strings), param_num, &wbuffer_z);
}
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ){
@ -600,14 +618,16 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
// since this is an output string, assure there is enough space to hold the requested size and
// set all the variables necessary (param_z, buffer, buffer_len, and ind_ptr)
resize_output_buffer_if_necessary( stmt, param_z, param_num, encoding, c_type, sql_type, column_size, decimal_digits,
buffer, buffer_len TSRMLS_CC );
buffer, buffer_len );
// save the parameter to be adjusted and/or converted after the results are processed
sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast<SQLUINTEGER>( buffer_len ) );
// no need to use wide chars for numeric types
SQLSRV_ENCODING enc = (is_numeric) ? SQLSRV_ENCODING_CHAR : encoding;
sqlsrv_output_param output_param(param_ref, enc, param_num, static_cast<SQLUINTEGER>(buffer_len));
output_param.saveMetaData(sql_type, column_size, decimal_digits);
save_output_param_for_later( stmt, output_param TSRMLS_CC );
save_output_param_for_later( stmt, output_param );
// For output parameters, if we set the column_size to be same as the buffer_len,
// then if there is a truncation due to the data coming from the server being
@ -638,7 +658,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." );
sqlsrv_stream stream_encoding( param_z, encoding );
HashTable* streams_ht = Z_ARRVAL( stmt->param_streams );
core::sqlsrv_zend_hash_index_update_mem( *stmt, streams_ht, param_num, &stream_encoding, sizeof(stream_encoding) TSRMLS_CC );
core::sqlsrv_zend_hash_index_update_mem( *stmt, streams_ht, param_num, &stream_encoding, sizeof(stream_encoding) );
buffer = reinterpret_cast<SQLPOINTER>( param_num );
Z_TRY_ADDREF_P( param_z ); // so that it doesn't go away while we're using it
buffer_len = 0;
@ -659,7 +679,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
bool valid_class_name_found = false;
zend_class_entry *class_entry = Z_OBJCE_P( param_z TSRMLS_CC );
zend_class_entry *class_entry = Z_OBJCE_P( param_z );
while( class_entry != NULL ){
SQLSRV_ASSERT( class_entry->name != NULL, "core_sqlsrv_bind_param: class_entry->name is NULL." );
@ -698,7 +718,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
params[0] = format_z;
// This is equivalent to the PHP code: $param_z->format( $format_z ); where param_z is the
// DateTime object and $format_z is the format string.
int zr = call_user_function( EG( function_table ), param_z, &function_z, &buffer_z, 1, params TSRMLS_CC );
int zr = call_user_function( EG( function_table ), param_z, &function_z, &buffer_z, 1, params );
zend_string_release( Z_STR( format_z ));
zend_string_release( Z_STR( function_z ));
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ){
@ -727,17 +747,25 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
}
core::SQLBindParameter( stmt, param_num + 1, direction,
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr TSRMLS_CC );
if ( stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP )
{
if( decimal_digits == 3 )
core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER );
else if (decimal_digits == 0)
core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER );
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr );
// When calling SQLDescribeParam() on a parameter targeting a Datetime column, the return values for ParameterType, ColumnSize and DecimalDigits are SQL_TYPE_TIMESTAMP, 23, and 3 respectively.
// 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.
// 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.
// TODO: how to correctly distinguish datetime from datetime2(3)? Both have the same decimal_digits and column_size
if (stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP) {
if (decimal_digits == 3) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER);
} else if (decimal_digits == 0 && column_size == 16) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER);
}
}
}
catch( core::CoreException& e ){
stmt->free_param_data( TSRMLS_C );
stmt->free_param_data();
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
throw e;
}
@ -751,14 +779,14 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
// Return:
// true if there is data, false if there is not
SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_reads_bytes_(sql_len) const char* sql, _In_ int sql_len )
SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ int sql_len )
{
SQLRETURN r = SQL_ERROR;
try {
// close the stream to release the resource
close_active_stream( stmt TSRMLS_CC );
close_active_stream( stmt );
if( sql ) {
@ -778,25 +806,25 @@ SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_reads_by
throw core::CoreException();
}
}
r = core::SQLExecDirectW( stmt, wsql_string TSRMLS_CC );
r = core::SQLExecDirectW( stmt, wsql_string );
}
else {
r = core::SQLExecute( stmt TSRMLS_CC );
r = core::SQLExecute( stmt );
}
// if data is needed (streams were bound) and they should be sent at execute time, then do so now
if( r == SQL_NEED_DATA && stmt->send_streams_at_exec ) {
send_param_streams( stmt TSRMLS_CC );
send_param_streams( stmt );
}
stmt->new_result_set( TSRMLS_C );
stmt->new_result_set();
stmt->executed = true;
// if all the data has been sent and no data was returned then finalize the output parameters
if( stmt->send_streams_at_exec && ( r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) {
if( stmt->send_streams_at_exec && ( r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt ))) {
finalize_output_parameters( stmt TSRMLS_CC );
finalize_output_parameters( stmt );
}
// stream parameters are sent, clean the Hashtable
if ( stmt->send_streams_at_exec ) {
@ -809,7 +837,7 @@ SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_reads_by
// if the statement executed but failed in a subsequent operation before returning,
// we need to cancel the statement and deref the output and stream parameters
if ( stmt->send_streams_at_exec ) {
finalize_output_parameters( stmt TSRMLS_CC );
finalize_output_parameters( stmt, true );
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
if( stmt->executed ) {
@ -832,7 +860,7 @@ SQLRETURN core_sqlsrv_execute( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_reads_by
// Nothing, exception thrown if an error. stmt->past_fetch_end is set to true if the
// user scrolls past a non-scrollable result set
bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLULEN fetch_offset TSRMLS_DC )
bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orientation, _In_ SQLULEN fetch_offset )
{
// pre-condition check
SQLSRV_ASSERT( fetch_orientation >= SQL_FETCH_NEXT || fetch_orientation <= SQL_FETCH_RELATIVE,
@ -857,7 +885,7 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
if (stmt->column_count != ACTIVE_NUM_COLS_INVALID) {
has_fields = stmt->column_count;
} else {
has_fields = core::SQLNumResultCols( stmt TSRMLS_CC );
has_fields = core::SQLNumResultCols( stmt );
stmt->column_count = has_fields;
}
@ -867,7 +895,7 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
}
// close the stream to release the resource
close_active_stream( stmt TSRMLS_CC );
close_active_stream( stmt );
// if the statement has rows and is not scrollable but doesn't yet have
// fetch_called, this must be the first time we've called sqlsrv_fetch.
@ -878,7 +906,7 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
// move to the record requested. For absolute records, we use a 0 based offset, so +1 since
// SQLFetchScroll uses a 1 based offset, otherwise for relative, just use the fetch_offset provided.
SQLRETURN r = stmt->current_results->fetch( fetch_orientation, ( fetch_orientation == SQL_FETCH_RELATIVE ) ? fetch_offset : fetch_offset + 1 TSRMLS_CC );
SQLRETURN r = stmt->current_results->fetch( fetch_orientation, ( fetch_orientation == SQL_FETCH_RELATIVE ) ? fetch_offset : fetch_offset + 1 );
if( r == SQL_NO_DATA ) {
// if this is a forward only cursor, mark that we've passed the end so future calls result in an error
@ -911,7 +939,7 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
// Return:
// A field_meta_data* consisting of the field metadata.
field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno TSRMLS_DC )
field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno )
{
// pre-condition check
SQLSRV_ASSERT( colno >= 0, "core_sqlsrv_field_metadata: Invalid column number provided." );
@ -927,7 +955,7 @@ field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQL
try{
core::SQLDescribeColW( stmt, colno + 1, field_name_temp, SS_MAXCOLNAMELEN + 1, &field_len_temp,
&( meta_data->field_type ), & ( meta_data->field_size ), & ( meta_data->field_scale ),
&( meta_data->field_is_nullable ) TSRMLS_CC );
&( meta_data->field_is_nullable ) );
}
catch ( core::CoreException& e ) {
throw e;
@ -971,7 +999,7 @@ field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQL
SQLSMALLINT out_buff_len;
SQLLEN not_used;
core::SQLColAttribute(stmt, colno + 1, SQL_DESC_TYPE_NAME, field_type_name,
sizeof( field_type_name ), &out_buff_len, &not_used TSRMLS_CC);
sizeof( field_type_name ), &out_buff_len, &not_used);
if (!strcmp(field_type_name, "money") || !strcmp(field_type_name, "smallmoney")) {
meta_data->field_is_money_type = true;
@ -986,10 +1014,11 @@ field_meta_data* core_sqlsrv_field_metadata( _Inout_ sqlsrv_stmt* stmt, _In_ SQL
return result_field_meta_data;
}
void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt )
{
sqlsrv_malloc_auto_ptr<unsigned char> dcbuf;
SQLINTEGER dclen = 0;
DWORD dcVersion = 0;
SQLINTEGER dclen = 0, dcIRD = 0;
SQLINTEGER dclenout = 0;
SQLHANDLE ird;
SQLRETURN r;
@ -1011,14 +1040,14 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// Reference: https://docs.microsoft.com/sql/connect/odbc/data-classification
// 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
r = ::SQLGetStmtAttr(stmt->handle(), SQL_ATTR_IMP_ROW_DESC, (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) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in getting Implementation Row Descriptor handle." );
throw core::CoreException();
}
// First call to get dclen
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, dcbuf, 0, &dclen);
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, reinterpret_cast<SQLPOINTER>(dcbuf.get()), 0, &dclen);
if (r != SQL_SUCCESS || dclen == 0) {
// log the error first
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW first time." );
@ -1027,7 +1056,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
SQLRETURN rc;
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {'\0'};
SQLSMALLINT len;
rc = ::SQLGetDiagField(SQL_HANDLE_DESC, ird, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC);
rc = ::SQLGetDiagField(SQL_HANDLE_DESC, ird, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
CHECK_SQL_ERROR_OR_WARNING(rc, stmt) {
throw core::CoreException();
@ -1045,7 +1074,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// Call again to read SQL_CA_SS_DATA_CLASSIFICATION data
dcbuf = static_cast<unsigned char*>(sqlsrv_malloc(dclen * sizeof(char)));
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, dcbuf, dclen, &dclenout);
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION, reinterpret_cast<SQLPOINTER>(dcbuf.get()), dclen, &dclenout);
if (r != SQL_SUCCESS) {
LOG(SEV_ERROR, "core_sqlsrv_sensitivity_metadata: failed in calling SQLGetDescFieldW again." );
@ -1056,6 +1085,16 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// Start parsing the data (blob)
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.
// When the function is successful and the version is right, rank info is available for retrieval
bool getRankInfo = false;
r = ::SQLGetDescFieldW(ird, 0, SQL_CA_SS_DATA_CLASSIFICATION_VERSION, reinterpret_cast<SQLPOINTER>(&dcVersion), SQL_IS_INTEGER, &dcIRD);
if (r == SQL_SUCCESS && dcVersion >= VERSION_RANK_AVAILABLE) {
getRankInfo = true;
}
// Start parsing the data (blob)
unsigned char *dcptr = dcbuf;
sqlsrv_malloc_auto_ptr<sensitivity_metadata> sensitivity_meta;
@ -1066,7 +1105,7 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
parse_sensitivity_name_id_pairs(stmt, sensitivity_meta->num_infotypes, &sensitivity_meta->infotypes, &dcptr);
// Next parse the sensitivity properties
parse_column_sensitivity_props(sensitivity_meta, &dcptr);
parse_column_sensitivity_props(sensitivity_meta, &dcptr, getRankInfo);
unsigned char *dcend = dcbuf;
dcend += dclen;
@ -1095,12 +1134,12 @@ void core_sqlsrv_sensitivity_metadata( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ sqlsrv_phptype sqlsrv_php_type_in, _In_ bool prefer_string,
_Outref_result_bytebuffer_maybenull_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len, _In_ bool cache_field,
_Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC)
_Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out)
{
try {
// close the stream to release the resource
close_active_stream(stmt TSRMLS_CC);
close_active_stream(stmt);
// if the field has been retrieved before, return the previous result
field_cache* cached = NULL;
@ -1139,7 +1178,7 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
for( int i = stmt->last_field_index + 1; i < field_index; ++i ) {
SQLSRV_ASSERT( reinterpret_cast<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), i )) == NULL, "Field already cached." );
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field, sqlsrv_php_type_out TSRMLS_CC );
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field, sqlsrv_php_type_out );
// delete the value returned since we only want it cached, not the actual value
if( field_value ) {
efree( field_value );
@ -1183,12 +1222,12 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
*sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>( sqlsrv_php_type.typeinfo.type );
// Retrieve the data
core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len );
// if the user wants us to cache the field, we'll do it
if( cache_field ) {
field_cache cache( field_value, *field_len, sqlsrv_php_type );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) );
}
}
@ -1205,7 +1244,7 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
// Return:
// true if any results are present, false otherwise.
bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt )
{
SQLSMALLINT num_cols;
SQLLEN rows_affected;
@ -1215,7 +1254,7 @@ bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
}
else {
// Use SQLNumResultCols to determine if we have rows or not
num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
num_cols = core::SQLNumResultCols( stmt );
stmt->column_count = num_cols;
}
@ -1224,7 +1263,7 @@ bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
}
else {
// Use SQLRowCount to determine if there is a rows status waiting
rows_affected = core::SQLRowCount( stmt TSRMLS_CC );
rows_affected = core::SQLRowCount( stmt );
stmt->row_count = rows_affected;
}
@ -1238,7 +1277,7 @@ bool core_sqlsrv_has_any_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// Returns
// Nothing, exception thrown if problem occurs
void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool finalize_output_params, _In_ bool throw_on_errors )
void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt, _In_ bool finalize_output_params, _In_ bool throw_on_errors )
{
try {
@ -1251,14 +1290,14 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
throw core::CoreException();
}
close_active_stream( stmt TSRMLS_CC );
close_active_stream( stmt );
//Clear column sql types and sql display sizes.
zend_hash_clean( Z_ARRVAL( stmt->col_cache ));
SQLRETURN r;
if( throw_on_errors ) {
r = core::SQLMoreResults( stmt TSRMLS_CC );
r = core::SQLMoreResults( stmt );
}
else {
r = SQLMoreResults( stmt->handle() );
@ -1268,7 +1307,7 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
if( finalize_output_params ) {
// if we're finished processing result sets, handle the output parameters
finalize_output_parameters( stmt TSRMLS_CC );
finalize_output_parameters( stmt );
}
// mark we are past the end of all results
@ -1276,7 +1315,7 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
return;
}
stmt->new_result_set( TSRMLS_C );
stmt->new_result_set();
}
catch( core::CoreException& e ) {
@ -1295,26 +1334,26 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
// Returns:
// Nothing, exception thrown if problem occurs
void core_sqlsrv_post_param( _Inout_ sqlsrv_stmt* stmt, _In_ zend_ulong param_num, zval* param_z TSRMLS_DC )
void core_sqlsrv_post_param( _Inout_ sqlsrv_stmt* stmt, _In_ zend_ulong param_num, zval* param_z )
{
SQLSRV_ASSERT( Z_TYPE( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." );
SQLSRV_ASSERT( Z_TYPE( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." );
// if the parameter was an input string, delete it from the array holding input parameter strings
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_input_strings ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_input_strings ), param_num TSRMLS_CC );
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_input_strings ), param_num );
}
// if the parameter was an input stream, decrement our reference to it and delete it from the array holding input streams
// PDO doesn't need the reference count, but sqlsrv does since the stream can be live after sqlsrv_execute by sending it
// with sqlsrv_send_stream_data.
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_streams ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_streams ), param_num TSRMLS_CC );
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_streams ), param_num );
}
}
//Calls SQLSetStmtAttr to set a cursor.
void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long cursor_type TSRMLS_DC )
void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long cursor_type )
{
try {
@ -1322,27 +1361,27 @@ void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long c
case SQL_CURSOR_STATIC:
core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_STATIC ), SQL_IS_UINTEGER TSRMLS_CC );
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_STATIC ), SQL_IS_UINTEGER );
break;
case SQL_CURSOR_DYNAMIC:
core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_DYNAMIC ), SQL_IS_UINTEGER TSRMLS_CC );
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_DYNAMIC ), SQL_IS_UINTEGER );
break;
case SQL_CURSOR_KEYSET_DRIVEN:
core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_KEYSET_DRIVEN ), SQL_IS_UINTEGER TSRMLS_CC );
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_KEYSET_DRIVEN ), SQL_IS_UINTEGER );
break;
case SQL_CURSOR_FORWARD_ONLY:
core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER TSRMLS_CC );
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER );
break;
case SQLSRV_CURSOR_BUFFERED:
core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER TSRMLS_CC );
reinterpret_cast<SQLPOINTER>( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER );
break;
default:
@ -1358,17 +1397,17 @@ void core_sqlsrv_set_scrollable( _Inout_ sqlsrv_stmt* stmt, _In_ unsigned long c
}
}
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{
if( Z_TYPE_P( value_z ) != IS_LONG ) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_BUFFER_LIMIT );
}
core_sqlsrv_set_buffered_query_limit( stmt, Z_LVAL_P( value_z ) TSRMLS_CC );
core_sqlsrv_set_buffered_query_limit( stmt, Z_LVAL_P( value_z ) );
}
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLEN limit TSRMLS_DC )
void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLEN limit )
{
if( limit <= 0 ) {
@ -1382,7 +1421,7 @@ void core_sqlsrv_set_buffered_query_limit( _Inout_ sqlsrv_stmt* stmt, _In_ SQLLE
// Extracts the long value and calls the core_sqlsrv_set_query_timeout
// which accepts timeout parameter as a long. If the zval is not of type long
// than throws error.
void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* value_z TSRMLS_DC )
void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* value_z )
{
try {
@ -1401,7 +1440,7 @@ void core_sqlsrv_set_query_timeout( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* val
}
}
void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC)
void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z)
{
try {
// first check if the input is an integer
@ -1422,10 +1461,8 @@ void core_sqlsrv_set_decimal_places(_Inout_ sqlsrv_stmt* stmt, _In_ zval* value_
}
}
void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z )
{
TSRMLS_C;
// zend_is_true does not fail. It either returns true or false.
stmt->send_streams_at_exec = ( zend_is_true( value_z )) ? true : false;
}
@ -1442,13 +1479,13 @@ void core_sqlsrv_set_send_at_exec( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z
// Returns:
// true if more data remains to be sent, false if all data processed
bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt )
{
// if there no current parameter to process, get the next one
// (probably because this is the first call to sqlsrv_send_stream_data)
if( stmt->current_stream.stream_z == NULL ) {
if( check_for_next_stream_parameter( stmt TSRMLS_CC ) == false ) {
if( check_for_next_stream_parameter( stmt ) == false ) {
stmt->current_stream = sqlsrv_stream( NULL, SQLSRV_ENCODING_CHAR );
stmt->current_stream_read = 0;
@ -1460,7 +1497,7 @@ bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// get the stream from the zval we bound
php_stream* param_stream = NULL;
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, stmt->current_stream.stream_z TSRMLS_CC );
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, stmt->current_stream.stream_z );
// if we're at the end, then reset both current_stream and current_stream_read
if (php_stream_eof(param_stream)) {
@ -1489,7 +1526,7 @@ bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
if (read == 0) {
// send an empty string, which is what a 0 length does.
char buff[1]; // temp storage to hand to SQLPutData
core::SQLPutData(stmt, buff, 0 TSRMLS_CC);
core::SQLPutData(stmt, buff, 0);
}
else if (read > 0) {
// if this is a UTF-8 stream, then we will use the UTF-8 encoding to determine if we're in the middle of a character
@ -1516,7 +1553,7 @@ bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
// this will calculate how many bytes were cut off from the last UTF-8 character and read that many more
// in, then reattempt the conversion. If it fails the second time, then an error is returned.
size_t need_to_read = calc_utf8_missing( stmt, buffer, read TSRMLS_CC );
size_t need_to_read = calc_utf8_missing( stmt, buffer, read );
// read the missing bytes
size_t new_read = php_stream_read( param_stream, static_cast<char*>( buffer ) + read,
need_to_read );
@ -1535,17 +1572,17 @@ bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
throw core::CoreException();
}
}
core::SQLPutData( stmt, wbuffer, wsize * sizeof( SQLWCHAR ) TSRMLS_CC );
core::SQLPutData( stmt, wbuffer, wsize * sizeof( SQLWCHAR ) );
}
else {
core::SQLPutData( stmt, buffer, read TSRMLS_CC );
core::SQLPutData( stmt, buffer, read );
}
}
}
}
catch( core::CoreException& e ) {
stmt->free_param_data( TSRMLS_C );
stmt->free_param_data();
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
SQLCancel( stmt->handle() );
stmt->current_stream = sqlsrv_stream( NULL, SQLSRV_ENCODING_DEFAULT );
@ -1556,30 +1593,28 @@ bool core_sqlsrv_send_stream_packet( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
return true;
}
void stmt_option_functor::operator()( _Inout_ sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, _In_ zval* /*value_z*/ TSRMLS_DC )
void stmt_option_functor::operator()( _Inout_ sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, _In_ zval* /*value_z*/ )
{
TSRMLS_C;
// This implementation should never get called.
DIE( "Not implemented." );
}
void stmt_option_query_timeout:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_query_timeout:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z )
{
core_sqlsrv_set_query_timeout( stmt, value_z TSRMLS_CC );
core_sqlsrv_set_query_timeout( stmt, value_z );
}
void stmt_option_send_at_exec:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_send_at_exec:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
core_sqlsrv_set_send_at_exec( stmt, value_z TSRMLS_CC );
core_sqlsrv_set_send_at_exec( stmt, value_z );
}
void stmt_option_buffered_query_limit:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_buffered_query_limit:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
core_sqlsrv_set_buffered_query_limit( stmt, value_z TSRMLS_CC );
core_sqlsrv_set_buffered_query_limit( stmt, value_z );
}
void stmt_option_date_as_string:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_date_as_string:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z )
{
if (zend_is_true(value_z)) {
stmt->date_as_string = true;
@ -1589,7 +1624,7 @@ void stmt_option_date_as_string:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_op
}
}
void stmt_option_format_decimals:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_format_decimals:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z )
{
if (zend_is_true(value_z)) {
stmt->format_decimals = true;
@ -1599,12 +1634,12 @@ void stmt_option_format_decimals:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_o
}
}
void stmt_option_decimal_places:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_decimal_places:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z )
{
core_sqlsrv_set_decimal_places(stmt, value_z TSRMLS_CC);
core_sqlsrv_set_decimal_places(stmt, value_z);
}
void stmt_option_data_classification:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_data_classification:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /**/, _In_ zval* value_z )
{
if (zend_is_true(value_z)) {
stmt->data_classification = true;
@ -1616,7 +1651,7 @@ void stmt_option_data_classification:: operator()( _Inout_ sqlsrv_stmt* stmt, st
// internal function to release the active stream. Called by each main API function
// that will alter the statement and cancel any retrieval of data from a stream.
void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
void close_active_stream( _Inout_ sqlsrv_stmt* stmt )
{
// if there is no active stream, return
if( Z_TYPE( stmt->active_stream ) == IS_UNDEF ) {
@ -1678,7 +1713,7 @@ bool is_a_numeric_type(_In_ SQLSMALLINT sql_type)
return false;
}
void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLLEN sql_type, _Inout_ SQLLEN& size TSRMLS_DC )
void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLLEN sql_type, _Inout_ SQLLEN& size )
{
try {
@ -1712,7 +1747,7 @@ void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index,
case SQL_SS_VARIANT:
{
// unixODBC 2.3.1 requires wide calls to support pooling
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &size TSRMLS_CC );
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &size );
break;
}
@ -1722,7 +1757,7 @@ void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index,
case SQL_WVARCHAR:
{
// unixODBC 2.3.1 requires wide calls to support pooling
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &size TSRMLS_CC );
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &size );
break;
}
@ -1739,7 +1774,7 @@ void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index,
// calculates how many characters were cut off from the end of a buffer when reading
// in UTF-8 encoded text
size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) const char* buffer, _In_ size_t buffer_end TSRMLS_DC )
size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) const char* buffer, _In_ size_t buffer_end )
{
const char* last_char = buffer + buffer_end - 1;
size_t need_to_read = 0;
@ -1778,11 +1813,11 @@ size_t calc_utf8_missing( _Inout_ sqlsrv_stmt* stmt, _In_reads_(buffer_end) cons
// the driver layer would have to calculate size of the field_value
// to decide the amount of memory allocation.
void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype
sqlsrv_php_type, _Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len TSRMLS_DC )
sqlsrv_php_type, _Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len )
{
try {
close_active_stream( stmt TSRMLS_CC );
close_active_stream( stmt );
// make sure that fetch is called before trying to retrieve.
CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) {
@ -1804,7 +1839,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
*field_value_temp = 0;
SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( SQLLEN ),
field_len, true /*handle_warning*/ TSRMLS_CC );
field_len, true /*handle_warning*/ );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
@ -1828,9 +1863,10 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
{
sqlsrv_malloc_auto_ptr<double> field_value_temp;
field_value_temp = static_cast<double*>( sqlsrv_malloc( sizeof( double )));
*field_value_temp = 0.0;
SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ),
field_len, true /*handle_warning*/ TSRMLS_CC );
field_len, true /*handle_warning*/ );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
@ -1852,7 +1888,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
case SQLSRV_PHPTYPE_STRING:
{
get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len );
break;
}
@ -1868,7 +1904,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
field_value_temp = static_cast<char*>(sqlsrv_malloc(MAX_DATETIME_STRING_LEN));
memset(field_value_temp, '\0', MAX_DATETIME_STRING_LEN);
SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_CHAR, field_value_temp, MAX_DATETIME_STRING_LEN, &field_len_temp, true TSRMLS_CC);
SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_CHAR, field_value_temp, MAX_DATETIME_STRING_LEN, &field_len_temp, true);
if (r == SQL_NO_DATA || field_len_temp == SQL_NULL_DATA) {
field_value_temp.reset();
@ -1902,7 +1938,11 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
throw core::CoreException();
}
stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL );
// 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
// 'b' option might be important in other systems.
// For details check https://www.php.net/manual/en/internals2.ze1.streams.php
stream = php_stream_open_wrapper("sqlsrv://sqlncli10", "rb", REPORT_ERRORS, NULL);
CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) {
throw core::CoreException();
@ -1950,7 +1990,7 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
// check_for_next_stream_parameter
// see if there is another stream to be sent. Returns true and sets the stream as current in the statement structure, otherwise
// returns false
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt )
{
zend_ulong stream_index = 0;
SQLRETURN r = SQL_SUCCESS;
@ -1958,7 +1998,7 @@ bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
zval* param_z = NULL;
// get the index into the streams_ht from the parameter data we set in core_sqlsrv_bind_param
r = core::SQLParamData( stmt, reinterpret_cast<SQLPOINTER*>( &stream_index ) TSRMLS_CC );
r = core::SQLParamData( stmt, reinterpret_cast<SQLPOINTER*>( &stream_index ) );
// if no more data, we've exhausted the bound parameters, so return that we're done
if( SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) {
@ -2046,7 +2086,7 @@ bool convert_input_param_to_utf16( _In_ zval* input_param_z, _Inout_ zval* conve
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval const* param_z, _In_ SQLSRV_ENCODING encoding TSRMLS_DC )
SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval const* param_z, _In_ SQLSMALLINT sql_type, _In_ SQLSRV_ENCODING encoding )
{
SQLSMALLINT sql_c_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P( param_z );
@ -2072,18 +2112,34 @@ SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno,
sql_c_type = SQL_C_SLONG;
break;
case IS_LONG:
//ODBC 64-bit long and integer type are 4 byte values.
if ((Z_LVAL_P(param_z) < INT_MIN) || (Z_LVAL_P(param_z) > INT_MAX)) {
sql_c_type = SQL_C_SBIGINT;
}
else {
sql_c_type = SQL_C_SLONG;
}
// 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
// 8-byte integers
#ifdef ZEND_ENABLE_ZVAL_LONG64
sql_c_type = SQL_C_SBIGINT;
#else
sql_c_type = SQL_C_SLONG;
#endif
break;
case IS_DOUBLE:
sql_c_type = SQL_C_DOUBLE;
break;
case IS_STRING:
switch (encoding) {
case SQLSRV_ENCODING_CHAR:
sql_c_type = SQL_C_CHAR;
break;
case SQLSRV_ENCODING_BINARY:
sql_c_type = SQL_C_BINARY;
break;
case CP_UTF8:
sql_c_type = (is_a_numeric_type(sql_type)) ? SQL_C_CHAR : SQL_C_WCHAR;
break;
default:
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_INVALID_PARAMETER_ENCODING, paramno);
break;
}
break;
case IS_RESOURCE:
switch( encoding ) {
case SQLSRV_ENCODING_CHAR:
@ -2117,7 +2173,7 @@ SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno,
// given a zval and encoding, determine the appropriate sql type
void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding,
_Out_ SQLSMALLINT& sql_type TSRMLS_DC )
_Out_ SQLSMALLINT& sql_type )
{
sql_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P(param_z);
@ -2195,7 +2251,7 @@ void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_
// given a zval and encoding, determine the appropriate column size, and decimal scale (if appropriate)
void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned int paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC )
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits )
{
int php_type = Z_TYPE_P( param_z );
column_size = 0;
@ -2274,129 +2330,63 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
// Likewise, if decimals_places is larger than the field scale, decimals_places wil be ignored. This is to ensure the
// number of decimals adheres to the column field scale. If smaller, the output value may be rounded up.
//
// Note: it's possible that the decimal data does not contain a decimal dot because the field scale is 0.
// Thus, first check if the decimal dot exists. If not, no formatting necessary, regardless of
// Note: it's possible that the decimal data does not contain a decimal point because the field scale is 0.
// Thus, first check if the decimal point exists. If not, no formatting necessary, regardless of
// format_decimals and decimals_places
//
std::string str = field_value;
size_t pos = str.find_first_of('.');
// The decimal dot is not found, simply return
if (pos == std::string::npos) {
// Check if it's a negative number and if necessary to add the leading zero
bool is_negative = (*field_value == '-');
char *src = field_value + is_negative;
bool add_leading_zero = false;
// If the decimal point is not found, simply return
char *pt = strchr(src, DECIMAL_POINT);
if (pt == NULL) {
return;
}
SQLSMALLINT num_decimals = decimals_places;
if (num_decimals > field_scale) {
num_decimals = field_scale;
else if (pt == src) {
add_leading_zero = true;
}
// We want the rounding to be consistent with php number_format(), http://php.net/manual/en/function.number-format.php
// as well as SQL Server Management studio, such that the least significant digit will be rounded up if it is
// followed by 5 or above.
bool isNegative = false;
// If negative, remove the minus sign for now so as not to complicate the rounding process
if (str[0] == '-') {
isNegative = true;
std::ostringstream oss;
oss << str.substr(1);
str = oss.str();
pos = str.find_first_of('.');
SQLSMALLINT scale = decimals_places;
if (scale > field_scale) {
scale = field_scale;
}
// Adds the leading zero if not exists
if (pos == 0) {
std::ostringstream oss;
oss << '0' << str;
str = oss.str();
pos++;
}
char buffer[50] = " "; // A buffer with two blank spaces, as leeway
short offset = 1 + is_negative;
short src_length = strlen(src);
if (num_decimals == NO_CHANGE_DECIMAL_PLACES) {
// Add the minus sign back if negative
if (isNegative) {
std::ostringstream oss;
oss << '-' << str.substr(0);
str = oss.str();
}
} else {
// Start formatting
size_t last = 0;
if (num_decimals == 0) {
// Chop all decimal digits, including the decimal dot
size_t pos2 = pos + 1;
short n = str[pos2] - '0';
if (n >= 5) {
// Start rounding up - starting from the digit left of the dot all the way to the first digit
bool carry_over = true;
for (short p = pos - 1; p >= 0 && carry_over; p--) {
n = str[p] - '0';
if (n == 9) {
str[p] = '0' ;
carry_over = true;
}
else {
n++;
carry_over = false;
str[p] = '0' + n;
}
}
if (carry_over) {
std::ostringstream oss;
oss << '1' << str.substr(0, pos);
str = oss.str();
pos++;
}
}
last = pos;
}
else {
size_t pos2 = pos + num_decimals + 1;
// No need to check if rounding is necessary when pos2 has passed the last digit in the input string
if (pos2 < str.length()) {
short n = str[pos2] - '0';
if (n >= 5) {
// Start rounding up - starting from the digit left of pos2 all the way to the first digit
bool carry_over = true;
for (short p = pos2 - 1; p >= 0 && carry_over; p--) {
if (str[p] == '.') { // Skip the dot
continue;
}
n = str[p] - '0';
if (n == 9) {
str[p] = '0' ;
carry_over = true;
}
else {
n++;
carry_over = false;
str[p] = '0' + n;
}
}
if (carry_over) {
std::ostringstream oss;
oss << '1' << str.substr(0, pos2);
str = oss.str();
pos2++;
}
}
}
last = pos2;
}
// Add the minus sign back if negative
if (isNegative) {
std::ostringstream oss;
oss << '-' << str.substr(0, last);
str = oss.str();
} else {
str = str.substr(0, last);
}
if (add_leading_zero) {
buffer[offset++] = '0';
}
// Copy the original numerical value to the buffer
memcpy_s(buffer + offset, src_length, src, src_length);
size_t len = str.length();
str.copy(field_value, len);
int last_pos = src_length + offset;
// If no need to adjust decimal places, skip formatting
if (decimals_places != NO_CHANGE_DECIMAL_PLACES) {
short num_decimals = src_length - (pt - src) - 1;
if (num_decimals > scale) {
last_pos = round_up_decimal_numbers(buffer, (pt - src) + offset, scale, offset, last_pos);
}
}
// Remove the extra white space if not used
char *p = buffer;
offset = 0;
while (isspace(*p++)) {
offset++;
}
if (is_negative) {
buffer[--offset] = '-';
}
short len = last_pos - offset;
memcpy_s(field_value, len, buffer + offset, len);
field_value[len] = '\0';
*field_len = len;
}
@ -2407,10 +2397,18 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
// parameters passed to SQLBindParameter. It also converts output strings from UTF-16 to UTF-8 if necessary.
// For integer or float parameters, it sets those to NULL if a NULL was returned by SQL Server
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt, _In_opt_ bool exception_thrown /*= false*/ )
{
if (Z_ISUNDEF(stmt->output_params))
return;
// If an error occurs or an exception is thrown during an execution, the values of any output
// parameters or columns are undefined. Therefore, do not depend on them having any specific
// values, because the ODBC driver may or may not have modified them.
if (exception_thrown) {
zend_hash_clean(Z_ARRVAL(stmt->output_params));
return;
}
HashTable* params_ht = Z_ARRVAL(stmt->output_params);
zend_ulong index = -1;
@ -2558,7 +2556,7 @@ void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
}
void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype sqlsrv_php_type,
_Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len TSRMLS_DC )
_Inout_updates_bytes_(*field_len) void*& field_value, _Inout_ SQLLEN* field_len )
{
SQLRETURN r;
SQLSMALLINT c_type;
@ -2584,40 +2582,39 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
sql_field_type = stmt->current_meta_data[field_index]->field_type;
// Calculate the field size.
calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC );
calc_string_size( stmt, field_index, sql_field_type, sql_display_size );
col_cache cache( sql_field_type, sql_display_size );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->col_cache ), field_index, &cache, sizeof( col_cache ) TSRMLS_CC );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->col_cache ), field_index, &cache, sizeof( col_cache ) );
}
// Determine the correct encoding
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) {
sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding();
}
// For numbers, no need to convert
if (is_a_numeric_type(sql_field_type)) {
sqlsrv_php_type.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
// Set the C type and account for null characters at the end of the data.
switch( sqlsrv_php_type.typeinfo.encoding ) {
case CP_UTF8:
c_type = SQL_C_WCHAR;
extra = sizeof( SQLWCHAR );
break;
case SQLSRV_ENCODING_BINARY:
if (sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_BINARY) {
c_type = SQL_C_BINARY;
extra = 0;
break;
default:
} else {
c_type = SQL_C_CHAR;
extra = sizeof( SQLCHAR );
break;
extra = sizeof(SQLCHAR);
// For numbers, no need to convert
if (sqlsrv_php_type.typeinfo.encoding == CP_UTF8 && !is_a_numeric_type(sql_field_type)) {
c_type = SQL_C_WCHAR;
extra = sizeof(SQLWCHAR);
}
}
// if this is a large type, then read the first few bytes to get the actual length from SQLGetData
if( sql_display_size == 0 || sql_display_size == INT_MAX ||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) {
// If this is a large type, then read the first few bytes to get the actual length from SQLGetData
// 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.
// For varchar(max) and nvarchar(max), sql_display_size will be 0, regardless
if (sql_display_size == 0 || sql_display_size == INT_MAX ||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ||
(sql_display_size > SQL_SERVER_MAX_FIELD_SIZE &&
(sql_field_type == SQL_WLONGVARCHAR || sql_field_type == SQL_LONGVARCHAR || sql_field_type == SQL_LONGVARBINARY))) {
field_len_temp = intial_field_len;
@ -2626,7 +2623,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
field_value_temp = static_cast<char*>( sqlsrv_malloc( field_len_temp + extra + 1 ));
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ),
&field_len_temp, false /*handle_warning*/ TSRMLS_CC );
&field_len_temp, false /*handle_warning*/ );
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
throw core::CoreException();
@ -2643,7 +2640,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {L'\0'};
SQLSMALLINT len = 0;
stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC );
stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
// with Linux connection pooling may not get a truncated warning back but the actual field_len_temp
// can be greater than the initallen value.
@ -2673,7 +2670,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
// Get the rest of the data.
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len,
field_len_temp + extra, &dummy_field_len, false /*handle_warning*/ TSRMLS_CC );
field_len_temp + extra, &dummy_field_len, false /*handle_warning*/ );
// the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL
// so we calculate the actual length of the string with that.
if ( dummy_field_len != SQL_NO_TOTAL )
@ -2683,7 +2680,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
if( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
);
}
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
@ -2698,7 +2695,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
// Get the rest of the data.
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + intial_field_len,
field_len_temp + extra, &dummy_field_len, true /*handle_warning*/ TSRMLS_CC );
field_len_temp + extra, &dummy_field_len, true /*handle_warning*/ );
field_len_temp += intial_field_len;
if( dummy_field_len == SQL_NULL_DATA ) {
@ -2720,8 +2717,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
}
} // if( r == SQL_SUCCESS_WITH_INFO )
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) {
if (c_type == SQL_C_WCHAR) {
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( sqlsrv_php_type.typeinfo.encoding ),
&field_value_temp, field_len_temp );
@ -2750,7 +2746,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
// get the data
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size,
&field_len_temp, true /*handle_warning*/ TSRMLS_CC );
&field_len_temp, true /*handle_warning*/ );
CHECK_SQL_ERROR( r, stmt ) {
throw core::CoreException();
}
@ -2764,8 +2760,7 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
return;
}
if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) {
if (c_type == SQL_C_WCHAR) {
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( sqlsrv_php_type.typeinfo.encoding ),
&field_value_temp, field_len_temp );
@ -2828,7 +2823,7 @@ field_value = field_value_temp;
// return the option from the stmt_opts array that matches the key. If no option found,
// NULL is returned.
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, _In_ zend_ulong key, _In_ const stmt_option stmt_opts[] TSRMLS_DC )
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, _In_ zend_ulong key, _In_ const stmt_option stmt_opts[] )
{
for( int i = 0; stmt_opts[i].key != SQLSRV_STMT_OPTION_INVALID; ++i ) {
@ -2896,7 +2891,7 @@ bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type )
void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z, _In_ SQLULEN paramno, SQLSRV_ENCODING encoding,
_In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _In_ SQLSMALLINT decimal_digits,
_Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len TSRMLS_DC )
_Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len )
{
SQLSRV_ASSERT( column_size != SQLSRV_UNKNOWN_SIZE, "column size should be set to a known value." );
buffer_len = Z_STRLEN_P( param_z );
@ -2972,159 +2967,11 @@ void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
}
}
void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits ) {
// 38 is the maximum length of a stringified decimal number
size_t maxDecimalPrecision = 38;
// 6 is derived from: 1 for '.'; 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
// if the length is greater than maxDecimalStrLen, do not change the string
size_t maxDecimalStrLen = maxDecimalPrecision + 6;
if (Z_STRLEN_P(param_z) > maxDecimalStrLen) {
return;
}
std::vector<size_t> digits;
unsigned char* ptr = reinterpret_cast<unsigned char*>(ZSTR_VAL( Z_STR_P( param_z )));
bool isNeg = false;
bool isScientificNot = false;
char scientificChar = ' ';
short scientificExp = 0;
if( strchr( reinterpret_cast<char*>( ptr ), 'e' ) || strchr( reinterpret_cast<char*>( ptr ), 'E' )){
isScientificNot = true;
}
// parse digits in param_z into the vector digits
if( *ptr == '+' || *ptr == '-' ){
if( *ptr == '-' ){
isNeg = true;
}
ptr++;
}
short numInt = 0;
short numDec = 0;
while( isdigit( *ptr )){
digits.push_back( *ptr - '0' );
ptr++;
numInt++;
}
if( *ptr == '.' ){
ptr++;
if( !isScientificNot ){
while( isdigit( *ptr ) && numDec < decimal_digits + 1 ){
digits.push_back( *ptr - '0' );
ptr++;
numDec++;
}
// make sure the rest of the number are digits
while( isdigit( *ptr )){
ptr++;
}
}
else {
while( isdigit( *ptr )){
digits.push_back( *ptr - '0' );
ptr++;
numDec++;
}
}
}
if( isScientificNot ){
if ( *ptr == 'e' || *ptr == 'E' ) {
scientificChar = *ptr;
}
ptr++;
bool isNegExp = false;
if( *ptr == '+' || *ptr == '-' ){
if( *ptr == '-' ){
isNegExp = true;
}
ptr++;
}
while( isdigit( *ptr )){
scientificExp = scientificExp * 10 + ( *ptr - '0' );
ptr++;
}
SQLSRV_ASSERT( scientificExp <= maxDecimalPrecision, "Input decimal overflow: sql decimal type only supports up to a precision of 38." );
if( isNegExp ){
scientificExp = scientificExp * -1;
}
}
// if ptr is not pointing to a null terminator at this point, that means the decimal string input is invalid
// do not change the string and let SQL Server handle the invalid decimal string
if ( *ptr != '\0' ) {
return;
}
// if number of decimal is less than the exponent, that means the number is a whole number, so no need to adjust the precision
if( numDec > scientificExp ){
int decToRemove = numDec - scientificExp - decimal_digits;
if( decToRemove > 0 ){
bool carryOver = false;
short backInd = 0;
// pop digits from the vector until there is only 1 more decimal place than required decimal_digits
while( decToRemove != 1 && !digits.empty() ){
digits.pop_back();
decToRemove--;
}
if( !digits.empty() ){
// check if the last digit to be popped is greater than 5, if so, the digit before it needs to round up
carryOver = digits.back() >= 5;
digits.pop_back();
backInd = static_cast<short>(digits.size() - 1);
// round up from the end until no more carry over
while( carryOver && backInd >= 0 ){
if( digits.at( backInd ) != 9 ){
digits.at( backInd )++;
carryOver = false;
}
else{
digits.at( backInd ) = 0;
}
backInd--;
}
}
std::ostringstream oss;
if( isNeg ){
oss << '-';
}
// insert 1 if carry over persist all the way to the beginning of the number
if( carryOver && backInd == -1 ){
oss << 1;
}
if( digits.empty() && !carryOver ){
oss << 0;
}
else{
short i = 0;
for( i; i < numInt && i < digits.size(); i++ ){
oss << digits[i];
}
// fill string with 0 if the number of digits in digits is less then numInt
if( i < numInt ){
for( i; i < numInt; i++ ){
oss << 0;
}
}
if( numInt < digits.size() ){
oss << '.';
for( i; i < digits.size(); i++ ){
oss << digits[i];
}
}
if( scientificExp != 0 ){
oss << scientificChar << std::to_string( scientificExp );
}
}
std::string str = oss.str();
zend_string* zstr = zend_string_init( str.c_str(), str.length(), 0 );
zend_string_release( Z_STR_P( param_z ));
ZVAL_NEW_STR( param_z, zstr );
}
}
}
// output parameters have their reference count incremented so that they do not disappear
// while the query is executed and processed. They are saved in the statement so that
// their reference count may be decremented later (after results are processed)
void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param TSRMLS_DC )
void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param )
{
HashTable* param_ht = Z_ARRVAL( stmt->output_params );
zend_ulong paramno = static_cast<zend_ulong>( param.param_num );
@ -3135,9 +2982,9 @@ void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_outp
// send all the stream data
void send_param_streams( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
void send_param_streams( _Inout_ sqlsrv_stmt* stmt )
{
while( core_sqlsrv_send_stream_packet( stmt TSRMLS_CC )) { }
while( core_sqlsrv_send_stream_packet( stmt )) { }
}
@ -3157,4 +3004,199 @@ void sqlsrv_stream_dtor( _Inout_ zval* data )
sqlsrv_free( stream_encoding );
}
void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits)
{
char* value = Z_STRVAL_P(param_z);
short value_len = Z_STRLEN_P(param_z);
// 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);
// 1 for sign of scientific exponent; 2 for length of scientific exponent
const int MAX_DECIMAL_STRLEN = SQL_SERVER_DECIMAL_MAXIMUM_PRECISION + 6;
if (value_len > MAX_DECIMAL_STRLEN) {
return;
}
// If std::stold() succeeds, 'idx' is the position of the first character after the numerical value
long double d = 0;
size_t idx;
try {
d = std::stold(std::string(value), &idx);
}
catch (const std::logic_error& err) {
return; // invalid input caused the conversion to throw an exception
}
if (idx < value_len) {
return; // the input contains something else apart from the numerical value
}
// Navigate to the first digit or the decimal point
bool is_negative = (d < 0);
char *src = value + is_negative;
while (*src != DECIMAL_POINT && !isdigit(*src)) {
src++;
}
// Check if the value is in scientific notation
char *exp = strchr(src, 'E');
if (exp == NULL) {
exp = strchr(src, 'e');
}
// Find the decimal point
char *pt = strchr(src, DECIMAL_POINT);
char buffer[50] = " "; // A buffer with 2 blank spaces, as leeway
short offset = 1 + is_negative; // The position to start copying the original numerical value
if (exp == NULL) {
if (pt == NULL) {
return; // decimal point not found
}
short src_length = strlen(src);
short num_decimals = src_length - (pt - src) - 1;
if (num_decimals <= decimal_digits) {
return; // no need to adjust number of decimals
}
memcpy_s(buffer + offset, src_length, src, src_length);
round_up_decimal_numbers(buffer, (pt - src) + offset, decimal_digits, offset, src_length + offset);
}
else {
int power = atoi(exp+1);
if (abs(power) > SQL_SERVER_DECIMAL_MAXIMUM_PRECISION) {
return; // Out of range, so let the server handle this
}
short num_decimals = 0;
if (power == 0) {
// Simply chop off the exp part
short length = (exp - src);
memcpy_s(buffer + offset, length, src, length);
if (pt != NULL) {
// Adjust decimal places only if decimal point is found and number of decimals more than decimal_digits
num_decimals = exp - pt - 1;
if (num_decimals > decimal_digits) {
round_up_decimal_numbers(buffer, (pt - src) + offset, decimal_digits, offset, length + offset);
}
}
} else {
short oldpos = 0;
if (pt == NULL) {
oldpos = exp - src; // Decimal point not found, use the exp sign
}
else {
oldpos = pt - src;
num_decimals = exp - pt - 1;
if (power > 0 && num_decimals <= power) {
return; // The result will be a whole number, do nothing and return
}
}
// Derive the new position for the decimal point in the buffer
short newpos = oldpos + power;
if (power > 0) {
newpos = newpos + offset;
if (num_decimals == 0) {
memset(buffer + offset + oldpos, '0', power); // Fill parts of the buffer with zeroes first
}
else {
buffer[newpos] = DECIMAL_POINT;
}
}
else {
// The negative "power" part shows exactly how many places to move the decimal point.
// Whether to pad zeroes depending on the original position of the decimal point pos.
if (newpos <= 0) {
// If newpos is negative or zero, pad zeroes (size of '0.' + places to move) in the buffer
short numzeroes = 2 + abs(newpos);
memset(buffer + offset, '0', numzeroes);
newpos = offset + 1; // The new decimal position should be offset + '0'
buffer[newpos] = DECIMAL_POINT; // Replace that '0' with the decimal point
offset = numzeroes + offset; // Short offset now in the buffer
}
else {
newpos = newpos + offset;
buffer[newpos] = DECIMAL_POINT;
}
}
// Start copying the content to the buffer until the exp sign or one more digit after decimal_digits
char *p = src;
short idx = offset;
short lastpos = newpos + decimal_digits + 1;
while (p != exp && idx <= lastpos) {
if (*p == DECIMAL_POINT) {
p++;
continue;
}
if (buffer[idx] == DECIMAL_POINT) {
idx++;
}
buffer[idx++] = *p;
p++;
}
// Round up is required only when number of decimals is more than decimal_digits
num_decimals = idx - newpos - 1;
if (num_decimals > decimal_digits) {
round_up_decimal_numbers(buffer, newpos, decimal_digits, offset, idx);
}
}
}
// Set the minus sign if negative
if (is_negative) {
buffer[0] = '-';
}
zend_string* zstr = zend_string_init(buffer, strlen(buffer), 0);
zend_string_release(Z_STR_P(param_z));
ZVAL_NEW_STR(param_z, zstr);
}
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ short decimal_pos, _In_ short num_decimals, _In_ short offset, _In_ short lastpos)
{
// This helper method assumes the 'buffer' has some extra blank spaces at the beginning without the minus '-' sign.
// We want the rounding to be consistent with php number_format(), http://php.net/manual/en/function.number-format.php
// as well as SQL Server Management studio, such that the least significant digit will be rounded up if it is
// followed by 5 or above.
short pos = decimal_pos + num_decimals + 1;
if (pos < lastpos) {
short n = buffer[pos] - '0';
if (n >= 5) {
// Start rounding up - starting from the digit left of pos all the way to the first digit
bool carry_over = true;
for (short p = pos - 1; p >= offset && carry_over; p--) {
if (buffer[p] == DECIMAL_POINT) {
continue;
}
n = buffer[p] - '0';
carry_over = (++n == 10);
if (n == 10) {
n = 0;
}
buffer[p] = '0' + n;
}
if (carry_over) {
buffer[offset - 1] = '1';
}
}
if (num_decimals == 0) {
buffer[decimal_pos] = '\0';
return decimal_pos;
}
else {
buffer[pos] = '\0';
return pos;
}
}
// Do nothing and just return
return lastpos;
}
}

View file

@ -3,7 +3,7 @@
//
// Contents: Implementation of PHP streams for reading SQL Server data
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -23,7 +23,7 @@ namespace {
// close a stream and free the PHP resources used by it
int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ TSRMLS_DC )
int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ )
{
sqlsrv_stream* ss = static_cast<sqlsrv_stream*>( stream->abstract );
SQLSRV_ASSERT( ss != NULL && ss->stmt != NULL, "sqlsrv_stream_close: sqlsrv_stream* ss was null." );
@ -45,9 +45,9 @@ int sqlsrv_stream_close( _Inout_ php_stream* stream, int /*close_handle*/ TSRMLS
// set when sqlsrv_get_field is called by the user specifying which field type they want.
#if PHP_VERSION_ID >= 70400
ssize_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count TSRMLS_DC)
ssize_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count)
#else
size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count TSRMLS_DC)
size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count) char* buf, _Inout_ size_t count)
#endif
{
SQLLEN read = 0;
@ -94,7 +94,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
}
// Warnings will be handled below
SQLRETURN r = ss->stmt->current_results->get_data(ss->field_index + 1, c_type, get_data_buffer, count /*BufferLength*/, &read, false /*handle_warning*/ TSRMLS_CC);
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 ) {
stream->eof = 1;
@ -114,7 +114,7 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
SQLCHAR state[SQL_SQLSTATE_BUFSIZE] = {L'\0'};
SQLSMALLINT len = 0;
ss->stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC );
ss->stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
if( read == SQL_NO_TOTAL ) {
SQLSRV_ASSERT( is_truncated_warning( state ), "sqlsrv_stream_read: truncation warning was expected but it "
@ -222,7 +222,7 @@ php_stream_ops sqlsrv_stream_ops = {
// 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.
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 TSRMLS_DC )
_In_opt_ int options, _In_ zend_string **, php_stream_context* STREAMS_DC )
{
#if ZEND_DEBUG
@ -238,9 +238,12 @@ static php_stream* sqlsrv_stream_opener( _In_opt_ php_stream_wrapper* wrapper, _
ss = static_cast<sqlsrv_stream*>( sqlsrv_malloc( sizeof( sqlsrv_stream )));
memset( ss, 0, sizeof( sqlsrv_stream ));
// check for valid options
if( options != REPORT_ERRORS ) {
php_stream_wrapper_log_error( wrapper, options TSRMLS_CC, "Invalid option: no options except REPORT_ERRORS may be specified with a sqlsrv stream" );
// 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,
// the argument "options" will be zero.
// For details check this pull request: https://github.com/php/php-src/pull/6190
if (options != 0) {
php_stream_wrapper_log_error(wrapper, options, "Invalid option: no options except REPORT_ERRORS may be specified with a sqlsrv stream");
return NULL;
}

View file

@ -5,7 +5,7 @@
//
// Comments: Mostly error handling and some type handling
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -57,7 +57,7 @@ void log_activity(_In_opt_ const char* msg, _In_opt_ va_list* print_args)
std::copy(INTERNAL_FORMAT_ERROR, INTERNAL_FORMAT_ERROR + sizeof(INTERNAL_FORMAT_ERROR), log_msg);
}
php_log_err(log_msg TSRMLS_CC);
php_log_err(log_msg);
}
}
@ -70,10 +70,10 @@ SQLCHAR SSPWARN[] = "01SSP";
// write to the php log if the severity and subsystem match the filters currently set in the INI or
// the script (sqlsrv_configure).
void write_to_log( _In_ unsigned int severity TSRMLS_DC, _In_ const char* msg, ...)
void write_to_log( _In_ unsigned int severity, _In_ const char* msg, ...)
{
SQLSRV_ASSERT( !(g_driver_severity == NULL), "Must register a driver checker function." );
if (!g_driver_severity(severity TSRMLS_CC)) {
if (!g_driver_severity(severity)) {
return;
}
@ -146,6 +146,11 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_(
return true;
}
#ifndef _WIN32
// Allocate enough space to hold the largest possible number of bytes for UTF-8 conversion
// instead of calling FromUtf16, for performance reasons
cchOutLen = 4 * cchInLen;
#else
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0;
@ -154,11 +159,6 @@ bool convert_string_from_utf16( _In_ SQLSRV_ENCODING encoding, _In_reads_bytes_(
flags = WC_ERR_INVALID_CHARS;
}
#ifndef _WIN32
// Allocate enough space to hold the largest possible number of bytes for UTF-8 conversion
// instead of calling FromUtf16, for performance reasons
cchOutLen = 4*cchInLen;
#else
// Calculate the number of output bytes required - no performance hit here because
// WideCharToMultiByte is highly optimised
cchOutLen = WideCharToMultiByte( encoding, flags,
@ -242,7 +242,7 @@ void convert_datetime_string_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_opt_ char* i
params[0] = value_temp_z;
if (call_user_function(EG(function_table), NULL, &function_z, &out_zval, 1,
params TSRMLS_CC) == FAILURE) {
params) == FAILURE) {
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED);
}
@ -257,8 +257,7 @@ void convert_datetime_string_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_opt_ char* i
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error, _In_ logging_severity severity
TSRMLS_DC )
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error, _In_ logging_severity severity, _In_opt_ bool check_warning /* = false */)
{
SQLHANDLE h = ctx.handle();
SQLSMALLINT h_type = ctx.handle_type();
@ -297,17 +296,9 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message,
SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wmessage_len );
// don't use the CHECK* macros here since it will trigger reentry into the error handling system
// Workaround for a bug in unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV).
// Instead of returning false, we return an empty error message to prevent the driver from throwing an exception.
// To reproduce:
// Create a connection and close it (return it to the pool)
// Create a new connection from the pool.
// Prepare and execute a statement that generates an info message (such as 'USE tempdb;')
#ifdef __APPLE__
if( r == SQL_NO_DATA && ctx.driver() != NULL /*PDO SQLSRV*/ ) {
r = SQL_SUCCESS;
}
#endif // __APPLE__
// removed the workaround for Mac users with unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV), for two reasons:
// (1) not recommended to use connection pooling with unixODBC < 2.3.7
// (2) the problem was not reproducible with unixODBC 2.3.7
if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) {
return false;
}
@ -348,6 +339,15 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
break;
}
// Only overrides 'severity' if 'check_warning' is true (false by default)
if (check_warning) {
// 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.
// 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') {
severity = SEV_WARNING;
}
}
// log the error first
LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), error->sqlstate );
@ -361,7 +361,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
// format and return a driver specfic 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 TSRMLS_DC, _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
formatted_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error();
@ -489,6 +489,7 @@ namespace data_classification {
const char* INFOTYPE = "Information Type";
const char* NAME = "name";
const char* ID = "id";
const char* RANK = "rank";
void convert_sensivity_field(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_ENCODING encoding, _In_ unsigned char *ptr, _In_ int len, _Inout_updates_bytes_(cchOutLen) char** field_name)
{
@ -566,10 +567,18 @@ namespace data_classification {
*pptr = ptr;
}
void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr)
void parse_column_sensitivity_props(_Inout_ sensitivity_metadata* meta, _Inout_ unsigned char **pptr, _In_ bool getRankInfo)
{
unsigned char *ptr = *pptr;
unsigned short ncols;
int queryrank, colrank;
// Get rank info
if (getRankInfo) {
queryrank = *(reinterpret_cast<long*>(ptr));
ptr += sizeof(int);
meta->rank = queryrank;
}
// Get number of columns
meta->num_columns = ncols = *(reinterpret_cast<unsigned short*>(ptr));
@ -594,6 +603,12 @@ namespace data_classification {
typeidx = *(reinterpret_cast<unsigned short*>(ptr));
ptr += sizeof(unsigned short);
if (getRankInfo) {
colrank = *(reinterpret_cast<long*>(ptr));
ptr += sizeof(int);
pair.rank = colrank;
}
pair.label_idx = labelidx;
pair.infotype_idx = typeidx;
@ -606,7 +621,7 @@ namespace data_classification {
*pptr = ptr;
}
USHORT fill_column_sensitivity_array(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Inout_ zval *return_array TSRMLS_CC)
USHORT fill_column_sensitivity_array(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT colno, _Inout_ zval *return_array)
{
sensitivity_metadata* meta = stmt->current_sensitivity_metadata;
if (meta == NULL) {
@ -617,52 +632,64 @@ namespace data_classification {
zval data_classification;
ZVAL_UNDEF(&data_classification);
core::sqlsrv_array_init(*stmt, &data_classification TSRMLS_CC );
array_init(&data_classification);
USHORT num_pairs = meta->columns_sensitivity[colno].num_pairs;
if (num_pairs == 0) {
core::sqlsrv_add_assoc_zval(*stmt, return_array, DATA_CLASS, &data_classification TSRMLS_CC);
add_assoc_zval(return_array, DATA_CLASS, &data_classification);
return 0;
}
zval sensitivity_properties;
ZVAL_UNDEF(&sensitivity_properties);
core::sqlsrv_array_init(*stmt, &sensitivity_properties TSRMLS_CC);
array_init(&sensitivity_properties);
for (USHORT j = 0; j < num_pairs; j++) {
zval label_array, infotype_array;
ZVAL_UNDEF(&label_array);
ZVAL_UNDEF(&infotype_array);
core::sqlsrv_array_init(*stmt, &label_array TSRMLS_CC);
core::sqlsrv_array_init(*stmt, &infotype_array TSRMLS_CC);
array_init(&label_array);
array_init(&infotype_array);
USHORT labelidx = meta->columns_sensitivity[colno].label_info_pairs[j].label_idx;
USHORT typeidx = meta->columns_sensitivity[colno].label_info_pairs[j].infotype_idx;
int column_rank = meta->columns_sensitivity[colno].label_info_pairs[j].rank;
char *label = meta->labels[labelidx]->name;
char *label_id = meta->labels[labelidx]->id;
char *infotype = meta->infotypes[typeidx]->name;
char *infotype_id = meta->infotypes[typeidx]->id;
core::sqlsrv_add_assoc_string(*stmt, &label_array, NAME, label, 1 TSRMLS_CC);
core::sqlsrv_add_assoc_string(*stmt, &label_array, ID, label_id, 1 TSRMLS_CC);
add_assoc_string(&label_array, NAME, label);
add_assoc_string(&label_array, ID, label_id);
core::sqlsrv_add_assoc_zval(*stmt, &sensitivity_properties, LABEL, &label_array TSRMLS_CC);
add_assoc_zval(&sensitivity_properties, LABEL, &label_array);
core::sqlsrv_add_assoc_string(*stmt, &infotype_array, NAME, infotype, 1 TSRMLS_CC);
core::sqlsrv_add_assoc_string(*stmt, &infotype_array, ID, infotype_id, 1 TSRMLS_CC);
add_assoc_string(&infotype_array, NAME, infotype);
add_assoc_string(&infotype_array, ID, infotype_id);
core::sqlsrv_add_assoc_zval(*stmt, &sensitivity_properties, INFOTYPE, &infotype_array TSRMLS_CC);
add_assoc_zval(&sensitivity_properties, INFOTYPE, &infotype_array);
// add column sensitivity rank info to sensitivity_properties
if (column_rank > RANK_NOT_DEFINED) {
add_assoc_long(&sensitivity_properties, RANK, column_rank);
}
// add the pair of sensitivity properties to data_classification
core::sqlsrv_add_next_index_zval(*stmt, &data_classification, &sensitivity_properties TSRMLS_CC );
add_next_index_zval(&data_classification, &sensitivity_properties);
}
// add query sensitivity rank info to data_classification
int query_rank = meta->rank;
if (query_rank > RANK_NOT_DEFINED) {
add_assoc_long(&data_classification, RANK, query_rank);
}
// add data classfication as associative array
core::sqlsrv_add_assoc_zval(*stmt, return_array, DATA_CLASS, &data_classification TSRMLS_CC);
add_assoc_zval(return_array, DATA_CLASS, &data_classification);
return num_pairs;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@
//
// Contents: Contains portable classes for localization
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -247,6 +247,7 @@ inline UINT SystemLocale::MaxCharCchSize( UINT codepage )
switch ( codepage )
{
case CP_UTF8:
case 54936:
return 4;
case 932:
case 936:

View file

@ -5,7 +5,7 @@
// Must be included in one c/cpp file per binary
// A build error will occur if this inclusion policy is not followed
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -77,6 +77,7 @@ const cp_iconv cp_iconv::g_cp_iconv[] = {
{ 1256, "CP1256" TRANSLIT },
{ 1257, "CP1257" TRANSLIT },
{ 1258, "CP1258" TRANSLIT },
{ 54936, "GB18030" TRANSLIT},
{ CP_ISO8859_1, "ISO8859-1" TRANSLIT },
{ CP_ISO8859_2, "ISO8859-2" TRANSLIT },
{ CP_ISO8859_3, "ISO8859-3" TRANSLIT },
@ -342,6 +343,11 @@ SystemLocale::SystemLocale( const char * localeName )
const LocaleCP lcpTable[] = {
{ "utf8", CP_UTF8 },
{ "UTF-8", CP_UTF8 },
{ "BIG5", 950 },
{ "BIG5-HKSCS", 950 },
{ "gb18030", 54936 },
{ "gb2312", 936 },
{ "gbk", 936 },
CPxxx(1252), CPxxx(850), CPxxx(437), CPxxx(874), CPxxx(932), CPxxx(936), CPxxx(949), CPxxx(950),
CPxxx(1250), CPxxx(1251), CPxxx(1253), CPxxx(1254), CPxxx(1255), CPxxx(1256), CPxxx(1257), CPxxx(1258),
ISO8859(1), ISO8859(2), ISO8859(3), ISO8859(4), ISO8859(5), ISO8859(6),

View file

@ -20,7 +20,7 @@
// pecuniary loss) arising out of the use of or inability to use
// this SDK, even if Microsoft has been advised of the possibility
// of such damages.
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -94,6 +94,10 @@
#define SQL_COPT_SS_AUTHENTICATION (SQL_COPT_SS_BASE_EX+15)// The authentication method used for the connection
#define SQL_COPT_SS_ACCESS_TOKEN (SQL_COPT_SS_BASE_EX+16)// The authentication access token used for the connection
/* SQLSetConnectAttr MS driver additional specific defines. */
#define SQL_COPT_SS_BASE_ADD 1400
#define SQL_COPT_SS_DATACLASSIFICATION_VERSION (SQL_COPT_SS_BASE_ADD + 0) // The flag to Set/Get DATACLASSIFICATION version support
// SQLColAttributes driver specific defines.
// SQLSetDescField/SQLGetDescField driver specific defines.
// Microsoft has 1200 thru 1249 reserved for Microsoft ODBC Driver for SQL Server usage.
@ -146,6 +150,7 @@
// Data Classification
#define SQL_CA_SS_DATA_CLASSIFICATION (SQL_CA_SS_BASE+37) // retrieve data classification information
#define SQL_CA_SS_DATA_CLASSIFICATION_VERSION (SQL_CA_SS_BASE+38) // retrieve data classification version
#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+38)

View file

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

View file

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

View file

@ -4,7 +4,7 @@
// File: version.h
// Contents: Version number constants
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -26,12 +26,12 @@
// Increase Minor with backward compatible new functionalities and API changes.
// Increase Patch for backward compatible fixes.
#define SQLVERSION_MAJOR 5
#define SQLVERSION_MINOR 8
#define SQLVERSION_PATCH 1
#define SQLVERSION_MINOR 9
#define SQLVERSION_PATCH 0
#define SQLVERSION_BUILD 0
// For previews, set this constant to 1. Otherwise, set it to 0
#define PREVIEW 0
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
#define PREVIEW 1
#define SEMVER_PRERELEASE
// Semantic versioning build metadata, build meta data is not counted in precedence order.
@ -52,7 +52,7 @@
// "preview" for ETP
#if PREVIEW > 0
#undef SEMVER_PRERELEASE
#define SEMVER_PRERELEASE "preview"
#define SEMVER_PRERELEASE "preview" STRINGIFY(PREVIEW)
#define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA
#else
#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ dnl
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
dnl Microsoft Drivers 5.8 for PHP for SQL Server
dnl Microsoft Drivers 5.9 for PHP for SQL Server
dnl Copyright(c) Microsoft Corporation
dnl All rights reserved.
dnl MIT License

View file

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

View file

@ -3,7 +3,7 @@
//
// Contents: Routines that use connection handles
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -35,10 +35,8 @@ unsigned int current_log_subsystem = LOG_CONN;
struct date_as_string_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
TSRMLS_C; // show as used to avoid a warning
ss_sqlsrv_conn* ss_conn = static_cast<ss_sqlsrv_conn*>( conn );
if( zend_is_true( value )) {
@ -52,10 +50,8 @@ struct date_as_string_func {
struct format_decimals_func
{
static void func(connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC)
static void func(connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/)
{
TSRMLS_C; // show as used to avoid a warning
ss_sqlsrv_conn* ss_conn = static_cast<ss_sqlsrv_conn*>(conn);
if (zend_is_true(value)) {
@ -70,10 +66,8 @@ struct format_decimals_func
struct decimal_places_func
{
static void func(connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC)
static void func(connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/)
{
TSRMLS_C; // show as used to avoid a warning
// first check if the input is an integer
if (Z_TYPE_P(value) != IS_LONG) {
THROW_SS_ERROR(conn, SQLSRV_ERROR_INVALID_DECIMAL_PLACES);
@ -91,7 +85,7 @@ struct decimal_places_func
struct conn_char_set_func {
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
convert_to_string( value );
const char* encoding = Z_STRVAL_P( value );
@ -121,9 +115,8 @@ struct conn_char_set_func {
struct bool_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC )
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{
TSRMLS_C;
char const* val_str;
if( zend_is_true( value )) {
val_str = "yes";
@ -140,9 +133,8 @@ struct bool_conn_str_func {
struct int_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC )
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{
TSRMLS_C;
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_LONG, "An integer is expected for this keyword" )
std::string val_str = std::to_string( Z_LVAL_P( value ));
@ -157,11 +149,11 @@ struct int_conn_str_func {
template <unsigned int Attr>
struct int_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )), SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )), SQL_IS_UINTEGER );
}
catch( core::CoreException& ) {
throw;
@ -172,10 +164,10 @@ struct int_conn_attr_func {
template <unsigned int Attr>
struct bool_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
{
try {
core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)), SQL_IS_UINTEGER TSRMLS_CC);
core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)), SQL_IS_UINTEGER);
}
catch( core::CoreException& ) {
@ -187,15 +179,15 @@ struct bool_conn_attr_func {
//// *** internal functions ***
void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn TSRMLS_DC );
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,
_Inout_ HashTable* ss_conn_options_ht TSRMLS_DC );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht TSRMLS_DC );
_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 add_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len,
_Inout_ HashTable* options_ht, _Inout_ zval* data TSRMLS_DC );
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 TSRMLS_DC );
int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In_ size_t key_len, _Inout_ zval const* value_z TSRMLS_DC );
int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len TSRMLS_DC );
_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_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len );
}
@ -633,7 +625,7 @@ PHP_FUNCTION ( sqlsrv_connect )
g_ss_henv_cp->set_func(_FN_);
g_ss_henv_ncp->set_func(_FN_);
reset_errors( TSRMLS_C );
reset_errors();
const char* server = NULL;
zval* options_z = NULL;
@ -643,7 +635,7 @@ PHP_FUNCTION ( sqlsrv_connect )
zval conn_z;
ZVAL_UNDEF(&conn_z);
// get the server name and connection options
int result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "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" ) {
RETURN_FALSE;
@ -659,26 +651,26 @@ PHP_FUNCTION ( sqlsrv_connect )
ALLOC_HASHTABLE( ss_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, ss_conn_options_ht, 10 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
ZVAL_PTR_DTOR, 0 /*persistent*/ );
// 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 TSRMLS_CC );
::validate_conn_options( *g_ss_henv_cp, options_z, &uid, &pwd, ss_conn_options_ht );
// 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>,
server, uid, pwd, ss_conn_options_ht, ss_error_handler,
SS_CONN_OPTS, NULL, "sqlsrv_connect" TSRMLS_CC ));
SS_CONN_OPTS, NULL, "sqlsrv_connect" ));
SQLSRV_ASSERT( conn != NULL, "sqlsrv_connect: Invalid connection returned. Exception should have been thrown." );
// create a bunch of statements
ALLOC_HASHTABLE( stmts );
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, stmts, 5, NULL /* dtor */, 0 /* persistent */ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, stmts, 5, NULL /* dtor */, 0 /* persistent */ );
// register the connection with the PHP runtime
ss::zend_register_resource(conn_z, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name TSRMLS_CC);
ss::zend_register_resource(conn_z, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name);
conn->stmts = stmts;
stmts.transferred();
@ -739,7 +731,7 @@ PHP_FUNCTION( sqlsrv_begin_transaction )
try {
core_sqlsrv_begin_transaction( conn TSRMLS_CC );
core_sqlsrv_begin_transaction( conn );
conn->in_transaction = true;
RETURN_TRUE;
}
@ -778,7 +770,7 @@ PHP_FUNCTION( sqlsrv_close )
ss_sqlsrv_conn* conn = NULL;
sqlsrv_context_auto_ptr error_ctx;
reset_errors( TSRMLS_C );
reset_errors();
try {
@ -786,10 +778,10 @@ PHP_FUNCTION( sqlsrv_close )
error_ctx = new (sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_);
if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_r) == FAILURE ) {
if( zend_parse_parameters(ZEND_NUM_ARGS(), "r", &conn_r) == FAILURE ) {
// Check if it was a zval
int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "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_ ) {
throw ss::SSException();
}
@ -803,7 +795,7 @@ PHP_FUNCTION( sqlsrv_close )
}
}
SQLSRV_ASSERT( conn_r != NULL, "sqlsrv_close: conn_r was null" );
conn = static_cast<ss_sqlsrv_conn*>( zend_fetch_resource( Z_RES_P( conn_r ) TSRMLS_CC, ss_sqlsrv_conn::resource_name, ss_sqlsrv_conn::descriptor ));
conn = static_cast<ss_sqlsrv_conn*>( zend_fetch_resource( Z_RES_P( conn_r ), ss_sqlsrv_conn::resource_name, ss_sqlsrv_conn::descriptor ));
// if sqlsrv_close was called on an already closed connection then we just return success.
if ( Z_RES_TYPE_P( conn_r ) == RSRC_INVALID_TYPE) {
@ -820,9 +812,13 @@ PHP_FUNCTION( sqlsrv_close )
// 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
// removal fails, so we just log it and move on.
if( zend_list_close( Z_RES_P( conn_r ) ) == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove connection resource %1!d!", Z_RES_HANDLE_P( conn_r ));
#if PHP_VERSION_ID < 80000
if (zend_list_close(Z_RES_P(conn_r)) == FAILURE) {
LOG(SEV_ERROR, "Failed to remove connection resource %1!d!", Z_RES_HANDLE_P(conn_r));
}
#else
zend_list_close(Z_RES_P(conn_r));
#endif
// when conn_r is first parsed in zend_parse_parameters, conn_r becomes a zval that points to a zend_resource with a refcount of 2
// need to DELREF here so the refcount becomes 1 and conn_r can be appropriate destroyed by the garbage collector when it goes out of scope
@ -842,7 +838,7 @@ PHP_FUNCTION( sqlsrv_close )
}
}
void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc TSRMLS_DC )
void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc )
{
// 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.
@ -855,10 +851,10 @@ void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc TSRMLS_DC )
conn->set_func(__func__);
// close all statements associated with the connection.
sqlsrv_conn_close_stmts( conn TSRMLS_CC );
sqlsrv_conn_close_stmts( conn );
// close the connection itself.
core_sqlsrv_close( conn TSRMLS_CC );
core_sqlsrv_close( conn );
rsrc->ptr = NULL;
}
@ -901,7 +897,7 @@ PHP_FUNCTION( sqlsrv_commit )
try {
conn->in_transaction = false;
core_sqlsrv_commit( conn TSRMLS_CC );
core_sqlsrv_commit( conn );
RETURN_TRUE;
}
@ -955,7 +951,7 @@ PHP_FUNCTION( sqlsrv_rollback )
try {
conn->in_transaction = false;
core_sqlsrv_rollback( conn TSRMLS_CC );
core_sqlsrv_rollback( conn );
RETURN_TRUE;
}
catch( core::CoreException& ){
@ -975,28 +971,22 @@ PHP_FUNCTION( sqlsrv_rollback )
PHP_FUNCTION( sqlsrv_client_info )
{
LOG_FUNCTION( "sqlsrv_client_info" );
ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 );
try {
core_sqlsrv_get_client_info(conn, return_value);
core_sqlsrv_get_client_info( conn, return_value TSRMLS_CC );
// Add the sqlsrv driver's file version
//Declarations below eliminate compiler warnings about string constant to char* conversions
const char* extver = "ExtensionVer";
std::string filever = VER_FILEVERSION_STR;
core::sqlsrv_add_assoc_string( *conn, return_value, extver, &filever[0], 1 /*duplicate*/ TSRMLS_CC );
}
catch( core::CoreException& ) {
add_assoc_string(return_value, extver, &filever[0]);
} catch (core::CoreException&) {
RETURN_FALSE;
}
catch( ... ) {
DIE( "sqlsrv_client_info: Unknown exception caught." );
} catch (...) {
DIE("sqlsrv_client_info: Unknown exception caught.");
}
}
@ -1019,20 +1009,15 @@ PHP_FUNCTION( sqlsrv_client_info )
PHP_FUNCTION( sqlsrv_server_info )
{
try {
LOG_FUNCTION( "sqlsrv_server_info" );
LOG_FUNCTION("sqlsrv_server_info");
ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 );
PROCESS_PARAMS(conn, "r", _FN_, 0);
core_sqlsrv_get_server_info( conn, return_value TSRMLS_CC );
}
catch( core::CoreException& ) {
core_sqlsrv_get_server_info(conn, return_value);
} catch (core::CoreException&) {
RETURN_FALSE;
}
catch( ... ) {
DIE( "sqlsrv_server_info: Unknown exception caught." );
} catch (...) {
DIE("sqlsrv_server_info: Unknown exception caught.");
}
}
@ -1093,9 +1078,9 @@ PHP_FUNCTION( sqlsrv_prepare )
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
ZVAL_PTR_DTOR, 0 /*persistent*/ );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht );
}
@ -1114,9 +1099,9 @@ PHP_FUNCTION( sqlsrv_prepare )
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_error_handler, NULL TSRMLS_CC ) );
ss_error_handler, NULL ) );
core_sqlsrv_prepare( stmt, sql, sql_len TSRMLS_CC );
core_sqlsrv_prepare( stmt, sql, sql_len );
if (params_z) {
stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval));
@ -1126,13 +1111,13 @@ PHP_FUNCTION( sqlsrv_prepare )
stmt->prepared = true;
// register the statement with the PHP runtime
ss::zend_register_resource( stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name TSRMLS_CC );
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
// can release this statement when it closes.
zend_long next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z TSRMLS_CC);
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z);
stmt->conn_index = next_index;
@ -1150,7 +1135,7 @@ PHP_FUNCTION( sqlsrv_prepare )
stmt->~ss_sqlsrv_stmt();
}
if (!Z_ISUNDEF(stmt_z)) {
free_stmt_resource(&stmt_z TSRMLS_CC);
free_stmt_resource(&stmt_z);
}
RETURN_FALSE;
@ -1216,9 +1201,9 @@ PHP_FUNCTION( sqlsrv_query )
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 5 /* # of buckets */, ZVAL_PTR_DTOR,
0 /*persistent*/ TSRMLS_CC );
0 /*persistent*/ );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht );
}
if( params_z && Z_TYPE_P( params_z ) != IS_ARRAY ) {
@ -1236,7 +1221,7 @@ PHP_FUNCTION( sqlsrv_query )
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_error_handler, NULL TSRMLS_CC ) );
ss_error_handler, NULL ) );
if( params_z ) {
stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval));
@ -1245,18 +1230,18 @@ PHP_FUNCTION( sqlsrv_query )
stmt->set_func( "sqlsrv_query" );
bind_params( stmt TSRMLS_CC );
bind_params( stmt );
// execute the statement
core_sqlsrv_execute( stmt TSRMLS_CC, sql, static_cast<int>( sql_len ) );
core_sqlsrv_execute( stmt, sql, static_cast<int>( sql_len ) );
// register the statement with the PHP runtime
ss::zend_register_resource(stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name TSRMLS_CC);
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
// can release this statement when it closes.
zend_ulong next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z TSRMLS_CC);
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z);
stmt->conn_index = next_index;
stmt.transferred();
@ -1271,7 +1256,7 @@ PHP_FUNCTION( sqlsrv_query )
stmt->~ss_sqlsrv_stmt();
}
if (!Z_ISUNDEF(stmt_z)) {
free_stmt_resource(&stmt_z TSRMLS_CC);
free_stmt_resource(&stmt_z);
}
RETURN_FALSE;
@ -1282,11 +1267,16 @@ PHP_FUNCTION( sqlsrv_query )
}
}
void free_stmt_resource( _Inout_ zval* stmt_z TSRMLS_DC )
void free_stmt_resource( _Inout_ zval* stmt_z )
{
if( FAILURE == zend_list_close( Z_RES_P( stmt_z ))) {
#if PHP_VERSION_ID < 80000
if (FAILURE == zend_list_close(Z_RES_P(stmt_z))) {
LOG(SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_HANDLE_P(stmt_z));
}
#else
zend_list_close(Z_RES_P(stmt_z));
#endif
ZVAL_NULL( stmt_z );
zval_ptr_dtor(stmt_z);
}
@ -1298,7 +1288,7 @@ namespace {
// must close all statement handles opened by this connection before closing the connection
// no errors are returned, since close should always succeed
void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn TSRMLS_DC )
void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn )
{
//pre-condition check
SQLSRV_ASSERT(( conn->handle() != NULL ), "sqlsrv_conn_close_stmts: Connection handle is NULL. Trying to destroy an "
@ -1336,9 +1326,13 @@ void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn TSRMLS_DC )
// this would call the destructor on the statement.
// There's nothing we can do if the removal fails, so we just log it and move on.
if( zend_list_close( Z_RES_P(rsrc_ptr) ) == FAILURE ) {
#if PHP_VERSION_ID < 80000
if (zend_list_close(Z_RES_P(rsrc_ptr)) == FAILURE) {
LOG(SEV_ERROR, "Failed to remove statement resource %1!d! when closing the connection", Z_RES_HANDLE_P(rsrc_ptr));
}
#else
zend_list_close(Z_RES_P(rsrc_ptr));
#endif
} ZEND_HASH_FOREACH_END();
zend_hash_destroy( conn->stmts );
@ -1346,7 +1340,7 @@ void sqlsrv_conn_close_stmts( _Inout_ ss_sqlsrv_conn* conn TSRMLS_DC )
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 TSRMLS_DC )
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 )
{
@ -1407,7 +1401,7 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
return SQLSRV_CONN_OPTION_INVALID;
}
int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len TSRMLS_DC )
int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len )
{
for( int i = 0; SS_STMT_OPTS[i].key != SQLSRV_STMT_OPTION_INVALID; ++i )
{
@ -1419,9 +1413,9 @@ int get_stmt_option_key( _In_ zend_string* key, _In_ size_t key_len TSRMLS_DC )
}
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 TSRMLS_DC )
_Inout_ HashTable* options_ht, _Inout_ zval* data )
{
int option_key = ::get_stmt_option_key( key, key_len TSRMLS_CC );
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 ) ) {
@ -1429,27 +1423,27 @@ void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _I
}
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 TSRMLS_CC );
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,
_Inout_ HashTable* options_ht, _Inout_ zval* data TSRMLS_DC )
_Inout_ HashTable* options_ht, _Inout_ zval* data )
{
int option_key = ::get_conn_option_key( ctx, key, key_len, data TSRMLS_CC );
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 ) ) {
throw ss::SSException();
}
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 TSRMLS_CC );
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
// 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.
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht TSRMLS_DC )
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht )
{
try {
if( stmt_options ) {
@ -1471,7 +1465,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
}
else if ( key != NULL ) {
key_len = ZSTR_LEN( key ) + 1;
add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data TSRMLS_CC );
add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data );
}
else {
DIE( "validate_stmt_options: key was null." );
@ -1489,7 +1483,7 @@ void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_opti
// 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.
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 TSRMLS_DC )
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 )
{
try {
@ -1524,7 +1518,7 @@ void validate_conn_options( _Inout_ sqlsrv_context& ctx, _In_ zval* user_options
}
else {
::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data TSRMLS_CC );
::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data );
}
}
else {

View file

@ -2,7 +2,7 @@
// File: init.cpp
// Contents: initialization routines for the extension
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -98,6 +98,8 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX( sqlsrv_fetch_arginfo, 0, 0, 1 )
ZEND_ARG_INFO( 0, stmt )
ZEND_ARG_INFO( 0, row )
ZEND_ARG_INFO( 0, offset )
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX( sqlsrv_fetch_array_arginfo, 0, 0, 1 )
@ -551,14 +553,14 @@ PHP_MINIT_FUNCTION(sqlsrv)
}
}
if( php_register_url_stream_wrapper( SQLSRV_STREAM_WRAPPER, &g_sqlsrv_stream_wrapper TSRMLS_CC ) == FAILURE ) {
if( php_register_url_stream_wrapper( SQLSRV_STREAM_WRAPPER, &g_sqlsrv_stream_wrapper ) == FAILURE ) {
LOG( SEV_ERROR, "%1!s!: stream registration failed", _FN_ );
return FAILURE;
}
try {
// retrieve the handles for the environments
core_sqlsrv_minit( &g_ss_henv_cp, &g_ss_henv_ncp, ss_error_handler, "PHP_MINIT_FUNCTION for sqlsrv" TSRMLS_CC );
core_sqlsrv_minit( &g_ss_henv_cp, &g_ss_henv_ncp, ss_error_handler, "PHP_MINIT_FUNCTION for sqlsrv" );
}
catch( core::CoreException& ) {
@ -610,7 +612,7 @@ PHP_MSHUTDOWN_FUNCTION(sqlsrv)
core_sqlsrv_mshutdown( *g_ss_henv_cp, *g_ss_henv_ncp );
if( php_unregister_url_stream_wrapper( SQLSRV_STREAM_WRAPPER TSRMLS_CC ) == FAILURE ) {
if( php_unregister_url_stream_wrapper( SQLSRV_STREAM_WRAPPER ) == FAILURE ) {
return FAILURE;
}
@ -692,9 +694,9 @@ PHP_RSHUTDOWN_FUNCTION(sqlsrv)
SQLSRV_UNUSED( type );
LOG_FUNCTION( "PHP_RSHUTDOWN for php_sqlsrv" );
reset_errors( TSRMLS_C );
reset_errors();
// TODO - destruction
// destruction
zval_ptr_dtor( &SQLSRV_G( errors ));
zval_ptr_dtor( &SQLSRV_G( warnings ));

View file

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

View file

@ -8,7 +8,7 @@
//
// Comments: Also contains "internal" declarations shared across source files.
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -91,8 +91,8 @@ struct ss_sqlsrv_conn : sqlsrv_conn
static int descriptor;
// initialize with default values
ss_sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv TSRMLS_DC ) :
sqlsrv_conn( h, e, drv, SQLSRV_ENCODING_SYSTEM TSRMLS_CC ),
ss_sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv ) :
sqlsrv_conn( h, e, drv, SQLSRV_ENCODING_SYSTEM ),
stmts( NULL ),
date_as_string( false ),
format_decimals( false ),
@ -103,8 +103,7 @@ struct ss_sqlsrv_conn : sqlsrv_conn
};
// resource destructor
void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc TSRMLS_DC );
void __cdecl sqlsrv_conn_dtor( _Inout_ zend_resource *rsrc );
//*********************************************************************************************************************************
// Statement
@ -117,27 +116,22 @@ struct sqlsrv_fetch_field_name {
};
struct stmt_option_ss_scrollable : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC );
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
// This object inherits and overrides the callbacks necessary
struct ss_sqlsrv_stmt : public sqlsrv_stmt {
ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv TSRMLS_DC );
ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv );
virtual ~ss_sqlsrv_stmt( void );
void new_result_set( TSRMLS_D );
void new_result_set( void );
// 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 );
// driver specific way to set query timeout
virtual void set_query_timeout();
bool prepared; // whether the statement has been prepared yet (used for error messages)
zend_ulong conn_index; // index into the connection hash that contains this statement structure
zend_ulong conn_index; // index into the connection hash that contains this statement structure
zval* params_z; // hold parameters passed to sqlsrv_prepare but not used until sqlsrv_execute
sqlsrv_fetch_field_name* fetch_field_names; // field names for current results used by sqlsrv_fetch_array/object as keys
int fetch_fields_count;
@ -166,14 +160,14 @@ struct sqlsrv_stream_encoding {
};
// resource destructor
void __cdecl sqlsrv_stmt_dtor( _Inout_ zend_resource *rsrc TSRMLS_DC );
void __cdecl sqlsrv_stmt_dtor( _Inout_ zend_resource *rsrc );
// "internal" statement functions shared by functions in conn.cpp and stmt.cpp
void bind_params( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC );
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
TSRMLS_DC );
void free_odbc_resources( ss_sqlsrv_stmt* stmt TSRMLS_DC );
void free_stmt_resource( _Inout_ zval* stmt_z TSRMLS_DC );
);
void free_odbc_resources( ss_sqlsrv_stmt* stmt );
void free_stmt_resource( _Inout_ zval* stmt_z );
//*********************************************************************************************************************************
@ -218,7 +212,7 @@ enum SS_ERROR_CODES {
extern ss_error SS_ERRORS[];
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning TSRMLS_DC, _In_opt_ va_list* print_args );
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args );
// convert from the default encoding specified by the "CharacterSet"
// connection option to UTF-16. mbcs_len and utf16_len are sizes in
@ -235,13 +229,13 @@ SQLWCHAR* utf16_string_from_mbcs_string( _In_ unsigned int php_encoding, _In_rea
// *** internal error macros and functions ***
bool handle_error( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp TSRMLS_DC, ... );
sqlsrv_error const* ssphp, ... );
void handle_warning( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp TSRMLS_DC, ... );
void __cdecl sqlsrv_error_dtor( zend_resource *rsrc TSRMLS_DC );
sqlsrv_error const* ssphp, ... );
void __cdecl sqlsrv_error_dtor( zend_resource *rsrc );
// release current error lists and set to NULL
inline void reset_errors( TSRMLS_D )
inline void reset_errors( void )
{
if( Z_TYPE( SQLSRV_G( errors )) != IS_ARRAY && Z_TYPE( SQLSRV_G( errors )) != IS_NULL ) {
DIE( "sqlsrv_errors contains an invalid type" );
@ -264,7 +258,7 @@ inline void reset_errors( TSRMLS_D )
}
#define THROW_SS_ERROR( ctx, error_code, ... ) \
(void)call_error_handler( ctx, error_code TSRMLS_CC, false /*warning*/, ## __VA_ARGS__ ); \
(void)call_error_handler( ctx, error_code, false /*warning*/, ## __VA_ARGS__ ); \
throw ss::SSException();
@ -317,7 +311,7 @@ public:
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
bool ss_severity_check(_In_ unsigned int severity TSRMLS_DC);
bool ss_severity_check(_In_ unsigned int severity);
// subsystems that may report log messages. These may be used to filter which systems write to the log to prevent noise.
enum logging_subsystems {
@ -345,7 +339,7 @@ namespace ss {
}
};
inline void zend_register_resource( _Inout_ zval& rsrc_result, _Inout_ void* rsrc_pointer, _In_ int rsrc_type, _In_opt_ const char* rsrc_name TSRMLS_DC)
inline void zend_register_resource( _Inout_ zval& rsrc_result, _Inout_ void* rsrc_pointer, _In_ int rsrc_type, _In_opt_ const char* rsrc_name)
{
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,
@ -369,10 +363,10 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
SQLSRV_UNUSED( return_value );
zval* rsrc;
H* h;
H* h = NULL;
// reset the errors from the previous API call
reset_errors( TSRMLS_C );
reset_errors();
if( ZEND_NUM_ARGS() > param_count + 1 ) {
DIE( "Param count and argument count don't match." );
@ -406,35 +400,35 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
switch( param_count ) {
case 0:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>( param_spec ), &rsrc );
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc );
break;
case 1:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>( param_spec ), &rsrc, arr[0] );
result = zend_parse_parameters( ZEND_NUM_ARGS(), const_cast<char*>( param_spec ), &rsrc, arr[0] );
break;
case 2:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, 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] );
break;
case 3:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, 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] );
break;
case 4:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, 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] );
break;
case 5:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, 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] );
break;
case 6:
result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, 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] );
break;
@ -451,7 +445,7 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_s
}
// get the resource registered
h = static_cast<H*>( zend_fetch_resource(Z_RES_P(rsrc) TSRMLS_CC, 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 ) {

View file

@ -3,7 +3,7 @@
//
// Contents: Routines that use statement handles
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -89,21 +89,20 @@ const char* NULLABLE = "Nullable";
void convert_to_zval( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSRV_PHPTYPE sqlsrv_php_type, _In_opt_ void* in_val, _In_ SQLLEN field_len, _Inout_ zval& out_zval );
SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt* stmt);
void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_type, _Out_ zval& fields, _In_ bool allow_empty_field_names
TSRMLS_DC );
void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_type, _Out_ zval& fields, _In_ bool allow_empty_field_names );
bool determine_column_size_or_precision( sqlsrv_stmt const* stmt, _In_ sqlsrv_sqltype sqlsrv_type, _Inout_ SQLULEN* column_size,
_Out_ SQLSMALLINT* decimal_digits );
sqlsrv_phptype determine_sqlsrv_php_type( sqlsrv_stmt const* stmt, SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string );
void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC );
void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt );
bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type );
bool is_valid_sqlsrv_sqltype( _In_ sqlsrv_sqltype type );
void parse_param_array( _Inout_ ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction,
_Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC );
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits );
void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type );
bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding TSRMLS_DC );
bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding );
}
@ -126,15 +125,15 @@ namespace SSCursorTypes {
const char QUERY_OPTION_SCROLLABLE_BUFFERED[] = "buffered";
}
ss_sqlsrv_stmt::ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv TSRMLS_DC ) :
sqlsrv_stmt( c, handle, e, drv TSRMLS_CC ),
ss_sqlsrv_stmt::ss_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv ) :
sqlsrv_stmt( c, handle, e, drv ),
prepared( false ),
conn_index( -1 ),
params_z( NULL ),
fetch_field_names( NULL ),
fetch_fields_count ( 0 )
{
core_sqlsrv_set_buffered_query_limit( this, SQLSRV_G( buffered_query_limit ) TSRMLS_CC );
core_sqlsrv_set_buffered_query_limit( this, SQLSRV_G( buffered_query_limit ) );
// inherit other values based on the corresponding connection options
ss_sqlsrv_conn* ss_conn = static_cast<ss_sqlsrv_conn*>(conn);
@ -164,7 +163,7 @@ ss_sqlsrv_stmt::~ss_sqlsrv_stmt( void )
// to be called whenever a new result set is created, such as after an
// execute or next_result. Resets the state variables and calls the subclass.
void ss_sqlsrv_stmt::new_result_set( TSRMLS_D )
void ss_sqlsrv_stmt::new_result_set( void )
{
if( fetch_field_names != NULL ) {
@ -177,7 +176,7 @@ void ss_sqlsrv_stmt::new_result_set( TSRMLS_D )
fetch_field_names = NULL;
fetch_fields_count = 0;
sqlsrv_stmt::new_result_set( TSRMLS_C );
sqlsrv_stmt::new_result_set();
}
// Returns a php type for a given sql type. Also sets the encoding wherever applicable.
@ -262,29 +261,6 @@ sqlsrv_phptype ss_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type, _
return ss_phptype;
}
void ss_sqlsrv_stmt::set_query_timeout()
{
if (query_timeout == QUERY_TIMEOUT_INVALID || query_timeout < 0) {
return;
}
// set the statement attribute
core::SQLSetStmtAttr(this, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>( (SQLLEN)query_timeout ), SQL_IS_UINTEGER TSRMLS_CC );
// a query timeout of 0 indicates "no timeout", which means that lock_timeout should also be set to "no timeout" which
// is represented by -1.
int lock_timeout = (( query_timeout == 0 ) ? -1 : query_timeout * 1000 /*convert to milliseconds*/ );
// set the LOCK_TIMEOUT on the server.
char lock_timeout_sql[32] = {'\0'};
int written = snprintf( lock_timeout_sql, sizeof( lock_timeout_sql ), "SET LOCK_TIMEOUT %d", lock_timeout );
SQLSRV_ASSERT( (written != -1 && written != sizeof( lock_timeout_sql )),
"stmt_option_query_timeout: snprintf failed. Shouldn't ever fail." );
core::SQLExecDirect(this, lock_timeout_sql TSRMLS_CC );
}
// statement specific parameter proccessing. Uses the generic function specialised to return a statement
// resource.
#define PROCESS_PARAMS( rsrc, param_spec, calling_func, param_count, ... ) \
@ -327,14 +303,14 @@ PHP_FUNCTION( sqlsrv_execute )
// to prepare to execute the next statement, we skip any remaining results (and skip parameter finalization too)
while( stmt->past_next_result_end == false ) {
core_sqlsrv_next_result( stmt TSRMLS_CC, false, false );
core_sqlsrv_next_result( stmt, false, false );
}
}
// bind parameters before executing
bind_params( stmt TSRMLS_CC );
bind_params( stmt );
core_sqlsrv_execute( stmt TSRMLS_CC );
core_sqlsrv_execute( stmt );
RETURN_TRUE;
}
@ -383,7 +359,7 @@ PHP_FUNCTION( sqlsrv_fetch )
throw ss::SSException();
}
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset TSRMLS_CC );
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset );
if( !result ) {
RETURN_NULL();
}
@ -442,13 +418,13 @@ PHP_FUNCTION( sqlsrv_fetch_array )
throw ss::SSException();
}
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset TSRMLS_CC );
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset );
if( !result ) {
RETURN_NULL();
}
zval fields;
ZVAL_UNDEF( &fields );
fetch_fields_common( stmt, fetch_type, fields, true /*allow_empty_field_names*/ TSRMLS_CC );
fetch_fields_common( stmt, fetch_type, fields, true /*allow_empty_field_names*/ );
RETURN_ARR( Z_ARRVAL( fields ));
}
@ -500,8 +476,8 @@ PHP_FUNCTION( sqlsrv_field_metadata )
}
zval result_meta_data;
ZVAL_UNDEF( &result_meta_data );
core::sqlsrv_array_init( *stmt, &result_meta_data TSRMLS_CC );
ZVAL_UNDEF(&result_meta_data);
array_init(&result_meta_data);
for( SQLSMALLINT f = 0; f < num_cols; ++f ) {
field_meta_data* core_meta_data = stmt->current_meta_data[f];
@ -509,13 +485,13 @@ PHP_FUNCTION( sqlsrv_field_metadata )
// initialize the array
zval field_array;
ZVAL_UNDEF( &field_array );
core::sqlsrv_array_init( *stmt, &field_array TSRMLS_CC );
array_init(&field_array );
// add the field name to the associative array but keep a copy
core::sqlsrv_add_assoc_string(*stmt, &field_array, FieldMetaData::NAME,
reinterpret_cast<char*>(core_meta_data->field_name.get()), 1 TSRMLS_CC);
add_assoc_string(&field_array, FieldMetaData::NAME, reinterpret_cast<char*>(core_meta_data->field_name.get()));
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::TYPE, core_meta_data->field_type TSRMLS_CC );
//core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::TYPE, core_meta_data->field_type );
add_assoc_long(&field_array, FieldMetaData::TYPE, core_meta_data->field_type);
switch( core_meta_data->field_type ) {
case SQL_DECIMAL:
@ -524,9 +500,9 @@ PHP_FUNCTION( sqlsrv_field_metadata )
case SQL_TYPE_DATE:
case SQL_SS_TIME2:
case SQL_SS_TIMESTAMPOFFSET:
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::SCALE, core_meta_data->field_scale TSRMLS_CC );
add_assoc_null(&field_array, FieldMetaData::SIZE);
add_assoc_long(&field_array, FieldMetaData::PREC, core_meta_data->field_precision);
add_assoc_long(&field_array, FieldMetaData::SCALE, core_meta_data->field_scale);
break;
case SQL_BIT:
case SQL_TINYINT:
@ -536,27 +512,26 @@ PHP_FUNCTION( sqlsrv_field_metadata )
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SCALE TSRMLS_CC );
add_assoc_null(&field_array, FieldMetaData::SIZE);
add_assoc_long(&field_array, FieldMetaData::PREC, core_meta_data->field_precision);
add_assoc_null(&field_array, FieldMetaData::SCALE);
break;
default:
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::SIZE, core_meta_data->field_size TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::PREC TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SCALE TSRMLS_CC );
add_assoc_long(&field_array, FieldMetaData::SIZE, core_meta_data->field_size);
add_assoc_null(&field_array, FieldMetaData::PREC);
add_assoc_null(&field_array, FieldMetaData::SCALE);
break;
}
// add the nullability to the array
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::NULLABLE, core_meta_data->field_is_nullable
TSRMLS_CC );
add_assoc_long(&field_array, FieldMetaData::NULLABLE, core_meta_data->field_is_nullable);
if (stmt->data_classification) {
data_classification::fill_column_sensitivity_array(stmt, f, &field_array TSRMLS_CC);
data_classification::fill_column_sensitivity_array(stmt, f, &field_array);
}
// add this field's meta data to the result set meta data
core::sqlsrv_add_next_index_zval( *stmt, &result_meta_data, &field_array TSRMLS_CC );
add_next_index_zval(&result_meta_data, &field_array);
}
// return our built collection and transfer ownership
@ -600,7 +575,7 @@ PHP_FUNCTION( sqlsrv_next_result )
try {
core_sqlsrv_next_result( stmt TSRMLS_CC, true );
core_sqlsrv_next_result( stmt, true );
// clear the current meta data since the new result will generate new meta data
std::for_each(stmt->current_meta_data.begin(), stmt->current_meta_data.end(), meta_data_free);
@ -659,7 +634,7 @@ PHP_FUNCTION( sqlsrv_rows_affected )
throw ss::SSException();
}
rows = stmt->current_results->row_count( TSRMLS_C );
rows = stmt->current_results->row_count();
RETURN_LONG( rows );
}
@ -708,7 +683,7 @@ PHP_FUNCTION( sqlsrv_num_rows )
throw ss::SSException();
}
rows = stmt->current_results->row_count( TSRMLS_C );
rows = stmt->current_results->row_count();
RETURN_LONG( rows );
}
@ -747,7 +722,7 @@ PHP_FUNCTION( sqlsrv_num_fields )
try {
// retrieve the number of columns from ODBC
fields = core::SQLNumResultCols( stmt TSRMLS_CC );
fields = core::SQLNumResultCols( stmt );
RETURN_LONG( fields );
}
@ -844,18 +819,18 @@ PHP_FUNCTION( sqlsrv_fetch_object )
}
// fetch the data
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset TSRMLS_CC );
bool result = core_sqlsrv_fetch( stmt, static_cast<SQLSMALLINT>(fetch_style), fetch_offset );
if( !result ) {
RETURN_NULL();
}
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, retval_z, false /*allow_empty_field_names*/ TSRMLS_CC );
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, retval_z, false /*allow_empty_field_names*/ );
properties_ht = Z_ARRVAL( retval_z );
// find the zend_class_entry of the class the user requested (stdClass by default) for use below
zend_class_entry* class_entry = NULL;
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 TSRMLS_CC ))) ? SUCCESS : FAILURE;
int zr = ( NULL != ( class_entry = zend_lookup_class( class_name_str_z ))) ? SUCCESS : FAILURE;
zend_string_release( class_name_str_z );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_BAD_CLASS, class_name ) {
throw ss::SSException();
@ -873,7 +848,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// causes duplicate properties when the visibilities are different and also references the
// default parameters directly in the object, meaning the default property value is changed when
// the object's property is changed.
zend_merge_properties( &retval_z, properties_ht TSRMLS_CC );
zend_merge_properties( &retval_z, properties_ht );
zend_hash_destroy( properties_ht );
FREE_HASHTABLE( properties_ht );
@ -940,7 +915,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
fcic.object = Z_OBJ_P( &retval_z );
zr = zend_call_function( &fci, &fcic TSRMLS_CC );
zr = zend_call_function( &fci, &fcic );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
@ -1004,7 +979,7 @@ PHP_FUNCTION( sqlsrv_has_rows )
if( !stmt->has_rows && !stmt->fetch_called ) {
determine_stmt_has_rows( stmt TSRMLS_CC );
determine_stmt_has_rows( stmt );
}
if( stmt->has_rows ) {
@ -1057,7 +1032,7 @@ PHP_FUNCTION( sqlsrv_send_stream_data )
}
// send the next packet
bool more = core_sqlsrv_send_stream_packet( stmt TSRMLS_CC );
bool more = core_sqlsrv_send_stream_packet( stmt );
// if more to send, return true
if( more ) {
@ -1130,7 +1105,7 @@ PHP_FUNCTION( sqlsrv_get_field )
}
core_sqlsrv_get_field( stmt, static_cast<SQLUSMALLINT>( field_index ), sqlsrv_php_type, false, field_value, &field_len, false/*cache_field*/,
&sqlsrv_php_type_out TSRMLS_CC );
&sqlsrv_php_type_out );
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, retval_z );
sqlsrv_free( field_value );
RETURN_ZVAL( &retval_z, 1, 1 );
@ -1213,7 +1188,7 @@ PHP_FUNCTION(SQLSRV_SQLTYPE_VARCHAR)
type_and_size_calc( INTERNAL_FUNCTION_PARAM_PASSTHRU, SQL_VARCHAR );
}
void bind_params( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
void bind_params( _Inout_ ss_sqlsrv_stmt* stmt )
{
// if there's nothing to do, just return
if( stmt->params_z == NULL ) {
@ -1222,7 +1197,7 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
try {
stmt->free_param_data( TSRMLS_C );
stmt->free_param_data();
stmt->executed = false;
@ -1263,7 +1238,7 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
// parse the parameter array that the user gave
parse_param_array( stmt, param_z, index, direction, php_out_type, encoding, sql_type, column_size,
decimal_digits TSRMLS_CC );
decimal_digits );
value_z = var;
}
else {
@ -1275,14 +1250,14 @@ void bind_params( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
// bind the parameter
SQLSRV_ASSERT( value_z != NULL, "bind_params: value_z is null." );
core_sqlsrv_bind_param( stmt, static_cast<SQLUSMALLINT>( index ), direction, value_z, php_out_type, encoding, sql_type, column_size,
decimal_digits TSRMLS_CC );
decimal_digits );
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
}
catch( core::CoreException& ) {
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
zval_ptr_dtor( stmt->params_z );
sqlsrv_free( stmt->params_z );
sqlsrv_free( stmt->params_z );
stmt->params_z = NULL;
throw;
}
@ -1312,7 +1287,7 @@ PHP_FUNCTION( sqlsrv_cancel )
try {
// close the stream to release the resource
close_active_stream( stmt TSRMLS_CC );
close_active_stream( stmt );
SQLRETURN r = SQLCancel( stmt->handle() );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
@ -1331,7 +1306,7 @@ PHP_FUNCTION( sqlsrv_cancel )
}
}
void __cdecl sqlsrv_stmt_dtor( _Inout_ zend_resource *rsrc TSRMLS_DC )
void __cdecl sqlsrv_stmt_dtor( _Inout_ zend_resource *rsrc )
{
LOG_FUNCTION( "sqlsrv_stmt_dtor" );
@ -1377,7 +1352,7 @@ PHP_FUNCTION( sqlsrv_free_stmt )
ss_sqlsrv_stmt* stmt = NULL;
sqlsrv_context_auto_ptr error_ctx;
reset_errors( TSRMLS_C );
reset_errors();
try {
@ -1386,10 +1361,10 @@ PHP_FUNCTION( sqlsrv_free_stmt )
error_ctx->set_func(_FN_);
// take only the statement resource
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "r", &stmt_r ) == FAILURE ) {
if( zend_parse_parameters( ZEND_NUM_ARGS(), "r", &stmt_r ) == FAILURE ) {
// Check if it was a zval
int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "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_ ) {
throw ss::SSException();
@ -1406,14 +1381,14 @@ PHP_FUNCTION( sqlsrv_free_stmt )
}
// verify the resource so we know we're deleting a statement
stmt = static_cast<ss_sqlsrv_stmt*>(zend_fetch_resource_ex(stmt_r TSRMLS_CC, ss_sqlsrv_stmt::resource_name, ss_sqlsrv_stmt::descriptor));
stmt = static_cast<ss_sqlsrv_stmt*>(zend_fetch_resource_ex(stmt_r, ss_sqlsrv_stmt::resource_name, ss_sqlsrv_stmt::descriptor));
// if sqlsrv_free_stmt was called on an already closed statment then we just return success.
// zend_list_close sets the type of the closed statment to -1.
// if sqlsrv_free_stmt was called on an already closed statment then we just return success.
// zend_list_close sets the type of the closed statment to -1.
SQLSRV_ASSERT( stmt_r != NULL, "sqlsrv_free_stmt: stmt_r is null." );
if ( Z_RES_TYPE_P( stmt_r ) == RSRC_INVALID_TYPE ) {
RETURN_TRUE;
}
if ( Z_RES_TYPE_P( stmt_r ) == RSRC_INVALID_TYPE ) {
RETURN_TRUE;
}
if( stmt == NULL ) {
@ -1421,9 +1396,13 @@ PHP_FUNCTION( sqlsrv_free_stmt )
}
// delete the resource from Zend's master list, which will trigger the statement's destructor
if( zend_list_close( Z_RES_P(stmt_r) ) == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_P( stmt_r )->handle);
#if PHP_VERSION_ID < 80000
if (zend_list_close(Z_RES_P(stmt_r)) == FAILURE) {
LOG(SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_P(stmt_r)->handle);
}
#else
zend_list_close(Z_RES_P(stmt_r));
#endif
// when stmt_r is first parsed in zend_parse_parameters, stmt_r becomes a zval that points to a zend_resource with a refcount of 2
// need to DELREF here so the refcount becomes 1 and stmt_r can be appropriate destroyed by the garbage collector when it goes out of scope
@ -1445,7 +1424,7 @@ PHP_FUNCTION( sqlsrv_free_stmt )
}
}
void stmt_option_ss_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
void stmt_option_ss_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_STRING ), stmt, SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE ) {
throw ss::SSException();
@ -1485,7 +1464,7 @@ void stmt_option_ss_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_opt
THROW_SS_ERROR( stmt, SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE );
}
core_sqlsrv_set_scrollable( stmt, cursor_type TSRMLS_CC );
core_sqlsrv_set_scrollable( stmt, cursor_type );
}
@ -1753,7 +1732,7 @@ sqlsrv_phptype determine_sqlsrv_php_type( _In_ ss_sqlsrv_stmt const* stmt, _In_
// The return value simply states whether or not if an error occurred during the determination.
// (All errors are posted here before returning.)
void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt )
{
SQLRETURN r = SQL_SUCCESS;
@ -1766,7 +1745,7 @@ void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
stmt->has_rows = false;
// if there are no columns then there are no rows
if( core::SQLNumResultCols( stmt TSRMLS_CC ) == 0 ) {
if( core::SQLNumResultCols( stmt ) == 0 ) {
return;
}
@ -1775,13 +1754,13 @@ void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
// fetch the first row, and then roll the cursor back to be prior to the first row
if( stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY ) {
r = stmt->current_results->fetch( SQL_FETCH_FIRST, 0 TSRMLS_CC );
r = stmt->current_results->fetch( SQL_FETCH_FIRST, 0 );
if( SQL_SUCCEEDED( r )) {
stmt->has_rows = true;
CHECK_SQL_WARNING( r, stmt );
// restore the cursor to its original position.
r = stmt->current_results->fetch( SQL_FETCH_ABSOLUTE, 0 TSRMLS_CC );
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 "
"of the result set." );
}
@ -1792,7 +1771,7 @@ void determine_stmt_has_rows( _Inout_ ss_sqlsrv_stmt* stmt TSRMLS_DC )
// flag and simply skips the first fetch, knowing it was already done. It records its own
// flags to know if it should fetch on subsequent calls.
r = core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC );
r = core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 );
if( SQL_SUCCEEDED( r )) {
stmt->has_rows = true;
@ -1813,7 +1792,7 @@ SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt * stmt)
if (num_cols == 0) {
getMetaData = true;
if (stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
num_cols = core::SQLNumResultCols(stmt TSRMLS_CC);
num_cols = core::SQLNumResultCols(stmt);
stmt->column_count = num_cols;
} else {
num_cols = stmt->column_count;
@ -1824,7 +1803,7 @@ SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt * stmt)
if (getMetaData) {
for (int i = 0; i < num_cols; i++) {
sqlsrv_malloc_auto_ptr<field_meta_data> core_meta_data;
core_meta_data = core_sqlsrv_field_metadata(stmt, i TSRMLS_CC);
core_meta_data = core_sqlsrv_field_metadata(stmt, i);
stmt->current_meta_data.push_back(core_meta_data.get());
core_meta_data.transferred();
}
@ -1838,8 +1817,7 @@ SQLSMALLINT get_resultset_meta_data(_Inout_ sqlsrv_stmt * stmt)
return num_cols;
}
void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_type, _Out_ zval& fields, _In_ bool allow_empty_field_names
TSRMLS_DC )
void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_type, _Out_ zval& fields, _In_ bool allow_empty_field_names )
{
void* field_value = NULL;
sqlsrv_phptype sqlsrv_php_type;
@ -1876,61 +1854,51 @@ void fetch_fields_common( _Inout_ ss_sqlsrv_stmt* stmt, _In_ zend_long fetch_typ
}
int zr = SUCCESS;
#if PHP_VERSION_ID < 70300
CHECK_ZEND_ERROR(array_init(&fields), stmt, SQLSRV_ERROR_ZEND_HASH) {
throw ss::SSException();
}
#else
array_init(&fields);
#endif
for( int i = 0; i < num_cols; ++i ) {
SQLLEN field_len = -1;
for( int i = 0; i < num_cols; ++i ) {
SQLLEN field_len = -1;
core_sqlsrv_get_field( stmt, i, sqlsrv_php_type, true /*prefer string*/,
field_value, &field_len, false /*cache_field*/, &sqlsrv_php_type_out TSRMLS_CC );
core_sqlsrv_get_field( stmt, i, sqlsrv_php_type, true /*prefer string*/,
field_value, &field_len, false /*cache_field*/, &sqlsrv_php_type_out );
zval field;
ZVAL_UNDEF( &field );
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, field );
sqlsrv_free( field_value );
if( fetch_type & SQLSRV_FETCH_NUMERIC ) {
zval field;
ZVAL_UNDEF( &field );
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, field );
sqlsrv_free( field_value );
if( fetch_type & SQLSRV_FETCH_NUMERIC ) {
zr = add_next_index_zval( &fields, &field );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
}
zr = add_next_index_zval( &fields, &field );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
}
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,
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY) {
throw ss::SSException();
}
CHECK_CUSTOM_WARNING_AS_ERROR(( stmt->fetch_field_names[i].len == 0 && !allow_empty_field_names ), stmt,
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY) {
throw ss::SSException();
}
if( stmt->fetch_field_names[i].len > 0 || allow_empty_field_names ) {
if( stmt->fetch_field_names[i].len > 0 || allow_empty_field_names ) {
zr = add_assoc_zval( &fields, stmt->fetch_field_names[i].name, &field );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
}
}
//only addref when the fetch_type is BOTH because this is the only case when fields(hashtable)
//has 2 elements pointing to field. Do not addref if the type is NUMERIC or ASSOC because
//fields now only has 1 element pointing to field and we want the ref count to be only 1
if (fetch_type == SQLSRV_FETCH_BOTH) {
Z_TRY_ADDREF(field);
}
} //for loop
add_assoc_zval(&fields, stmt->fetch_field_names[i].name, &field);
}
}
//only addref when the fetch_type is BOTH because this is the only case when fields(hashtable)
//has 2 elements pointing to field. Do not addref if the type is NUMERIC or ASSOC because
//fields now only has 1 element pointing to field and we want the ref count to be only 1
if (fetch_type == SQLSRV_FETCH_BOTH) {
Z_TRY_ADDREF(field);
}
} //for loop
}
void parse_param_array( _Inout_ ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction,
_Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC )
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits )
{
zval* var_or_val = NULL;
zval* temp = NULL;
@ -2165,7 +2133,7 @@ bool is_valid_sqlsrv_sqltype( _In_ sqlsrv_sqltype sql_type )
// verify an encoding given to type_and_encoding by looking through the list
// of standard encodings created at module initialization time
bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding TSRMLS_DC )
bool verify_and_set_encoding( _In_ const char* encoding_string, _Inout_ sqlsrv_phptype& phptype_encoding )
{
void* encoding_temp = NULL;
zend_ulong index = -1;
@ -2195,8 +2163,7 @@ void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type )
size_t size_len = 0;
int size = 0;
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &size_p, &size_len ) == FAILURE ) {
if( zend_parse_parameters( ZEND_NUM_ARGS(), "s", &size_p, &size_len ) == FAILURE ) {
return;
}
if (size_p) {
@ -2246,8 +2213,7 @@ void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, _In_ int type )
zend_long prec = SQLSRV_INVALID_PRECISION;
zend_long scale = SQLSRV_INVALID_SCALE;
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &prec, &scale ) == FAILURE ) {
if( zend_parse_parameters( ZEND_NUM_ARGS(), "|ll", &prec, &scale ) == FAILURE ) {
return;
}
@ -2290,12 +2256,11 @@ void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, _In_ int type )
sqlsrv_php_type.typeinfo.type = type;
sqlsrv_php_type.typeinfo.encoding = SQLSRV_ENCODING_INVALID;
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &encoding_param, &encoding_param_len ) == FAILURE ) {
if( zend_parse_parameters( ZEND_NUM_ARGS(), "s", &encoding_param, &encoding_param_len ) == FAILURE ) {
ZVAL_LONG( return_value, sqlsrv_php_type.value );
}
if( !verify_and_set_encoding( encoding_param, sqlsrv_php_type TSRMLS_CC )) {
if( !verify_and_set_encoding( encoding_param, sqlsrv_php_type )) {
LOG( SEV_ERROR, "Invalid encoding for php type." );
}

View file

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

View file

@ -5,7 +5,7 @@
//
// Comments: Mostly error handling and some type handling
//
// Microsoft Drivers 5.8 for PHP for SQL Server
// Microsoft Drivers 5.9 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -34,13 +34,13 @@ unsigned int current_log_subsystem = LOG_UTIL;
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,
_In_ bool warning TSRMLS_DC );
bool ignore_warning( _In_ char* sql_state, _In_ int native_code TSRMLS_DC );
_In_ bool warning );
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,
_In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args TSRMLS_DC );
_In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args );
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest TSRMLS_DC );
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z TSRMLS_DC );
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest );
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z );
}
@ -451,12 +451,12 @@ ss_error SS_ERRORS[] = {
};
// 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 TSRMLS_DC)
bool ss_severity_check(_In_ unsigned int severity)
{
return ((severity & SQLSRV_G(log_severity)) && (SQLSRV_G(current_subsystem) & SQLSRV_G(log_subsystems)));
}
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning TSRMLS_DC, _In_opt_ va_list* print_args )
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args )
{
logging_severity severity = SEV_ERROR;
if( warning && !SQLSRV_G( warnings_return_as_errors )) {
@ -464,7 +464,7 @@ bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_err
}
return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
print_args TSRMLS_CC );
print_args );
}
// sqlsrv_errors( [int $errorsAndOrWarnings] )
@ -512,29 +512,23 @@ PHP_FUNCTION( sqlsrv_errors )
LOG_FUNCTION( "sqlsrv_errors" );
if(( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) ||
if(( zend_parse_parameters( ZEND_NUM_ARGS(), "|l", &flags ) == FAILURE ) ||
( flags != SQLSRV_ERR_ALL && flags != SQLSRV_ERR_ERRORS && flags != SQLSRV_ERR_WARNINGS )) {
LOG( SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ );
RETURN_FALSE;
}
zval err_z;
ZVAL_UNDEF(&err_z);
#if PHP_VERSION_ID < 70300
if (array_init(&err_z) == FAILURE) {
RETURN_FALSE;
}
#else
array_init(&err_z);
#endif
if( flags == SQLSRV_ERR_ALL || flags == SQLSRV_ERR_ERRORS ) {
if( Z_TYPE( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( errors ) TSRMLS_CC )) {
if( Z_TYPE( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( errors ) )) {
zval_ptr_dtor(&err_z);
RETURN_FALSE;
}
}
if( flags == SQLSRV_ERR_ALL || flags == SQLSRV_ERR_WARNINGS ) {
if( Z_TYPE( SQLSRV_G( warnings )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( warnings ) TSRMLS_CC )) {
if( Z_TYPE( SQLSRV_G( warnings )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( warnings ) )) {
zval_ptr_dtor(&err_z);
RETURN_FALSE;
}
@ -574,7 +568,7 @@ PHP_FUNCTION( sqlsrv_configure )
RETVAL_FALSE;
reset_errors( TSRMLS_C );
reset_errors();
try {
@ -582,7 +576,7 @@ PHP_FUNCTION( sqlsrv_configure )
error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_);
int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "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_ ) {
throw ss::SSException();
@ -694,7 +688,7 @@ PHP_FUNCTION( sqlsrv_get_config )
LOG_FUNCTION( "sqlsrv_get_config" );
reset_errors( TSRMLS_C );
reset_errors();
try {
@ -702,7 +696,7 @@ PHP_FUNCTION( sqlsrv_get_config )
error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
error_ctx->set_func(_FN_);
int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "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_ ) {
throw ss::SSException();
@ -761,51 +755,37 @@ 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,
_In_ bool warning TSRMLS_DC )
_In_ bool warning )
{
#if PHP_VERSION_ID < 70300
if (array_init(error_z) == FAILURE) {
DIE( "Fatal error during error processing" );
}
#else
array_init(error_z);
#endif
// sqlstate
zval temp;
ZVAL_UNDEF(&temp);
core::sqlsrv_zval_stringl( &temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE );
//TODO: reference?
Z_TRY_ADDREF_P( &temp );
ZVAL_UNDEF(&temp);
core::sqlsrv_zval_stringl( &temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE );
Z_TRY_ADDREF_P( &temp );
if( add_next_index_zval( error_z, &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_zval( error_z, "SQLSTATE", &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
add_assoc_zval(error_z, "SQLSTATE", &temp);
// native_code
if( add_next_index_long( error_z, error->native_code ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_long( error_z, "code", error->native_code ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
add_assoc_long(error_z, "code", error->native_code);
// native_message
ZVAL_UNDEF(&temp);
ZVAL_UNDEF(&temp);
ZVAL_STRING( &temp, reinterpret_cast<char*>( error->native_message ) );
//TODO: reference?
Z_TRY_ADDREF_P(&temp);
Z_TRY_ADDREF_P(&temp);
if( add_next_index_zval( error_z, &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_zval( error_z, "message", &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
add_assoc_zval(error_z, "message", &temp);
// If it is an error or if warning_return_as_errors is true than
// add the error or warning to the reported_chain.
@ -813,7 +793,7 @@ void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error,
{
// if the warning is part of the ignored warning list than
// add to the ignored chain if the ignored chain is not null.
if( warning && ignore_warning( reinterpret_cast<char*>(error->sqlstate), error->native_code TSRMLS_CC ) &&
if( warning && ignore_warning( reinterpret_cast<char*>(error->sqlstate), error->native_code ) &&
ignored_chain != NULL ) {
if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) {
@ -841,7 +821,7 @@ void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error,
}
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_ bool warning, _In_opt_ va_list* print_args TSRMLS_DC )
_In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args )
{
bool result = true;
bool errors_ignored = false;
@ -856,13 +836,7 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
if( Z_TYPE_P( reported_chain ) == IS_NULL ) {
reported_chain_was_null = true;
#if PHP_VERSION_ID < 70300
if (array_init(reported_chain) == FAILURE) {
DIE( "Fatal error during error processing" );
}
#else
array_init(reported_chain);
#endif
}
else {
prev_reported_cnt = zend_hash_num_elements( Z_ARRVAL_P( reported_chain ));
@ -874,28 +848,22 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
if( Z_TYPE_P( ignored_chain ) == IS_NULL ) {
ignored_chain_was_null = true;
#if PHP_VERSION_ID < 70300
if (array_init(ignored_chain) == FAILURE) {
DIE( "Fatal error in handle_errors_and_warnings" );
}
#else
array_init( ignored_chain );
#endif
}
}
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, log_severity TSRMLS_CC, print_args );
copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning TSRMLS_CC );
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 );
}
SQLSMALLINT record_number = 0;
do {
result = core_sqlsrv_get_odbc_error( ctx, ++record_number, error, log_severity TSRMLS_CC );
result = core_sqlsrv_get_odbc_error( ctx, ++record_number, error, log_severity );
if( result ) {
copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning TSRMLS_CC );
copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning );
}
} while( result );
@ -933,7 +901,7 @@ bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* repo
// return whether or not a warning should be ignored or returned as an error if WarningsReturnAsErrors is true
// see RINIT in init.cpp for information about which errors are ignored.
bool ignore_warning( _In_ char* sql_state, _In_ int native_code TSRMLS_DC )
bool ignore_warning( _In_ char* sql_state, _In_ int native_code )
{
zend_ulong index = -1;
zend_string* key = NULL;
@ -954,7 +922,7 @@ bool ignore_warning( _In_ char* sql_state, _In_ int native_code TSRMLS_DC )
return false;
}
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest TSRMLS_DC )
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest )
{
zval_ptr_dtor( dest );
return ZEND_HASH_APPLY_REMOVE;
@ -962,7 +930,7 @@ int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest TSRMLS_DC )
// sqlsrv_merge_zend_hash
// merge a source hash into a dest hash table and return any errors.
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z TSRMLS_DC )
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z )
{
if( Z_TYPE_P( dest_z ) != IS_ARRAY && Z_TYPE_P( dest_z ) != IS_NULL ) DIE( "dest_z must be an array or null" );
if( Z_TYPE_P( src_z ) != IS_ARRAY && Z_TYPE_P( src_z ) != IS_NULL ) DIE( "src_z must be an array or null" );
@ -978,14 +946,14 @@ bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z TSRMLS_DC )
ZEND_HASH_FOREACH_KEY_VAL( src_ht, index, key, value_z ) {
if ( !value_z ) {
zend_hash_apply( Z_ARRVAL_P(dest_z), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
zend_hash_apply( Z_ARRVAL_P(dest_z), sqlsrv_merge_zend_hash_dtor );
return false;
}
int result = add_next_index_zval( dest_z, value_z );
if( result == FAILURE ) {
zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor );
return false;
}
Z_TRY_ADDREF_P( value_z );

View file

@ -1,7 +1,7 @@
--TEST--
a variable bound to a column in a result set
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
after a variable is bound, changing the value changes the value passed in the query
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
accesses an output parameter
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
uses an input/output parameter
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
after a value $contact is bound, changing the value does not change the value passed in the query
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
closes the cursor
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
returns the number of columns in a result set for 3 queries
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
displays a prepared statement
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,11 +1,12 @@
--TEST--
shows the error code of a SQL query with a mispelled table
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:server=$server; Database = $databaseName", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$stmt = $conn->prepare('SELECT * FROM Person.Addressx');
$stmt->execute();
@ -13,8 +14,8 @@ echo "Error Code: ";
print $stmt->errorCode();
// free the statement and connection
$stmt=null;
$conn=null;
unset($stmt);
unset($conn);
?>
--EXPECT--
Error Code: 42S02

View file

@ -1,24 +1,28 @@
--TEST--
reports the error info of a SQL statement with a mispelled table name
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:server=$server ; Database = $databaseName", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$stmt = $conn->prepare('SELECT * FROM Person.Addressx');
$stmt->execute();
print_r ($stmt->errorInfo());
// free the statement and connection
$stmt=null;
$conn=null;
unset($stmt);
unset($conn);
?>
--EXPECTREGEX--
Array
\(
\[0\] => 42S02
\[1\] => 208
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid object name 'Person.Addressx'.
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid object name 'Person.Addressx'\.
\[3\] => 42000
\[4\] => 8180
\[5\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Statement\(s\) could not be prepared\.
\)

View file

@ -1,7 +1,7 @@
--TEST--
Executes a statement
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
fetch with all fetch styles
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
fetches the rows in a result set in an array
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
fetches a column in a row
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
fetches the next row as an object
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
fetches the next row as an object of a user defined class
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
//create class of contactType

View file

@ -1,7 +1,7 @@
--TEST--
retrieves metadata for a column
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
moves the cursor to the next result set and fetches results
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
returns the number of rows added to a table; returns the number of rows in a result set when you specify a scrollable cursor
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
sets the query timeout attribute
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
specifies the fetch mode before fetching
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
starts a transaction, insert 2 rows and commit the transaction
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
connect to a server and specify a database
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
connect to a server, specifying the database later
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
connect to a server, setting MARS to false
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,18 +1,19 @@
--TEST--
reports the error code of querying a misspelled column
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:Server=$server ; Database = $databaseName ", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$query = "SELECT * FROM Person.Address where Cityx = 'Essen'";
$conn->query($query);
print $conn->errorCode();
//free the connection
$conn=null;
unset($conn);
?>
--EXPECT--
42S22

View file

@ -1,11 +1,12 @@
--TEST--
reports the error info of querying a misspelled column
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:Server=$server ; Database = $databaseName ", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$query = "SELECT * FROM Person.Address where Cityx = 'Essen'";
$conn->query($query);
@ -23,4 +24,7 @@ Array
\[0\] => 42S22
\[1\] => 207
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid column name 'Cityx'.
\[3\] => 42000
\[4\] => 8180
\[5\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Statement\(s\) could not be prepared\.
\)

View file

@ -1,7 +1,7 @@
--TEST--
execute a delete and reports how many rows were deleted
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,39 +1,30 @@
--TEST--
shows the PDO::ATR_ERRMODE attribute, before and after changing its value
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:Server=$server ; Database = $databaseName", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$attributes1 = array( "ERRMODE" );
foreach ( $attributes1 as $val ) {
echo "PDO::ATTR_$val: ";
var_dump ($conn->getAttribute( constant( "PDO::ATTR_$val" ) ));
echo "PDO::ATTR_$val: ";
var_dump ($conn->getAttribute( constant( "PDO::ATTR_$val" ) ));
}
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$attributes1 = array( "ERRMODE" );
foreach ( $attributes1 as $val ) {
echo "PDO::ATTR_$val: ";
var_dump ($conn->getAttribute( constant( "PDO::ATTR_$val" ) ));
echo "PDO::ATTR_$val: ";
var_dump ($conn->getAttribute( constant( "PDO::ATTR_$val" ) ));
}
// An example using PDO::ATTR_CLIENT_VERSION
print_r($conn->getAttribute( PDO::ATTR_CLIENT_VERSION ));
//free the connection
$conn=null;
unset($conn);
?>
--EXPECTREGEX--
PDO::ATTR_ERRMODE: int\(0\)
PDO::ATTR_ERRMODE: int\(2\)
Array
\(
\[DriverDllName\]|\[DriverName\] => (msodbcsql[0-9]{2}\.dll|(libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib))
\[DriverODBCVer\] => [0-9]{1,2}\.[0-9]{1,2}
\[DriverVer\] => [0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}
\[ExtensionVer\] => [0-9].[0-9]\.[0-9](-(RC[0-9]?|preview))?(\.[0-9]+)?(\+[0-9]+)?
\)

View file

@ -1,7 +1,7 @@
--TEST--
check if sqlsrv is in the array of available PDO drivers
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
$drivers = PDO::getAvailableDrivers();

View file

@ -1,7 +1,7 @@
--TEST--
prepares a statement with parameter markers and forward-only (server-side) cursor
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
prepares a statement with a client-side cursor
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
prepares a statement with a client-side cursor and specifies scroll type to buffered
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
default query; query for a column; query with a new class; query into an existing class
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
insert with quoted parameters
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php

View file

@ -1,11 +1,12 @@
--TEST--
sets to PDO::ATTR_ERRMODE
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO( "sqlsrv:server=$server ; Database = $databaseName", "$uid", "$pwd");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$attributes1 = array( "ERRMODE" );
foreach ( $attributes1 as $val ) {
@ -22,7 +23,7 @@ sets to PDO::ATTR_ERRMODE
}
//free the connection
$conn=null;
unset($conn);
?>
--EXPECT--
PDO::ATTR_ERRMODE: int(0)

View file

@ -1,42 +1,45 @@
--TEST--
sets to PDO::SQLSRV_ATTR_DIRECT_QUERY
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');
$conn = new PDO("sqlsrv:Server=$server", "$uid", "$pwd");
$conn->setAttribute(constant('PDO::SQLSRV_ATTR_DIRECT_QUERY'), true);
require('connect.inc');
$conn = new PDO("sqlsrv:Server=$server", "$uid", "$pwd");
$conn->setAttribute(constant('PDO::SQLSRV_ATTR_DIRECT_QUERY'), true);
$stmt1 = $conn->query("DROP TABLE #php_test_table");
$tableName = 'pdo_direct_query';
$tsql = "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" . $tableName . "') AND type in (N'U')) DROP TABLE $tableName";
$stmt2 = $conn->query("CREATE TABLE #php_test_table ([c1_int] int, [c2_int] int)");
$stmt1 = $conn->query($tsql);
$stmt2 = $conn->query("CREATE TABLE $tableName ([c1_int] int, [c2_int] int)");
$v1 = 1;
$v2 = 2;
$v1 = 1;
$v2 = 2;
$stmt3 = $conn->prepare("INSERT INTO #php_test_table (c1_int, c2_int) VALUES (:var1, :var2)");
$stmt3 = $conn->prepare("INSERT INTO $tableName (c1_int, c2_int) VALUES (:var1, :var2)");
if ($stmt3) {
if ($stmt3) {
$stmt3->bindValue(1, $v1);
$stmt3->bindValue(2, $v2);
if ($stmt3->execute())
if ($stmt3->execute()) {
echo "Execution succeeded\n";
else
} else {
echo "Execution failed\n";
}
else
}
} else {
var_dump($conn->errorInfo());
}
$stmt4 = $conn->query("DROP TABLE #php_test_table");
$stmt4 = $conn->query("DROP TABLE $tableName");
// free the statements and connection
$stmt1=null;
$stmt2=null;
$stmt3=null;
$stmt4=null;
$conn=null;
?>
// free the statements and connection
unset($stmt1);
unset($stmt2);
unset($stmt3);
unset($stmt4);
unset($conn);
?>
--EXPECT--
Execution succeeded

View file

@ -1,7 +1,7 @@
--TEST--
call a stored procedure and retrieve the errorNumber that is returned
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
call a stored procedure and retrieve the errorNumber that is returned
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
call a stored procedure and retrieve the errorString that is returned
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -0,0 +1,4 @@
<?php
if (!extension_loaded("pdo_sqlsrv")) {
die("skip Extension not loaded");
}

View file

@ -1,7 +1,7 @@
--TEST--
executes two queries as part of a transaction
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
delete in a transaction
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
/* Connect to the local server using Windows Authentication and

View file

@ -1,7 +1,7 @@
--TEST--
executes a query, then comsumes and counts results until reaches a specified amount. The remaining query results are then discarded.
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,7 +1,7 @@
--TEST--
cancels a statement then reuse.
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

View file

@ -1,35 +0,0 @@
--TEST--
client information.
--SKIPIF--
--FILE--
<?php
require('connect.inc');
$connectionInfo = array( "Database"=>"$databaseName", "UID"=>"$uid", "PWD"=>"$pwd");
$conn = sqlsrv_connect( $server, $connectionInfo);
if( $conn === false )
{
echo "Could not connect.\n";
die( print_r( sqlsrv_errors(), true));
}
if( $client_info = sqlsrv_client_info( $conn))
{
foreach( $client_info as $key => $value)
{
echo $key.": ".$value."\n";
}
}
else
{
echo "Client info error.\n";
}
/* Close connection resources. */
sqlsrv_close( $conn);
?>
--EXPECTREGEX--
DriverDllName|DriverName: (msodbcsql[0-9]{2}\.dll|(libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]|libmsodbcsql.[0-9]{2}.dylib))
DriverODBCVer: [0-9]{1,2}\.[0-9]{1,2}
DriverVer: [0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}
ExtensionVer: [0-9].[0-9]\.[0-9](-(RC[0-9]?|preview))?(\.[0-9]+)?(\+[0-9]+)?

View file

@ -1,7 +1,7 @@
--TEST--
closes a connection.
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require('connect.inc');

Some files were not shown because too many files have changed in this diff Show more