Merge pull request #731 from Microsoft/dev

Merge master with dev
This commit is contained in:
Yuki Wong 2018-03-29 13:53:19 -07:00 committed by GitHub
commit ffd8e2113e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 10530 additions and 3191 deletions

View file

@ -1,58 +1,52 @@
sudo: required
os: linux
dist: trusty
group: edge
services:
- docker
env:
global:
- REPORT_EXIT_STATUS=1
- ACCEPT_EULA=Y
- PHPSQLDIR=/REPO/msphpsql-dev
- TEST_PHP_SQL_SERVER=sql
- SQLSRV_DBNAME=msphpsql_sqlsrv
- PDOSQLSRV_DBNAME=msphpsql_pdosqlsrv
- TEST_PHP_SQL_UID=sa
- TEST_PHP_SQL_PWD=Password123
before_install:
- docker pull microsoft/mssql-server-linux:2017-latest
install:
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password123' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d microsoft/mssql-server-linux:2017-latest
- docker build --build-arg PHPSQLDIR=$PHPSQLDIR -t msphpsql-dev -f Dockerfile-msphpsql .
before_script:
- sleep 30
script:
- travis_retry docker run -e TRAVIS_JOB_ID -t -d -w $PHPSQLDIR --name=client --link $TEST_PHP_SQL_SERVER msphpsql-dev
- docker ps -a
- docker logs client
- travis_retry docker exec client python ./test/functional/setup/setup_dbs.py -dbname $SQLSRV_DBNAME
- travis_retry docker exec client python ./test/functional/setup/setup_dbs.py -dbname $PDOSQLSRV_DBNAME
- docker exec client cp ./source/shared/msodbcsql.h ./test/functional/setup/
- travis_retry docker exec client python ./test/functional/setup/build_ksp.py
- docker exec client cp ./test/functional/setup/myKSP.so ./test/functional/sqlsrv/
- docker exec client cp ./test/functional/setup/myKSP.so ./test/functional/pdo_sqlsrv/
- travis_retry docker exec client python ./test/functional/setup/run_ksp.py -server $TEST_PHP_SQL_SERVER -dbname $SQLSRV_DBNAME -uid $TEST_PHP_SQL_UID -pwd $TEST_PHP_SQL_PWD
- travis_retry docker exec client python ./test/functional/setup/run_ksp.py -server $TEST_PHP_SQL_SERVER -dbname $PDOSQLSRV_DBNAME -uid $TEST_PHP_SQL_UID -pwd $TEST_PHP_SQL_PWD
- travis_retry docker exec client php ./source/pdo_sqlsrv/run-tests.php ./test/functional/pdo_sqlsrv/*.phpt
- travis_retry docker exec client php ./source/sqlsrv/run-tests.php ./test/functional/sqlsrv/*.phpt
- docker exec client bash -c 'for f in ./test/functional/sqlsrv/*.diff; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/sqlsrv/*.out; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/pdo_sqlsrv/*.diff; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/pdo_sqlsrv/*.out; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $SQLSRV_DBNAME
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $PDOSQLSRV_DBNAME
- docker exec client coveralls -e ./source/shared/ --gcov-options '\-lp'
- docker stop client
- docker ps -a
notifications:
email: false
sudo: required
os: linux
dist: trusty
group: edge
services:
- docker
env:
global:
- REPORT_EXIT_STATUS=1
- ACCEPT_EULA=Y
- PHPSQLDIR=/REPO/msphpsql-dev
- TEST_PHP_SQL_SERVER=sql
- SQLSRV_DBNAME=msphpsql_sqlsrv
- PDOSQLSRV_DBNAME=msphpsql_pdosqlsrv
- TEST_PHP_SQL_UID=sa
- TEST_PHP_SQL_PWD=Password123
before_install:
- docker pull microsoft/mssql-server-linux:2017-latest
install:
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password123' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d microsoft/mssql-server-linux:2017-latest
- docker build --build-arg PHPSQLDIR=$PHPSQLDIR -t msphpsql-dev -f Dockerfile-msphpsql .
before_script:
- sleep 30
script:
- travis_retry docker run -e TRAVIS_JOB_ID -t -d -w $PHPSQLDIR --name=client --link $TEST_PHP_SQL_SERVER msphpsql-dev
- docker ps -a
- docker logs client
- travis_retry docker exec client python ./test/functional/setup/setup_dbs.py -dbname $SQLSRV_DBNAME
- travis_retry docker exec client python ./test/functional/setup/setup_dbs.py -dbname $PDOSQLSRV_DBNAME
- travis_retry docker exec client php ./source/pdo_sqlsrv/run-tests.php ./test/functional/pdo_sqlsrv/*.phpt
- travis_retry docker exec client php ./source/sqlsrv/run-tests.php ./test/functional/sqlsrv/*.phpt
- docker exec client bash -c 'for f in ./test/functional/sqlsrv/*.diff; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/sqlsrv/*.out; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/pdo_sqlsrv/*.diff; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client bash -c 'for f in ./test/functional/pdo_sqlsrv/*.out; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $SQLSRV_DBNAME
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $PDOSQLSRV_DBNAME
- docker exec client coveralls -e ./source/shared/ --gcov-options '\-lp'
- docker stop client
- docker ps -a
notifications:
email: false

View file

@ -3,6 +3,61 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
## Windows/Linux/macOS 5.2.0 - 2017-02-14
Updated PECL release packages. Here is the list of updates:
### Added
- Added support for Always Encrypted with basic CRUD functionalities (see [here](https://github.com/Microsoft/msphpsql/wiki/Features#aebindparam))
- Support for Windows Certificate Store (use connection keyword ColumnEncryption)
- Support for inserting into and modifying an encrypted column
- Support for fetching from an encrypted column
- Added support for PHP 7.2
- Added support for MS ODBC Driver 17
- Added support for Ubuntu 17 (requires MS ODBC Driver 17)
- Added support for Debian 9 (requires MS ODBC Driver 17)
- Added support for SUSE 12
- Added Driver option to set the MS ODBC driver, Added "Driver" option, valid values are "ODBC Driver 17 for SQL Server", "ODBC Driver 13 for SQL Server", and "ODBC Driver 11 for SQL Server"
- The default driver is ODBC Driver 17 for SQL Server
### Changed
- Implementation of PDO::lastInsertId($name) to return the last inserted sequence number if the sequence name is supplied to the function ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid))
### Fixed
- Issue [#555](https://github.com/Microsoft/msphpsql/issues/555) - Hebrew strings truncation (requires MS ODBC Driver 17)
- Adjusted precisions for numeric/decimal inputs with Always Encrypted
- Support for non-UTF8 locales in Linux and macOS
- Fixed crash caused by executing an invalid query in a transaction (Issue [#434](https://github.com/Microsoft/msphpsql/issues/434))
- Added error handling for using PDO::SQLSRV_ATTR_DIRECT_QUERY or PDO::ATTR_EMULATE_PREPARES in a Column Encryption enabled connection
- Added error handling for binding TEXT, NTEXT or IMAGE as output parameter (Issue [#231](https://github.com/Microsoft/msphpsql/issues/231))
- PDO::quote with string containing ASCII NUL character (Issue [#538]( https://github.com/Microsoft/msphpsql/issues/538))
- Decimal types with no decimals are correctly handled when AE is enabled (PR [#544](https://github.com/Microsoft/msphpsql/pull/544))
- BIGINT as an output param no longer results in value out of range exception when the returned value is larger than a maximum integer ([PR #567](https://github.com/Microsoft/msphpsql/pull/567))
### Removed
- Dropped support for Ubuntu 15
- Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid))
### Limitations
- Always Encrypted is not supported in Linux and macOS
- In Linux and macOS, setlocale() only takes effect if it is invoked before the first connection. Attempting to set the locale after connection will not work
- Always Encrypted functionalities are only supported using MS ODBC Driver 17
- [Always Encrypted limitations](https://github.com/Microsoft/msphpsql/wiki/Features#aelimitation)
- When using sqlsrv_query with Always Encrypted feature, SQL type has to be specified for each input (see [here](https://github.com/Microsoft/msphpsql/wiki/Features#aebindparam))
- No support for inout / output params when using sql_variant type
### Known Issues
- Connection pooling on Linux doesn't work properly when using MS ODBC Driver 17
- When pooling is enabled in Linux or macOS
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostics 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/Connection-Pooling-on-Linux-and-Mac)
- Connection with Connection Resiliency enabled does not resume properly with Connection Pooling (Issue [#678](https://github.com/Microsoft/msphpsql/issues/678))
- With ColumnEncryption enabled, calling stored procedure with XML parameter does not work (Issue [#674](https://github.com/Microsoft/msphpsql/issues/674))
- Cannot connect with both Connection Resiliency enabled and ColumnEncryption enabled (Issue [#577](https://github.com/Microsoft/msphpsql/issues/577))
- With ColumnEncryption enabled, retrieving a negative decimal value as output parameter causes truncation of the last digit (Issue [#705](https://github.com/Microsoft/msphpsql/issues/705))
- With ColumnEncryption enabled, cannot insert a double into a decimal column with precision and scale of (38, 38) (Issue [#706](https://github.com/Microsoft/msphpsql/issues/706))
- With ColumnEncryption enabled, when fetching decimals as output parameters bound to PDO::PARAM_BOOL or PDO::PARAM_INT, floats are returned, not integers (Issue [#707](https://github.com/Microsoft/msphpsql/issues/707))
## Windows/Linux/macOS 5.2.0-RC - 2017-12-20
Updated PECL release packages. Here is the list of updates:

View file

@ -37,6 +37,13 @@ RUN locale-gen en_US
RUN locale-gen en_US.UTF-8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
#install ODBC driver
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y apt-get install -y msodbcsql17 mssql-tools
ENV PATH="/opt/mssql-tools/bin:${PATH}"
#install coveralls
RUN pip install --upgrade pip && pip install cpp-coveralls
@ -45,18 +52,12 @@ RUN pip install --upgrade pip && pip install cpp-coveralls
#another option is to copy source to build directory on image
RUN mkdir -p $PHPSQLDIR
COPY . $PHPSQLDIR
#install ODBC 17 preview driver
WORKDIR $PHPSQLDIR
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y dpkg -i "./ODBC 17 binaries preview/Ubuntu 16/msodbcsql_17.0.0.5-1_amd64.deb"
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y dpkg -i "./ODBC 17 binaries preview/Ubuntu 16/mssql-tools_17.0.0.5-1_amd64.deb"
ENV PATH="/opt/mssql-tools/bin:${PATH}"
WORKDIR $PHPSQLDIR/source/
RUN chmod +x ./packagize.sh
RUN /bin/bash -c "./packagize.sh"
RUN echo "extension = pdo_sqlsrv.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
RUN echo "extension = pdo_sqlsrv.so" >> /etc/php/7.0/cli/conf.d/20-pdo_sqlsrv.ini
RUN echo "extension = sqlsrv.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
WORKDIR $PHPSQLDIR/source/sqlsrv

305
Linux-mac-install.md Normal file
View file

@ -0,0 +1,305 @@
# PHP Linux and Mac Drivers Installation Tutorial
The following instructions assume a clean environment and show how to install PHP 7.x, the Microsoft ODBC driver, Apache, and the Microsoft drivers for PHP for Microsoft SQL Server on Ubuntu 16.04 and 17.10, RedHat 7, Debian 8 and 9, Suse 12, and macOS 10.11 and 10.12. These instructions advise installing the drivers using PECL, but you can also download the prebuilt binaries from the [Microsoft Drivers for PHP for Microsoft 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 Microsoft SQL Server](../../connect/php/loading-the-php-sql-driver.md). For an explanation of extension loading and why we do not add the extensions to php.ini, see the section on [loading the drivers](../../connect/php/loading-the-php-sql-driver.md##loading-the-driver-at-php-startup).
These instruction install PHP 7.2 by default -- see the notes at the beginning of each section to install PHP 7.0 or 7.1.
## Contents of this page:
- [Installing the drivers on Ubuntu 16.04 and 17.10](#installing-the-drivers-on-ubuntu-1604-and-1710)
- [Installing the drivers on Red Hat 7](#installing-the-drivers-on-red-hat-7)
- [Installing the drivers on Debian 8 and 9](#installing-the-drivers-on-debian-8-and-9)
- [Installing the drivers on Suse 12](#installing-the-drivers-on-suse-12)
- [Installing the drivers on macOS El Capitan and Sierra](#installing-the-drivers-on-macos-el-capitan-and-sierra)
## Installing the drivers on Ubuntu 16.04 and 17.10
> [!NOTE]
> To install PHP 7.0 or 7.1, replace 7.2 with 7.0 or 7.1 in the following commands.
### Step 1. Install PHP
```
sudo su
add-apt-repository ppa:ondrej/php -y
apt-get update
apt-get install php7.2 php7.2-dev php7.2-xml -y --allow-unauthenticated
```
### Step 2. Install prerequisites
Install the ODBC driver for Ubuntu by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/30-pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/20-sqlsrv.ini
exit
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
```
### Step 4. Install Apache and configure driver loading
```
sudo su
apt-get install libapache2-mod-php7.2 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.2
echo "extension=sqlsrv.so" >> /etc/php/7.2/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php/7.2/apache2/php.ini
```
### Step 5. Restart Apache and test the sample script
```
sudo service apache2 restart
```
To test your installation, see [Testing your installation](#testing-your-installation) at the end of this document.
## Installing the drivers on Red Hat 7
> [!NOTE]
> To install PHP 7.0 or 7.1, replace remi-php72 with remi-php70 or remi-php71 respectively in the following commands.
### Step 1. Install PHP
```
sudo su
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
subscription-manager repos --enable=rhel-7-server-optional-rpms
yum-config-manager --enable remi-php72
yum update
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
```
### Step 2. Install prerequisites
Install the ODBC driver for Red Hat 7 by following the instructions on the [Linux and macOS installation page](../../connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server.md).
Compiling the PHP drivers with PECL with PHP 7.2 requires a more recent GCC than the default:
```
sudo yum-config-manager --enable rhel-server-rhscl-7-rpms
sudo yum install devtoolset-7
scl enable devtoolset-7 bash
```
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/30-pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/20-sqlsrv.ini
exit
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
```
An issue in PECL may prevent correct installation of the latest version of the drivers even if you have upgraded GCC. To install, download the packages and compile manually:
```
pecl download sqlsrv
tar xvzf sqlsrv-5.2.0.tgz
cd sqlsrv-5.2.0/
phpize
./configure --with-php-config=/usr/bin/php-config
make
sudo make install
```
You can alternatively download the prebuilt binaries from the [Github project page](https://github.com/Microsoft/msphpsql/releases), or install from the Remi repo:
```
sudo yum install php-sqlsrv php-pdo_sqlsrv
```
### Step 4. Install Apache
```
sudo yum install httpd
```
SELinux is installed by default and runs in Enforcing mode. To allow Apache to connect to databases through SELinux, run the following command:
```
sudo setsebool -P httpd_can_network_connect_db 1
```
### Step 5. Restart Apache and test the sample script
```
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 and 9
> [!NOTE]
> To install PHP 7.0 or 7.1, replace 7.2 in the following commands with 7.0 or 7.1.
### Step 1. Install PHP
```
sudo su
apt-get install curl apt-transport-https
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
apt-get update
apt-get install y php7.2 php7.2-dev php7.2-xml
```
### Step 2. Install prerequisites
Install the ODBC driver for Debian by following the instructions on the [Linux and macOS installation page](../../connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server.md).
You may also need to generate the correct locale to get PHP output to display correctly in a browser. For example, for the en_US UTF-8 locale, run the following commands:
```
sudo su
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
locale-gen
```
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/30-pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/20-sqlsrv.ini
exit
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
```
### Step 4. Install Apache and configure driver loading
```
sudo su
apt-get install libapache2-mod-php7.2 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.2
echo "extension=sqlsrv.so" >> /etc/php/7.2/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php/7.2/apache2/php.ini
```
### Step 5. Restart Apache and test the sample script
```
sudo service apache2 restart
```
To test your installation, see [Testing your installation](#testing-your-installation) at the end of this document.
## Installing the drivers on Suse 12
> [!NOTE]
> To install PHP 7.0, skip the command below adding the repository - 7.0 is the default PHP on suse 12.
> To install PHP 7.1, replace the repository URL below with the following URL:
`http://download.opensuse.org/repositories/devel:/languages:/php:/php71/SLE_12/devel:languages:php:php71.repo`
### Step 1. Install PHP
```
sudo su
zypper -n ar -f http://download.opensuse.org/repositories/devel:languages:php/SLE_12/devel:languages:php.repo
zypper --gpg-auto-import-keys refresh
zypper -n install php7 php7-pear php7-devel
```
### Step 2. Install prerequisites
Install the ODBC driver for Suse 12 by following the instructions on the [Linux and macOS installation page](../../connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server.md).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/sqlsrv.ini
exit
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
```
### Step 4. Install Apache and configure driver loading
```
sudo su
zypper install apache2 apache2-mod_php7
a2enmod php7
echo "extension=sqlsrv.so" >> /etc/php7/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php7/apache2/php.ini
```
### Step 5. Restart Apache and test the sample script
```
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 macOS El Capitan and Sierra
If you do not already have it, install brew as follows:
```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```
> [!NOTE]
> To install PHP 7.0 or 7.1, replace php72 with php70 or php71 respectively in the following commands.
### Step 1. Install PHP
```
brew tap
brew tap homebrew/dupes
brew tap homebrew/versions
brew tap homebrew/homebrew-php
brew install php72 --with-pear --with-httpd24 --with-cgi
echo 'export PATH="/usr/local/sbin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
```
### Step 2. Install prerequisites
Install the ODBC driver for macOS by following the instructions on the [Linux and macOS installation page](../../connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server.md).
In addition, you may need to install the GNU make tools:
```
brew install autoconf automake libtool
```
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
chmod -R ug+w /usr/local/opt/php72/lib/php
pear config-set php_ini /usr/local/etc/php/7.2/php.ini system
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
```
### Step 4. Install Apache and configure driver loading
```
(echo "<FilesMatch .php$>"; echo "SetHandler application/x-httpd-php"; echo "</FilesMatch>";) >> /usr/local/etc/httpd/httpd.conf
```
### Step 5. Restart Apache and test the sample script
```
sudo apachectl restart
```
To test your installation, see [Testing your installation](#testing-your-installation) at the end of this document.
## Testing Your Installation
To test this sample script, create a file called testsql.php in your system's document root. This is `/var/www/html/` on Ubuntu, Debian, and Redhat, `/srv/www/htdocs` on SUSE, or `/usr/local/var/www` on macOS. Copy the following script to it, replacing the server, database, username, and password as appropriate.
```
<?php
$serverName = "yourServername";
$connectionOptions = array(
"Database" => "yourDatabase",
"Uid" => "yourUsername",
"PWD" => "yourPassword"
);
//Establishes the connection
$conn = sqlsrv_connect($serverName, $connectionOptions);
if( $conn === false ) {
die( FormatErrors( sqlsrv_errors()));
}
//Select Query
$tsql= "SELECT @@Version as SQL_VERSION";
//Executes the query
$getResults= sqlsrv_query($conn, $tsql);
//Error handling
if ($getResults == FALSE)
die(FormatErrors(sqlsrv_errors()));
?>
<h1> Results : </h1>
<?php
while ($row = sqlsrv_fetch_array($getResults, SQLSRV_FETCH_ASSOC)) {
echo ($row['SQL_VERSION']);
echo ("<br/>");
}
sqlsrv_free_stmt($getResults);
function FormatErrors( $errors )
{
/* Display errors. */
echo "Error information: <br/>";
foreach ( $errors as $error )
{
echo "SQLSTATE: ".$error['SQLSTATE']."<br/>";
echo "Code: ".$error['code']."<br/>";
echo "Message: ".$error['message']."<br/>";
}
}
?>
```
Point your browser to http://localhost/testsql.php (http://localhost:8080/testsql.php on macOS). You should now be able to connect to your SQL Server/Azure SQL database.

View file

@ -1,6 +0,0 @@
The ODBC driver 17 preview binaries in this directory are required in order to use Always Encrypted functionality. Please note that these drivers should be considered to be preview versions -- they should not be used in production and are not supported by Microsoft. They will be replaced upon the official release of ODBC driver 17.
On Windows, the ODBC 17 preview binaries require the Visual C/C++ 2013 runtime libraries installed separately. These are installed with the [Visual Studio C++ 2013 Redistributable](https://www.microsoft.com/en-ca/download/details.aspx?id=40784) or with the [SQL Server command line utilities](https://www.microsoft.com/en-ca/download/details.aspx?id=53591). Once you have these, simply run the msi to install.
For instructions on installing the binaries on Linux platforms, please see [this page](https://github.com/Microsoft/msphpsql/wiki/Install-and-configuration#odbc-17-linux-installation).

457
README.md
View file

@ -1,10 +1,10 @@
# Microsoft Drivers for PHP for SQL Server
# Microsoft Drivers for PHP for Microsoft SQL Server
**Welcome to the Microsoft Drivers for PHP for SQL Server PHP 7**
**Welcome to the Microsoft Drivers for PHP for Microsoft SQL Server**
The Microsoft Drivers for PHP for SQL Server are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PDO for accessing data in all editions of SQL Server 2008 R2 and later (including Azure SQL DB). These drivers rely on the Microsoft ODBC Driver for SQL Server to handle the low-level communication with SQL Server.
The Microsoft Drivers for PHP for Microsoft SQL Server are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PHP Data Objects (PDO) for accessing data in all editions of SQL Server 2008 R2 and later (including Azure SQL DB). These drivers rely on the Microsoft ODBC Driver for SQL Server to handle the low-level communication with SQL Server.
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7.0.* or above with improvements on both drivers and some limitations (see Limitations below for details). Upcoming release(s) will contain more functionality, bug fixes, and more (see Plans below for more details).
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7.* with improvements on both drivers and some limitations (see Limitations below for details). Upcoming releases will contain additional functionality, bug fixes, and more (see Plans below for more details).
SQL Server Team
@ -31,10 +31,10 @@ Thank you for taking the time to participate in our last survey. You can continu
## Get Started
* [**Windows + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/windows)
* [**Ubuntu + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/ubuntu)
* [**RedHat + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/rhel)
* [**SUSE + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/sles)
* [**Windows + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/windows)
* [**macOS + SQL Server + PHP 7**](https://www.microsoft.com/en-us/sql-server/developer-get-started/php/mac/)
* [**Docker**](https://hub.docker.com/r/lbosqmsft/mssql-php-msphpsql/)
@ -43,441 +43,66 @@ Thank you for taking the time to participate in our last survey. You can continu
Please visit the [blog][blog] for more announcements.
## Prerequisites
## Build (Windows)
Note: if you prefer, you can use the pre-compiled binaries found in the [releases](https://github.com/Microsoft/msphpsql/releases)
#### Prerequisites
You must first be able to build PHP 7.0.* or above without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP in Windows.
#### Compile the drivers
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.0* using Visual C++ 2017 v15.0.
For details, please read the documentation and/or take a look at the sample [build scripts](https://github.com/Microsoft/msphpsql/tree/dev/buildscripts#windows).
## Install (Windows)
#### Prerequisites
For full details on the system requirements for the drivers, see the [system requirements](https://docs.microsoft.com/en-us/sql/connect/php/system-requirements-for-the-php-sql-driver) on MSDN.
On the client machine:
- PHP 7.0.x, 7.1.x, or 7.2.x (7.2.0 and up on Unix, 7.2.1 and up on Windows)
- A Web server such as Internet Information Services (IIS) is required. Your Web server must be configured to run PHP
- [Microsoft ODBC Driver 11][odbc11], [Microsoft ODBC Driver 13][odbc13] or [Microsoft ODBC Driver 17][odbc17]
- [Microsoft ODBC Driver 11][odbc11], [Microsoft ODBC Driver 13][odbc13], or [Microsoft ODBC Driver 17][odbc17]
#### Enable the drivers
On the server side, Microsoft SQL Server 2008 R2 and above on Windows is supported, as is Microsoft SQL Server 2016 and above on Linux.
1. Make sure that the driver is in your PHP extension directory (you can simply copy it there if you did not use nmake install).
## Building and Installing the Drivers on Windows
2. Enable it within your PHP installation's php.ini: `extension=php_sqlsrv.dll` and/or `extension=php_pdo_sqlsrv.dll`. If necessary, specify the extension directory using extension_dir, for example: `extension_dir = "C:\PHP\ext"`. Note that the precompiled binaries have different names -- substitute accordingly in php.ini.
The drivers are distributed as pre-compiled extensions for PHP found on the [releases page](https://github.com/Microsoft/msphpsql/releases). They are available in thread-safe and non thread-safe versions, and in 32-bit and 64-bit versions. The source code for the drivers is also available, and you can compile them as thread safe or non-thread safe versions. The thread safety configuration of your web server will determine which version you need.
If you choose to build the drivers, you must be able to build PHP 7 without including these extensions. For help building PHP on Windows, see the [official PHP website][phpbuild]. For details on compiling the drivers, see the [documentation](https://github.com/Microsoft/msphpsql/tree/dev/buildscripts#windows) -- an example buildscript is provided, but you can also compile the drivers manually.
3. Restart the Web server.
To load the drivers, make sure that the driver is in your PHP extension directory and enable it in your PHP installation's php.ini file by adding `extension=php_sqlsrv.dll` and/or `extension=php_pdo_sqlsrv.dll` to it. If necessary, specify the extension directory using `extension_dir`, for example: `extension_dir = "C:\PHP\ext"`. Note that the precompiled binaries have different names -- substitute accordingly in php.ini. For more details on loading the drivers, see [Loading the PHP SQL Driver](https://docs.microsoft.com/en-us/sql/connect/php/loading-the-php-sql-driver) on MSDN.
Finally, restart the Web server.
## Install (UNIX)
The following instructions assume a clean environment and show how to install PHP 7.x, Microsoft ODBC driver, Apache, and Microsoft PHP drivers on Ubuntu 16, 17 RedHat 7, Debian 8, 9 SUSE 12, and macOS 10.11, 10.12.
Note that [Microsoft ODBC Driver 17][odbc17] is required for Ubuntu 17 and Debian 9.
### Step 1: Install PHP7+
#### PHP 7.0
**Ubuntu 16.04, 17.10**
sudo su
apt-get update
apt-get -y install php7.0 mcrypt php7.0-mcrypt php-mbstring php-pear php7.0-dev php7.0-xml
**RedHat 7**
sudo su
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
subscription-manager repos --enable=rhel-7-server-optional-rpms
yum-config-manager --enable remi-php70
yum update
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
**Debian 8**
sudo su
apt-get install curl apt-transport-https
curl https://www.dotdeb.org/dotdeb.gpg | apt-key add -
echo "deb http://packages.dotdeb.org jessie all" >> /etc/apt/sources.list
echo "deb-src http://packages.dotdeb.org jessie all" >> /etc/apt/sources.list
apt-get update
apt-get install -y php7.0 php-pear php7.0-dev php7.0-xml
**Debian 9**
sudo su
apt-get install curl apt-transport-https
curl https://www.dotdeb.org/dotdeb.gpg | apt-key add -
echo "deb http://packages.dotdeb.org stretch all" >> /etc/apt/sources.list
echo "deb-src http://packages.dotdeb.org stretch all" >> /etc/apt/sources.list
apt-get update
apt-get install -y php7.0 php-pear php7.0-dev php7.0-xml
**SUSE 12**
sudo su
zypper refresh
zypper install -y php7 php7-pear php7-devel
**macOS 10.11, 10.12**
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew tap
brew tap homebrew/dupes
brew tap homebrew/versions
brew tap homebrew/homebrew-php
brew install php70 --with-pear --with-httpd24 --with-cgi
echo 'export PATH="/usr/local/sbin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
#### PHP 7.1
**Ubuntu 16.04, 17.10**
sudo su
add-apt-repository ppa:ondrej/php
apt-get update
apt-get -y install php7.1 mcrypt php7.1-mcrypt php-mbstring php-pear php7.1-dev php7.1-xml
**RedHat 7**
sudo su
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
subscription-manager repos --enable=rhel-7-server-optional-rpms
yum-config-manager --enable remi-php71
yum update
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
**Debian 8, 9**
sudo su
apt-get install curl apt-transport-https
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
apt-get update
apt-get install -y php7.1 php-pear php7.1-dev php7.1-xml
**SUSE 12**
sudo su
zypper -n ar -f http://download.opensuse.org/repositories/devel:/languages:/php/openSUSE_Leap_42.3/ devel:languages:php
zypper --gpg-auto-import-keys refresh
zypper -n install php7 php7-pear php7-devel
**macOS 10.11, 10.12**
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew tap
brew tap homebrew/dupes
brew tap homebrew/versions
brew tap homebrew/homebrew-php
brew install php71 --with-pear --with-httpd24 --with-cgi
echo 'export PATH="/usr/local/sbin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile
### Step 2: Install Prerequisites
**Ubuntu 16.04**
sudo su
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
exit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql mssql-tools
sudo apt-get install unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
**Ubuntu 17.10 (available upon the official release of ODBC driver 17)**
sudo su
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/ubuntu/17.10/prod.list > /etc/apt/sources.list.d/mssql-release.list
exit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql mssql-tools
sudo apt-get install unixodbc-dev
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
**RedHat 7**
sudo su
curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssql-release.repo
exit
sudo yum update
sudo yum remove unixODBC-utf16-devel #to avoid conflicts
sudo ACCEPT_EULA=Y yum install msodbcsql mssql-tools
sudo yum install unixODBC-devel
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
**Debian 8**
sudo su
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/debian/8/prod.list > /etc/apt/sources.list.d/mssql-release.list
apt-get install -y locales
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
locale-gen
exit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql
sudo apt-get install unixodbc-dev
**Debian 9 (available upon the official release of ODBC driver 17)**
sudo su
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/debian/9/prod.list > /etc/apt/sources.list.d/mssql-release.list
apt-get install -y locales
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
locale-gen
exit
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install msodbcsql
sudo apt-get install unixodbc-dev
**SUSE 12**
sudo su
zypper ar https://packages.microsoft.com/config/sles/12/prod.repo
sudo zypper --gpg-auto-import-keys refresh
exit
sudo ACCEPT_EULA=Y zypper install msodbcsql
sudo ACCEPT_EULA=Y zypper install mssql-tools
sudo zypper install unixODBC-devel
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc
**macOS 10.11, 10.12**
brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-mssql-release
brew update
brew install --no-sandbox msodbcsql
brew install mssql-tools
brew install autoconf
*Note: Be sure to install PHP 7+ before proceeding to step 3. The Microsoft PHP Drivers for SQL Server will only work for PHP 7+.
### Step 3: Install the Microsoft PHP Drivers for SQL Server
*Note: You can run `sudo pecl search sqlsrv` to search for the latest releases and `sudo pecl install sqlsrv-[version]` to install a specific version. PECL installs the stable version when version is not specified. Drivers are Mac-compatible starting from `4.1.7preview` release.
On Ubuntu, Debian, and SUSE systems only, run:
sudo pear config-set php_ini `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` system
On macOS, run:
chmod -R ug+w /usr/local/opt/php71/lib/php
pear config-set php_ini /usr/local/etc/php/7.1/php.ini system
On all systems, run:
pecl install sqlsrv
pecl install pdo_sqlsrv
### Step 4: Install and Configure Apache
#### PHP 7.0
**Ubuntu and Debian**
sudo su
apt-get install libapache2-mod-php7.0 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.0
echo "extension=sqlsrv.so" >> /etc/php/7.0/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php/7.0/apache2/php.ini
**RedHat**
sudo su
yum install httpd
echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini
echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini
**SUSE**
sudo su
zypper install apache2 apache2-mod_php7
a2enmod php7
echo "extension=sqlsrv.so" >> /etc/php7/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php7/apache2/php.ini
**macOS**
(echo "<FilesMatch .php$>"; echo "SetHandler application/x-httpd-php"; echo "</FilesMatch>";) >> /usr/local/etc/apache2/2.4/httpd.conf
#### PHP 7.1
**Ubuntu and Debian**
sudo su
apt-get install libapache2-mod-php7.1 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.1
echo "extension=sqlsrv.so" >> /etc/php/7.1/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php/7.1/apache2/php.ini
**RedHat**
sudo su
yum install httpd
echo "extension=sqlsrv.so" > /etc/php.d/sqlsrv.ini
echo "extension=pdo_sqlsrv.so" > /etc/php.d/pdo_sqlsrv.ini
**SUSE**
sudo su
zypper install apache2 apache2-mod_php7
a2enmod php7
echo "extension=sqlsrv.so" >> /etc/php7/apache2/php.ini
echo "extension=pdo_sqlsrv.so" >> /etc/php7/apache2/php.ini
**macOS**
(echo "<FilesMatch .php$>"; echo "SetHandler application/x-httpd-php"; echo "</FilesMatch>";) >> /usr/local/etc/apache2/2.4/httpd.conf
### Step 5: Restart Apache to load the new php.ini file
**Ubuntu, Debian, and SUSE**
sudo systemctl restart apache2
**RedHat**
sudo systemctl restart httpd
Note: On RedHat, SELinux is installed by default and runs in Enforcing mode. To allow Apache to connect to a database through SELinux, run the following command:
sudo setsebool -P httpd_can_network_connect_db 1
**macOS**
sudo apachectl restart
### Step 6: Create your sample app
Navigate to your system's document root -- `/var/www/html` on Ubuntu, Debian, and Redhat, `/srv/www/htdocs` on SUSE, or `/usr/local/var/www/htdocs` on Mac. Create a new file called testsql.php. Copy and paste the following code into testsql.php and change the servername, username, password and databasename.
<?php
$serverName = "yourServername";
$connectionOptions = array(
"Database" => "yourDatabase",
"Uid" => "yourUsername",
"PWD" => "yourPassword"
);
//Establishes the connection
$conn = sqlsrv_connect( $serverName, $connectionOptions );
if( $conn === false ) {
die( FormatErrors( sqlsrv_errors()));
}
//Select Query
$tsql= "SELECT @@Version as SQL_VERSION";
//Executes the query
$getResults= sqlsrv_query( $conn, $tsql );
//Error handling
if ( $getResults == FALSE )
die( FormatErrors( sqlsrv_errors()));
?>
<h1> Results : </h1>
<?php
while ( $row = sqlsrv_fetch_array( $getResults, SQLSRV_FETCH_ASSOC )) {
echo ( $row['SQL_VERSION']);
echo ("<br/>");
}
sqlsrv_free_stmt( $getResults );
function FormatErrors( $errors )
{
/* Display errors. */
echo "Error information: <br/>";
foreach ( $errors as $error )
{
echo "SQLSTATE: ".$error['SQLSTATE']."<br/>";
echo "Code: ".$error['code']."<br/>";
echo "Message: ".$error['message']."<br/>";
}
}
?>
### Step 7: Run your sample app
Go to your browser and type in http://localhost/testsql.php (http://localhost:8080/testsql.php on Mac)
You should be able to connect to your SQL Server/Azure SQL Database.
The drivers are distributed as shared binary extensions for PHP. They are available in thread safe (*_ts.so) and-non thread safe (*_nts.so) versions. The source code for the drivers is also available, and you can choose whether to compile them as thread safe or non-thread safe versions. The thread safety configuration of your web server will determine which version you need.
For full instructions on installing the drivers on all supported Unix platforms, see [the installation instructions on MSDN](https://docs.microsoft.com/en-us/sql/connect/php/installation-tutorial-linux-mac).
## Sample Code
For samples, please see the sample folder. For setup instructions, see [here](https://docs.microsoft.com/en-us/azure/sql-database/sql-database-develop-php-simple).
For PHP code samples, please see the [sample](https://github.com/Microsoft/msphpsql/tree/master/sample) folder or the [code samples on MSDN](https://docs.microsoft.com/en-us/sql/connect/php/code-samples-for-php-sql-driver).
## Limitations
- This release contains the PHP 7 port of the SQLSRV and PDO_SQLSRV drivers, and does not provide backwards compatibility with PHP 5.
- Binding output parameters using emulate prepare is not supported.
- Linux
- ODBC 3.52 is supported but not 3.8.
- Connection using named instances using '\' is not supported.
- Local encodings other than UTF-8 are not supported, and SQLSRV_ENC_CHAR only supports ASCII characters with ASCII code of 0 to 127.
## Known Issues
- When pooling is enabled in Linux or macOS
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostics 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#connection-pooling-in-linux-and-mac)
## Limitations and Known Issues
Please refer to [Releases](https://github.com/Microsoft/msphpsql/releases) for the latest limitations and known issues.
## Version number
Version number of PHP drivers follow the [semantic versioning](http://semver.org/):
The version numbers of the PHP drivers follow [semantic versioning](http://semver.org/):
Given a version number MAJOR.MINOR.PATCH,
- MAJOR version is incremented when an incompatible API changes is made,
- MINOR version is incremented when a functionality in a backwards-compatible manner is added, and
- MAJOR version is incremented when an incompatible API change is made,
- MINOR version is incremented when functionality is added in a backwards-compatible manner, and
- PATCH version is incremented when backwards-compatible bug fixes are made.
version number MAY have trailing pre-release version to indicate the stability, and/or build meta data.
The version number may have trailing pre-release version identifiers to indicate the stability and/or build metadata.
- Pre-release version is denoted by hyphen followed by `preview` or `rc` keyword and may be followed by a series of dot separated identifiers. Production quality releases do not contain the pre-release version. `preview` has lower precedence than `rc`. Example of precedence: *preview < preview.1 < rc < rc.1*.
*Note that PECL package version does not have the hyphen before pre-release version, due to restrictions in PECL. Example of PECL package version: 1.2.3preview*
- Build metadata MAY be denoted by a plus sign followed by 4 digits, such as `1.2.3-preview+5678` or `1.2.3+5678`. Build meta data does NOT figure into the precedence order.
- Pre-release version is denoted by a hyphen followed by `preview` or `RC` and may be followed by a series of dot separated identifiers. Production quality releases do not contain the pre-release version. `preview` has lower precedence than `RC`. Example of precedence: *preview < preview.1 < RC < RC.1*. Note that the PECL package version numbers do not have the hyphen before the pre-release version, owing to restrictions in PECL. Example of a PECL package version number: 1.2.3preview
- Build metadata may be denoted by a plus sign followed by 4 or 5 digits, such as `1.2.3-preview+5678` or `1.2.3+5678`. Build metadata does not figure into the precedence order.
## Future Plans
- Expand SQL 16 Feature Support (example: Always Encrypted).
- Add More Verification/Fundamental Tests.
- Bug Fixes.
- Expand SQL Server 2016 feature support (example: Always Encrypted)
- Add more verification/fundamental tests
- Bug fixes
## Guidelines for Reporting Issues
We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you:
- Report each issue as a new issue (but check first if it's already been reported)
- Try to be detailed in your report. Useful information for good bug reports include:
- Try to be detailed in your report. Useful information for good bug reports includes:
* What you are seeing and what the expected behaviour is
* Can you connect to SQL Server via `sqlcmd`?
* Which driver: SQLSRV or PDO_SQLSRV?
* Environment details: e.g. PHP version, thread safe (TS) or non-thread safe (NTS), 32-bit &/or 64-bit?
* Table schema (for some issues the data types make a big difference!)
* Environment details: e.g. PHP version, thread safe (TS) or non-thread safe (NTS), 32-bit or 64-bit?
* Table schema (for some issues, the data types make a big difference!)
* Any other relevant information you want to share
- Try to include a PHP script demonstrating the isolated problem.
@ -486,11 +111,11 @@ Thank you!
## FAQs
**Q:** Can we get dates for any of the Future Plans listed above?
**A:** At this time, Microsoft is not able to announce dates. We are working extremely hard to release future versions of the driver. We will share future plans as appropriate.
**A:** At this time, Microsoft is not able to announce dates. We are working hard to release future versions of the driver and will share future plans as appropriate.
**Q:** What's next?
**A:** On July 6, 2017 we released the production release version 4.3.0 of our PHP Driver. We will continue working on our future plans and releasing previews of upcoming releases frequently.
**A:** On March 23, 2018 we released the production release version 5.2.0 of our PHP Driver. We will continue working on our future plans and releasing previews of upcoming releases frequently.
**Q:** Is Microsoft taking pull requests for this project?
@ -516,9 +141,9 @@ This project has adopted the Microsoft Open Source Code of Conduct. For more inf
[blog]: http://blogs.msdn.com/b/sqlphp/
[project]: https://github.com/Azure/msphpsql
[project]: https://github.com/Microsoft/msphpsql
[issues]: https://github.com/Azure/msphpsql/issues
[issues]: https://github.com/Microsoft/msphpsql/issues
[phpweb]: http://php.net
@ -534,8 +159,6 @@ This project has adopted the Microsoft Open Source Code of Conduct. For more inf
[odbcLinux]: https://msdn.microsoft.com/library/hh568454(v=sql.110).aspx
[phpazure]: https://azure.microsoft.com/documentation/articles/sql-database-develop-php-simple-windows/
[PHPMan]: http://php.net/manual/install.unix.php
[LinuxDM]: https://msdn.microsoft.com/library/hh568449(v=sql.110).aspx
@ -544,6 +167,4 @@ This project has adopted the Microsoft Open Source Code of Conduct. For more inf
[apr_source]: http://apr.apache.org/
[httpdconf]: http://php.net/manual/en/install.unix.apache2.php
[ODBCinstallers]: https://blogs.msdn.microsoft.com/sqlnativeclient/2016/09/06/preview-release-of-the-sql-server-cc-odbc-driver-13-0-0-for-linux
[httpdconf]: http://php.net/manual/en/install.unix.apache2.php

View file

@ -18,6 +18,15 @@ environment:
PHP_DEPSVER: 7.0
PHP_SDK: c:\projects\php
matrix:
- BUILD_PLATFORM: x64
TEST_PHP_SQL_SERVER: (local)\SQL2012SP1
SQL_INSTANCE: SQL2012SP1
PHP_VC: 14
PHP_MAJOR_VER: 7.1
PHP_MINOR_VER: latest
PHP_SDK_DIR: c:\projects\php\x64
PHP_INSTALL_DIR: c:\projects\php\x64\bin
platform: x64
- BUILD_PLATFORM: x86
TEST_PHP_SQL_SERVER: (local)\SQL2016
SQL_INSTANCE: SQL2016
@ -28,15 +37,6 @@ environment:
PHP_INSTALL_DIR: c:\projects\php\x86\bin
PHP_ZTS: --disable-zts
platform: x86
- BUILD_PLATFORM: x64
TEST_PHP_SQL_SERVER: (local)\SQL2012SP1
SQL_INSTANCE: SQL2012SP1
PHP_VC: 14
PHP_MAJOR_VER: 7.1
PHP_MINOR_VER: latest
PHP_SDK_DIR: c:\projects\php\x64
PHP_INSTALL_DIR: c:\projects\php\x64\bin
platform: x64
# PHP_MAJOR_VER is PHP major version to build (7.0, 7.1)
# PHP_MINOR_VER is PHP point release number (or latest for latest release)
@ -78,20 +78,24 @@ install:
Set-Service SQLBrowser -StartupType Manual;
Start-Service SQLBrowser;
- echo Set PHP version...
- appveyor DownloadFile http://windows.php.net/downloads/releases/sha1sum.txt
# determine latest PHP versions
- ps: >-
- echo Downloading prerequisites
- ps: |
$client = New-Object Net.WebClient;
$client.Headers.Add("user-agent", "appveyor-ci-build1");
$client.DownloadFile("http://windows.php.net/downloads/php-sdk/php-sdk-binary-tools-20110915.zip", "c:\projects\php-sdk-binary-tools-20110915.zip");
- ps: |
$client = New-Object Net.WebClient;
$client.Headers.Add("user-agent", "appveyor-ci-build2");
$client.DownloadFile("http://windows.php.net/downloads/releases/sha1sum.txt", "c:\projects\sha1sum.txt");
If ($env:PHP_MINOR_VER -Match "latest") {
$env:PHP_VERSION=type sha1sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } ;
$env:PHP_VERSION=type c:\projects\sha1sum.txt | where { $_ -match "php-($env:PHP_MAJOR_VER\.\d+)-src" } | foreach { $matches[1] } ;
} Else {
$env:PHP_VERSION=$env:PHP_MAJOR_VER + '.' + $env:PHP_MINOR_VER;
}
- echo Downloading PHP-SDK
- appveyor DownloadFile http://windows.php.net/downloads/php-sdk/php-sdk-binary-tools-20110915.zip
- move php-sdk-binary-tools-20110915.zip ..
- echo Downloading PHP source code [%PHP_VERSION%]
- ps: (new-object net.webclient).DownloadFile('http://windows.php.net/downloads/releases/php-' + ${env:PHP_VERSION} + '-src.zip', ${env:APPVEYOR_BUILD_FOLDER} + '\..\php.zip')
- ps: |
$client = New-Object Net.WebClient;
$client.Headers.Add("user-agent", "appveyor-ci-build3");
$client.DownloadFile("http://windows.php.net/downloads/releases/php-" + ${env:PHP_VERSION} + "-src.zip", ${env:APPVEYOR_BUILD_FOLDER} + "\..\php.zip");
- echo Downloading MSODBCSQL 13.1
# 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/D/5/E/D5EEF288-A277-45C8-855B-8E2CB7E25B96/x64/msodbcsql.msi', 'c:\projects\msodbcsql.msi')
@ -99,10 +103,9 @@ install:
- echo Checking the version of MSODBCSQL
- reg query "HKLM\SOFTWARE\ODBC\odbcinst.ini\ODBC Driver 13 for SQL Server"
- dir C:\Windows\System32\msodbcsql13.dll
- cd ..
- cd
- 7z x -y php-sdk-binary-tools-20110915.zip -o%PHP_SDK%
- 7z x -y php.zip -o%PHP_SDK_DIR%
- cd c:\projects
- 7z x -y .\php-sdk-binary-tools-20110915.zip -o%PHP_SDK%
- 7z x -y .\php.zip -o%PHP_SDK_DIR%
- echo update SQL connection string
- ps: (Get-Content ${env:APPVEYOR_BUILD_FOLDER}\test\functional\pdo_sqlsrv\MsSetup.inc) | ForEach-Object { $_ -replace "TARGET_SERVER", ${env:TEST_PHP_SQL_SERVER} -replace "TARGET_DATABASE", ${env:PDOSQLSRV_DBNAME} -replace "TARGET_USERNAME", ${env:TEST_PHP_SQL_UID} -replace "TARGET_PASSWORD", ${env:TEST_PHP_SQL_PWD} } | Set-Content ${env:APPVEYOR_BUILD_FOLDER}\test\functional\pdo_sqlsrv\MsSetup.inc
- ps: Get-Content ${env:APPVEYOR_BUILD_FOLDER}\test\functional\pdo_sqlsrv\MsSetup.inc
@ -150,12 +153,6 @@ test_script:
- python %APPVEYOR_BUILD_FOLDER%\test\functional\setup\setup_dbs.py -dbname %SQLSRV_DBNAME%
- Echo setup test database for PDO_SQLSRV tests - %PDOSQLSRV_DBNAME%
- python %APPVEYOR_BUILD_FOLDER%\test\functional\setup\setup_dbs.py -dbname %PDOSQLSRV_DBNAME%
#- copy %APPVEYOR_BUILD_FOLDER%\source\shared\msodbcsql.h %APPVEYOR_BUILD_FOLDER%\test\functional\setup\
#- python %APPVEYOR_BUILD_FOLDER%\test\functional\setup\build_ksp.py
#- copy %APPVEYOR_BUILD_FOLDER%\test\functional\setup\*.dll %APPVEYOR_BUILD_FOLDER%\test\functional\sqlsrv\
#- copy %APPVEYOR_BUILD_FOLDER%\test\functional\setup\*.dll %APPVEYOR_BUILD_FOLDER%\test\functional\pdo_sqlsrv\
#- python %APPVEYOR_BUILD_FOLDER%\test\functional\setup\run_ksp.py -server %TEST_PHP_SQL_SERVER% -dbname %SQLSRV_DBNAME% -uid %TEST_PHP_SQL_UID% -pwd %TEST_PHP_SQL_PWD%
#- python %APPVEYOR_BUILD_FOLDER%\test\functional\setup\run_ksp.py -server %TEST_PHP_SQL_SERVER% -dbname %PDOSQLSRV_DBNAME% -uid %TEST_PHP_SQL_UID% -pwd %TEST_PHP_SQL_PWD%
- ps: >-
If ($env:SQL_INSTANCE -Match "SQL2016") {
Write-Host "Running phpt tests via OpenCppCoverage..."

View file

@ -14,7 +14,7 @@ To use the sample build scripts `builddrivers.py` and `buildtools.py`, install P
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.
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.0 beta using Visual C++ 2017 v15.0.
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.
### Manually building from source
@ -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.0beta2`)
* PHP Version (e.g. `7.1.7` or `7.2.1`)
* 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.0.22 --ARCH=x64 --THREAD=nts --DRIVER=sqlsrv --SOURCE`
* `py builddrivers.py --PHPVER=7.1.8 --ARCH=x86 --THREAD=ts --DEBUG`
* `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`
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

@ -33,7 +33,6 @@ class BuildDriver(object):
util # BuildUtil object whose constructor takes phpver, driver, arch, thread, debug
repo # GitHub repository
branch # GitHub repository branch
download_source # download source from GitHub or not
dest_path # alternative destination for the drivers (None for development builds)
rebuild # a boolean flag - whether the user is rebuilding
make_clean # a boolean flag - whether make clean is necessary
@ -41,16 +40,15 @@ class BuildDriver(object):
testing # whether the user has turned on testing mode
"""
def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, path, testing):
def __init__(self, phpver, driver, arch, thread, debug, repo, branch, source, path, testing):
self.util = BuildUtil(phpver, driver, arch, thread, debug)
self.repo = repo
self.branch = branch
self.download_source = download
self.source_path = source
self.dest_path = path
self.testing = testing
self.rebuild = False
self.make_clean = False
self.source_path = None # None initially but will be set later if not downloading from GitHub
def show_config(self):
print()
@ -58,6 +56,7 @@ class BuildDriver(object):
print('Arch: ', self.util.arch)
print('Thread: ', self.util.thread)
print('Driver: ', self.util.driver)
print('Source: ', self.source_path)
print('Debug enabled: ', self.util.debug_enabled)
print()
@ -91,6 +90,28 @@ class BuildDriver(object):
os.chdir(work_dir) # change back to the working directory
def get_local_source(self, source_path):
"""This assumes interactive mode (not testing) and takes care of getting
the user's input to the path of the local source files for the drivers
"""
while True:
if source_path is None:
source = input('Enter the full path to the source folder: ')
else:
source = input("Hit ENTER to use '" + source_path + "' or provide another path to the source folder: ")
if len(source) == 0:
source = source_path
valid = True
if os.path.exists(source) and os.path.exists(os.path.join(source, 'shared')):
# Checking the existence of 'shared' folder only, assuming
# sqlsrv and/or pdo_sqlsrv are also present if it exists
self.source_path = source
break
print("The path provided is invalid. Please re-enter.")
return source
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.
@ -102,28 +123,15 @@ class BuildDriver(object):
"""
work_dir = os.path.dirname(os.path.realpath(__file__))
if self.download_source:
if self.source_path is None:
# This will download from the specified branch on GitHub repo and copy the source
self.util.download_msphpsql_source(repo, branch)
else:
# This case only happens when building for development
while True:
if self.source_path is None:
source = input('Enter the full path to the Source folder: ')
else:
source = input("Hit ENTER to reuse '" + self.source_path + "' or provide another path to the Source folder: ")
if len(source) == 0:
source = self.source_path
valid = True
if os.path.exists(source) and os.path.exists(os.path.join(source, 'shared')):
# Checking the existence of 'shared' folder only, assuming
# sqlsrv and/or pdo_sqlsrv are also present if it exists
self.source_path = source
break
print("The path provided is invalid. Please re-enter.")
source = self.source_path
# Do not prompt user for input if it's in a testing mode
if not self.testing:
source = self.get_local_source(self.source_path)
print('Copying source files from', source)
os.system('ROBOCOPY ' + source + '\shared ' + work_dir + '\Source\shared /xx /xo ')
@ -169,7 +177,10 @@ class BuildDriver(object):
quit = False
while not quit:
if not self.rebuild and not self.testing:
if self.testing:
self.make_clean = True
self.util.remove_old_builds(work_dir)
elif not self.rebuild:
self.clean_or_remove(root_dir, work_dir)
logfile = self.util.get_logfile_name()
@ -223,7 +234,7 @@ if __name__ == '__main__':
parser.add_argument('--DEBUG', action='store_true', help="enable debug mode (default: False)")
parser.add_argument('--REPO', default='Microsoft', help="GitHub repository (default: Microsoft)")
parser.add_argument('--BRANCH', default='dev', help="GitHub repository branch (default: dev)")
parser.add_argument('--SOURCE', action='store_true', help="get source from a local path (default: False)")
parser.add_argument('--SOURCE', default=None, help="a local path to source file (default: None)")
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)")
@ -236,7 +247,7 @@ if __name__ == '__main__':
debug = args.DEBUG
repo = args.REPO
branch = args.BRANCH
download = args.SOURCE is False
source = args.SOURCE
path = args.DESTPATH
testing = args.TESTING
@ -259,9 +270,7 @@ if __name__ == '__main__':
debug_mode = input("Debug enabled? [y/n]: ")
answer = input("Download source from a GitHub repo? [y/n]: ")
download = False
if answer == 'yes' or answer == 'y' or answer == '':
download = True
repo = input("Name of the repo (hit enter for 'Microsoft'): ")
branch = input("Name of the branch (hit enter for 'dev'): ")
if repo == '':
@ -282,7 +291,7 @@ if __name__ == '__main__':
debug,
repo,
branch,
download,
source,
path,
testing)
builder.build()

View file

@ -246,14 +246,13 @@ class BuildUtil(object):
driver_dir = os.path.join(source_dir, driver)
if self.debug_enabled:
# Remove the optimization flag in the config file for this driver
# because '/O2' option is incompatible with Debug mode
print('Removing optimization flag for', driver)
# Adding linker flags for creating more debugging information in the binaries
print('Adding linker flags for', driver)
config_file = os.path.join(driver_dir, 'config.w32')
if driver == 'sqlsrv':
self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_SQLSRV", "/O2" );', '')
self.update_file_content(config_file, 'ADD_FLAG( "LDFLAGS_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf" );', 'ADD_FLAG( "LDFLAGS_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf /debugtype:cv,fixup" );')
elif driver == 'pdo_sqlsrv':
self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/O2" );', '')
self.update_file_content(config_file, 'ADD_FLAG( "LDFLAGS_PDO_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf" );', 'ADD_FLAG( "LDFLAGS_PDO_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf /debugtype:cv,fixup" );')
# Update Template.rc
template_file = os.path.join(driver_dir, 'template.rc')

View file

@ -1,74 +1,78 @@
PHP_ARG_WITH(pdo_sqlsrv, for pdo_sqlsrv support,
[ --with-pdo_sqlsrv Include pdo_sqlsrv support])
if test "$PHP_PDO_SQLSRV" != "no"; then
if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then
AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])
fi
ifdef([PHP_CHECK_PDO_INCLUDES],
[
PHP_CHECK_PDO_INCLUDES
],[
AC_MSG_CHECKING([for PDO includes])
if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$abs_srcdir/ext
elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$abs_srcdir/ext
elif test -f $phpincludedir/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$phpincludedir/ext
else
AC_MSG_ERROR([Cannot find php_pdo_driver.h.])
fi
AC_MSG_RESULT($pdo_cv_inc_path)
])
pdo_sqlsrv_src_class="\
pdo_dbh.cpp \
pdo_parser.cpp \
pdo_util.cpp \
pdo_init.cpp \
pdo_stmt.cpp \
"
shared_src_class="\
shared/core_conn.cpp \
shared/core_results.cpp \
shared/core_stream.cpp \
shared/core_init.cpp \
shared/core_stmt.cpp \
shared/core_util.cpp \
shared/FormattedPrint.cpp \
shared/localizationimpl.cpp \
shared/StringFunctions.cpp \
"
AC_MSG_CHECKING([for PDO_SQLSRV headers])
if test -f $srcdir/ext/pdo_sqlsrv/shared/core_sqlsrv.h; then
pdo_sqlsrv_inc_path=$srcdir/ext/pdo_sqlsrv/shared/
elif test -f $srcdir/shared/core_sqlsrv.h; then
pdo_sqlsrv_inc_path=$srcdir/shared/
else
AC_MSG_ERROR([Cannot find PDO_SQLSRV headers])
fi
AC_MSG_RESULT($pdo_sqlsrv_inc_path)
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
fi
CXXFLAGS="$CXXFLAGS -std=c++11"
CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -O2"
CXXFLAGS="$CXXFLAGS -fstack-protector"
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, 1, PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_LIBRARY(odbc, 1, PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_LIBRARY(odbcinst, 1, PDO_SQLSRV_SHARED_LIBADD)
AC_DEFINE(HAVE_PDO_SQLSRV, 1, [ ])
PHP_ADD_INCLUDE([$pdo_sqlsrv_inc_path])
PHP_NEW_EXTENSION(pdo_sqlsrv, $pdo_sqlsrv_src_class $shared_src_class, $ext_shared,,-I$pdo_cv_inc_path -std=c++11)
PHP_SUBST(PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_EXTENSION_DEP(pdo_sqlsrv, pdo)
PHP_ADD_BUILD_DIR([$ext_builddir/shared], 1)
fi
PHP_ARG_WITH(pdo_sqlsrv, for pdo_sqlsrv support,
[ --with-pdo_sqlsrv Include pdo_sqlsrv support])
if test "$PHP_PDO_SQLSRV" != "no"; then
if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then
AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])
fi
ifdef([PHP_CHECK_PDO_INCLUDES],
[
PHP_CHECK_PDO_INCLUDES
],[
AC_MSG_CHECKING([for PDO includes])
if test -f $abs_srcdir/include/php/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$abs_srcdir/ext
elif test -f $abs_srcdir/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$abs_srcdir/ext
elif test -f $phpincludedir/ext/pdo/php_pdo_driver.h; then
pdo_cv_inc_path=$phpincludedir/ext
else
AC_MSG_ERROR([Cannot find php_pdo_driver.h.])
fi
AC_MSG_RESULT($pdo_cv_inc_path)
])
pdo_sqlsrv_src_class="\
pdo_dbh.cpp \
pdo_parser.cpp \
pdo_util.cpp \
pdo_init.cpp \
pdo_stmt.cpp \
"
shared_src_class="\
shared/core_conn.cpp \
shared/core_results.cpp \
shared/core_stream.cpp \
shared/core_init.cpp \
shared/core_stmt.cpp \
shared/core_util.cpp \
shared/FormattedPrint.cpp \
shared/localizationimpl.cpp \
shared/StringFunctions.cpp \
"
AC_MSG_CHECKING([for PDO_SQLSRV headers])
if test -f $srcdir/ext/pdo_sqlsrv/shared/core_sqlsrv.h; then
pdo_sqlsrv_inc_path=$srcdir/ext/pdo_sqlsrv/shared/
elif test -f $srcdir/shared/core_sqlsrv.h; then
pdo_sqlsrv_inc_path=$srcdir/shared/
else
AC_MSG_ERROR([Cannot find PDO_SQLSRV headers])
fi
AC_MSG_RESULT($pdo_sqlsrv_inc_path)
CXXFLAGS="$CXXFLAGS -std=c++11"
CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -O2"
CXXFLAGS="$CXXFLAGS -fstack-protector"
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
PDO_SQLSRV_SHARED_LIBADD="$PDO_SQLSRV_SHARED_LIBADD -Wl,-bind_at_load"
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
else
PDO_SQLSRV_SHARED_LIBADD="$PDO_SQLSRV_SHARED_LIBADD -Wl,-z,now"
fi
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, 1, PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_LIBRARY(odbc, 1, PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_LIBRARY(odbcinst, 1, PDO_SQLSRV_SHARED_LIBADD)
AC_DEFINE(HAVE_PDO_SQLSRV, 1, [ ])
PHP_ADD_INCLUDE([$pdo_sqlsrv_inc_path])
PHP_NEW_EXTENSION(pdo_sqlsrv, $pdo_sqlsrv_src_class $shared_src_class, $ext_shared,,-I$pdo_cv_inc_path -std=c++11)
PHP_SUBST(PDO_SQLSRV_SHARED_LIBADD)
PHP_ADD_EXTENSION_DEP(pdo_sqlsrv, pdo)
PHP_ADD_BUILD_DIR([$ext_builddir/shared], 1)
fi

View file

@ -30,11 +30,11 @@ if( PHP_PDO_SQLSRV != "no" ) {
CHECK_HEADER_ADD_INCLUDE("sql.h", "CFLAGS_PDO_SQLSRV_ODBC");
CHECK_HEADER_ADD_INCLUDE("sqlext.h", "CFLAGS_PDO_SQLSRV_ODBC");
ADD_SOURCES( configure_module_dirname + "\\shared", shared_src_class, "pdo_sqlsrv" );
ADD_FLAG( "LDFLAGS_PDO_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug" );
ADD_FLAG( "LDFLAGS_PDO_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf" );
ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/EHsc" );
ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/GS" );
ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/Zi" );
ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/O2" );
if (PHP_DEBUG != "yes") ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/guard:cf /O2" );
ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/D ZEND_WIN32_FORCE_INLINE" );
ADD_EXTENSION_DEP('pdo_sqlsrv', 'pdo');
EXTENSION("pdo_sqlsrv", pdo_sqlsrv_src_class, PHP_PDO_SQLSRV_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");

View file

@ -42,13 +42,9 @@ const char ApplicationIntent[] = "ApplicationIntent";
const char AttachDBFileName[] = "AttachDbFileName";
const char ConnectionPooling[] = "ConnectionPooling";
const char Authentication[] = "Authentication";
const char ColumnEncryption[] = "ColumnEncryption";
const char Driver[] = "Driver";
const char CEKeystoreProvider[] = "CEKeystoreProvider";
const char CEKeystoreName[] = "CEKeystoreName";
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
#ifdef _WIN32
const char ColumnEncryption[] = "ColumnEncryption";
const char ConnectRetryCount[] = "ConnectRetryCount";
const char ConnectRetryInterval[] = "ConnectRetryInterval";
#endif // _WIN32
@ -226,15 +222,6 @@ const connection_option PDO_CONN_OPTS[] = {
CONN_ATTR_BOOL,
conn_null_func::func
},
{
PDOConnOptionNames::ColumnEncryption,
sizeof(PDOConnOptionNames::ColumnEncryption),
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
ODBCConnOptions::ColumnEncryption,
sizeof(ODBCConnOptions::ColumnEncryption),
CONN_ATTR_STRING,
column_encryption_set_func::func
},
{
PDOConnOptionNames::Driver,
sizeof(PDOConnOptionNames::Driver),
@ -244,34 +231,16 @@ const connection_option PDO_CONN_OPTS[] = {
CONN_ATTR_STRING,
driver_set_func::func
},
{
PDOConnOptionNames::CEKeystoreProvider,
sizeof(PDOConnOptionNames::CEKeystoreProvider),
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
ODBCConnOptions::CEKeystoreProvider,
sizeof(ODBCConnOptions::CEKeystoreProvider),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
{
PDOConnOptionNames::CEKeystoreName,
sizeof(PDOConnOptionNames::CEKeystoreName),
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
ODBCConnOptions::CEKeystoreName,
sizeof(ODBCConnOptions::CEKeystoreName),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
{
PDOConnOptionNames::CEKeystoreEncryptKey,
sizeof(PDOConnOptionNames::CEKeystoreEncryptKey),
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
ODBCConnOptions::CEKeystoreEncryptKey,
sizeof(ODBCConnOptions::CEKeystoreEncryptKey),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
#ifdef _WIN32
{
PDOConnOptionNames::ColumnEncryption,
sizeof(PDOConnOptionNames::ColumnEncryption),
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
ODBCConnOptions::ColumnEncryption,
sizeof(ODBCConnOptions::ColumnEncryption),
CONN_ATTR_STRING,
column_encryption_set_func::func
},
{
PDOConnOptionNames::ConnectRetryCount,
sizeof( PDOConnOptionNames::ConnectRetryCount ),

View file

@ -59,7 +59,7 @@ pdo_error PDO_ERRORS[] = {
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server to "
"communicate with SQL Server. Access the following URL to download the ODBC Driver for SQL Server "
"for %1!s!: "
"http://go.microsoft.com/fwlink/?LinkId=163712", -1, true }
"https://go.microsoft.com/fwlink/?LinkId=163712", -1, true }
},
{
SQLSRV_ERROR_ZEND_HASH,
@ -381,22 +381,6 @@ pdo_error PDO_ERRORS[] = {
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword or ActiveDirectoryPassword is supported.", -73, false }
},
{
SQLSRV_ERROR_KEYSTORE_NAME_MISSING,
{ IMSSP, (SQLCHAR*) "The name of the custom keystore provider is missing.", -74, false}
},
{
SQLSRV_ERROR_KEYSTORE_PATH_MISSING,
{ IMSSP, (SQLCHAR*) "The path to the custom keystore provider is missing.", -75, false}
},
{
SQLSRV_ERROR_KEYSTORE_KEY_MISSING,
{ IMSSP, (SQLCHAR*) "The encryption key for the custom keystore provider is missing.", -76, false}
},
{
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
{ IMSSP, (SQLCHAR*) "Invalid value for loading a custom keystore provider.", -77, false}
},
{
SQLSRV_ERROR_CE_DRIVER_REQUIRED,
{ IMSSP, (SQLCHAR*) "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.", -78, false }

View file

@ -50,7 +50,7 @@ const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver names.
// the order of this list should match the order of DRIVER_VERSION enum
std::vector<std::string> CONNECTION_STRING_DRIVER_NAME{ "Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};", "Driver={ODBC Driver 17 for SQL Server};" };
std::vector<std::string> CONNECTION_STRING_DRIVER_NAME{ "Driver={ODBC Driver 17 for SQL Server};", "Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};" };
// default options if only the server is specified
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes};";
@ -71,7 +71,6 @@ 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_configure_ksp( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
}
// core_sqlsrv_connect
@ -183,11 +182,11 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
// https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server#microsoft-odbc-driver-11-for-sql-server-on-linux
DRIVER_VERSION odbc_version = ODBC_DRIVER_UNKNOWN;
if( core_search_odbc_driver_unix( ODBC_DRIVER_13 ) ) {
odbc_version = ODBC_DRIVER_13;
}
else if ( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) {
if( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) {
odbc_version = ODBC_DRIVER_17;
}
else if ( core_search_odbc_driver_unix( ODBC_DRIVER_13 ) ) {
odbc_version = ODBC_DRIVER_13;
}
CHECK_CUSTOM_ERROR( odbc_version == ODBC_DRIVER_UNKNOWN, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch() ) {
@ -246,8 +245,6 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
throw core::CoreException();
}
load_configure_ksp( conn );
// determine the version of the server we're connected to. The server version is left in the
// connection upon return.
//
@ -935,66 +932,6 @@ void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
conn->server_version = version_major;
}
// Column Encryption feature: if a custom keystore provider is specified,
// load and configure it when column encryption is enabled, but this step has
// to be executed after the connection has been established
void load_configure_ksp( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
{
// If column encryption is not enabled simply do nothing. Otherwise, check if a custom keystore provider
// is required for encryption or decryption. Note, in order to load and configure a custom keystore provider,
// all KSP fields in conn->ce_option must be defined.
if ( ! conn->ce_option.enabled || ! conn->ce_option.ksp_required )
return;
// Do something like the following sample
// use the KSP related fields in conn->ce_option
// CEKEYSTOREDATA is defined in msodbcsql.h
// https://docs.microsoft.com/en-us/sql/connect/odbc/custom-keystore-providers
CHECK_CUSTOM_ERROR( conn->ce_option.ksp_name == NULL, conn, SQLSRV_ERROR_KEYSTORE_NAME_MISSING) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR( conn->ce_option.ksp_path == NULL, conn, SQLSRV_ERROR_KEYSTORE_PATH_MISSING) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR( conn->ce_option.key_size == 0, conn, SQLSRV_ERROR_KEYSTORE_KEY_MISSING) {
throw core::CoreException();
}
char* ksp_name = Z_STRVAL_P( conn->ce_option.ksp_name );
char* ksp_path = Z_STRVAL_P( conn->ce_option.ksp_path );
unsigned int name_len = static_cast<unsigned int>( Z_STRLEN_P( conn->ce_option.ksp_name ));
unsigned int key_size = static_cast<unsigned int>( conn->ce_option.key_size );
sqlsrv_malloc_auto_ptr<unsigned char> ksp_data;
ksp_data = reinterpret_cast<unsigned char*>( sqlsrv_malloc( sizeof( CEKEYSTOREDATA ) + key_size ) );
CEKEYSTOREDATA *pKsd = reinterpret_cast<CEKEYSTOREDATA*>( ksp_data.get() );
pKsd->dataSize = key_size;
// First, convert conn->ce_option.ksp_name to a WCHAR version
unsigned int wname_len = 0;
sqlsrv_malloc_auto_ptr<SQLWCHAR> wksp_name;
wksp_name = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, ksp_name, name_len, &wname_len );
CHECK_CUSTOM_ERROR( wksp_name == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE ) {
throw core::CoreException();
}
pKsd->name = (wchar_t *) wksp_name.get();
// Next, extract the character string from conn->ce_option.ksp_encrypt_key into encrypt_key
char* encrypt_key = Z_STRVAL_P( conn->ce_option.ksp_encrypt_key );
memcpy_s( pKsd->data, key_size * sizeof( char ) , encrypt_key, key_size );
core::SQLSetConnectAttr( conn, SQL_COPT_SS_CEKEYSTOREPROVIDER, ksp_path, SQL_NTS );
core::SQLSetConnectAttr( conn, SQL_COPT_SS_CEKEYSTOREDATA, reinterpret_cast<SQLPOINTER>( pKsd ), 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 )
{
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
@ -1068,36 +1005,6 @@ void column_encryption_set_func::func( _In_ connection_option const* option, _In
conn_str += ";";
}
void ce_ksp_provider_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
{
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
size_t value_len = Z_STRLEN_P( value );
CHECK_CUSTOM_ERROR( value_len == 0, conn, SQLSRV_ERROR_KEYSTORE_INVALID_VALUE ) {
throw core::CoreException();
}
switch ( option->conn_option_key ) {
case SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER:
conn->ce_option.ksp_path = value;
conn->ce_option.ksp_required = true;
break;
case SQLSRV_CONN_OPTION_CEKEYSTORE_NAME:
conn->ce_option.ksp_name = value;
conn->ce_option.ksp_required = true;
break;
case SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY:
conn->ce_option.ksp_encrypt_key = value;
conn->ce_option.key_size = value_len;
conn->ce_option.ksp_required = true;
break;
default:
SQLSRV_ASSERT(false, "ce_ksp_provider_set_func: Invalid KSP option!");
break;
}
}
// helper function to evaluate whether a string value is true or false.
// Values = ("true" or "1") are treated as true values. Everything else is treated as false.
// Returns 1 for true and 0 for false.

View file

@ -83,7 +83,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, _In_ size_t row_count TSRMLS_DC );
_In_ zend_long mem_used TSRMLS_DC );
// dtor for each row in the cache
void cache_row_dtor( _In_ zval* data );
@ -687,7 +687,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, row_count TSRMLS_CC );
*lob_addr = read_lob_field( stmt, i, meta[i], mem_used TSRMLS_CC );
// a NULL pointer means NULL field
if( *lob_addr == NULL ) {
*out_buffer_length = SQL_NULL_DATA;
@ -1498,7 +1498,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, _In_ size_t row_count TSRMLS_DC )
_In_ zend_long mem_used TSRMLS_DC )
{
SQLSMALLINT extra = 0;
SQLULEN* output_buffer_len = NULL;
@ -1563,19 +1563,7 @@ SQLPOINTER read_lob_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_in
SQLSRV_ASSERT( SQL_SUCCEEDED( r ), "Unknown SQL error not triggered" );
if ( stmt->conn->ce_option.enabled == true ) {
// cursor type SQLSRV_CURSOR_BUFFERED has to be FORWARD_ONLY
// thus has to close and reopen cursor to reset the cursor buffer
core::SQLCloseCursor(stmt);
core::SQLExecute(stmt);
// FETCH_NEXT until the cursor reaches the row that it was at
for (int i = 0; i <= row_count; i++) {
core::SQLFetchScroll(stmt, SQL_FETCH_NEXT, 0);
}
}
else {
already_read += to_read - already_read;
}
already_read += to_read - already_read;
// if the type of the field returns the total to be read, we use that and preallocate the buffer
if( last_field_len != SQL_NO_TOTAL ) {

View file

@ -1043,10 +1043,10 @@ enum SERVER_VERSION {
enum DRIVER_VERSION {
ODBC_DRIVER_UNKNOWN = -1,
FIRST = 0,
ODBC_DRIVER_13 = FIRST,
ODBC_DRIVER_11 = 1,
ODBC_DRIVER_17 = 2,
LAST = ODBC_DRIVER_17
ODBC_DRIVER_17 = FIRST,
ODBC_DRIVER_13 = 1,
ODBC_DRIVER_11 = 2,
LAST = ODBC_DRIVER_11
};
// forward decl
@ -1056,13 +1056,8 @@ struct stmt_option;
// This holds the various details of column encryption.
struct col_encryption_option {
bool enabled; // column encryption enabled, false by default
zval_auto_ptr ksp_name; // keystore provider name
zval_auto_ptr ksp_path; // keystore provider path to the dynamically linked libary (either a *.dll or *.so)
zval_auto_ptr ksp_encrypt_key; // the encryption key used to configure the keystore provider
size_t key_size; // the length of ksp_encrypt_key without the NULL terminator
bool ksp_required; // a keystore provider is required to enable column encryption, false by default
col_encryption_option() : enabled( false ), key_size ( 0 ), ksp_required( false )
col_encryption_option() : enabled( false )
{
}
};
@ -1109,14 +1104,11 @@ const char APP[] = "APP";
const char ApplicationIntent[] = "ApplicationIntent";
const char AttachDBFileName[] = "AttachDbFileName";
const char Authentication[] = "Authentication";
const char ColumnEncryption[] = "ColumnEncryption";
const char Driver[] = "Driver";
const char CEKeystoreProvider[] = "CEKeystoreProvider";
const char CEKeystoreName[] = "CEKeystoreName";
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
const char CharacterSet[] = "CharacterSet";
const char ConnectionPooling[] = "ConnectionPooling";
#ifdef _WIN32
const char ColumnEncryption[] = "ColumnEncryption";
const char ConnectRetryCount[] = "ConnectRetryCount";
const char ConnectRetryInterval[] = "ConnectRetryInterval";
#endif // _WIN32
@ -1380,8 +1372,6 @@ struct sqlsrv_stmt : public sqlsrv_context {
bool past_fetch_end; // Core_sqlsrv_fetch sets this field when the statement goes beyond the last row
sqlsrv_result_set* current_results; // Current result set
SQLULEN cursor_type; // Type of cursor for the current result set
int fwd_row_index; // fwd_row_index is the current row index, SQL_CURSOR_FORWARD_ONLY
int curr_result_set; // the current active result set, 0 by default but will be incremented by core_sqlsrv_next_result
bool has_rows; // Has_rows is set if there are actual rows in the row set
bool fetch_called; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called
int last_field_index; // last field retrieved by core_sqlsrv_get_field
@ -1711,10 +1701,6 @@ enum SQLSRV_ERROR_CODES {
SQLSRV_ERROR_FIELD_INDEX_ERROR,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
SQLSRV_ERROR_INVALID_BUFFER_LIMIT,
SQLSRV_ERROR_KEYSTORE_NAME_MISSING,
SQLSRV_ERROR_KEYSTORE_PATH_MISSING,
SQLSRV_ERROR_KEYSTORE_KEY_MISSING,
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
SQLSRV_ERROR_OUTPUT_PARAM_TYPES_NOT_SUPPORTED,
SQLSRV_ERROR_ENCRYPTED_STREAM_FETCH,

View file

@ -64,7 +64,6 @@ struct col_cache {
};
const int INITIAL_FIELD_STRING_LEN = 2048; // base allocation size when retrieving a string field
const int INITIAL_AE_FIELD_STRING_LEN = 8000; // base allocation size when retrieving a string field when AE is enabled
// 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;
@ -117,7 +116,6 @@ void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
_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 );
bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt );
void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param TSRMLS_DC );
// send all the stream data
void send_param_streams( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
@ -137,8 +135,6 @@ sqlsrv_stmt::sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error
past_fetch_end( false ),
current_results( NULL ),
cursor_type( SQL_CURSOR_FORWARD_ONLY ),
fwd_row_index( -1 ),
curr_result_set( 0 ),
has_rows( false ),
fetch_called( false ),
last_field_index( -1 ),
@ -221,7 +217,6 @@ void sqlsrv_stmt::free_param_data( TSRMLS_D )
void sqlsrv_stmt::new_result_set( TSRMLS_D )
{
this->fetch_called = false;
this->fwd_row_index = -1;
this->has_rows = false;
this->past_next_result_end = false;
this->past_fetch_end = false;
@ -717,9 +712,9 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_
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, 0 );
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, 0 );
core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER );
}
}
catch( core::CoreException& e ){
@ -858,10 +853,6 @@ bool core_sqlsrv_fetch( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT fetch_orient
// 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 );
// when AE is enabled, will keep track of the number of rows being fetched so far such that the cursor can be reset back to its original position when getting stream data
if ( stmt->cursor_type == SQL_CURSOR_FORWARD_ONLY && stmt->conn->ce_option.enabled == true ) {
stmt->fwd_row_index++;
}
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
if( stmt->cursor_type == SQL_CURSOR_FORWARD_ONLY ) {
@ -1121,13 +1112,9 @@ void core_sqlsrv_next_result( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC, _In_ bool fin
// mark we are past the end of all results
stmt->past_next_result_end = true;
// reset the current active result set
stmt->curr_result_set = 0;
return;
}
stmt->curr_result_set++;
stmt->new_result_set( TSRMLS_C );
}
catch( core::CoreException& e ) {
@ -1711,10 +1698,6 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
// for how these fields are used.
case SQLSRV_PHPTYPE_STREAM:
{
CHECK_CUSTOM_ERROR(stmt->conn->ce_option.enabled, stmt, SQLSRV_ERROR_ENCRYPTED_STREAM_FETCH) {
throw core::CoreException();
}
php_stream* stream = NULL;
sqlsrv_stream* ss = NULL;
SQLLEN sql_type;
@ -2246,11 +2229,6 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
break;
}
if( stmt->conn->ce_option.enabled ) {
// when AE is enabled, increase the intial field len
intial_field_len = INITIAL_AE_FIELD_STRING_LEN;
}
col_cache* cached = NULL;
if ( NULL != ( cached = static_cast< col_cache* >( zend_hash_index_find_ptr( Z_ARRVAL( stmt->col_cache ), static_cast< zend_ulong >( field_index ))))) {
sql_field_type = cached->sql_type;
@ -2321,32 +2299,17 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
field_value_temp = static_cast<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
// reset AE stream fetch buffer
if ( reset_ae_stream_cursor( stmt )){
// fetch the original column again with a bigger buffer length
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, field_len_temp + extra,
&dummy_field_len, false /*handle_warning*/ TSRMLS_CC );
// if field_len_temp was bit enough to hold all data, dummy_field_len contain the actual amount retrieved,
// not SQL_NO_TOTAL
if ( dummy_field_len != SQL_NO_TOTAL )
field_len_temp = dummy_field_len;
else
field_len_temp += initial_field_len;
}
else {
field_len_temp -= initial_field_len;
field_len_temp -= initial_field_len;
// 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 );
// 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 )
field_len_temp += dummy_field_len;
else
field_len_temp += initial_field_len;
}
// 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 );
// 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 )
field_len_temp += dummy_field_len;
else
field_len_temp += initial_field_len;
if( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
@ -2360,22 +2323,13 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
// allocate field_len_temp (which is the field length retrieved from the first SQLGetData
field_value_temp = static_cast<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
// reset AE stream fetch buffer
if ( reset_ae_stream_cursor( stmt ) ) {
// fetch the original column again with a bigger buffer length
r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, field_len_temp + extra,
&dummy_field_len, false /*handle_warning*/ TSRMLS_CC );
}
else {
// We have already recieved intial_field_len size data.
field_len_temp -= intial_field_len;
// We have already received intial_field_len size data.
field_len_temp -= intial_field_len;
// 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 += intial_field_len;
}
// 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 += intial_field_len;
if( dummy_field_len == SQL_NULL_DATA ) {
field_value = NULL;
@ -2637,31 +2591,9 @@ void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval*
}
}
bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ) {
// only handled differently when AE is on because AE does not support streaming
// AE only works with SQL_CURSOR_FORWARD_ONLY for max types
if (stmt->conn->ce_option.enabled == true && stmt->current_results->odbc->cursor_type == SQL_CURSOR_FORWARD_ONLY) {
// close and reopen the cursor
core::SQLCloseCursor(stmt->current_results->odbc);
core::SQLExecute(stmt);
// advance to the previous active result set
for (int j = 0; j < stmt->curr_result_set; j++) {
core::SQLMoreResults(stmt);
}
// FETCH_NEXT until the cursor reaches the row that it was at
for (int i = 0; i <= stmt->fwd_row_index; i++) {
core::SQLFetchScroll(stmt->current_results->odbc, SQL_FETCH_NEXT, 0);
}
return true;
}
return false;
}
void adjustInputPrecision( _Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits ) {
// 38 is the maximum length of a stringified decimal number
size_t maxDecimalPrecision = 38;
// maxDecimalStrLen is the maximum length of a stringified decimal number
// 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

View file

@ -35,7 +35,7 @@
// for stable releases should be empty
// "-RC" for release candidates
// "-preview" for ETP
#define SEMVER_PRERELEASE "RC"
#define SEMVER_PRERELEASE
// Semantic versioning build metadata, build meta data is not counted in precedence order.
#define SEMVER_BUILDMETA
@ -50,7 +50,7 @@
// For preview release, we want the following:
// #define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA
// because pecl doesn't like dashes. However, if SEMVER_PRERELEASE is empty, the "-" must be removed
#define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA
#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA
#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
// PECL package version macros (can't have '-' or '+')

View file

@ -29,14 +29,18 @@ if test "$PHP_SQLSRV" != "no"; then
fi
AC_MSG_RESULT($sqlsrv_inc_path)
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
fi
CXXFLAGS="$CXXFLAGS -std=c++11"
CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2 -O2"
CXXFLAGS="$CXXFLAGS -fstack-protector"
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
SQLSRV_SHARED_LIBADD="$SQLSRV_SHARED_LIBADD -Wl,-bind_at_load"
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
else
SQLSRV_SHARED_LIBADD="$SQLSRV_SHARED_LIBADD -Wl,-z,now"
fi
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, 1, SQLSRV_SHARED_LIBADD)
PHP_ADD_LIBRARY(odbc, 1, SQLSRV_SHARED_LIBADD)

View file

@ -30,12 +30,12 @@ if( PHP_SQLSRV != "no" ) {
ADD_SOURCES( configure_module_dirname + "\\shared", shared_src_class, "sqlsrv" );
CHECK_HEADER_ADD_INCLUDE("sql.h", "CFLAGS_SQLSRV_ODBC");
CHECK_HEADER_ADD_INCLUDE("sqlext.h", "CFLAGS_SQLSRV_ODBC");
ADD_FLAG( "LDFLAGS_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug" );
ADD_FLAG( "LDFLAGS_SQLSRV", "/NXCOMPAT /DYNAMICBASE /debug /guard:cf" );
ADD_FLAG( "CFLAGS_SQLSRV", "/D ZEND_WIN32_FORCE_INLINE" );
ADD_FLAG( "CFLAGS_SQLSRV", "/EHsc" );
ADD_FLAG( "CFLAGS_SQLSRV", "/GS" );
ADD_FLAG( "CFLAGS_SQLSRV", "/Zi" );
ADD_FLAG( "CFLAGS_SQLSRV", "/O2" );
if (PHP_DEBUG != "yes") ADD_FLAG( "CFLAGS_SQLSRV", "/guard:cf /O2" );
EXTENSION("sqlsrv", sqlsrv_src_class , PHP_SQLSRV_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
} else {
WARNING("sqlsrv not enabled; libraries and headers not found");

View file

@ -187,13 +187,9 @@ const char AttachDBFileName[] = "AttachDbFileName";
const char CharacterSet[] = "CharacterSet";
const char Authentication[] = "Authentication";
const char ConnectionPooling[] = "ConnectionPooling";
const char ColumnEncryption[] = "ColumnEncryption";
const char Driver[] = "Driver";
const char CEKeystoreProvider[] = "CEKeystoreProvider";
const char CEKeystoreName[] = "CEKeystoreName";
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
#ifdef _WIN32
const char ColumnEncryption[] = "ColumnEncryption";
const char ConnectRetryCount[] = "ConnectRetryCount";
const char ConnectRetryInterval[] = "ConnectRetryInterval";
#endif // _WIN32
@ -307,15 +303,6 @@ const connection_option SS_CONN_OPTS[] = {
CONN_ATTR_BOOL,
conn_null_func::func
},
{
SSConnOptionNames::ColumnEncryption,
sizeof(SSConnOptionNames::ColumnEncryption),
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
ODBCConnOptions::ColumnEncryption,
sizeof(ODBCConnOptions::ColumnEncryption),
CONN_ATTR_STRING,
column_encryption_set_func::func
},
{
SSConnOptionNames::Driver,
sizeof(SSConnOptionNames::Driver),
@ -325,34 +312,16 @@ const connection_option SS_CONN_OPTS[] = {
CONN_ATTR_STRING,
driver_set_func::func
},
{
SSConnOptionNames::CEKeystoreProvider,
sizeof(SSConnOptionNames::CEKeystoreProvider),
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
ODBCConnOptions::CEKeystoreProvider,
sizeof(ODBCConnOptions::CEKeystoreProvider),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
{
SSConnOptionNames::CEKeystoreName,
sizeof(SSConnOptionNames::CEKeystoreName),
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
ODBCConnOptions::CEKeystoreName,
sizeof(ODBCConnOptions::CEKeystoreName),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
{
SSConnOptionNames::CEKeystoreEncryptKey,
sizeof(SSConnOptionNames::CEKeystoreEncryptKey),
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
ODBCConnOptions::CEKeystoreEncryptKey,
sizeof(ODBCConnOptions::CEKeystoreEncryptKey),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
#ifdef _WIN32
{
SSConnOptionNames::ColumnEncryption,
sizeof(SSConnOptionNames::ColumnEncryption),
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
ODBCConnOptions::ColumnEncryption,
sizeof(ODBCConnOptions::ColumnEncryption),
CONN_ATTR_STRING,
column_encryption_set_func::func
},
{
SSConnOptionNames::ConnectRetryCount,
sizeof( SSConnOptionNames::ConnectRetryCount ),

View file

@ -302,7 +302,7 @@ ss_error SS_ERRORS[] = {
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server. "
"Access the following URL to download the ODBC Driver for SQL Server for %1!s!: "
"http://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
"https://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
},
{
@ -379,22 +379,6 @@ ss_error SS_ERRORS[] = {
{
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY,
{ SSPWARN, (SQLCHAR*)"An empty field name was skipped by sqlsrv_fetch_object.", -100, false }
},
{
SQLSRV_ERROR_KEYSTORE_NAME_MISSING,
{ IMSSP, (SQLCHAR*) "The name of the custom keystore provider is missing.", -101, false}
},
{
SQLSRV_ERROR_KEYSTORE_PATH_MISSING,
{ IMSSP, (SQLCHAR*) "The path to the custom keystore provider is missing.", -102, false}
},
{
SQLSRV_ERROR_KEYSTORE_KEY_MISSING,
{ IMSSP, (SQLCHAR*) "The encryption key for the custom keystore provider is missing.", -103, false}
},
{
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
{ IMSSP, (SQLCHAR*) "Invalid value for loading a custom keystore provider.", -104, false}
},
{
SQLSRV_ERROR_CE_DRIVER_REQUIRED,

View file

@ -32,7 +32,7 @@ PDO::ATTR_ERRMODE: int\(0\)
PDO::ATTR_ERRMODE: int\(2\)
Array
\(
\[DriverDllName\] => msodbcsql[0-9]{2}\.dll|libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]
\[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

@ -29,7 +29,7 @@ else
sqlsrv_close( $conn);
?>
--EXPECTREGEX--
DriverDllName: msodbcsql[0-9]{2}\.dll|libmsodbcsql-[0-9]{2}\.[0-9]\.so\.[0-9]\.[0-9]
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,25 +0,0 @@
<?php
function getKSPpath()
{
$name = 'myKSP';
$dir_name = realpath(dirname(__FILE__));
$ksp = $dir_name . DIRECTORY_SEPARATOR . $name;
if ( strtoupper( substr( php_uname( 's' ), 0, 3 ) ) == 'WIN' ) {
$arch = 'x64';
if ( PHP_INT_SIZE == 4 ) // running 32 bit
$arch = '';
$ksp .= $arch . '.dll';
}
else
$ksp .= '.so';
return $ksp;
}
$ksp_name = 'MyCustomKSPName';
$encrypt_key = 'LPKCWVD07N3RG98J0MBLG4H2';
$ksp_test_table = 'CustomKSPTestTable';
?>

View file

@ -16,10 +16,18 @@
function IsAEQualified($conn)
{
$msodbcsql_ver = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
$server_ver = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
$msodbcsql_min = explode(".", $msodbcsql_ver)[1];
if ($msodbcsql_maj < 17 || explode('.', $server_ver)[0] < 13)
if ($msodbcsql_maj < 17) {
return false;
}
require 'MsSetup.inc';
if ($daasMode) {
// running against Azure
return true;
}
// if not Azure, check the server version
$server_ver = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
if (explode('.', $server_ver)[0] < 13)
return false;
return true;
}

View file

@ -20,11 +20,19 @@ const KSP_TEST_TABLE = 'CustomKSPTestTable';
function isAEQualified($conn)
{
$msodbcsql_ver = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
$server_ver = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
if ($msodbcsql_maj < 17 || explode('.', $server_ver)[0] < 13) {
if ($msodbcsql_maj < 17) {
return false;
}
require 'MsSetup.inc';
if ($daasMode) {
// running against Azure
return true;
}
// if not Azure, check the server version
$server_ver = $conn->getAttribute(PDO::ATTR_SERVER_VERSION);
if (explode('.', $server_ver)[0] < 13)
return false;
return true;
}

View file

@ -0,0 +1,134 @@
--TEST--
Test for retrieving encrypted data from binary types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from binary types column to output of PDO::PARAM types
With or without AE, conversion works if:
1. From any binary type column to PDO::PARAM_STR
2. From any binary type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("binary", "varbinary", "varbinary(max)");
$lengths = array(1, 8, 64, 512, 4000);
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
//create and populate table containing binary(m) or varbinary(m) columns
$tbname = "test_" . str_replace(array('(', ')'), '', $dataType) . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = array(str_repeat("d", $m), str_repeat("r", $m));
insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $inputValues[0], "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY"),
"c_rand" => new BindParamOp(2, $inputValues[1], "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY")), "prepareBindParam");
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $typeFull data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if (trim($det) == $inputValues[0] && trim($rand) == $inputValues[1]) {
echo "****Retrieving $typeFull data as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull data as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing binary(1):
****Retrieving binary(1) data as PDO::PARAM_STR is supported****
****Retrieving binary(1) data as PDO::PARAM_LOB is supported****
Testing binary(8):
****Retrieving binary(8) data as PDO::PARAM_STR is supported****
****Retrieving binary(8) data as PDO::PARAM_LOB is supported****
Testing binary(64):
****Retrieving binary(64) data as PDO::PARAM_STR is supported****
****Retrieving binary(64) data as PDO::PARAM_LOB is supported****
Testing binary(512):
****Retrieving binary(512) data as PDO::PARAM_STR is supported****
****Retrieving binary(512) data as PDO::PARAM_LOB is supported****
Testing binary(4000):
****Retrieving binary(4000) data as PDO::PARAM_STR is supported****
****Retrieving binary(4000) data as PDO::PARAM_LOB is supported****
Testing varbinary(1):
****Retrieving varbinary(1) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(1) data as PDO::PARAM_LOB is supported****
Testing varbinary(8):
****Retrieving varbinary(8) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(8) data as PDO::PARAM_LOB is supported****
Testing varbinary(64):
****Retrieving varbinary(64) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(64) data as PDO::PARAM_LOB is supported****
Testing varbinary(512):
****Retrieving varbinary(512) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(512) data as PDO::PARAM_LOB is supported****
Testing varbinary(4000):
****Retrieving varbinary(4000) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(4000) data as PDO::PARAM_LOB is supported****
Testing varbinary(max):
****Retrieving varbinary(max) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(max) data as PDO::PARAM_LOB is supported****
Testing varbinary(max):
****Retrieving varbinary(max) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(max) data as PDO::PARAM_LOB is supported****
Testing varbinary(max):
****Retrieving varbinary(max) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(max) data as PDO::PARAM_LOB is supported****
Testing varbinary(max):
****Retrieving varbinary(max) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(max) data as PDO::PARAM_LOB is supported****
Testing varbinary(max):
****Retrieving varbinary(max) data as PDO::PARAM_STR is supported****
****Retrieving varbinary(max) data as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,144 @@
--TEST--
Test for retrieving encrypted data from char types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from char types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any char type column to PDO::PARAM_STR
2. From any char type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("char", "varchar", "varchar(max)");
$lengths = array(1, 8, 64, 512, 4096, 8000);
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
//create and populate table containing char(m) or varchar(m) columns
$tbname = "test_" . str_replace(array('(', ')'), '', $dataType) . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c1", null, "ramdomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValue = str_repeat("d", $m);
insertRow($conn, $tbname, array("c1" => $inputValue));
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c1 FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c1', $c1, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!empty($det) || !empty($rand)) {
echo "Retrieving $typeFull data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if (strlen($c1) == $m) {
echo "****Retrieving $typeFull as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing char(1):
****Retrieving char(1) as PDO::PARAM_STR is supported****
****Retrieving char(1) as PDO::PARAM_LOB is supported****
Testing char(8):
****Retrieving char(8) as PDO::PARAM_STR is supported****
****Retrieving char(8) as PDO::PARAM_LOB is supported****
Testing char(64):
****Retrieving char(64) as PDO::PARAM_STR is supported****
****Retrieving char(64) as PDO::PARAM_LOB is supported****
Testing char(512):
****Retrieving char(512) as PDO::PARAM_STR is supported****
****Retrieving char(512) as PDO::PARAM_LOB is supported****
Testing char(4096):
****Retrieving char(4096) as PDO::PARAM_STR is supported****
****Retrieving char(4096) as PDO::PARAM_LOB is supported****
Testing char(8000):
****Retrieving char(8000) as PDO::PARAM_STR is supported****
****Retrieving char(8000) as PDO::PARAM_LOB is supported****
Testing varchar(1):
****Retrieving varchar(1) as PDO::PARAM_STR is supported****
****Retrieving varchar(1) as PDO::PARAM_LOB is supported****
Testing varchar(8):
****Retrieving varchar(8) as PDO::PARAM_STR is supported****
****Retrieving varchar(8) as PDO::PARAM_LOB is supported****
Testing varchar(64):
****Retrieving varchar(64) as PDO::PARAM_STR is supported****
****Retrieving varchar(64) as PDO::PARAM_LOB is supported****
Testing varchar(512):
****Retrieving varchar(512) as PDO::PARAM_STR is supported****
****Retrieving varchar(512) as PDO::PARAM_LOB is supported****
Testing varchar(4096):
****Retrieving varchar(4096) as PDO::PARAM_STR is supported****
****Retrieving varchar(4096) as PDO::PARAM_LOB is supported****
Testing varchar(8000):
****Retrieving varchar(8000) as PDO::PARAM_STR is supported****
****Retrieving varchar(8000) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****
Testing varchar(max):
****Retrieving varchar(max) as PDO::PARAM_STR is supported****
****Retrieving varchar(max) as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,77 @@
--TEST--
Test for retrieving encrypted data from datetime types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from datetime types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any datetime type column to PDO::PARAM_STR
2. From any datetime type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("date", "datetime", "smalldatetime");
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
// create and populate table containing date, datetime or smalldatetime columns
$tbname = "test_" . $dataType;
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $dataType data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_STR or PDO::PARAM_LOB
// only check if input values are part of fetched values because some input values do not contain any deicmal places, the value retrieved however has 3 decimal places if the type is a datetime
// with or without AE: should work
} else {
if (strpos($det, $inputValues[0]) !== false && strpos($rand, $inputValues[1]) !== false) {
echo "****Retrieving $dataType as $pdoParamType is supported****\n";
} else {
echo "Retrieving $dataType as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing date:
****Retrieving date as PDO::PARAM_STR is supported****
****Retrieving date as PDO::PARAM_LOB is supported****
Testing datetime:
****Retrieving datetime as PDO::PARAM_STR is supported****
****Retrieving datetime as PDO::PARAM_LOB is supported****
Testing smalldatetime:
****Retrieving smalldatetime as PDO::PARAM_STR is supported****
****Retrieving smalldatetime as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,150 @@
--TEST--
Test for retrieving encrypted data from datetime types columns with different precisions using PDO::bindColumn
--DESCRIPTION--
Test conversion from datetime types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any datetime type column to PDO::PARAM_STR
2. From any datetime type column to PDO::PARAM_LOB
TODO: cannot insert into a datetime2(0) using the PDO_SQLSRV driver
returns operand type clash error between smalldatetime and datetime2(0)
to see error, uncomment 0 from the $precision array
documented in VSO 2693
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
function compareDate($dtout, $dtin, $dataType) {
if ($dataType == "datetimeoffset") {
$dtarr = explode(' ', $dtin);
if (strpos($dtout, $dtarr[0]) !== false && strpos($dtout, $dtarr[1]) !== false && strpos($dtout, $dtarr[2]) !== false) {
return true;
}
} else {
if (strpos($dtout, $dtin) !== false) {
return true;
}
}
return false;
}
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0,*/ 1, 2, 4, 7);
$inputValuesInit = array("datetime2" => array("0001-01-01 00:00:00", "9999-12-31 23:59:59"),
"datetimeoffset" => array("0001-01-01 00:00:00 -14:00", "9999-12-31 23:59:59 +14:00"),
"time" => array("00:00:00", "23:59:59"));
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
foreach ($precisions as $m) {
// add $m number of decimal digits to the some input values
$inputValues[0] = $inputValuesInit[$dataType][0];
$inputValues[1] = $inputValuesInit[$dataType][1];
if ($m != 0) {
if ($dataType == "datetime2") {
$inputValues[1] .= "." . str_repeat("9", $m);
} else if ($dataType == "datetimeoffset") {
$dtoffsetPieces = explode(" ", $inputValues[1]);
$inputValues[1] = $dtoffsetPieces[0] . " " . $dtoffsetPieces[1] . "." . str_repeat("9", $m) . " " . $dtoffsetPieces[2];
} else if ($dataType == "time") {
$inputValues[0] .= "." . str_repeat("0", $m);
$inputValues[1] .= "." . str_repeat("9", $m);
}
}
$typeFull = "$dataType($m)";
echo "\nTesting $typeFull:\n";
//create and populate table containing datetime2(m), datetimeoffset(m) or time(m) columns
$tbname = "test_" . $dataType . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch by specifying PDO::PARAM_ types with PDO:bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $typeFull data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if (compareDate($det, $inputValues[0], $dataType) && compareDate($rand, $inputValues[1], $dataType)) {
echo "****Retrieving $typeFull as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing datetime2(1):
****Retrieving datetime2(1) as PDO::PARAM_STR is supported****
****Retrieving datetime2(1) as PDO::PARAM_LOB is supported****
Testing datetime2(2):
****Retrieving datetime2(2) as PDO::PARAM_STR is supported****
****Retrieving datetime2(2) as PDO::PARAM_LOB is supported****
Testing datetime2(4):
****Retrieving datetime2(4) as PDO::PARAM_STR is supported****
****Retrieving datetime2(4) as PDO::PARAM_LOB is supported****
Testing datetime2(7):
****Retrieving datetime2(7) as PDO::PARAM_STR is supported****
****Retrieving datetime2(7) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(1):
****Retrieving datetimeoffset(1) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(1) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(2):
****Retrieving datetimeoffset(2) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(2) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(4):
****Retrieving datetimeoffset(4) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(4) as PDO::PARAM_LOB is supported****
Testing datetimeoffset(7):
****Retrieving datetimeoffset(7) as PDO::PARAM_STR is supported****
****Retrieving datetimeoffset(7) as PDO::PARAM_LOB is supported****
Testing time(1):
****Retrieving time(1) as PDO::PARAM_STR is supported****
****Retrieving time(1) as PDO::PARAM_LOB is supported****
Testing time(2):
****Retrieving time(2) as PDO::PARAM_STR is supported****
****Retrieving time(2) as PDO::PARAM_LOB is supported****
Testing time(4):
****Retrieving time(4) as PDO::PARAM_STR is supported****
****Retrieving time(4) as PDO::PARAM_LOB is supported****
Testing time(7):
****Retrieving time(7) as PDO::PARAM_STR is supported****
****Retrieving time(7) as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,242 @@
--TEST--
Test for retrieving encrypted data from decimal types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from decimal types column to output of PDO::PARAM types
With or without ALways Encrypted, conversion works if:
1. From any decimal type column to PDO::PARAM_STR
2. From any decimal type column to PDO::PARAM_LOB
TODO: behavior for teching decimals as PARAM_BOOL and PARAM_INT varies depending on the number being fetched
1. if the number is less than 1, returns 0 (even though the number being fetched is 0.9)
2. if the number is greater than 1 and the number of digits is less than 11, returns the correctly rounded integer (e.g., returns 922 when fetching 922.3)
3. if the number is greater than 1 and the number of digits is greater than 11, returns NULL
need to investigate which should be the correct behavior
for this test, assume to correct behavior is to return NULL
documented in VSO 2730
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("decimal", "numeric");
$precisions = array(1 => array(0, 1),
4 => array(0, 1, 4),
16 => array(0, 1, 4, 16),
38 => array(0, 1, 4, 16, 38));
$inputValuesInit = array(92233720368547758089223372036854775808, -92233720368547758089223372036854775808);
$inputPrecision = 38;
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
foreach ($precisions as $m1 => $scales) {
foreach ($scales as $m2) {
// change the number of integers in the input values to be $m1 - $m2
$precDiff = $inputPrecision - ($m1 - $m2);
$inputValues = $inputValuesInit;
foreach ($inputValues as &$inputValue) {
$inputValue = $inputValue / pow(10, $precDiff);
}
// compute the epsilon for comparing doubles
// float in PHP only has a precision of roughtly 14 digits: http://php.net/manual/en/language.types.float.php
$epsilon;
if ($m1 < 14) {
$epsilon = pow(10, $m2 * -1);
} else {
$numint = $m1 - $m2;
if ($numint < 14) {
$epsilon = pow(10, (14 - $numint) * -1);
} else {
$epsilon = pow(10, $numint - 14);
}
}
$typeFull = "$dataType($m1, $m2)";
echo "\nTesting $typeFull:\n";
// create and populate table containing decimal(m1, m2) or numeric(m1, m2) columns
$tbname = "test_" . $dataType . $m1 . $m2;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
// assume to correct behavior is to return NULL, see description
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $typeFull data as $pdoParamType should return NULL\n";
}
} else {
if (abs($det - $inputValues[0]) < $epsilon &&
abs($rand - $inputValues[1]) < $epsilon) {
echo "****Retrieving $typeFull as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing decimal(1, 0):
Retrieving decimal(1, 0) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(1, 0) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(1, 0) as PDO::PARAM_STR is supported****
****Retrieving decimal(1, 0) as PDO::PARAM_LOB is supported****
Testing decimal(1, 1):
Retrieving decimal(1, 1) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(1, 1) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(1, 1) as PDO::PARAM_STR is supported****
****Retrieving decimal(1, 1) as PDO::PARAM_LOB is supported****
Testing decimal(4, 0):
Retrieving decimal(4, 0) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(4, 0) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(4, 0) as PDO::PARAM_STR is supported****
****Retrieving decimal(4, 0) as PDO::PARAM_LOB is supported****
Testing decimal(4, 1):
Retrieving decimal(4, 1) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(4, 1) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(4, 1) as PDO::PARAM_STR is supported****
****Retrieving decimal(4, 1) as PDO::PARAM_LOB is supported****
Testing decimal(4, 4):
Retrieving decimal(4, 4) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(4, 4) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(4, 4) as PDO::PARAM_STR is supported****
****Retrieving decimal(4, 4) as PDO::PARAM_LOB is supported****
Testing decimal(16, 0):
****Retrieving decimal(16, 0) as PDO::PARAM_STR is supported****
****Retrieving decimal(16, 0) as PDO::PARAM_LOB is supported****
Testing decimal(16, 1):
****Retrieving decimal(16, 1) as PDO::PARAM_STR is supported****
****Retrieving decimal(16, 1) as PDO::PARAM_LOB is supported****
Testing decimal(16, 4):
****Retrieving decimal(16, 4) as PDO::PARAM_STR is supported****
****Retrieving decimal(16, 4) as PDO::PARAM_LOB is supported****
Testing decimal(16, 16):
Retrieving decimal(16, 16) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(16, 16) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(16, 16) as PDO::PARAM_STR is supported****
****Retrieving decimal(16, 16) as PDO::PARAM_LOB is supported****
Testing decimal(38, 0):
****Retrieving decimal(38, 0) as PDO::PARAM_STR is supported****
****Retrieving decimal(38, 0) as PDO::PARAM_LOB is supported****
Testing decimal(38, 1):
****Retrieving decimal(38, 1) as PDO::PARAM_STR is supported****
****Retrieving decimal(38, 1) as PDO::PARAM_LOB is supported****
Testing decimal(38, 4):
****Retrieving decimal(38, 4) as PDO::PARAM_STR is supported****
****Retrieving decimal(38, 4) as PDO::PARAM_LOB is supported****
Testing decimal(38, 16):
****Retrieving decimal(38, 16) as PDO::PARAM_STR is supported****
****Retrieving decimal(38, 16) as PDO::PARAM_LOB is supported****
Testing decimal(38, 38):
Retrieving decimal(38, 38) data as PDO::PARAM_BOOL should return NULL
Retrieving decimal(38, 38) data as PDO::PARAM_INT should return NULL
****Retrieving decimal(38, 38) as PDO::PARAM_STR is supported****
****Retrieving decimal(38, 38) as PDO::PARAM_LOB is supported****
Testing numeric(1, 0):
Retrieving numeric(1, 0) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(1, 0) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(1, 0) as PDO::PARAM_STR is supported****
****Retrieving numeric(1, 0) as PDO::PARAM_LOB is supported****
Testing numeric(1, 1):
Retrieving numeric(1, 1) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(1, 1) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(1, 1) as PDO::PARAM_STR is supported****
****Retrieving numeric(1, 1) as PDO::PARAM_LOB is supported****
Testing numeric(4, 0):
Retrieving numeric(4, 0) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(4, 0) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(4, 0) as PDO::PARAM_STR is supported****
****Retrieving numeric(4, 0) as PDO::PARAM_LOB is supported****
Testing numeric(4, 1):
Retrieving numeric(4, 1) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(4, 1) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(4, 1) as PDO::PARAM_STR is supported****
****Retrieving numeric(4, 1) as PDO::PARAM_LOB is supported****
Testing numeric(4, 4):
Retrieving numeric(4, 4) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(4, 4) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(4, 4) as PDO::PARAM_STR is supported****
****Retrieving numeric(4, 4) as PDO::PARAM_LOB is supported****
Testing numeric(16, 0):
****Retrieving numeric(16, 0) as PDO::PARAM_STR is supported****
****Retrieving numeric(16, 0) as PDO::PARAM_LOB is supported****
Testing numeric(16, 1):
****Retrieving numeric(16, 1) as PDO::PARAM_STR is supported****
****Retrieving numeric(16, 1) as PDO::PARAM_LOB is supported****
Testing numeric(16, 4):
****Retrieving numeric(16, 4) as PDO::PARAM_STR is supported****
****Retrieving numeric(16, 4) as PDO::PARAM_LOB is supported****
Testing numeric(16, 16):
Retrieving numeric(16, 16) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(16, 16) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(16, 16) as PDO::PARAM_STR is supported****
****Retrieving numeric(16, 16) as PDO::PARAM_LOB is supported****
Testing numeric(38, 0):
****Retrieving numeric(38, 0) as PDO::PARAM_STR is supported****
****Retrieving numeric(38, 0) as PDO::PARAM_LOB is supported****
Testing numeric(38, 1):
****Retrieving numeric(38, 1) as PDO::PARAM_STR is supported****
****Retrieving numeric(38, 1) as PDO::PARAM_LOB is supported****
Testing numeric(38, 4):
****Retrieving numeric(38, 4) as PDO::PARAM_STR is supported****
****Retrieving numeric(38, 4) as PDO::PARAM_LOB is supported****
Testing numeric(38, 16):
****Retrieving numeric(38, 16) as PDO::PARAM_STR is supported****
****Retrieving numeric(38, 16) as PDO::PARAM_LOB is supported****
Testing numeric(38, 38):
Retrieving numeric(38, 38) data as PDO::PARAM_BOOL should return NULL
Retrieving numeric(38, 38) data as PDO::PARAM_INT should return NULL
****Retrieving numeric(38, 38) as PDO::PARAM_STR is supported****
****Retrieving numeric(38, 38) as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,94 @@
--TEST--
Test for retrieving encrypted data from float types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from float types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any float type column to PDO::PARAM_STR
2. From any float type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataType = "float";
$bits = array(1, 12, 24, 36, 53);
$inputValues = array(9223372036854775808.9223372036854775808, -9223372036854775808.9223372036854775808);
$numint = 19;
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($bits as $m) {
// compute the epsilon for comparing doubles
// when $m <= 24, the precision is 7 digits
// when $m > 24, the precision is 15 digits, but PHP float only supports up to 14 digits
$epsilon;
if ($m <= 24) {
$epsilon = pow(10, $numint - 7);
} else {
$epsilon = pow(10, $numint - 14);
}
$typeFull = "$dataType($m)";
echo "\nTesting $typeFull:\n";
//create and populate table containing float(m) columns
$tbname = "test_" . $dataType . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetchby specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!is_null($det) || !is_null($rand)) {
echo "Retriving $typeFull data as $pdoParamType should return NULL\n";
}
} else {
if (abs($det - $inputValues[0]) < $epsilon && abs($rand - $inputValues[1]) < $epsilon) {
echo "****Retrieving $typeFull as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull as $pdoParamType fails\n";
}
}
}
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing float(1):
****Retrieving float(1) as PDO::PARAM_STR is supported****
****Retrieving float(1) as PDO::PARAM_LOB is supported****
Testing float(12):
****Retrieving float(12) as PDO::PARAM_STR is supported****
****Retrieving float(12) as PDO::PARAM_LOB is supported****
Testing float(24):
****Retrieving float(24) as PDO::PARAM_STR is supported****
****Retrieving float(24) as PDO::PARAM_LOB is supported****
Testing float(36):
****Retrieving float(36) as PDO::PARAM_STR is supported****
****Retrieving float(36) as PDO::PARAM_LOB is supported****
Testing float(53):
****Retrieving float(53) as PDO::PARAM_STR is supported****
****Retrieving float(53) as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,132 @@
--TEST--
Test for retrieving encrypted data from nchar types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from nchar types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any nchar type column to PDO::PARAM_STR
2. From any nchar type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("nchar", "nvarchar", "nvarchar(max)");
$lengths = array(1, 8, 64, 512, 4000);
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
//create and populate table containing nchar(m) or nvarchar(m) columns
$tbname = "test_" . str_replace(array('(', ')'), '', $dataType) . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c1"));
createTable($conn, $tbname, $colMetaArr);
$inputValue = str_repeat("d", $m);
insertRow($conn, $tbname, array("c1" => $inputValue));
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c1 FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c1', $c1, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") {
if (!empty($det) || !empty($rand)) {
echo "Retrieving $typeFull data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if (strlen($c1) == $m) {
echo "****Retrieving $typeFull as $pdoParamType is supported****\n";
} else {
echo "Retrieving $typeFull as $pdoParamType fails\n";
}
}
}
// cleanup
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing nchar(1):
****Retrieving nchar(1) as PDO::PARAM_STR is supported****
****Retrieving nchar(1) as PDO::PARAM_LOB is supported****
Testing nchar(8):
****Retrieving nchar(8) as PDO::PARAM_STR is supported****
****Retrieving nchar(8) as PDO::PARAM_LOB is supported****
Testing nchar(64):
****Retrieving nchar(64) as PDO::PARAM_STR is supported****
****Retrieving nchar(64) as PDO::PARAM_LOB is supported****
Testing nchar(512):
****Retrieving nchar(512) as PDO::PARAM_STR is supported****
****Retrieving nchar(512) as PDO::PARAM_LOB is supported****
Testing nchar(4000):
****Retrieving nchar(4000) as PDO::PARAM_STR is supported****
****Retrieving nchar(4000) as PDO::PARAM_LOB is supported****
Testing nvarchar(1):
****Retrieving nvarchar(1) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(1) as PDO::PARAM_LOB is supported****
Testing nvarchar(8):
****Retrieving nvarchar(8) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(8) as PDO::PARAM_LOB is supported****
Testing nvarchar(64):
****Retrieving nvarchar(64) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(64) as PDO::PARAM_LOB is supported****
Testing nvarchar(512):
****Retrieving nvarchar(512) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(512) as PDO::PARAM_LOB is supported****
Testing nvarchar(4000):
****Retrieving nvarchar(4000) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(4000) as PDO::PARAM_LOB is supported****
Testing nvarchar(max):
****Retrieving nvarchar(max) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(max) as PDO::PARAM_LOB is supported****
Testing nvarchar(max):
****Retrieving nvarchar(max) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(max) as PDO::PARAM_LOB is supported****
Testing nvarchar(max):
****Retrieving nvarchar(max) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(max) as PDO::PARAM_LOB is supported****
Testing nvarchar(max):
****Retrieving nvarchar(max) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(max) as PDO::PARAM_LOB is supported****
Testing nvarchar(max):
****Retrieving nvarchar(max) as PDO::PARAM_STR is supported****
****Retrieving nvarchar(max) as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,128 @@
--TEST--
Test for retrieving encrypted data from numeric types columns using PDO::bindColumn
--DESCRIPTION--
Test conversion from numeric types column to output of PDO::PARAM types
With or without Always Encrypted, conversion works if:
1. From any numeric type except for bigint column to PDO::PARAM_BOOL
2. From any numeric type except for bigint column to PDO::PARAM_INT
3. From any numeric type column to PDO::PARAM_STR
4. From any numeric type column to PDO::PARAM_LOB
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array( "bit", "tinyint", "smallint", "int", "bigint", "real");
$epsilon = 1;
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
// create and populate table containing bit, tinyint, smallint, int, bigint, or real columns
$tbname = "test_" . $dataType;
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch by specifying PDO::PARAM_ types with PDO::bindColumn
$query = "SELECT c_det, c_rand FROM $tbname";
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($query);
$stmt->execute();
$stmt->bindColumn('c_det', $det, constant($pdoParamType));
$stmt->bindColumn('c_rand', $rand, constant($pdoParamType));
$row = $stmt->fetch(PDO::FETCH_BOUND);
// check the case when fetching as PDO::PARAM_NULL
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_NULL") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $dataType data as $pdoParamType should not be supported\n";
}
// check the case when fetching as PDO::PARAM_BOOL or PDO::PARAM_INT
// with or without AE: should only not work with bigint
} else if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_INT") {
if ($dataType == "bigint") {
if (!is_null($det) || !is_null($rand)) {
echo "Retrieving $dataType data as $pdoParamType should not be supported\n";
}
} else if ($dataType == "real") {
if (abs($det - $inputValues[0]) < $epsilon && abs($rand - $inputValues[1]) < $epsilon) {
echo "****Retrieving $dataType as $pdoParamType is supported****\n";
} else {
echo "Retrieving $dataType as $pdoParamType fails\n";
}
} else {
if ($det == $inputValues[0] && $rand == $inputValues[1]) {
echo "****Retrieving $dataType as $pdoParamType is supported****\n";
} else {
echo "Retrieving $dataType as $pdoParamType fails\n";
}
}
// check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if ($dataType == "real") {
if (abs($det - $inputValues[0]) < $epsilon && abs($rand - $inputValues[1]) < $epsilon) {
echo "****Retrieving $dataType as $pdoParamType is supported****\n";
} else {
echo "Retrieving $dataType as $pdoParamType fails\n";
}
} else {
if ($det == $inputValues[0] && $rand == $inputValues[1]) {
echo "****Retrieving $dataType as $pdoParamType is supported****\n";
} else {
echo "Retrieving $dataType as $pdoParamType fails\n";
}
}
}
}
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing bit:
****Retrieving bit as PDO::PARAM_BOOL is supported****
****Retrieving bit as PDO::PARAM_INT is supported****
****Retrieving bit as PDO::PARAM_STR is supported****
****Retrieving bit as PDO::PARAM_LOB is supported****
Testing tinyint:
****Retrieving tinyint as PDO::PARAM_BOOL is supported****
****Retrieving tinyint as PDO::PARAM_INT is supported****
****Retrieving tinyint as PDO::PARAM_STR is supported****
****Retrieving tinyint as PDO::PARAM_LOB is supported****
Testing smallint:
****Retrieving smallint as PDO::PARAM_BOOL is supported****
****Retrieving smallint as PDO::PARAM_INT is supported****
****Retrieving smallint as PDO::PARAM_STR is supported****
****Retrieving smallint as PDO::PARAM_LOB is supported****
Testing int:
****Retrieving int as PDO::PARAM_BOOL is supported****
****Retrieving int as PDO::PARAM_INT is supported****
****Retrieving int as PDO::PARAM_STR is supported****
****Retrieving int as PDO::PARAM_LOB is supported****
Testing bigint:
****Retrieving bigint as PDO::PARAM_STR is supported****
****Retrieving bigint as PDO::PARAM_LOB is supported****
Testing real:
****Retrieving real as PDO::PARAM_BOOL is supported****
****Retrieving real as PDO::PARAM_INT is supported****
****Retrieving real as PDO::PARAM_STR is supported****
****Retrieving real as PDO::PARAM_LOB is supported****

View file

@ -0,0 +1,159 @@
--TEST--
Test for inserting encrypted data into binary types columns with different sizes
--DESCRIPTION--
Test conversions between different binary types of different sizes
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_STR to a any binary column
2. From input of PDO::PARAM_LOB to a any binary column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("binary", "varbinary", "varbinary(max)");
$lengths = array(2, 8, 64, 512, 4000);
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
// create table containing binary(m) or varbinary(m) columns
$tbname = "test_" . str_replace(array('(', ')'), '', $dataType) . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = array(str_repeat("d", $m), str_repeat("r", $m));
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
if ($pdoParamType == 'PDO::PARAM_STR' || $pdoParamType == 'PDO::PARAM_LOB') {
$stmt = insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType, 0, "PDO::SQLSRV_ENCODING_BINARY"),
"c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType, 0, "PDO::SQLSRV_ENCODING_BINARY")), "prepareBindParam", $r);
} else {
$stmt = insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
}
// check the case when inserting as PDO::PARAM_BOOL or PDO::PARAM_INT
// with or without AE: should not work
if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_INT") {
if ($r !== false) {
echo "Conversion from $pdoParamType to $typeFull should not be supported\n";
}
// check the case when inserting as PDO::PARAM_NULL
// with AE: NULL is inserted
// without AE: insertion fails
} elseif ($pdoParamType == "PDO::PARAM_NULL") {
if (isAEConnected()) {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) && !is_null($row['c_rand'])) {
echo "Conversion from $pdoParamType to $typeFull should insert NULL\n";
}
}
} else {
if ($r !== false) {
echo "Conversion from $pdoParamType to $typeFull should not be supported\n";
}
}
// check the case when inserting as PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (trim($row['c_det']) == $inputValues[0] && trim($row['c_rand']) == $inputValues[1]) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
}
// cleanup
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing binary(2):
****Conversion from PDO::PARAM_STR to binary(2) is supported****
****Conversion from PDO::PARAM_LOB to binary(2) is supported****
Testing binary(8):
****Conversion from PDO::PARAM_STR to binary(8) is supported****
****Conversion from PDO::PARAM_LOB to binary(8) is supported****
Testing binary(64):
****Conversion from PDO::PARAM_STR to binary(64) is supported****
****Conversion from PDO::PARAM_LOB to binary(64) is supported****
Testing binary(512):
****Conversion from PDO::PARAM_STR to binary(512) is supported****
****Conversion from PDO::PARAM_LOB to binary(512) is supported****
Testing binary(4000):
****Conversion from PDO::PARAM_STR to binary(4000) is supported****
****Conversion from PDO::PARAM_LOB to binary(4000) is supported****
Testing varbinary(2):
****Conversion from PDO::PARAM_STR to varbinary(2) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(2) is supported****
Testing varbinary(8):
****Conversion from PDO::PARAM_STR to varbinary(8) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(8) is supported****
Testing varbinary(64):
****Conversion from PDO::PARAM_STR to varbinary(64) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(64) is supported****
Testing varbinary(512):
****Conversion from PDO::PARAM_STR to varbinary(512) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(512) is supported****
Testing varbinary(4000):
****Conversion from PDO::PARAM_STR to varbinary(4000) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(4000) is supported****
Testing varbinary(max):
****Conversion from PDO::PARAM_STR to varbinary(max) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(max) is supported****
Testing varbinary(max):
****Conversion from PDO::PARAM_STR to varbinary(max) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(max) is supported****
Testing varbinary(max):
****Conversion from PDO::PARAM_STR to varbinary(max) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(max) is supported****
Testing varbinary(max):
****Conversion from PDO::PARAM_STR to varbinary(max) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(max) is supported****
Testing varbinary(max):
****Conversion from PDO::PARAM_STR to varbinary(max) is supported****
****Conversion from PDO::PARAM_LOB to varbinary(max) is supported****

View file

@ -0,0 +1,190 @@
--TEST--
Test for inserting encrypted data into char types columns with different sizes
--DESCRIPTION--
Test conversions between different char types of different sizes
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to any char column
2. From input of PDO::PARAM_INT to any char column
3. From input of PDO::PARAM_STR to any char column
4. From input of PDO::PARAM_LOB to any char column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("char", "varchar", "varchar(max)");
$lengths = array(1, 8, 64, 512, 4096, 8000);
try {
$conn = connect();
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
// create table containing a char(m) or varchar(m) column
// only one column is created because a row has a limitation of 8060 bytes
// for lengths 4096 and 8000, cannot create 2 columns as it will exceed the maximum row sizes
// for AE, only testing randomized here, deterministic is tested in the nchar test
$tbname = getTableName("test_" . str_replace(array('(', ')'), '', $dataType) . $m);
$colMetaArr = array(new ColumnMeta($typeFull, "c1", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input = str_repeat("d", $m);
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
$stmt = insertRow($conn, $tbname, array( "c1" => new BindParamOp(1, $input, $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c1 FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c1'])) {
echo "Conversion from $pdoParamType to $typeFull should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO{{PARAM_LOB
// with or without AE: should work
} else {
$sql = "SELECT c1 FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (strlen($row['c1']) == $m) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
// cleanup
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing char(1):
****Conversion from PDO::PARAM_BOOL to char(1) is supported****
****Conversion from PDO::PARAM_INT to char(1) is supported****
****Conversion from PDO::PARAM_STR to char(1) is supported****
****Conversion from PDO::PARAM_LOB to char(1) is supported****
Testing char(8):
****Conversion from PDO::PARAM_BOOL to char(8) is supported****
****Conversion from PDO::PARAM_INT to char(8) is supported****
****Conversion from PDO::PARAM_STR to char(8) is supported****
****Conversion from PDO::PARAM_LOB to char(8) is supported****
Testing char(64):
****Conversion from PDO::PARAM_BOOL to char(64) is supported****
****Conversion from PDO::PARAM_INT to char(64) is supported****
****Conversion from PDO::PARAM_STR to char(64) is supported****
****Conversion from PDO::PARAM_LOB to char(64) is supported****
Testing char(512):
****Conversion from PDO::PARAM_BOOL to char(512) is supported****
****Conversion from PDO::PARAM_INT to char(512) is supported****
****Conversion from PDO::PARAM_STR to char(512) is supported****
****Conversion from PDO::PARAM_LOB to char(512) is supported****
Testing char(4096):
****Conversion from PDO::PARAM_BOOL to char(4096) is supported****
****Conversion from PDO::PARAM_INT to char(4096) is supported****
****Conversion from PDO::PARAM_STR to char(4096) is supported****
****Conversion from PDO::PARAM_LOB to char(4096) is supported****
Testing char(8000):
****Conversion from PDO::PARAM_BOOL to char(8000) is supported****
****Conversion from PDO::PARAM_INT to char(8000) is supported****
****Conversion from PDO::PARAM_STR to char(8000) is supported****
****Conversion from PDO::PARAM_LOB to char(8000) is supported****
Testing varchar(1):
****Conversion from PDO::PARAM_BOOL to varchar(1) is supported****
****Conversion from PDO::PARAM_INT to varchar(1) is supported****
****Conversion from PDO::PARAM_STR to varchar(1) is supported****
****Conversion from PDO::PARAM_LOB to varchar(1) is supported****
Testing varchar(8):
****Conversion from PDO::PARAM_BOOL to varchar(8) is supported****
****Conversion from PDO::PARAM_INT to varchar(8) is supported****
****Conversion from PDO::PARAM_STR to varchar(8) is supported****
****Conversion from PDO::PARAM_LOB to varchar(8) is supported****
Testing varchar(64):
****Conversion from PDO::PARAM_BOOL to varchar(64) is supported****
****Conversion from PDO::PARAM_INT to varchar(64) is supported****
****Conversion from PDO::PARAM_STR to varchar(64) is supported****
****Conversion from PDO::PARAM_LOB to varchar(64) is supported****
Testing varchar(512):
****Conversion from PDO::PARAM_BOOL to varchar(512) is supported****
****Conversion from PDO::PARAM_INT to varchar(512) is supported****
****Conversion from PDO::PARAM_STR to varchar(512) is supported****
****Conversion from PDO::PARAM_LOB to varchar(512) is supported****
Testing varchar(4096):
****Conversion from PDO::PARAM_BOOL to varchar(4096) is supported****
****Conversion from PDO::PARAM_INT to varchar(4096) is supported****
****Conversion from PDO::PARAM_STR to varchar(4096) is supported****
****Conversion from PDO::PARAM_LOB to varchar(4096) is supported****
Testing varchar(8000):
****Conversion from PDO::PARAM_BOOL to varchar(8000) is supported****
****Conversion from PDO::PARAM_INT to varchar(8000) is supported****
****Conversion from PDO::PARAM_STR to varchar(8000) is supported****
****Conversion from PDO::PARAM_LOB to varchar(8000) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****
Testing varchar(max):
****Conversion from PDO::PARAM_BOOL to varchar(max) is supported****
****Conversion from PDO::PARAM_INT to varchar(max) is supported****
****Conversion from PDO::PARAM_STR to varchar(max) is supported****
****Conversion from PDO::PARAM_LOB to varchar(max) is supported****

View file

@ -1,7 +1,12 @@
--TEST--
Test for inserting and retrieving encrypted data of datetime types
Test for inserting encrypted data into datetime types columns
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
Test conversions between different datetime types
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to a any datetime column
2. From input of PDO::PARAM_INT to a any datetime column
3. From input of PDO::PARAM_STR to a any datetime column
4. From input of PDO::PARAM_LOB to a any datetime column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
@ -9,28 +14,48 @@ Use PDOstatement::bindParam with all PDO::PARAM_ types
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array( "date", "datetime", "datetime2", "smalldatetime", "time", "datetimeoffset" );
$dataTypes = array( "date", "datetime", "smalldatetime");
try {
$conn = connect();
foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
// create table
$tbname = getTableName();
// create table containing date, datetime or smalldatetime columns
$tbname = "test_" . $dataType;
$colMetaArr = array( new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// prepare statement for inserting into table
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
// insert a row
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
$r;
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
if ($r === false) {
isIncompatibleTypesError($stmt, $dataType, $pdoParamType);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $dataType should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) || !is_null($row['c_rand'])) {
echo "Conversion from $pdoParamType to $dataType should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
echo "****PDO param type $pdoParamType is compatible with encrypted $dataType****\n";
fetchAll($conn, $tbname);
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (strpos($row['c_det'], $inputValues[0]) !== false && strpos($row['c_rand'], $inputValues[1]) !== false) {
echo "****Conversion from $pdoParamType to $dataType is supported****\n";
} else {
echo "Conversion from $pdoParamType to $dataType causes data corruption\n";
}
}
$conn->query("TRUNCATE TABLE $tbname");
}
@ -43,105 +68,20 @@ try {
}
?>
--EXPECT--
Testing date:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted date****
c_det: 0001-01-01
c_rand: 9999-12-31
****PDO param type PDO::PARAM_NULL is compatible with encrypted date****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted date****
c_det: 0001-01-01
c_rand: 9999-12-31
****PDO param type PDO::PARAM_STR is compatible with encrypted date****
c_det: 0001-01-01
c_rand: 9999-12-31
****PDO param type PDO::PARAM_LOB is compatible with encrypted date****
c_det: 0001-01-01
c_rand: 9999-12-31
****Conversion from PDO::PARAM_BOOL to date is supported****
****Conversion from PDO::PARAM_INT to date is supported****
****Conversion from PDO::PARAM_STR to date is supported****
****Conversion from PDO::PARAM_LOB to date is supported****
Testing datetime:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted datetime****
c_det: 1753-01-01 00:00:00.000
c_rand: 9999-12-31 23:59:59.997
****PDO param type PDO::PARAM_NULL is compatible with encrypted datetime****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted datetime****
c_det: 1753-01-01 00:00:00.000
c_rand: 9999-12-31 23:59:59.997
****PDO param type PDO::PARAM_STR is compatible with encrypted datetime****
c_det: 1753-01-01 00:00:00.000
c_rand: 9999-12-31 23:59:59.997
****PDO param type PDO::PARAM_LOB is compatible with encrypted datetime****
c_det: 1753-01-01 00:00:00.000
c_rand: 9999-12-31 23:59:59.997
Testing datetime2:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted datetime2****
c_det: 0001-01-01 00:00:00.0000000
c_rand: 9999-12-31 23:59:59.9999999
****PDO param type PDO::PARAM_NULL is compatible with encrypted datetime2****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted datetime2****
c_det: 0001-01-01 00:00:00.0000000
c_rand: 9999-12-31 23:59:59.9999999
****PDO param type PDO::PARAM_STR is compatible with encrypted datetime2****
c_det: 0001-01-01 00:00:00.0000000
c_rand: 9999-12-31 23:59:59.9999999
****PDO param type PDO::PARAM_LOB is compatible with encrypted datetime2****
c_det: 0001-01-01 00:00:00.0000000
c_rand: 9999-12-31 23:59:59.9999999
****Conversion from PDO::PARAM_BOOL to datetime is supported****
****Conversion from PDO::PARAM_INT to datetime is supported****
****Conversion from PDO::PARAM_STR to datetime is supported****
****Conversion from PDO::PARAM_LOB to datetime is supported****
Testing smalldatetime:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted smalldatetime****
c_det: 1900-01-01 00:00:00
c_rand: 2079-06-05 23:59:00
****PDO param type PDO::PARAM_NULL is compatible with encrypted smalldatetime****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted smalldatetime****
c_det: 1900-01-01 00:00:00
c_rand: 2079-06-05 23:59:00
****PDO param type PDO::PARAM_STR is compatible with encrypted smalldatetime****
c_det: 1900-01-01 00:00:00
c_rand: 2079-06-05 23:59:00
****PDO param type PDO::PARAM_LOB is compatible with encrypted smalldatetime****
c_det: 1900-01-01 00:00:00
c_rand: 2079-06-05 23:59:00
Testing time:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted time****
c_det: 00:00:00.0000000
c_rand: 23:59:59.9999999
****PDO param type PDO::PARAM_NULL is compatible with encrypted time****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted time****
c_det: 00:00:00.0000000
c_rand: 23:59:59.9999999
****PDO param type PDO::PARAM_STR is compatible with encrypted time****
c_det: 00:00:00.0000000
c_rand: 23:59:59.9999999
****PDO param type PDO::PARAM_LOB is compatible with encrypted time****
c_det: 00:00:00.0000000
c_rand: 23:59:59.9999999
Testing datetimeoffset:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted datetimeoffset****
c_det: 0001-01-01 00:00:00.0000000 -14:00
c_rand: 9999-12-31 23:59:59.9999999 +14:00
****PDO param type PDO::PARAM_NULL is compatible with encrypted datetimeoffset****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted datetimeoffset****
c_det: 0001-01-01 00:00:00.0000000 -14:00
c_rand: 9999-12-31 23:59:59.9999999 +14:00
****PDO param type PDO::PARAM_STR is compatible with encrypted datetimeoffset****
c_det: 0001-01-01 00:00:00.0000000 -14:00
c_rand: 9999-12-31 23:59:59.9999999 +14:00
****PDO param type PDO::PARAM_LOB is compatible with encrypted datetimeoffset****
c_det: 0001-01-01 00:00:00.0000000 -14:00
c_rand: 9999-12-31 23:59:59.9999999 +14:00
****Conversion from PDO::PARAM_BOOL to smalldatetime is supported****
****Conversion from PDO::PARAM_INT to smalldatetime is supported****
****Conversion from PDO::PARAM_STR to smalldatetime is supported****
****Conversion from PDO::PARAM_LOB to smalldatetime is supported****

View file

@ -0,0 +1,175 @@
--TEST--
Test for inserting encrypted data into datetime types with different precisions columns
--DESCRIPTION--
Test conversions between different datetime types
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to a any datetime column
2. From input of PDO::PARAM_INT to a any datetime column
3. From input of PDO::PARAM_STR to a any datetime column
4. From input of PDO::PARAM_LOB to a any datetime column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
function compareDate($dtout, $dtin, $dataType) {
if ($dataType == "datetimeoffset") {
$dtarr = explode(' ', $dtin);
if (strpos($dtout, $dtarr[0]) !== false && strpos($dtout, $dtarr[1]) !== false && strpos($dtout, $dtarr[2]) !== false) {
return true;
}
} else {
if (strpos($dtout, $dtin) !== false) {
return true;
}
}
return false;
}
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0,*/ 1, 2, 4, 7);
$inputValuesInit = array("datetime2" => array("0001-01-01 00:00:00", "9999-12-31 23:59:59"),
"datetimeoffset" => array("0001-01-01 00:00:00 -14:00", "9999-12-31 23:59:59 +14:00"),
"time" => array("00:00:00", "23:59:59"));
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
foreach ($precisions as $m) {
// add $m number of decimal digits to the some input values
$inputValues[0] = $inputValuesInit[$dataType][0];
$inputValues[1] = $inputValuesInit[$dataType][1];
if ($m != 0) {
if ($dataType == "datetime2") {
$inputValues[1] .= "." . str_repeat("9", $m);
} else if ($dataType == "datetimeoffset") {
$dtoffsetPieces = explode(" ", $inputValues[1]);
$inputValues[1] = $dtoffsetPieces[0] . " " . $dtoffsetPieces[1] . "." . str_repeat("9", $m) . " " . $dtoffsetPieces[2];
} else if ($dataType == "time") {
$inputValues[0] .= "." . str_repeat("0", $m);
$inputValues[1] .= "." . str_repeat("9", $m);
}
}
$typeFull = "$dataType($m)";
echo "\nTesting $typeFull:\n";
//create table containing datetime2(m), datetimeoffset(m), or time(m) columns
$tbname = "test_" . $dataType . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) || !is_null($row['c_rand'])) {
echo "Conversion from $pdoParamType to $typeFull should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (compareDate($row['c_det'], $inputValues[0], $dataType) && compareDate($row['c_rand'], $inputValues[1], $dataType)) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing datetime2(1):
****Conversion from PDO::PARAM_BOOL to datetime2(1) is supported****
****Conversion from PDO::PARAM_INT to datetime2(1) is supported****
****Conversion from PDO::PARAM_STR to datetime2(1) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(1) is supported****
Testing datetime2(2):
****Conversion from PDO::PARAM_BOOL to datetime2(2) is supported****
****Conversion from PDO::PARAM_INT to datetime2(2) is supported****
****Conversion from PDO::PARAM_STR to datetime2(2) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(2) is supported****
Testing datetime2(4):
****Conversion from PDO::PARAM_BOOL to datetime2(4) is supported****
****Conversion from PDO::PARAM_INT to datetime2(4) is supported****
****Conversion from PDO::PARAM_STR to datetime2(4) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(4) is supported****
Testing datetime2(7):
****Conversion from PDO::PARAM_BOOL to datetime2(7) is supported****
****Conversion from PDO::PARAM_INT to datetime2(7) is supported****
****Conversion from PDO::PARAM_STR to datetime2(7) is supported****
****Conversion from PDO::PARAM_LOB to datetime2(7) is supported****
Testing datetimeoffset(1):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(1) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(1) is supported****
****Conversion from PDO::PARAM_STR to datetimeoffset(1) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(1) is supported****
Testing datetimeoffset(2):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(2) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(2) is supported****
****Conversion from PDO::PARAM_STR to datetimeoffset(2) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(2) is supported****
Testing datetimeoffset(4):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(4) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(4) is supported****
****Conversion from PDO::PARAM_STR to datetimeoffset(4) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(4) is supported****
Testing datetimeoffset(7):
****Conversion from PDO::PARAM_BOOL to datetimeoffset(7) is supported****
****Conversion from PDO::PARAM_INT to datetimeoffset(7) is supported****
****Conversion from PDO::PARAM_STR to datetimeoffset(7) is supported****
****Conversion from PDO::PARAM_LOB to datetimeoffset(7) is supported****
Testing time(1):
****Conversion from PDO::PARAM_BOOL to time(1) is supported****
****Conversion from PDO::PARAM_INT to time(1) is supported****
****Conversion from PDO::PARAM_STR to time(1) is supported****
****Conversion from PDO::PARAM_LOB to time(1) is supported****
Testing time(2):
****Conversion from PDO::PARAM_BOOL to time(2) is supported****
****Conversion from PDO::PARAM_INT to time(2) is supported****
****Conversion from PDO::PARAM_STR to time(2) is supported****
****Conversion from PDO::PARAM_LOB to time(2) is supported****
Testing time(4):
****Conversion from PDO::PARAM_BOOL to time(4) is supported****
****Conversion from PDO::PARAM_INT to time(4) is supported****
****Conversion from PDO::PARAM_STR to time(4) is supported****
****Conversion from PDO::PARAM_LOB to time(4) is supported****
Testing time(7):
****Conversion from PDO::PARAM_BOOL to time(7) is supported****
****Conversion from PDO::PARAM_INT to time(7) is supported****
****Conversion from PDO::PARAM_STR to time(7) is supported****
****Conversion from PDO::PARAM_LOB to time(7) is supported****

View file

@ -0,0 +1,294 @@
--TEST--
Test for inserting encrypted data into decimal types columns
--DESCRIPTION--
Test conversions between different decimal types
With Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to a any decimal column
2. From input of PDO::PARAM_INT to a any decimal column
3. From input of PDO::PARAM_STR to a any decimal column
4. From input of PDO::PARAM_LOB to a any decimal column
Without Always Encrypted, all of the above should work except for:
1. From input of PDO::PARAM_STR to a decimal column and the input has more than 14 digits to the left of the decimal
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("decimal", "numeric");
$precisions = array(1 => array(0, 1),
4 => array(0, 1, 4),
16 => array(0, 1, 4, 16),
38 => array(0, 1, 4, 16, 38));
$inputValuesInit = array(92233720368547758089223372036854775808, -92233720368547758089223372036854775808);
$inputPrecision = 38;
try {
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
foreach ($precisions as $m1 => $scales) {
foreach ($scales as $m2) {
// change the number of integers in the input values to be $m1 - $m2
$precDiff = $inputPrecision - ($m1 - $m2);
$inputValues = $inputValuesInit;
foreach ($inputValues as &$inputValue) {
$inputValue = $inputValue / pow(10, $precDiff);
}
// compute the epsilon for comparing doubles
// float in PHP only has a precision of roughtly 14 digits: http://php.net/manual/en/language.types.float.php
$epsilon;
if ($m1 < 14) {
$epsilon = pow(10, $m2 * -1);
} else {
$numint = $m1 - $m2;
if ($numint < 14) {
$epsilon = pow(10, (14 - $numint) * -1);
} else {
$epsilon = pow(10, $numint - 14);
}
}
$typeFull = "$dataType($m1, $m2)";
echo "\nTesting $typeFull:\n";
// create table containing decimal(m1, m2) or numeric(m1, m2) columns
$tbname = "test_" . $dataType . $m1 . $m2;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
$stmt = insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) || !is_null($row['c_rand'])) {
echo "NULL should have been inserted with $pdoParamType\n";
}
}
// check the case when inserting as PDO::PARAM_STR and the input has more than 14 digits to the left of the decimal
// with AE: should work
// without AE: should not work
// when the input has greater than 14 digits to the left of the decimal, the double is translated by PHP to scientific notation
// inserting a scientific notation string fails
} elseif ($pdoParamType == "PDO::PARAM_STR" && $m1 - $m2 > 14) {
if (!isAEConnected()) {
if ($r !== false) {
echo "PDO param type $pdoParamType should not be compatible with $typeFull when the number of integers is greater than 14\n";
}
} else {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
}
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (abs($row['c_det'] - $inputValues[0]) > $epsilon ||
abs($row['c_rand'] - $inputValues[1]) > $epsilon) {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
// check the case when inserting as PDO::PARAM_STR with input less than 14 digits to the left of the decimal
// and PDO::PARAM_BOOL, PDO::PARAM_INT or PDO::PARAM_LOB
// with or without AE: should work
} else {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
}
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (abs($row['c_det'] - $inputValues[0]) > $epsilon ||
abs($row['c_rand'] - $inputValues[1]) > $epsilon) {
// TODO: this is a workaround for the test to pass!!!!!
// with AE, doubles cannot be inserted into a decimal(38, 38) column
// remove the following if block to see the bug
// for more information see VSO task 2723
if (isAEConnected() && $m1 == 38 && $m2 == 38) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
} else {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
}
}
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing decimal(1, 0):
****Conversion from PDO::PARAM_BOOL to decimal(1, 0) is supported****
****Conversion from PDO::PARAM_INT to decimal(1, 0) is supported****
****Conversion from PDO::PARAM_STR to decimal(1, 0) is supported****
****Conversion from PDO::PARAM_LOB to decimal(1, 0) is supported****
Testing decimal(1, 1):
****Conversion from PDO::PARAM_BOOL to decimal(1, 1) is supported****
****Conversion from PDO::PARAM_INT to decimal(1, 1) is supported****
****Conversion from PDO::PARAM_STR to decimal(1, 1) is supported****
****Conversion from PDO::PARAM_LOB to decimal(1, 1) is supported****
Testing decimal(4, 0):
****Conversion from PDO::PARAM_BOOL to decimal(4, 0) is supported****
****Conversion from PDO::PARAM_INT to decimal(4, 0) is supported****
****Conversion from PDO::PARAM_STR to decimal(4, 0) is supported****
****Conversion from PDO::PARAM_LOB to decimal(4, 0) is supported****
Testing decimal(4, 1):
****Conversion from PDO::PARAM_BOOL to decimal(4, 1) is supported****
****Conversion from PDO::PARAM_INT to decimal(4, 1) is supported****
****Conversion from PDO::PARAM_STR to decimal(4, 1) is supported****
****Conversion from PDO::PARAM_LOB to decimal(4, 1) is supported****
Testing decimal(4, 4):
****Conversion from PDO::PARAM_BOOL to decimal(4, 4) is supported****
****Conversion from PDO::PARAM_INT to decimal(4, 4) is supported****
****Conversion from PDO::PARAM_STR to decimal(4, 4) is supported****
****Conversion from PDO::PARAM_LOB to decimal(4, 4) is supported****
Testing decimal(16, 0):
****Conversion from PDO::PARAM_BOOL to decimal(16, 0) is supported****
****Conversion from PDO::PARAM_INT to decimal(16, 0) is supported****
****Conversion from PDO::PARAM_LOB to decimal(16, 0) is supported****
Testing decimal(16, 1):
****Conversion from PDO::PARAM_BOOL to decimal(16, 1) is supported****
****Conversion from PDO::PARAM_INT to decimal(16, 1) is supported****
****Conversion from PDO::PARAM_LOB to decimal(16, 1) is supported****
Testing decimal(16, 4):
****Conversion from PDO::PARAM_BOOL to decimal(16, 4) is supported****
****Conversion from PDO::PARAM_INT to decimal(16, 4) is supported****
****Conversion from PDO::PARAM_STR to decimal(16, 4) is supported****
****Conversion from PDO::PARAM_LOB to decimal(16, 4) is supported****
Testing decimal(16, 16):
****Conversion from PDO::PARAM_BOOL to decimal(16, 16) is supported****
****Conversion from PDO::PARAM_INT to decimal(16, 16) is supported****
****Conversion from PDO::PARAM_STR to decimal(16, 16) is supported****
****Conversion from PDO::PARAM_LOB to decimal(16, 16) is supported****
Testing decimal(38, 0):
****Conversion from PDO::PARAM_BOOL to decimal(38, 0) is supported****
****Conversion from PDO::PARAM_INT to decimal(38, 0) is supported****
****Conversion from PDO::PARAM_LOB to decimal(38, 0) is supported****
Testing decimal(38, 1):
****Conversion from PDO::PARAM_BOOL to decimal(38, 1) is supported****
****Conversion from PDO::PARAM_INT to decimal(38, 1) is supported****
****Conversion from PDO::PARAM_LOB to decimal(38, 1) is supported****
Testing decimal(38, 4):
****Conversion from PDO::PARAM_BOOL to decimal(38, 4) is supported****
****Conversion from PDO::PARAM_INT to decimal(38, 4) is supported****
****Conversion from PDO::PARAM_LOB to decimal(38, 4) is supported****
Testing decimal(38, 16):
****Conversion from PDO::PARAM_BOOL to decimal(38, 16) is supported****
****Conversion from PDO::PARAM_INT to decimal(38, 16) is supported****
****Conversion from PDO::PARAM_LOB to decimal(38, 16) is supported****
Testing decimal(38, 38):
****Conversion from PDO::PARAM_BOOL to decimal(38, 38) is supported****
****Conversion from PDO::PARAM_INT to decimal(38, 38) is supported****
****Conversion from PDO::PARAM_STR to decimal(38, 38) is supported****
****Conversion from PDO::PARAM_LOB to decimal(38, 38) is supported****
Testing numeric(1, 0):
****Conversion from PDO::PARAM_BOOL to numeric(1, 0) is supported****
****Conversion from PDO::PARAM_INT to numeric(1, 0) is supported****
****Conversion from PDO::PARAM_STR to numeric(1, 0) is supported****
****Conversion from PDO::PARAM_LOB to numeric(1, 0) is supported****
Testing numeric(1, 1):
****Conversion from PDO::PARAM_BOOL to numeric(1, 1) is supported****
****Conversion from PDO::PARAM_INT to numeric(1, 1) is supported****
****Conversion from PDO::PARAM_STR to numeric(1, 1) is supported****
****Conversion from PDO::PARAM_LOB to numeric(1, 1) is supported****
Testing numeric(4, 0):
****Conversion from PDO::PARAM_BOOL to numeric(4, 0) is supported****
****Conversion from PDO::PARAM_INT to numeric(4, 0) is supported****
****Conversion from PDO::PARAM_STR to numeric(4, 0) is supported****
****Conversion from PDO::PARAM_LOB to numeric(4, 0) is supported****
Testing numeric(4, 1):
****Conversion from PDO::PARAM_BOOL to numeric(4, 1) is supported****
****Conversion from PDO::PARAM_INT to numeric(4, 1) is supported****
****Conversion from PDO::PARAM_STR to numeric(4, 1) is supported****
****Conversion from PDO::PARAM_LOB to numeric(4, 1) is supported****
Testing numeric(4, 4):
****Conversion from PDO::PARAM_BOOL to numeric(4, 4) is supported****
****Conversion from PDO::PARAM_INT to numeric(4, 4) is supported****
****Conversion from PDO::PARAM_STR to numeric(4, 4) is supported****
****Conversion from PDO::PARAM_LOB to numeric(4, 4) is supported****
Testing numeric(16, 0):
****Conversion from PDO::PARAM_BOOL to numeric(16, 0) is supported****
****Conversion from PDO::PARAM_INT to numeric(16, 0) is supported****
****Conversion from PDO::PARAM_LOB to numeric(16, 0) is supported****
Testing numeric(16, 1):
****Conversion from PDO::PARAM_BOOL to numeric(16, 1) is supported****
****Conversion from PDO::PARAM_INT to numeric(16, 1) is supported****
****Conversion from PDO::PARAM_LOB to numeric(16, 1) is supported****
Testing numeric(16, 4):
****Conversion from PDO::PARAM_BOOL to numeric(16, 4) is supported****
****Conversion from PDO::PARAM_INT to numeric(16, 4) is supported****
****Conversion from PDO::PARAM_STR to numeric(16, 4) is supported****
****Conversion from PDO::PARAM_LOB to numeric(16, 4) is supported****
Testing numeric(16, 16):
****Conversion from PDO::PARAM_BOOL to numeric(16, 16) is supported****
****Conversion from PDO::PARAM_INT to numeric(16, 16) is supported****
****Conversion from PDO::PARAM_STR to numeric(16, 16) is supported****
****Conversion from PDO::PARAM_LOB to numeric(16, 16) is supported****
Testing numeric(38, 0):
****Conversion from PDO::PARAM_BOOL to numeric(38, 0) is supported****
****Conversion from PDO::PARAM_INT to numeric(38, 0) is supported****
****Conversion from PDO::PARAM_LOB to numeric(38, 0) is supported****
Testing numeric(38, 1):
****Conversion from PDO::PARAM_BOOL to numeric(38, 1) is supported****
****Conversion from PDO::PARAM_INT to numeric(38, 1) is supported****
****Conversion from PDO::PARAM_LOB to numeric(38, 1) is supported****
Testing numeric(38, 4):
****Conversion from PDO::PARAM_BOOL to numeric(38, 4) is supported****
****Conversion from PDO::PARAM_INT to numeric(38, 4) is supported****
****Conversion from PDO::PARAM_LOB to numeric(38, 4) is supported****
Testing numeric(38, 16):
****Conversion from PDO::PARAM_BOOL to numeric(38, 16) is supported****
****Conversion from PDO::PARAM_INT to numeric(38, 16) is supported****
****Conversion from PDO::PARAM_LOB to numeric(38, 16) is supported****
Testing numeric(38, 38):
****Conversion from PDO::PARAM_BOOL to numeric(38, 38) is supported****
****Conversion from PDO::PARAM_INT to numeric(38, 38) is supported****
****Conversion from PDO::PARAM_STR to numeric(38, 38) is supported****
****Conversion from PDO::PARAM_LOB to numeric(38, 38) is supported****

View file

@ -0,0 +1,113 @@
--TEST--
Test for inserting encrypted data into float types columns
--DESCRIPTION--
Test conversions between different float types
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to a float column
2. From input of PDO::PARAM_INT to a float column
3. From input of PDO::PARAM_STR to a float column
4. From input of PDO::PARAM_LOB to a float column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataType = "float";
$bits = array(1, 12, 24, 36, 53);
$inputValues = array(9223372036854775808.9223372036854775808, -9223372036854775808.9223372036854775808);
$numint = 19;
try {
$conn = connect();
foreach ($bits as $m) {
// compute the epsilon for comparing doubles
// when $m <= 24, the precision is 7 digits
// when $m > 24, the precision is 15 digits, but PHP float only supports up to 14 digits
$epsilon;
if ($m <= 24) {
$epsilon = pow(10, $numint - 7);
} else {
$epsilon = pow(10, $numint - 14);
}
$typeFull = "$dataType($m)";
echo "\nTesting $typeFull:\n";
//create table containing float(m) columns
$tbname = "test_" . $dataType . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) || !is_null($row['c_rand'])) {
echo "Conversion from $pdoParamType to $typeFull should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (abs($row['c_det'] - $inputValues[0]) < $epsilon && abs($row['c_rand'] - $inputValues[1]) < $epsilon) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
// cleanup
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing float(1):
****Conversion from PDO::PARAM_BOOL to float(1) is supported****
****Conversion from PDO::PARAM_INT to float(1) is supported****
****Conversion from PDO::PARAM_STR to float(1) is supported****
****Conversion from PDO::PARAM_LOB to float(1) is supported****
Testing float(12):
****Conversion from PDO::PARAM_BOOL to float(12) is supported****
****Conversion from PDO::PARAM_INT to float(12) is supported****
****Conversion from PDO::PARAM_STR to float(12) is supported****
****Conversion from PDO::PARAM_LOB to float(12) is supported****
Testing float(24):
****Conversion from PDO::PARAM_BOOL to float(24) is supported****
****Conversion from PDO::PARAM_INT to float(24) is supported****
****Conversion from PDO::PARAM_STR to float(24) is supported****
****Conversion from PDO::PARAM_LOB to float(24) is supported****
Testing float(36):
****Conversion from PDO::PARAM_BOOL to float(36) is supported****
****Conversion from PDO::PARAM_INT to float(36) is supported****
****Conversion from PDO::PARAM_STR to float(36) is supported****
****Conversion from PDO::PARAM_LOB to float(36) is supported****
Testing float(53):
****Conversion from PDO::PARAM_BOOL to float(53) is supported****
****Conversion from PDO::PARAM_INT to float(53) is supported****
****Conversion from PDO::PARAM_STR to float(53) is supported****
****Conversion from PDO::PARAM_LOB to float(53) is supported****

View file

@ -0,0 +1,172 @@
--TEST--
Test for inserting encrypted data into nchar types columns with different sizes
--DESCRIPTION--
Test conversions between different nchar types of different sizes
With or without Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to any nchar column
2. From input of PDO::PARAM_INT to any nchar column
3. From input of PDO::PARAM_STR to any nchar column
4. From input of PDO::PARAM_LOB to any nchar column
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("nchar", "nvarchar", "nvarchar(max)");
$lengths = array(1, 8, 64, 512, 4000);
try {
$conn = connect();
foreach ($dataTypes as $dataType) {
$maxcol = strpos($dataType, "(max)");
foreach ($lengths as $m) {
if ($maxcol !== false) {
$typeFull = $dataType;
} else {
$typeFull = "$dataType($m)";
}
echo "\nTesting $typeFull:\n";
// create table containing nchar(m) or nvarchar(m) columns
// only one column is created because a row has a limitation of 8060 bytes
// for lengths 4096 and 8000, cannot create 2 columns as it will exceed the maximum row sizes
// for AE, only testing deterministic here, randomized is tested in the char test
$tbname = "test_" . str_replace(array('(', ')'), '', $dataType) . $m;
$colMetaArr = array(new ColumnMeta($typeFull, "c1"));
createTable($conn, $tbname, $colMetaArr);
$input = str_repeat("d", $m);
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
$r;
$stmt = insertRow($conn, $tbname, array( "c1" => new BindParamOp(1, $input, $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $typeFull should be supported\n";
} else {
$sql = "SELECT c1 FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c1'])) {
echo "Conversion from $pdoParamType to $typeFull should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL, PDO::PARAM_INT, PDO::PARAM_STR or PDO{{PARAM_LOB
// with or without AE: should work
} else {
$sql = "SELECT c1 FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (strlen($row['c1']) == $m) {
echo "****Conversion from $pdoParamType to $typeFull is supported****\n";
} else {
echo "Conversion from $pdoParamType to $typeFull causes data corruption\n";
}
}
// cleanup
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing nchar(1):
****Conversion from PDO::PARAM_BOOL to nchar(1) is supported****
****Conversion from PDO::PARAM_INT to nchar(1) is supported****
****Conversion from PDO::PARAM_STR to nchar(1) is supported****
****Conversion from PDO::PARAM_LOB to nchar(1) is supported****
Testing nchar(8):
****Conversion from PDO::PARAM_BOOL to nchar(8) is supported****
****Conversion from PDO::PARAM_INT to nchar(8) is supported****
****Conversion from PDO::PARAM_STR to nchar(8) is supported****
****Conversion from PDO::PARAM_LOB to nchar(8) is supported****
Testing nchar(64):
****Conversion from PDO::PARAM_BOOL to nchar(64) is supported****
****Conversion from PDO::PARAM_INT to nchar(64) is supported****
****Conversion from PDO::PARAM_STR to nchar(64) is supported****
****Conversion from PDO::PARAM_LOB to nchar(64) is supported****
Testing nchar(512):
****Conversion from PDO::PARAM_BOOL to nchar(512) is supported****
****Conversion from PDO::PARAM_INT to nchar(512) is supported****
****Conversion from PDO::PARAM_STR to nchar(512) is supported****
****Conversion from PDO::PARAM_LOB to nchar(512) is supported****
Testing nchar(4000):
****Conversion from PDO::PARAM_BOOL to nchar(4000) is supported****
****Conversion from PDO::PARAM_INT to nchar(4000) is supported****
****Conversion from PDO::PARAM_STR to nchar(4000) is supported****
****Conversion from PDO::PARAM_LOB to nchar(4000) is supported****
Testing nvarchar(1):
****Conversion from PDO::PARAM_BOOL to nvarchar(1) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(1) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(1) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(1) is supported****
Testing nvarchar(8):
****Conversion from PDO::PARAM_BOOL to nvarchar(8) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(8) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(8) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(8) is supported****
Testing nvarchar(64):
****Conversion from PDO::PARAM_BOOL to nvarchar(64) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(64) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(64) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(64) is supported****
Testing nvarchar(512):
****Conversion from PDO::PARAM_BOOL to nvarchar(512) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(512) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(512) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(512) is supported****
Testing nvarchar(4000):
****Conversion from PDO::PARAM_BOOL to nvarchar(4000) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(4000) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(4000) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(4000) is supported****
Testing nvarchar(max):
****Conversion from PDO::PARAM_BOOL to nvarchar(max) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(max) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(max) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(max) is supported****
Testing nvarchar(max):
****Conversion from PDO::PARAM_BOOL to nvarchar(max) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(max) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(max) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(max) is supported****
Testing nvarchar(max):
****Conversion from PDO::PARAM_BOOL to nvarchar(max) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(max) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(max) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(max) is supported****
Testing nvarchar(max):
****Conversion from PDO::PARAM_BOOL to nvarchar(max) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(max) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(max) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(max) is supported****
Testing nvarchar(max):
****Conversion from PDO::PARAM_BOOL to nvarchar(max) is supported****
****Conversion from PDO::PARAM_INT to nvarchar(max) is supported****
****Conversion from PDO::PARAM_STR to nvarchar(max) is supported****
****Conversion from PDO::PARAM_LOB to nvarchar(max) is supported****

View file

@ -1,39 +1,123 @@
--TEST--
Test for inserting and retrieving encrypted data of numeric types
Test for inserting encrypted data into numeric types columns
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
Test conversions between different numeric types
With Always Encrypted, implicit conversion works if:
1. From input of PDO::PARAM_BOOL to a real column
2. From input of PDO::PARAM_INT to any numeric column
3. From input of PDO::PARAM_STR to any numeric column
4. From input of PDO::PARAM_LOB to any numeric column
Without Always Encrypted, all of the above work except for input of PDO::PARAM_STR to a bigint column in a x86 platform
PDO::PARAM_STR does not work for bigint in a x86 platform because the maximum value of an int is about 2147483647
Whereas in a x64 platform, the maximum value is about 9E18
In a x86 platform, when an integer is > 2147483647, PHP implicitly changees it to a float, represented by scientific notation
When inserting a scientific notation form numeric string, SQL Server returns a converting data type nvarchar to bigint error
Works for with AE because the sqltype used for binding parameter is determined by SQLDescribeParam,
unlike without AE, the sqltype is predicted to be nvarchar or varchar when the input is a string
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array( "bit", "tinyint", "smallint", "int", "decimal(18,5)", "numeric(10,5)", "float", "real" );
$dataTypes = array("bit", "tinyint", "smallint", "int", "bigint", "real");
$epsilon = 1;
try {
$conn = connect();
$conn = connect("", array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
// create table
$tbname = getTableName();
// create table containing bit, tinyint, smallint, int, bigint, or real columns
$tbname = "test_" . $dataType;
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// test each PDO::PARAM_ type
// insert by specifying PDO::PARAM_ types
foreach ($pdoParamTypes as $pdoParamType) {
// insert a row
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
$r;
if ($dataType == "decimal(18,5)") {
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, (string)$inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, (string)$inputValues[1], $pdoParamType)), "prepareBindParam", $r);
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
// check the case when inserting as PDO::PARAM_NULL
// with or without AE: NULL is inserted
if ($pdoParamType == "PDO::PARAM_NULL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $dataType should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_null($row['c_det']) || !is_null($row['c_rand'])) {
echo "Conversion from $pdoParamType to $dataType should insert NULL\n";
}
}
// check the case when inserting as PDO::PARAM_BOOL
// with or without AE: 1 or 0 should be inserted when inserting into an integer column
// double is inserted when inserting into a real column
} else if ($pdoParamType == "PDO::PARAM_BOOL") {
if ($r === false) {
echo "Conversion from $pdoParamType to $dataType should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($dataType == "real") {
if (abs($row['c_det'] - $inputValues[0]) < $epsilon && abs($row['c_rand'] - $inputValues[1]) < $epsilon) {
echo "****Conversion from $pdoParamType to $dataType is supported****\n";
} else {
echo "Conversion from $pdoParamType to $dataType causes data corruption\n";
}
} else {
if ($row['c_det'] != ($inputValues[0] != 0) && $row['c_rand'] != ($inputValues[1] != 0)) {
echo "Conversion from $pdoParamType to $dataType insert a boolean\n";
}
}
}
// check the case when inserting as PDO::PARAM_STR into a bigint column
// with AE: should work
// without AE: should not work on a x86 platform
} else if ($dataType == "bigint" && $pdoParamType == "PDO::PARAM_STR") {
if (!isAEConnected() && PHP_INT_SIZE == 4) {
if ($r !== false) {
echo "Conversion from $pdoParamType to $dataType should not be supported\n";
}
} else {
if ($r === false) {
echo "Conversion from $pdoParamType to $dataType should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row['c_det'] != $inputValues[0] && $row['c_rand'] != $inputValues[1]) {
echo "Conversion from $pdoParamType to $dataType causes data corruption\n";
}
}
}
// check the case when inserting as PDO::PARAM_INT, PDO::PARAM_STR or PDO::PARAM_LOB
// with or without AE: should work
} else {
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType), "c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
}
if ($r === false) {
isIncompatibleTypesError($stmt, $dataType, $pdoParamType);
} else {
echo "****PDO param type $pdoParamType is compatible with encrypted $dataType****\n";
fetchAll($conn, $tbname);
if ($r === false) {
echo "Conversion from $pdoParamType to $dataType should be supported\n";
} else {
$sql = "SELECT c_det, c_rand FROM $tbname";
$stmt = $conn->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($dataType == "real") {
if (abs($row['c_det'] - $inputValues[0]) < $epsilon && abs($row['c_rand'] - $inputValues[1]) < $epsilon) {
echo "****Conversion from $pdoParamType to $dataType is supported****\n";
} else {
echo "Conversion from $pdoParamType to $dataType causes data corruption\n";
}
} else {
if ($row['c_det'] == $inputValues[0] && $row['c_rand'] == $inputValues[1]) {
echo "****Conversion from $pdoParamType to $dataType is supported****\n";
} else {
echo "Conversion from $pdoParamType to $dataType causes data corruption\n";
}
}
}
}
$conn->query("TRUNCATE TABLE $tbname");
}
@ -46,139 +130,32 @@ try {
}
?>
--EXPECT--
Testing bit:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted bit****
c_det: 1
c_rand: 0
****PDO param type PDO::PARAM_NULL is compatible with encrypted bit****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted bit****
c_det: 1
c_rand: 0
****PDO param type PDO::PARAM_STR is compatible with encrypted bit****
c_det: 1
c_rand: 0
****PDO param type PDO::PARAM_LOB is compatible with encrypted bit****
c_det: 1
c_rand: 0
****Conversion from PDO::PARAM_INT to bit is supported****
****Conversion from PDO::PARAM_STR to bit is supported****
****Conversion from PDO::PARAM_LOB to bit is supported****
Testing tinyint:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted tinyint****
c_det: 0
c_rand: 1
****PDO param type PDO::PARAM_NULL is compatible with encrypted tinyint****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted tinyint****
c_det: 0
c_rand: 255
****PDO param type PDO::PARAM_STR is compatible with encrypted tinyint****
c_det: 0
c_rand: 255
****PDO param type PDO::PARAM_LOB is compatible with encrypted tinyint****
c_det: 0
c_rand: 255
****Conversion from PDO::PARAM_INT to tinyint is supported****
****Conversion from PDO::PARAM_STR to tinyint is supported****
****Conversion from PDO::PARAM_LOB to tinyint is supported****
Testing smallint:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted smallint****
c_det: 1
c_rand: 1
****PDO param type PDO::PARAM_NULL is compatible with encrypted smallint****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted smallint****
c_det: -32767
c_rand: 32767
****PDO param type PDO::PARAM_STR is compatible with encrypted smallint****
c_det: -32767
c_rand: 32767
****PDO param type PDO::PARAM_LOB is compatible with encrypted smallint****
c_det: -32767
c_rand: 32767
****Conversion from PDO::PARAM_INT to smallint is supported****
****Conversion from PDO::PARAM_STR to smallint is supported****
****Conversion from PDO::PARAM_LOB to smallint is supported****
Testing int:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted int****
c_det: 1
c_rand: 1
****PDO param type PDO::PARAM_NULL is compatible with encrypted int****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted int****
c_det: -2147483647
c_rand: 2147483647
****PDO param type PDO::PARAM_STR is compatible with encrypted int****
c_det: -2147483647
c_rand: 2147483647
****PDO param type PDO::PARAM_LOB is compatible with encrypted int****
c_det: -2147483647
c_rand: 2147483647
****Conversion from PDO::PARAM_INT to int is supported****
****Conversion from PDO::PARAM_STR to int is supported****
****Conversion from PDO::PARAM_LOB to int is supported****
Testing decimal(18,5):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted decimal(18,5)****
c_det: -9223372036854.80000
c_rand: 9223372036854.80000
****PDO param type PDO::PARAM_NULL is compatible with encrypted decimal(18,5)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted decimal(18,5)****
c_det: -9223372036854.80000
c_rand: 9223372036854.80000
****PDO param type PDO::PARAM_STR is compatible with encrypted decimal(18,5)****
c_det: -9223372036854.80000
c_rand: 9223372036854.80000
****PDO param type PDO::PARAM_LOB is compatible with encrypted decimal(18,5)****
c_det: -9223372036854.80000
c_rand: 9223372036854.80000
Testing numeric(10,5):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted numeric(10,5)****
c_det: -21474.83647
c_rand: 21474.83647
****PDO param type PDO::PARAM_NULL is compatible with encrypted numeric(10,5)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted numeric(10,5)****
c_det: -21474.83647
c_rand: 21474.83647
****PDO param type PDO::PARAM_STR is compatible with encrypted numeric(10,5)****
c_det: -21474.83647
c_rand: 21474.83647
****PDO param type PDO::PARAM_LOB is compatible with encrypted numeric(10,5)****
c_det: -21474.83647
c_rand: 21474.83647
Testing float:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted float****
c_det: -9223372036.8547993
c_rand: 9223372036.8547993
****PDO param type PDO::PARAM_NULL is compatible with encrypted float****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted float****
c_det: -9223372036.8547993
c_rand: 9223372036.8547993
****PDO param type PDO::PARAM_STR is compatible with encrypted float****
c_det: -9223372036.8547993
c_rand: 9223372036.8547993
****PDO param type PDO::PARAM_LOB is compatible with encrypted float****
c_det: -9223372036.8547993
c_rand: 9223372036.8547993
Testing bigint:
****Conversion from PDO::PARAM_INT to bigint is supported****
****Conversion from PDO::PARAM_LOB to bigint is supported****
Testing real:
****PDO param type PDO::PARAM_BOOL is compatible with encrypted real****
c_det: -2147.4829
c_rand: 2147.4829
****PDO param type PDO::PARAM_NULL is compatible with encrypted real****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted real****
c_det: -2147.4829
c_rand: 2147.4829
****PDO param type PDO::PARAM_STR is compatible with encrypted real****
c_det: -2147.4829
c_rand: 2147.4829
****PDO param type PDO::PARAM_LOB is compatible with encrypted real****
c_det: -2147.4829
c_rand: 2147.4829
****Conversion from PDO::PARAM_BOOL to real is supported****
****Conversion from PDO::PARAM_INT to real is supported****
****Conversion from PDO::PARAM_STR to real is supported****
****Conversion from PDO::PARAM_LOB to real is supported****

View file

@ -1,112 +0,0 @@
--TEST--
Test for inserting and retrieving encrypted data of string types
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("AEData.inc");
$dataTypes = array("char(5)", "varchar(max)", "nchar(5)", "nvarchar(max)");
try {
$conn = connect('', array(), PDO::ERRMODE_SILENT);
foreach ($dataTypes as $dataType) {
echo "\nTesting $dataType:\n";
// create table
$tbname = getTableName();
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
// prepare statement for inserting into table
foreach ($pdoParamTypes as $pdoParamType) {
// insert a row
$inputValues = array_slice(${explode("(", $dataType)[0] . "_params"}, 1, 2);
$r;
$stmt = insertRow($conn, $tbname, array( "c_det" => new BindParamOp(1, $inputValues[0], $pdoParamType),"c_rand" => new BindParamOp(2, $inputValues[1], $pdoParamType)), "prepareBindParam", $r);
if ($r === false) {
isIncompatibleTypesError($stmt, $dataType, $pdoParamType);
} else {
echo "****PDO param type $pdoParamType is compatible with encrypted $dataType****\n";
fetchAll($conn, $tbname);
}
$conn->query("TRUNCATE TABLE $tbname");
}
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Testing char(5):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted char(5)****
c_det: -leng
c_rand: th, n
****PDO param type PDO::PARAM_NULL is compatible with encrypted char(5)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted char(5)****
c_det: -leng
c_rand: th, n
****PDO param type PDO::PARAM_STR is compatible with encrypted char(5)****
c_det: -leng
c_rand: th, n
****PDO param type PDO::PARAM_LOB is compatible with encrypted char(5)****
c_det: -leng
c_rand: th, n
Testing varchar(max):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted varchar(max)****
c_det: Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.
c_rand: Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.
****PDO param type PDO::PARAM_NULL is compatible with encrypted varchar(max)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted varchar(max)****
c_det: Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.
c_rand: Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.
****PDO param type PDO::PARAM_STR is compatible with encrypted varchar(max)****
c_det: Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.
c_rand: Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.
****PDO param type PDO::PARAM_LOB is compatible with encrypted varchar(max)****
c_det: Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.
c_rand: Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.
Testing nchar(5):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted nchar(5)****
c_det: -leng
c_rand: th Un
****PDO param type PDO::PARAM_NULL is compatible with encrypted nchar(5)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted nchar(5)****
c_det: -leng
c_rand: th Un
****PDO param type PDO::PARAM_STR is compatible with encrypted nchar(5)****
c_det: -leng
c_rand: th Un
****PDO param type PDO::PARAM_LOB is compatible with encrypted nchar(5)****
c_det: -leng
c_rand: th Un
Testing nvarchar(max):
****PDO param type PDO::PARAM_BOOL is compatible with encrypted nvarchar(max)****
c_det: When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).
c_rand: Otherwise, the implicit conversion will result in a Unicode large-value (max).
****PDO param type PDO::PARAM_NULL is compatible with encrypted nvarchar(max)****
c_det:
c_rand:
****PDO param type PDO::PARAM_INT is compatible with encrypted nvarchar(max)****
c_det: When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).
c_rand: Otherwise, the implicit conversion will result in a Unicode large-value (max).
****PDO param type PDO::PARAM_STR is compatible with encrypted nvarchar(max)****
c_det: When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).
c_rand: Otherwise, the implicit conversion will result in a Unicode large-value (max).
****PDO param type PDO::PARAM_LOB is compatible with encrypted nvarchar(max)****
c_det: When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).
c_rand: Otherwise, the implicit conversion will result in a Unicode large-value (max).

View file

@ -0,0 +1,210 @@
--TEST--
Test for retrieving encrypted data of binary types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("binary", "varbinary", "varbinary(max)");
$lengths = array(1, 2, 4, 8, 64, 512, 4000);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "07006" => "Restricted data type attribute violation");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function convert2Hex($ch, $length)
{
// Without AE, the binary values returned as integers will
// have lengths no more than 4 times the original hex string value
// (e.g. string(8) "65656565") - limited by the buffer sizes
if (!isAEConnected()) {
$count = ($length <= 2) ? $length : 4;
} else {
$count = $length;
}
return str_repeat(bin2hex($ch), $count);
}
function testOutputBinary($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_binary_types";
$spname = "test_binary_proc";
$ch0 = 'd';
$ch1 = 'e';
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat($ch0, $length);
$input1 = str_repeat($ch1, $length);
$ord0 = convert2Hex($ch0, $length);
$ord1 = convert2Hex($ch1, $length);
insertRow($conn, $tbname, array("c_det" => new BindParamOp(1, $input0, "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY"),
"c_rand" => new BindParamOp(2, $input1, "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY")), "prepareBindParam");
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout && $pdoParamType != PDO::PARAM_STR) {
// Currently do not support getting binary as strings + INOUT param
// See VSO 2829 for details
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$det = "";
$rand = "";
if ($pdoParamType == PDO::PARAM_STR || $pdoParamType == PDO::PARAM_LOB) {
$stmt->bindParam(1, $det, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY);
$stmt->bindParam(2, $rand, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY);
} elseif ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$det = $rand = 0;
$stmt->bindParam(1, $det, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
$stmt->bindParam(2, $rand, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
} else {
$stmt->bindParam(1, $det, $paramType, $length);
$stmt->bindParam(2, $rand, $paramType, $length);
}
try {
$stmt->execute();
$errMsg = "****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_STR) {
if ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
// for boolean values, they should all be bool(true)
// because all floats are non-zeroes
// This only occurs without AE
// With AE enabled, this would have caused an exception
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType is PDO::PARAM_INT
// This only occurs without AE -- likely a rare use case
// With AE enabled, this would have caused an exception
if (strval($det) != $ord0 || strval($rand) != $ord1) {
printValues($errMsg, $det, $rand, $ord0, $ord1);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL || PDO::PARAM_INT) {
if (isAEConnected()) {
if ($pdoParamType == PDO::PARAM_INT) {
// Expected to fail with this message
$error = "String data, right truncated for output parameter";
$found = strpos($message, $error);
} else {
// PDO::PARAM_BOOL -
// Expected error 07006 with AE enabled:
// "Restricted data type attribute violation"
// The data value returned for a parameter bound as
// SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not
// be converted to the data type identified by the
// ValueType argument in SQLBindParameter.
$found = strpos($message, $errors['07006']);
}
} else {
// When not AE enabled, expected to fail with something like this message
// "Implicit conversion from data type nvarchar(max) to binary is not allowed. Use the CONVERT function to run this query."
// Sometimes it's about nvarchar too
$error = "to $dataType is not allowed. Use the CONVERT function to run this query.";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// catch all
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputBinary(false);
testOutputBinary(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_binary_types";
$spname = "test_binary_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,171 @@
--TEST--
Test for retrieving encrypted data of char types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("char", "varchar", "varchar(max)");
$lengths = array(1, 8, 64, 512, 4000);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.",
"22003" => "Numeric value out of range");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function testOutputChars($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_char_types";
$spname = "test_char_proc";
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat("1", $length);
$input1 = str_repeat("2", $length);
insertRow($conn, $tbname, array("c_det" => $input0,
"c_rand" => $input1));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = $length;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($length < 64 && $pdoParamType != PDO::PARAM_STR) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// For boolean values, they should all be bool(true)
// because all "string literals" are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType = PDO::PARAM_INT
// Expect numeric values
if ($det != intval($input0) || $rand != intval($input1)) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
} elseif ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
$found = strpos($message, $errors['22003']);
} else {
// When not AE enabled, expected to fail to convert
// whatever char type to integers
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputChars(false);
testOutputChars(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_char_types";
$spname = "test_char_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,204 @@
--TEST--
Test for retrieving encrypted data of datetimes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("datetime2", "datetimeoffset", "time");
$precisions = array(/*0, */1, 2, 4, 7);
$inputValuesInit = array("datetime2" => array("0001-01-01 00:00:00", "9999-12-31 23:59:59"),
"datetimeoffset" => array("0001-01-01 00:00:00 -14:00", "9999-12-31 23:59:59 +14:00"),
"time" => array("00:00:00", "23:59:59"));
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "07006" => "Restricted data type attribute violation");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
// compareDate() returns true when the date/time values are basically the same
// e.g. 00:00:00.000 is the same as 00:00:00
function compareDate($dtout, $dtin, $dataType)
{
if ($dataType == "datetimeoffset") {
$dtarr = explode(' ', $dtin);
if (strpos($dtout, $dtarr[0]) !== false && strpos($dtout, $dtarr[1]) !== false && strpos($dtout, $dtarr[2]) !== false) {
return true;
}
} else {
if (strpos($dtout, $dtin) !== false) {
return true;
}
}
return false;
}
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function testOutputDatetimes($inout)
{
global $pdoParamTypes, $dataTypes, $precisions, $inputValuesInit, $errors;
try {
$conn = connect();
$tbname = "test_datetimes_types";
$spname = "test_datetimes_proc";
foreach ($dataTypes as $dataType) {
foreach ($precisions as $precision) {
// change the input values depending on the precision
$inputValues[0] = $inputValuesInit[$dataType][0];
$inputValues[1] = $inputValuesInit[$dataType][1];
if ($precision != 0) {
if ($dataType == "datetime2") {
$inputValues[1] .= "." . str_repeat("9", $precision);
} else if ($dataType == "datetimeoffset") {
$inputPieces = explode(" ", $inputValues[1]);
$inputValues[1] = $inputPieces[0] . " " . $inputPieces[1] . "." . str_repeat("9", $precision) . " " . $inputPieces[2];
} else if ($dataType == "time") {
$inputValues[0] .= "." . str_repeat("0", $precision);
$inputValues[1] .= "." . str_repeat("9", $precision);
}
}
$type = "$dataType($precision)";
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$stmt = insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = 0;
$rand = 0;
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
// What follows only happens with OUTPUT parameter
if ($inout) {
echo "Any datetime type as INOUT param should have caused an exception!\n";
}
if ($pdoParamType == PDO::PARAM_INT) {
// Expect an integer, the first part of the date time string
$ch = ($dataType == "time")? ':' : '-';
$tmp0 = explode($ch, $inputValues[0]);
$tmp1 = explode($ch, $inputValues[1]);
if ($det != intval($tmp0[0]) || $rand != intval($tmp1[0])) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif (!compareDate($det, $inputValues[0], $dataType) ||
!compareDate($rand, $inputValues[1], $dataType)) {
printValues($errMsg, $det, $rand, $inputValues);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:\n$message****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
} elseif (isAEConnected()) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// Expected error 07006: "Restricted data type attribute violation"
// What does this error mean?
// The data value returned for a parameter bound as
// SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not
// be converted to the data type identified by the
// ValueType argument in SQLBindParameter.
$found = strpos($message, $errors['07006']);
} else {
$error = "Invalid character value for cast specification";
$found = strpos($message, $error);
}
} else {
if ($pdoParamType == PDO::PARAM_BOOL) {
$error = "Operand type clash: int is incompatible with $dataType";
} else {
$error = "Error converting data type nvarchar to $dataType";
}
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputDatetimes(false);
testOutputDatetimes(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_datetimes_types";
$spname = "test_datetimes_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,242 @@
--TEST--
Test for retrieving encrypted data of decimals/numerics as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("decimal", "numeric");
$precisions = array(1 => array(0, 1),
4 => array(0, 1, 4),
16 => array(0, 1, 4, 16),
38 => array(0, 1, 4, 16, 38));
$inputValuesInit = array(92233720368547758089223372036854775808, -92233720368547758089223372036854775808);
$inputPrecision = 38;
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
// this function returns true if the floats are more different than expected
function compareFloats($actual, $expected)
{
$epsilon = 0.00001;
$diff = abs(($actual - $expected) / $expected);
return ($diff > $epsilon);
}
// function compareIntegers() returns false when the fetched values
// are different from the expected inputs
function compareIntegers($det, $rand, $inputValues, $pdoParamType)
{
///////////////////////////////////////////////////////////////////////
// See GitHub issue 707 - Fix this method when the problem is addressed
//
// Assume $pdoParamType is PDO::PARAM_BOOL or PDO::PARAM_INT
if (is_string($det)) {
return (!compareFloats(floatval($det), $inputValues[0])
&& !compareFloats(floatval($rand), $inputValues[1]));
} elseif ($pdoParamType == PDO::PARAM_INT) {
$input0 = floor($inputValues[0]); // the positive float
$input1 = ceil($inputValues[1]); // the negative float
return ($det == $input0 && $rand == $input1);
} else {
// $pdoParamType == PDO::PARAM_BOOL
// Expect bool(true) or bool(false) depending on the rounded input values
// But with AE enabled (aforementioned GitHub issue), the fetched values
// are floats instead, which should be fixed
$input0 = floor($inputValues[0]); // the positive float
$input1 = ceil($inputValues[1]); // the negative float
if (isAEConnected()) {
$det = boolval(floor($det));
$rand = boolval(ceil($rand));
}
return ($det == boolval($input0) && $rand == boolval($input1));
}
}
// function compareDecimals() returns false when the fetched values
// are different from the inputs, based on precision, scale
function compareDecimals($det, $rand, $inputValues, $pdoParamType, $precision, $scale)
{
// Assume $pdoParamType is PDO::PARAM_STR
for ($i = 0; $i < 2; $i++) {
$inputStr = strval($inputValues[$i]);
$fetchedStr = ($i == 0) ? strval(floatval($det)) : strval(floatval($rand));
if ($precision == $scale) {
// compare up to $precision + digits left if radix point ('.') +
// 1 digit ('.') + possibly the negative sign
$len = $precision + 2 + $i;
} elseif ($scale > 0) {
// compare up to $precision + 1 digit ('.')
// + possibly the negative sign
$len = $precision + 1 + $i;
} else {
// in this case, $scale = 0
// compare up to $precision + possibly the negative sign
$len = $precision + $i;
}
trace("Comparing $len...");
$result = substr_compare($inputStr, $fetchedStr, 0, $len);
if ($result != 0) {
return false;
}
}
return true;
}
function testOutputDecimals($inout)
{
global $pdoParamTypes, $dataTypes, $inputValuesInit, $precisions, $inputPrecision, $errors;
try {
$conn = connect();
$tbname = "test_decimals_types";
$spname = "test_decimals_proc";
foreach ($dataTypes as $dataType) {
foreach ($precisions as $precision => $scales) {
foreach ($scales as $scale) {
// construct the input values depending on the precision and scale
$precDiff = $inputPrecision - ($precision - $scale);
$inputValues = $inputValuesInit;
foreach ($inputValues as &$inputValue) {
$inputValue = $inputValue / pow(10, $precDiff);
}
$type = "$dataType($precision, $scale)";
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$stmt = insertRow($conn, $tbname, array("c_det" => $inputValues[0], "c_rand" => $inputValues[1]));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = $rand = 0.0;
$stmt = $conn->prepare($outSql);
$len = 2048;
// Do not initialize $det or $rand as empty strings
// See VSO 2915 for details
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
if (!compareIntegers($det, $rand, $inputValues, $pdoParamType)) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
// When $pdoParamType is PDO::PARAM_STR, the accuracies
// should have been preserved based on the original
// precision and scale, so compare the retrieved values
// against the input values with more details
if (!compareDecimals($det, $rand, $inputValues, $pdoParamType, $precision, $scale)) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif (!isAEConnected() && $precision >= 16 && $pdoParamType == PDO::PARAM_BOOL) {
// When not AE enabled, large numbers are expected to
// fail when converting to booleans
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputDecimals(false);
testOutputDecimals(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_decimals_types";
$spname = "test_decimals_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,181 @@
--TEST--
Test for retrieving encrypted data of floats as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
// this function returns true if the floats are more different than expected
function compareFloats($actual, $expected)
{
$epsilon = 0.0001;
$diff = abs(($actual - $expected) / $expected);
return ($diff > $epsilon);
}
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function testOutputFloats($fetchNumeric, $inout)
{
global $pdoParamTypes, $inputValues, $errors;
try {
$conn = connect();
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE, $fetchNumeric);
$tbname = "test_floats_types";
$spname = "test_floats_proc";
$bits = array(1, 12, 24, 36, 53);
foreach ($bits as $bit) {
$type = "float($bit)";
trace("\nTesting $type:\n");
$inputValues = array();
// create random input values
for ($i = 0; $i < 2; $i++) {
$mantissa = rand(1, 100000000);
$decimals = rand(1, 100000000);
$floatNum = $mantissa + $decimals / 10000000;
if ($i > 0) {
// make the second input negative
$floatNum *= -1;
}
array_push($inputValues, $floatNum);
}
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
insertRow($conn,
$tbname,
array("c_det" => new BindParamOp(1, $inputValues[0], 'PDO::PARAM_INT'),
"c_rand" => new BindParamOp(2, $inputValues[1], 'PDO::PARAM_INT')),
"prepareBindParam");
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
if ($pdoParamType == PDO::PARAM_INT && (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN' || substr(PHP_VERSION, 0, 3) == "7.0")) {
// Bug 2876 in VSO: Linux or PHP 7.0 - when retrieving a float as OUTPUT
// or INOUT parameter with PDO::PARAM_INT, the returned values
// are always single digits, regardless of the original floats
continue;
}
$det = 0.0;
$rand = 0.0;
$stmt = $conn->prepare($outSql);
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = 0;
$rand = 0;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_BOOL) {
// for boolean values, they should all be bool(true)
// because all floats are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
// Compare the retrieved values against the input values
// if either of them is very different, print them all
if (compareFloats(floatval($det), $inputValues[0]) ||
compareFloats(floatval($rand), $inputValues[1])) {
printValues($errMsg, $det, $rand, $inputValues);
}
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputFloats(false, false);
testOutputFloats(true, false);
testOutputFloats(false, true);
testOutputFloats(true, true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_floats_types";
$spname = "test_floats_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,193 @@
--TEST--
Test for retrieving encrypted data of integral types as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("bit", "tinyint", "smallint", "int", "bigint");
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "22003" => "Numeric value out of range", "42000" => "Error converting data type bigint to int");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $inputValues)
{
echo $msg;
echo "input 0: "; var_dump($inputValues[0]);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($inputValues[1]);
echo "fetched: "; var_dump($rand);
}
function generateInputs($dataType)
{
// create random input values based on data types
// make the second input negative but only for some data types
if ($dataType == "bit") {
$inputValues = array(0, 1);
} elseif ($dataType == "tinyint") {
$inputValues = array();
for ($i = 0; $i < 2; $i++) {
$randomNum = rand(0, 255);
array_push($inputValues, $randomNum);
}
} else {
switch ($dataType) {
case "smallint":
$max = 32767;
break;
case "int":
$max = 2147483647;
break;
default:
$max = getrandmax();
}
$inputValues = array();
for ($i = 0; $i < 2; $i++) {
$randomNum = rand(0, $max);
if ($i > 0) {
// make the second input negative but only for some data types
$randomNum *= -1;
}
array_push($inputValues, $randomNum);
if (traceMode()) {
echo "input: "; var_dump($inputValues[$i]);
}
}
}
return $inputValues;
}
function testOutputInts($inout)
{
global $pdoParamTypes, $dataTypes, $errors;
try {
$conn = connect();
$tbname = "test_integers_types";
$spname = "test_integers_proc";
foreach ($dataTypes as $dataType) {
trace("\nTesting $dataType:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($dataType, "c_det"), new ColumnMeta($dataType, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$inputValues = generateInputs($dataType);
insertRow($conn, $tbname, array("c_det" => $inputValues[0],
"c_rand" => $inputValues[1]));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $dataType OUTPUT, @c_rand $dataType OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = 0;
$rand = 0;
$stmt = $conn->prepare($outSql);
$len = 2048;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
}
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_STR) {
if ($det !== strval($inputValues[0]) || $rand !== strval($inputValues[1])) {
// comparisons between strings, use '!=='
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif ($pdoParamType == PDO::PARAM_INT || $pdoParamType == PDO::PARAM_BOOL) {
// comparisons between integers and booleans, do not use '!=='
if ($det != $inputValues[0] || $rand != $inputValues[1]) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$dataType as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} elseif ($dataType == "bigint" && ($pdoParamType == PDO::PARAM_INT || $pdoParamType == PDO::PARAM_BOOL)) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
// This is expected when converting big integer to integer or bool
$found = strpos($message, $errors['22003']);
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
// Expected error 42000: "Error converting data type bigint to int"
// This is expected when not AE connected and converting big integer to bool
$found = strpos($message, $errors['42000']);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $inputValues);
}
} else {
printValues($errMsg, $det, $rand, $inputValues);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputInts(false);
testOutputInts(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_integers_types";
$spname = "test_integers_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -0,0 +1,172 @@
--TEST--
Test for retrieving encrypted data of nchar types of various sizes as output parameters
--DESCRIPTION--
Use PDOstatement::bindParam with all PDO::PARAM_ types
Note: Because the maximum allowable table row size is 8060 bytes, 7 bytes of which are reserved for internal overhead. In other words, this allows up to two nvarchar() columns with length slightly
more than 2000 wide characters. Therefore, the max length in this test is 2010.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$dataTypes = array("nchar", "nvarchar", "nvarchar(max)");
$lengths = array(1, 8, 64, 512, 2010);
$errors = array("IMSSP" => "An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters.", "22003" => "Numeric value out of range");
$pdoParamTypes = array(
PDO::PARAM_BOOL, // 5
PDO::PARAM_NULL, // 0
PDO::PARAM_INT, // 1
PDO::PARAM_STR, // 2
PDO::PARAM_LOB // 3
);
//////////////////////////////////////////////////////////////////////////////////
function printValues($msg, $det, $rand, $input0, $input1)
{
echo $msg;
echo "input 0: "; var_dump($input0);
echo "fetched: "; var_dump($det);
echo "input 1: "; var_dump($input1);
echo "fetched: "; var_dump($rand);
}
function testOutputNChars($inout)
{
global $pdoParamTypes, $dataTypes, $lengths, $errors;
try {
$conn = connect();
$tbname = "test_nchar_types";
$spname = "test_nchar_proc";
foreach ($dataTypes as $dataType) {
$maxtype = strpos($dataType, "(max)");
foreach ($lengths as $length) {
if ($maxtype !== false) {
$type = $dataType;
} else {
$type = "$dataType($length)";
}
trace("\nTesting $type:\n");
//create and populate table
$colMetaArr = array(new ColumnMeta($type, "c_det"), new ColumnMeta($type, "c_rand", null, "randomized"));
createTable($conn, $tbname, $colMetaArr);
$input0 = str_repeat("1", $length);
$input1 = str_repeat("2", $length);
insertRow($conn, $tbname, array("c_det" => $input0,
"c_rand" => $input1));
// fetch with PDO::bindParam using a stored procedure
$procArgs = "@c_det $type OUTPUT, @c_rand $type OUTPUT";
$procCode = "SELECT @c_det = c_det, @c_rand = c_rand FROM $tbname";
createProc($conn, $spname, $procArgs, $procCode);
// call stored procedure
$outSql = getCallProcSqlPlaceholders($spname, 2);
foreach ($pdoParamTypes as $pdoParamType) {
$det = "";
$rand = "";
$stmt = $conn->prepare($outSql);
trace("\nParam $pdoParamType with INOUT = $inout\n");
if ($inout) {
$paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT;
} else {
$paramType = $pdoParamType;
}
$len = $length;
if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) {
$len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE;
$det = $rand = 0;
}
$stmt->bindParam(1, $det, $paramType, $len);
$stmt->bindParam(2, $rand, $paramType, $len);
try {
$stmt->execute();
$errMsg = "****$type as $pdoParamType failed with INOUT = $inout:****\n";
// When $length >= 64, a string is returned regardless of $pdoParamType
if ($length < 64 && $pdoParamType != PDO::PARAM_STR) {
if ($pdoParamType == PDO::PARAM_BOOL) {
// For boolean values, they should all be bool(true)
// because all "string literals" are non-zeroes
if (!$det || !$rand) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
// $pdoParamType = PDO::PARAM_INT
// Expect numeric values
if ($det != intval($input0) || $rand != intval($input1)) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
} elseif ($det !== $input0 || $rand !== $input1) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} catch (PDOException $e) {
$message = $e->getMessage();
$errMsg = "EXCEPTION: ****$type as $pdoParamType failed with INOUT = $inout:****\n";
if ($pdoParamType == PDO::PARAM_NULL || $pdoParamType == PDO::PARAM_LOB) {
// Expected error IMSSP: "An invalid PHP type was specified
// as an output parameter. DateTime objects, NULL values, and
// streams cannot be specified as output parameters."
$found = strpos($message, $errors['IMSSP']);
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} elseif ($pdoParamType == PDO::PARAM_BOOL) {
if (isAEConnected()) {
// Expected error 22003: "Numeric value out of range"
$found = strpos($message, $errors['22003']);
} else {
// When not AE enabled, expected to fail to convert
// whatever char type to integers
$error = "Error converting data type $dataType to int";
$found = strpos($message, $error);
}
if ($found === false) {
printValues($errMsg, $det, $rand, $input0, $input1);
}
} else {
printValues($errMsg, $det, $rand, $input0, $input1);
}
}
}
dropProc($conn, $spname);
dropTable($conn, $tbname);
}
}
unset($stmt);
unset($conn);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
testOutputNChars(false);
testOutputNChars(true);
echo "Done\n";
?>
--CLEAN--
<?php
// drop the temporary table and stored procedure in case
// the test failed without dropping them
require_once("MsCommon_mid-refactor.inc");
$tbname = "test_nchar_types";
$spname = "test_nchar_proc";
$conn = connect();
dropProc($conn, $spname);
dropTable($conn, $tbname);
unset($conn);
?>
--EXPECT--
Done

View file

@ -1,160 +1,138 @@
--TEST--
Test new connection keyword Driver with valid and invalid values
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require_once('MsSetup.inc');
try {
$conn = new PDO("sqlsrv:server = $server", $uid, $pwd);
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)['DriverVer'];
$msodbcsqlMaj = explode(".", $msodbcsqlVer)[0];
} catch(PDOException $e) {
echo "Failed to connect\n";
print_r($e->getMessage());
echo "\n";
}
$conn = null;
// start test
testValidValues();
testInvalidValues();
testEncryptedWithODBC();
testWrongODBC();
echo "Done";
// end test
///////////////////////////
function connectVerifyOutput($connectionOptions, $expected = '')
{
global $server, $uid, $pwd;
try {
$conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd);
} catch(PDOException $e) {
if (strpos($e->getMessage(), $expected) === false) {
print_r($e->getMessage());
echo "\n";
}
}
}
function testValidValues()
{
global $msodbcsqlMaj;
$value = "";
// The major version number of ODBC 11 can be 11 or 12
// Test with {}
switch ($msodbcsqlMaj) {
case 17:
$value = "{ODBC Driver 17 for SQL Server}";
break;
case 14:
case 13:
$value = "{ODBC Driver 13 for SQL Server}";
break;
case 12:
case 11:
$value = "{ODBC Driver 11 for SQL Server}";
break;
default:
$value = "invalid value $msodbcsqlMaj";
}
$connectionOptions = "Driver = $value";
connectVerifyOutput($connectionOptions);
// Test without {}
switch ($msodbcsqlMaj) {
case 17:
$value = "ODBC Driver 17 for SQL Server";
break;
case 14:
case 13:
$value = "ODBC Driver 13 for SQL Server";
break;
case 12:
case 11:
$value = "ODBC Driver 11 for SQL Server";
break;
default:
$value = "invalid value $msodbcsqlMaj";
}
$connectionOptions = "Driver = $value";
connectVerifyOutput($connectionOptions);
}
function testInvalidValues()
{
$values = array("{SQL Server Native Client 11.0}",
"SQL Server Native Client 11.0",
"ODBC Driver 00 for SQL Server",
123,
false);
foreach ($values as $value) {
$connectionOptions = "Driver = $value";
$expected = "Invalid value $value was specified for Driver option.";
connectVerifyOutput($connectionOptions, $expected);
}
}
function testEncryptedWithODBC()
{
global $msodbcsqlMaj, $server, $uid, $pwd;
$value = "ODBC Driver 13 for SQL Server";
$connectionOptions = "Driver = $value; ColumnEncryption = Enabled;";
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
connectVerifyOutput($connectionOptions, $expected);
// TODO: the following block will change once ODBC 17 is officially released
$value = "ODBC Driver 17 for SQL Server";
$connectionOptions = "Driver = $value; ColumnEncryption = Enabled;";
$success = "Successfully connected with column encryption.";
$expected = "The specified ODBC Driver is not found.";
$message = $success;
try {
$conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd);
} catch(PDOException $e) {
$message = $e->getMessage();
}
if ($msodbcsqlMaj == 17) {
// this indicates that OCBC 17 is the only available driver
if (strcmp($message, $success)) {
print_r($message);
}
} else {
// OCBC 17 might or might not exist
if (strcmp($message, $success)) {
if (strpos($message, $expected) === false) {
print_r($message);
}
}
}
}
function testWrongODBC()
{
global $msodbcsqlMaj;
// TODO: this will change once ODBC 17 is officially released
$value = "ODBC Driver 17 for SQL Server";
if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) {
$value = "ODBC Driver 13 for SQL Server";
}
$connectionOptions = "Driver = $value;";
$expected = "The specified ODBC Driver is not found.";
connectVerifyOutput($connectionOptions, $expected);
}
?>
--EXPECT--
--TEST--
Test new connection keyword Driver with valid and invalid values
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
require_once('MsSetup.inc');
try {
$conn = new PDO("sqlsrv:server = $server", $uid, $pwd);
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)['DriverVer'];
$msodbcsqlMaj = explode(".", $msodbcsqlVer)[0];
} catch(PDOException $e) {
echo "Failed to connect\n";
print_r($e->getMessage());
echo "\n";
}
$conn = null;
// start test
testValidValues();
testInvalidValues();
testEncryptedWithODBC();
testWrongODBC();
echo "Done";
// end test
///////////////////////////
function connectVerifyOutput($connectionOptions, $expected = '')
{
global $server, $uid, $pwd;
try {
$conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd);
} catch(PDOException $e) {
if (strpos($e->getMessage(), $expected) === false) {
print_r($e->getMessage());
echo "\n";
}
}
}
function testValidValues()
{
global $msodbcsqlMaj;
$value = "";
// The major version number of ODBC 11 can be 11 or 12
// Test with {}
switch ($msodbcsqlMaj) {
case 17:
$value = "{ODBC Driver 17 for SQL Server}";
break;
case 14:
case 13:
$value = "{ODBC Driver 13 for SQL Server}";
break;
case 12:
case 11:
$value = "{ODBC Driver 11 for SQL Server}";
break;
default:
$value = "invalid value $msodbcsqlMaj";
}
$connectionOptions = "Driver = $value";
connectVerifyOutput($connectionOptions);
// Test without {}
switch ($msodbcsqlMaj) {
case 17:
$value = "ODBC Driver 17 for SQL Server";
break;
case 14:
case 13:
$value = "ODBC Driver 13 for SQL Server";
break;
case 12:
case 11:
$value = "ODBC Driver 11 for SQL Server";
break;
default:
$value = "invalid value $msodbcsqlMaj";
}
$connectionOptions = "Driver = $value";
connectVerifyOutput($connectionOptions);
}
function testInvalidValues()
{
$values = array("{SQL Server Native Client 11.0}",
"SQL Server Native Client 11.0",
"ODBC Driver 00 for SQL Server",
123,
false);
foreach ($values as $value) {
$connectionOptions = "Driver = $value";
$expected = "Invalid value $value was specified for Driver option.";
connectVerifyOutput($connectionOptions, $expected);
}
}
function testEncryptedWithODBC()
{
global $msodbcsqlMaj, $server, $uid, $pwd;
$value = "ODBC Driver 13 for SQL Server";
$connectionOptions = "Driver = $value; ColumnEncryption = Enabled;";
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
} else {
$expected = "An invalid keyword 'ColumnEncryption' was specified in the DSN string.";
}
connectVerifyOutput($connectionOptions, $expected);
}
function testWrongODBC()
{
global $msodbcsqlMaj;
// TODO: this will change once ODBC 17 is officially released
$value = "ODBC Driver 17 for SQL Server";
if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) {
$value = "ODBC Driver 13 for SQL Server";
}
$connectionOptions = "Driver = $value;";
$expected = "The specified ODBC Driver is not found.";
connectVerifyOutput($connectionOptions, $expected);
}
?>
--EXPECT--
Done

View file

@ -1,7 +1,7 @@
--TEST--
Test new connection keyword ColumnEncryption
--SKIPIF--
<?php require('skipif.inc'); ?>
<?php require('skipif_unix.inc'); ?>
--FILE--
<?php
require_once("MsSetup.inc");

View file

@ -1,47 +0,0 @@
--TEST--
Fetch data from a prepopulated test table given a custom keystore provider
--SKIPIF--
<?php require('skipif_not_ksp.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
try
{
$conn = connect();
echo "Connected successfully with ColumnEncryption enabled and KSP specified.\n";
}
catch( PDOException $e )
{
echo "Failed to connect.\n";
print_r( $e->getMessage() );
echo "\n";
}
$tbname = KSP_TEST_TABLE;
$tsql = "SELECT * FROM $tbname";
$stmt = $conn->query($tsql);
while ($row = $stmt->fetch(PDO::FETCH_NUM))
{
echo "c1=" . $row[0] . "\tc2=" . $row[1] . "\tc3=" . $row[2] . "\tc4=" . $row[3] . "\n";
}
unset($stmt);
unset($conn);
echo "Done\n";
?>
--EXPECT--
Connected successfully with ColumnEncryption enabled and KSP specified.
c1=1 c2=Sample data 0 for column 2 c3=abc c4=2017-08-10
c1=12 c2=Sample data 1 for column 2 c3=bcd c4=2017-08-11
c1=23 c2=Sample data 2 for column 2 c3=cde c4=2017-08-12
c1=34 c2=Sample data 3 for column 2 c3=def c4=2017-08-13
c1=45 c2=Sample data 4 for column 2 c3=efg c4=2017-08-14
c1=56 c2=Sample data 5 for column 2 c3=fgh c4=2017-08-15
c1=67 c2=Sample data 6 for column 2 c3=ghi c4=2017-08-16
c1=78 c2=Sample data 7 for column 2 c3=hij c4=2017-08-17
c1=89 c2=Sample data 8 for column 2 c3=ijk c4=2017-08-18
c1=100 c2=Sample data 9 for column 2 c3=jkl c4=2017-08-19
Done

View file

@ -1,51 +0,0 @@
--TEST--
Fetch encrypted data from a prepopulated test table given a custom keystore provider
--SKIPIF--
<?php require('skipif_not_ksp.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
try
{
$conn = connect();
echo "Connected successfully with ColumnEncryption disabled and KSP specified.\n";
}
catch( PDOException $e )
{
echo "Failed to connect.\n";
print_r( $e->getMessage() );
echo "\n";
}
$tbname = KSP_TEST_TABLE;
$tsql = "SELECT * FROM $tbname";
$stmt = $conn->query($tsql);
while ($row = $stmt->fetch(PDO::FETCH_NUM))
{
echo "c1=" . $row[0];
echo "\tc2=" . bin2hex($row[1]);
echo "\tc3=" . bin2hex($row[2]);
echo "\tc4=" . bin2hex($row[3]);
echo "\n" ;
}
unset($stmt);
unset($conn);
echo "Done\n";
?>
--EXPECTREGEX--
Connected successfully with ColumnEncryption disabled and KSP specified.
c1=1 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=12 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=23 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=34 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=45 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=56 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=67 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=78 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=89 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
c1=100 c2=[a-f0-9]+ c3=[a-f0-9]+ c4=[a-f0-9]+
Done

View file

@ -1,102 +0,0 @@
--TEST--
Connect using a custom keystore provider with some required inputs missing
--SKIPIF--
<?php require('skipif_not_ksp.inc'); ?>
--FILE--
<?php
require("MsSetup.inc");
require_once("MsCommon_mid-refactor.inc");
function kspConnect( $connectionInfo )
{
global $server, $uid, $pwd;
try
{
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
echo "Connected successfully with ColumnEncryption enabled and KSP specified.\n";
}
catch( PDOException $e )
{
echo "Failed to connect.\n";
print_r( $e->getMessage() );
echo "\n";
}
}
$ksp_path = getKSPpath();
$ksp_name = KSP_NAME;
$encrypt_key = ENCRYPT_KEY;
echo("Connecting... with column encryption\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
kspConnect( $connectionInfo );
echo("\nConnecting... with an invalid input to CEKeystoreProvider\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreName = 1; ";
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
kspConnect( $connectionInfo );
echo("\nConnecting... with an empty path\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
$connectionInfo .= "CEKeystoreProvider = ; ";
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
kspConnect( $connectionInfo );
echo("\nConnecting... without a path\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key;";
kspConnect( $connectionInfo );
echo("\nConnecting... without a name\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
kspConnect( $connectionInfo );
echo("\nConnecting... without a key\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
kspConnect( $connectionInfo );
echo("\nConnecting... with all required inputs\n");
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
kspConnect( $connectionInfo );
echo "Done\n";
?>
--EXPECTREGEX--
Connecting\.\.\. with column encryption
Connected successfully with ColumnEncryption enabled and KSP specified\.
Connecting\.\.\. with an invalid input to CEKeystoreProvider
Failed to connect.
SQLSTATE\[HY024\]: \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]Invalid attribute value
Connecting\.\.\. with an empty path
Failed to connect.
SQLSTATE\[IMSSP\]: Invalid value for loading a custom keystore provider\.
Connecting\.\.\. without a path
Failed to connect.
SQLSTATE\[IMSSP\]: The path to the custom keystore provider is missing\.
Connecting\.\.\. without a name
Failed to connect.
SQLSTATE\[IMSSP\]: The name of the custom keystore provider is missing\.
Connecting\.\.\. without a key
Failed to connect.
SQLSTATE\[IMSSP\]: The encryption key for the custom keystore provider is missing\.
Connecting\.\.\. with all required inputs
Connected successfully with ColumnEncryption enabled and KSP specified\.
Done

View file

@ -41,8 +41,7 @@ function cursorScrollFetchRows($conn, $tableName)
$stmt = $conn->prepare("SELECT * FROM $tableName ORDER BY c1_int", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
} else {
// ORDER BY is not supported for encrypted columns
// scrollable cursor is not supported for encrypted tablee; use client side buffered cursor
$stmt = $conn->prepare("SELECT * FROM $tableName", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED));
$stmt = $conn->prepare("SELECT * FROM $tableName", array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
}
$stmt->execute();

View file

@ -20,7 +20,7 @@ try {
--EXPECTREGEX--
Array
\(
\[(DriverDllName|DriverName)\] => (msodbcsql1[1-9].dll|libmsodbcsql-[1-9]{2}.[0-9].so.[0-9].[0-9])
\[(DriverDllName|DriverName)\] => (msodbcsql1[1-9].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

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

View file

@ -3,31 +3,23 @@ Test the fetch() method for different fetch orientations with PDO::ATTR_CURSOR s
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif.inc'); ?>
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once 'MsCommon.inc';
function FetchAll($execMode, $fetchMode)
{
require_once 'MsCommon.inc';
require 'MsSetup.inc';
$testName = "PDO Statement - Fetch Scrollable";
StartTest($testName);
try {
$conn1 = connect();
// Prepare test table
$dataCols = "id, val";
CreateTableEx($conn1, $tableName, "id int NOT NULL PRIMARY KEY, val VARCHAR(10)", null);
InsertRowEx($conn1, $tableName, $dataCols, "1, 'A'", null);
InsertRowEx($conn1, $tableName, $dataCols, "2, 'B'", null);
InsertRowEx($conn1, $tableName, $dataCols, "3, 'C'", null);
$tableName = "pdo_test_table";
createTable($conn1, $tableName, array(new ColumnMeta("int", "id", "NOT NULL PRIMARY KEY", "none"), "val" => "varchar(10)"));
insertRow($conn1, $tableName, array("id" => 1, "val" => "A"));
insertRow($conn1, $tableName, array("id" => 2, "val" => "B"));
insertRow($conn1, $tableName, array("id" => 3, "val" => "C"));
// Query table and retrieve data
$stmt1 = $conn1->prepare( "SELECT val FROM $tableName", array( PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL ));
$stmt1 = $conn1->prepare( "SELECT val FROM $tableName ORDER BY id", array( PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL ));
$stmt1->execute();
$row = $stmt1->fetch( PDO::FETCH_ASSOC, PDO::FETCH_ORI_LAST );
@ -158,33 +150,15 @@ function FetchAll($execMode, $fetchMode)
}
// Cleanup
DropTable($conn1, $tableName);
$stmt1 = null;
$conn1 = null;
dropTable($conn1, $tableName);
unset($stmt1);
unset($conn1);
EndTest($testName);
echo "Test 'PDO Statement - Fetch Scrollable' completed successfully.\n";
} catch (Exception $e) {
echo $e->getMessage();
}
//--------------------------------------------------------------------
// Repro
//
//--------------------------------------------------------------------
function Repro()
{
try
{
FetchAll(false, PDO::FETCH_BOTH);
}
catch (Exception $e)
{
echo $e->getMessage();
}
}
Repro();
?>
--EXPECT--
Test "PDO Statement - Fetch Scrollable" completed successfully.
Test 'PDO Statement - Fetch Scrollable' completed successfully.

View file

@ -61,9 +61,7 @@ try {
echo "Now selecting....\n";
$tsql = "SELECT * FROM [$tableName]";
$stmtOptions[PDO::ATTR_CURSOR] = PDO::CURSOR_SCROLL;
if (isColEncrypted()) {
$stmtOptions[PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE] = PDO::SQLSRV_CURSOR_BUFFERED;
}
$stmt1 = $conn1->prepare($tsql, $stmtOptions);
$stmt1->execute();
// The row order in the resultset when the column is encrypted (which is dependent on the encrytion key used)
@ -114,7 +112,6 @@ try {
} else {
// more and less than operators do not work for encrypted columns
$tsql = "SELECT * FROM [$tableName] WHERE NOT ID = :id";
unset($stmtOptions[PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE]);
$stmt2 = $conn1->prepare($tsql, $stmtOptions);
$id = 3;
$stmt2->bindParam(':id', $id);

View file

@ -1,16 +1,21 @@
<?php
if (!extension_loaded("pdo") || !extension_loaded('pdo_sqlsrv')) {
die("PDO driver cannot be loaded; skipping test.\n");
}
require_once("MsSetup.inc");
require_once("MsCommon_mid-refactor.inc");
$dsn = getDSN($server, null);
$conn = new PDO($dsn, $uid, $pwd);
if (! $conn) {
echo("Error: could not connect during SKIPIF!");
} elseif (isColEncrypted() && !isAEQualified($conn)) {
die("skip - AE feature not supported in the current environment.");
}
<?php
if (!extension_loaded("pdo") || !extension_loaded('pdo_sqlsrv')) {
die("PDO driver cannot be loaded; skipping test.\n");
}
require_once("MsSetup.inc");
require_once("MsCommon_mid-refactor.inc");
$dsn = getDSN($server, null);
$conn = new PDO($dsn, $uid, $pwd);
if (! $conn) {
echo("Error: could not connect during SKIPIF!");
} elseif (isColEncrypted()) {
if (!(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')) {
die( "Skip, AE test on windows only." );
}
if (!isAEQualified($conn)) {
die("skip - AE feature not supported in the current environment.");
}
}

View file

@ -1,22 +0,0 @@
<?php
if (! extension_loaded( 'pdo' ) || ! extension_loaded( 'pdo_sqlsrv' ))
die( "PDO driver cannot be loaded; skipping test.\n" );
require_once( "MsSetup.inc" );
if ($keystore != 'ksp')
die ( 'skip - this test requires a custom keystore provider.' );
require_once( "MsCommon.inc" );
$conn = new PDO( "sqlsrv:server = $server;", $uid, $pwd );
if( ! $conn )
{
echo( "Error: could not connect during SKIPIF!" );
}
else if(! IsAEQualified($conn))
{
die( "skip - AE feature not supported in the current environment." );
}
?>

View file

@ -1,6 +1,3 @@
USE $(dbname)
GO
/* DROP Column Encryption Key first, Column Master Key cannot be dropped until no encryption depends on it */
IF EXISTS (SELECT * FROM sys.column_encryption_keys WHERE [name] LIKE '%AEColumnKey%')

View file

@ -45,13 +45,14 @@ def executeBulkCopy(conn_options, dbname, tblname, datafile):
inst_command = redirect_string.format(dbname, tblname, datafile) + conn_options
executeCommmand(inst_command)
def setupAE( conn_options, dbname, azure ):
if (platform.system() == 'Windows' and azure.lower() == 'no'):
def setupAE(conn_options, dbname):
if (platform.system() == 'Windows'):
# import self signed certificate
inst_command = "certutil -user -p '' -importPFX My PHPcert.pfx NoRoot"
executeCommmand(inst_command)
# create Column Master Key and Column Encryption Key
executeSQLscript('ae_keys.sql', conn_options, dbname)
script_command = 'sqlcmd ' + conn_options + ' -i ae_keys.sql -d ' + dbname
executeCommmand(script_command)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@ -84,7 +85,7 @@ if __name__ == '__main__':
# populate these tables
populateTables(conn_options, args.DBNAME)
# setup AE (certificate, column master key and column encryption key)
setupAE(conn_options, args.DBNAME, args.AZURE)
setupAE(conn_options, args.DBNAME)
os.chdir(current_working_dir)

View file

@ -23,8 +23,6 @@ function runTest($fieldType)
sqlsrv_fetch($stmt)
|| die(print_r(sqlsrv_errors(), true));
// Do not support getting stream if AE enabled, so expect
// it to fail with the correct error message
$stream = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STREAM("char"));
if ($stream) {
stream_filter_append($originalStream, "convert.base64-encode")
@ -37,11 +35,7 @@ function runTest($fieldType)
}
}
} else {
if (AE\isColEncrypted()) {
verifyError(sqlsrv_errors()[0], 'IMSSP', 'Connection with Column Encryption enabled does not support fetching stream. Please fetch the data as a string.');
} else {
fatalError('Fetching data stream failed!');
}
fatalError('Fetching data stream failed!');
}
dropTable($conn, $params['tableName']);

View file

@ -1,69 +1,63 @@
--TEST--
scrollable results with no rows.
--DESCRIPTION--
this test is very similar to test_scrollable.phpt... might consider removing this test as a duplicate
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
sqlsrv_configure('WarningsReturnAsErrors', 0);
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);
require_once('MsCommon.inc');
$conn = AE\connect();
$tableName = 'ScrollTest';
$columns = array(new AE\ColumnMeta('int', 'id'),
new AE\ColumnMeta('char(10)', 'value'));
$stmt = AE\createTable($conn, $tableName, $columns);
if (!$stmt) {
fatalError("Failed to create table for the test]n");
}
sqlsrv_free_stmt($stmt);
// Always Encrypted feature only supports SQLSRV_CURSOR_FORWARD or
// SQLSRV_CURSOR_CLIENT_BUFFERED
$query = "SELECT * FROM $tableName";
if (AE\isColEncrypted()) {
$options = array('Scrollable' => SQLSRV_CURSOR_FORWARD);
} else {
$options = array('Scrollable' => 'static');
}
$stmt = sqlsrv_query($conn, $query, array(), $options);
$rows = sqlsrv_has_rows($stmt);
if ($rows != false) {
fatalError("Should be no rows present");
};
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt);
print_r($row);
if ($row === false) {
print_r(sqlsrv_errors(), true);
}
$stmt = sqlsrv_query($conn, $query);
$rows = sqlsrv_has_rows($stmt);
if ($rows != false) {
fatalError("Should be no rows present");
};
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt);
print_r($row);
if ($row === false) {
print_r(sqlsrv_errors(), true);
}
dropTable($conn, $tableName);
echo "Test succeeded.\n";
?>
--EXPECT--
Test succeeded.
--TEST--
scrollable results with no rows.
--DESCRIPTION--
this test is very similar to test_scrollable.phpt... might consider removing this test as a duplicate
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--FILE--
<?php
sqlsrv_configure('WarningsReturnAsErrors', 0);
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);
require_once('MsCommon.inc');
$conn = AE\connect();
$tableName = 'ScrollTest';
$columns = array(new AE\ColumnMeta('int', 'id'),
new AE\ColumnMeta('char(10)', 'value'));
$stmt = AE\createTable($conn, $tableName, $columns);
if (!$stmt) {
fatalError("Failed to create table for the test]n");
}
sqlsrv_free_stmt($stmt);
$query = "SELECT * FROM $tableName";
$options = array('Scrollable' => 'static');
$stmt = sqlsrv_query($conn, $query, array(), $options);
$rows = sqlsrv_has_rows($stmt);
if ($rows != false) {
fatalError("Should be no rows present");
};
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt);
print_r($row);
if ($row === false) {
print_r(sqlsrv_errors(), true);
}
$stmt = sqlsrv_query($conn, $query);
$rows = sqlsrv_has_rows($stmt);
if ($rows != false) {
fatalError("Should be no rows present");
};
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt);
print_r($row);
if ($row === false) {
print_r(sqlsrv_errors(), true);
}
dropTable($conn, $tableName);
echo "Test succeeded.\n";
?>
--EXPECT--
Test succeeded.

View file

@ -78,8 +78,16 @@ if ($t === false) {
die(print_r(sqlsrv_errors(), true));
}
if ($t != "So?e sä???? ?SCII-te×t") {
die("varchar(100) doesn't match So?e sä???? ?SCII-te×t");
// If connected with AE, $t may be different in Windows and other platforms
// this is a workaround for now -- to make sure there are some '?' in $t
if (!AE\isColEncrypted() && $t !== "So?e sä???? ?SCII-te×t") {
die("varchar(100) \'$t\' doesn't match So?e sä???? ?SCII-te×t");
} else {
$arr = explode('?', $t);
if (count($arr) == 1) {
// this means there is no question mark in $t
die("varchar(100) value \'$t\' is unexpected");
}
}
$t = sqlsrv_get_field($s, 1, SQLSRV_PHPTYPE_STRING('utf-8'));
@ -87,7 +95,7 @@ if ($t === false) {
die(print_r(sqlsrv_errors(), true));
}
if ($t != $utf8) {
if ($t !== $utf8) {
die("nvarchar(100) doesn't match the inserted UTF-8 text.");
}
@ -96,7 +104,7 @@ if ($t === false) {
die(print_r(sqlsrv_errors(), true));
}
if ($t != $utf8) {
if ($t !== $utf8) {
die("nvarchar(max) doesn't match the inserted UTF-8 text.");
}
@ -129,7 +137,7 @@ if ($s === false) {
die(print_r(sqlsrv_errors(), true));
}
if ($t != $utf8) {
if ($t !== $utf8) {
die("Incorrect results from Utf8OutProc\n");
}
@ -148,7 +156,7 @@ if ($s === false) {
// retrieve all the results
while (sqlsrv_next_result($s));
if ($t != $utf8) {
if ($t !== $utf8) {
die("Incorrect results from Utf8OutWithResultsetProc\n");
}
@ -169,7 +177,7 @@ if ($s === false) {
die(print_r(sqlsrv_errors(), true));
}
if ($t != $utf8) {
if ($t !== $utf8) {
die("Incorrect results from Utf8InOutProc 1\n");
}

View file

@ -38,11 +38,7 @@ inserting and retrieving UTF-8 text.
$u = sqlsrv_get_field($s, 1, SQLSRV_PHPTYPE_STREAM('utf-8'));
if ($u === false) {
if (AE\isColEncrypted()) {
verifyError(sqlsrv_errors()[0], 'IMSSP', 'Connection with Column Encryption enabled does not support fetching stream. Please fetch the data as a string.');
} else {
die(print_r(sqlsrv_errors(), true));
}
die(print_r(sqlsrv_errors(), true));
} else {
$utf8_2 = fread($u, 10000);
if ($utf8 != $utf8_2) {

View file

@ -1,113 +1,170 @@
<?php
// exact numerics
$bigint_params = array( 2147483648, -922337203685479936, 922337203685479936, -2147583649, 461168601735364608, -461168601735364608 );
$int_params = array( 32768, -2147483647, 2147483647, -32769, 1073725440, -1073725440 );
$smallint_params = array( 256, -32767, 32767, -1, 16256, -16256 );
$tinyint_params = array( 128, 0, 255, 96, 64, 162 );
$decimal_params = array( 21474.83648, -9223372036854.80000, 9223372036854.80000, -21475.83649, -4611686017353.64608, -4611686017353.64608 );
$numeric_params = array( 0.32768, -21474.83647, 21474.83647, -0.32769, 10737.25440, -10737.25440 );
$money_params = array( 214748.3648, -92233720368548.0000, 92233720368548.0000, -214758.3649, 46116860173536.608, -46116860173536.608 );
$smallmoney_params = array( 0, -214748.3647, 214748.3647, 161061.2736, 107374.1824, -107374.1824 );
$bit_params = array( 0, 1, 0, 1, 0, 1 );
// approximate numerics
$float_params = array( 21474.83648, -9223372036.8548, 9223372036.8548, -21475, 4611686017, -4611686017 );
$real_params = array( 0, -2147.483, 2147.483, 1610, 1073, -1073 );
// date and time
$date_params = array( '1900-01-01', '0001-01-01', '9999-12-31', '5000-07-15', '2500-04-08', '7500-10-23' );
$datetime2_params = array( '1900-01-01 00:00:00', '0001-01-01 00:00:00', '9999-12-31 23:59:59.123456', '5000-07-15 12:30:30.5555', '2500-04-08 06:15:15.33', '7500-10-23 18:45:45.888888' );
$datetime_params = array( '1900-01-01 00:00:00', '1753-01-01 00:00:00', '9999-12-31 23:59:59.997', '5000-07-15 12:30:30.5', '2500-04-08 06:15:15.33', '7500-10-23 18:45:45.888' );
$datetimeoffset_params = array( '1900-01-01 00:00:00 +01:00', '0001-01-01 00:00:00 -14:00', '9999-12-31 23:59:59.123456+14:00', '5000-07-15 12:30:30.55 -03:00', '2500-04-08 06:15:15.3333 -07:00', '7500-10-23 18:45:45.888888 +07:00' );
$smalldatetime_params = array( '1900-01-01 00:00:00', '1900-01-01 00:00:00', '2079-06-05 23:59:00', '1990-07-15 12:30:00', '1945-04-08 06:15:00', '2000-10-23 18:45:00' );
$time_params = array( '00:00:00', '00:00:00.0000000', '23:59:59.123456', '12:30:30.5555', '06:15:15.33', '18:45:45.888888' );
// character strings
$char_params = array( 'Fixed', '-leng', 'th, n', 'on-Un', 'icode', 'strin' );
$varchar_params = array( 'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.',
'Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.',
'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.' );
// unicode character strings
$nchar_params = array( 'Fixed', '-leng', 'th Un', 'icode', 'strin', 'g dat' );
$nvarchar_params = array( 'max indicates that the maximum storage size is 2^31-1 bytes (2 GB).',
'When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).',
'Otherwise, the implicit conversion will result in a Unicode large-value (max).',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.' );
// binary strings
$binary_params = array( 'Fixed', '-leng', 'th, n', 'on-Un', 'icode', 'strin' );
$varbinary_params = array( 'Variable-length, non-', 'Unicode string data. n', 'defines the string length', 'and can be a value from 1', 'through 8,000.', 'The storage size is the' );
$varbinarymax_params = array( 'max indicates that the maximum storage size is 2^31-1 bytes (2 GB)',
'Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.',
'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.' );
// Array containing all SQLSRV_SQLTYPE_ types to pass into a bind param prepare statement
$sqlTypes = array(
'SQLSRV_SQLTYPE_BIGINT',
'SQLSRV_SQLTYPE_BINARY',
'SQLSRV_SQLTYPE_BIT',
'SQLSRV_SQLTYPE_CHAR',
'SQLSRV_SQLTYPE_DATE',
'SQLSRV_SQLTYPE_DATETIME',
'SQLSRV_SQLTYPE_DATETIME2',
'SQLSRV_SQLTYPE_DATETIMEOFFSET',
'SQLSRV_SQLTYPE_DECIMAL',
'SQLSRV_SQLTYPE_FLOAT',
'SQLSRV_SQLTYPE_IMAGE',
'SQLSRV_SQLTYPE_INT',
'SQLSRV_SQLTYPE_MONEY',
'SQLSRV_SQLTYPE_NCHAR',
'SQLSRV_SQLTYPE_NUMERIC',
'SQLSRV_SQLTYPE_NVARCHAR',
'SQLSRV_SQLTYPE_NTEXT',
'SQLSRV_SQLTYPE_REAL',
'SQLSRV_SQLTYPE_SMALLDATETIME',
'SQLSRV_SQLTYPE_SMALLINT',
'SQLSRV_SQLTYPE_SMALLMONEY',
'SQLSRV_SQLTYPE_TEXT',
'SQLSRV_SQLTYPE_TIME',
'SQLSRV_SQLTYPE_TIMESTAMP',
'SQLSRV_SQLTYPE_TINYINT',
'SQLSRV_SQLTYPE_UNIQUEIDENTIFIER',
'SQLSRV_SQLTYPE_VARBINARY',
'SQLSRV_SQLTYPE_VARCHAR',
'SQLSRV_SQLTYPE_XML'
);
// Checks if the current error is the incompatible types error
// if so, state which sql type is incompatible with which data type
function is_incompatible_types_error( $dataType, $sqlType )
{
$errors = sqlsrv_errors();
foreach ( $errors as $error )
{
// 22018 is the SQLSTATE for the operand crash error for incompatible types
if ( $error['SQLSTATE'] == 22018 )
{
echo "Encrypted $sqlType is incompatible with encrypted $dataType\n";
}
}
}
function get_default_size_prec( $sqlType )
{
if ( $sqlType == 'SQLSRV_SQLTYPE_DECIMAL' )
$sqlType .= "(18, 5)";
elseif ( $sqlType == 'SQLSRV_SQLTYPE_NUMERIC' )
$sqlType .= "(10, 5)";
elseif ( $sqlType == 'SQLSRV_SQLTYPE_CHAR' || $sqlType == 'SQLSRV_SQLTYPE_NCHAR' )
$sqlType .= "(5)";
return $sqlType;
}
?>
<?php
// exact numerics
$bigint_params = array( 2147483648, -922337203685479936, 922337203685479936, -2147583649, 461168601735364608, -461168601735364608 );
$int_params = array( 32768, -2147483647, 2147483647, -32769, 1073725440, -1073725440 );
$smallint_params = array( 256, -32767, 32767, -1, 16256, -16256 );
$tinyint_params = array( 128, 0, 255, 96, 64, 162 );
$decimal_params = array( 21474.83648, -9223372036854.80000, 9223372036854.80000, -21475.83649, -4611686017353.64608, -4611686017353.64608 );
$numeric_params = array( 0.32768, -21474.83647, 21474.83647, -0.32769, 10737.25440, -10737.25440 );
$money_params = array( 214748.3648, -92233720368548.0000, 92233720368548.0000, -214758.3649, 46116860173536.608, -46116860173536.608 );
$smallmoney_params = array( 0, -214748.3647, 214748.3647, 161061.2736, 107374.1824, -107374.1824 );
$bit_params = array( 0, 1, 0, 1, 0, 1 );
// approximate numerics
$float_params = array( 21474.83648, -9223372036.8548, 9223372036.8548, -21475, 4611686017, -4611686017 );
$real_params = array( 0, -2147.483, 2147.483, 1610, 1073, -1073 );
// date and time
$date_params = array( '1900-01-01', '0001-01-01', '9999-12-31', '5000-07-15', '2500-04-08', '7500-10-23' );
$datetime2_params = array( '1900-01-01 00:00:00', '0001-01-01 00:00:00', '9999-12-31 23:59:59.123456', '5000-07-15 12:30:30.5555', '2500-04-08 06:15:15.33', '7500-10-23 18:45:45.888888' );
$datetime_params = array( '1900-01-01 00:00:00', '1753-01-01 00:00:00', '9999-12-31 23:59:59.997', '5000-07-15 12:30:30.5', '2500-04-08 06:15:15.33', '7500-10-23 18:45:45.888' );
$datetimeoffset_params = array( '1900-01-01 00:00:00 +01:00', '0001-01-01 00:00:00 -14:00', '9999-12-31 23:59:59.123456+14:00', '5000-07-15 12:30:30.55 -03:00', '2500-04-08 06:15:15.3333 -07:00', '7500-10-23 18:45:45.888888 +07:00' );
$smalldatetime_params = array( '1900-01-01 00:00:00', '1900-01-01 00:00:00', '2079-06-05 23:59:00', '1990-07-15 12:30:00', '1945-04-08 06:15:00', '2000-10-23 18:45:00' );
$time_params = array( '00:00:00', '00:00:00.0000000', '23:59:59.123456', '12:30:30.5555', '06:15:15.33', '18:45:45.888888' );
// character strings
$char_params = array( 'Fixed', '-leng', 'th, n', 'on-Un', 'icode', 'strin' );
$varchar_params = array( 'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.',
'Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.',
'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.' );
// unicode character strings
$nchar_params = array( 'Fixed', '-leng', 'th Un', 'icode', 'strin', 'g dat' );
$nvarchar_params = array( 'max indicates that the maximum storage size is 2^31-1 bytes (2 GB).',
'When prefixing a string constant with the letter N, the implicit conversion will result in a Unicode string if the constant to convert does not exceed the max length for a Unicode string data type (4,000).',
'Otherwise, the implicit conversion will result in a Unicode large-value (max).',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.' );
// binary strings
$binary_params = array( 'Fixed', '-leng', 'th, n', 'on-Un', 'icode', 'strin' );
$varbinary_params = array( 'Variable-length, non-', 'Unicode string data. n', 'defines the string length', 'and can be a value from 1', 'through 8,000.', 'The storage size is the' );
$varbinarymax_params = array( 'max indicates that the maximum storage size is 2^31-1 bytes (2 GB)',
'Use varchar(max) when the sizes of the column data entries vary considerably, and the size might exceed 8,000 bytes.',
'Each non-null varchar(max) or nvarchar(max) column requires 24 bytes of additional fixed allocation which counts against the 8,060 byte row limit during a sort operation.',
'This can create an implicit limit to the number of non-null varchar(max) or nvarchar(max) columns that can be created in a table.',
'No special error is provided when the table is created (beyond the usual warning that the maximum row size exceeds the allowed maximum of 8060 bytes) or at the time of data insertion.',
'This large row size can cause errors (such as error 512) during some normal operations, such as a clustered index key update, or sorts of the full column set, which users cannot anticipate until performing an operation.' );
// Array containing all SQLSRV_SQLTYPE_ types to pass into a bind param prepare statement
$sqlTypes = array(
'SQLSRV_SQLTYPE_BIGINT',
'SQLSRV_SQLTYPE_BINARY',
'SQLSRV_SQLTYPE_BIT',
'SQLSRV_SQLTYPE_CHAR',
'SQLSRV_SQLTYPE_DATE',
'SQLSRV_SQLTYPE_DATETIME',
'SQLSRV_SQLTYPE_DATETIME2',
'SQLSRV_SQLTYPE_DATETIMEOFFSET',
'SQLSRV_SQLTYPE_DECIMAL',
'SQLSRV_SQLTYPE_FLOAT',
'SQLSRV_SQLTYPE_IMAGE',
'SQLSRV_SQLTYPE_INT',
'SQLSRV_SQLTYPE_MONEY',
'SQLSRV_SQLTYPE_NCHAR',
'SQLSRV_SQLTYPE_NUMERIC',
'SQLSRV_SQLTYPE_NVARCHAR',
'SQLSRV_SQLTYPE_NTEXT',
'SQLSRV_SQLTYPE_REAL',
'SQLSRV_SQLTYPE_SMALLDATETIME',
'SQLSRV_SQLTYPE_SMALLINT',
'SQLSRV_SQLTYPE_SMALLMONEY',
'SQLSRV_SQLTYPE_TEXT',
'SQLSRV_SQLTYPE_TIME',
'SQLSRV_SQLTYPE_TIMESTAMP',
'SQLSRV_SQLTYPE_TINYINT',
'SQLSRV_SQLTYPE_UNIQUEIDENTIFIER',
'SQLSRV_SQLTYPE_VARBINARY',
'SQLSRV_SQLTYPE_VARCHAR',
'SQLSRV_SQLTYPE_XML'
);
// Checks if the current error is the incompatible types error
// if so, state which sql type is incompatible with which data type
function is_incompatible_types_error( $dataType, $sqlType )
{
$errors = sqlsrv_errors();
foreach ( $errors as $error )
{
// 22018 is the SQLSTATE for the operand crash error for incompatible types
if ( $error['SQLSTATE'] == 22018 )
{
echo "Encrypted $sqlType is incompatible with encrypted $dataType\n";
}
}
}
function get_default_size_prec( $sqlType )
{
if ( $sqlType == 'SQLSRV_SQLTYPE_DECIMAL' )
$sqlType .= "(18, 5)";
elseif ( $sqlType == 'SQLSRV_SQLTYPE_NUMERIC' )
$sqlType .= "(10, 5)";
elseif ( $sqlType == 'SQLSRV_SQLTYPE_CHAR' || $sqlType == 'SQLSRV_SQLTYPE_NCHAR' )
$sqlType .= "(5)";
return $sqlType;
}
// get sqlType constant value from string
function get_sqlType_constant( $sqlType )
{
switch ( $sqlType ) {
case 'SQLSRV_SQLTYPE_BIGINT':
case 'SQLSRV_SQLTYPE_BINARY':
case 'SQLSRV_SQLTYPE_BIT':
case 'SQLSRV_SQLTYPE_DATE':
case 'SQLSRV_SQLTYPE_DATETIME':
case 'SQLSRV_SQLTYPE_DATETIME2':
case 'SQLSRV_SQLTYPE_DATETIMEOFFSET':
case 'SQLSRV_SQLTYPE_FLOAT':
case 'SQLSRV_SQLTYPE_IMAGE':
case 'SQLSRV_SQLTYPE_INT':
case 'SQLSRV_SQLTYPE_MONEY':
case 'SQLSRV_SQLTYPE_NVARCHAR':
case 'SQLSRV_SQLTYPE_NTEXT':
case 'SQLSRV_SQLTYPE_REAL':
case 'SQLSRV_SQLTYPE_SMALLDATETIME':
case 'SQLSRV_SQLTYPE_SMALLINT':
case 'SQLSRV_SQLTYPE_SMALLMONEY':
case 'SQLSRV_SQLTYPE_TEXT':
case 'SQLSRV_SQLTYPE_TIME':
case 'SQLSRV_SQLTYPE_TIMESTAMP':
case 'SQLSRV_SQLTYPE_TINYINT':
case 'SQLSRV_SQLTYPE_UNIQUEIDENTIFIER':
case 'SQLSRV_SQLTYPE_VARBINARY':
case 'SQLSRV_SQLTYPE_VARCHAR':
case 'SQLSRV_SQLTYPE_XML':
return constant( $sqlType );
break;
case 'SQLSRV_SQLTYPE_CHAR':
// our tests always use precision 5 for SQLSRV_SQLTYPE_CHAR
return SQLSRV_SQLTYPE_CHAR(5);
break;
case 'SQLSRV_SQLTYPE_DECIMAL':
// our tests always use precision 18 scale 5 for SQLSRV_SQLTYPE_DECIMAL
return SQLSRV_SQLTYPE_DECIMAL(18, 5);
break;
case 'SQLSRV_SQLTYPE_NCHAR':
// our tests always use precision 5 for SQLSRV_SQLTYPE_NCHAR
return SQLSRV_SQLTYPE_NCHAR(5);
break;
case 'SQLSRV_SQLTYPE_NUMERIC':
// our tests always use precision 10 scale 5 for SQLSRV_SQLTYPE_NUMERIC
return SQLSRV_SQLTYPE_NUMERIC(10, 5);
break;
default:
die( "get_sqlType_constant: Invalid SQL Type $sqlType\n" );
break;
}
}
function isDateTimeType( $sqlType )
{
return ($sqlType == 'SQLSRV_SQLTYPE_DATE' ||
$sqlType == 'SQLSRV_SQLTYPE_DATETIME' ||
$sqlType == 'SQLSRV_SQLTYPE_DATETIME2' ||
$sqlType == 'SQLSRV_SQLTYPE_DATETIMEOFFSET' ||
$sqlType == 'SQLSRV_SQLTYPE_SMALLDATETIME' ||
$sqlType == 'SQLSRV_SQLTYPE_TIME');
}
?>

View file

@ -84,18 +84,6 @@ function isDaasMode()
return ($daasMode ? true : false);
}
// function isAEQualified($conn)
// {
// $msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
// $server_ver = sqlsrv_server_info($conn)['SQLServerVersion'];
// $msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
// $msodbcsql_min = explode(".", $msodbcsql_ver)[1];
// if ($msodbcsql_maj < 17 || explode('.', $server_ver)[0] < 13) {
// return false;
// }
// return true;
// }
function startTest($testName)
{
if (traceMode()) {
@ -454,11 +442,36 @@ function handleErrors()
}
}
function setUSAnsiLocale()
{
if (!isWindows()) {
// macOS the locale names are different in Linux or macOS
$locale = strtoupper(PHP_OS) === 'LINUX' ? "en_US.ISO-8859-1" : "en_US.ISO8859-1";
setlocale(LC_ALL, $locale);
}
}
function resetLocaleToDefault()
{
// Like setUSAnsiLocale() above, this method is only needed in non-Windows environment
if (!isWindows()) {
setlocale(LC_ALL, null);
}
}
// non-UTF8 locale support in ODBC 17 and above only
// if AE enabled, only supported in Windows (AE limitations)
function isLocaleSupported()
{
if (isWindows()) {
return true;
}
if (AE\isColEncrypted()) {
return false;
}
// now check ODBC version
$conn = AE\connect();
$msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
if (explode(".", $msodbcsql_ver)[0] < 17) {
return false;

View file

@ -22,10 +22,6 @@ const INSERT_PREPARE = 2;
const INSERT_QUERY_PARAMS = 3;
const INSERT_PREPARE_PARAMS = 4;
const KSP_NAME = 'MyCustomKSPName';
const ENCRYPT_KEY = 'LPKCWVD07N3RG98J0MBLG4H2';
const KSP_TEST_TABLE = 'CustomKSPTestTable';
/**
* class for encapsulating column metadata needed for creating a table
*/
@ -161,13 +157,16 @@ class BindParamOption
$type_size = explode("(", $this->sqlType);
$type = $type_size[0];
if (count($type_size) > 1) {
$size = $type_size[1];
$size = rtrim($type_size[1], ")");
$prec_scal = explode(",", $size);
if (count($prec_scal) > 1) {
$prec = $prec_scal[0];
$scal = rtrim($prec_scal[1], ")");
$scal = $prec_scal[1];
$size = null;
}
if (strpos($size, "max") !== false) {
$size = trim($size, "'");
}
}
// get the sqlType constant
try {
@ -214,29 +213,6 @@ function getCekName()
return $cekName;
}
/**
* @return the path to the KSP dll/so file
*/
function getKSPpath()
{
$name = 'myKSP';
$dir_name = realpath(dirname(__FILE__));
$ksp = $dir_name . DIRECTORY_SEPARATOR . $name;
if (strtoupper(substr(php_uname('s'), 0, 3)) == 'WIN') {
$arch = 'x64';
if (PHP_INT_SIZE == 4) {
// running 32 bit
$arch = '';
}
$ksp .= $arch . '.dll';
} else {
$ksp .= '.so';
}
return $ksp;
}
/**
* @return string default column name when a name is not provided in the ColumnMeta class
*/
@ -330,12 +306,8 @@ function getSeqPlaceholders($num)
*/
function isColEncrypted()
{
global $keystore, $dataEncrypted;
if ($keystore === KEYSTORE_NONE) {
return false;
} else {
return true;
}
global $keystore;
return ($keystore !== KEYSTORE_NONE);
}
/**
@ -345,11 +317,7 @@ function isColEncrypted()
function isDataEncrypted()
{
global $keystore, $dataEncrypted;
if ($keystore === KEYSTORE_NONE || !$dataEncrypted) {
return false;
} else {
return true;
}
return ($keystore !== KEYSTORE_NONE && $dataEncrypted);
}
/**
@ -361,6 +329,12 @@ function isQualified($conn)
if (explode(".", $msodbcsql_ver)[0] < 17) {
return false;
}
global $daasMode;
if ($daasMode) {
// running against Azure
return true;
}
// if not Azure, check the server version
$server_ver = sqlsrv_server_info($conn)['SQLServerVersion'];
if (explode('.', $server_ver)[0] < 13) {
return false;
@ -385,13 +359,6 @@ function connect($options = array(), $disableCE = false)
if (isColEncrypted()) {
$connectionOptions = array_merge($connectionOptions, array("ColumnEncryption" => "Enabled"));
}
if ($keystore == "ksp") {
$ksp_path = getKSPPath();
$ksp_options = array("CEKeystoreProvider"=>$ksp_path,
"CEKeystoreName"=>KSP_NAME,
"CEKeystoreEncryptKey"=>ENCRYPT_KEY);
$connectionOptions = array_merge($connectionOptions, $ksp_options);
}
}
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {

View file

@ -6,18 +6,23 @@ Validates that a prepared statement can be successfully executed more than once.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?php
// locale must be set before 1st connection
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
require_once('MsCommon.inc');
function prepareAndExecute($noPasses)
{
$testName = "Statement - Prepare and Execute";
startTest($testName);
setup();
$conn1 = AE\connect();
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
}
$tableName = 'TC34test';
AE\createTestTable($conn1, $tableName);
@ -80,20 +85,36 @@ function prepareAndExecute($noPasses)
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
if (!isWindows()) {
setUTF8Data(true);
// locale must be set before 1st connection
setUSAnsiLocale();
$testName = "Statement - Prepare and Execute";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isLocaleSupported()) {
try {
setUTF8Data(false);
prepareAndExecute(5);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
prepareAndExecute(5);
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Statement - Prepare and Execute" completed successfully.
Test "Statement - Prepare and Execute" completed successfully.

View file

@ -6,20 +6,20 @@ retrieving fields from a table including rows with all supported SQL types (28 t
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?php
// locale must be set before 1st connection
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
require_once('MsCommon.inc');
function fetchFields()
{
$testName = "Fetch - Field";
startTest($testName);
setup();
$tableName = 'TC42test';
if (! isWindows()) {
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
@ -33,9 +33,6 @@ function fetchFields()
$stmt1 = AE\selectFromTable($conn1, $tableName);
$numFields = sqlsrv_num_fields($stmt1);
$errState = 'IMSSP';
$errMessage = 'Connection with Column Encryption enabled does not support fetching stream. Please fetch the data as a string.';
trace("Retrieving $noRowsInserted rows with $numFields fields each ...");
for ($i = 0; $i < $noRowsInserted; $i++) {
$row = sqlsrv_fetch($stmt1);
@ -44,16 +41,9 @@ function fetchFields()
}
for ($j = 0; $j < $numFields; $j++) {
$fld = sqlsrv_get_field($stmt1, $j);
// With AE enabled, those fields that sqlsrv_get_field() will fetch
// as stream data will return a specific error message
$col = $j+1;
if ($fld === false) {
if (AE\isColEncrypted() && isStreamData($col)) {
verifyError(sqlsrv_errors()[0], $errState, $errMessage);
} else {
fatalError("Field $j of Row $i is missing\n", true);
}
fatalError("Field $j of Row $i is missing\n", true);
}
}
}
@ -63,16 +53,36 @@ function fetchFields()
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
// locale must be set before 1st connection
setUSAnsiLocale();
$testName = "Fetch - Field";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isLocaleSupported()) {
try {
setUTF8Data(false);
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
endTest($testName);
?>
--EXPECT--
Test "Fetch - Field" completed successfully.
Test "Fetch - Field" completed successfully.

View file

@ -5,9 +5,8 @@ PHPT_EXEC=true
--SKIPIF--
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
@ -96,15 +95,12 @@ function checkData($col, $actual, $expected)
return ($success);
}
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
$testName = "Fetch - Field Data";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
if (isLocaleSupported()) {
try {
setUTF8Data(false);
@ -119,6 +115,7 @@ endTest($testName);
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();

View file

@ -8,9 +8,8 @@ PHPT_EXEC=true
--SKIPIF--
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
@ -152,16 +151,12 @@ function checkData($row, $stmt, $index, $mode)
}
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
setUSAnsiLocale();
$testName = "Fetch - Array";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
if (isLocaleSupported()) {
try {
setUTF8Data(false);
fetchRow(1, 4);
@ -175,6 +170,7 @@ endTest($testName);
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchRow(1, 4);
} catch (Exception $e) {
echo $e->getMessage();

View file

@ -5,7 +5,11 @@ Verifies data retrieval via "sqlsrv_fetch_object".
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?php
// locale must be set before 1st connection
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
require_once('MsCommon.inc');
@ -19,14 +23,10 @@ class TestClass
function fetchRow($minFetchMode, $maxFetchMode)
{
$testName = "Fetch - Object";
startTest($testName);
setup();
$tableName = 'TC45test';
if (! isWindows()) {
$conn1 = AE\connect(array( 'CharacterSet'=>'UTF-8' ));
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
}
@ -73,8 +73,6 @@ function fetchRow($minFetchMode, $maxFetchMode)
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
@ -89,7 +87,7 @@ function fetchObject($stmt, $rows, $fields, $useClass)
$obj = sqlsrv_fetch_object($stmt);
}
if ($obj === false) {
fatalError("Row $i is missing");
fatalError("In fetchObject: Row $i is missing");
}
$values[$i] = $obj;
}
@ -103,7 +101,7 @@ function fetchArray($stmt, $rows, $fields)
for ($i = 0; $i < $rows; $i++) {
$row = sqlsrv_fetch_array($stmt);
if ($row === false) {
fatalError("Row $i is missing");
fatalError("In fetchArray: Row $i is missing");
}
$values[$i] = $row;
}
@ -127,12 +125,34 @@ function checkData($rows, $fields, $actualValues, $expectedValues)
}
}
// locale must be set before 1st connection
setUSAnsiLocale();
$testName = "Fetch - Object";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isLocaleSupported()) {
try {
setUTF8Data(false);
fetchRow(0, 2);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchRow(0, 2);
} catch (Exception $e) {
echo $e->getMessage();
}
endTest($testName);
?>
--EXPECT--
Test "Fetch - Object" completed successfully.
Test "Fetch - Object" completed successfully.

View file

@ -7,9 +7,8 @@ PHPT_EXEC=true
--SKIPIF--
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
@ -85,16 +84,12 @@ function fetchFields()
}
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
setUSAnsiLocale();
$testName = "Fetch - Next Result";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
if (isLocaleSupported()) {
try {
setUTF8Data(false);
fetchFields();
@ -108,6 +103,7 @@ endTest($testName);
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();

View file

@ -5,19 +5,20 @@ Verifies data retrieval with scrollable result sets.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?php
// locale must be set before 1st connection
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
require_once('MsCommon.inc');
function fetchRow($noRows)
{
$testName = "Fetch - Scrollable";
startTest($testName);
setup();
$tableName = 'TC48test';
if (! isWindows()) {
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
@ -44,36 +45,30 @@ function fetchRow($noRows)
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
// Always Encrypted feature does not support the following options
// https://github.com/Microsoft/msphpsql/wiki/Features#aelimitation
if (!AE\isColEncrypted()) {
// fetch object - STATIC cursor
$options = array('Scrollable' => SQLSRV_CURSOR_STATIC);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_RELATIVE);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
// fetch object - STATIC cursor
$options = array('Scrollable' => SQLSRV_CURSOR_STATIC);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_RELATIVE);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
// fetch object - DYNAMIC cursor
$options = array('Scrollable' => SQLSRV_CURSOR_DYNAMIC);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_ABSOLUTE);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
// fetch object - DYNAMIC cursor
$options = array('Scrollable' => SQLSRV_CURSOR_DYNAMIC);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_ABSOLUTE);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
// fetch object - KEYSET cursor
$options = array('Scrollable' => SQLSRV_CURSOR_KEYSET);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_PRIOR, 0);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
}
// fetch object - KEYSET cursor
$options = array('Scrollable' => SQLSRV_CURSOR_KEYSET);
$stmt2 = AE\executeQueryEx($conn1, $query, $options);
$actual = fetchObject($stmt2, $noRowsInserted, $numFields, SQLSRV_SCROLL_PRIOR, 0);
sqlsrv_free_stmt($stmt2);
checkData($noRowsInserted, $numFields, $actual, $expected);
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
function fetchArray($stmt, $rows, $fields)
@ -135,12 +130,34 @@ function checkData($rows, $fields, $actualValues, $expectedValues)
}
}
// locale must be set before 1st connection
setUSAnsiLocale();
$testName = "Fetch - Scrollable";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isLocaleSupported()) {
try {
setUTF8Data(false);
fetchRow(10);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
fetchRow(10);
} catch (Exception $e) {
echo $e->getMessage();
}
endTest($testName);
?>
--EXPECT--
Test "Fetch - Scrollable" completed successfully.
Test "Fetch - Scrollable" completed successfully.

View file

@ -7,9 +7,8 @@ can be successfully retrieved as streams.
PHPT_EXEC=true
--SKIPIF--
<?// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
@ -69,7 +68,7 @@ function verifyStream($stmt, $row, $colIndex)
}
}
if ($stream === false) {
verifyStreamError("Failed to read field $col: $type");
fatalError("Failed to read field $col: $type");
} else {
$value = '';
if ($stream) {
@ -88,16 +87,6 @@ function verifyStream($stmt, $row, $colIndex)
}
}
function verifyStreamError($message)
{
global $errState, $errMessage;
if (AE\isColEncrypted()) {
verifyError(sqlsrv_errors()[0], $errState, $errMessage);
} else {
fatalError($message);
}
}
function checkData($col, $actual, $expected)
{
$success = true;
@ -125,20 +114,13 @@ function checkData($col, $actual, $expected)
}
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
global $testName;
$testName = "Stream - Read";
// error message expected with AE enabled
$errState = 'IMSSP';
$errMessage = 'Connection with Column Encryption enabled does not support fetching stream. Please fetch the data as a string.';
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
if (isLocaleSupported()) {
try {
setUTF8Data(false);
streamRead(20, 1);
@ -152,6 +134,7 @@ endTest($testName);
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
streamRead(20, 1);
} catch (Exception $e) {
echo $e->getMessage();

View file

@ -7,9 +7,8 @@ PHPT_EXEC=true
--SKIPIF--
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
setUSAnsiLocale();
require('skipif_versions_old.inc');
?>
--FILE--
<?php
@ -30,57 +29,31 @@ function streamScroll($noRows, $startRow)
AE\insertTestRowsByRange($conn1, $tableName, $startRow, $startRow + $noRows - 1);
$query = "SELECT * FROM [$tableName] ORDER BY c27_timestamp";
// Always Encrypted feature does not support SQLSRV_CURSOR_STATIC
// https://github.com/Microsoft/msphpsql/wiki/Features#aelimitation
if (AE\isColEncrypted()) {
$options = array('Scrollable' => SQLSRV_CURSOR_FORWARD);
} else {
$options = array('Scrollable' => SQLSRV_CURSOR_STATIC);
}
$options = array('Scrollable' => SQLSRV_CURSOR_STATIC);
$stmt1 = AE\executeQueryEx($conn1, $query, $options);
$numFields = sqlsrv_num_fields($stmt1);
if (AE\isColEncrypted()) {
$row = $startRow;
while ($row <= $noRows) {
if (!sqlsrv_fetch($stmt1, SQLSRV_SCROLL_NEXT)) {
$row = $noRows;
while ($row >= 1) {
if ($row == $noRows) {
if (!sqlsrv_fetch($stmt1, SQLSRV_SCROLL_LAST)) {
fatalError("Failed to fetch row ".$row);
}
trace("\nStreaming row $row:\n");
for ($j = 0; $j < $numFields; $j++) {
$col = $j + 1;
if (!isUpdatable($col)) {
continue;
}
if (isStreamable($col)) {
verifyStream($stmt1, $startRow + $row - 1, $j);
}
} else {
if (!sqlsrv_fetch($stmt1, SQLSRV_SCROLL_PRIOR)) {
fatalError("Failed to fetch row ".$row);
}
$row++;
}
} else {
$row = $noRows;
while ($row >= 1) {
if ($row == $noRows) {
if (!sqlsrv_fetch($stmt1, SQLSRV_SCROLL_LAST)) {
fatalError("Failed to fetch row ".$row);
}
} else {
if (!sqlsrv_fetch($stmt1, SQLSRV_SCROLL_PRIOR)) {
fatalError("Failed to fetch row ".$row);
}
trace("\nStreaming row $row:\n");
for ($j = 0; $j < $numFields; $j++) {
$col = $j + 1;
if (!isUpdatable($col)) {
continue;
}
trace("\nStreaming row $row:\n");
for ($j = 0; $j < $numFields; $j++) {
$col = $j + 1;
if (!isUpdatable($col)) {
continue;
}
if (isStreamable($col)) {
verifyStream($stmt1, $startRow + $row - 1, $j);
}
if (isStreamable($col)) {
verifyStream($stmt1, $startRow + $row - 1, $j);
}
$row--;
}
$row--;
}
sqlsrv_free_stmt($stmt1);
@ -162,11 +135,7 @@ function checkData($col, $actual, $expected)
}
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
setUSAnsiLocale();
$testName = "Stream - Scrollable";
// error message expected with AE enabled
@ -175,7 +144,7 @@ $errMessage = 'Connection with Column Encryption enabled does not support fetchi
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
if (isLocaleSupported()) {
try {
setUTF8Data(false);
streamScroll(20, 1);
@ -189,6 +158,7 @@ endTest($testName);
startTest($testName);
try {
setUTF8Data(true);
resetLocaleToDefault();
streamScroll(20, 1);
} catch (Exception $e) {
echo $e->getMessage();

View file

@ -272,9 +272,6 @@ function runTest($noPasses, $noRows, $tableName, $conn, $prepared, $release, $mo
break;
case 5: // fetch fields
$errState = 'IMSSP';
$errMessage = 'Connection with Column Encryption enabled does not support fetching stream. Please fetch the data as a string.';
$stmt = execQuery($conn, $tableName, $prepared);
$numFields = sqlsrv_num_fields($stmt);
while (sqlsrv_fetch($stmt)) {
@ -284,11 +281,7 @@ function runTest($noPasses, $noRows, $tableName, $conn, $prepared, $release, $mo
$col = $i + 1;
if ($fld === false) {
if (AE\isColEncrypted() && isStreamData($col)) {
verifyError(sqlsrv_errors()[0], $errState, $errMessage);
} else {
fatalError("Field $i of row $rowCount is missing");
}
fatalError("Field $i of row $rowCount is missing");
}
unset($fld);
}

View file

@ -1,18 +0,0 @@
<?php
if (!extension_loaded("sqlsrv"))
die("skip extension not loaded");
require_once('MsCommon.inc');
if ($keystore != AE\KEYSTORE_KSP) {
die('skip - this test requires a custom keystore provider.');
}
$conn = AE\connect();
if (! $conn) {
echo("Error: could not connect during SKIPIF!");
} elseif (AE\isColEncrypted() && !AE\isQualified($conn)) {
die("skip - AE feature not supported in the current environment.");
}
?>

View file

@ -1,15 +1,21 @@
<?php
if (! extension_loaded("sqlsrv")) {
die("skip extension not loaded");
}
require_once('MsCommon.inc');
$conn = AE\connect();
if (! $conn) {
echo("Error: could not connect during SKIPIF!");
} elseif (AE\isColEncrypted() && !AE\isQualified($conn)) {
die("skip - AE feature not supported in the current environment.");
}
<?php
if (! extension_loaded("sqlsrv")) {
die("skip extension not loaded");
}
require_once('MsCommon.inc');
$conn = AE\connect();
if (! $conn) {
echo("Error: could not connect during SKIPIF!");
} elseif (AE\isColEncrypted()) {
if (!isWindows()) {
die( "Skip, AE test on windows only." );
}
if (!AE\isQualified($conn)) {
die("skip - AE feature not supported in the current environment.");
}
}
?>

View file

@ -0,0 +1,59 @@
--TEST--
GitHub issue #623 - data is correctly fetched using a client buffer even with varchar(max) in the result set
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
<?php
sqlsrv_configure('WarningsReturnAsErrors', 1);
// step 01: Connect without Always Encrypted feature
require_once('MsSetup.inc');
$conn = sqlsrv_connect($server, $connectionOptions);
if (! $conn) {
fatalError("Failed to connect\n");
}
$tableName = 'systemtag';
// step 02: Setup table
require_once('MsCommon.inc');
dropTable($conn, $tableName);
$sql = "CREATE TABLE [$tableName](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](255) NOT NULL,
[tag] [varchar](max) NULL,
CONSTRAINT [PK_usertag] PRIMARY KEY CLUSTERED (
[id] ASC
))";
$stmt = sqlsrv_query($conn, $sql);
// step 03: Insert test data
$name = 'Disclaimer e-mail';
$tag = 'De informatie van deze e-mail en de eventueel bijgevoegde bestanden is vertrouwelijk en kan juridisch beschermd zijn. Het is uitsluitend bedoeld voor degene(n) aan wie het gericht is of degene(n) die geautoriseerd zijn om het bericht te ontvangen. Indien het bericht niet voor u bestemd is, wordt u verzocht de inhoud ervan niet te lezen, en het bericht aan ons terug te sturen. In dat geval wijzen wij u er tevens op dat het kopië';
$sql = "INSERT INTO $tableName (name, tag) VALUES (?, ?)";
$parameters = [$name, $tag];
$stmt = sqlsrv_query($conn, $sql, $parameters);
// step 04: Fetch the data
$sql = "SELECT name, tag FROM $tableName";
$stmt = sqlsrv_query( $conn, $sql, [], ['Scrollable' => SQLSRV_CURSOR_CLIENT_BUFFERED]);
$result = sqlsrv_fetch($stmt);
if ($result) {
$value1 = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
var_dump($value1 === $name);
$value2 = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
var_dump($value2 === $tag);
} else {
fatalError('Something went wrong\n');
}
dropTable($conn, $tableName);
echo "Done\n";
?>
--EXPECT--
bool(true)
bool(true)
Done

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