resolved merge conflicts
This commit is contained in:
commit
117b2bf777
24
.travis.yml
24
.travis.yml
|
@ -16,21 +16,33 @@ env:
|
|||
- 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
|
||||
|
||||
install:
|
||||
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password123' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d microsoft/mssql-server-linux
|
||||
- docker build --build-arg PHPSQLDIR=$PHPSQLDIR -t msphpsql-dev -f Dockerfile-msphpsql .
|
||||
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password12@' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d microsoft/mssql-server-linux
|
||||
|
||||
before_script:
|
||||
- sleep 30
|
||||
|
||||
script:
|
||||
- docker run -e TRAVIS_JOB_ID -t -d -w $PHPSQLDIR --link $TEST_PHP_SQL_SERVER --name=client msphpsql-dev
|
||||
- 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 exec client python ./test/functional/setup/setup_dbs.py -dbname $SQLSRV_DBNAME
|
||||
- docker exec client python ./test/functional/setup/setup_dbs.py -dbname $PDOSQLSRV_DBNAME
|
||||
- docker exec client php ./source/pdo_sqlsrv/run-tests.php ./test/functional/pdo_sqlsrv/*.phpt
|
||||
- docker exec client php ./source/sqlsrv/run-tests.php ./test/functional/sqlsrv/*.phpt
|
||||
- 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'
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -3,6 +3,27 @@ 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/MAC 5.0.0-preview - 2017-07-31
|
||||
Updated PECL release packages. Here is the list of updates:
|
||||
|
||||
### Added
|
||||
- Added support for PHP 7.2 Beta 1
|
||||
|
||||
### 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))
|
||||
|
||||
### Removed
|
||||
- No longer support Ubuntu 15
|
||||
- Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid))
|
||||
|
||||
### Limitation
|
||||
- No support for inout / output params when using sql_variant type
|
||||
|
||||
### Known Issues
|
||||
- When pooling is enabled in Linux or MAC
|
||||
- unixODBC <= 2.3.4 (Linux and MAC) 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)
|
||||
|
||||
## Windows/Linux/MAC 4.3.0 - 2017-07-06
|
||||
Production Ready release for SQLSRV and PDO_SQLSRV drivers on Sierra, El Capitan, Debian 8, Ubuntu 15, Ubuntu 16, CentOS 7, and Windows. Here is the changlog since the last Production Ready release.
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && \
|
|||
ARG PHPSQLDIR=/REPO/msphpsql-dev
|
||||
ENV TEST_PHP_SQL_SERVER sql
|
||||
ENV TEST_PHP_SQL_UID sa
|
||||
ENV TEST_PHP_SQL_PWD Password12@
|
||||
ENV TEST_PHP_SQL_PWD Password123
|
||||
|
||||
# set locale to utf-8
|
||||
RUN locale-gen en_US.UTF-8
|
||||
|
@ -36,10 +36,7 @@ ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
|
|||
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 echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/mssql-ubuntu-xenial-release/ xenial main" > /etc/apt/sources.list.d/mssqlpreview.list
|
||||
#RUN apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893
|
||||
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y apt-get install -y msodbcsql mssql-tools
|
||||
|
||||
ENV PATH="/opt/mssql-tools/bin:${PATH}"
|
||||
|
||||
#install coveralls
|
||||
|
@ -47,11 +44,9 @@ RUN pip install --upgrade pip && pip install cpp-coveralls
|
|||
|
||||
#Either Install git / download zip (One can see other strategies : https://ryanfb.github.io/etc/2015/07/29/git_strategies_for_docker.html )
|
||||
#One option is to get source from zip file of repository.
|
||||
|
||||
#another option is to copy source to build directory on image
|
||||
RUN mkdir -p $PHPSQLDIR
|
||||
COPY . $PHPSQLDIR
|
||||
|
||||
WORKDIR $PHPSQLDIR/source/
|
||||
|
||||
RUN chmod +x ./packagize.sh
|
||||
|
@ -79,6 +74,9 @@ RUN sed -i -e 's/TARGET_DATABASE/msphpsql_sqlsrv/g' MsSetup.inc
|
|||
RUN sed -i -e 's/TARGET_USERNAME/'"$TEST_PHP_SQL_UID"'/g' MsSetup.inc
|
||||
RUN sed -i -e 's/TARGET_PASSWORD/'"$TEST_PHP_SQL_PWD"'/g' MsSetup.inc
|
||||
|
||||
WORKDIR $PHPSQLDIR
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
CMD /bin/bash ./entrypoint.sh
|
||||
|
||||
ENV REPORT_EXIT_STATUS 1
|
||||
ENV TEST_PHP_EXECUTABLE /usr/bin/php
|
||||
|
||||
|
|
183
README.md
183
README.md
|
@ -20,8 +20,8 @@ Thank you for taking time to take our February survey. Let us know how we are do
|
|||
|-------------------------|--------------------------| ------------------
|
||||
| [![av-image][]][av-site]| [![tv-image][]][tv-site] |[![Coverage Status][]][coveralls-site]
|
||||
|
||||
[av-image]: https://ci.appveyor.com/api/projects/status/github/Microsoft/msphpsql?branch=dev&svg=true
|
||||
[av-site]: https://ci.appveyor.com/project/Microsoft-PHPSQL/msphpsql
|
||||
[av-image]: https://ci.appveyor.com/api/projects/status/xhp4nq9ouljnhxqf/branch/dev?svg=true
|
||||
[av-site]: https://ci.appveyor.com/project/Microsoft-PHPSQL/msphpsql-frhmr/branch/dev
|
||||
[tv-image]: https://travis-ci.org/Microsoft/msphpsql.svg?branch=dev
|
||||
[tv-site]: https://travis-ci.org/Microsoft/msphpsql/
|
||||
[Coverage Status]: https://coveralls.io/repos/github/Microsoft/msphpsql/badge.svg?branch=dev
|
||||
|
@ -31,6 +31,7 @@ Thank you for taking time to take our February survey. Let us know how we are do
|
|||
|
||||
* [**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)
|
||||
* [**Docker**](https://hub.docker.com/r/lbosqmsft/mssql-php-msphpsql/)
|
||||
|
||||
|
@ -42,27 +43,16 @@ Thank you for taking time to take our February survey. Let us know how we are do
|
|||
|
||||
## Build (Windows)
|
||||
|
||||
Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Azure/msphpsql/releases)
|
||||
Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Microsoft/msphpsql/releases)
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows.
|
||||
You must first be able to build PHP 7* 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
|
||||
|
||||
1. Copy the sqlsrv and/or pdo_sqlsrv source code directory from this repository into the ext subdirectory.
|
||||
|
||||
2. Run `buildconf.bat` to rebuild the configure.js script to include the driver.
|
||||
|
||||
3. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available.
|
||||
* For SQLSRV use: `--enable-sqlsrv=shared`
|
||||
* For PDO_SQLSRV use: `--enable-pdo=shared --with-pdo-sqlsrv=shared`
|
||||
|
||||
4. Run `nmake`. It is suggested that you run the entire build. If you wish to do so, run `nmake clean` first.
|
||||
|
||||
5. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory.
|
||||
|
||||
This software has been compiled and tested under PHP 7.0.8 using the Visual C++ 2015 compiler.
|
||||
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.
|
||||
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)
|
||||
|
||||
|
@ -75,31 +65,29 @@ This software has been compiled and tested under PHP 7.0.8 using the Visual C++
|
|||
|
||||
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).
|
||||
|
||||
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"`
|
||||
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.
|
||||
|
||||
3. 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 15, 16, RedHat 7 and Mac OS X. To see how to get PHP SQLSRV drivers running on Debian, please visit [Wiki](https://github.com/Microsoft/msphpsql/wiki/Dockerfile-for-getting-pdo_sqlsrv-for-PHP-7.0-on-Debian-in-3-ways). Note that Debian is not officially supported and this instruction hasn't been tested in our test lab.
|
||||
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 15, 16, RedHat 7, Debian 8, SUSE 12, and macOS.
|
||||
|
||||
### Step 1: Install PHP7+
|
||||
|
||||
#### PHP 7.0
|
||||
|
||||
**Ubuntu 15.04, Ubuntu 15.10**
|
||||
**Ubuntu 15.10**
|
||||
|
||||
sudo su
|
||||
sh -c 'echo "deb http://packages.dotdeb.org jessie all \ndeb-src http://packages.dotdeb.org jessie all" >> /etc/apt/sources.list'
|
||||
apt-get update
|
||||
apt-get install php7.0 php7.0-fpm php-pear php7.0-dev mcrypt php7.0-mcrypt php-mbstring php7.0-xml re2c gcc g++
|
||||
|
||||
apt-get install php7.0 php7.0-fpm php-pear php7.0-dev mcrypt php7.0-mcrypt php-mbstring php7.0-xml
|
||||
|
||||
**Ubuntu 16.04**
|
||||
|
||||
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 re2c gcc g++
|
||||
|
||||
apt-get -y install php7.0 mcrypt php7.0-mcrypt php-mbstring php-pear php7.0-dev php7.0-xml
|
||||
|
||||
**RedHat 7**
|
||||
|
||||
|
@ -112,7 +100,23 @@ The following instructions assume a clean environment and show how to install PH
|
|||
yum update
|
||||
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
|
||||
|
||||
**Mac OS X**
|
||||
**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
|
||||
|
||||
**SUSE 12**
|
||||
|
||||
sudo su
|
||||
zypper refresh
|
||||
zypper install -y php7 php7-pear php7-devel
|
||||
|
||||
**macOS**
|
||||
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
brew tap
|
||||
|
@ -126,6 +130,7 @@ The following instructions assume a clean environment and show how to install PH
|
|||
|
||||
#### PHP 7.1
|
||||
|
||||
Note that there are no PHP 7.1 packages available for Ubuntu 15.10.
|
||||
|
||||
**Ubuntu 16.04**
|
||||
|
||||
|
@ -143,9 +148,25 @@ The following instructions assume a clean environment and show how to install PH
|
|||
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
|
||||
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
|
||||
|
||||
**Mac OS X**
|
||||
**Debian 8**
|
||||
|
||||
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**
|
||||
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
brew tap
|
||||
|
@ -160,7 +181,6 @@ The following instructions assume a clean environment and show how to install PH
|
|||
|
||||
### Step 2: Install Prerequisites
|
||||
|
||||
|
||||
**Ubuntu 15.10**
|
||||
|
||||
sudo su
|
||||
|
@ -174,7 +194,6 @@ The following instructions assume a clean environment and show how to install PH
|
|||
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
|
||||
|
||||
**Ubuntu 16.04**
|
||||
|
||||
sudo su
|
||||
|
@ -201,29 +220,60 @@ The following instructions assume a clean environment and show how to install PH
|
|||
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
|
||||
**Mac OS X**
|
||||
**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
|
||||
|
||||
**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**
|
||||
|
||||
brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-mssql-release
|
||||
brew update
|
||||
brew install msodbcsql
|
||||
brew install --no-sandbox msodbcsql
|
||||
brew install mssql-tools
|
||||
brew install autoconf
|
||||
|
||||
*Note: You need to make sure you install PHP 7+ before you proceed to step 3. The Microsoft PHP Drivers for SQL Server will only work for PHP 7+.
|
||||
*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: The first step is not required in Mac OS X. PECL installs the stable version when version is not specified. You may run `sudo pecl search sqlsrv` to search for the latest releases and `sudo pecl install sqlsrv-[version]` to install a specific version. Drivers are Mac-compatible starting from `4.1.7preview` release.
|
||||
*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
|
||||
sudo pecl install sqlsrv
|
||||
sudo pecl install pdo_sqlsrv
|
||||
|
||||
On all systems, run:
|
||||
|
||||
pecl install sqlsrv
|
||||
pecl install pdo_sqlsrv
|
||||
|
||||
### Step 4: Install and Configure Apache
|
||||
|
||||
#### PHP 7.0
|
||||
|
||||
**Ubuntu**
|
||||
**Ubuntu and Debian**
|
||||
|
||||
sudo su
|
||||
apt-get install libapache2-mod-php7.0 apache2
|
||||
|
@ -232,19 +282,29 @@ The following instructions assume a clean environment and show how to install PH
|
|||
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
|
||||
exit
|
||||
|
||||
**RedHat**
|
||||
|
||||
sudo yum install httpd
|
||||
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
|
||||
|
||||
**Mac OS X**
|
||||
**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**
|
||||
**Ubuntu and Debian**
|
||||
|
||||
sudo su
|
||||
apt-get install libapache2-mod-php7.1 apache2
|
||||
|
@ -253,35 +313,48 @@ The following instructions assume a clean environment and show how to install PH
|
|||
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
|
||||
exit
|
||||
|
||||
**RedHat**
|
||||
|
||||
sudo yum install httpd
|
||||
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
|
||||
|
||||
**Mac OS X**
|
||||
**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**
|
||||
**Ubuntu, Debian, and SUSE**
|
||||
|
||||
sudo service apache2 restart
|
||||
sudo systemctl restart apache2
|
||||
|
||||
**RedHat**
|
||||
|
||||
sudo apachectl restart
|
||||
sudo systemctl restart httpd
|
||||
|
||||
**Mac OS X**
|
||||
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
|
||||
|
||||
*Note to RedHat users: SELinux is installed by default and runs in Enforcing mode. To allow Apache to connect to database through SELinux, do this `sudo setsebool -P httpd_can_network_connect_db 1`
|
||||
|
||||
### Step 6: Create your sample app
|
||||
Navigate to `/var/www/html` (`/usr/local/var/www/htdocs` on Mac) and create a new file called testsql.php. Copy and paste the following code in tetsql.php and change the servername, username, password and databasename.
|
||||
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";
|
||||
|
@ -338,7 +411,7 @@ For samples, please see the sample folder. For setup instructions, see [here](h
|
|||
## 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 parameter using emulate prepare is not supported.
|
||||
- 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.
|
||||
|
@ -374,7 +447,7 @@ version number MAY have trailing pre-release version to indicate the stability,
|
|||
|
||||
## Future Plans
|
||||
- Expand SQL 16 Feature Support (example: Always Encrypted).
|
||||
- Build Verification/Fundamental Tests.
|
||||
- Add More Verification/Fundamental Tests.
|
||||
- Bug Fixes.
|
||||
|
||||
## Guidelines for Reporting Issues
|
||||
|
@ -395,15 +468,15 @@ 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 once they solidify over the next few weeks.
|
||||
**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.
|
||||
|
||||
**Q:** What's next?
|
||||
|
||||
**A:** On July 20, 2016 we released the early technical preview for our PHP Driver. We will continue releasing frequent technical previews until we reach production quality.
|
||||
**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.
|
||||
|
||||
**Q:** Is Microsoft taking pull requests for this project?
|
||||
|
||||
**A:** We will not be seeking to take pull requests until GA, Build Verification, and Fundamental tests are released. At this point Microsoft will also begin actively developing using this GitHub project as the prime repository.
|
||||
**A:** Yes. Please submit pull requests to the **dev** branch and not the **master** branch.
|
||||
|
||||
|
||||
|
||||
|
@ -417,7 +490,7 @@ This project has adopted the Microsoft Open Source Code of Conduct. For more inf
|
|||
|
||||
## Resources
|
||||
|
||||
**Documentation**: [MSDN Online Documentation][phpdoc]. Please note that this documentation is not yet updated for PHP 7.
|
||||
**Documentation**: [MSDN Online Documentation][phpdoc].
|
||||
|
||||
**Team Blog**: Browse our blog for comments and announcements from the team in the [team blog][blog].
|
||||
|
||||
|
|
|
@ -166,6 +166,12 @@ 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%
|
||||
- php run-tests.php -p php.exe %APPVEYOR_BUILD_FOLDER%\test\functional\sqlsrv\*.phpt > %APPVEYOR_BUILD_FOLDER%\test\functional\sqlsrv.log 2>&1
|
||||
- type %APPVEYOR_BUILD_FOLDER%\test\functional\sqlsrv.log
|
||||
- php run-tests.php -p php.exe %APPVEYOR_BUILD_FOLDER%\test\functional\pdo_sqlsrv\*.phpt > %APPVEYOR_BUILD_FOLDER%\test\functional\pdo_sqlsrv.log 2>&1
|
||||
|
|
100
buildscripts/README.md
Normal file
100
buildscripts/README.md
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Windows
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To build extensions for
|
||||
1. PHP 7.0* or PHP 7.1*
|
||||
* install Visual Studio 2015 and make sure C++ tools are enabled.
|
||||
2. PHP 7.2*
|
||||
* install Visual Studio 2017, including Visual C++ toolset and the Windows SDK components.
|
||||
|
||||
To use the sample build scripts `builddrivers.py` and `buildtools.py`, install Python 3.x and Git for Windows (which comes with Visual Studio 2017). If `git` is unrecognized in a regular command prompt, make sure the environment path is set up correctly.
|
||||
|
||||
## Compile the drivers
|
||||
|
||||
You must first be able to build PHP 7.* without including our PHP extensions. For help with building PHP 7.0* or PHP 7.1* in Windows, see the [official PHP website](https://wiki.php.net/internals/windows/stepbystepbuild). For PHP 7.2 or above, visit [PHP SDK page](https://github.com/OSTC/php-sdk-binary-tools) for new instructions.
|
||||
|
||||
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.
|
||||
|
||||
### Manually building from source
|
||||
|
||||
1. Download the *source* directory from this repository
|
||||
|
||||
2. Make a copy of the *shared* folder as a subfolder in *sqlsrv* and/or *pdo_sqlsrv* folder
|
||||
|
||||
3. Copy the *sqlsrv* and/or *pdo_sqlsrv* folder(s) into the PHP source *ext* subdirectory
|
||||
|
||||
4. Run `buildconf --force` to rebuild the configure.js script to include the *sqlsrv* and/or *pdo_sqlsrv* driver(s).
|
||||
|
||||
5. Run `configure.bat` with the desired driver options (as shown below) to generate the makefile. You can run `configure.bat --help` to see what other options are available. For example, for non-thread safe build, add this option `--disable-zts`.
|
||||
* For SQLSRV add: `--enable-sqlsrv=shared`
|
||||
* For PDO_SQLSRV add: `--enable-pdo --with-pdo-sqlsrv=shared`
|
||||
|
||||
6. Run `nmake`. Optionally, you can run `nmake clean` first.
|
||||
|
||||
7. To install the drivers, there are two ways:
|
||||
* Run `nmake install`, or
|
||||
* Copy the drivers:
|
||||
* Find the directory where the newly compiled *php.exe* is
|
||||
* Locate the compiled *php_sqlsrv.dll* and/or *php_pdo_sqlsrv.dll*
|
||||
* Copy the dll(s) to the *ext* subfolder
|
||||
|
||||
### Using the sample build scripts
|
||||
|
||||
The sample build scripts, `builddrivers.py` and `buildtools.py`, can be used to build our extensions for PHP in Windows.
|
||||
|
||||
#### Overview
|
||||
|
||||
When asked to provide the PHP version, you should enter values like `7.1.7`. If it's alpha, beta, or RC version, make sure the name you provide matches the PHP tag name without the prefix `php-`. For example, for PHP 7.2 beta 2, the tag name is `php-7.2.0beta2`, so you will enter `7.2.0beta2`. Visit [PHP SRC]( https://github.com/php/php-src) to find the appropriate tag names.
|
||||
|
||||
PHP recommends to unzip the PHP SDK into the shortest possible path, preferrably somewhere near the root drive. Therefore, this script will, by default, create a `php-sdk` folder in the C:\ drive, and this `php-sdk` directory tree will remain unless you remove it yourself. For ongoing development, we suggest you keep it around. The build scripts will handle updating the PHP SDK if a new version is available.
|
||||
|
||||
#### Steps
|
||||
|
||||
1. Launch a regular `cmd` prompt
|
||||
|
||||
2. Change to the directory where the Python scripts `builddrivers.py` and `buildtools.py` are
|
||||
|
||||
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`)
|
||||
* 64-bit?
|
||||
* Thread safe?
|
||||
* Driver?
|
||||
* Debug enabled?
|
||||
* Download source from GitHub?
|
||||
* For `yes/no` questions, you can simply hit `ENTER` key for `yes`. Other questions are self-explanatory.
|
||||
|
||||
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`
|
||||
|
||||
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.)
|
||||
* `clean` to remove previous builds (binaries)
|
||||
* `superclean` to remove the entire `php-<version>-src` directory, which is often unnecessary
|
||||
|
||||
6. If you choose not to download from a GitHub repository, you will be asked to provide the full path to your local Source folder.
|
||||
|
||||
7. If the compilation is successful, you will be given the option to rebuild or quit.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
If something went wrong or the build failed, the log file will be launched (you can find the log files in `C:\php-sdk`). Otherwise, the log file will not be shown, and they remain in `C:\php-sdk` until you remove them manually.
|
||||
|
||||
In addition to the log files in `C:\php-sdk`, you can examine the contents of `C:\php-sdk\phpsdk-build-task.bat`, which is overwritten every time you run the build scripts.
|
||||
|
||||
#### Testing mode and/or setting alternative destination
|
||||
|
||||
If your main goal is to build the drivers for testing, and/or there is no need to keep the `php-sdk` directory around, you can invoke `py builddrivers.py` with the necessary command-line arguments plus `--TESTING`, which turns on the *testing* mode (it is False by default).
|
||||
|
||||
Setting the testing mode automatically turns off the looping mechanism. When the build is finished, you will find a copy of the drivers (unless the build failed) and the `php-sdk` folder in the same directory of these Python scripts.
|
||||
|
||||
In addition, you can set an alternative destination using `--DESTPATH=<some valid path>`, which is **None** by default. Note that these two options are *not* available in the interactive mode. However, they are particularly useful for testing purposes (such as testing in a virtual machine) in which these build scripts are copied to a temporary folder. After the drivers have been successfully compiled and copied to the designated location, the temporary folder can be safely removed.
|
||||
|
||||
|
||||
|
||||
|
||||
|
288
buildscripts/builddrivers.py
Normal file
288
buildscripts/builddrivers.py
Normal file
|
@ -0,0 +1,288 @@
|
|||
#!/usr/bin/python3
|
||||
#########################################################################################
|
||||
#
|
||||
# Description: This script helps to build drivers in a Windows environment for PHP 7+ (32-bit/64-bit)
|
||||
#
|
||||
# Requirement:
|
||||
# python 3.x
|
||||
# PHP SDK and PHP Source
|
||||
# Driver source code folder / GitHub repository
|
||||
# Visual Studio 2015 (PHP 7.0* and 7.1*) and Visual Studio 2017 (PHP 7.2*)
|
||||
#
|
||||
# Execution: Run with command line with required options.
|
||||
# Examples:
|
||||
# py builddrivers.py (for interactive mode)
|
||||
# py builddrivers.py --PHPVER=7.0.22 --ARCH=x64 --THREAD=nts --DRIVER=all --DEBUG
|
||||
#
|
||||
# Output: Build the drivers using PHP SDK. When running for local development, if build is unsuccessful,
|
||||
# the log file will be launched for examination. Otherwise, the drivers will be renamed
|
||||
# and copied to the designated location (if defined).
|
||||
#
|
||||
#############################################################################################
|
||||
|
||||
import sys
|
||||
import shutil
|
||||
import os.path
|
||||
import argparse
|
||||
from buildtools import BuildUtil
|
||||
|
||||
class BuildDriver(object):
|
||||
"""Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties:
|
||||
|
||||
Attributes:
|
||||
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
|
||||
source_path # path to a local source folder
|
||||
testing # whether the user has turned on testing mode
|
||||
"""
|
||||
|
||||
def __init__(self, phpver, driver, arch, thread, debug, repo, branch, download, path, testing):
|
||||
self.util = BuildUtil(phpver, driver, arch, thread, debug)
|
||||
self.repo = repo
|
||||
self.branch = branch
|
||||
self.download_source = download
|
||||
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()
|
||||
print('PHP Version: ', self.util.phpver)
|
||||
print('Arch: ', self.util.arch)
|
||||
print('Thread: ', self.util.thread)
|
||||
print('Driver: ', self.util.driver)
|
||||
print('Debug enabled: ', self.util.debug_enabled)
|
||||
print()
|
||||
|
||||
def clean_or_remove(self, root_dir, work_dir):
|
||||
"""Only check this for local development and not rebuilding. If the php source directory
|
||||
already exists, this will prompt user whether to rebuild, clean, or superclean, the last option
|
||||
will remove the entire php source directory.
|
||||
|
||||
:param root_dir: the C:\ drive
|
||||
:param work_dir: the directory of this script
|
||||
:outcome: the old binaries, if exist, will be removed
|
||||
"""
|
||||
phpsrc = self.util.phpsrc_root(root_dir)
|
||||
if os.path.exists( phpsrc ):
|
||||
print(phpsrc + " exists.")
|
||||
build_choice = validate_input("(r)ebuild for the same configuration, (c)lean otherwise, (s)uperclean if unsure ", "r/c/s")
|
||||
self.make_clean = False
|
||||
if build_choice == 'r':
|
||||
print('Will rebuild the binaries')
|
||||
# only the old binaries based on the current configuration will be removed
|
||||
self.util.remove_prev_build(root_dir)
|
||||
elif build_choice == 'c':
|
||||
print('Will make clean')
|
||||
self.make_clean = True
|
||||
# all old builds are removed, and this step is necessary because
|
||||
# the user might have changed the configuration
|
||||
self.util.remove_old_builds(root_dir)
|
||||
else:
|
||||
print('Will remove ' + phpsrc)
|
||||
os.system('RMDIR /s /q ' + phpsrc)
|
||||
|
||||
os.chdir(work_dir) # change back to the working directory
|
||||
|
||||
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.
|
||||
|
||||
:param root_dir: the root directory
|
||||
:param logfile: the name of the logfile
|
||||
:outcome: the drivers and symbols will renamed and placed in the appropriate location(s)
|
||||
|
||||
"""
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
if self.download_source:
|
||||
# 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.")
|
||||
|
||||
print('Copying source files from', source)
|
||||
|
||||
os.system('ROBOCOPY ' + source + '\shared ' + work_dir + '\Source\shared /xx /xo ')
|
||||
os.system('ROBOCOPY ' + source + '\sqlsrv ' + work_dir + '\Source\sqlsrv /xx /xo ')
|
||||
os.system('ROBOCOPY ' + source + '\pdo_sqlsrv ' + work_dir + '\Source\pdo_sqlsrv /xx /xo ')
|
||||
|
||||
print('Start building PHP with the extension...')
|
||||
|
||||
# If not testing, dest should be the root drive. Otherwise, dest should be None.
|
||||
dest = None if self.testing else root_dir
|
||||
|
||||
# ext_dir is the directory where we can find the built extension(s)
|
||||
ext_dir = self.util.build_drivers(self.make_clean, dest, logfile)
|
||||
|
||||
# Copy the binaries if a destination path is defined
|
||||
if self.dest_path is not None:
|
||||
dest_drivers = os.path.join(self.dest_path, self.util.major_version(), self.util.arch)
|
||||
dest_symbols = os.path.join(dest_drivers, 'Symbols', self.util.thread)
|
||||
|
||||
# All intermediate directories will be created in order to create the leaf directory
|
||||
if os.path.exists(dest_symbols) == False:
|
||||
os.makedirs(dest_symbols)
|
||||
|
||||
# Now copy all the binaries
|
||||
if self.util.driver == 'all':
|
||||
self.util.copy_binary(ext_dir, dest_drivers, 'sqlsrv', '.dll')
|
||||
self.util.copy_binary(ext_dir, dest_symbols, 'sqlsrv', '.pdb')
|
||||
self.util.copy_binary(ext_dir, dest_drivers, 'pdo_sqlsrv', '.dll')
|
||||
self.util.copy_binary(ext_dir, dest_symbols, 'pdo_sqlsrv', '.pdb')
|
||||
else:
|
||||
self.util.copy_binary(ext_dir, dest_drivers, self.util.driver, '.dll')
|
||||
self.util.copy_binary(ext_dir, dest_symbols, self.util.driver, '.pdb')
|
||||
|
||||
|
||||
def build(self):
|
||||
"""This is the main entry point of building drivers for PHP.
|
||||
For development, this will loop till the user decides to quit.
|
||||
"""
|
||||
self.show_config()
|
||||
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
root_dir = 'C:' + os.sep
|
||||
|
||||
quit = False
|
||||
while not quit:
|
||||
if not self.rebuild and not self.testing:
|
||||
self.clean_or_remove(root_dir, work_dir)
|
||||
|
||||
logfile = self.util.get_logfile_name()
|
||||
|
||||
try:
|
||||
self.build_extensions(root_dir, logfile)
|
||||
print('Build Completed')
|
||||
except:
|
||||
print('Something went wrong, launching log file', logfile)
|
||||
# display log file only when not testing
|
||||
if not self.testing:
|
||||
os.startfile(os.path.join(root_dir, 'php-sdk', logfile))
|
||||
os.chdir(work_dir)
|
||||
break
|
||||
|
||||
if not self.testing:
|
||||
choice = input("Rebuild using the same configuration(yes) or quit (no) [yes/no]: ")
|
||||
choice = choice.lower()
|
||||
if choice == 'yes' or choice == 'y' or choice == '':
|
||||
print('Rebuilding drivers...')
|
||||
self.make_clean = False
|
||||
self.rebuild = True
|
||||
self.util.remove_prev_build(root_dir)
|
||||
else:
|
||||
quit = True
|
||||
else:
|
||||
quit = True
|
||||
|
||||
os.chdir(work_dir)
|
||||
|
||||
def validate_input(question, values):
|
||||
"""Return the user selected value, and it must be valid based on *values*."""
|
||||
while True:
|
||||
options = values.split('/')
|
||||
prompt = '[' + values + ']'
|
||||
value = input(question + prompt + ': ')
|
||||
value = value.lower()
|
||||
if not value in options:
|
||||
print("An invalid choice is entered. Choose from", prompt)
|
||||
else:
|
||||
break
|
||||
return value
|
||||
|
||||
################################### Main Function ###################################
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--PHPVER', help="PHP version, e.g. 7.1.*, 7.2.* etc.")
|
||||
parser.add_argument('--ARCH', choices=['x64', 'x86'])
|
||||
parser.add_argument('--THREAD', choices=['nts', 'ts'])
|
||||
parser.add_argument('--DRIVER', default='all', choices=['all', 'sqlsrv', 'pdo_sqlsrv'], help="driver to build (default: all)")
|
||||
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('--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)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
phpver = args.PHPVER
|
||||
arch = args.ARCH
|
||||
thread = args.THREAD
|
||||
driver = args.DRIVER
|
||||
debug = args.DEBUG
|
||||
repo = args.REPO
|
||||
branch = args.BRANCH
|
||||
download = args.SOURCE is False
|
||||
path = args.DESTPATH
|
||||
testing = args.TESTING
|
||||
|
||||
if phpver is None:
|
||||
# starts interactive mode, testing mode is False
|
||||
# will not prompt for drivers' destination path, which is None by default
|
||||
while True:
|
||||
# perform some minimal checks
|
||||
phpver = input("PHP Version (e.g. 7.1.* or 7.2.*): ")
|
||||
if phpver == '':
|
||||
print('Empty PHP version entered! Please try again.')
|
||||
elif phpver[0] < '7':
|
||||
print('Only PHP 7.0 or above is supported. Please try again.')
|
||||
else:
|
||||
break
|
||||
|
||||
arch_version = input("64-bit? [y/n]: ")
|
||||
thread = validate_input("Thread safe? ", "nts/ts")
|
||||
driver = validate_input("Driver to build? ", "all/sqlsrv/pdo_sqlsrv")
|
||||
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 == '':
|
||||
repo = 'Microsoft'
|
||||
if branch == '':
|
||||
branch = 'dev'
|
||||
|
||||
arch_version = arch_version.lower()
|
||||
arch = 'x64' if arch_version == 'yes' or arch_version == 'y' or arch_version == '' else 'x86'
|
||||
|
||||
debug_mode = debug_mode.lower()
|
||||
debug = debug_mode == 'yes' or debug_mode == 'y' or debug_mode == ''
|
||||
|
||||
builder = BuildDriver(phpver,
|
||||
driver,
|
||||
arch,
|
||||
thread,
|
||||
debug,
|
||||
repo,
|
||||
branch,
|
||||
download,
|
||||
path,
|
||||
testing)
|
||||
builder.build()
|
476
buildscripts/buildtools.py
Normal file
476
buildscripts/buildtools.py
Normal file
|
@ -0,0 +1,476 @@
|
|||
#!/usr/bin/python3
|
||||
#########################################################################################
|
||||
#
|
||||
# Description: The class BuildUtil will build Microsoft SQL Server PHP 7+ Drivers
|
||||
# for 32 bit and 64 bit.
|
||||
#
|
||||
# Requirement:
|
||||
# python 3.x
|
||||
# PHP SDK and PHP Source
|
||||
# Driver source code folder
|
||||
# Git for Windows
|
||||
# Visual Studio 2015 (PHP 7.0* and 7.1*) and Visual Studio 2017 (PHP 7.2*)
|
||||
#
|
||||
# Output: The drivers will be renamed and copied to the specified location.
|
||||
#
|
||||
#############################################################################################
|
||||
|
||||
import shutil
|
||||
import os.path
|
||||
import stat
|
||||
import datetime
|
||||
import urllib.request
|
||||
import zipfile
|
||||
import fileinput
|
||||
|
||||
class BuildUtil(object):
|
||||
"""Build sqlsrv and/or pdo_sqlsrv drivers with PHP source with the following properties:
|
||||
|
||||
Attributes:
|
||||
phpver # PHP version, e.g. 7.1.*, 7.2.* etc.
|
||||
driver # all, sqlsrv, or pdo_sqlsrv
|
||||
arch # x64 or x86
|
||||
thread # nts or ts
|
||||
debug_enabled # whether debug is enabled
|
||||
"""
|
||||
|
||||
def __init__(self, phpver, driver, arch, thread, debug_enabled = False):
|
||||
self.phpver = phpver
|
||||
self.driver = driver.lower()
|
||||
self.arch = arch.lower()
|
||||
self.thread = thread.lower()
|
||||
self.debug_enabled = debug_enabled
|
||||
|
||||
def major_version(self):
|
||||
"""Return the major version number based on the PHP version."""
|
||||
return self.phpver[0:3]
|
||||
|
||||
def version_label(self):
|
||||
"""Return the version label based on the PHP version."""
|
||||
major_ver = self.major_version()
|
||||
|
||||
if major_ver[2] == '0':
|
||||
version = major_ver[0]
|
||||
else:
|
||||
version = major_ver[0] + major_ver[2]
|
||||
return version
|
||||
|
||||
def driver_name(self, driver, suffix):
|
||||
"""Return the *driver* name with *suffix* after PHP is successfully compiled."""
|
||||
return 'php_' + driver + suffix
|
||||
|
||||
def driver_new_name(self, driver, suffix):
|
||||
"""Return the *driver* name with *suffix* based on PHP version and thread."""
|
||||
version = self.version_label()
|
||||
return 'php_' + driver + '_' + version + '_' + self.thread + suffix
|
||||
|
||||
def compiler_version(self):
|
||||
"""Return the appropriate compiler version based on PHP version."""
|
||||
VC = 'vc14'
|
||||
version = self.version_label()
|
||||
if version >= '72': # Compiler version for PHP 7.2 or above
|
||||
VC = 'vc15'
|
||||
return VC
|
||||
|
||||
def phpsrc_root(self, sdk_dir):
|
||||
"""Return the path to the PHP source folder based on *sdk_dir*."""
|
||||
vc = self.compiler_version()
|
||||
return os.path.join(sdk_dir, 'php-sdk', 'phpdev', vc, self.arch, 'php-'+self.phpver+'-src')
|
||||
|
||||
def build_abs_path(self, sdk_dir):
|
||||
"""Return the absolute path to the PHP build folder based on *sdk_dir*."""
|
||||
phpsrc = self.phpsrc_root(sdk_dir)
|
||||
|
||||
build_dir = 'Release'
|
||||
if self.debug_enabled:
|
||||
build_dir = 'Debug'
|
||||
|
||||
if self.thread == 'ts':
|
||||
build_dir = build_dir + '_TS'
|
||||
|
||||
if self.arch == 'x64':
|
||||
build_dir = self.arch + os.sep + build_dir
|
||||
|
||||
return os.path.join(phpsrc, build_dir)
|
||||
|
||||
def remove_old_builds(self, sdk_dir):
|
||||
"""Remove the extensions, e.g. the driver subfolders in php-7.*-src\ext."""
|
||||
print('Removing old builds...')
|
||||
|
||||
phpsrc = self.phpsrc_root(sdk_dir)
|
||||
ext_path = os.path.join(phpsrc, 'ext')
|
||||
if os.path.exists( ext_path ):
|
||||
shutil.rmtree(os.path.join(ext_path, 'sqlsrv'), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(ext_path, 'pdo_sqlsrv'), ignore_errors=True)
|
||||
|
||||
if self.arch == 'x64':
|
||||
shutil.rmtree(os.path.join(phpsrc, self.arch), ignore_errors=True)
|
||||
else:
|
||||
shutil.rmtree(os.path.join(phpsrc, 'Debug'), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(phpsrc, 'Debug_TS'), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(phpsrc, 'Release'), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(phpsrc, 'Release_TS'), ignore_errors=True)
|
||||
|
||||
def remove_prev_build(self, sdk_dir):
|
||||
"""Remove all binaries and source code in the Release* or Debug*
|
||||
folders according to the current configuration
|
||||
"""
|
||||
print('Removing previous build...')
|
||||
build_dir = self.build_abs_path(sdk_dir)
|
||||
if not os.path.exists(build_dir):
|
||||
return
|
||||
|
||||
os.chdir(build_dir)
|
||||
os.system('DEL *sqlsrv*')
|
||||
|
||||
# remove the extensions in the phpsrc's release* or debug* folder's ext subfolder
|
||||
release_ext_path = os.path.join(build_dir, 'ext')
|
||||
if os.path.exists( release_ext_path ):
|
||||
shutil.rmtree(os.path.join(release_ext_path, 'sqlsrv'), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(release_ext_path, 'pdo_sqlsrv'), ignore_errors=True)
|
||||
|
||||
# next remove the binaries too
|
||||
os.chdir(release_ext_path)
|
||||
os.system('DEL *sqlsrv*')
|
||||
|
||||
@staticmethod
|
||||
def get_logfile_name():
|
||||
"""Return the filename for the log file based on timestamp."""
|
||||
return 'Build_' + datetime.datetime.now().strftime("%Y%m%d_%H%M") + '.log'
|
||||
|
||||
@staticmethod
|
||||
def update_file_content(file, search_str, new_str):
|
||||
"""Find *search_str* and replace it by *new_str* in a *file*"""
|
||||
os.chmod(file, stat.S_IWRITE)
|
||||
with fileinput.FileInput(file, inplace=True) as f:
|
||||
for line in f:
|
||||
print(line.replace(search_str, new_str), end='')
|
||||
|
||||
@staticmethod
|
||||
def generateMMDD():
|
||||
"""Return the generated Microsoft PHP Build Version Number"""
|
||||
d = datetime.date.today()
|
||||
|
||||
startYear = 2009
|
||||
startMonth = 4
|
||||
passYear = int( '%02d' % d.year ) - startYear
|
||||
passMonth = int( '%02d' % d.month ) - startMonth
|
||||
MM = passYear * 12 + passMonth
|
||||
dd = d.day
|
||||
|
||||
MMDD = "" + str( MM )
|
||||
if( dd < 10 ):
|
||||
return MMDD + "0" + str( dd )
|
||||
else:
|
||||
return MMDD + str( dd )
|
||||
|
||||
@staticmethod
|
||||
def get_driver_version(version_file):
|
||||
"""Read the *version_file* and return the driver version."""
|
||||
with open(version_file) as f:
|
||||
for line in f:
|
||||
if 'SQLVERSION_MAJOR' in line: # major version
|
||||
major = line.split()[2]
|
||||
elif 'SQLVERSION_MINOR' in line: # minor version
|
||||
minor = line.split()[2]
|
||||
elif 'SQLVERSION_PATCH' in line: # patch
|
||||
patch = line.split()[2]
|
||||
break
|
||||
|
||||
return major + '.' + minor + '.' + patch
|
||||
|
||||
@staticmethod
|
||||
def write_lines_to_copy_source(driver, file):
|
||||
"""Write to file the commands to copy *driver* source."""
|
||||
source = '%currDir%' + os.sep + 'Source' + os.sep + driver
|
||||
dest = '%phpSrc%' + os.sep + 'ext' + os.sep + driver
|
||||
file.write('@CALL ROBOCOPY ' + source + ' ' + dest + ' /s /xx /xo' + os.linesep)
|
||||
|
||||
source = '%currDir%' + os.sep + 'Source' + os.sep + 'shared'
|
||||
dest = '%phpSrc%' + os.sep + 'ext' + os.sep + driver + os.sep + 'shared'
|
||||
file.write('@CALL ROBOCOPY ' + source + ' ' + dest + ' /s /xx /xo' + os.linesep)
|
||||
|
||||
@staticmethod
|
||||
def download_msphpsql_source(repo, branch, dest_folder = 'Source', clean_up = True):
|
||||
"""Download to *dest_folder* the msphpsql archive of the specified
|
||||
GitHub *repo* and *branch*. The downloaded files will be removed by default.
|
||||
"""
|
||||
try:
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
temppath = os.path.join(work_dir, 'temp')
|
||||
if os.path.exists(temppath):
|
||||
shutil.rmtree(temppath)
|
||||
os.makedirs(temppath)
|
||||
os.chdir(temppath)
|
||||
|
||||
file = branch + '.zip'
|
||||
url = 'https://github.com/' + repo + '/msphpsql/archive/' + branch + '.zip'
|
||||
|
||||
print('Downloading ' + url + ' ...')
|
||||
try:
|
||||
with urllib.request.urlopen(url) as response, open(file, 'wb') as out_file:
|
||||
shutil.copyfileobj(response, out_file)
|
||||
except:
|
||||
print ("Resort to skip ssl verification...")
|
||||
# need to skip ssl verification on some agents
|
||||
# see https://www.python.org/dev/peps/pep-0476/
|
||||
with urllib.request.urlopen(url, context=ssl._create_unverified_context()) as response, open(file, 'wb') as out_file:
|
||||
shutil.copyfileobj(response, out_file)
|
||||
|
||||
print('Extracting ' + file + ' ...')
|
||||
zip = zipfile.ZipFile(file)
|
||||
zip.extractall()
|
||||
zip.close()
|
||||
|
||||
msphpsqlFolder = os.path.join(temppath, 'msphpsql-' + branch)
|
||||
source = os.path.join(msphpsqlFolder, 'source')
|
||||
os.chdir(work_dir)
|
||||
|
||||
os.system('ROBOCOPY ' + source + '\shared ' + dest_folder + '\shared /xx /xo')
|
||||
os.system('ROBOCOPY ' + source + '\pdo_sqlsrv ' + dest_folder + '\pdo_sqlsrv /xx /xo')
|
||||
os.system('ROBOCOPY ' + source + '\sqlsrv ' + dest_folder + '\sqlsrv /xx /xo')
|
||||
|
||||
if clean_up:
|
||||
shutil.rmtree(temppath)
|
||||
|
||||
except:
|
||||
print('Error occurred when downloading source')
|
||||
raise
|
||||
|
||||
def update_driver_source(self, source_dir, driver):
|
||||
"""Update the *driver* source in *source_path* with the
|
||||
latest version, file descriptions, etc.
|
||||
If debug is enabled, will remove the optimization flag
|
||||
"""
|
||||
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)
|
||||
config_file = os.path.join(driver_dir, 'config.w32')
|
||||
if driver == 'sqlsrv':
|
||||
self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_SQLSRV", "/O2" );', '')
|
||||
elif driver == 'pdo_sqlsrv':
|
||||
self.update_file_content(config_file, 'ADD_FLAG( "CFLAGS_PDO_SQLSRV", "/O2" );', '')
|
||||
|
||||
# Update Template.rc
|
||||
template_file = os.path.join(driver_dir, 'template.rc')
|
||||
if driver == 'sqlsrv':
|
||||
drivername = self.driver_new_name(driver, '.dll')
|
||||
self.update_file_content(template_file, 'FILE_NAME \"\\0\"', '"' + drivername + '\\0"')
|
||||
self.update_file_content(template_file, '\"Microsoft Drivers for PHP for SQL Server\\0\"', '"Microsoft Drivers for PHP for SQL Server (SQLSRV Driver)\\0"')
|
||||
elif driver == 'pdo_sqlsrv':
|
||||
drivername = self.driver_new_name(driver, '.dll')
|
||||
self.update_file_content(template_file, 'FILE_NAME \"\\0\"', '"' + drivername + '\\0"')
|
||||
self.update_file_content(template_file, '\"Microsoft Drivers for PHP for SQL Server\\0\"', '"Microsoft Drivers for PHP for SQL Server (PDO Driver)\\0"')
|
||||
|
||||
# Update Version.h
|
||||
version_file = os.path.join(source_dir, 'shared', 'version.h')
|
||||
build_number = self.generateMMDD()
|
||||
self.update_file_content(version_file, 'SQLVERSION_BUILD 0', 'SQLVERSION_BUILD ' + build_number)
|
||||
|
||||
# get the latest version
|
||||
version = self.get_driver_version(version_file) + '.' + build_number
|
||||
print('Driver version is: ', version)
|
||||
|
||||
# Update CREDIT file
|
||||
credits_file = os.path.join(driver_dir, 'CREDITS')
|
||||
if driver == 'sqlsrv':
|
||||
self.update_file_content(credits_file, 'Microsoft Drivers for PHP for SQL Server', 'Microsoft Drivers ' + version + ' for PHP for SQL Server (' + self.driver.upper() + ' driver)')
|
||||
elif driver == 'pdo_sqlsrv':
|
||||
self.update_file_content(credits_file, 'Microsoft Drivers for PHP for SQL Server (PDO driver)', 'Microsoft Drivers ' + version + ' for PHP for SQL Server (' + self.driver.upper() + ' driver)')
|
||||
|
||||
def generate_build_options(self):
|
||||
"""Return the generated build configuration and arguments"""
|
||||
cmd_line = ''
|
||||
if self.debug_enabled:
|
||||
cmd_line = ' --enable-debug '
|
||||
|
||||
if self.driver == 'all':
|
||||
cmd_line = ' --enable-sqlsrv=shared --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line
|
||||
else:
|
||||
if self.driver == 'sqlsrv':
|
||||
cmd_line = ' --enable-sqlsrv=shared ' + cmd_line
|
||||
else: # pdo_sqlsrv
|
||||
cmd_line = ' --enable-pdo --with-pdo-sqlsrv=shared ' + cmd_line
|
||||
|
||||
cmd_line = 'cscript configure.js --disable-all --enable-cli --enable-cgi --enable-embed' + cmd_line
|
||||
if self.thread == 'nts':
|
||||
cmd_line = cmd_line + ' --disable-zts'
|
||||
return cmd_line
|
||||
|
||||
def create_local_batch_file(self, make_clean, cmd_line, log_file):
|
||||
"""Generate the batch file to be picked up by the PHP starter script."""
|
||||
filename = 'phpsdk-build-task.bat'
|
||||
print('Generating ', filename)
|
||||
try:
|
||||
file = open(filename, 'w')
|
||||
file.write('@ECHO OFF' + os.linesep)
|
||||
file.write('SET currDir=%CD%' + os.linesep)
|
||||
file.write('SET LOG_NAME=%currDir%\\' + log_file + os.linesep)
|
||||
file.write('@CALL phpsdk_buildtree phpdev > %LOG_NAME% 2>&1' + os.linesep)
|
||||
|
||||
# for PHP version with release tags, such as 'RC', 'beta', etc.
|
||||
# we need to remove the hyphen '-' between the version number and tag
|
||||
# because in https://github.com/php/php-src the released tags have no hyphens
|
||||
|
||||
php_tag = 'php-' + self.phpver.replace('-', '')
|
||||
php_src = 'php-' + self.phpver +'-src'
|
||||
|
||||
# if not exists, check out the specified tag
|
||||
file.write('IF NOT EXIST ' + php_src + ' @CALL git clone -b ' + php_tag + ' --depth 1 --single-branch https://github.com/php/php-src.git ' + php_src + os.linesep)
|
||||
file.write('CD ' + php_src + os.linesep)
|
||||
file.write('SET phpSrc=%CD%' + os.linesep)
|
||||
file.write('@CALL phpsdk_deps -u >> %LOG_NAME% 2>&1' + os.linesep)
|
||||
|
||||
# copy source files to extension
|
||||
if self.driver == 'all':
|
||||
self.write_lines_to_copy_source('sqlsrv', file)
|
||||
self.write_lines_to_copy_source('pdo_sqlsrv', file)
|
||||
else:
|
||||
self.write_lines_to_copy_source(self.driver, file)
|
||||
|
||||
# configure and build
|
||||
file.write('@CALL buildconf --force >> %LOG_NAME% 2>&1' + os.linesep)
|
||||
file.write('@CALL ' + cmd_line + ' >> %LOG_NAME% 2>&1' + os.linesep)
|
||||
if make_clean:
|
||||
file.write('nmake clean >> %LOG_NAME% 2>&1' + os.linesep)
|
||||
file.write('nmake >> %LOG_NAME% 2>&1' + os.linesep)
|
||||
file.write('exit' + os.linesep)
|
||||
file.close()
|
||||
return filename
|
||||
except:
|
||||
print('Cannot create ', filename)
|
||||
|
||||
def build_drivers(self, make_clean = False, dest = None, log_file = None):
|
||||
"""Build sqlsrv/pdo_sqlsrv extensions for PHP, assuming the Source folder
|
||||
exists in the working directory, and this folder will be removed when the build
|
||||
is complete.
|
||||
"""
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# First, update the driver source file contents
|
||||
source_dir = os.path.join(work_dir, 'Source')
|
||||
if self.driver == 'all':
|
||||
self.update_driver_source(source_dir, 'sqlsrv')
|
||||
self.update_driver_source(source_dir, 'pdo_sqlsrv')
|
||||
else:
|
||||
self.update_driver_source(source_dir, self.driver)
|
||||
|
||||
# Next, generate the build configuration and arguments
|
||||
cmd_line = self.generate_build_options()
|
||||
print('cmd_line: ' + cmd_line)
|
||||
|
||||
# Generate a batch file based on the inputs
|
||||
if log_file is None:
|
||||
log_file = self.get_logfile_name()
|
||||
|
||||
batch_file = self.create_local_batch_file(make_clean, cmd_line, log_file)
|
||||
|
||||
# Reference: https://github.com/OSTC/php-sdk-binary-tools
|
||||
# Clone the master branch of PHP sdk if the directory does not exist
|
||||
print('Downloading the latest php SDK...')
|
||||
|
||||
# if *dest* is None, simply use the current working directory
|
||||
sdk_dir = dest
|
||||
copy_to_ext = True # this determines where to copy the binaries to
|
||||
if dest is None:
|
||||
sdk_dir = work_dir
|
||||
copy_to_ext = False
|
||||
|
||||
phpSDK = os.path.join(sdk_dir, 'php-sdk')
|
||||
if not os.path.exists( phpSDK ):
|
||||
os.system('git clone https://github.com/OSTC/php-sdk-binary-tools.git --branch master --single-branch --depth 1 ' + phpSDK)
|
||||
os.chdir(phpSDK)
|
||||
os.system('git pull ')
|
||||
|
||||
# Move the generated batch file to phpSDK for the php starter script
|
||||
sdk_batch_file = os.path.join(phpSDK, batch_file)
|
||||
if os.path.exists(sdk_batch_file):
|
||||
os.remove(sdk_batch_file)
|
||||
shutil.move(os.path.join(work_dir, batch_file), phpSDK)
|
||||
|
||||
sdk_source = os.path.join(phpSDK, 'Source')
|
||||
# Sometimes, for various reasons, the Source folder from previous build
|
||||
# might exist in phpSDK. If so, remove it first
|
||||
if os.path.exists(sdk_source):
|
||||
os.chmod(sdk_source, stat.S_IWRITE)
|
||||
shutil.rmtree(sdk_source, ignore_errors=True)
|
||||
shutil.move(source_dir, phpSDK)
|
||||
|
||||
# Invoke phpsdk-<vc>-<arch>.bat
|
||||
vc = self.compiler_version()
|
||||
starter_script = 'phpsdk-' + vc + '-' + self.arch + '.bat'
|
||||
print('Running starter script: ', starter_script)
|
||||
os.system(starter_script + ' -t ' + batch_file)
|
||||
|
||||
# Now we can safely remove the Source folder, because its contents have
|
||||
# already been modified prior to building the extensions
|
||||
shutil.rmtree(os.path.join(phpSDK, 'Source'), ignore_errors=True)
|
||||
|
||||
# Next, rename the newly compiled PHP extensions
|
||||
self.rename_binaries(sdk_dir)
|
||||
|
||||
# Final step, copy the binaries to the right place
|
||||
ext_dir = self.copy_binaries(sdk_dir, copy_to_ext)
|
||||
return ext_dir
|
||||
|
||||
def rename_binary(self, path, driver):
|
||||
"""Rename the *driver* binary (sqlsrv or pdo_sqlsrv) (only the dlls)."""
|
||||
driver_old_name = self.driver_name(driver, '.dll')
|
||||
driver_new_name = self.driver_new_name(driver, '.dll')
|
||||
|
||||
os.rename(os.path.join(path, driver_old_name), os.path.join(path, driver_new_name))
|
||||
|
||||
def rename_binaries(self, sdk_dir):
|
||||
"""Rename the sqlsrv and/or pdo_sqlsrv dlls according to the PHP
|
||||
version and thread.
|
||||
"""
|
||||
|
||||
# Derive the path to where the extensions are located
|
||||
ext_dir = self.build_abs_path(sdk_dir)
|
||||
print("Renaming binaries in ", ext_dir)
|
||||
|
||||
if self.driver == 'all':
|
||||
self.rename_binary(ext_dir, 'sqlsrv')
|
||||
self.rename_binary(ext_dir, 'pdo_sqlsrv')
|
||||
else:
|
||||
self.rename_binary(ext_dir, self.driver)
|
||||
|
||||
def copy_binary(self, from_dir, dest_dir, driver, suffix):
|
||||
"""Copy sqlsrv or pdo_sqlsrv binary (based on *suffix*) to *dest_dir*."""
|
||||
if suffix == '.dll':
|
||||
binary = self.driver_new_name(driver, suffix)
|
||||
else:
|
||||
binary = self.driver_name(driver, suffix)
|
||||
shutil.copy2(os.path.join(from_dir, binary), dest_dir)
|
||||
|
||||
def copy_binaries(self, sdk_dir, copy_to_ext):
|
||||
"""Copy the sqlsrv and/or pdo_sqlsrv binaries, including the pdb files,
|
||||
to the right place, depending on *copy_to_ext*. The default is to
|
||||
copy them to the 'ext' folder.
|
||||
"""
|
||||
build_dir = self.build_abs_path(sdk_dir)
|
||||
print('Copying the binaries from', build_dir)
|
||||
if copy_to_ext:
|
||||
dest_dir = os.path.join(build_dir, 'ext')
|
||||
else:
|
||||
# Simply make a copy of the binaries in sdk_dir
|
||||
dest_dir = sdk_dir
|
||||
|
||||
print('Destination:', dest_dir)
|
||||
|
||||
# Now copy the binaries
|
||||
if self.driver == 'all':
|
||||
self.copy_binary(build_dir, dest_dir, 'sqlsrv', '.dll')
|
||||
self.copy_binary(build_dir, dest_dir, 'sqlsrv', '.pdb')
|
||||
self.copy_binary(build_dir, dest_dir, 'pdo_sqlsrv', '.dll')
|
||||
self.copy_binary(build_dir, dest_dir, 'pdo_sqlsrv', '.pdb')
|
||||
else:
|
||||
self.copy_binary(build_dir, dest_dir, self.driver, '.dll')
|
||||
self.copy_binary(build_dir, dest_dir, self.driver, '.pdb')
|
||||
|
||||
return dest_dir
|
||||
|
16
entrypoint.sh
Normal file
16
entrypoint.sh
Normal file
|
@ -0,0 +1,16 @@
|
|||
set -e
|
||||
|
||||
testConnection="/opt/mssql-tools/bin/sqlcmd -S sql -U sa -P Password123"
|
||||
|
||||
for run in {1..10}; do
|
||||
|
||||
>&2 echo "SQL Server is starting up.."
|
||||
if $testConnection; then
|
||||
>&2 echo "SQL Server is up!"
|
||||
break;
|
||||
else
|
||||
sleep 6
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: JScript build configuration used by buildconf.bat
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Implements the PDO object for PDO_SQLSRV
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -30,8 +30,8 @@ namespace {
|
|||
|
||||
const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;";
|
||||
const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer
|
||||
const char TABLE_LAST_INSERT_ID_QUERY[] = "SELECT IDENT_CURRENT(%s)";
|
||||
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( TABLE_LAST_INSERT_ID_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes
|
||||
const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s";
|
||||
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes
|
||||
|
||||
// List of PDO supported connection options.
|
||||
namespace PDOConnOptionNames {
|
||||
|
@ -43,6 +43,10 @@ const char AttachDBFileName[] = "AttachDbFileName";
|
|||
const char ConnectionPooling[] = "ConnectionPooling";
|
||||
const char Authentication[] = "Authentication";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
|
||||
#ifdef _WIN32
|
||||
const char ConnectRetryCount[] = "ConnectRetryCount";
|
||||
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
||||
|
@ -230,6 +234,33 @@ const connection_option PDO_CONN_OPTS[] = {
|
|||
CONN_ATTR_STRING,
|
||||
column_encryption_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::ConnectRetryCount,
|
||||
|
@ -1245,7 +1276,7 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
|
|||
size_t quoted_len = 0;
|
||||
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), "ed_table, "ed_len, PDO_PARAM_NULL TSRMLS_CC );
|
||||
SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name.");
|
||||
snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, TABLE_LAST_INSERT_ID_QUERY, quoted_table );
|
||||
snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table );
|
||||
sqlsrv_free( quoted_table );
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: initialization routines for PDO_SQLSRV
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// Copyright Microsoft Corporation
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Implements the PDOStatement object for the PDO_SQLSRV
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Utility functions used by both connection or statement functions
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -380,6 +380,22 @@ 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_AE_ERROR_DRIVER_NOT_INSTALLED,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// Contents: Declarations for the extension
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Version resource
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// Contents: Contains functions for handling Windows format strings
|
||||
// and UTF-16 on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: Contains functions for handling Windows format strings
|
||||
// and UTF-16 on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains functions for handling UTF-16 on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains functions for handling UTF-16 on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -71,7 +71,7 @@ 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
|
||||
|
@ -148,7 +148,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
build_connection_string_and_set_conn_attr(conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_options TSRMLS_CC);
|
||||
bool missing_driver_error = false;
|
||||
if (conn->ce_option.enabled) {
|
||||
r = core_odbc_connect(conn, conn_options, DRIVER_VERSION::ODBC_DRIVER_13, wconn_string, wconn_len, missing_driver_error);
|
||||
r = core_odbc_connect(conn, conn_options, DRIVER_VERSION::ODBC_DRIVER_17, wconn_string, wconn_len, missing_driver_error);
|
||||
|
||||
CHECK_CUSTOM_ERROR(missing_driver_error, conn, SQLSRV_AE_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||
throw core::CoreException();
|
||||
|
@ -156,7 +156,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
}
|
||||
else {
|
||||
|
||||
for (std::size_t i = DRIVER_VERSION::ODBC_DRIVER_11; i <= DRIVER_VERSION::LAST; ++i) {
|
||||
for (std::size_t i = DRIVER_VERSION::ODBC_DRIVER_13; i <= DRIVER_VERSION::LAST; ++i) {
|
||||
r = core_odbc_connect(conn, conn_options, static_cast<DRIVER_VERSION>(i), wconn_string, wconn_len, missing_driver_error);
|
||||
// if it's a IM002, meaning that the correct ODBC driver is not installed
|
||||
CHECK_CUSTOM_ERROR(missing_driver_error && (i == DRIVER_VERSION::LAST), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||
|
@ -176,6 +176,8 @@ 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.
|
||||
//
|
||||
|
@ -794,6 +796,66 @@ 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 = Z_STRLEN_P( conn->ce_option.ksp_name );
|
||||
unsigned int key_size = 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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: common initialization routines shared by PDO and sqlsrv
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Result sets
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -1048,9 +1048,13 @@ 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)
|
||||
col_encryption_option() : enabled( false ), key_size ( 0 ), ksp_required( false )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -1099,6 +1103,9 @@ const char ApplicationIntent[] = "ApplicationIntent";
|
|||
const char AttachDBFileName[] = "AttachDbFileName";
|
||||
const char Authentication[] = "Authentication";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
const char CharacterSet[] = "CharacterSet";
|
||||
const char ConnectionPooling[] = "ConnectionPooling";
|
||||
#ifdef _WIN32
|
||||
|
@ -1146,6 +1153,9 @@ enum SQLSRV_CONN_OPTIONS {
|
|||
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER,
|
||||
SQLSRV_CONN_OPTION_AUTHENTICATION,
|
||||
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
|
||||
SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION,
|
||||
#ifdef _WIN32
|
||||
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT,
|
||||
|
@ -1649,6 +1659,10 @@ 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,
|
||||
|
||||
// Driver specific error codes starts from here.
|
||||
SQLSRV_ERROR_DRIVER_SPECIFIC = 1000,
|
||||
|
@ -2401,7 +2415,7 @@ struct str_conn_attr_func {
|
|||
|
||||
struct column_encryption_set_func {
|
||||
|
||||
static void func(connection_option const* option, zval* value, sqlsrv_conn* conn, std::string& conn_str TSRMLS_DC)
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC)
|
||||
{
|
||||
convert_to_string(value);
|
||||
const char* value_str = Z_STRVAL_P(value);
|
||||
|
@ -2419,4 +2433,37 @@ struct column_encryption_set_func {
|
|||
}
|
||||
};
|
||||
|
||||
struct ce_ksp_provider_set_func {
|
||||
|
||||
static void 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CORE_SQLSRV_H
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Implementation of PHP streams for reading SQL Server data
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// Comments: Mostly error handling and some type handling
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: Contains functions for handling Windows format strings
|
||||
// and UTF-16 on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: Contains a portable abstraction for interlocked, atomic
|
||||
// operations on int32_t and pointer types.
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: Contains a portable abstraction for interlocked, atomic
|
||||
// operations on int32_t and pointer types.
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: Contains a portable abstraction for interlocked, singly
|
||||
// linked list.
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains portable classes for localization
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Must be included in one c/cpp file per binary
|
||||
// A build error will occur if this inclusion policy is not followed
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// pecuniary loss) arising out of the use of or inability to use
|
||||
// this SDK, even if Microsoft has been advised of the possibility
|
||||
// of such damages.
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -370,6 +370,45 @@
|
|||
#pragma warning(disable:4200)
|
||||
#endif
|
||||
|
||||
// Keystore Provider interface definition
|
||||
typedef struct CEKeystoreContext
|
||||
{
|
||||
void *envCtx;
|
||||
void *dbcCtx;
|
||||
void *stmtCtx;
|
||||
} CEKEYSTORECONTEXT;
|
||||
|
||||
typedef void errFunc(CEKEYSTORECONTEXT *ctx, const wchar_t *msg, ...);
|
||||
|
||||
#define IDS_MSG(x) ((const wchar_t*)(x))
|
||||
|
||||
typedef struct CEKeystoreProvider
|
||||
{
|
||||
wchar_t *Name;
|
||||
int (__stdcall *Init)(CEKEYSTORECONTEXT *ctx, errFunc *onError);
|
||||
int (__stdcall *Read)(CEKEYSTORECONTEXT *ctx, errFunc *onError, void *data, unsigned int *len);
|
||||
int (__stdcall *Write)(CEKEYSTORECONTEXT *ctx, errFunc *onError, void *data, unsigned int len);
|
||||
int (__stdcall *DecryptCEK)(
|
||||
CEKEYSTORECONTEXT *ctx,
|
||||
errFunc *onError,
|
||||
const wchar_t *keyPath,
|
||||
const wchar_t *alg,
|
||||
unsigned char *ecek,
|
||||
unsigned short ecekLen,
|
||||
unsigned char **cekOut,
|
||||
unsigned short *cekLen);
|
||||
int(__stdcall *EncryptCEK)(
|
||||
CEKEYSTORECONTEXT *ctx,
|
||||
errFunc *onError,
|
||||
const wchar_t *keyPath,
|
||||
const wchar_t *alg,
|
||||
unsigned char *cek,
|
||||
unsigned short cekLen,
|
||||
unsigned char **ecekOut,
|
||||
unsigned short *ecekLen);
|
||||
void (__stdcall *Free)();
|
||||
} CEKEYSTOREPROVIDER;
|
||||
|
||||
// Communication between the driver and application via the CEKeystoreData structure
|
||||
typedef struct CEKeystoreData
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains the minimal definitions to build on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//---------------------------------------------------------------------------------------------------------------------------------
|
||||
// File: typedefs_for_linux.h
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// File: version.h
|
||||
// Contents: Version number constants
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -26,8 +26,8 @@
|
|||
// Increase Major number with backward incompatible breaking changes.
|
||||
// Increase Minor with backward compatible new functionalities and API changes.
|
||||
// Increase Patch for backward compatible fixes.
|
||||
#define SQLVERSION_MAJOR 4
|
||||
#define SQLVERSION_MINOR 3
|
||||
#define SQLVERSION_MAJOR 5
|
||||
#define SQLVERSION_MINOR 0
|
||||
#define SQLVERSION_PATCH 0
|
||||
#define SQLVERSION_BUILD 0
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
|||
// for stable releases should be empty
|
||||
// "-RC" for release candidates
|
||||
// "-preview" for ETP
|
||||
#define SEMVER_PRERELEASE
|
||||
#define SEMVER_PRERELEASE "preview"
|
||||
// Semantic versioning build metadata, build meta data is not counted in precedence order.
|
||||
#define SEMVER_BUILDMETA
|
||||
|
||||
|
@ -47,8 +47,10 @@
|
|||
// Main version, dot separated 3 digits, Major.Minor.Patch
|
||||
#define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH )
|
||||
|
||||
// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases)
|
||||
#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA
|
||||
// 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 _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
|
||||
|
||||
// PECL package version macros (can't have '-' or '+')
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: include for definition of Windows types for non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Contents: This module defines helper functions to prevent
|
||||
// integer overflow bugs.
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains the minimal definitions to build on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Contains the minimal definitions to build on non-Windows platforms
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: JScript build configuration used by buildconf.bat
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Routines that use connection handles
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -189,6 +189,10 @@ const char CharacterSet[] = "CharacterSet";
|
|||
const char Authentication[] = "Authentication";
|
||||
const char ConnectionPooling[] = "ConnectionPooling";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
|
||||
#ifdef _WIN32
|
||||
const char ConnectRetryCount[] = "ConnectRetryCount";
|
||||
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
||||
|
@ -296,10 +300,10 @@ const connection_option SS_CONN_OPTS[] = {
|
|||
},
|
||||
{
|
||||
SSConnOptionNames::ConnectionPooling,
|
||||
sizeof( SSConnOptionNames::ConnectionPooling ),
|
||||
sizeof(SSConnOptionNames::ConnectionPooling),
|
||||
SQLSRV_CONN_OPTION_CONN_POOLING,
|
||||
ODBCConnOptions::ConnectionPooling,
|
||||
sizeof( ODBCConnOptions::ConnectionPooling ),
|
||||
sizeof(ODBCConnOptions::ConnectionPooling),
|
||||
CONN_ATTR_BOOL,
|
||||
conn_null_func::func
|
||||
},
|
||||
|
@ -312,6 +316,33 @@ const connection_option SS_CONN_OPTS[] = {
|
|||
CONN_ATTR_STRING,
|
||||
column_encryption_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::ConnectRetryCount,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// File: init.cpp
|
||||
// Contents: initialization routines for the extension
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Comments: Also contains "internal" declarations shared across source files.
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Routines that use statement handles
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//
|
||||
// Contents: Version resource
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
// Comments: Mostly error handling and some type handling
|
||||
//
|
||||
// Microsoft Drivers 4.3 for PHP for SQL Server
|
||||
// Microsoft Drivers 5.0 for PHP for SQL Server
|
||||
// Copyright(c) Microsoft Corporation
|
||||
// All rights reserved.
|
||||
// MIT License
|
||||
|
@ -375,6 +375,22 @@ 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_AE_ERROR_DRIVER_NOT_INSTALLED,
|
||||
|
@ -382,6 +398,7 @@ ss_error SS_ERRORS[] = {
|
|||
"communicate with SQL Server with ColumnEncryption attribute enabled.", -105, false }
|
||||
},
|
||||
|
||||
|
||||
// terminate the list of errors/warnings
|
||||
{ UINT_MAX, {} }
|
||||
};
|
||||
|
|
|
@ -1,40 +1,41 @@
|
|||
## Setup Environment on a clean machine
|
||||
|
||||
### Windows
|
||||
Install Visual Studio 2015 before running the following commands. Make sure C++ tools are enabled.
|
||||
Run `cmd` as administrator.
|
||||
The PHP zip file can be downloaded from <http://php.net/downloads.php>.
|
||||
|
||||
The SQLSRV and PDO_SQLSRV driver binaries can be downloaded from <https://github.com/Microsoft/msphpsql/releases>.
|
||||
|
||||
### Windows
|
||||
Install Visual Studio 2015 redistributable before running the following commands.
|
||||
Run `Windows PowerShell` as administrator.
|
||||
|
||||
powershell
|
||||
Set-ExecutionPolicy Unrestricted
|
||||
.\setup_env_windows.ps1 <PHP_VERSION - 7.x.x> <PHP_THREAD - ts or nts> <Absolute path to driver source folder> <ARCH - x86 or x64>
|
||||
.\setup_env_windows.ps1 <absolute path to the PHP zip file> <absolute path to the SQLSRV driver DLL> <absolute path to the PDO_SQLSRV driver DLL>
|
||||
|
||||
### Ubuntu 16
|
||||
sudo env "PATH=$PATH" bash setup_env_unix.sh Ubuntu16 <PHP_VERSION - 7.x.x> <PHP_THREAD - ts or nts> <Absolute path to driver source folder>
|
||||
sudo env "PATH=$PATH" bash setup_env_unix.sh Ubuntu16 <PHP_VERSION - 7.x.y> <PHP_THREAD - ts or nts> <absolute path to the SQLSRV driver so> <absolute path to the PDO_SQLSRV driver so>
|
||||
### RedHat 7
|
||||
sudo env "PATH=$PATH" bash setup_env_unix.sh RedHat7 <PHP_VERSION - 7.x.x> <PHP_THREAD - ts or nts> <Absolute path to driver source folder>
|
||||
### Sierra
|
||||
sudo env "PATH=$PATH" bash setup_env_unix.sh RedHat7 <PHP_VERSION - 7.x.y> <PHP_THREAD - ts or nts> <absolute path to the SQLSRV driver so> <absolute path to the PDO_SQLSRV driver so>
|
||||
### SUSE 12
|
||||
sudo env "PATH=$PATH" bash setup_env_unix.sh SUSE12 <PHP_VERSION - 7.x.y> <PHP_THREAD - ts or nts> <absolute path to the SQLSRV driver so> <absolute path to the PDO_SQLSRV driver so>
|
||||
### MacOS 10.12 Sierra
|
||||
`brew` cannot be run with `sudo` on Sierra. Either enable passwordless `sudo` on the machine or enter the password when prompted.
|
||||
|
||||
bash setup_env_unix.sh Sierra <PHP_VERSION - 7.x.x> <PHP_THREAD - ts or nts> <Absolute path to driver source folder>
|
||||
bash setup_env_unix.sh Sierra <PHP_VERSION - 7.x.y> <PHP_THREAD - ts or nts> <absolute path to the SQLSRV driver so> <absolute path to the PDO_SQLSRV driver so>
|
||||
## Run benchmarks
|
||||
PHPBench is used to run the benchmarks. Visit http://phpbench.readthedocs.io/en/latest/introduction.html to have an idea how the tool works.
|
||||
|
||||
### 1. Modify lib/connect.php with the test database credentials
|
||||
### 2. Execute run-perf_tests.py.
|
||||
##### 1. Modify lib/connect.php with the test database credentials
|
||||
##### 2. Modify lib/result_db.php with the result database credentials
|
||||
##### 3. The number of iterations for each test can be modified in the test itself (e.g., in test/Performance/benchmark/sqlsrv/SqlsrvSelectVersionBench.php). Each bench class in a test, or the parent class that it extends from, has a @Iteration(n) annotation. If you change the number in this annotation, you will change the number of iterations run for this test. By default, most tests are set to 1000 iterations.
|
||||
##### 4. Execute run-perf_tests.py.
|
||||
|
||||
### Windows
|
||||
py.exe run-perf_tests.py -platform <PLATFORM> -iterations <ITERATIONS> -iterations-large <ITERATIONS_LARGE> -result-server <RESULT_SERVER> -result-db <RESULT_DB> -result-uid <RESULT_UID> -result-pwd <RESULT_PWD
|
||||
py.exe run-perf_tests.py -platform <PLATFORM>
|
||||
### Linux and Mac
|
||||
On Linux and Mac, the script must be executed with `sudo python3` because to enable pooling it needs to modify odbcinst.ini system file. As an improvement, the location of the odbcinst.ini file can be changed so that, sudo is not requiered.
|
||||
|
||||
python3 run-perf_tests.py -platform <PLATFORM> -iterations <ITERATIONS> -iterations-large <ITERATIONS_LARGE> -result-server <RESULT_SERVER> -result-db <RESULT_DB> -result-uid <RESULT_UID> -result-pwd <RESULT_PWD>
|
||||
|
||||
`-platform` - The platform that the tests are ran on. Must be one of the following: Windows10, WindowsServer2016 WindowsServer2012 Ubuntu16 RedHat7 Sierra
|
||||
`-iterations` - The number of iterations for regular tests.
|
||||
`-iterations-large` - The number of iterations for the tests that fetch large data. Usually set to 1.
|
||||
`-result-server` - The server of result database. It is assumed that, the result database already setup before running the tests.
|
||||
`-result-db` - Database name. With the current result database setup files, this should be set to `TestResults`
|
||||
`-result-uid` - Result database username
|
||||
`-result-pwd` Result database password
|
||||
|
||||
On Linux and Mac, the script must be executed with `sudo python3` because to enable pooling it needs to modify odbcinst.ini system file. As an improvement, the location of the odbcinst.ini file can be changed so that sudo is not required.
|
||||
|
||||
python3 run-perf_tests.py -platform <PLATFORM> | tee run-perf_output.txt
|
||||
|
||||
`-platform` - The platform that the tests are ran on. Must be one of the following: Windows10, WindowsServer2016, WindowsServer2012, Ubuntu16, RedHat7, SUSE12, Sierra.
|
||||
`-php-driver` (optional) - The driver that the tests are ran on. Must be one of the following: sqlsrv, pdo_sqlsrv, or both. Default is both.
|
||||
`-testname` (optional) - The test to run. Must be the file name (not including path) of one test or 'all'. Default is 'all'. If one test is specified, must also specify the -php-driver option to sqlsrv or pdo_sqlsrv.
|
79
test/Performance/benchmark/pdo_sqlsrv/PDOCRUDBench.php
Normal file
79
test/Performance/benchmark/pdo_sqlsrv/PDOCRUDBench.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "generateUpdateValues", "generateUpdateParams"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
class PDOSqlsrvCRUDBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
private $updateValues;
|
||||
private $updateParams;
|
||||
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable()
|
||||
{
|
||||
PDOSqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = PDOSqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function generateUpdateValues()
|
||||
{
|
||||
$this->updateValues = PDOSqlsrvUtil::generateUpdateValues();
|
||||
}
|
||||
|
||||
public function generateUpdateParams()
|
||||
{
|
||||
$this->updateParams = PDOSqlsrvUtil::generateUpdateParams();
|
||||
}
|
||||
/**
|
||||
* Each iteration does the following $loopsPerCRUDIter times:
|
||||
* (i) insert a row into the table with insertWithPrepare
|
||||
* (ii) fetch the row with fetchWithPrepare
|
||||
* (iii) update the row's contents with updateWithPrepare
|
||||
* (iv) delete the row with delete
|
||||
* Every insertion calls prepare, bindParam and execute APIs.
|
||||
* Every fetch calls prepare, execute and fetch APIs.
|
||||
* Every update calls prepare, bindParam and execute APIs.
|
||||
* Every delete calls prepare and execute APIs.
|
||||
*/
|
||||
public function benchCRUDWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
PDOSqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
PDOSqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
PDOSqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
PDOSqlsrvUtil::deleteWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable()
|
||||
{
|
||||
PDOSqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
|
||||
class PDOConnectionBench{
|
||||
class PDOConnectionBench extends CRUDBaseBenchmark
|
||||
{
|
||||
/*
|
||||
* Opens a connection and closes it immediately
|
||||
*/
|
||||
public function benchConnectAndDisconnect(){
|
||||
public function benchConnectAndDisconnect()
|
||||
{
|
||||
$conn = PDOSqlsrvUtil::connect();
|
||||
PDOSqlsrvUtil::disconnect( $conn );
|
||||
}
|
|
@ -1,21 +1,25 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect"})
|
||||
* @AfterMethods({"disconnect"})
|
||||
*/
|
||||
class PDOCreateDbTableProcBench{
|
||||
class PDOCreateDbTableProcBench extends CRUDBaseBenchmark
|
||||
{
|
||||
private $conn;
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
/*
|
||||
* Each iteration creates a database, a table and a stored procedure in that database and drops the database at the end.
|
||||
* Note that, execDirect function are used to execute all the queries.
|
||||
*/
|
||||
public function benchCreateDbTableProc(){
|
||||
public function benchCreateDbTableProc()
|
||||
{
|
||||
$randomNum = rand();
|
||||
$databaseName = "test_db_$randomNum";
|
||||
$tableName = "test_table_$randomNum";
|
||||
|
@ -24,7 +28,8 @@ class PDOCreateDbTableProcBench{
|
|||
PDOSqlsrvUtil::dropDatabase( $this->conn, $databaseName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,34 +1,42 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare"})
|
||||
* @AfterMethods({ "dropTable","disconnect"})
|
||||
*/
|
||||
class PDODeleteBench{
|
||||
class PDODeleteBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
PDOSqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = PDOSqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
PDOSqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
}
|
||||
|
@ -36,17 +44,21 @@ class PDODeleteBench{
|
|||
* Each iteration inserts 1000 rows into the table, benchDelete deletes top row from the table 1000 times.
|
||||
* Note that, every delete calls prepare and execute APIs.
|
||||
*/
|
||||
public function benchDelete(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
public function benchDelete()
|
||||
{
|
||||
for( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
PDOSqlsrvUtil::deleteWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
PDOSqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
|
@ -1,32 +1,39 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare"})
|
||||
* @AfterMethods({"dropTable", "disconnect"})
|
||||
*/
|
||||
class PDOFetchBench{
|
||||
class PDOFetchBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
PDOSqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = PDOSqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
PDOSqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
|
||||
|
@ -34,17 +41,21 @@ class PDOFetchBench{
|
|||
* Each iteration inserts a row into the table, benchFetchWithPrepare() fetches that row 1000 times.
|
||||
* Note that, every fetch calls prepare, execute and fetch APIs.
|
||||
*/
|
||||
public function benchFetchWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++){
|
||||
public function benchFetchWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++)
|
||||
{
|
||||
PDOSqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
PDOSqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -2,30 +2,36 @@
|
|||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
/**
|
||||
* @Iterations(1)
|
||||
* @BeforeMethods({"connect", "setTableName" })
|
||||
* @AfterMethods({ "disconnect"})
|
||||
*/
|
||||
class PDOFetchLargeBench{
|
||||
class PDOFetchLargeBench
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
//Assumes the table is already populated with data
|
||||
$this->tableName = "LargeDB.dbo.datatypes";
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
/*
|
||||
* Each iteration calls prepare, execute and fetch APIs to fetch the already populdated data
|
||||
* Each iteration calls prepare, execute and fetch APIs to fetch the already populated data
|
||||
*/
|
||||
public function benchFetchWithPrepare(){
|
||||
public function benchFetchWithPrepare()
|
||||
{
|
||||
PDOSqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,45 +1,55 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
class PDOInsertBench{
|
||||
class PDOInsertBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
PDOSqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = PDOSqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
/**
|
||||
* Each iteration inserts 1000 rows into the table.
|
||||
* Note that, every insertion calls prepare, bindParam and execute APIs.
|
||||
*/
|
||||
public function benchInsertWithPrepare(){
|
||||
for ( $i=0; $i<1000; $i++){
|
||||
public function benchInsertWithPrepare()
|
||||
{
|
||||
for ( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++)
|
||||
{
|
||||
PDOSqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
PDOSqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -2,24 +2,29 @@
|
|||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
/**
|
||||
* @Iterations(10000)
|
||||
* @BeforeMethods({"connect"})
|
||||
* @AfterMethods({"disconnect"})
|
||||
*/
|
||||
class PDOSelectVersionBench{
|
||||
class PDOSelectVersionBench
|
||||
{
|
||||
|
||||
private $conn;
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
/*
|
||||
* Each iteration calls execDirect API to fetch @@Version
|
||||
*/
|
||||
public function benchSelectVersion(){
|
||||
public function benchSelectVersion()
|
||||
{
|
||||
$version = PDOSqlsrvUtil::selectVersion( $this->conn );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
<?php
|
||||
|
||||
use PDOSqlsrvPerfTest\PDOSqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare", "generateUpdateValues", "generateUpdateParams"})
|
||||
* @AfterMethods({"dropTable", "disconnect"})
|
||||
*/
|
||||
class PDOUpdateBench{
|
||||
class PDOUpdateBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
|
@ -13,48 +15,59 @@ class PDOUpdateBench{
|
|||
private $updateValues;
|
||||
private $updateParams;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = PDOSqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
PDOSqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = PDOSqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
PDOSqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
|
||||
public function generateUpdateValues(){
|
||||
public function generateUpdateValues()
|
||||
{
|
||||
$this->updateValues = PDOSqlsrvUtil::generateUpdateValues();
|
||||
}
|
||||
|
||||
public function generateUpdateParams(){
|
||||
public function generateUpdateParams()
|
||||
{
|
||||
$this->updateParams = PDOSqlsrvUtil::generateUpdateParams();
|
||||
}
|
||||
/**
|
||||
* Each iteration inserts a row into the table, updateWithPrepare() updates that row 1000 times.
|
||||
* Note that, every update calls prepare, bindParam and execute APIs.
|
||||
*/
|
||||
public function benchUpdateWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
$stmt = PDOSqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
public function benchUpdateWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<PDOSqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
PDOSqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
PDOSqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
PDOSqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
79
test/Performance/benchmark/sqlsrv/SqlsrvCRUDBench.php
Normal file
79
test/Performance/benchmark/sqlsrv/SqlsrvCRUDBench.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "generateUpdateValues", "generateUpdateParams"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
class SqlsrvCRUDBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
private $updateValues;
|
||||
private $updateParams;
|
||||
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable()
|
||||
{
|
||||
SqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = SqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function generateUpdateValues()
|
||||
{
|
||||
$this->updateValues = SqlsrvUtil::generateUpdateValues();
|
||||
}
|
||||
|
||||
public function generateUpdateParams()
|
||||
{
|
||||
$this->updateParams = SqlsrvUtil::generateUpdateParams();
|
||||
}
|
||||
/**
|
||||
* Each iteration does the following $loopsPerCRUDIter times:
|
||||
* (i) insert a row into the table with insertWithPrepare
|
||||
* (ii) fetch the row with fetchWithPrepare
|
||||
* (iii) update the row's contents with updateWithPrepare
|
||||
* (iv) delete the row with delete
|
||||
* Every insertion calls prepare, bindParam and execute APIs.
|
||||
* Every fetch calls prepare, execute and fetch APIs.
|
||||
* Every update calls prepare, bindParam and execute APIs.
|
||||
* Every delete calls prepare and execute APIs.
|
||||
*/
|
||||
public function benchCRUDWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
SqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
SqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
SqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
SqlsrvUtil::delete( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable()
|
||||
{
|
||||
SqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
|
||||
class SqlsrvConnectionBench{
|
||||
class SqlsrvConnectionBench extends CRUDBaseBenchmark
|
||||
{
|
||||
/*
|
||||
* Opens a connection and closes it immediately
|
||||
*/
|
||||
public function benchConnectAndDisconnect(){
|
||||
public function benchConnectAndDisconnect()
|
||||
{
|
||||
$conn = SqlsrvUtil::connect();
|
||||
SqlsrvUtil::disconnect( $conn );
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect"})
|
||||
* @AfterMethods({"disconnect"})
|
||||
*/
|
||||
class SqlsrvCreateDbTableProcBench{
|
||||
class SqlsrvCreateDbTableProcBench extends CRUDBaseBenchmark
|
||||
{
|
||||
private $conn;
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
|
@ -16,7 +19,8 @@ class SqlsrvCreateDbTableProcBench{
|
|||
* Each iteration creates a database, a table and a stored procedure in that database and drops the database at the end.
|
||||
* Note that, ODBC SQLExecDirect function are used to execute all the queries.
|
||||
*/
|
||||
public function benchCreateDbTableProc(){
|
||||
public function benchCreateDbTableProc()
|
||||
{
|
||||
$randomNum = rand();
|
||||
$databaseName = "test_db_$randomNum";
|
||||
$tableName = "test_table_$randomNum";
|
||||
|
@ -25,7 +29,8 @@ class SqlsrvCreateDbTableProcBench{
|
|||
SqlsrvUtil::dropDatabase( $this->conn, $databaseName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,34 +1,42 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
class SqlsrvDeleteBench{
|
||||
class SqlsrvDeleteBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
SqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = SqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
for ( $i=0; $i<1000; $i++ ){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
for ( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
SqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
}
|
||||
|
@ -36,17 +44,21 @@ class SqlsrvDeleteBench{
|
|||
* Each iteration inserts 1000 rows into the table, benchDelete deletes top row from the table 1000 times.
|
||||
* Note that, every delete calls prepare and execute APIs.
|
||||
*/
|
||||
public function benchDelete(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
public function benchDelete()
|
||||
{
|
||||
for( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
SqlsrvUtil::delete( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
SqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
|
@ -1,35 +1,40 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
|
||||
|
||||
class SqlsrvFetchBench{
|
||||
class SqlsrvFetchBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
SqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = SqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
SqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
|
||||
|
@ -37,17 +42,21 @@ class SqlsrvFetchBench{
|
|||
* Each iteration inserts a row into the table, benchFetchWithPrepare() fetches that row 1000 times.
|
||||
* Note that, every fetch calls prepare, execute and fetch APIs.
|
||||
*/
|
||||
public function benchFetchWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++){
|
||||
public function benchFetchWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++)
|
||||
{
|
||||
SqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
SqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -2,30 +2,36 @@
|
|||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
/**
|
||||
* @Iterations(1)
|
||||
* @BeforeMethods({"connect", "setTableName" })
|
||||
* @AfterMethods({ "disconnect"})
|
||||
*/
|
||||
class SqlsrvFetchLargeBench{
|
||||
class SqlsrvFetchLargeBench
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
//Assumes the table is already populated with data
|
||||
$this->tableName = "LargeDB.dbo.datatypes";
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
/*
|
||||
* Each iteration calls prepare, execute and fetch APIs to fetch the already populdated data
|
||||
* Each iteration calls prepare, execute and fetch APIs to fetch the already populated data
|
||||
*/
|
||||
public function benchFetchWithPrepare(){
|
||||
public function benchFetchWithPrepare()
|
||||
{
|
||||
SqlsrvUtil::fetchWithPrepare( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,46 +1,56 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues"})
|
||||
* @AfterMethods({"dropTable","disconnect"})
|
||||
*/
|
||||
class SqlsrvInsertBench{
|
||||
class SqlsrvInsertBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
private $insertValues;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
SqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = SqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
/**
|
||||
* Each iteration inserts 1000 rows into the table.
|
||||
* Note that, every insertion calls prepare, bindParam and execute APIs.
|
||||
*/
|
||||
public function benchInsertWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
public function benchInsertWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
SqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
SqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -2,24 +2,29 @@
|
|||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
/**
|
||||
* @Iterations(10000)
|
||||
* @BeforeMethods({"connect"})
|
||||
* @AfterMethods({"disconnect"})
|
||||
*/
|
||||
class SqlsrvSelectVersionBench{
|
||||
class SqlsrvSelectVersionBench
|
||||
{
|
||||
|
||||
private $conn;
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
/*
|
||||
* Each iteration calls execDirect API to fetch @@Version
|
||||
*/
|
||||
public function benchSelectVersion(){
|
||||
public function benchSelectVersion()
|
||||
{
|
||||
$version = SqlsrvUtil::selectVersion( $this->conn );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
<?php
|
||||
|
||||
use SqlsrvPerfTest\SqlsrvUtil;
|
||||
include_once __DIR__ . "/../../lib/CRUDBaseBenchmark.php";
|
||||
/**
|
||||
* @BeforeMethods({"connect", "setTableName", "createTable", "generateInsertValues", "insertWithPrepare", "generateUpdateValues", "generateUpdateParams"})
|
||||
* @AfterMethods({ "dropTable", "disconnect"})
|
||||
*/
|
||||
class SqlsrvUpdateBench{
|
||||
class SqlsrvUpdateBench extends CRUDBaseBenchmark
|
||||
{
|
||||
|
||||
private $conn;
|
||||
private $tableName;
|
||||
|
@ -13,48 +15,59 @@ class SqlsrvUpdateBench{
|
|||
private $updateValues;
|
||||
private $updateParams;
|
||||
|
||||
public function setTableName(){
|
||||
public function setTableName()
|
||||
{
|
||||
$this->tableName = "datatypes_".rand();
|
||||
}
|
||||
|
||||
public function connect(){
|
||||
public function connect()
|
||||
{
|
||||
$this->conn = SqlsrvUtil::connect();
|
||||
}
|
||||
|
||||
public function createTable(){
|
||||
public function createTable()
|
||||
{
|
||||
SqlsrvUtil::createCRUDTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function generateInsertValues(){
|
||||
public function generateInsertValues()
|
||||
{
|
||||
$this->insertValues = SqlsrvUtil::generateInsertValues();
|
||||
}
|
||||
|
||||
public function insertWithPrepare(){
|
||||
public function insertWithPrepare()
|
||||
{
|
||||
SqlsrvUtil::insertWithPrepare( $this->conn, $this->tableName, $this->insertValues );
|
||||
}
|
||||
|
||||
public function generateUpdateValues(){
|
||||
public function generateUpdateValues()
|
||||
{
|
||||
$this->updateValues = SqlsrvUtil::generateUpdateValues();
|
||||
}
|
||||
|
||||
public function generateUpdateParams(){
|
||||
public function generateUpdateParams()
|
||||
{
|
||||
$this->updateParams = SqlsrvUtil::generateUpdateParams();
|
||||
}
|
||||
/**
|
||||
* Each iteration inserts a row into the table, updateWithPrepare() updates that row 1000 times.
|
||||
* Note that, every update calls prepare, bindParam and execute APIs.
|
||||
*/
|
||||
public function benchUpdateWithPrepare(){
|
||||
for( $i=0; $i<1000; $i++ ){
|
||||
$stmt = SqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
public function benchUpdateWithPrepare()
|
||||
{
|
||||
for( $i=0; $i<SqlsrvUtil::$loopsPerCRUDIter; $i++ )
|
||||
{
|
||||
SqlsrvUtil::updateWithPrepare( $this->conn, $this->tableName, $this->updateValues, $this->updateParams );
|
||||
}
|
||||
}
|
||||
|
||||
public function dropTable(){
|
||||
public function dropTable()
|
||||
{
|
||||
SqlsrvUtil::dropTable( $this->conn, $this->tableName );
|
||||
}
|
||||
|
||||
public function disconnect(){
|
||||
public function disconnect()
|
||||
{
|
||||
SqlsrvUtil::disconnect( $this->conn );
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
set options=%2
|
||||
set options=%options:"=%
|
||||
C:\php-sdk\bin\phpsdk_setvars.bat && "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %1 && .\buildconf --force && .\configure %options% && nmake && nmake install
|
9
test/Performance/lib/CRUDBaseBenchmark.php
Normal file
9
test/Performance/lib/CRUDBaseBenchmark.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @Iterations(1000)
|
||||
*/
|
||||
abstract class CRUDBaseBenchmark
|
||||
{
|
||||
}
|
||||
?>
|
|
@ -2,32 +2,41 @@
|
|||
|
||||
namespace PDOSqlsrvPerfTest;
|
||||
use PDO;
|
||||
class PDOSqlsrvUtil{
|
||||
class PDOSqlsrvUtil
|
||||
{
|
||||
|
||||
public static function connect(){
|
||||
public static $loopsPerCRUDIter = 100;
|
||||
|
||||
public static function connect()
|
||||
{
|
||||
require dirname(__FILE__).DIRECTORY_SEPARATOR.'connect.php';
|
||||
try{
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:Server=$server; Database=$database; ConnectionPooling=$pooling; MultipleActiveResultSets=$mars" , $uid, $pwd );
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
return $conn;
|
||||
}
|
||||
catch( PDOException $e ){
|
||||
catch( PDOException $e )
|
||||
{
|
||||
var_dump( $e );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
public static function disconnect( $conn ){
|
||||
public static function disconnect( $conn )
|
||||
{
|
||||
$conn = null;
|
||||
}
|
||||
|
||||
public static function selectVersion( $conn ){
|
||||
public static function selectVersion( $conn )
|
||||
{
|
||||
$sql = "SELECT @@Version";
|
||||
$stmt = self::query( $conn, $sql );
|
||||
return self::fetch( $stmt );
|
||||
}
|
||||
|
||||
public static function createDbTableProc( $conn, $databaseName, $tableName, $procName ){
|
||||
public static function createDbTableProc( $conn, $databaseName, $tableName, $procName )
|
||||
{
|
||||
$tableParams = "id INTEGER, name VARCHAR(32), value INTEGER, start_date DATE, time_added TIMESTAMP, set_time TIME(7)";
|
||||
$procParams = "@id INTEGER, @name VARCHAR(32)";
|
||||
$procTextBase = "SET NOCOUNT ON; SELECT id, name, value FROM $databaseName.$tableName WHERE id = @id AND name = @name";
|
||||
|
@ -37,7 +46,8 @@ class PDOSqlsrvUtil{
|
|||
self::createStoredProc( $conn, $procName, $procParams, $procTextBase );
|
||||
}
|
||||
|
||||
public static function generateInsertValues(){
|
||||
public static function generateInsertValues()
|
||||
{
|
||||
$vcharVal = "test string";
|
||||
$nvcharVal = "wstring";
|
||||
$intVal = 3;
|
||||
|
@ -52,7 +62,8 @@ class PDOSqlsrvUtil{
|
|||
return $values;
|
||||
}
|
||||
|
||||
public static function generateUpdateValues(){
|
||||
public static function generateUpdateValues()
|
||||
{
|
||||
$vcharVal = "test string updated";
|
||||
$nvcharVal = "wstring updated";
|
||||
$intVal = 5;
|
||||
|
@ -67,7 +78,8 @@ class PDOSqlsrvUtil{
|
|||
return $updatedValues;
|
||||
}
|
||||
|
||||
public static function generateUpdateParams(){
|
||||
public static function generateUpdateParams()
|
||||
{
|
||||
$fieldNames = array(
|
||||
"vstring",
|
||||
"nvstring",
|
||||
|
@ -81,41 +93,47 @@ class PDOSqlsrvUtil{
|
|||
"dtoffset");
|
||||
|
||||
$params = "";
|
||||
foreach( $fieldNames as $fieldName ){
|
||||
foreach( $fieldNames as $fieldName )
|
||||
{
|
||||
$params = $params.$fieldName."=?,";
|
||||
}
|
||||
$params = rtrim($params,", ");
|
||||
return $params;
|
||||
}
|
||||
|
||||
public static function insertWithPrepare( $conn, $tableName, $values ){
|
||||
public static function insertWithPrepare( $conn, $tableName, $values )
|
||||
{
|
||||
$sql = "INSERT INTO $tableName VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = self::prepare( $conn, $sql );
|
||||
self::bindParams( $stmt, $values );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
public static function fetchWithPrepare( $conn, $tableName ){
|
||||
public static function fetchWithPrepare( $conn, $tableName )
|
||||
{
|
||||
$sql = "SELECT * FROM $tableName";
|
||||
$stmt = self::prepare( $conn, $sql );
|
||||
self::execute( $stmt );
|
||||
while ( $row = self::fetch( $stmt )){}
|
||||
}
|
||||
|
||||
public static function deleteWithPrepare( $conn, $tableName ){
|
||||
public static function deleteWithPrepare( $conn, $tableName )
|
||||
{
|
||||
$sql = "DELETE TOP (1) FROM $tableName";
|
||||
$stmt = self::prepare( $conn, $sql );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
public static function updateWithPrepare( $conn, $tableName, $updateValues, $params ){
|
||||
public static function updateWithPrepare( $conn, $tableName, $updateValues, $params )
|
||||
{
|
||||
$sql = "UPDATE $tableName SET ".$params;
|
||||
$stmt = self::prepare( $conn, $sql );
|
||||
self::bindParams( $stmt, $updateValues );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
private function bindParams( $stmt, $values ){
|
||||
private function bindParams( $stmt, $values )
|
||||
{
|
||||
//This functionn assumes the fields are from createCRUDTable()
|
||||
self::bindParam( $stmt, 1, $values[0], PDO::PARAM_STR);
|
||||
self::bindParam( $stmt, 2, $values[1], PDO::PARAM_STR);
|
||||
|
@ -129,7 +147,8 @@ class PDOSqlsrvUtil{
|
|||
self::bindParam( $stmt, 10, $values[9], PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
public static function createCRUDTable( $conn, $tableName ){
|
||||
public static function createCRUDTable( $conn, $tableName )
|
||||
{
|
||||
$fields = array(
|
||||
"vstring" => "VARCHAR(64)",
|
||||
"nvstring" => "NVARCHAR(64)",
|
||||
|
@ -142,86 +161,106 @@ class PDOSqlsrvUtil{
|
|||
"vbin" => "VARBINARY",
|
||||
"dtoffset" => "DATETIMEOFFSET");
|
||||
$params = "";
|
||||
foreach( $fields as $fieldName => $type ){
|
||||
foreach( $fields as $fieldName => $type )
|
||||
{
|
||||
$params .= $fieldName." ".$type.",";
|
||||
}
|
||||
$params = rtrim($params,", ");
|
||||
self::createTable( $conn, $tableName, $params );
|
||||
}
|
||||
|
||||
private function createDatabase( $conn, $dbName ){
|
||||
private function createDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "CREATE DATABASE $dbName";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
public static function dropDatabase( $conn, $dbName ){
|
||||
public static function dropDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "USE MASTER;DROP DATABASE $dbName";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
public static function createTable( $conn, $tableName, $params ){
|
||||
public static function createTable( $conn, $tableName, $params )
|
||||
{
|
||||
$sql = "CREATE TABLE $tableName ($params)";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
public static function dropTable( $conn, $tableName ){
|
||||
public static function dropTable( $conn, $tableName )
|
||||
{
|
||||
$sql = "DROP TABLE $tableName";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
private function useDatabase( $conn, $dbName ){
|
||||
private function useDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "USE $dbName";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
private function createStoredProc( $conn, $procName, $params, $text ){
|
||||
private function createStoredProc( $conn, $procName, $params, $text )
|
||||
{
|
||||
$sql = "CREATE PROCEDURE $procName $params AS $text";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
private function dropStoredProc( $conn, $procName ){
|
||||
private function dropStoredProc( $conn, $procName )
|
||||
{
|
||||
$sql = "DROP PROCEDURE $procName";
|
||||
$conn->exec( $sql );
|
||||
}
|
||||
|
||||
private function query( $conn, $sql ){
|
||||
try{
|
||||
private function query( $conn, $sql )
|
||||
{
|
||||
try
|
||||
{
|
||||
return $conn->query( $sql );
|
||||
}
|
||||
catch( PDOException $e ){
|
||||
catch( PDOException $e )
|
||||
{
|
||||
var_dump( $e );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
private function fetch( $stmt ){
|
||||
private function fetch( $stmt )
|
||||
{
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
private function prepare( $conn, $sql ){
|
||||
try{
|
||||
private function prepare( $conn, $sql )
|
||||
{
|
||||
try
|
||||
{
|
||||
$stmt = $conn->prepare( $sql );
|
||||
if( $stmt === false ){
|
||||
if( $stmt === false )
|
||||
{
|
||||
die( "Failed to prepare\n");
|
||||
}
|
||||
return $stmt;
|
||||
}
|
||||
catch( PDOException $e ){
|
||||
catch( PDOException $e )
|
||||
{
|
||||
var_dump( $e );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
private function execute( $stmt ){
|
||||
private function execute( $stmt )
|
||||
{
|
||||
$ret = $stmt->execute();
|
||||
if( $ret === false ){
|
||||
if( $ret === false )
|
||||
{
|
||||
die( "Failed to execute\n" );
|
||||
}
|
||||
}
|
||||
|
||||
private function bindParam( $stmt, $index, $value, $type ){
|
||||
private function bindParam( $stmt, $index, $value, $type )
|
||||
{
|
||||
$ret = $stmt->bindParam( $index, $value, $type );
|
||||
if ( $ret === false){
|
||||
if ( $ret === false)
|
||||
{
|
||||
die( "Faild to bind\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,35 +2,45 @@
|
|||
|
||||
namespace SqlsrvPerfTest;
|
||||
|
||||
class SqlsrvUtil{
|
||||
class SqlsrvUtil
|
||||
{
|
||||
|
||||
public static function connect(){
|
||||
public static $loopsPerCRUDIter = 100;
|
||||
|
||||
public static function connect()
|
||||
{
|
||||
require dirname(__FILE__).DIRECTORY_SEPARATOR.'connect.php';
|
||||
$options = array( "Database"=>$database, "UID"=>$uid, "PWD"=>$pwd, "ConnectionPooling"=>$pooling, "MultipleActiveResultSets"=>$mars );
|
||||
$conn = sqlsrv_connect( $server, $options );
|
||||
if ( $conn === false ){
|
||||
if ( $conn === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
return $conn;
|
||||
}
|
||||
|
||||
public static function disconnect( $conn ){
|
||||
if ( $conn === false || $conn === null ){
|
||||
public static function disconnect( $conn )
|
||||
{
|
||||
if ( $conn === false || $conn === null )
|
||||
{
|
||||
die( print_r( "Invalid connection resource\n"));
|
||||
}
|
||||
$ret = sqlsrv_close( $conn );
|
||||
if ( $ret === false ){
|
||||
if ( $ret === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
}
|
||||
|
||||
public static function selectVersion( $conn ){
|
||||
public static function selectVersion( $conn )
|
||||
{
|
||||
$sql = "SELECT @@Version";
|
||||
$stmt = self::query( $conn, $sql );
|
||||
return self::fetchArray( $stmt );
|
||||
}
|
||||
|
||||
public static function createDbTableProc( $conn, $databaseName, $tableName, $procName ){
|
||||
public static function createDbTableProc( $conn, $databaseName, $tableName, $procName )
|
||||
{
|
||||
$tableParams = "id INTEGER, name VARCHAR(32), value INTEGER, start_date DATE, time_added TIMESTAMP, set_time TIME(7)";
|
||||
$procParams = "@id INTEGER, @name VARCHAR(32)";
|
||||
$procTextBase = "SET NOCOUNT ON; SELECT id, name, value FROM $databaseName.$tableName WHERE id = @id AND name = @name";
|
||||
|
@ -40,7 +50,8 @@ class SqlsrvUtil{
|
|||
self::createStoredProc( $conn, $procName, $procParams, $procTextBase );
|
||||
}
|
||||
|
||||
public static function generateInsertValues(){
|
||||
public static function generateInsertValues()
|
||||
{
|
||||
$vcharVal = "test string";
|
||||
$nvcharVal = "wstring";
|
||||
$intVal = 3;
|
||||
|
@ -55,7 +66,8 @@ class SqlsrvUtil{
|
|||
return $values;
|
||||
}
|
||||
|
||||
public static function generateUpdateValues(){
|
||||
public static function generateUpdateValues()
|
||||
{
|
||||
$vcharVal = "test string updated";
|
||||
$nvcharVal = "wstring updated";
|
||||
$intVal = 5;
|
||||
|
@ -70,7 +82,8 @@ class SqlsrvUtil{
|
|||
return $updatedValues;
|
||||
}
|
||||
|
||||
public static function generateUpdateParams(){
|
||||
public static function generateUpdateParams()
|
||||
{
|
||||
$fieldNames = array(
|
||||
"vstring",
|
||||
"nvstring",
|
||||
|
@ -84,33 +97,38 @@ class SqlsrvUtil{
|
|||
"dtoffset");
|
||||
|
||||
$params = "";
|
||||
foreach( $fieldNames as $fieldName ){
|
||||
foreach( $fieldNames as $fieldName )
|
||||
{
|
||||
$params = $params.$fieldName."=?,";
|
||||
}
|
||||
$params = rtrim($params,", ");
|
||||
return $params;
|
||||
}
|
||||
|
||||
public static function insertWithPrepare( $conn, $tableName, $values ){
|
||||
public static function insertWithPrepare( $conn, $tableName, $values )
|
||||
{
|
||||
$sql = "INSERT INTO $tableName VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = self::prepare( $conn, $sql, $values );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
public static function updateWithPrepare( $conn, $tableName, $updateValues, $params ){
|
||||
public static function updateWithPrepare( $conn, $tableName, $updateValues, $params )
|
||||
{
|
||||
$sql = "UPDATE $tableName SET ".$params;
|
||||
$stmt = self::prepare( $conn, $sql, $updateValues );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
public static function fetchWithPrepare( $conn, $tableName ){
|
||||
public static function fetchWithPrepare( $conn, $tableName )
|
||||
{
|
||||
$sql = "SELECT * FROM $tableName";
|
||||
$stmt = self::prepare( $conn, $sql, array());
|
||||
self::execute( $stmt );
|
||||
while( $row = self::fetchArray( $stmt ) ) {}
|
||||
}
|
||||
|
||||
public static function createCRUDTable( $conn, $tableName ){
|
||||
public static function createCRUDTable( $conn, $tableName )
|
||||
{
|
||||
$fields = array(
|
||||
"vstring" => "VARCHAR(64)",
|
||||
"nvstring" => "NVARCHAR(64)",
|
||||
|
@ -123,108 +141,131 @@ class SqlsrvUtil{
|
|||
"vbin" => "VARBINARY",
|
||||
"dtoffset" => "DATETIMEOFFSET");
|
||||
$params = "";
|
||||
foreach( $fields as $fieldName => $type ){
|
||||
foreach( $fields as $fieldName => $type )
|
||||
{
|
||||
$params .= $fieldName." ".$type.",";
|
||||
}
|
||||
$params = rtrim($params,", ");
|
||||
self::createTable( $conn, $tableName, $params );
|
||||
}
|
||||
|
||||
public static function query( $conn, $sql ){
|
||||
public static function query( $conn, $sql )
|
||||
{
|
||||
$stmt = sqlsrv_query( $conn, $sql );
|
||||
if( $stmt === false ){
|
||||
if( $stmt === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
public static function fetch( $stmt ){
|
||||
public static function fetch( $stmt )
|
||||
{
|
||||
$ret = sqlsrv_fetch( $stmt );
|
||||
if( $ret === false ){
|
||||
if( $ret === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function fetchArray( $stmt ){
|
||||
public static function fetchArray( $stmt )
|
||||
{
|
||||
$row = sqlsrv_fetch_array( $stmt );
|
||||
if ( $row === false ){
|
||||
if ( $row === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
public static function getField( $stmt, $index ){
|
||||
public static function getField( $stmt, $index )
|
||||
{
|
||||
return sqlsrv_get_field( $stmt, $index );
|
||||
}
|
||||
|
||||
private function createDatabase( $conn, $dbName ){
|
||||
private function createDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "CREATE DATABASE $dbName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
public static function dropDatabase( $conn, $dbName ){
|
||||
public static function dropDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "USE MASTER;DROP DATABASE $dbName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
public static function createTable( $conn, $tableName, $params ){
|
||||
public static function createTable( $conn, $tableName, $params )
|
||||
{
|
||||
$sql = "CREATE TABLE $tableName ($params)";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
public static function dropTable( $conn, $tableName ){
|
||||
public static function dropTable( $conn, $tableName )
|
||||
{
|
||||
$sql = "DROP TABLE $tableName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
private function useDatabase( $conn, $dbName ){
|
||||
private function useDatabase( $conn, $dbName )
|
||||
{
|
||||
$sql = "USE $dbName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
private function createStoredProc( $conn, $procName, $params, $text ){
|
||||
private function createStoredProc( $conn, $procName, $params, $text )
|
||||
{
|
||||
$sql = "CREATE PROCEDURE $procName $params AS $text";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
private function dropStoredProc( $conn, $procName ){
|
||||
private function dropStoredProc( $conn, $procName )
|
||||
{
|
||||
$sql = "DROP PROCEDURE $procName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
private function insert( $conn, $tableName, $values ){
|
||||
private function insert( $conn, $tableName, $values )
|
||||
{
|
||||
$sql = "INSERT INTO $tableName values ($values)";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
private function update( $conn, $tableName, $params, $condition ){
|
||||
private function update( $conn, $tableName, $params, $condition )
|
||||
{
|
||||
$sql = "UPDATE $tableName SET $params WHERE $condition";
|
||||
self::query( $sql );
|
||||
}
|
||||
|
||||
public function delete( $conn, $tableName){
|
||||
public function delete( $conn, $tableName)
|
||||
{
|
||||
$sql = "DELETE TOP (1) FROM $tableName";
|
||||
self::query( $conn, $sql );
|
||||
}
|
||||
|
||||
public function deleteWithPrepare( $conn, $tableName ){
|
||||
public function deleteWithPrepare( $conn, $tableName )
|
||||
{
|
||||
$sql = "DELETE TOP (1) FROM $tableName";
|
||||
$stmt = self::prepare( $conn, $sql, array() );
|
||||
self::execute( $stmt );
|
||||
}
|
||||
|
||||
private function prepare( $conn, $sql, $params ){
|
||||
private function prepare( $conn, $sql, $params )
|
||||
{
|
||||
$stmt = sqlsrv_prepare( $conn, $sql, $params );
|
||||
if( $stmt === false ){
|
||||
if( $stmt === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
public function execute( $stmt ){
|
||||
public function execute( $stmt )
|
||||
{
|
||||
$ret = sqlsrv_execute( $stmt );
|
||||
if ( $ret === false ){
|
||||
if ( $ret === false )
|
||||
{
|
||||
die( print_r( sqlsrv_errors(), true));
|
||||
}
|
||||
}
|
||||
|
|
6
test/Performance/lib/result_db.php
Normal file
6
test/Performance/lib/result_db.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$server = 'server';
|
||||
$database = 'testdb';
|
||||
$uid = 'usr';
|
||||
$pwd = 'pwd';
|
||||
?>
|
|
@ -1,8 +1,9 @@
|
|||
--The script can be run to read the results. You can filter out the results ran on a certain date by changing the date at the end.
|
||||
DECLARE @convByteToMegabyte int = 1048576
|
||||
select t1.ResultId, Test, Client, Server, Driver, Duration, Memory, Success, Team, StartTime from
|
||||
(
|
||||
select pr.ResultId, pr.Success, pt.TestName as Test, cl.HostName as Client, srv.HostName as Server,
|
||||
tm.TeamName as Team, st.value as Driver, bi.value as Duration, bi2.value as Memory, dt.value as StartTime from
|
||||
tm.TeamName as Team, st.value as Driver, bi.value as Duration, CAST(bi2.value AS decimal(10,2))/@convByteToMegabyte as Memory, dt.value as StartTime from
|
||||
KeyValueTableBigInt bi,
|
||||
KeyValueTableBigInt bi2,
|
||||
KeyValueTableString st,
|
||||
|
@ -21,4 +22,3 @@ and pt.TestId = pr.TestId
|
|||
and tm.TeamId = pr.TeamId
|
||||
and srv.ServerId = pr.ServerId
|
||||
) t1 where StartTime like '%2017-06-23%'
|
||||
|
||||
|
|
|
@ -25,19 +25,18 @@ from time import strftime
|
|||
import hashlib
|
||||
|
||||
"""
|
||||
Paths to current benchmarks. These constants should be modified if there are any changes in folder structure of the project. "regular" folder contains the benchmarks that can be run for any iterations. "large" folder contains the benchmarks ( currently the benchmark that fetches large amount of data ) that take long time to run and meant to have less number of iterations than the regular ones.
|
||||
Paths to current benchmarks. These constants should be modified if there are any changes in folder structure of the project.
|
||||
"""
|
||||
|
||||
sqlsrv_regular_path = "benchmark" + os.sep + "sqlsrv" + os.sep + "regular"
|
||||
sqlsrv_large_path = "benchmark" + os.sep + "sqlsrv" + os.sep + "large"
|
||||
pdo_regular_path = "benchmark" + os.sep + "pdo_sqlsrv" + os.sep + "regular"
|
||||
pdo_large_path = "benchmark" + os.sep + "pdo_sqlsrv" + os.sep + "large"
|
||||
sqlsrv_path = "benchmark" + os.sep + "sqlsrv"
|
||||
pdo_path = "benchmark" + os.sep + "pdo_sqlsrv"
|
||||
|
||||
"""
|
||||
Path to the connect.php file that contains test database credentials. Note that, the benchmarks are run against this database and it is different from Result database.
|
||||
"""
|
||||
connect_file = "lib" + os.sep + "connect.php"
|
||||
connect_file_bak = connect_file + ".bak"
|
||||
result_file = "lib" + os.sep + "result_db.php"
|
||||
|
||||
"""
|
||||
Global data format used across the script
|
||||
|
@ -59,6 +58,7 @@ def validate_platform( platform_name ):
|
|||
, "WindowsServer2012"
|
||||
, "Ubuntu16"
|
||||
, "RedHat7"
|
||||
, "SUSE12"
|
||||
, "Sierra"]
|
||||
if platform_name not in platforms:
|
||||
print ( "Platform must be one of the following:" )
|
||||
|
@ -122,6 +122,7 @@ def get_test_name( name ):
|
|||
test_name_dict = {
|
||||
'SqlsrvConnectionBench': 'connection'
|
||||
, 'SqlsrvCreateDbTableProcBench': 'create'
|
||||
, 'SqlsrvCRUDBench': 'crud'
|
||||
, 'SqlsrvInsertBench': 'crud-create'
|
||||
, 'SqlsrvFetchBench': 'crud-retrieve'
|
||||
, 'SqlsrvUpdateBench': 'crud-update'
|
||||
|
@ -130,6 +131,7 @@ def get_test_name( name ):
|
|||
, 'SqlsrvSelectVersionBench': 'version'
|
||||
, 'PDOConnectionBench': 'connection'
|
||||
, 'PDOCreateDbTableProcBench': 'create'
|
||||
, 'PDOCRUDBench': 'crud'
|
||||
, 'PDOInsertBench': 'crud-create'
|
||||
, 'PDOFetchBench': 'crud-retrieve'
|
||||
, 'PDOUpdateBench': 'crud-update'
|
||||
|
@ -139,18 +141,17 @@ def get_test_name( name ):
|
|||
}
|
||||
return test_name_dict[ name ]
|
||||
|
||||
def get_run_command( path_to_tests, iterations, dump_file ):
|
||||
def get_run_command( path_to_tests, dump_file ):
|
||||
"""
|
||||
This module returns the command to run the tests
|
||||
Args:
|
||||
path_to_tests (str): The folder that contains the tests to be run
|
||||
iterations (str): Number of iterations
|
||||
dump_file (str): The name of the XML file to output the results
|
||||
Returns:
|
||||
The command to run the tests
|
||||
"""
|
||||
command = "vendor" + os.sep + "bin" + os.sep + "phpbench run {0} --iterations {1} --dump-file={2}"
|
||||
return command.format( path_to_tests, iterations, dump_file )
|
||||
command = "vendor" + os.sep + "bin" + os.sep + "phpbench run {0} --dump-file={1}"
|
||||
return command.format( path_to_tests, dump_file )
|
||||
|
||||
def get_id( conn, id_field, table, name_field, value ):
|
||||
"""
|
||||
|
@ -196,14 +197,14 @@ def get_id_no_quote( conn, id_field, table, name_field, value ):
|
|||
return id[0]
|
||||
return id
|
||||
|
||||
def get_test_database():
|
||||
def get_test_database( database_file ):
|
||||
"""
|
||||
This module reads test database details from connect.php and stores them into an instance of DB class
|
||||
Returns:
|
||||
A DB object that contains database credentials
|
||||
"""
|
||||
test_db = DB()
|
||||
for line in open( connect_file ):
|
||||
for line in open( database_file ):
|
||||
if "server" in line:
|
||||
test_db.server_name = line.split("=")[1].strip()[1:-2]
|
||||
elif "database" in line:
|
||||
|
@ -611,21 +612,23 @@ def disable_pooling():
|
|||
copyfile( odbcinst_bak, odbcinst )
|
||||
os.remove( odbcinst_bak )
|
||||
|
||||
def run_tests( iterations, iterations_large ):
|
||||
def run_tests( php_driver, test_name ):
|
||||
"""
|
||||
This module runs the tests using PHPBench
|
||||
Args:
|
||||
iterations (int): Number of iterations the tests in "regular" folder are run for
|
||||
iterations_large (int): Number of iterations the tests in "large" folder are run for
|
||||
php_driver (str): Name of the driver to be tested: sqlsrv, pdo_sqlsrv, or both
|
||||
test_name (str): File name of the test or all
|
||||
Returns:
|
||||
N/A
|
||||
"""
|
||||
print("Running the tests...")
|
||||
call( get_run_command( sqlsrv_regular_path, iterations, "sqlsrv-regular.xml" ), shell=True )
|
||||
call( get_run_command( sqlsrv_large_path, iterations_large, "sqlsrv-large.xml" ), shell=True )
|
||||
|
||||
call( get_run_command( pdo_regular_path, iterations, "pdo_sqlsrv-regular.xml" ), shell=True )
|
||||
call( get_run_command( pdo_large_path, iterations_large, "pdo_sqlsrv-large.xml" ), shell=True )
|
||||
add_to_path = ''
|
||||
if test_name != 'all':
|
||||
add_to_path = os.sep + test_name
|
||||
if php_driver == 'sqlsrv' or php_driver == 'both':
|
||||
call( get_run_command( sqlsrv_path + add_to_path, "sqlsrv-results.xml" ), shell=True )
|
||||
if php_driver == 'pdo_sqlsrv' or php_driver == 'both':
|
||||
call( get_run_command( pdo_path + add_to_path, "pdo_sqlsrv-results.xml" ), shell=True )
|
||||
|
||||
def parse_results( dump_file ):
|
||||
"""
|
||||
|
@ -656,6 +659,7 @@ def parse_results( dump_file ):
|
|||
# If the bechmark was run successfully, parse the results. This is where you would add code to parse more details about the benchmark.
|
||||
else:
|
||||
xml_result.success = 1
|
||||
# convert microseconds to seconds
|
||||
xml_result.duration = int( round( int( benchmark[0][0].find( 'stats' ).get( 'sum' )) / 1000000 ))
|
||||
iterations = benchmark[0][0].findall( 'iteration' )
|
||||
xml_result.iterations = len( iterations )
|
||||
|
@ -685,6 +689,11 @@ def parse_and_store_results( dump_file, test_db, result_db, platform, driver, st
|
|||
Returns:
|
||||
N/A
|
||||
"""
|
||||
# Check if the xml file actually exist
|
||||
if not os.path.exists(dump_file):
|
||||
print(dump_file + " does not exist")
|
||||
return
|
||||
|
||||
# Connect to the Result Database
|
||||
conn = connect( result_db )
|
||||
|
||||
|
@ -742,20 +751,14 @@ def parse_and_store_results_all( test_db, result_db, platform, start_time, mars,
|
|||
|
||||
"""
|
||||
print("Parsing and storing the results...")
|
||||
parse_and_store_results( "sqlsrv-regular.xml", test_db, result_db, platform, "sqlsrv", start_time, mars, pooling )
|
||||
parse_and_store_results( "sqlsrv-large.xml", test_db, result_db, platform, "sqlsrv", start_time, mars, pooling )
|
||||
parse_and_store_results( "pdo_sqlsrv-regular.xml", test_db, result_db, platform, "pdo_sqlsrv", start_time, mars, pooling )
|
||||
parse_and_store_results( "pdo_sqlsrv-large.xml", test_db, result_db, platform, "pdo_sqlsrv", start_time, mars, pooling )
|
||||
parse_and_store_results( "sqlsrv-results.xml", test_db, result_db, platform, "sqlsrv", start_time, mars, pooling )
|
||||
parse_and_store_results( "pdo_sqlsrv-results.xml", test_db, result_db, platform, "pdo_sqlsrv", start_time, mars, pooling )
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument( '-platform', '--PLATFORM', required=True, help='The name of the platform the tests run on' )
|
||||
parser.add_argument( '-iterations', '--ITERATIONS', required=True, help='Number of iterations for regular tests', type=int )
|
||||
parser.add_argument( '-iterations-large', '--ITERATIONS_LARGE', required=True, help='Number of iterations for regular tests', type=int )
|
||||
parser.add_argument( '-result-server', '--RESULT_SERVER', required=True, help='IP address of the Result Server' )
|
||||
parser.add_argument( '-result-db', '--RESULT_DB', required=True, help='Name of the Result Database' )
|
||||
parser.add_argument( '-result-uid', '--RESULT_UID', required=True, help='Username to connect to the Result Database' )
|
||||
parser.add_argument( '-result-pwd', '--RESULT_PWD', required=True, help='Password to connect to the Result Database' )
|
||||
parser.add_argument( '-php-driver', '--PHP_DRIVER', default='both', help='Name of the PHP driver: sqlsrv, pdo_sqlsrv or both')
|
||||
parser.add_argument( '-testname', '--TESTNAME', default='all', help='File name for only one test or all' )
|
||||
args = parser.parse_args()
|
||||
|
||||
# Start time is recorded only in the beginning of this script execution. So it is not benchmark specific.
|
||||
|
@ -764,12 +767,12 @@ if __name__ == '__main__':
|
|||
print( "Start time: " + start_time )
|
||||
|
||||
validate_platform( args.PLATFORM )
|
||||
result_db = DB( args.RESULT_SERVER, args.RESULT_DB, args.RESULT_UID, args.RESULT_PWD )
|
||||
test_db = get_test_database()
|
||||
result_db = get_test_database( result_file )
|
||||
test_db = get_test_database( connect_file )
|
||||
|
||||
print("Running the tests with default settings...")
|
||||
|
||||
run_tests( args.ITERATIONS, args.ITERATIONS_LARGE )
|
||||
run_tests( args.PHP_DRIVER, args.TESTNAME )
|
||||
parse_and_store_results_all( test_db, result_db, args.PLATFORM, start_time, 0, 0 )
|
||||
"""
|
||||
The following lines are commented out, because it already takes a long time to run the tests with the default settings.
|
||||
|
@ -777,14 +780,14 @@ if __name__ == '__main__':
|
|||
|
||||
print("Running the tests with MARS ON...")
|
||||
enable_mars()
|
||||
run_tests( args.ITERATIONS, args.ITERATIONS_LARGE )
|
||||
run_tests( args.PHP_DRIVER, args.TESTNAME )
|
||||
parse_and_store_results_all( test_db, result_db, args.PLATFORM, start_time, 1, 0 )
|
||||
disable_mars()
|
||||
|
||||
|
||||
print("Running the tests with Pooling ON...")
|
||||
enable_pooling()
|
||||
run_tests( args.ITERATIONS, args.ITERATIONS_LARGE )
|
||||
run_tests( args.PHP_DRIVER, args.TESTNAME )
|
||||
parse_and_store_results_all( test_db, result_db, args.PLATFORM, start_time, 0, 1 )
|
||||
disable_pooling()
|
||||
"""
|
||||
|
|
|
@ -1,140 +1,212 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
if [[ ("$1" = "Ubuntu16" || "$1" = "RedHat7" || "$1" = "Sierra") ]]; then
|
||||
|
||||
if [[ ("$1" = "Ubuntu16" || "$1" = "RedHat7" || "$1" = "SUSE12" || "$1" = "Sierra") ]]; then
|
||||
PLATFORM=$1
|
||||
else
|
||||
echo "First argument must be one of Ubuntu16, RedHat7, Sierra. Exiting..."
|
||||
echo "1st argument must be one of Ubuntu16, RedHat7, SUSE12, Sierra. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$2" != 7.*.* ]]; then
|
||||
echo "Second argument must be PHP version in format of 7.x.x.Exiting..."
|
||||
echo "2nd argument must be PHP version in format of 7.x.y. Exiting..."
|
||||
exit
|
||||
else
|
||||
PHP_VERSION=$2
|
||||
fi
|
||||
|
||||
if [[ ("$3" != "nts" && "$3" != "ts") ]]; then
|
||||
echo "Thrid argument must be either nts or ts. Exiting..."
|
||||
echo "3rd argument must be either nts or ts. Exiting..."
|
||||
exit 1
|
||||
else
|
||||
PHP_THREAD=$3
|
||||
fi
|
||||
if [[ (! -d "$4") || (! -d $4/sqlsrv) || (! -d $4/pdo_sqlsrv) || (! -d $4/shared) ]]; then
|
||||
echo "Fourth argument must be path to source folder.Path not found.Exiting..."
|
||||
|
||||
if [[ (! -f "$4") || (! -f "$5") ]]; then
|
||||
echo "4th and 5th argument must be paths to sqlsrv and pdo drivers. Exiting..."
|
||||
exit 1
|
||||
else
|
||||
DRIVER_SOURCE_PATH=$4
|
||||
SQLSRV_DRIVER=$4
|
||||
PDO_DRIVER=$5
|
||||
fi
|
||||
rm -rf env_setup.log
|
||||
touch env_setup.log
|
||||
|
||||
if [ $PLATFORM = "Ubuntu16" ]; then
|
||||
echo "Update..."
|
||||
yes | sudo dpkg --configure -a >> env_setup.log 2>&1
|
||||
yes | sudo apt-get update >> env_setup.log 2>&1
|
||||
echo "Installing git, zip, curl, libxml, autoconf, openssl, python3, pip3..."
|
||||
yes | sudo apt-get install git zip curl autoconf libxml2-dev libssl-dev pkg-config python3 python3-pip >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing MSODBCSQL..."
|
||||
printf "Update..."
|
||||
yes | sudo dpkg --configure -a > env_setup.log
|
||||
yes | sudo apt-get update >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing git, zip, curl, libxml, autoconf, openssl, python3, pip3..."
|
||||
yes | sudo apt-get install git zip curl autoconf libxml2-dev libssl-dev pkg-config python3 python3-pip >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing MSODBCSQL..."
|
||||
curl -s https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
|
||||
curl -s https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
|
||||
yes | sudo apt-get update >> env_setup.log 2>&1
|
||||
yes | sudo ACCEPT_EULA=Y apt-get install msodbcsql >> env_setup.log 2>&1
|
||||
yes | sudo apt-get install -qq unixodbc-dev >> env_setup.log 2>&1
|
||||
echo "Installing pyodbc"
|
||||
pip3 install --upgrade pip >> env_setup.log 2>&1
|
||||
pip3 install pyodbc >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
yes | sudo apt-get update >> env_setup.log
|
||||
yes | sudo ACCEPT_EULA=Y apt-get install msodbcsql >> env_setup.log
|
||||
yes | sudo apt-get install -qq unixodbc-dev >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing pyodbc"
|
||||
pip3 install --upgrade pip >> env_setup.log
|
||||
pip3 install pyodbc >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
elif [ $PLATFORM = "RedHat7" ]; then
|
||||
echo "Update..."
|
||||
yes | sudo yum update >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Enabling EPEL repo..."
|
||||
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm >> env_setup.log 2>&1
|
||||
yes | sudo yum install epel-release-latest-7.noarch.rpm >> env_setup.log 2>&1 || true
|
||||
echo "OK"
|
||||
echo "Installing python34-setuptools..."
|
||||
yes | sudo yum install python34-setuptools -y >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing gcc, git, zip libxml, openssl, EPEL, python3, pip3..."
|
||||
yes | sudo yum install -y gcc-c++ libxml2-devel git zip openssl-devel python34 python34-devel python34-pip >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing MSODBCSQL..."
|
||||
printf "Update..."
|
||||
yes | sudo yum update >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Enabling EPEL repo..."
|
||||
# pipe non-error to log file (wget and yum install reports error when there's nothing to do)
|
||||
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm >> env_setup.log
|
||||
yes | sudo yum install epel-release-latest-7.noarch.rpm >> env_setup.log || true
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing python34-setuptools..."
|
||||
yes | sudo yum install python34-setuptools -y >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing gcc, git, zip libxml, openssl, EPEL, python3, pip3..."
|
||||
yes | sudo yum install -y gcc-c++ libxml2-devel git zip openssl-devel python34 python34-devel python34-pip >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing MSODBCSQL..."
|
||||
curl -s https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssql-release.repo
|
||||
(yes | sudo ACCEPT_EULA=Y yum install -y msodbcsql >> env_setup.log 2>&1)
|
||||
(yes | sudo yum install -y unixODBC-devel autoconf >> env_setup.log 2>&1)
|
||||
echo "OK"
|
||||
echo "Installing pyodbc"
|
||||
pip3 install --upgrade pip >> env_setup.log 2>&1
|
||||
pip3 install pyodbc >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
(yes | sudo ACCEPT_EULA=Y yum install -y msodbcsql >> env_setup.log)
|
||||
(yes | sudo yum install -y unixODBC-devel autoconf >> env_setup.log)
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing pyodbc"
|
||||
pip3 install --upgrade pip >> env_setup.log
|
||||
pip3 install pyodbc >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
elif [ $PLATFORM = "SUSE12" ]; then
|
||||
printf "Update..."
|
||||
sudo zypper refresh >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing autoconf, gcc, g++, git, zip, libxml, openssl, python3, pip3..."
|
||||
sudo zypper -n install autoconf gcc gcc-c++ libxml2-devel git zip libopenssl-devel python3-devel python3-pip python3-setuptools >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing MSODBCSQL..."
|
||||
zypper -n ar https://packages.microsoft.com/config/sles/12/prod.repo
|
||||
zypper --gpg-auto-import-keys refresh
|
||||
ACCEPT_EULA=Y zypper -n install msodbcsql >> env_setup.log
|
||||
ACCEPT_EULA=Y zypper -n install mssql-tools >> env_setup.log
|
||||
zypper -n install unixODBC-devel >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing pyodbc"
|
||||
pip3 install --upgrade pip >> env_setup.log
|
||||
pip3 install pyodbc >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
elif [ $PLATFORM = "Sierra" ]; then
|
||||
echo "Installing homebrew..."
|
||||
yes | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing wget..."
|
||||
brew install wget >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing svn..."
|
||||
brew install svn >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing openssl..."
|
||||
brew install pkg-config >> env_setup.log 2>&1
|
||||
brew install openssl >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing python3..."
|
||||
brew install python3 >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing MSODBCSQL..."
|
||||
brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-msodbcsql >> env_setup.log 2>&1
|
||||
brew update >> env_setup.log 2>&1
|
||||
yes | ACCEPT_EULA=Y brew install msodbcsql >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
yes | brew install autoconf >> env_setup.log 2>&1
|
||||
echo "Installing pyodbc..."
|
||||
pip3 install pyodbc >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
printf "Installing homebrew..."
|
||||
yes | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing wget..."
|
||||
brew install wget >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing svn..."
|
||||
brew install svn >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing openssl..."
|
||||
brew install pkg-config >> env_setup.log
|
||||
brew install openssl >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing python3..."
|
||||
brew install python3 >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing MSODBCSQL..."
|
||||
brew tap microsoft/msodbcsql https://github.com/Microsoft/homebrew-msodbcsql >> env_setup.log
|
||||
brew update >> env_setup.log
|
||||
yes | ACCEPT_EULA=Y brew install --no-sandbox msodbcsql >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
yes | brew install autoconf >> env_setup.log
|
||||
printf "Installing pyodbc..."
|
||||
pip3 install pyodbc >> env_setup.log
|
||||
printf "done\n"
|
||||
fi
|
||||
echo "Downloading PHP-$PHP_VERSION source tarball..."
|
||||
wget http://ca1.php.net/get/php-$PHP_VERSION.tar.gz/from/this/mirror -O php-$PHP_VERSION.tar.gz >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Extracting PHP source tarball..."
|
||||
|
||||
printf "Downloading PHP-$PHP_VERSION source tarball..."
|
||||
# pipe non-error to log file
|
||||
wget http://ca1.php.net/get/php-$PHP_VERSION.tar.gz/from/this/mirror -O php-$PHP_VERSION.tar.gz >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Extracting PHP source tarball..."
|
||||
rm -rf php-$PHP_VERSION
|
||||
tar -xf php-$PHP_VERSION.tar.gz
|
||||
echo "OK"
|
||||
cd php-$PHP_VERSION
|
||||
mkdir ext/sqlsrv ext/pdo_sqlsrv
|
||||
cp -r $DRIVER_SOURCE_PATH/sqlsrv/* $DRIVER_SOURCE_PATH/shared ext/sqlsrv
|
||||
cp -r $DRIVER_SOURCE_PATH/pdo_sqlsrv/* $DRIVER_SOURCE_PATH/shared ext/pdo_sqlsrv
|
||||
./buildconf --force >> ../env_setup.log 2>&1
|
||||
CONFIG_OPTIONS="--enable-cli --enable-cgi --enable-pdo --enable-sqlsrv=shared --with-pdo_sqlsrv=shared --with-odbcver=0x0380 --with-zlib --enable-mbstring --prefix=/usr/local"
|
||||
printf "done\n"
|
||||
|
||||
phpDir=php-$PHP_VERSION
|
||||
cd $phpDir
|
||||
|
||||
printf "Configuring PHP..."
|
||||
./buildconf --force >> ../env_setup.log
|
||||
CONFIG_OPTIONS="--enable-cli --enable-cgi --with-zlib --enable-mbstring --prefix=/usr/local"
|
||||
[ "${PHP_THREAD}" == "ts" ] && CONFIG_OPTIONS=${CONFIG_OPTIONS}" --enable-maintainer-zts"
|
||||
if [ $PLATFORM = "Sierra" ]; then
|
||||
CONFIG_OPTIONS=$CONFIG_OPTIONS" --with-openssl=/usr/local/opt/openssl/"
|
||||
else
|
||||
CONFIG_OPTIONS=$CONFIG_OPTIONS" --with-openssl"
|
||||
fi
|
||||
echo "Configuring PHP..."
|
||||
(./configure $CONFIG_OPTIONS >> ../env_setup.log 2>&1)
|
||||
echo "OK"
|
||||
echo "Compiling PHP and the drivers..."
|
||||
make >> ../env_setup.log 2>&1
|
||||
echo "OK"
|
||||
sudo make install >> ../env_setup.log 2>&1
|
||||
#pipe non-error to log file
|
||||
(./configure $CONFIG_OPTIONS >> ../env_setup.log)
|
||||
printf "done\n"
|
||||
|
||||
printf "Compiling and installing PHP..."
|
||||
make >> ../env_setup.log
|
||||
sudo make install >> ../env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
# check PHP version
|
||||
/usr/local/bin/php -v
|
||||
|
||||
printf "Setting up drivers..."
|
||||
phpExtDir=`/usr/local/bin/php-config --extension-dir`
|
||||
cp php.ini-production php.ini
|
||||
echo "extension=sqlsrv.so" >> php.ini
|
||||
echo "extension=pdo_sqlsrv.so" >> php.ini
|
||||
driverName=$(basename $SQLSRV_DRIVER)
|
||||
echo "extension=$driverName" >> php.ini
|
||||
sudo cp -r $SQLSRV_DRIVER $phpExtDir/$driverName
|
||||
sudo chmod a+r $phpExtDir/$driverName
|
||||
|
||||
driverName=$(basename $PDO_DRIVER)
|
||||
echo "extension=$driverName" >> php.ini
|
||||
sudo cp -r $PDO_DRIVER $phpExtDir/$driverName
|
||||
sudo chmod a+r $phpExtDir/$driverName
|
||||
|
||||
sudo cp php.ini /usr/local/lib
|
||||
printf "done\n"
|
||||
|
||||
# check drivers
|
||||
/usr/local/bin/php --ri sqlsrv
|
||||
/usr/local/bin/php --ri pdo_sqlsrv
|
||||
|
||||
printf "Installing Composer..."
|
||||
cd ..
|
||||
php -v
|
||||
php --ri sqlsrv
|
||||
php --ri pdo_sqlsrv
|
||||
echo "Installing Composer..."
|
||||
wget https://getcomposer.org/installer -O composer-setup.php >> env_setup.log 2>&1
|
||||
php composer-setup.php >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Installing PHPBench..."
|
||||
php composer.phar install >> env_setup.log 2>&1
|
||||
echo "OK"
|
||||
echo "Cleaning up..."
|
||||
rm -rf php-$PHP_VERSION* compser-setup.php
|
||||
echo "OK"
|
||||
# pipe non-error to log file
|
||||
wget https://getcomposer.org/installer -O composer-setup.php >> env_setup.log
|
||||
/usr/local/bin/php composer-setup.php >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Installing PHPBench...\n"
|
||||
/usr/local/bin/php composer.phar install >> env_setup.log
|
||||
printf "done\n"
|
||||
|
||||
printf "Cleaning up..."
|
||||
rm -rf $phpDir compser-setup.php
|
||||
printf "done\n"
|
||||
|
||||
echo "Setup completed!"
|
|
@ -1,28 +1,13 @@
|
|||
Param(
|
||||
[Parameter(Mandatory=$True,Position=1)]
|
||||
[string]$PHP_VERSION,
|
||||
[string]$PHP_ZIP,
|
||||
[Parameter(Mandatory=$True,Position=2)]
|
||||
[string]$PHP_THREAD,
|
||||
[string]$SQLSRV_DRIVER,
|
||||
[Parameter(Mandatory=$True,Position=3)]
|
||||
[string]$DRIVER_SOURCE_PATH,
|
||||
[Parameter(Mandatory=$True,Position=4)]
|
||||
[string]$ARCH
|
||||
[string]$PDO_DRIVER
|
||||
)
|
||||
|
||||
IF($ARCH -ne "x64" -And $ARCH -ne "x86"){
|
||||
Write-Host "ARCH must either x64 or x86"
|
||||
Break
|
||||
}
|
||||
|
||||
IF($PHP_THREAD -ne "nts" -And $PHP_THREAD -ne "ts"){
|
||||
Write-Host "PHP_THREAD must either nts or ts"
|
||||
Break
|
||||
}
|
||||
|
||||
IF($PHP_VERSION -NotMatch "7.[0-1].[0-9]"){
|
||||
Write-Host "PHP_VERSION must be in format of 7.x.x"
|
||||
Break
|
||||
}
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$startingDir=$pwd.Path
|
||||
$tempFolder=Join-Path $startingDir "temp"
|
||||
|
@ -30,17 +15,12 @@ $tempFolder=Join-Path $startingDir "temp"
|
|||
Remove-Item temp -Recurse -Force -ErrorAction Ignore
|
||||
New-Item -ItemType directory -Path temp
|
||||
|
||||
(New-Object System.Net.WebClient).DownloadFile("http://windows.php.net/downloads/releases/sha1sum.txt","$tempFolder\sha1sum.txt")
|
||||
$PHP70_LATEST_VERSION=type $tempFolder\sha1sum.txt | where { $_ -match "php-(7.0\.\d+)-src" } | foreach { $matches[1] }
|
||||
$PHP71_LATEST_VERSION=type $tempFolder\sha1sum.txt | where { $_ -match "php-(7.1\.\d+)-src" } | foreach { $matches[1] }
|
||||
|
||||
$PHP_VERSION_MINOR=$PHP_VERSION.split(".")[1]
|
||||
|
||||
Write-Host "Installing chocolatey..."
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
Write-Host "Installing Git..."
|
||||
choco install -y git
|
||||
Set-Alias git 'C:\Program Files\Git\cmd\git.exe'
|
||||
|
||||
$gitDir = 'C:\Program Files\Git\cmd\git.exe'
|
||||
Write-Host "Installing Python3..."
|
||||
choco install -y python3
|
||||
RefreshEnv
|
||||
|
@ -54,63 +34,36 @@ msiexec /quiet /passive /qn /i $tempFolder\msodbcsql.msi IACCEPTMSODBCSQLLICENSE
|
|||
Write-Host "Installing 7-Zip..."
|
||||
choco install -y 7zip.install
|
||||
|
||||
Write-Host "Downloading PHP-SDK..."
|
||||
(New-Object System.Net.WebClient).DownloadFile('http://windows.php.net/downloads/php-sdk/php-sdk-binary-tools-20110915.zip', "$tempFolder\binary_tools.zip")
|
||||
Write-Host "Downloading PHP-$PHP_VERSION source..."
|
||||
IF($PHP_VERSION -eq $PHP70_LATEST_VERSION -Or $PHP_VERSION -eq $PHP71_LATEST_VERSION){
|
||||
(New-Object System.Net.WebClient).DownloadFile("http://windows.php.net/downloads/releases/php-$PHP_VERSION-src.zip", "$tempFolder\php-$PHP_VERSION-src.zip")
|
||||
}
|
||||
ELSE{
|
||||
(New-Object System.Net.WebClient).DownloadFile("http://windows.php.net/downloads/releases/archives/php-$PHP_VERSION-src.zip", "$tempFolder\php-$PHP_VERSION-src.zip")
|
||||
}
|
||||
Write-Host "Installing PHP..."
|
||||
$phpDir="C:\php"
|
||||
|
||||
Write-Host "Downloading Dependencies..."
|
||||
(New-Object System.Net.WebClient).DownloadFile("http://windows.php.net/downloads/php-sdk/deps-7.$PHP_VERSION_MINOR-vc14-$ARCH.7z", "$tempFolder\deps-7.$PHP_VERSION_MINOR-vc14-$ARCH.7z")
|
||||
# remove existing PHP and setup new one
|
||||
Remove-Item $phpDir -Recurse -ErrorAction Ignore
|
||||
New-Item -ItemType directory -Path $phpDir
|
||||
Expand-Archive $PHP_ZIP -DestinationPath $phpDir
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
Remove-Item C:\php-sdk -Recurse -Force -ErrorAction Ignore
|
||||
New-Item -ItemType directory -Path C:\php-sdk
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$tempFolder\binary_tools.zip", "C:\php-sdk")
|
||||
cd C:\php-sdk\
|
||||
bin\phpsdk_buildtree.bat phpdev
|
||||
New-Item -ItemType directory -Path .\phpdev\vc14
|
||||
Copy-Item .\phpdev\vc9\* phpdev\vc14\ -recurse
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$tempFolder\php-$PHP_VERSION-src.zip", "C:\php-sdk\phpdev\vc14\$ARCH\")
|
||||
7z.exe x $tempFolder\deps-7.$PHP_VERSION_MINOR-vc14-$ARCH.7z -oC:\php-sdk\phpdev\vc14\$ARCH\
|
||||
# copy drivers to extensions directory and rename to a standard nomenclature
|
||||
# for consistency with run-perf_tests.py
|
||||
Copy-Item $SQLSRV_DRIVER $phpDir\ext\php_sqlsrv.dll
|
||||
Copy-Item $PDO_DRIVER $phpDir\ext\php_pdo_sqlsrv.dll
|
||||
|
||||
bin\phpsdk_setvars.bat
|
||||
# setup driver
|
||||
Copy-Item $phpDir\php.ini-production $phpDir\php.ini
|
||||
Add-Content $phpDir\php.ini "extension=$phpDir\ext\php_openssl.dll"
|
||||
Add-Content $phpDir\php.ini "extension=$phpDir\ext\php_mbstring.dll"
|
||||
|
||||
cd C:\php-sdk\phpdev\vc14\$ARCH\php-$PHP_VERSION-src
|
||||
|
||||
New-Item -ItemType directory -Path .\ext\sqlsrv
|
||||
New-Item -ItemType directory -Path .\ext\pdo_sqlsrv
|
||||
Copy-Item $DRIVER_SOURCE_PATH\sqlsrv\* .\ext\sqlsrv\ -recurse
|
||||
Copy-Item $DRIVER_SOURCE_PATH\shared\ .\ext\sqlsrv\ -recurse
|
||||
Copy-Item $DRIVER_SOURCE_PATH\pdo_sqlsrv\* .\ext\pdo_sqlsrv\ -recurse
|
||||
Copy-Item $DRIVER_SOURCE_PATH\shared\ .\ext\pdo_sqlsrv\ -recurse
|
||||
|
||||
|
||||
$CONFIG_OPTIONS="--enable-cli --enable-cgi --enable-sqlsrv=shared --enable-pdo=shared --with-pdo-sqlsrv=shared --with-odbcver=0x0380 --enable-mbstring --with-openssl"
|
||||
if ($PHP_THREAD -ceq "nts") {
|
||||
$CONFIG_OPTIONS=$CONFIG_OPTIONS + " --disable-zts"
|
||||
}
|
||||
& $startingDir\compile_php.bat $ARCH $CONFIG_OPTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
Copy-Item php.ini-production php.ini
|
||||
Add-Content php.ini "extension=C:\php\ext\php_sqlsrv.dll"
|
||||
Add-Content php.ini "extension=C:\php\ext\php_pdo_sqlsrv.dll"
|
||||
Add-Content php.ini "extension=C:\php\ext\php_openssl.dll"
|
||||
Move-Item php.ini C:\Windows -force
|
||||
Copy-Item C:\php-sdk\phpdev\vc14\$ARCH\deps\bin\ssleay32.dll C:\Windows -force
|
||||
Copy-Item C:\php-sdk\phpdev\vc14\$ARCH\deps\bin\libeay32.dll C:\Windows -force
|
||||
Add-Content $phpDir\php.ini "extension=$phpDir\ext\php_sqlsrv.dll"
|
||||
Add-Content $phpDir\php.ini "extension=$phpDir\ext\php_pdo_sqlsrv.dll"
|
||||
|
||||
Move-Item $phpDir\php.ini C:\Windows -force
|
||||
Copy-Item $phpDir\ssleay32.dll C:\Windows -force
|
||||
Copy-Item $phpDir\libeay32.dll C:\Windows -force
|
||||
cd $startingDir
|
||||
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\php\", [System.EnvironmentVariableTarget]::Machine)
|
||||
$env:Path += ";C:\php\"
|
||||
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";" + $phpDir + ";" + $gitDir, [System.EnvironmentVariableTarget]::Machine)
|
||||
$env:Path = $env:Path + ";" + $phpDir + ";" + $gitDir
|
||||
RefreshEnv
|
||||
|
||||
# setup composer
|
||||
wget https://getcomposer.org/installer -O composer-setup.php
|
||||
php composer-setup.php
|
||||
php composer.phar install
|
||||
|
|
25
test/functional/pdo_sqlsrv/AE_Ksp.inc
Normal file
25
test/functional/pdo_sqlsrv/AE_Ksp.inc
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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';
|
||||
|
||||
?>
|
61
test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt
Normal file
61
test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt
Normal file
|
@ -0,0 +1,61 @@
|
|||
--TEST--
|
||||
Provide name in lastInsertId to retrieve the last sequence number
|
||||
--SKIPIF--
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
try{
|
||||
$database = "tempdb";
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName", $uid, $pwd);
|
||||
|
||||
// sequence is only supported in SQL server 2012 and up (or version 11 and up)
|
||||
// Output Done once the server version is found to be < 11
|
||||
$version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION));
|
||||
if ($version_arr[0] < 11) {
|
||||
echo "Done\n";
|
||||
}
|
||||
else {
|
||||
$tableName1 = GetTempTableName('tab1', false);
|
||||
$tableName2 = GetTempTableName('tab2', false);
|
||||
$sequenceName = 'sequence1';
|
||||
|
||||
$stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName");
|
||||
$sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)";
|
||||
$stmt = $conn->query($sql);
|
||||
$sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))";
|
||||
$stmt = $conn->query($sql);
|
||||
|
||||
$sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE";
|
||||
$stmt = $conn->query($sql);
|
||||
|
||||
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )");
|
||||
// return the last sequence number is sequence name is provided
|
||||
$lastSeq = $conn->lastInsertId($sequenceName);
|
||||
// defaults to $tableName2 -- because it returns the last inserted id value
|
||||
$lastRow = $conn->lastInsertId();
|
||||
|
||||
if ($lastSeq == 3 && $lastRow == 1) {
|
||||
echo "Done\n";
|
||||
}
|
||||
else {
|
||||
echo "sequence value or identity does not match as expected\n";
|
||||
}
|
||||
$stmt = $conn->query("DROP TABLE $tableName1");
|
||||
$stmt = $conn->query("DROP TABLE $tableName2");
|
||||
$stmt = $conn->query("DROP SEQUENCE $sequenceName");
|
||||
$stmt = null;
|
||||
}
|
||||
$conn = null;
|
||||
}
|
||||
catch (Exception $e){
|
||||
echo "Exception $e\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
64
test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt
Normal file
64
test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt
Normal file
|
@ -0,0 +1,64 @@
|
|||
--TEST--
|
||||
LastInsertId returns the last sequences operating on the same table
|
||||
--SKIPIF--
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
try{
|
||||
$database = "tempdb";
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName", $uid, $pwd);
|
||||
|
||||
// sequence is only supported in SQL server 2012 and up (or version 11 and up)
|
||||
// Output Done once the server version is found to be < 11
|
||||
$version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION));
|
||||
if ($version_arr[0] < 11) {
|
||||
echo "Done\n";
|
||||
}
|
||||
else {
|
||||
$tableName = GetTempTableName('tab', false);
|
||||
$sequence1 = 'sequence1';
|
||||
$sequence2 = 'sequenceNeg1';
|
||||
$stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1");
|
||||
$stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2");
|
||||
$sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)";
|
||||
$stmt = $conn->query($sql);
|
||||
$sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100";
|
||||
$stmt = $conn->query($sql);
|
||||
|
||||
$sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200";
|
||||
$stmt = $conn->query($sql);
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )");
|
||||
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )");
|
||||
// return the last sequence number of 'sequence1'
|
||||
$lastSeq1 = $conn->lastInsertId($sequence1);
|
||||
|
||||
// return the last sequence number of 'sequenceNeg1'
|
||||
$lastSeq2 = $conn->lastInsertId($sequence2);
|
||||
|
||||
// providing a table name in lastInsertId should return an empty string
|
||||
$lastSeq3 = $conn->lastInsertId($tableName);
|
||||
|
||||
if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") {
|
||||
echo "Done\n";
|
||||
}
|
||||
|
||||
$stmt = $conn->query("DROP TABLE $tableName");
|
||||
$stmt = $conn->query("DROP SEQUENCE $sequence1");
|
||||
$stmt = $conn->query("DROP SEQUENCE $sequence2");
|
||||
$stmt = null;
|
||||
}
|
||||
$conn = null;
|
||||
}
|
||||
catch (Exception $e){
|
||||
echo "Exception $e\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
54
test/functional/pdo_sqlsrv/pdo_connect_encrypted_ksp.phpt
Normal file
54
test/functional/pdo_sqlsrv/pdo_connect_encrypted_ksp.phpt
Normal file
|
@ -0,0 +1,54 @@
|
|||
--TEST--
|
||||
Fetch data from a prepopulated test table given a custom keystore provider
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
|
||||
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
|
||||
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
$tsql = "SELECT * FROM CustomKSPTestTable";
|
||||
$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";
|
||||
}
|
||||
|
||||
$stmt = null;
|
||||
$conn = null;
|
||||
|
||||
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
|
|
@ -0,0 +1,58 @@
|
|||
--TEST--
|
||||
Fetch encrypted data from a prepopulated test table given a custom keystore provider
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
$connectionInfo = "Database = $databaseName; ";
|
||||
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
|
||||
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
|
||||
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
|
||||
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
echo "Connected successfully with ColumnEncryption disabled and KSP specified.\n";
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
$tsql = "SELECT * FROM CustomKSPTestTable";
|
||||
$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" ;
|
||||
}
|
||||
|
||||
$stmt = null;
|
||||
$conn = null;
|
||||
|
||||
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
|
100
test/functional/pdo_sqlsrv/pdo_connect_encrypted_ksp_errors.phpt
Normal file
100
test/functional/pdo_sqlsrv/pdo_connect_encrypted_ksp_errors.phpt
Normal file
|
@ -0,0 +1,100 @@
|
|||
--TEST--
|
||||
Connect using a custom keystore provider with some required inputs missing
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
function connect( $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();
|
||||
|
||||
echo("Connecting... with column encryption\n");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
connect( $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; ";
|
||||
connect( $connectionInfo );
|
||||
|
||||
echo("\nConnecting... with an empty path\n");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
|
||||
$connectionInfo .= "CEKeystoreProvider = ; ";
|
||||
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
|
||||
connect( $connectionInfo );
|
||||
|
||||
echo("\nConnecting... without a path\n");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
|
||||
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key;";
|
||||
connect( $connectionInfo );
|
||||
|
||||
echo("\nConnecting... without a name\n");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
|
||||
$connectionInfo .= "CEKeystoreEncryptKey = $encrypt_key; ";
|
||||
connect( $connectionInfo );
|
||||
|
||||
echo("\nConnecting... without a key\n");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled; ";
|
||||
$connectionInfo .= "CEKeystoreProvider = $ksp_path; ";
|
||||
$connectionInfo .= "CEKeystoreName = $ksp_name; ";
|
||||
connect( $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; ";
|
||||
connect( $connectionInfo );
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
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 13 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
|
31
test/functional/pdo_sqlsrv/skipif_server_old.inc
Normal file
31
test/functional/pdo_sqlsrv/skipif_server_old.inc
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
if (!extension_loaded("pdo") || !extension_loaded('pdo_sqlsrv'))
|
||||
die("PDO driver cannot be loaded; skipping test.\n");
|
||||
|
||||
require_once( "MsSetup.inc" );
|
||||
$conn = new PDO("sqlsrv:server = $server;", $uid, $pwd );
|
||||
if( ! $conn )
|
||||
{
|
||||
echo ( "Error: could not connect during SKIPIF!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$attr = $conn->getAttribute(constant('PDO::ATTR_SERVER_VERSION'));
|
||||
$version = substr($attr, 0, 2);
|
||||
if ($version < 13)
|
||||
{
|
||||
// older than SQL Server 2016
|
||||
die( "skip - feature not supported in this version of SQL Server." );
|
||||
}
|
||||
|
||||
// check ODBC driver version
|
||||
$attr = $conn->getAttribute(constant('PDO::ATTR_CLIENT_VERSION'));
|
||||
$version = substr($attr['DriverVer'], 0, 2);
|
||||
if ($version < 13)
|
||||
{
|
||||
// older than ODBC 13
|
||||
die( "skip - feature not supported in this version of ODBC driver." );
|
||||
}
|
||||
}
|
||||
?>
|
122
test/functional/setup/build_ksp.py
Normal file
122
test/functional/setup/build_ksp.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/python3
|
||||
#########################################################################################
|
||||
#
|
||||
# Description: This script builds a custom keystore provider and compiles the app that
|
||||
# uses this KSP. Their names can be passed as arguments, but the outputs
|
||||
# are always
|
||||
# - myKSP.dll (myKSPx64.dll) / myKSP.so
|
||||
# - ksp_app.exe / ksp_app
|
||||
#
|
||||
# Requirement:
|
||||
# python 3.x
|
||||
# myKSP.c (or any equivalent)
|
||||
# ksp_app.c (or any equivalent)
|
||||
# msodbcsql.h (odbc header file)
|
||||
#
|
||||
# Execution: Run with command line with optional options
|
||||
# py build_ksp.py --KSP myKSP --APP ksp_app
|
||||
#
|
||||
#############################################################################################
|
||||
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import argparse
|
||||
|
||||
# This creates a batch *filename*, which compiles a C program according to
|
||||
# *command* and *arch* (either x86 or x64)
|
||||
def create_batch_file(arch, filename, command):
|
||||
root_dir = 'C:' + os.sep
|
||||
vcvarsall = os.path.join(root_dir, "Program Files (x86)", "Microsoft Visual Studio 14.0", "VC", "vcvarsall.bat")
|
||||
|
||||
try:
|
||||
file = open(filename, 'w')
|
||||
file.write('@ECHO OFF' + os.linesep)
|
||||
if arch == 'x64':
|
||||
file.write('@CALL "' + vcvarsall + '" amd64' + os.linesep)
|
||||
else:
|
||||
file.write('@CALL "' + vcvarsall + '" x86' + os.linesep)
|
||||
|
||||
# compile the code
|
||||
file.write('@CALL ' + command + os.linesep)
|
||||
file.close()
|
||||
except:
|
||||
print('Cannot create ', filename)
|
||||
|
||||
# This invokes the newly created batch file to compile the code,
|
||||
# according to *arch* (either x86 or x64). The batch file will be
|
||||
# removed afterwards
|
||||
def compile_KSP_windows(arch, ksp_src):
|
||||
output = 'myKSP'
|
||||
if arch == 'x64':
|
||||
output = output + arch + '.dll'
|
||||
else:
|
||||
output = output + '.dll'
|
||||
|
||||
command = 'cl {0} /LD /MD /link /out:'.format(ksp_src) + output
|
||||
batchfile = 'build_KSP.bat'
|
||||
create_batch_file(arch, batchfile, command)
|
||||
os.system(batchfile)
|
||||
os.remove(batchfile)
|
||||
|
||||
# This compiles myKSP.c
|
||||
#
|
||||
# In Windows, this will create batch files to compile two dll(s).
|
||||
# Otherwise, this will compile the code and generate a .so file.
|
||||
#
|
||||
# Output: A custom keystore provider created
|
||||
def compile_KSP(ksp_src):
|
||||
print('Compiling ', ksp_src)
|
||||
if platform.system() == 'Windows':
|
||||
compile_KSP_windows('x64', ksp_src)
|
||||
compile_KSP_windows('x86', ksp_src)
|
||||
else:
|
||||
os.system('gcc -fshort-wchar -fPIC -o myKSP.so -shared {0}'.format(ksp_src))
|
||||
|
||||
# This compiles ksp app, which assumes the existence of the .dll or the .so file.
|
||||
#
|
||||
# In Windows, a batch file is created in order to compile the code.
|
||||
def configure_KSP(app_src):
|
||||
print('Compiling ', app_src)
|
||||
if platform.system() == 'Windows':
|
||||
command = 'cl /MD {0} /link odbc32.lib /out:ksp_app.exe'.format(app_src)
|
||||
batchfile = 'build_app.bat'
|
||||
create_batch_file('x86', batchfile, command)
|
||||
os.system(batchfile)
|
||||
os.remove(batchfile)
|
||||
else:
|
||||
os.system('gcc -o ksp_app -fshort-wchar {0} -lodbc -ldl'.format(app_src))
|
||||
|
||||
################################### Main Function ###################################
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-ksp', '--KSPSRC', default='myKSP.c', help='The source file of KSP (keystore provider)')
|
||||
parser.add_argument('-app', '--APPSRC', default='ksp_app.c', help='The source file for the app that uses the KSP')
|
||||
args = parser.parse_args()
|
||||
|
||||
ksp_src = args.KSPSRC
|
||||
app_src = args.APPSRC
|
||||
header = 'msodbcsql.h'
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
# make sure all required source and header files are present
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
os.chdir(work_dir)
|
||||
|
||||
if not os.path.exists(os.path.join(work_dir, header)):
|
||||
print('Error: {0} not found!'.format(header))
|
||||
exit(1)
|
||||
if not os.path.exists(os.path.join(work_dir, ksp_src)):
|
||||
print('Error: {0}.c not found!'.format(ksp_src))
|
||||
exit(1)
|
||||
if not os.path.exists(os.path.join(work_dir, app_src)):
|
||||
print('Error: {0}.c not found!'.format(app_src))
|
||||
exit(1)
|
||||
|
||||
compile_KSP(ksp_src)
|
||||
configure_KSP(app_src)
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
|
305
test/functional/setup/ksp_app.c
Normal file
305
test/functional/setup/ksp_app.c
Normal file
|
@ -0,0 +1,305 @@
|
|||
/******************************************************************************
|
||||
Example application for demonstration of custom keystore provider usage
|
||||
|
||||
Windows: compile with cl /MD ksp_app.c /link odbc32.lib /out:ksp_app.exe
|
||||
Linux/mac: compile with gcc -o ksp_app -fshort-wchar ksp_app.c -lodbc -ldl
|
||||
|
||||
usage: kspapp connstr
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#define KSPNAME L"MyCustomKSPName"
|
||||
#define PROV_ENCRYPT_KEY "LPKCWVD07N3RG98J0MBLG4H2" /* this can be any character string */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define __stdcall
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include "msodbcsql.h"
|
||||
|
||||
enum job {
|
||||
set_up = 0,
|
||||
clean_up = 1
|
||||
};
|
||||
|
||||
/* Convenience functions */
|
||||
|
||||
int checkRC(SQLRETURN rc, char *msg, int ret, SQLHANDLE h, SQLSMALLINT ht) {
|
||||
if (rc == SQL_ERROR) {
|
||||
fprintf(stderr, "Error occurred upon %s\n", msg);
|
||||
if (h) {
|
||||
SQLSMALLINT i = 0;
|
||||
SQLSMALLINT outlen = 0;
|
||||
char errmsg[1024];
|
||||
while ((rc = SQLGetDiagField(
|
||||
ht, h, ++i, SQL_DIAG_MESSAGE_TEXT, errmsg, sizeof(errmsg), &outlen)) == SQL_SUCCESS
|
||||
|| rc == SQL_SUCCESS_WITH_INFO) {
|
||||
fprintf(stderr, "Err#%d: %s\n", i, errmsg);
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
exit(ret);
|
||||
return 0;
|
||||
}
|
||||
else if (rc == SQL_SUCCESS_WITH_INFO && h) {
|
||||
SQLSMALLINT i = 0;
|
||||
SQLSMALLINT outlen = 0;
|
||||
char errmsg[1024];
|
||||
printf("Success with info for %s:\n", msg);
|
||||
while ((rc = SQLGetDiagField(
|
||||
ht, h, ++i, SQL_DIAG_MESSAGE_TEXT, errmsg, sizeof(errmsg), &outlen)) == SQL_SUCCESS
|
||||
|| rc == SQL_SUCCESS_WITH_INFO) {
|
||||
fprintf(stderr, "Msg#%d: %s\n", i, errmsg);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void postKspError(CEKEYSTORECONTEXT *ctx, const wchar_t *msg, ...) {
|
||||
if (msg > (wchar_t*)65535)
|
||||
wprintf(L"Provider emitted message: %s\n", msg);
|
||||
else
|
||||
wprintf(L"Provider emitted message ID %d\n", msg);
|
||||
}
|
||||
|
||||
int setKSPLibrary(SQLHSTMT stmt) {
|
||||
unsigned char CEK[32];
|
||||
unsigned char *ECEK;
|
||||
unsigned short ECEKlen;
|
||||
unsigned char foundProv = 0;
|
||||
int i;
|
||||
#ifdef _WIN32
|
||||
HMODULE hProvLib;
|
||||
#else
|
||||
void *hProvLib;
|
||||
#endif
|
||||
CEKEYSTORECONTEXT ctx = {0};
|
||||
CEKEYSTOREPROVIDER **ppKsp, *pKsp;
|
||||
int(__stdcall *pEncryptCEK)(CEKEYSTORECONTEXT *, errFunc *, unsigned char *, unsigned short, unsigned char **, unsigned short *);
|
||||
|
||||
/* Load the provider library */
|
||||
#ifdef _WIN32
|
||||
if (!(hProvLib = LoadLibrary("myKSP.dll"))) {
|
||||
#else
|
||||
if (!(hProvLib = dlopen("./myKSP.so", RTLD_NOW))) {
|
||||
#endif
|
||||
fprintf(stderr, "Error loading KSP library\n");
|
||||
return 2;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!(ppKsp = (CEKEYSTOREPROVIDER**)GetProcAddress(hProvLib, "CEKeystoreProvider"))) {
|
||||
#else
|
||||
if (!(ppKsp = (CEKEYSTOREPROVIDER**)dlsym(hProvLib, "CEKeystoreProvider"))) {
|
||||
#endif
|
||||
fprintf(stderr, "The export CEKeystoreProvider was not found in the KSP library\n");
|
||||
return 3;
|
||||
}
|
||||
while (pKsp = *ppKsp++) {
|
||||
if (!memcmp(KSPNAME, pKsp->Name, sizeof(KSPNAME))) {
|
||||
foundProv = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! foundProv) {
|
||||
fprintf(stderr, "Could not find provider in the library\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (pKsp->Init && !pKsp->Init(&ctx, postKspError)) {
|
||||
fprintf(stderr, "Could not initialize provider\n");
|
||||
return 5;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!(pEncryptCEK = (LPVOID)GetProcAddress(hProvLib, "KeystoreEncrypt"))) {
|
||||
#else
|
||||
if (!(pEncryptCEK = dlsym(hProvLib, "KeystoreEncrypt"))) {
|
||||
#endif
|
||||
fprintf(stderr, "The export KeystoreEncrypt was not found in the KSP library\n");
|
||||
return 6;
|
||||
}
|
||||
if (!pKsp->Write) {
|
||||
fprintf(stderr, "Provider does not support configuration\n");
|
||||
return 7;
|
||||
}
|
||||
|
||||
/* Configure the provider with the key */
|
||||
if (!pKsp->Write(&ctx, postKspError, PROV_ENCRYPT_KEY, strlen(PROV_ENCRYPT_KEY))) {
|
||||
fprintf(stderr, "Error writing to KSP\n");
|
||||
return 8;
|
||||
}
|
||||
|
||||
/* Generate a CEK and encrypt it with the provider */
|
||||
srand(time(0) ^ getpid());
|
||||
for (i = 0; i < sizeof(CEK); i++)
|
||||
CEK[i] = rand();
|
||||
|
||||
if (!pEncryptCEK(&ctx, postKspError, CEK, sizeof(CEK), &ECEK, &ECEKlen)) {
|
||||
fprintf(stderr, "Error encrypting CEK\n");
|
||||
return 9;
|
||||
}
|
||||
|
||||
/* Create a CMK definition on the server */
|
||||
{
|
||||
static char cmkSql[] = "CREATE COLUMN MASTER KEY CustomCMK WITH ("
|
||||
"KEY_STORE_PROVIDER_NAME = 'MyCustomKSPName',"
|
||||
"KEY_PATH = 'TheOneAndOnlyKey')";
|
||||
printf("Create CMK: %s\n", cmkSql);
|
||||
SQLExecDirect(stmt, cmkSql, SQL_NTS);
|
||||
}
|
||||
|
||||
/* Create a CEK definition on the server */
|
||||
{
|
||||
const char cekSqlBefore[] = "CREATE COLUMN ENCRYPTION KEY CustomCEK WITH VALUES ("
|
||||
"COLUMN_MASTER_KEY = CustomCMK,"
|
||||
"ALGORITHM = 'none',"
|
||||
"ENCRYPTED_VALUE = 0x";
|
||||
char *cekSql = malloc(sizeof(cekSqlBefore) + 2 * ECEKlen + 2); /* 1 for ')', 1 for null terminator */
|
||||
strcpy(cekSql, cekSqlBefore);
|
||||
for (i = 0; i < ECEKlen; i++)
|
||||
sprintf(cekSql + sizeof(cekSqlBefore) - 1 + 2 * i, "%02x", ECEK[i]);
|
||||
strcat(cekSql, ")");
|
||||
printf("Create CEK: %s\n", cekSql);
|
||||
SQLExecDirect(stmt, cekSql, SQL_NTS);
|
||||
free(cekSql);
|
||||
#ifdef _WIN32
|
||||
LocalFree(ECEK);
|
||||
#else
|
||||
free(ECEK);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(hProvLib);
|
||||
#else
|
||||
dlclose(hProvLib);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void populateTestTable(SQLHDBC dbc, SQLHSTMT stmt)
|
||||
{
|
||||
SQLRETURN rc;
|
||||
int i, j;
|
||||
|
||||
/* Create a table with encrypted columns */
|
||||
{
|
||||
static char *tableSql = "CREATE TABLE CustomKSPTestTable ("
|
||||
"c1 int,"
|
||||
"c2 varchar(255) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),"
|
||||
"c3 char(5) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),"
|
||||
"c4 date ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'))";
|
||||
printf("Create table: %s\n", tableSql);
|
||||
SQLExecDirect(stmt, tableSql, SQL_NTS);
|
||||
}
|
||||
|
||||
/* Load provider into the ODBC Driver and configure it */
|
||||
{
|
||||
unsigned char ksd[sizeof(CEKEYSTOREDATA) + sizeof(PROV_ENCRYPT_KEY) - 1];
|
||||
CEKEYSTOREDATA *pKsd = (CEKEYSTOREDATA*)ksd;
|
||||
pKsd->name = L"MyCustomKSPName";
|
||||
pKsd->dataSize = sizeof(PROV_ENCRYPT_KEY) - 1;
|
||||
memcpy(pKsd->data, PROV_ENCRYPT_KEY, sizeof(PROV_ENCRYPT_KEY) - 1);
|
||||
#ifdef _WIN32
|
||||
rc = SQLSetConnectAttr(dbc, SQL_COPT_SS_CEKEYSTOREPROVIDER, "myKSP.dll", SQL_NTS);
|
||||
#else
|
||||
rc = SQLSetConnectAttr(dbc, SQL_COPT_SS_CEKEYSTOREPROVIDER, "./myKSP.so", SQL_NTS);
|
||||
#endif
|
||||
checkRC(rc, "Loading KSP into ODBC Driver", 7, dbc, SQL_HANDLE_DBC);
|
||||
rc = SQLSetConnectAttr(dbc, SQL_COPT_SS_CEKEYSTOREDATA, (SQLPOINTER)pKsd, SQL_IS_POINTER);
|
||||
checkRC(rc, "Configuring the KSP", 7, dbc, SQL_HANDLE_DBC);
|
||||
}
|
||||
|
||||
/* Insert some data */
|
||||
{
|
||||
int c1;
|
||||
char c2[256];
|
||||
char c3[6];
|
||||
SQL_DATE_STRUCT date;
|
||||
SQLLEN cbdate;
|
||||
rc = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &c1, 0, 0);
|
||||
checkRC(rc, "Binding parameters for insert", 9, stmt, SQL_HANDLE_STMT);
|
||||
rc = SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, c2, 255, 0);
|
||||
checkRC(rc, "Binding parameters for insert", 9, stmt, SQL_HANDLE_STMT);
|
||||
rc = SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 5, 0, c3, 5, 0);
|
||||
checkRC(rc, "Binding parameters for insert", 9, stmt, SQL_HANDLE_STMT);
|
||||
checkRC(rc, "Binding parameters for insert", 9, stmt, SQL_HANDLE_STMT);
|
||||
cbdate = sizeof(SQL_DATE_STRUCT);
|
||||
rc = SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 10, 0, &date, 0, &cbdate);
|
||||
checkRC(rc, "Binding parameters for insert", 9, stmt, SQL_HANDLE_STMT);
|
||||
|
||||
date.year = 2017;
|
||||
date.month = 8;
|
||||
for (i = 0; i < 10; i++) {
|
||||
date.day = i + 10;
|
||||
|
||||
c1 = i * 10 + i + 1;
|
||||
sprintf(c2, "Sample data %d for column 2", i);
|
||||
for (j = 0; j < 3; j++) {
|
||||
c3[j] = 'a' + i + j;
|
||||
}
|
||||
c3[3] = '\0';
|
||||
rc = SQLExecDirect(stmt, "INSERT INTO CustomKSPTestTable (c1, c2, c3, c4) values (?, ?, ?, ?)", SQL_NTS);
|
||||
checkRC(rc, "Inserting rows query", 10, stmt, SQL_HANDLE_STMT);
|
||||
}
|
||||
printf("(Encrypted) data has been inserted into CustomKSPTestTable. You may inspect the data now.\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char sqlbuf[1024];
|
||||
SQLHENV env;
|
||||
SQLHDBC dbc;
|
||||
SQLHSTMT stmt;
|
||||
SQLRETURN rc;
|
||||
int i;
|
||||
char connStr[1024];
|
||||
enum job task;
|
||||
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "usage: kspapp job server database uid pwd\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
task = atoi(argv[1]);
|
||||
|
||||
sprintf(connStr, "DRIVER={ODBC Driver 13 for SQL Server};SERVER=%s;ColumnEncryption=Enabled;DATABASE=%s;UID=%s;PWD=%s", argv[2], argv[3], argv[4], argv[5]);
|
||||
|
||||
/* Connect to Server */
|
||||
rc = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env);
|
||||
checkRC(rc, "allocating environment handle", 2, 0, 0);
|
||||
rc = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
|
||||
checkRC(rc, "setting ODBC version to 3.0", 3, env, SQL_HANDLE_ENV);
|
||||
rc = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
|
||||
checkRC(rc, "allocating connection handle", 4, env, SQL_HANDLE_ENV);
|
||||
rc = SQLDriverConnect(dbc, 0, connStr, strlen(connStr), NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
|
||||
checkRC(rc, "connecting to data source", 5, dbc, SQL_HANDLE_DBC);
|
||||
rc = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
checkRC(rc, "allocating statement handle", 6, dbc, SQL_HANDLE_DBC);
|
||||
|
||||
if (task == set_up) {
|
||||
printf("Setting up KSP...\n");
|
||||
setKSPLibrary(stmt);
|
||||
populateTestTable(dbc, stmt);
|
||||
}
|
||||
else if (task == clean_up) {
|
||||
printf("Cleaning up KSP...\n");
|
||||
|
||||
SQLExecDirect(stmt, "DROP TABLE CustomKSPTestTable", SQL_NTS);
|
||||
SQLExecDirect(stmt, "DROP COLUMN ENCRYPTION KEY CustomCEK", SQL_NTS);
|
||||
SQLExecDirect(stmt, "DROP COLUMN MASTER KEY CustomCMK", SQL_NTS);
|
||||
printf("Removed table, CEK, and CMK\n");
|
||||
}
|
||||
|
||||
SQLDisconnect(dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, env);
|
||||
return 0;
|
||||
}
|
132
test/functional/setup/myKSP.c
Normal file
132
test/functional/setup/myKSP.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/******************************************************************************
|
||||
Custom Keystore Provider Example
|
||||
|
||||
Windows: compile with cl myKSP.c /LD /MD /link /out:myKSP.dll
|
||||
Linux/mac: compile with gcc -fshort-wchar -fPIC -o myKSP.so -shared myKSP.c
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define __stdcall
|
||||
#endif
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sqltypes.h>
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include "msodbcsql.h"
|
||||
|
||||
int wcscmp_short(wchar_t *s1, wchar_t *s2) {
|
||||
while(*s1 && *s2 && *s1 == *s2)
|
||||
s1++, s2++;
|
||||
return *s1 - *s2;
|
||||
}
|
||||
|
||||
int __stdcall KeystoreInit(CEKEYSTORECONTEXT *ctx, errFunc *onError) {
|
||||
if (DEBUG)
|
||||
printf("KSP Init() function called\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned char *g_encryptKey;
|
||||
static unsigned int g_encryptKeyLen;
|
||||
|
||||
int __stdcall KeystoreWrite(CEKEYSTORECONTEXT *ctx, errFunc *onError, void *data, unsigned int len) {
|
||||
if (DEBUG)
|
||||
printf("KSP Write() function called (%d bytes)\n", len);
|
||||
if (len) {
|
||||
if (g_encryptKey)
|
||||
free(g_encryptKey);
|
||||
g_encryptKey = malloc(len);
|
||||
if (!g_encryptKey) {
|
||||
onError(ctx, L"Memory Allocation Error");
|
||||
return 0;
|
||||
}
|
||||
memcpy(g_encryptKey, data, len);
|
||||
g_encryptKeyLen = len;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Very simple "encryption" scheme - rotating XOR with the key
|
||||
int __stdcall KeystoreDecrypt(CEKEYSTORECONTEXT *ctx, errFunc *onError, const wchar_t *keyPath, const wchar_t *alg, unsigned char *ecek, unsigned short ecekLen, unsigned char **cekOut, unsigned short *cekLen) {
|
||||
unsigned int i;
|
||||
if (DEBUG)
|
||||
printf("KSP Decrypt() function called (keypath=%S alg=%S ecekLen=%u)\n", keyPath, alg, ecekLen);
|
||||
if (wcscmp_short(keyPath, L"TheOneAndOnlyKey")) {
|
||||
onError(ctx, L"Invalid key path");
|
||||
return 0;
|
||||
}
|
||||
if (wcscmp_short(alg, L"none")) {
|
||||
onError(ctx, L"Invalid algorithm");
|
||||
return 0;
|
||||
}
|
||||
if (!g_encryptKey) {
|
||||
onError(ctx, L"Keystore provider not initialized with key");
|
||||
return 0;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
*cekOut = malloc(ecekLen);
|
||||
#else
|
||||
*cekOut = LocalAlloc(LMEM_FIXED, ecekLen);
|
||||
#endif
|
||||
if (!*cekOut) {
|
||||
onError(ctx, L"Memory Allocation Error");
|
||||
return 0;
|
||||
}
|
||||
*cekLen = ecekLen;
|
||||
for (i = 0; i < ecekLen; i++)
|
||||
(*cekOut)[i] = ecek[i] ^ g_encryptKey[i % g_encryptKeyLen];
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Note that in the provider interface, this function would be referenced via the CEKEYSTOREPROVIDER
|
||||
// structure. However, that does not preclude keystore providers from exporting their own functions,
|
||||
// as illustrated by this example where the encryption is performed via a separate function (with a
|
||||
// different prototype than the one in the KSP interface.)
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int KeystoreEncrypt(CEKEYSTORECONTEXT *ctx, errFunc *onError,
|
||||
unsigned char *cek, unsigned short cekLen,
|
||||
unsigned char **ecekOut, unsigned short *ecekLen) {
|
||||
unsigned int i;
|
||||
|
||||
if (DEBUG)
|
||||
printf("KSP Encrypt() function called (cekLen=%u)\n", cekLen);
|
||||
if (!g_encryptKey) {
|
||||
onError(ctx, L"Keystore provider not initialized with key");
|
||||
return 0;
|
||||
}
|
||||
*ecekOut = malloc(cekLen);
|
||||
if (!*ecekOut) {
|
||||
onError(ctx, L"Memory Allocation Error");
|
||||
return 0;
|
||||
}
|
||||
*ecekLen = cekLen;
|
||||
for (i = 0; i < cekLen; i++)
|
||||
(*ecekOut)[i] = cek[i] ^ g_encryptKey[i % g_encryptKeyLen];
|
||||
return 1;
|
||||
}
|
||||
|
||||
CEKEYSTOREPROVIDER MyCustomKSPName_desc = {
|
||||
L"MyCustomKSPName",
|
||||
KeystoreInit,
|
||||
0,
|
||||
KeystoreWrite,
|
||||
KeystoreDecrypt,
|
||||
0
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
CEKEYSTOREPROVIDER *CEKeystoreProvider[] = {
|
||||
&MyCustomKSPName_desc,
|
||||
0
|
||||
};
|
57
test/functional/setup/run_ksp.py
Normal file
57
test/functional/setup/run_ksp.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/python3
|
||||
#########################################################################################
|
||||
#
|
||||
# Description: This script assumes the existence of the ksp_app executable and will
|
||||
# invoke it to create / remove the Column Master Key, the Column Encryption key,
|
||||
# and the table [CustomKSPTestTable] in the test database.
|
||||
#
|
||||
# Requirement:
|
||||
# python 3.x
|
||||
# ksp_app executable
|
||||
#
|
||||
# Execution: Run with command line with required options
|
||||
# py run_ksp.py --SERVER=server --DBNAME=database --UID=uid --PWD=pwd
|
||||
# py run_ksp.py --SERVER=server --DBNAME=database --UID=uid --PWD=pwd --REMOVE
|
||||
#
|
||||
#############################################################################################
|
||||
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import argparse
|
||||
|
||||
################################### Main Function ###################################
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-server', '--SERVER', required=True, help='SQL Server')
|
||||
parser.add_argument('-dbname', '--DBNAME', required=True, help='Name of an existing database')
|
||||
parser.add_argument('-uid', '--UID', required=True, help='User name')
|
||||
parser.add_argument('-pwd', '--PWD', required=True, help='User password')
|
||||
parser.add_argument('-remove', '--REMOVE', action='store_true', help='Clean up KSP related data, false by default')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
app_name = 'ksp_app'
|
||||
cwd = os.getcwd()
|
||||
|
||||
# first check if the ksp app is present
|
||||
work_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
os.chdir(work_dir)
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
path = os.path.join(work_dir, app_name + '.exe')
|
||||
executable = app_name
|
||||
else:
|
||||
path = os.path.join(work_dir, app_name)
|
||||
executable = './' + app_name
|
||||
|
||||
if not os.path.exists(path):
|
||||
print('Error: {0} not found!'.format(path))
|
||||
exit(1)
|
||||
|
||||
if args.REMOVE:
|
||||
os.system('{0} 1 {1} {2} {3} {4}'.format(executable, args.SERVER, args.DBNAME, args.UID, args.PWD))
|
||||
else:
|
||||
os.system('{0} 0 {1} {2} {3} {4}'.format(executable, args.SERVER, args.DBNAME, args.UID, args.PWD))
|
||||
|
||||
os.chdir(cwd)
|
25
test/functional/sqlsrv/AE_Ksp.inc
Normal file
25
test/functional/sqlsrv/AE_Ksp.inc
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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';
|
||||
|
||||
?>
|
|
@ -36,7 +36,7 @@ function ConnectionTest()
|
|||
Trace("\nValid connection attempt (to $server) ....\n");
|
||||
$conn2 = Connect();
|
||||
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
||||
if(count($errors) != 0)
|
||||
if (!empty($errors))
|
||||
{
|
||||
die("No errors were expected on valid connection attempts.");
|
||||
}
|
||||
|
|
40
test/functional/sqlsrv/skipif_server_old.inc
Normal file
40
test/functional/sqlsrv/skipif_server_old.inc
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
if (!extension_loaded("sqlsrv"))
|
||||
die("skip extension not loaded");
|
||||
|
||||
require_once( "MsSetup.inc" );
|
||||
$connectionInfo = array( "UID"=>$userName, "PWD"=>$userPassword );
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( ! $conn )
|
||||
{
|
||||
echo ( "Error: could not connect during SKIPIF!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$server_info = sqlsrv_server_info( $conn );
|
||||
if( $server_info )
|
||||
{
|
||||
// check SQL Server version
|
||||
$version = substr($server_info['SQLServerVersion'], 0, 2);
|
||||
if ($version < 13)
|
||||
{
|
||||
// older than SQL Server 2016
|
||||
die( "skip - feature not supported in this version of SQL Server." );
|
||||
}
|
||||
}
|
||||
|
||||
$client_info = sqlsrv_client_info( $conn );
|
||||
if( $client_info )
|
||||
{
|
||||
// check ODBC driver version
|
||||
$version = substr($client_info['DriverVer'], 0, 2);
|
||||
if ($version < 13)
|
||||
{
|
||||
// older than ODBC 13
|
||||
die( "skip - feature not supported in this version of ODBC driver." );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
File diff suppressed because one or more lines are too long
64
test/functional/sqlsrv/sqlsrv_connect_encrypted_ksp.phpt
Normal file
64
test/functional/sqlsrv/sqlsrv_connect_encrypted_ksp.phpt
Normal file
|
@ -0,0 +1,64 @@
|
|||
--TEST--
|
||||
Fetch data from a prepopulated test table given a custom keystore provider
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 1 );
|
||||
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
|
||||
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>$ksp_name,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key,
|
||||
'ReturnDatesAsStrings'=>true);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption enabled.\n";
|
||||
}
|
||||
|
||||
$tsql = "SELECT * FROM $ksp_test_table";
|
||||
$stmt = sqlsrv_prepare($conn, $tsql);
|
||||
if (! sqlsrv_execute($stmt) )
|
||||
{
|
||||
echo "Failed to fetch data.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
|
||||
// fetch data
|
||||
while ($row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC ))
|
||||
{
|
||||
echo "c1=" . $row[0] . "\tc2=" . $row[1] . "\tc3=" . $row[2] . "\tc4=" . $row[3] . "\n" ;
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Connected successfully with ColumnEncryption enabled.
|
||||
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
|
|
@ -0,0 +1,68 @@
|
|||
--TEST--
|
||||
Fetch encrypted data from a prepopulated test table given a custom keystore provider
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 1 );
|
||||
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
|
||||
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>$ksp_name,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key,
|
||||
'ReturnDatesAsStrings'=>true);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption disabled.\n";
|
||||
}
|
||||
|
||||
$tsql = "SELECT * FROM $ksp_test_table";
|
||||
$stmt = sqlsrv_prepare($conn, $tsql);
|
||||
if (! sqlsrv_execute($stmt) )
|
||||
{
|
||||
echo "Failed to fetch data.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
|
||||
// fetch data
|
||||
while ($row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_NUMERIC ))
|
||||
{
|
||||
// all columns should return binary data except the first column
|
||||
echo "c1=" . $row[0];
|
||||
echo "\tc2=" . bin2hex($row[1]);
|
||||
echo "\tc3=" . bin2hex($row[2]);
|
||||
echo "\tc4=" . bin2hex($row[3]);
|
||||
echo "\n" ;
|
||||
}
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
Connected successfully with ColumnEncryption disabled.
|
||||
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
|
130
test/functional/sqlsrv/sqlsrv_connect_encrypted_ksp_errors.phpt
Normal file
130
test/functional/sqlsrv/sqlsrv_connect_encrypted_ksp_errors.phpt
Normal file
|
@ -0,0 +1,130 @@
|
|||
--TEST--
|
||||
Connect using a custom keystore provider with some required inputs missing
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function connect( $server, $connectionInfo )
|
||||
{
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
$errors = sqlsrv_errors();
|
||||
foreach ( $errors[0] as $key => $error )
|
||||
{
|
||||
if( is_string( $key ) )
|
||||
echo "[$key] => $error\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption enabled.\n";
|
||||
}
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
|
||||
|
||||
require( 'MsSetup.inc' );
|
||||
require( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
echo("Connecting... with column encryption\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled");
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... with an invalid input to CEKeystoreProvider\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>1);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... with an empty path\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>"",
|
||||
"CEKeystoreName"=>$ksp_name,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... without a name\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... with an empty name\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>"",
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... without a key\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>$ksp_name);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo("Connecting... with all required inputs\n");
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>"enabled",
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>$ksp_name,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key);
|
||||
|
||||
connect( $server, $connectionInfo );
|
||||
|
||||
echo "Done\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Connecting... with column encryption
|
||||
Connected successfully with ColumnEncryption enabled.
|
||||
Connecting... with an invalid input to CEKeystoreProvider
|
||||
Failed to connect.
|
||||
[SQLSTATE] => IMSSP
|
||||
[code] => -33
|
||||
[message] => Invalid value type for option CEKeystoreProvider was specified. String type was expected.
|
||||
|
||||
Connecting... with an empty path
|
||||
Failed to connect.
|
||||
[SQLSTATE] => IMSSP
|
||||
[code] => -104
|
||||
[message] => Invalid value for loading a custom keystore provider.
|
||||
|
||||
Connecting... without a name
|
||||
Failed to connect.
|
||||
[SQLSTATE] => IMSSP
|
||||
[code] => -101
|
||||
[message] => The name of the custom keystore provider is missing.
|
||||
|
||||
Connecting... with an empty name
|
||||
Failed to connect.
|
||||
[SQLSTATE] => IMSSP
|
||||
[code] => -104
|
||||
[message] => Invalid value for loading a custom keystore provider.
|
||||
|
||||
Connecting... without a key
|
||||
Failed to connect.
|
||||
[SQLSTATE] => IMSSP
|
||||
[code] => -103
|
||||
[message] => The encryption key for the custom keystore provider is missing.
|
||||
|
||||
Connecting... with all required inputs
|
||||
Connected successfully with ColumnEncryption enabled.
|
||||
Done
|
248
test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt
Normal file
248
test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt
Normal file
|
@ -0,0 +1,248 @@
|
|||
--TEST--
|
||||
Test simple insert, fetch and update with ColumnEncryption enabled and a custome keystore provider
|
||||
--SKIPIF--
|
||||
<?php require('skipif_server_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
function CreatePatientsTable()
|
||||
{
|
||||
global $conn;
|
||||
$tablename = 'Patients';
|
||||
|
||||
$stmt = sqlsrv_query( $conn, "IF OBJECT_ID('$tablename', 'U') IS NOT NULL DROP TABLE $tablename" );
|
||||
sqlsrv_free_stmt( $stmt );
|
||||
|
||||
$tsql = "CREATE TABLE $tablename (
|
||||
[PatientId] [int] IDENTITY(1,1) NOT NULL,
|
||||
[SSN] [char](11) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
|
||||
[FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
|
||||
[LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = Deterministic, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
|
||||
[BirthDate] [date] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = CustomCEK, ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL)";
|
||||
|
||||
$stmt = sqlsrv_query( $conn, $tsql );
|
||||
if (! $stmt )
|
||||
{
|
||||
echo "Failed to create test table!\n";
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
return $tablename;
|
||||
}
|
||||
|
||||
function InsertData($ssn, $fname, $lname, $date)
|
||||
{
|
||||
global $conn, $tablename;
|
||||
|
||||
$params = array(
|
||||
array($ssn, null, null, SQLSRV_SQLTYPE_CHAR(11)), array($fname, null, null, SQLSRV_SQLTYPE_NVARCHAR(50)), array($lname, null, null, SQLSRV_SQLTYPE_NVARCHAR(50)), array($date, null, null, SQLSRV_SQLTYPE_DATE)
|
||||
);
|
||||
|
||||
$tsql = "INSERT INTO $tablename (SSN, FirstName, LastName, BirthDate) VALUES (?, ?, ?, ?)";
|
||||
if (! $stmt = sqlsrv_prepare($conn, $tsql, $params))
|
||||
{
|
||||
echo "Failed to prepare statement.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
|
||||
if (! sqlsrv_execute($stmt))
|
||||
{
|
||||
echo "Failed to insert a new record.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
}
|
||||
|
||||
function SelectData()
|
||||
{
|
||||
global $conn, $tablename;
|
||||
|
||||
$stmt = sqlsrv_query($conn, "SELECT * FROM $tablename");
|
||||
|
||||
while ($obj = sqlsrv_fetch_object( $stmt ))
|
||||
{
|
||||
echo $obj->PatientId . "\n";
|
||||
echo $obj->SSN . "\n";
|
||||
echo $obj->FirstName . "\n";
|
||||
echo $obj->LastName . "\n";
|
||||
echo $obj->BirthDate . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
function SelectDataBuffered()
|
||||
{
|
||||
global $conn, $tablename;
|
||||
|
||||
$stmt = sqlsrv_query($conn, "SELECT * FROM $tablename", array(), array("Scrollable"=>"buffered"));
|
||||
|
||||
$row_count = sqlsrv_num_rows($stmt);
|
||||
echo "\nRow count for result set is $row_count\n";
|
||||
|
||||
echo "First record=>\t";
|
||||
$row = sqlsrv_fetch($stmt, SQLSRV_SCROLL_FIRST);
|
||||
$SSN = sqlsrv_get_field( $stmt, 1);
|
||||
echo "SSN = $SSN \n";
|
||||
|
||||
echo "Next record=>\t";
|
||||
$row = sqlsrv_fetch($stmt, SQLSRV_SCROLL_NEXT);
|
||||
$BirthDate = sqlsrv_get_field( $stmt, 4);
|
||||
echo "BirthDate = $BirthDate \n";
|
||||
|
||||
echo "Last record=>\t";
|
||||
$row = sqlsrv_fetch($stmt, SQLSRV_SCROLL_LAST);
|
||||
$LastName = sqlsrv_get_field( $stmt, 3);
|
||||
echo "LastName = $LastName \n";
|
||||
}
|
||||
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 1 );
|
||||
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
|
||||
|
||||
require_once( 'MsSetup.inc' );
|
||||
require_once( 'AE_Ksp.inc' );
|
||||
|
||||
$ksp_path = getKSPpath();
|
||||
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ReturnDatesAsStrings"=>true, "ColumnEncryption"=>'Enabled',
|
||||
"CEKeystoreProvider"=>$ksp_path,
|
||||
"CEKeystoreName"=>$ksp_name,
|
||||
"CEKeystoreEncryptKey"=>$encrypt_key);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption enabled.\n";
|
||||
}
|
||||
|
||||
$tablename = CreatePatientsTable();
|
||||
|
||||
InsertData('748-68-0245', 'Jeannette', 'McDonald', '2002-11-28');
|
||||
InsertData('795-73-9838', 'John', 'Doe', '2001-05-29');
|
||||
InsertData('456-12-5486', 'Jonathan', 'Wong', '1999-12-20');
|
||||
InsertData('156-45-5486', 'Marianne', 'Smith', '1997-03-04');
|
||||
|
||||
SelectData();
|
||||
|
||||
///////////////////////////////////////////
|
||||
echo "Update Patient Jonathan Wong...\n";
|
||||
$params = array(array('1999-12-31', null, null, SQLSRV_SQLTYPE_DATE), array('Chang', null, null, SQLSRV_SQLTYPE_NVARCHAR(50)), array('456-12-5486', null, null, SQLSRV_SQLTYPE_CHAR(11)));
|
||||
|
||||
$tsql = "UPDATE Patients SET BirthDate = ?, LastName = ? WHERE SSN = ?";
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
|
||||
if (! $stmt)
|
||||
{
|
||||
echo "Failed to update record\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
|
||||
echo "Update his birthdate too...\n";
|
||||
$params = array(array('456-12-5486', null, null, SQLSRV_SQLTYPE_CHAR(11)));
|
||||
$tsql = "SELECT SSN, FirstName, LastName, BirthDate FROM Patients WHERE SSN = ?";
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
if (! $stmt)
|
||||
{
|
||||
echo "Failed to select with a WHERE clause\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$obj = sqlsrv_fetch_object( $stmt );
|
||||
|
||||
echo "BirthDate updated for $obj->FirstName:\n";
|
||||
echo $obj->SSN . "\n";
|
||||
echo $obj->FirstName . "\n";
|
||||
echo $obj->LastName . "\n";
|
||||
echo $obj->BirthDate . "\n\n";
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
$procName = '#phpAEProc1';
|
||||
$spArgs = "@p1 INT, @p2 DATE OUTPUT";
|
||||
$spCode = "SET @p2 = ( SELECT [BirthDate] FROM Patients WHERE [PatientId] = @p1 )";
|
||||
$stmt = sqlsrv_query($conn, "CREATE PROC [$procName] ($spArgs) AS BEGIN $spCode END");
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$callResult = '1900-01-01';
|
||||
$params = array( array( 1, SQLSRV_PARAM_IN ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_DATE));
|
||||
$callArgs = "?, ?";
|
||||
$stmt = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params);
|
||||
if (! $stmt )
|
||||
{
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "BirthDate for the first record is: $callResult\n";
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
$procName = '#phpAEProc2';
|
||||
$spArgs = "@p1 INT, @p2 CHAR(11) OUTPUT";
|
||||
$spCode = "SET @p2 = ( SELECT [SSN] FROM Patients WHERE [PatientId] = @p1 )";
|
||||
$stmt = sqlsrv_query($conn, "CREATE PROC [$procName] ($spArgs) AS BEGIN $spCode END");
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
$callResult = '000-00-0000';
|
||||
$params = array( array( 1, SQLSRV_PARAM_IN ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_CHAR(11)));
|
||||
$callArgs = "?, ?";
|
||||
$stmt = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params);
|
||||
if (! $stmt )
|
||||
{
|
||||
print_r( sqlsrv_errors() );
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "SSN for the first record is: $callResult\n";
|
||||
}
|
||||
|
||||
SelectDataBuffered();
|
||||
|
||||
echo "\nDone\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
Connected successfully with ColumnEncryption enabled.
|
||||
1
|
||||
748-68-0245
|
||||
Jeannette
|
||||
McDonald
|
||||
2002-11-28
|
||||
|
||||
2
|
||||
795-73-9838
|
||||
John
|
||||
Doe
|
||||
2001-05-29
|
||||
|
||||
3
|
||||
456-12-5486
|
||||
Jonathan
|
||||
Wong
|
||||
1999-12-20
|
||||
|
||||
4
|
||||
156-45-5486
|
||||
Marianne
|
||||
Smith
|
||||
1997-03-04
|
||||
|
||||
Update Patient Jonathan Wong...
|
||||
Update his birthdate too...
|
||||
BirthDate updated for Jonathan:
|
||||
456-12-5486
|
||||
Jonathan
|
||||
Chang
|
||||
1999-12-31
|
||||
|
||||
BirthDate for the first record is: 2002-11-28
|
||||
SSN for the first record is: 748-68-0245
|
||||
|
||||
Row count for result set is 4
|
||||
First record=> SSN = 748-68-0245
|
||||
Next record=> BirthDate = 2001-05-29
|
||||
Last record=> LastName = Smith
|
||||
|
||||
Done
|
|
@ -72,7 +72,7 @@ function FetchField($stmt, $idx, $numFields, $errorExpected)
|
|||
function PrintError($errorExpected = true)
|
||||
{
|
||||
$errors = sqlsrv_errors(SQLSRV_ERR_ALL);
|
||||
if (count($errors) > 0)
|
||||
if (!empty($errors))
|
||||
{
|
||||
$e = $errors[0];
|
||||
var_dump($e['message']);
|
||||
|
|
|
@ -52,7 +52,7 @@ sqlsrv_close($conn);
|
|||
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
Notice\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 26
|
||||
(Warning|Notice)\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' (\(this will throw an Error in a future version of PHP\) )?in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 26
|
||||
Array
|
||||
\(
|
||||
\[0\] => Array
|
||||
|
@ -67,7 +67,7 @@ Array
|
|||
|
||||
\)
|
||||
|
||||
Notice\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 33
|
||||
(Warning|Notice)\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' (\(this will throw an Error in a future version of PHP\) )?in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 33
|
||||
Array
|
||||
\(
|
||||
\[0\] => Array
|
||||
|
@ -81,4 +81,3 @@ Array
|
|||
\)
|
||||
|
||||
\)
|
||||
|
||||
|
|
113
test/functional/sqlsrv/test_max_length.phpt
Normal file
113
test/functional/sqlsrv/test_max_length.phpt
Normal file
|
@ -0,0 +1,113 @@
|
|||
--TEST--
|
||||
Maximum length outputs from stored procs for string types (nvarchar, varchar, and varbinary)
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
$inValue1 = str_repeat( 'A', 3999 );
|
||||
|
||||
$outValue1 = "TEST";
|
||||
|
||||
sqlsrv_configure('WarningsReturnAsErrors', 0);
|
||||
sqlsrv_configure('LogSubsystems', 15);
|
||||
|
||||
require( 'MsCommon.inc' );
|
||||
|
||||
$conn = Connect();
|
||||
|
||||
$field_type = 'NVARCHAR(4000)';
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP PROC [TestFullLenStringsOut]");
|
||||
$stmt = sqlsrv_query($conn, "CREATE PROC [TestFullLenStringsOut] (@p1 " . $field_type . ", @p2 " . $field_type . " OUTPUT)
|
||||
AS
|
||||
BEGIN
|
||||
SELECT @p2 = CONVERT(" . $field_type . ", @p1 + N'A')
|
||||
END");
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
// remember to increment buffer_len to 8001 to see what happens
|
||||
$stmt = sqlsrv_query($conn, "{CALL [TestFullLenStringsOut] (?, ?)}",
|
||||
array(
|
||||
array($inValue1, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8'), SQLSRV_SQLTYPE_NVARCHAR(4000)),
|
||||
array(&$outValue1, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING('utf-8'), SQLSRV_SQLTYPE_NVARCHAR(4000))));
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
while( sqlsrv_next_result( $stmt )) {}
|
||||
print_r( strlen( $outValue1 ));
|
||||
echo "\n";
|
||||
print_r( substr( $outValue1, -2, 2 ));
|
||||
|
||||
$field_type = 'VARCHAR(8000)';
|
||||
$inValue1 = str_repeat( 'A', 7999 );
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP PROC [TestFullLenStringsOut]");
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
$stmt = sqlsrv_query( $conn, "CREATE PROC [TestFullLenStringsOut] (@p1 " . $field_type . ", @p2 " . $field_type . " OUTPUT)
|
||||
AS
|
||||
BEGIN
|
||||
SELECT @p2 = CONVERT(" . $field_type . ", @p1 + 'A')
|
||||
END" );
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
$stmt = sqlsrv_query($conn, "{CALL [TestFullLenStringsOut] (?, ?)}",
|
||||
array(
|
||||
array($inValue1, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARCHAR(8000)),
|
||||
array(&$outValue1, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARCHAR(8000))));
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
while( sqlsrv_next_result( $stmt )) {}
|
||||
echo "\n";
|
||||
print_r( strlen( $outValue1 ));
|
||||
echo "\n";
|
||||
print_r( substr( $outValue1, -2, 2 ));
|
||||
|
||||
$field_type = 'VARBINARY(8000)';
|
||||
$inValue1 = str_repeat( 'A', 7999 );
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP PROC [TestFullLenStringsOut]");
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
$stmt = sqlsrv_query($conn, "CREATE PROC [TestFullLenStringsOut] (@p1 " . $field_type . ", @p2 " . $field_type . " OUTPUT)
|
||||
AS
|
||||
BEGIN
|
||||
SELECT @p2 = CONVERT(" . $field_type . ", @p1 + 0x42)
|
||||
END");
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
$stmt = sqlsrv_query($conn, "{CALL [TestFullLenStringsOut] (?, ?)}",
|
||||
array(
|
||||
array($inValue1, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY(8000)),
|
||||
array(&$outValue1, SQLSRV_PARAM_INOUT, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY(8000))));
|
||||
if( $stmt === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
while( sqlsrv_next_result( $stmt )) {}
|
||||
echo "\n";
|
||||
print_r( strlen( $outValue1 ));
|
||||
echo "\n";
|
||||
print_r( substr( $outValue1, -2, 2 ));
|
||||
|
||||
sqlsrv_close( $conn );
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
4000
|
||||
AA
|
||||
8000
|
||||
AA
|
||||
8000
|
||||
AB
|
Loading…
Reference in a new issue