Merge pull request #1221 from microsoft/dev

5.9.0-beta2
This commit is contained in:
Jenny Tam 2020-12-02 12:30:14 -08:00 committed by GitHub
commit e7e7a8d636
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1004 additions and 267 deletions

View file

@ -1,7 +1,7 @@
sudo: required
os: linux
dist: trusty
dist: bionic
group: edge
@ -20,10 +20,10 @@ env:
- TEST_PHP_SQL_PWD=Password123
before_install:
- docker pull mcr.microsoft.com/mssql/server:2019-latest
- docker pull mcr.microsoft.com/mssql/server:2017-latest
install:
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password123' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d mcr.microsoft.com/mssql/server:2019-latest
- docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Password123' -p 1433:1433 --name=$TEST_PHP_SQL_SERVER -d mcr.microsoft.com/mssql/server:2017-latest
- docker build --build-arg PHPSQLDIR=$PHPSQLDIR -t msphpsql-dev -f Dockerfile-msphpsql .
before_script:
@ -43,7 +43,6 @@ script:
- docker exec client bash -c 'for f in ./test/functional/pdo_sqlsrv/*.out; do ls $f 2>/dev/null; cat $f 2>/dev/null; done || true'
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $SQLSRV_DBNAME
- docker exec client python ./test/functional/setup/cleanup_dbs.py -dbname $PDOSQLSRV_DBNAME
- docker exec client coveralls -i ./source/ -e ./source/shared/ -e ./test/ --gcov-options '\-lp'
- docker stop client
- docker ps -a

View file

@ -3,6 +3,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
## 5.9.0-beta2 - 2020-12-02
Updated PECL release packages. Here is the list of updates:
### Added
- Support for PHP 8.0
### Removed
- Dropped support for PHP 7.2
### Fixed
- Pull Request [#1205](https://github.com/microsoft/msphpsql/pull/1205) - minimized compilation warnings on Linux and macOS
- Pull Request [#1209](https://github.com/microsoft/msphpsql/pull/1209) - fixed a bug in fetching varbinary max fields as char or wide chars
- Issue [#1210](https://github.com/microsoft/msphpsql/issues/1210) - switched from preview to beta terminology to enable Pickle support
- Issue [#1213](https://github.com/microsoft/msphpsql/issues/1213) - the MACOSX_DEPLOYMENT_TARGET in config files caused linker errors in macOS Big Sur - Pull Request [#1215](https://github.com/microsoft/msphpsql/pull/1215)
### Limitations
- No support for inout / output params when using sql_variant type
- No support for inout / output params when formatting decimal values
- In Linux and macOS, setlocale() only takes effect if it is invoked before the first connection. Attempting to set the locale after connecting will not work
- Always Encrypted requires [MS ODBC Driver 17+](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server)
- Only Windows Certificate Store and Azure Key Vault are supported. Custom Keystores are not yet supported
- Issue [#716](https://github.com/Microsoft/msphpsql/issues/716) - With Always Encrypted enabled, named parameters in subqueries are not supported
- Issue [#1050](https://github.com/microsoft/msphpsql/issues/1050) - With Always Encrypted enabled, insertion requires the column list for any tables with identity columns
- [Always Encrypted limitations](https://docs.microsoft.com/sql/connect/php/using-always-encrypted-php-drivers#limitations-of-the-php-drivers-when-using-always-encrypted)
### Known Issues
- This preview release requires ODBC Driver 17.4.2 or above. Otherwise, a warning about failing to set an attribute may be suppressed when using an older ODBC driver.
- Connection pooling on Linux or macOS is not recommended with [unixODBC](http://www.unixodbc.org/) < 2.3.7
- When pooling is enabled in Linux or macOS
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostic information, such as error messages, warnings and informative messages
- due to this unixODBC bug, fetch large data (such as xml, binary) as streams as a workaround. See the examples [here](https://github.com/Microsoft/msphpsql/wiki/Features#pooling)
## 5.9.0-preview1 - 2020-10-02
Updated PECL release packages. Here is the list of updates:

View file

@ -1,13 +1,13 @@
# Linux and macOS Installation Tutorial for the Microsoft Drivers for PHP for SQL Server
The following instructions assume a clean environment and show how to install PHP 7.2+, the Microsoft ODBC driver, the Apache web server, and the Microsoft Drivers for PHP for SQL Server on Ubuntu 16.04, 18.04, and 20.04, RedHat 7 and 8, Debian 9 and 10, Suse 12 and 15, Alpine 3.11 and 3.12, and macOS 10.13, 10.14, and 10.15. These instructions advise installing the drivers using PECL, but you can also download the prebuilt binaries from the [Microsoft Drivers for PHP for SQL Server](https://github.com/Microsoft/msphpsql/releases) Github project page and install them following the instructions in [Loading the Microsoft Drivers for PHP for SQL Server](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver). For an explanation of extension loading and why we do not add the extensions to php.ini, see the section on [loading the drivers](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver#loading-the-driver-at-php-startup).
The following instructions assume a clean environment and show how to install PHP 7.3+, the Microsoft ODBC driver, the Apache web server, and the Microsoft Drivers for PHP for SQL Server on Ubuntu 16.04, 18.04, and 20.04, RedHat 7 and 8, Debian 9 and 10, Suse 12 and 15, Alpine 3.11 and 3.12, and macOS 10.13, 10.14, and 10.15. These instructions advise installing the drivers using PECL, but you can also download the prebuilt binaries from the [Microsoft Drivers for PHP for SQL Server](https://github.com/Microsoft/msphpsql/releases) Github project page and install them following the instructions in [Loading the Microsoft Drivers for PHP for SQL Server](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver). For an explanation of extension loading and why we do not add the extensions to php.ini, see the section on [loading the drivers](https://docs.microsoft.com/sql/connect/php/loading-the-php-sql-driver#loading-the-driver-at-php-startup).
The following instructions install PHP 7.4 by default using `pecl install`. You may need to run `pecl channel-update pecl.php.net` first. Note that some supported Linux distros default to PHP 7.1 or earlier, which is not supported for the latest version of the PHP drivers for SQL Server -- please see the notes at the beginning of each section to install PHP 7.2 or 7.3 instead.
The following instructions install PHP 8.0 by default using `pecl install` if PHP 8.0 packages are available. You may need to run `pecl channel-update pecl.php.net` first. Note that some supported Linux distros default to PHP 7.1 or earlier, which is not supported for the latest version of the PHP drivers for SQL Server -- please see the notes at the beginning of each section to install PHP 7.4 or 7.3 instead.
Also included are instructions for installing the PHP FastCGI Process Manager, PHP-FPM, on Ubuntu. This is needed if using the nginx web server instead of Apache.
## Contents of this page:
- [Installing the drivers on Ubuntu 16.04, 18.04, and 19.10](#installing-the-drivers-on-ubuntu-1604-1804-and-1910)
- [Installing the drivers on Ubuntu 16.04, 18.04, and 20.04](#installing-the-drivers-on-ubuntu-1604-1804-and-2004)
- [Installing the drivers with PHP-FPM on Ubuntu](#installing-the-drivers-with-php-fpm-on-ubuntu)
- [Installing the drivers on Red Hat 7 and 8](#installing-the-drivers-on-red-hat-7-and-8)
- [Installing the drivers on Debian 9 and 10](#installing-the-drivers-on-debian-9-and-10)
@ -15,30 +15,30 @@ Also included are instructions for installing the PHP FastCGI Process Manager, P
- [Installing the drivers on Alpine 3.11 and 3.12](#installing-the-drivers-on-alpine-311-and-312)
- [Installing the drivers on macOS High Sierra, Mojave, and Catalina](#installing-the-drivers-on-macos-high-sierra-mojave-and-catalina)
## Installing the drivers on Ubuntu 16.04, 18.04, and 19.10
## Installing the drivers on Ubuntu 16.04, 18.04, and 20.04
> [!NOTE]
> To install PHP 7.2 or 7.3, replace 7.4 with 7.2 or 7.3 in the following commands.
> To install PHP 7.4 or 7.3, replace 8.0 with 7.4 or 7.3 in the following commands.
### Step 1. Install PHP
```bash
sudo su
add-apt-repository ppa:ondrej/php -y
apt-get update
apt-get install php7.4 php7.4-dev php7.4-xml -y --allow-unauthenticated
apt-get install php8.0 php8.0-dev php8.0-xml -y --allow-unauthenticated
```
### Step 2. Install prerequisites
Install the ODBC driver for Ubuntu by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Ubuntu by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/7.4/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/7.4/mods-available/pdo_sqlsrv.ini
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.0/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.0/mods-available/pdo_sqlsrv.ini
exit
sudo phpenmod -v 7.4 sqlsrv pdo_sqlsrv
sudo phpenmod -v 8.0 sqlsrv pdo_sqlsrv
```
If there is only one PHP version in the system, then the last step can be simplified to `phpenmod sqlsrv pdo_sqlsrv`.
@ -46,10 +46,10 @@ If there is only one PHP version in the system, then the last step can be simpli
### Step 4. Install Apache and configure driver loading
```
sudo su
apt-get install libapache2-mod-php7.4 apache2
apt-get install libapache2-mod-php8.0 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.4
a2enmod php8.0
exit
```
### Step 5. Restart Apache and test the sample script
@ -61,42 +61,42 @@ To test your installation, see [Testing your installation](#testing-your-install
## Installing the drivers with PHP-FPM on Ubuntu
> [!NOTE]
> To install PHP 7.2 or 7.3, replace 7.4 with 7.2 or 7.3 in the following commands.
> To install PHP 7.4 or 7.3, replace 8.0 with 7.4 or 7.3 in the following commands.
### Step 1. Install PHP
```bash
sudo su
add-apt-repository ppa:ondrej/php -y
apt-get update
apt-get install php7.4 php7.4-dev php7.4-xml php7.4-fpm -y --allow-unauthenticated
apt-get install php8.0 php8.0-dev php8.0-xml -y --allow-unauthenticated
```
Verify the status of the PHP-FPM service by running
```
systemctl status php7.4-fpm
systemctl status php8.0-fpm
```
### Step 2. Install prerequisites
Install the ODBC driver for Ubuntu by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Ubuntu by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl config-set php_ini /etc/php/7.3/fpm/php.ini
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl config-set php_ini /etc/php/8.0/fpm/php.ini
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/7.4/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/7.4/mods-available/pdo_sqlsrv.ini
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.0/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.0/mods-available/pdo_sqlsrv.ini
exit
sudo phpenmod -v 7.4 sqlsrv pdo_sqlsrv
sudo phpenmod -v 8.0 sqlsrv pdo_sqlsrv
```
If there is only one PHP version in the system, then the last step can be simplified to `phpenmod sqlsrv pdo_sqlsrv`.
Verify that `sqlsrv.ini` and `pdo_sqlsrv.ini` are located in `/etc/php/7.4/fpm/conf.d/`:
Verify that `sqlsrv.ini` and `pdo_sqlsrv.ini` are located in `/etc/php/8.0/fpm/conf.d/`:
```
ls /etc/php/7.4/fpm/conf.d/*sqlsrv.ini
ls /etc/php/8.0/fpm/conf.d/*sqlsrv.ini
```
Restart the PHP-FPM service:
```
sudo systemctl restart php7.4-fpm
sudo systemctl restart php8.0-fpm
```
### Step 4. Install and configure nginx
@ -116,7 +116,7 @@ Next, modify the section following `# pass PHP scripts to FastCGI server` as fol
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
}
```
### Step 5. Restart nginx and test the sample script
@ -131,40 +131,40 @@ To test your installation, see [Testing your installation](#testing-your-install
To install PHP on Red Hat 7, run the following:
> [!NOTE]
> To install PHP 7.2 or 7.3, replace remi-php74 with remi-php72 or remi-php73 respectively in the following commands.
> To install PHP 7.4 or 7.3, replace remi-php80 with remi-php74 or remi-php73 respectively in the following commands.
```
sudo su
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
subscription-manager repos --enable=rhel-7-server-optional-rpms
yum install yum-utils
yum-config-manager --enable remi-php74
yum-config-manager --enable remi-php80
yum update
yum install php php-pdo php-xml php-pear php-devel re2c gcc-c++ gcc
```
To install PHP on Red Hat 8, run the following:
> [!NOTE]
> To install PHP 7.2 or 7.3, replace remi-7.4 with remi-7.2 or remi-7.3 respectively in the following commands.
> To install PHP 7.4 or 7.3, replace remi-8.0 with remi-7.4 or remi-7.3 respectively in the following commands.
```
sudo su
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf install yum-utils
dnf module reset php
dnf module install php:remi-7.4
dnf module install php:remi-8.0
subscription-manager repos --enable codeready-builder-for-rhel-8-x86_64-rpms
dnf update
dnf install php-pdo php-pear php-devel
```
### Step 2. Install prerequisites
Install the ODBC driver for Red Hat 7 or 8 by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Red Hat 7 or 8 by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/30-pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/20-sqlsrv.ini
@ -192,7 +192,7 @@ To test your installation, see [Testing your installation](#testing-your-install
## Installing the drivers on Debian 9 and 10
> [!NOTE]
> To install PHP 7.2 or 7.3, replace 7.4 in the following commands with 7.2 or 7.3.
> To install PHP 7.4 or 7.3, replace 8.0 in the following commands with 7.4 or 7.3.
### Step 1. Install PHP
```
@ -201,10 +201,10 @@ 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.4 php7.4-dev php7.4-xml php7.4-intl
apt-get install -y php8.0 php8.0-dev php8.0-xml php8.0-intl
```
### Step 2. Install prerequisites
Install the ODBC driver for Debian by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Debian by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
You may also need to generate the correct locale to get PHP output to display correctly in a browser. For example, for the en_US UTF-8 locale, run the following commands:
```
@ -216,13 +216,13 @@ You may need to add `/usr/sbin` to your `$PATH`, as the `locale-gen` executable
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/7.4/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/7.4/mods-available/pdo_sqlsrv.ini
printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.0/mods-available/sqlsrv.ini
printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.0/mods-available/pdo_sqlsrv.ini
exit
sudo phpenmod -v 7.4 sqlsrv pdo_sqlsrv
sudo phpenmod -v 8.0 sqlsrv pdo_sqlsrv
```
If there is only one PHP version in the system, then the last step can be simplified to `phpenmod sqlsrv pdo_sqlsrv`. As with `locale-gen`, `phpenmod` is located in `/usr/sbin` so you may need to add this directory to your `$PATH`.
@ -230,10 +230,10 @@ If there is only one PHP version in the system, then the last step can be simpli
### Step 4. Install Apache and configure driver loading
```
sudo su
apt-get install libapache2-mod-php7.4 apache2
apt-get install libapache2-mod-php8.0 apache2
a2dismod mpm_event
a2enmod mpm_prefork
a2enmod php7.4
a2enmod php8.0
```
### Step 5. Restart Apache and test the sample script
```
@ -244,12 +244,10 @@ To test your installation, see [Testing your installation](#testing-your-install
## Installing the drivers on Suse 12 and 15
> [!NOTE]
> In the following instructions, replace `<SuseVersion>` with your version of Suse - if you are using Suse Enterprise Linux 15, it will be SLE_15 or SLE_15_SP1. For Suse 12, use SLE_12_SP4 (or above if applicable). Not all versions of PHP are available for all versions of Suse Linux - please refer to `http://download.opensuse.org/repositories/devel:/languages:/php` to see which versions of Suse have the default version PHP available, or to `http://download.opensuse.org/repositories/devel:/languages:/php:/` to see which other versions of PHP are available for which versions of Suse.
> In the following instructions, replace `<SuseVersion>` with your version of Suse - if you are using Suse Enterprise Linux 15, it will be SLE_15_SP1 or SLE_15_SP2. For Suse 12, use SLE_12_SP4 (or above if applicable). Not all versions of PHP are available for all versions of Suse Linux - please refer to `http://download.opensuse.org/repositories/devel:/languages:/php` to see which versions of Suse have the default version PHP available, or check `http://download.opensuse.org/repositories/devel:/languages:/php:/` to see which other versions of PHP are available for which versions of Suse.
> [!NOTE]
> Packages for PHP 7.4 are not available for Suse 12.
> To install PHP 7.2, replace the repository URL below with the following URL:
`https://download.opensuse.org/repositories/devel:/languages:/php:/php72/<SuseVersion>/devel:languages:php:php72.repo`.
> Packages for PHP 7.4 or above are not available for Suse 12 and Package for PHP 8.0 is not yet available for Suse 15.
> To install PHP 7.3, replace the repository URL below with the following URL:
`https://download.opensuse.org/repositories/devel:/languages:/php:/php73/<SuseVersion>/devel:languages:php:php73.repo`.
@ -261,15 +259,15 @@ zypper --gpg-auto-import-keys refresh
zypper -n install php7 php7-devel php7-openssl
```
### Step 2. Install prerequisites
Install the ODBC driver for Suse by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Suse by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
### Step 3. Install the PHP drivers for Microsoft SQL Server
> [!NOTE]
> If you get an error message saying `Connection to 'pecl.php.net:443' failed: Unable to find the socket transport "ssl"`, edit the pecl script at /usr/bin/pecl and remove the `-n` switch in the last line. This switch prevents PECL from loading ini files when PHP is called, which prevents the OpenSSL extension from loading.
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/sqlsrv.ini
@ -307,12 +305,12 @@ apk update
apk add php7 php7-dev php7-pear php7-pdo php7-openssl autoconf make g++
```
### Step 2. Install prerequisites
Install the ODBC driver for Alpine by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for Alpine by following the instructions on [Install the Microsoft ODBC driver for SQL Server (Linux)](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15).
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
sudo su
echo extension=pdo_sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/10_pdo_sqlsrv.ini
echo extension=sqlsrv.so >> `php --ini | grep "Scan for additional .ini files" | sed -e "s|.*:\s*||"`/00_sqlsrv.ini
@ -337,22 +335,23 @@ If you do not already have it, install brew as follows:
```
> [!NOTE]
> To install PHP 7.2 or 7.3, replace php@7.4 with php@7.2 or php@7.3 respectively in the following commands.
> To install PHP 7.4 or 7.3, replace php@8.0 with php@7.4 or php@7.3 respectively in the following commands.
### Step 1. Install PHP
```
brew tap
brew tap homebrew/core
brew install php@7.4
brew install php@8.0
```
PHP should now be in your path -- run `php -v` to verify that you are running the correct version of PHP. If PHP is not in your path or it is not the correct version, run the following:
```
brew link --force --overwrite php@7.4
brew link --force --overwrite php@8.0
```
### Step 2. Install prerequisites
Install the ODBC driver for macOS by following the instructions on the [Linux and macOS installation page](https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server).
Install the ODBC driver for macOS by following the instructions on [Install the Microsoft ODBC driver for SQL Server (macOS)](
https://docs.microsoft.com/sql/connect/odbc/linux-mac/install-microsoft-odbc-driver-sql-server-macos?view=sql-server-ver15).
In addition, you may need to install the GNU make tools:
```
@ -361,8 +360,8 @@ brew install autoconf automake libtool
### Step 3. Install the PHP drivers for Microsoft SQL Server
```
sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv
sudo pecl install sqlsrv-5.9.0beta2
sudo pecl install pdo_sqlsrv-5.9.0beta2
```
### Step 4. Install Apache and configure driver loading
```
@ -374,7 +373,7 @@ To find the Apache configuration file, `httpd.conf`, for your Apache installatio
```
The following commands append the required configuration to `httpd.conf`. Be sure to substitute the path returned by the preceding command in place of `/usr/local/etc/httpd/httpd.conf`:
```
echo "LoadModule php7_module /usr/local/opt/php@7.4/lib/httpd/modules/libphp7.so" >> /usr/local/etc/httpd/httpd.conf
echo "LoadModule php7_module /usr/local/opt/php@8.0/lib/httpd/modules/libphp7.so" >> /usr/local/etc/httpd/httpd.conf
(echo "<FilesMatch .php$>"; echo "SetHandler application/x-httpd-php"; echo "</FilesMatch>";) >> /usr/local/etc/httpd/httpd.conf
```
### Step 5. Restart Apache and test the sample script

View file

@ -4,7 +4,7 @@
The Microsoft Drivers for PHP for Microsoft SQL Server are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PHP Data Objects (PDO) for accessing data in all editions of SQL Server 2012 and later (including Azure SQL DB). These drivers rely on the [Microsoft ODBC Driver for SQL Server][odbcdoc] to handle the low-level communication with SQL Server.
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7.2+ with improvements on both drivers and some limitations. Upcoming [releases][releases] will contain additional functionalities, bug fixes, and more.
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7.3+ with improvements on both drivers and some limitations. Upcoming [releases][releases] will contain additional functionalities, bug fixes, and more.
## Take our survey
@ -45,7 +45,7 @@ Azure Pipelines | AppVeyor (Windows) | Travis CI (Linux) | Co
For full details on the system requirements for the drivers, see the [system requirements](https://docs.microsoft.com/sql/connect/php/system-requirements-for-the-php-sql-driver) on Microsoft Docs.
On the client machine:
- PHP 7.2.x (7.2.0 and up on Unix, 7.2.1 and up on Windows), 7.3.x, or 7.4.x
- 7.3.x, 7.4.x, 8.0.x
- [Microsoft ODBC Driver 17, Microsoft ODBC Driver 13, or Microsoft ODBC Driver 11][odbcdoc]
- If using a Web server such as Internet Information Services (IIS) or Apache, it must be configured to run PHP
@ -82,8 +82,8 @@ Given a version number MAJOR.MINOR.PATCH,
The version number may have trailing pre-release version identifiers to indicate the stability and/or build metadata.
- Pre-release version is denoted by a hyphen followed by `preview` or `RC` and may be followed by a series of dot separated identifiers. Production quality releases do not contain the pre-release version. `preview` has lower precedence than `RC`. Example of precedence: *preview < preview.1 < RC < RC.1*. Note that the PECL package version numbers do not have the hyphen before the pre-release version, owing to restrictions in PECL. Example of a PECL package version number: 1.2.3preview
- Build metadata may be denoted by a plus sign followed by 4 or 5 digits, such as `1.2.3-preview+5678` or `1.2.3+5678`. Build metadata does not figure into the precedence order.
- Pre-release version is denoted by a hyphen followed by `beta` or `RC` followed by a number. Production quality releases do not contain the pre-release version. `beta` has lower precedence than `RC`. Note that the PECL package version numbers do not have the hyphen before the pre-release version, owing to restrictions in PECL. An example of a PECL package version is `5.9.0beta2`.
- Build metadata may be denoted by a plus sign followed by a number of digits, such as `5.9.0-beta2+13930`. Build metadata does not affect the precedence order.
## Future Plans
- Expand SQL Server feature support (example: Azure Active Directory, Always Encrypted, etc.)

View file

@ -30,7 +30,7 @@ environment:
SQL_INSTANCE: SQL2019
PHP_VC: vs16
PHP_MAJOR_VER: 8.0
PHP_MINOR_VER: 0rc1
PHP_MINOR_VER: 0RC2
PHP_EXE_PATH: Release
THREAD: nts
platform: x86

View file

@ -13,6 +13,9 @@ variables:
trigger:
- dev
pr:
- dev
jobs:
- job: macOS
pool:
@ -30,8 +33,8 @@ jobs:
- script: |
brew tap
brew tap homebrew/core
brew install autoconf automake libtool
brew install php@$(phpVersion)
brew reinstall autoconf automake libtool
brew reinstall php@$(phpVersion)
php -v
displayName: 'Install PHP'
@ -60,7 +63,7 @@ jobs:
- job: Linux
variables:
phpver: 7.3
phpver: 7.4
pool:
vmImage: 'ubuntu-18.04'
steps:
@ -102,8 +105,15 @@ jobs:
displayName: 'Install prerequisites'
- script: |
docker pull mcr.microsoft.com/mssql/server:2017-latest
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=$(pwd)' -p 1433:1433 -h $(host) --name=$(host) -d mcr.microsoft.com/mssql/server:2017-latest
python -m pip install --upgrade pip
python -m pip install --upgrade requests
python -m pip install PyYAML
python -m pip install cpp-coveralls
displayName: 'Install coveralls (upgrade both pip and requests first)'
- script: |
docker pull mcr.microsoft.com/mssql/server:2019-latest
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=$(pwd)' -p 1433:1433 -h $(host) --name=$(host) -d mcr.microsoft.com/mssql/server:2019-latest
docker ps -a
sleep 10
docker exec -t $(host) /opt/mssql-tools/bin/sqlcmd -S $(server) -U $(uid) -P $(pwd) -Q 'select @@Version'
@ -144,7 +154,7 @@ jobs:
cd $(Build.SourcesDirectory)/source/sqlsrv
ls -al
phpize && ./configure && make && sudo make install
phpize && ./configure LDFLAGS="-lgcov" CXXFLAGS="-O0 --coverage" && make && sudo make install
cp run-tests.php $(Build.SourcesDirectory)/test/functional/sqlsrv
echo extension=sqlsrv.so >> 20-sqlsrv.ini
@ -153,7 +163,7 @@ jobs:
cd $(Build.SourcesDirectory)/source/pdo_sqlsrv
ls -al
phpize && ./configure && make && sudo make install
phpize && ./configure LDFLAGS="-lgcov" CXXFLAGS="-O0 --coverage" && make && sudo make install
cp run-tests.php $(Build.SourcesDirectory)/test/functional/pdo_sqlsrv
echo extension=pdo_sqlsrv.so >> 30-pdo_sqlsrv.ini
@ -189,6 +199,17 @@ jobs:
php run-tests.php -P ./*.phpt 2>&1 | tee ../pdo_sqlsrv.log
displayName: 'Run pdo_sqlsrv functional tests'
- script: |
cd $(Build.SourcesDirectory)
echo -e "service_name: Azure Pipelines\n" > .coveralls.yml
coveralls -i ./source/ -e ./source/shared/ -e ./test/ -e ./source/pdo_sqlsrv/shared/core_stream.cpp \
-E r'.*localization*' -E r'.*globalization*' --gcov-options '\-lp'
displayName: 'Invoke coveralls using repo token'
env:
COVERALLS_REPO_TOKEN: $(repo_token)
TRAVIS_JOB_ID: $(Build.BuildId)
TRAVIS_BRANCH: $(Build.SourceBranchName)
- script: |
cd $(Build.SourcesDirectory)/test/functional/
for f in sqlsrv/*.diff; do ls $f 2>/dev/null; cat $f 2>/dev/null; echo ''; done || true

View file

@ -79,7 +79,6 @@ if test "$PHP_PDO_SQLSRV" != "no"; then
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
PDO_SQLSRV_SHARED_LIBADD="$PDO_SQLSRV_SHARED_LIBADD -Wl,-bind_at_load"
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
else
PDO_SQLSRV_SHARED_LIBADD="$PDO_SQLSRV_SHARED_LIBADD -Wl,-z,now"
IS_ALPINE_1=`uname -a | cut -f 4 -d ' ' | cut -f 2 -d '-'`

View file

@ -912,6 +912,8 @@ int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh )
DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred.");
}
// Should not have reached here but adding this due to compilation warnings
return 0;
}
@ -954,6 +956,8 @@ int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh )
DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred.");
}
// Should not have reached here but adding this due to compilation warnings
return 0;
}
// pdo_sqlsrv_dbh_rollback
@ -993,6 +997,8 @@ int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh )
DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred.");
}
// Should not have reached here but adding this due to compilation warnings
return 0;
}
// pdo_sqlsrv_dbh_set_attr

View file

@ -116,7 +116,7 @@ void pdo_error_dtor( _Inout_ zval* elem ) {
PHP_MINIT_FUNCTION(pdo_sqlsrv)
{
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( type );
// our global variables are initialized in the RINIT function
#if defined(ZTS)
@ -185,7 +185,7 @@ PHP_MSHUTDOWN_FUNCTION(pdo_sqlsrv)
{
try {
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( type );
UNREGISTER_INI_ENTRIES();
@ -213,8 +213,8 @@ PHP_MSHUTDOWN_FUNCTION(pdo_sqlsrv)
PHP_RINIT_FUNCTION(pdo_sqlsrv)
{
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( module_number );
// SQLSRV_UNUSED( type );
#if defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
@ -247,8 +247,8 @@ PHP_RINIT_FUNCTION(pdo_sqlsrv)
PHP_RSHUTDOWN_FUNCTION(pdo_sqlsrv)
{
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( module_number );
// SQLSRV_UNUSED( type );
PDO_LOG_NOTICE("pdo_sqlsrv: entering rshutdown");

View file

@ -187,7 +187,7 @@ void conn_string_parser::add_key_value_pair( _In_reads_(len) const char* value,
memcpy_s( option, len + 1, value, len );
option[len] = '\0';
valid = core_is_authentication_option_valid( option, len );
valid = AzureADOptions::isAuthValid(option, len);
}
}
if( !valid ) {

View file

@ -724,6 +724,8 @@ int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orienta
DIE ("pdo_sqlsrv_stmt_fetch: Unexpected exception occurred.");
}
// Should not have reached here but adding this due to compilation warnings
return 0;
}
// pdo_sqlsrv_stmt_get_col_data
@ -832,15 +834,16 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
*len = sizeof(zval);
}
return 1;
}
return 1;
}
catch ( core::CoreException& ) {
return 0;
}
catch ( ... ) {
DIE ("pdo_sqlsrv_stmt_get_col_data: Unexpected exception occurred.");
}
// Should not have reached here but adding this due to compilation warnings
return 0;
}
// pdo_sqlsrv_stmt_set_attr

View file

@ -379,7 +379,7 @@ pdo_error PDO_ERRORS[] = {
},
{
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, or ActiveDirectoryMsi is supported.", -73, false }
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectorySPA is supported.", -73, false }
},
{
SQLSRV_ERROR_CE_DRIVER_REQUIRED,
@ -465,7 +465,7 @@ pdo_error PDO_ERRORS[] = {
{ UINT_MAX, {} }
};
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args )
{
SQLSRV_ASSERT((ctx != NULL), "pdo_sqlsrv_handle_env_error: sqlsrv_context was null");
@ -488,7 +488,7 @@ bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned
}
// pdo error handler for the dbh context.
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args )
{
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>( ctx.driver());
@ -520,7 +520,7 @@ bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned
}
// PDO error handler for the statement context.
bool pdo_sqlsrv_handle_stmt_error(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_stmt_error(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args)
{
pdo_stmt_t* pdo_stmt = reinterpret_cast<pdo_stmt_t*>(ctx.driver());
@ -635,20 +635,9 @@ void add_remaining_errors_to_array (_In_ sqlsrv_error const* error, _Inout_ zval
if (error->next != NULL && PDO_SQLSRV_G(report_additional_errors)) {
sqlsrv_error *p = error->next;
while (p != NULL) {
// check if sql state or native message is NULL and handle them accordingly
char *state = "";
char *msg = "";
if (p->sqlstate != NULL) {
state = reinterpret_cast<char*>(p->sqlstate);
}
if (p->native_message != NULL) {
msg = reinterpret_cast<char*>(p->native_message);
}
add_next_index_string(array_z, state);
add_next_index_string(array_z, reinterpret_cast<char*>(p->sqlstate));
add_next_index_long(array_z, p->native_code);
add_next_index_string(array_z, msg);
add_next_index_string(array_z, reinterpret_cast<char*>(p->native_message));
p = p-> next;
}

View file

@ -285,11 +285,11 @@ struct pdo_error {
// called when an error occurs in the core layer. These routines are set as the error_callback in a
// context. The context is passed to this function since it contains the function
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
// common routine to transfer a sqlsrv_context's error to a PDO zval

View file

@ -709,18 +709,38 @@ bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t v
return true;
}
// core_is_authentication_option_valid
// if the option for the authentication is valid, returns true. This returns false otherwise.
bool core_is_authentication_option_valid( _In_z_ const char* value, _In_ size_t value_len)
{
if (value_len <= 0)
return false;
namespace AzureADOptions {
enum AAD_AUTH_TYPE {
MIN_AAD_AUTH_TYPE = 0,
SQL_PASSWORD = 0,
AAD_PASSWORD,
AAD_MSI,
AAD_SPA,
MAX_AAD_AUTH_TYPE
};
if (!stricmp(value, AzureADOptions::AZURE_AUTH_SQL_PASSWORD) || !stricmp(value, AzureADOptions::AZURE_AUTH_AD_PASSWORD) || !stricmp(value, AzureADOptions::AZURE_AUTH_AD_MSI)) {
return true;
const char *AADAuths[] = { "SqlPassword", "ActiveDirectoryPassword", "ActiveDirectoryMsi", "ActiveDirectorySPA" };
bool isAuthValid(_In_z_ const char* value, _In_ size_t value_len)
{
if (value_len <= 0)
return false;
bool isValid = false;
for (short i = MIN_AAD_AUTH_TYPE; i < MAX_AAD_AUTH_TYPE && !isValid; i++)
{
if (!stricmp(value, AADAuths[i])) {
isValid = true;
}
}
return isValid;
}
return false;
bool isAADMsi(_In_z_ const char* value)
{
return (value != NULL && !stricmp(value, AADAuths[AAD_MSI]));
}
}
@ -789,9 +809,9 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
option = Z_STRVAL_P(auth_option);
}
if (option != NULL && !stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
activeDirectoryMSI = true;
//if (option != NULL && !stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
activeDirectoryMSI = AzureADOptions::isAADMsi(option);
if (activeDirectoryMSI) {
// There are two types of managed identities:
// (1) A system-assigned managed identity: UID must be NULL
// (2) A user-assigned managed identity: UID defined but must not be an empty string

View file

@ -882,7 +882,7 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
_In_ SQLLEN buffer_length, _Inout_ SQLLEN* out_buffer_length,
_Inout_ sqlsrv_error_auto_ptr& out_error )
{
// hex characters for the conversion loop below
// The hex characters for the conversion loop below
static char hex_chars[] = "0123456789ABCDEF";
SQLSRV_ASSERT( out_error == 0, "Pending error for sqlsrv_buffered_results_set::binary_to_string" );
@ -892,17 +892,19 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
// Set the amount of space necessary for null characters at the end of the data.
SQLSMALLINT extra = sizeof(Char);
SQLSRV_ASSERT( ((buffer_length - extra) % (extra * 2)) == 0, "Must be multiple of 2 for binary to system string or "
"multiple of 4 for binary to wide string" );
// TO convert a binary to a system string or a binary to a wide string, the buffer size minus
// 'extra' is ideally multiples of 2 or 4 (depending on Char), but calculating to_copy_hex below
// takes care of this.
// all fields will be treated as ODBC returns varchar(max) fields:
// All fields will be treated as ODBC returns varchar(max) fields:
// the entire length of the string is returned the first
// call in out_buffer_len. Successive calls return how much is
// left minus how much has already been read by previous reads
// *2 is for each byte to hex conversion and * extra is for either system or wide string allocation
// *2 is for each byte to hex conversion and * extra is for either system
// or wide string allocation
*out_buffer_length = (*reinterpret_cast<SQLLEN*>( field_data - sizeof( SQLULEN )) - read_so_far) * 2 * extra;
// copy as much as we can into the buffer
// Will copy as much as we can into the buffer
SQLLEN to_copy;
if( buffer_length < *out_buffer_length + extra ) {
to_copy = (buffer_length - extra);
@ -915,14 +917,14 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
to_copy = *out_buffer_length;
}
// if there are bytes to copy as hex
// If there are bytes to copy as hex
if( to_copy > 0 ) {
// quick hex conversion routine
Char* h = reinterpret_cast<Char*>( buffer );
BYTE* b = reinterpret_cast<BYTE*>( field_data );
Char* h = reinterpret_cast<Char*>(buffer);
BYTE* b = reinterpret_cast<BYTE*>(field_data + read_so_far);
// to_copy contains the number of bytes to copy, so we divide the number in half (or quarter)
// to get the number of hex digits we can copy
SQLLEN to_copy_hex = to_copy / (2 * extra);
// to get the maximum number of hex digits to copy
SQLLEN to_copy_hex = static_cast<SQLLEN>(floor(to_copy / (2 * extra)));
for( SQLLEN i = 0; i < to_copy_hex; ++i ) {
*h = hex_chars[(*b & 0xf0) >> 4];
h++;
@ -930,7 +932,7 @@ SQLRETURN binary_to_string( _Inout_ SQLCHAR* field_data, _Inout_ SQLLEN& read_so
h++;
}
read_so_far += to_copy_hex;
*h = static_cast<Char>( 0 );
*h = static_cast<Char>(0);
}
else {
reinterpret_cast<char*>( buffer )[0] = '\0';
@ -1066,7 +1068,7 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_double( _In_ SQLSMALLINT field_i
double* number_data = reinterpret_cast<double*>(buffer);
try {
*number_data = std::stod(std::string(string_data));
} catch (const std::logic_error& err) {
} catch (const std::logic_error& ) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
@ -1091,7 +1093,7 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( _In_ SQLSMALLINT field_
#else
*number_data = std::stod(getUTF8StringFromString(string_data));
#endif // _WIN32
} catch (const std::logic_error& err) {
} catch (const std::logic_error& ) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
@ -1112,7 +1114,7 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_long( _In_ SQLSMALLINT field_ind
LONG* number_data = reinterpret_cast<LONG*>(buffer);
try {
*number_data = std::stol(std::string(string_data));
} catch (const std::logic_error& err) {
} catch (const std::logic_error& ) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}
@ -1137,7 +1139,7 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( _In_ SQLSMALLINT field_in
#else
*number_data = std::stol(getUTF8StringFromString(string_data));
#endif // _WIN32
} catch (const std::logic_error& err) {
} catch (const std::logic_error& ) {
last_error = new (sqlsrv_malloc(sizeof(sqlsrv_error))) sqlsrv_error((SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103);
return SQL_ERROR;
}

View file

@ -191,9 +191,8 @@ const int SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION = 34;
const int SQL_SERVER_2008_DEFAULT_DATETIME_SCALE = 7;
namespace AzureADOptions {
const char AZURE_AUTH_SQL_PASSWORD[] = "SqlPassword";
const char AZURE_AUTH_AD_PASSWORD[] = "ActiveDirectoryPassword";
const char AZURE_AUTH_AD_MSI[] = "ActiveDirectoryMsi";
bool isAuthValid(_In_z_ const char* value, _In_ size_t value_len);
bool isAADMsi(_In_z_ const char* value);
}
// the message returned by ODBC Driver for SQL Server
@ -877,7 +876,7 @@ struct sqlsrv_conn;
// a driver specific callback for processing errors.
// ctx - the context holding the handles
// sqlsrv_error_code - specific error code to return.
typedef bool (*error_callback)( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool error, _In_opt_ va_list* print_args );
typedef bool (*error_callback)( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ int error, _In_opt_ va_list* print_args );
// sqlsrv_context
// a context holds relevant information to be passed with a connection and statement objects.
@ -1288,7 +1287,6 @@ void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval *se
void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info );
bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t value_len );
size_t core_str_zval_is_true( _Inout_ zval* str_zval );
bool core_is_authentication_option_valid( _In_z_ const char* value, _In_ size_t value_len );
bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version );
bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN r, _In_ const char* error_state );
@ -1933,7 +1931,7 @@ DWORD core_sqlsrv_format_message( _Out_ char* output_buffer, _In_ unsigned outpu
// convenience functions that overload either a reference or a pointer so we can use
// either in the CHECK_* functions.
inline bool call_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned long sqlsrv_error_code, _In_ bool warning, ... )
inline bool call_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned long sqlsrv_error_code, _In_ int warning, ... )
{
va_list print_params;
va_start( print_params, warning );
@ -1942,7 +1940,7 @@ inline bool call_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned long
return ignored;
}
inline bool call_error_handler( _Inout_ sqlsrv_context* ctx, _In_ unsigned long sqlsrv_error_code, _In_ bool warning, ... )
inline bool call_error_handler( _Inout_ sqlsrv_context* ctx, _In_ unsigned long sqlsrv_error_code, _In_ int warning, ... )
{
va_list print_params;
va_start( print_params, warning );
@ -1987,7 +1985,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
bool flag##unique = (condition); \
bool ignored##unique = true; \
if (flag##unique) { \
ignored##unique = call_error_handler( context, ssphp, /*warning*/false, ## __VA_ARGS__ ); \
ignored##unique = call_error_handler( context, ssphp, /*warning*/0, ## __VA_ARGS__ ); \
} \
if( !ignored##unique )
@ -2007,7 +2005,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
#define CHECK_WARNING_AS_ERROR_UNIQUE( unique, condition, context, ssphp, ... ) \
bool ignored##unique = true; \
if( condition ) { \
ignored##unique = call_error_handler( context, ssphp, /*warning*/true, ## __VA_ARGS__ ); \
ignored##unique = call_error_handler( context, ssphp, /*warning*/1, ## __VA_ARGS__ ); \
} \
if( !ignored##unique )
@ -2016,7 +2014,7 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
#define CHECK_SQL_WARNING( result, context, ... ) \
if( result == SQL_SUCCESS_WITH_INFO ) { \
(void)call_error_handler( context, 0, /*warning*/ true, ## __VA_ARGS__ ); \
(void)call_error_handler( context, 0, /*warning*/1, ## __VA_ARGS__ ); \
}
#define CHECK_CUSTOM_WARNING_AS_ERROR( condition, context, ssphp, ... ) \
@ -2029,16 +2027,16 @@ inline bool is_truncated_warning( _In_ SQLCHAR* state )
SQLSRV_ASSERT( result != SQL_INVALID_HANDLE, "Invalid handle returned." ); \
bool ignored = true; \
if( result == SQL_ERROR ) { \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, false, ##__VA_ARGS__ ); \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, 0, ##__VA_ARGS__ ); \
} \
else if( result == SQL_SUCCESS_WITH_INFO ) { \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, true, ##__VA_ARGS__ ); \
ignored = call_error_handler( context, SQLSRV_ERROR_ODBC, 1, ##__VA_ARGS__ ); \
} \
if( !ignored )
// throw an exception after it has been hooked into the custom error handler
#define THROW_CORE_ERROR( ctx, custom, ... ) \
(void)call_error_handler( ctx, custom, /*warning*/ false, ## __VA_ARGS__ ); \
(void)call_error_handler( ctx, custom, /*warning*/0, ## __VA_ARGS__ ); \
throw core::CoreException();
//*********************************************************************************************************************************

View file

@ -110,7 +110,7 @@ void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_
_Out_ SQLSMALLINT& sql_type );
void col_cache_dtor( _Inout_ zval* data_z );
void field_cache_dtor( _Inout_ zval* data_z );
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ short decimal_pos, _In_ short decimals_places, _In_ short offset, _In_ short lastpos);
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ int decimal_pos, _In_ int decimals_places, _In_ int offset, _In_ int lastpos);
void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT field_scale, _Inout_updates_bytes_(*field_len) char*& field_value, _Inout_ SQLLEN* field_len);
void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt, _In_opt_ bool exception_thrown = false );
void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _Inout_ sqlsrv_phptype sqlsrv_php_type,
@ -2355,8 +2355,8 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
}
char buffer[50] = " "; // A buffer with two blank spaces, as leeway
short offset = 1 + is_negative;
short src_length = strlen(src);
int offset = 1 + is_negative;
int src_length = strlen(src);
if (add_leading_zero) {
buffer[offset++] = '0';
@ -2368,7 +2368,7 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
// If no need to adjust decimal places, skip formatting
if (decimals_places != NO_CHANGE_DECIMAL_PLACES) {
short num_decimals = src_length - (pt - src) - 1;
int num_decimals = src_length - (pt - src) - 1;
if (num_decimals > scale) {
last_pos = round_up_decimal_numbers(buffer, (pt - src) + offset, scale, offset, last_pos);
@ -2385,7 +2385,7 @@ void format_decimal_numbers(_In_ SQLSMALLINT decimals_places, _In_ SQLSMALLINT f
buffer[--offset] = '-';
}
short len = last_pos - offset;
int len = last_pos - offset;
memcpy_s(field_value, len, buffer + offset, len);
field_value[len] = '\0';
*field_len = len;
@ -3007,7 +3007,7 @@ void sqlsrv_stream_dtor( _Inout_ zval* data )
void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digits)
{
char* value = Z_STRVAL_P(param_z);
short value_len = Z_STRLEN_P(param_z);
int value_len = Z_STRLEN_P(param_z);
// If the length is greater than maxDecimalStrLen, do not convert the string
// 6 is derived from: 1 for the decimal point; 1 for sign of the number; 1 for 'e' or 'E' (scientific notation);
@ -3023,7 +3023,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
try {
d = std::stold(std::string(value), &idx);
}
catch (const std::logic_error& err) {
catch (const std::logic_error& ) {
return; // invalid input caused the conversion to throw an exception
}
if (idx < value_len) {
@ -3047,15 +3047,15 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
char *pt = strchr(src, DECIMAL_POINT);
char buffer[50] = " "; // A buffer with 2 blank spaces, as leeway
short offset = 1 + is_negative; // The position to start copying the original numerical value
int offset = 1 + is_negative; // The position to start copying the original numerical value
if (exp == NULL) {
if (pt == NULL) {
return; // decimal point not found
}
short src_length = strlen(src);
short num_decimals = src_length - (pt - src) - 1;
int src_length = strlen(src);
int num_decimals = src_length - (pt - src) - 1;
if (num_decimals <= decimal_digits) {
return; // no need to adjust number of decimals
}
@ -3069,10 +3069,10 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
return; // Out of range, so let the server handle this
}
short num_decimals = 0;
int num_decimals = 0;
if (power == 0) {
// Simply chop off the exp part
short length = (exp - src);
int length = (exp - src);
memcpy_s(buffer + offset, length, src, length);
if (pt != NULL) {
@ -3083,7 +3083,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
}
}
} else {
short oldpos = 0;
int oldpos = 0;
if (pt == NULL) {
oldpos = exp - src; // Decimal point not found, use the exp sign
}
@ -3096,7 +3096,7 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
}
// Derive the new position for the decimal point in the buffer
short newpos = oldpos + power;
int newpos = oldpos + power;
if (power > 0) {
newpos = newpos + offset;
if (num_decimals == 0) {
@ -3125,8 +3125,8 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
// Start copying the content to the buffer until the exp sign or one more digit after decimal_digits
char *p = src;
short idx = offset;
short lastpos = newpos + decimal_digits + 1;
int idx = offset;
int lastpos = newpos + decimal_digits + 1;
while (p != exp && idx <= lastpos) {
if (*p == DECIMAL_POINT) {
p++;
@ -3156,14 +3156,14 @@ void adjustDecimalPrecision(_Inout_ zval* param_z, _In_ SQLSMALLINT decimal_digi
ZVAL_NEW_STR(param_z, zstr);
}
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ short decimal_pos, _In_ short num_decimals, _In_ short offset, _In_ short lastpos)
int round_up_decimal_numbers(_Inout_ char* buffer, _In_ int decimal_pos, _In_ int num_decimals, _In_ int offset, _In_ int lastpos)
{
// This helper method assumes the 'buffer' has some extra blank spaces at the beginning without the minus '-' sign.
// We want the rounding to be consistent with php number_format(), http://php.net/manual/en/function.number-format.php
// as well as SQL Server Management studio, such that the least significant digit will be rounded up if it is
// followed by 5 or above.
short pos = decimal_pos + num_decimals + 1;
int pos = decimal_pos + num_decimals + 1;
if (pos < lastpos) {
short n = buffer[pos] - '0';
if (n >= 5) {

View file

@ -101,13 +101,18 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
throw core::CoreException();
}
// if the stream returns either no data, NULL data, or returns data < than the count requested then
// we are at the "end of the stream" so we mark it
if( r == SQL_NO_DATA || read == SQL_NULL_DATA || ( static_cast<size_t>( read ) <= count && read != SQL_NO_TOTAL )) {
// If the stream returns no data or NULL data, mark the "end of the stream" and return
if( r == SQL_NO_DATA || read == SQL_NULL_DATA) {
stream->eof = 1;
return 0;
}
// If the stream returns data less than the count requested then we are at the "end of the stream" but continue processing
if (static_cast<size_t>(read) <= count && read != SQL_NO_TOTAL) {
stream->eof = 1;
}
// if ODBC returns the 01004 (truncated string) warning, then we return the count minus the null terminator
// If ODBC returns the 01004 (truncated string) warning, then we return the count minus the null terminator
// if it's not a binary encoded field
if( r == SQL_SUCCESS_WITH_INFO ) {
@ -120,26 +125,42 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
SQLSRV_ASSERT( is_truncated_warning( state ), "sqlsrv_stream_read: truncation warning was expected but it "
"did not occur." );
}
// with unixODBC connection pooling enabled the truncated state may not be returned so check the actual length read
// with buffer length.
// As per SQLGetData documentation, if the length of character data exceeds the BufferLength,
// SQLGetData truncates the data to BufferLength less the length of null-termination character.
// But when fetching binary fields as chars (wide chars), each byte is represented as 2 hex characters,
// each takes the size of a char (wide char). Note that BufferLength may not be multiples of 2 or 4.
bool is_binary = (ss->sql_type == SQL_BINARY || ss->sql_type == SQL_VARBINARY || ss->sql_type == SQL_LONGVARBINARY);
// With unixODBC connection pooling enabled the truncated state may not be returned so check the actual length read
// with buffer length.
#ifndef _WIN32
if( is_truncated_warning( state ) || count < read) {
#else
if( is_truncated_warning( state ) ) {
#endif // !_WIN32
size_t char_size = sizeof(SQLCHAR);
switch( c_type ) {
// As per SQLGetData documentation, if the length of character data exceeds the BufferLength,
// SQLGetData truncates the data to BufferLength less the length of null-termination character.
case SQL_C_BINARY:
read = count;
break;
case SQL_C_WCHAR:
read = ( count % 2 == 0 ? count - 2 : count - 3 );
char_size = sizeof(SQLWCHAR);
if (is_binary) {
// Each binary byte read will be 2 hex wide chars in the buffer
SQLLEN num_bytes_read = static_cast<SQLLEN>(floor((count - char_size) / (2 * char_size)));
read = num_bytes_read * char_size * 2 ;
} else {
read = (count % 2 == 0 ? count - 2 : count - 3);
}
break;
case SQL_C_CHAR:
read = count - 1;
if (is_binary) {
read = ((count - char_size) % 2 == 0 ? count - char_size : count - char_size - 1);
} else {
read = count - 1;
}
break;
default:
DIE( "sqlsrv_stream_read: should have never reached in this switch case.");
@ -151,10 +172,10 @@ size_t sqlsrv_stream_read(_Inout_ php_stream* stream, _Out_writes_bytes_(count)
}
}
// if the encoding is UTF-8
// If the encoding is UTF-8
if( c_type == SQL_C_WCHAR ) {
count *= 2;
// undo the shift to use the full buffer
// Undo the shift to use the full buffer
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
// convert to UTF-8

View file

@ -469,7 +469,7 @@ size_t SystemLocale::Utf8To16( const char *src, SSIZE_T cchSrc, WCHAR *dest, siz
}
usrc++;
ucode = (ucode&15)<<12 | (c1&0x3F)<<6 | (c2&0x3F);
if (ucode < 0x800 || ucode >= 0xD800 && ucode <= 0xDFFF)
if (ucode < 0x800 || (ucode >= 0xD800 && ucode <= 0xDFFF))
{
goto Invalid;
}
@ -511,7 +511,7 @@ size_t SystemLocale::Utf8To16( const char *src, SSIZE_T cchSrc, WCHAR *dest, siz
if (ucode < 0x10000 // overlong encoding
|| ucode > 0x10FFFF // exceeds Unicode range
|| ucode >= 0xD800 && ucode <= 0xDFFF) // surrogate pairs
|| (ucode >= 0xD800 && ucode <= 0xDFFF)) // surrogate pairs
{
goto Invalid;
}
@ -594,7 +594,7 @@ size_t SystemLocale::Utf8To16Strict( const char *src, SSIZE_T cchSrc, WCHAR *des
}
usrc++;
ucode = (ucode&15)<<12 | (c1&0x3F)<<6 | (c2&0x3F);
if (ucode < 0x800 || ucode >= 0xD800 && ucode <= 0xDFFF)
if (ucode < 0x800 || (ucode >= 0xD800 && ucode <= 0xDFFF))
{
goto Invalid;
}
@ -636,7 +636,7 @@ size_t SystemLocale::Utf8To16Strict( const char *src, SSIZE_T cchSrc, WCHAR *des
if (ucode < 0x10000 // overlong encoding
|| ucode > 0x10FFFF // exceeds Unicode range
|| ucode >= 0xD800 && ucode <= 0xDFFF) // surrogate pairs
|| (ucode >= 0xD800 && ucode <= 0xDFFF)) // surrogate pairs
{
goto Invalid;
}
@ -794,7 +794,7 @@ size_t SystemLocale::Utf8From16( const WCHAR *src, SSIZE_T cchSrc, char *dest, s
return 0;
}
*dest++ = 0xE0 | (wch >> 12);
*dest++ = 0x80 | (wch >> 6)&0x3F;
*dest++ = 0x80 | ((wch >> 6)&0x3F);
*dest++ = 0x80 | (wch &0x3F);
}
else if (wch < 0xDC00) // 65536 to end of Unicode: 4 bytes
@ -832,9 +832,9 @@ size_t SystemLocale::Utf8From16( const WCHAR *src, SSIZE_T cchSrc, char *dest, s
return 0;
}
*dest++ = 0xF0 | (wch >> 18);
*dest++ = 0x80 | (wch >>12)&0x3F;
*dest++ = 0x80 | (wch >> 6)&0x3F;
*dest++ = 0x80 | wch&0x3F;
*dest++ = 0x80 | ((wch >>12)&0x3F);
*dest++ = 0x80 | ((wch >> 6)&0x3F);
*dest++ = 0x80 | (wch&0x3F);
}
else // unexpected trail surrogate
{
@ -933,7 +933,7 @@ size_t SystemLocale::Utf8From16Strict( const WCHAR *src, SSIZE_T cchSrc, char *d
return 0;
}
*dest++ = 0xE0 | (wch >> 12);
*dest++ = 0x80 | (wch >> 6)&0x3F;
*dest++ = 0x80 | ((wch >> 6)&0x3F);
*dest++ = 0x80 | (wch &0x3F);
}
else if (wch < 0xDC00) // 65536 to end of Unicode: 4 bytes
@ -964,9 +964,9 @@ size_t SystemLocale::Utf8From16Strict( const WCHAR *src, SSIZE_T cchSrc, char *d
return 0;
}
*dest++ = 0xF0 | (wch >> 18);
*dest++ = 0x80 | (wch >>12)&0x3F;
*dest++ = 0x80 | (wch >> 6)&0x3F;
*dest++ = 0x80 | wch&0x3F;
*dest++ = 0x80 | ((wch >>12)&0x3F);
*dest++ = 0x80 | ((wch >> 6)&0x3F);
*dest++ = 0x80 | (wch&0x3F);
}
else // unexpected trail surrogate
{

View file

@ -31,7 +31,7 @@
#define SQLVERSION_BUILD 0
// For previews, set this constant to 1, 2 and so on. Otherwise, set it to 0
#define PREVIEW 1
#define PREVIEW 2
#define SEMVER_PRERELEASE
// Semantic versioning build metadata, build meta data is not counted in precedence order.
@ -52,7 +52,7 @@
// "preview" for ETP
#if PREVIEW > 0
#undef SEMVER_PRERELEASE
#define SEMVER_PRERELEASE "preview" STRINGIFY(PREVIEW)
#define SEMVER_PRERELEASE "beta" STRINGIFY(PREVIEW)
#define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA
#else
#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA
@ -61,7 +61,7 @@
#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD
// PECL package version macros ('-' or '+' is not allowed)
#define PHP_SQLSRV_VERSION VER_APIVERSION_STR SEMVER_PRERELEASE
#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION
#define PHP_SQLSRV_VERSION "5.9.0beta2"
#define PHP_PDO_SQLSRV_VERSION "5.9.0beta2"
#endif // VERSION_H

View file

@ -59,7 +59,6 @@ if test "$PHP_SQLSRV" != "no"; then
HOST_OS_ARCH=`uname`
if test "${HOST_OS_ARCH}" = "Darwin"; then
SQLSRV_SHARED_LIBADD="$SQLSRV_SHARED_LIBADD -Wl,-bind_at_load"
MACOSX_DEPLOYMENT_TARGET=`sw_vers -productVersion`
else
SQLSRV_SHARED_LIBADD="$SQLSRV_SHARED_LIBADD -Wl,-z,now"
IS_ALPINE_1=`uname -a | cut -f 4 -d ' ' | cut -f 2 -d '-'`

View file

@ -1383,7 +1383,7 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
bool valid = true;
if( stricmp( SS_CONN_OPTS[i].sqlsrv_name, SSConnOptionNames::Authentication ) == 0 ) {
valid = core_is_authentication_option_valid( value, value_len );
valid = AzureADOptions::isAuthValid(value, value_len);
}
CHECK_CUSTOM_ERROR( !valid, ctx, SS_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, SS_CONN_OPTS[i].sqlsrv_name ) {
@ -1393,7 +1393,10 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
break;
}
}
case CONN_ATTR_INVALID:
SQLSRV_ASSERT(false, "Should not have reached CONN_ATTR_INVALID.");
break;
}
return SS_CONN_OPTS[i].conn_option_key;
}

View file

@ -271,7 +271,7 @@ zend_module_entry g_sqlsrv_module_entry =
PHP_MINIT_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( type );
core_sqlsrv_register_severity_checker(ss_severity_check);
@ -596,7 +596,7 @@ void sqlsrv_encoding_dtor( _Inout_ zval* elem ) {
PHP_MSHUTDOWN_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( type );
UNREGISTER_INI_ENTRIES();
@ -630,8 +630,8 @@ PHP_MSHUTDOWN_FUNCTION(sqlsrv)
PHP_RINIT_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( module_number );
// SQLSRV_UNUSED( type );
#if defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
@ -690,8 +690,8 @@ PHP_RINIT_FUNCTION(sqlsrv)
PHP_RSHUTDOWN_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
// SQLSRV_UNUSED( module_number );
// SQLSRV_UNUSED( type );
LOG_FUNCTION( "PHP_RSHUTDOWN for php_sqlsrv" );
reset_errors();

View file

@ -212,7 +212,7 @@ enum SS_ERROR_CODES {
extern ss_error SS_ERRORS[];
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args );
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args );
// convert from the default encoding specified by the "CharacterSet"
// connection option to UTF-16. mbcs_len and utf16_len are sizes in
@ -258,7 +258,7 @@ inline void reset_errors( void )
}
#define THROW_SS_ERROR( ctx, error_code, ... ) \
(void)call_error_handler( ctx, error_code, false /*warning*/, ## __VA_ARGS__ ); \
(void)call_error_handler( ctx, error_code, 0 /*warning*/, ## __VA_ARGS__ ); \
throw ss::SSException();
@ -360,7 +360,7 @@ namespace ss {
template <typename H>
inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, _In_ char const* param_spec, _In_ const char* calling_func, _In_ size_t param_count, ... )
{
SQLSRV_UNUSED( return_value );
// SQLSRV_UNUSED( return_value );
zval* rsrc;
H* h = NULL;

View file

@ -37,7 +37,7 @@ void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error,
_In_ bool warning );
bool ignore_warning( _In_ char* sql_state, _In_ int native_code );
bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity,
_In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args );
_In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args );
int sqlsrv_merge_zend_hash_dtor( _Inout_ zval* dest );
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z );
@ -365,7 +365,7 @@ ss_error SS_ERRORS[] = {
},
{
SS_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
{ IMSSP, (SQLCHAR*)"Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, or ActiveDirectoryMsi is supported.", -62, false }
{ IMSSP, (SQLCHAR*)"Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectorySPA is supported.", -62, false }
},
{
SS_SQLSRV_ERROR_AE_QUERY_SQLTYPE_REQUIRED,
@ -456,7 +456,7 @@ bool ss_severity_check(_In_ unsigned int severity)
return ((severity & SQLSRV_G(log_severity)) && (SQLSRV_G(current_subsystem) & SQLSRV_G(log_subsystems)));
}
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args )
bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args )
{
logging_severity severity = SEV_ERROR;
if( warning && !SQLSRV_G( warnings_return_as_errors )) {
@ -506,7 +506,7 @@ bool ss_error_handler( _Inout_ sqlsrv_context& ctx, _In_ unsigned int sqlsrv_err
PHP_FUNCTION( sqlsrv_errors )
{
SQLSRV_UNUSED( execute_data );
// SQLSRV_UNUSED( execute_data );
zend_long flags = SQLSRV_ERR_ALL;
@ -557,7 +557,7 @@ PHP_FUNCTION( sqlsrv_errors )
PHP_FUNCTION( sqlsrv_configure )
{
SQLSRV_UNUSED( execute_data );
// SQLSRV_UNUSED( execute_data );
LOG_FUNCTION( "sqlsrv_configure" );
@ -680,7 +680,7 @@ PHP_FUNCTION( sqlsrv_configure )
PHP_FUNCTION( sqlsrv_get_config )
{
SQLSRV_UNUSED( execute_data );
// SQLSRV_UNUSED( execute_data );
char* option = NULL;
size_t option_len;
@ -821,7 +821,7 @@ void copy_error_to_zval( _Inout_ zval* error_z, _In_ sqlsrv_error_const* error,
}
bool handle_errors_and_warnings( _Inout_ sqlsrv_context& ctx, _Inout_ zval* reported_chain, _Inout_ zval* ignored_chain, _In_ logging_severity log_severity,
_In_ unsigned int sqlsrv_error_code, _In_ bool warning, _In_opt_ va_list* print_args )
_In_ unsigned int sqlsrv_error_code, _In_ int warning, _In_opt_ va_list* print_args )
{
bool result = true;
bool errors_ignored = false;

View file

@ -24,6 +24,8 @@ $adServer = 'TARGET_AD_SERVER';
$adDatabase = 'TARGET_AD_DATABASE';
$adUser = 'TARGET_AD_USERNAME';
$adPassword = 'TARGET_AD_PASSWORD';
$adSPClientId = 'TARGET_ADSP_CLIENT_ID';
$adSPClientSecret = 'TARGET_ADSP_CLIENT_SECRET';
$driverType = true;
$driver = "ODBC Driver 17 for SQL Server";

View file

@ -96,5 +96,5 @@ if ($azureServer != 'TARGET_AD_SERVER') {
Connected successfully with Authentication=SqlPassword.
string(1) "%d"
Could not connect with Authentication=ActiveDirectoryIntegrated.
SQLSTATE[IMSSP]: Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, or ActiveDirectoryMsi is supported.
SQLSTATE[IMSSP]: Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectorySPA is supported.
%s with Authentication=ActiveDirectoryPassword.

View file

@ -0,0 +1,117 @@
--TEST--
Test some basics of Azure AD service principal support
--SKIPIF--
<?php
require_once('skipif.inc');
require_once('MsSetup.inc');
try {
$conn = new PDO("sqlsrv:server = $server; driver=$driver;", $uid, $pwd);
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
$msodbcsqlMaj = explode(".", $msodbcsqlVer)[0];
$msodbcsqlMin = explode(".", $msodbcsqlVer)[1];
if ($msodbcsqlMaj < 17 || $msodbcsqlMin < 7) {
die("skip: Requires ODBC driver 17.7 or above");
}
} catch (PDOException $e) {
die("skip: Failed to connect in skipif.");
}
?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
function simpleTest($conn)
{
// Create table
$tableName = 'pdoTestSPA';
$col1 = 'Testing service principal with pdo';
dropTable($conn, $tableName);
$query = "CREATE TABLE $tableName(ID INT IDENTITY(1,1), COL1 VARCHAR(50))";
$stmt = $conn->query($query);
// Insert one row
$query = "INSERT INTO $tableName VALUES ('$col1')";
$stmt = $conn->query($query);
// Fetch data
$query = "SELECT * FROM $tableName";
$stmt = $conn->query($query);
$result = $stmt->fetch(PDO::FETCH_NUM);
$id = $result[0];
if ($id != 1) {
echo "AzureAD service principal test: fetched id $id unexpected\n";
}
$field = $result[1];
if ($field !== $col1) {
echo "AzureAD service principal test: fetched value $field unexpected\n";
}
dropTable($conn, $tableName);
}
function connectAzureDB($showException)
{
global $adServer, $adDatabase, $adSPClientId, $adSPClientSecret, $maxAttempts;
$conn = false;
try {
$connectionInfo = "Database = $adDatabase; Authentication = ActiveDirectorySPA;";
$conn = new PDO("sqlsrv:server = $adServer; $connectionInfo", $adSPClientId, $adSPClientSecret);
} catch (PDOException $e) {
if ($showException) {
echo "Could not connect with Azure AD Service Principal after $maxAttempts retries.\n";
print_r($e->getMessage());
echo PHP_EOL;
}
}
return $conn;
}
// First test connecting to regular sql server
require_once('MsSetup.inc');
try {
$conn = new PDO("sqlsrv:server = $server; Authentication = ActiveDirectorySPA;", $uid, $pwd);
echo "Expect regular connection to fail\n";
} catch(PDOException $e) {
// do nothing
}
// Next, test connecting with a valid service principal and perform some simple tasks
$maxAttempts = 3;
try {
if ($adServer != 'TARGET_AD_SERVER' && $adSPClientId != 'TARGET_ADSP_CLIENT_ID') {
$conn = false;
$numAttempts = 0;
do {
$conn = connectAzureDB($numAttempts == ($maxAttempts - 1));
if ($conn === false) {
$numAttempts++;
sleep(10);
}
} while ($conn === false && $numAttempts < $maxAttempts);
// Proceed when successfully connected
if ($conn) {
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE, true);
simpleTest($conn);
unset($conn);
}
}
} catch(PDOException $e) {
print_r($e->getMessage());
echo PHP_EOL;
}
echo "Done\n";
?>
--EXPECT--
Done

View file

@ -0,0 +1,153 @@
--TEST--
Test fetching varbinary, varchar, nvarchar max fields with client buffer
--DESCRIPTION--
Similar to sqlsrv_fetch_large_stream test but fetching varbinary, varchar, nvarchar max fields as strings with or without client buffer
--SKIPIF--
<?php require_once('skipif_mid-refactor.inc'); ?>
--ENV--
PHPT_EXEC=true
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
$tableName = 'pdoFetchLobTest';
$binaryColumn = 'varbinary_max';
$strColumn = 'varchar_max';
$nstrColumn = 'nvarchar_max';
$bin = 'abcdefghijklmnopqrstuvwxyz';
$binaryValue = str_repeat($bin, 100);
$hexValue = str_repeat(strtoupper(bin2hex($bin)), 100);
$strValue = str_repeat("stuvwxyz", 400);
$nstrValue = str_repeat("ÃÜðßZZýA©", 200);
function checkData($actual, $expected)
{
trace("Actual:\n$actual\n");
$success = true;
$pos = strpos($actual, $expected);
if (($pos === false) || ($pos > 1)) {
$success = false;
}
return ($success);
}
function fetchBinary($conn, $buffered)
{
global $tableName, $binaryColumn, $binaryValue, $hexValue;
try {
$query = "SELECT $binaryColumn FROM $tableName";
if ($buffered) {
$stmt = $conn->prepare($query, array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE=>PDO::SQLSRV_CURSOR_BUFFERED));
} else {
$stmt = $conn->prepare($query);
}
$stmt->bindColumn($binaryColumn, $value, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_BOUND);
if (!checkData($value, $binaryValue)) {
echo "Fetched binary value unexpected ($buffered): $value\n";
}
$stmt->bindColumn($binaryColumn, $value, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_SYSTEM);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_BOUND);
if (!checkData($value, $hexValue)) {
echo "Fetched binary value a char string ($buffered): $value\n";
}
$stmt->bindColumn($binaryColumn, $value, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_UTF8);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_BOUND);
if (!checkData($value, $hexValue)) {
echo "Fetched binary value as UTF-8 string ($buffered): $value\n";
}
} catch (PdoException $e) {
echo "Caught exception in fetchBinary ($buffered):\n";
echo $e->getMessage() . PHP_EOL;
}
}
function fetchAsString($conn, $buffered)
{
global $tableName, $strColumn, $strValue;
global $nstrColumn, $nstrValue;
try {
$query = "SELECT $strColumn, $nstrColumn FROM $tableName";
if ($buffered) {
$stmt = $conn->prepare($query, array(PDO::ATTR_CURSOR=>PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE=>PDO::SQLSRV_CURSOR_BUFFERED));
} else {
$stmt = $conn->prepare($query);
}
$stmt->execute();
$stmt->bindColumn($strColumn, $value1, PDO::PARAM_STR);
$stmt->bindColumn($nstrColumn, $value2, PDO::PARAM_STR);
$row = $stmt->fetch(PDO::FETCH_BOUND);
if (!checkData($value1, $strValue)) {
echo "Fetched string value ($buffered): $value1\n";
}
if (!checkData($value2, $nstrValue)) {
echo "Fetched string value ($buffered): $value2\n";
}
$stmt->execute();
$stmt->bindColumn($strColumn, $value, PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_SYSTEM);
$row = $stmt->fetch(PDO::FETCH_BOUND);
if (!checkData($value, $strValue)) {
echo "Fetched string value: $value\n";
}
} catch (PdoException $e) {
echo "Caught exception in fetchBinary ($buffered):\n";
echo $e->getMessage() . PHP_EOL;
}
}
try {
$conn = connect();
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Create table of one max column
$colMeta = array(new ColumnMeta('varbinary(max)', $binaryColumn),
new ColumnMeta('varchar(max)', $strColumn),
new ColumnMeta('nvarchar(max)', $nstrColumn));
createTable($conn, $tableName, $colMeta);
// Insert one row
$query = "INSERT INTO $tableName ($binaryColumn, $strColumn, $nstrColumn) VALUES (?, ?, ?)";
$stmt = $conn->prepare($query);
$stmt->bindParam(1, $binaryValue, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
$stmt->bindParam(2, $strValue, PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_SYSTEM);
$stmt->bindParam(3, $nstrValue, PDO::PARAM_STR);
$stmt->execute();
unset($stmt);
// Starting fetching with or without client buffer
fetchBinary($conn, false);
fetchBinary($conn, true);
fetchAsString($conn, false);
fetchAsString($conn, true);
dropTable($conn, $tableName);
echo "Done\n";
unset($conn);
} catch (PdoException $e) {
echo $e->getMessage() . PHP_EOL;
}
?>
--EXPECT--
Done

View file

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

View file

@ -131,7 +131,7 @@ array\(4\) {
\["DriverVer"\]=>
string\(10\) "[0-9]{2}.[0-9]{2}.[0-9]{4}"
\["ExtensionVer"\]=>
string\([0-9]*\) \"[0-9].[0-9]\.[0-9](-(RC[1-9]?|preview[1-9]))?(\.[0-9]+)?(\+[0-9]+)?\"
string\([0-9]*\) \"[0-9].[0-9]\.[0-9](-(RC[1-9]?|beta[1-9]))?(\.[0-9]+)?(\+[0-9]+)?\"
}
Test_6:

View file

@ -58,7 +58,7 @@ function fetchBoth($conn, $tbname)
echo "Expect getColumnMeta to fail with -1\n";
} catch (Error $e) {
if (PHP_MAJOR_VERSION == 8) {
$error = '*PDOStatement::getColumnMeta(): Argument #1 ($index) must be greater than or equal to 0*';
$error = '*PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0*';
} else {
$error = '*Invalid column reference: column number must be non-negative*';
}

View file

@ -63,7 +63,7 @@ function fetchBoth($conn, $tbname)
echo "Expect getColumnMeta to fail with -1\n";
} catch (Error $e) {
if (PHP_MAJOR_VERSION == 8) {
$error = '*PDOStatement::getColumnMeta(): Argument #1 ($index) must be greater than or equal to 0*';
$error = '*PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0*';
} else {
$error = '*Invalid column reference: column number must be non-negative*';
}

View file

@ -31,6 +31,8 @@ $adServer = 'TARGET_AD_SERVER';
$adDatabase = 'TARGET_AD_DATABASE';
$adUser = 'TARGET_AD_USERNAME';
$adPassword = 'TARGET_AD_PASSWORD';
$adSPClientId = 'TARGET_ADSP_CLIENT_ID';
$adSPClientSecret = 'TARGET_ADSP_CLIENT_SECRET';
if (isset($_ENV['MSSQL_SERVER']) || isset($_ENV['MSSQL_USER']) || isset($_ENV['MSSQL_PASSWORD'])) {
$server = $_ENV['MSSQL_SERVER'];

View file

@ -106,7 +106,7 @@ Array
[SQLSTATE] => IMSSP
[1] => -62
[code] => -62
[2] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, or ActiveDirectoryMsi is supported.
[message] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, or ActiveDirectoryMsi is supported.
[2] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectorySPA is supported.
[message] => Invalid option for the Authentication keyword. Only SqlPassword, ActiveDirectoryPassword, ActiveDirectoryMsi or ActiveDirectorySPA is supported.
)
%s with Authentication=ActiveDirectoryPassword.

View file

@ -0,0 +1,116 @@
--TEST--
Test some basics of Azure AD Service Principal support
--SKIPIF--
<?php
require_once('skipif.inc');
require_once('MsSetup.inc');
$connectionInfo = array("UID"=>$userName, "PWD"=>$userPassword, "Driver" => $driver);
$conn = sqlsrv_connect($server, $connectionInfo);
if ($conn === false) {
die("skip: Failed to connect in skipif.");
}
$msodbcsqlVer = sqlsrv_client_info($conn)['DriverVer'];
$version = explode(".", $msodbcsqlVer);
if ($version[0] < 17 || $version[1] < 7) {
die("skip: Requires ODBC driver 17.7 or above");
}
?>
--FILE--
<?php
require_once('MsCommon.inc');
function simpleTest($conn)
{
// Create table
$tableName = 'testSPA';
$col1 = 'Testing service principal';
dropTable($conn, $tableName);
$query = "CREATE TABLE $tableName(ID INT IDENTITY(1,1), COL1 VARCHAR(50))";
$stmt = sqlsrv_query($conn, $query);
if (!$stmt) {
fatalError("AzureAD service principal test: failed to create a table\n");
}
// Insert one row
$query = "INSERT INTO $tableName VALUES ('$col1')";
$stmt = sqlsrv_query($conn, $query);
if (!$stmt) {
fatalError("AzureAD service principal test: failed to insert a row\n");
}
// Fetch data
$query = "SELECT * FROM $tableName";
$stmt = sqlsrv_query($conn, $query);
if (!$stmt) {
fatalError("AzureAD service principal test: failed to fetch a table\n");
}
while (sqlsrv_fetch($stmt)) {
$id = sqlsrv_get_field($stmt, 0);
if ($id != 1) {
fatalError("AzureAD service principal test: fetched id $id unexpected\n");
}
$field = sqlsrv_get_field($stmt, 1);
if ($field !== $col1) {
fatalError("AzureAD service principal test: fetched value $field unexpected\n");
}
}
dropTable($conn, $tableName);
}
function connectAzureDB($showException)
{
global $adServer, $adDatabase, $adSPClientId, $adSPClientSecret, $maxAttempts;
$conn = false;
$connectionInfo = array("Database"=>$adDatabase,
"Authentication"=>"ActiveDirectorySPA",
"UID"=>$adSPClientId,
"PWD"=>$adSPClientSecret);
$conn = sqlsrv_connect($adServer, $connectionInfo);
if ($conn === false) {
if ($showException) {
fatalError("Could not connect with Azure AD Service Principal after $maxAttempts retries.\n");
}
} else {
simpleTest($conn);
sqlsrv_close($conn);
}
return $conn;
}
// Try connecting to an invalid server. Expect this to fail.
$connectionInfo = array("Authentication"=>"ActiveDirectorySPA");
$conn = sqlsrv_connect('invalidServer', $connectionInfo);
if ($conn) {
fatalError("AzureAD Service Principal test: expected to fail with invalidServer\n");
}
// Next, test connecting with Service Principal
$maxAttempts = 3;
if ($adServer != 'TARGET_AD_SERVER' && $adSPClientId != 'TARGET_ADSP_CLIENT_ID') {
$conn = false;
$numAttempts = 0;
do {
$conn = connectAzureDB($numAttempts == ($maxAttempts - 1));
if ($conn === false) {
$numAttempts++;
sleep(10);
}
} while ($conn === false && $numAttempts < $maxAttempts);
}
echo "Done\n";
?>
--EXPECT--
Done

View file

@ -21,5 +21,5 @@ array\(4\) {
\[\"DriverVer\"\]=>
string\(10\) \"[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{4}\"
\[\"ExtensionVer\"\]=>
string\([0-9]+\) \"[0-9]\.[0-9]\.[0-9](-(RC[1-9]?|preview[1-9]))?(\.[0-9]+)?(\+[0-9]+)?\"
string\([0-9]+\) \"[0-9]\.[0-9]\.[0-9](-(RC[1-9]?|beta[1-9]))?(\.[0-9]+)?(\+[0-9]+)?\"
}

View file

@ -1,55 +1,113 @@
--TEST--
Streaming Field Test
Test fetching varchar and nvarchar max fields
--DESCRIPTION--
Verifies the streaming behavior and proper error handling with Always Encrypted
Test fetching varchar and nvarchar max fields as streams or strings with or without client buffer
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--ENV--
PHPT_EXEC=true
--FILE--
<?php
require_once('MsCommon.inc');
$conn = AE\connect();
$tableName = "test_max_fields";
AE\createTable($conn, $tableName, array(new AE\ColumnMeta("varchar(max)", "varchar_max_col")));
$tableName = "char_max_fields";
$columns = array(new AE\ColumnMeta("varchar(max)", "varchar_max_col"),
new AE\ColumnMeta("nvarchar(max)", "nvarchar_max_col"));
$inValue = str_repeat("ÃÜðßZZýA©", 600);
$insertSql = "INSERT INTO $tableName (varchar_max_col) VALUES (?)";
$params = array($inValue);
AE\createTable($conn, $tableName, $columns);
$strValue = str_repeat("SimpleTest", 450);
$nstrValue = str_repeat("ÃÜðßZZýA©", 600);
$insertSql = "INSERT INTO $tableName (varchar_max_col, nvarchar_max_col) VALUES (?, ?)";
$params = array(array($strValue, null, null, SQLSRV_SQLTYPE_VARCHAR('max')),
array($nstrValue, null, SQLSRV_PHPTYPE_STRING('UTF-8'), SQLSRV_SQLTYPE_NVARCHAR('max')));
$stmt = sqlsrv_prepare($conn, $insertSql, $params);
if ($stmt) {
sqlsrv_execute($stmt);
}
$query = "SELECT * FROM $tableName";
$stmt = sqlsrv_prepare($conn, $query);
if ($stmt) {
sqlsrv_execute($stmt);
}
if (!sqlsrv_fetch($stmt)) {
fatalError("Failed to fetch row ");
}
$stream = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR));
$success = false;
if ($stream !== false) {
$value = '';
$num = 0;
while (!feof($stream)) {
$value .= fread($stream, 8192);
$res = sqlsrv_execute($stmt);
if (!$res) {
fatalError("Failed to insert data");
}
fclose($stream);
if (checkData($value, $inValue)) { // compare the data to see if they match!
$success = true;
}
}
if ($success) {
echo "Done.\n";
} else {
fatalError("Failed to fetch stream ");
fatalError("Failed to prepare insert statement");
}
runTest($conn, false);
runTest($conn, true);
dropTable($conn, $tableName);
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
echo "Done\n";
///////////////////////////////////////////////////////////////////////////////////////////////
function runTest($conn, $buffered)
{
global $tableName, $strValue, $nstrValue;
trace("runTest ($buffered)\n");
$query = "SELECT * FROM $tableName";
if ($buffered) {
$stmt = sqlsrv_prepare($conn, $query, null, array("Scrollable"=>SQLSRV_CURSOR_CLIENT_BUFFERED));
} else {
$stmt = sqlsrv_prepare($conn, $query);
}
if (!$stmt) {
fatalError("runTest ($buffered): failed to prepare select statement");
}
if (!sqlsrv_execute($stmt)) {
fatalError("runTest ($buffered): failed to execute select");
}
if (!sqlsrv_fetch($stmt)) {
fatalError("runTest ($buffered): failed to fetch data");
}
fetchAsString($stmt, 0, $strValue);
fetchAsString($stmt, 1, $nstrValue);
if (!sqlsrv_execute($stmt)) {
fatalError("runTest ($buffered): failed to execute select");
}
if (!sqlsrv_fetch($stmt)) {
fatalError("runTest ($buffered): failed to fetch data");
}
fetchAsStream($stmt, 0, $strValue);
fetchAsStream($stmt, 1, $nstrValue);
}
function fetchAsString($stmt, $index, $expected)
{
trace("fetchAsString ($index):\n");
$sqltype = ($index > 0) ? SQLSRV_PHPTYPE_STRING('UTF-8') : SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR);
$value = sqlsrv_get_field($stmt, $index, $sqltype);
if (!checkData($value, $expected)) {
echo("fetchAsString ($index) expected:\n$expected\nActual:\n$value\n");
}
}
function fetchAsStream($stmt, $index, $expected)
{
trace("fetchAsStream ($index):\n");
$sqltype = ($index > 0) ? SQLSRV_PHPTYPE_STREAM('UTF-8') : SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR);
$stream = sqlsrv_get_field($stmt, $index, $sqltype);
if ($stream !== false) {
$value = '';
while (!feof($stream)) {
$value .= fread($stream, 8192);
}
fclose($stream);
if (!checkData($value, $expected)) {
echo("fetchAsStream ($index) expected:\n$expected\nActual:\n$value\n");
}
}
}
function checkData($actual, $expected)
@ -58,15 +116,11 @@ function checkData($actual, $expected)
$pos = strpos($actual, $expected);
if (($pos === false) || ($pos > 1)) {
$success = false;
$success = false;
}
if (!$success) {
trace("\nData error\nExpected:\n$expected\nActual:\n$actual\n");
}
return ($success);
}
?>
--EXPECT--
Done.
Done

View file

@ -0,0 +1,202 @@
--TEST--
Test fetching varbinary max fields with client buffer
--DESCRIPTION--
Test fetching varbinary max fields as streams or strings using client buffer
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
--ENV--
PHPT_EXEC=true
--FILE--
<?php
require_once('MsCommon.inc');
function fetchNull($stmt, $sqltype, $message)
{
$result = sqlsrv_get_field($stmt, 1, $sqltype);
if (!is_null($result)) {
echo("$message: expected NULL\n");
}
}
function fetchNullStream($stmt, $sqltype, $message)
{
$stream = sqlsrv_get_field($stmt, 1, $sqltype);
if ($stream !== false) {
$value = fread($stream, 8192);
fclose($stream);
if (!empty($value)) {
echo("$message: expected an empty value\n");
}
}
}
function fetchStream($stmt, $test)
{
global $binaryValue, $hexValue;
if (!sqlsrv_execute($stmt)) {
fatalError("fetchStream: failed to execute select");
}
if (!sqlsrv_fetch($stmt)) {
fatalError("fetchStream: failed to fetch row");
}
switch ($test) {
case 1:
$sqltype = SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_CHAR);
$type = 'char string';
$expected = $hexValue;
break;
case 2:
$sqltype = SQLSRV_PHPTYPE_STREAM('UTF-8');
$type = 'UTF-8 string';
$expected = $hexValue;
break;
case 3:
$sqltype = SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY);
$type = 'binary string';
$expected = $binaryValue;
break;
default:
echo "fetchStream: something went wrong\n";
break;
}
trace("fetchStream ($type):\n");
$stream = sqlsrv_get_field($stmt, 0, $sqltype);
if ($stream !== false) {
$value = '';
while (!feof($stream)) {
$value .= fread($stream, 8192);
}
fclose($stream);
if (!checkData($value, $expected)) {
echo("fetchStream ($type)\nExpected:\n$expected\nActual:\n$value\n");
}
} else {
fatalError("fetchStream ($type) failed");
}
fetchNullStream($stmt, $sqltype, "fetchStream ($type)\n");
}
function fetchData($stmt, $test)
{
global $binaryValue, $hexValue;
if (!sqlsrv_execute($stmt)) {
fatalError("fetchData: failed to execute select");
}
if (!sqlsrv_fetch($stmt)) {
fatalError("fetchData: failed to fetch row");
}
switch ($test) {
case 1:
$sqltype = SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR);
$type = 'char string';
$expected = $hexValue;
break;
case 2:
$sqltype = SQLSRV_PHPTYPE_STRING('UTF-8');
$type = 'UTF-8 string';
$expected = $hexValue;
break;
case 3:
$sqltype = SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY);
$type = 'binary string';
$expected = $binaryValue;
break;
default:
echo "fetchData: something went wrong\n";
break;
}
trace("fetchData ($type):\n");
$value = sqlsrv_get_field($stmt, 0, $sqltype);
if (!checkData($value, $expected)) {
echo("fetchData ($type)\nExpected:\n$expected\nActual:\n$value\n");
}
fetchNull($stmt, $sqltype, "fetchData ($type)\n");
}
function runTest($conn, $buffered)
{
global $tableName, $binaryValue, $hexValue;
$query = "SELECT * FROM $tableName";
if ($buffered) {
trace("Test using a client buffer\n");
$stmt = sqlsrv_prepare($conn, $query, null, array("Scrollable"=>SQLSRV_CURSOR_CLIENT_BUFFERED));
} else {
trace("Test without using a client buffer\n");
$stmt = sqlsrv_prepare($conn, $query);
}
if (!$stmt) {
fatalError("runTest: failed to prepare select statement");
}
fetchData($stmt, 1);
fetchData($stmt, 2);
fetchData($stmt, 3);
fetchStream($stmt, 1);
fetchStream($stmt, 2);
fetchStream($stmt, 3);
}
function checkData($actual, $expected)
{
$success = true;
$pos = strpos($actual, $expected);
if (($pos === false) || ($pos > 1)) {
$success = false;
}
return ($success);
}
$conn = AE\connect();
$tableName = "binary_max_fields";
$columns = array(new AE\ColumnMeta("varbinary(max)", "varbinary_max_col"),
new AE\ColumnMeta("varbinary(max)", "varbinary_null_col"));
AE\createTable($conn, $tableName, $columns);
$bin = 'abcdefghijk';
$binaryValue = str_repeat($bin, 400);
$hexValue = strtoupper(bin2hex($binaryValue));
$insertSql = "INSERT INTO $tableName (varbinary_max_col, varbinary_null_col) VALUES (?, ?)";
$params = array(array($binaryValue, null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max')),
array(null, null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARBINARY('max')));
$stmt = sqlsrv_prepare($conn, $insertSql, $params);
if ($stmt) {
$res = sqlsrv_execute($stmt);
if (!$res) {
fatalError("Failed to insert data");
}
} else {
fatalError("Failed to prepare insert statement");
}
runTest($conn, false);
runTest($conn, true);
echo "Done\n";
dropTable($conn, $tableName);
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
?>
--EXPECT--
Done