2017-07-19 23:10:53 +02:00
//---------------------------------------------------------------------------------------------------------------------------------
// file: pdo_dbh.cpp
//
// Contents: Implements the PDO object for PDO_SQLSRV
//
2021-09-08 02:38:17 +02:00
// Microsoft Drivers 5.10 for PHP for SQL Server
2017-07-19 23:10:53 +02:00
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
2018-12-20 20:49:06 +01:00
extern " C " {
# include "php_pdo_sqlsrv.h"
}
# include "php_pdo_sqlsrv_int.h"
2017-07-19 23:10:53 +02:00
# include <string>
# include <sstream>
typedef const zend_function_entry pdo_sqlsrv_function_entry ;
// *** internal variables and constants ***
namespace {
const char LAST_INSERT_ID_QUERY [ ] = " SELECT @@IDENTITY; " ;
2018-04-06 20:35:32 +02:00
const size_t LAST_INSERT_ID_BUFF_LEN = 50 ; // size of the buffer to hold the string value of the last inserted id, which may be an int, bigint, decimal(p,0) or numeric(p,0)
2021-07-29 00:45:04 +02:00
const char SEQUENCE_CURRENT_VALUE_QUERY [ ] = " SELECT current_value FROM sys.sequences WHERE name=N'%s' " ;
2017-07-19 23:10:53 +02:00
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 {
const char Server [ ] = " Server " ;
const char APP [ ] = " APP " ;
2018-09-06 20:32:04 +02:00
const char AccessToken [ ] = " AccessToken " ;
2017-07-19 23:10:53 +02:00
const char ApplicationIntent [ ] = " ApplicationIntent " ;
const char AttachDBFileName [ ] = " AttachDbFileName " ;
const char Authentication [ ] = " Authentication " ;
2018-02-08 18:00:16 +01:00
const char ColumnEncryption [ ] = " ColumnEncryption " ;
2018-05-06 02:08:01 +02:00
const char ConnectionPooling [ ] = " ConnectionPooling " ;
2019-02-13 13:56:12 +01:00
const char Language [ ] = " Language " ;
2017-07-19 23:10:53 +02:00
const char ConnectRetryCount [ ] = " ConnectRetryCount " ;
const char ConnectRetryInterval [ ] = " ConnectRetryInterval " ;
const char Database [ ] = " Database " ;
2018-05-06 02:08:01 +02:00
const char Driver [ ] = " Driver " ;
2017-07-19 23:10:53 +02:00
const char Encrypt [ ] = " Encrypt " ;
const char Failover_Partner [ ] = " Failover_Partner " ;
2018-05-06 02:08:01 +02:00
const char KeyStoreAuthentication [ ] = " KeyStoreAuthentication " ;
const char KeyStorePrincipalId [ ] = " KeyStorePrincipalId " ;
const char KeyStoreSecret [ ] = " KeyStoreSecret " ;
2017-07-19 23:10:53 +02:00
const char LoginTimeout [ ] = " LoginTimeout " ;
const char MARS_Option [ ] = " MultipleActiveResultSets " ;
const char MultiSubnetFailover [ ] = " MultiSubnetFailover " ;
const char QuotedId [ ] = " QuotedId " ;
const char TraceFile [ ] = " TraceFile " ;
const char TraceOn [ ] = " TraceOn " ;
const char TrustServerCertificate [ ] = " TrustServerCertificate " ;
const char TransactionIsolation [ ] = " TransactionIsolation " ;
const char TransparentNetworkIPResolution [ ] = " TransparentNetworkIPResolution " ;
const char WSID [ ] = " WSID " ;
2021-12-08 00:29:03 +01:00
const char ComputePool [ ] = " ComputePool " ;
2022-01-05 17:59:48 +01:00
const char HostNameInCertificate [ ] = " HostNameInCertificate " ;
2017-07-19 23:10:53 +02:00
}
enum PDO_CONN_OPTIONS {
PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC ,
} ;
enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC ,
PDO_STMT_OPTION_DIRECT_QUERY ,
PDO_STMT_OPTION_CURSOR_SCROLL_TYPE ,
PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE ,
PDO_STMT_OPTION_EMULATE_PREPARES ,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE ,
2018-11-02 22:34:27 +01:00
PDO_STMT_OPTION_FETCHES_DATETIME_TYPE ,
2018-11-28 02:18:38 +01:00
PDO_STMT_OPTION_FORMAT_DECIMALS ,
2019-05-01 17:03:33 +02:00
PDO_STMT_OPTION_DECIMAL_PLACES ,
PDO_STMT_OPTION_DATA_CLASSIFICATION
2017-07-19 23:10:53 +02:00
} ;
// List of all the statement options supported by this driver.
const stmt_option PDO_STMT_OPTS [ ] = {
{ NULL , 0 , SQLSRV_STMT_OPTION_QUERY_TIMEOUT , std : : unique_ptr < stmt_option_query_timeout > ( new stmt_option_query_timeout ) } ,
{ NULL , 0 , SQLSRV_STMT_OPTION_SCROLLABLE , std : : unique_ptr < stmt_option_pdo_scrollable > ( new stmt_option_pdo_scrollable ) } ,
{ NULL , 0 , PDO_STMT_OPTION_ENCODING , std : : unique_ptr < stmt_option_encoding > ( new stmt_option_encoding ) } ,
{ NULL , 0 , PDO_STMT_OPTION_DIRECT_QUERY , std : : unique_ptr < stmt_option_direct_query > ( new stmt_option_direct_query ) } ,
{ NULL , 0 , PDO_STMT_OPTION_CURSOR_SCROLL_TYPE , std : : unique_ptr < stmt_option_cursor_scroll_type > ( new stmt_option_cursor_scroll_type ) } ,
{ NULL , 0 , PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE , std : : unique_ptr < stmt_option_buffered_query_limit > ( new stmt_option_buffered_query_limit ) } ,
{ NULL , 0 , PDO_STMT_OPTION_EMULATE_PREPARES , std : : unique_ptr < stmt_option_emulate_prepares > ( new stmt_option_emulate_prepares ) } ,
{ NULL , 0 , PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE , std : : unique_ptr < stmt_option_fetch_numeric > ( new stmt_option_fetch_numeric ) } ,
2018-09-18 01:24:52 +02:00
{ NULL , 0 , PDO_STMT_OPTION_FETCHES_DATETIME_TYPE , std : : unique_ptr < stmt_option_fetch_datetime > ( new stmt_option_fetch_datetime ) } ,
2018-11-02 22:34:27 +01:00
{ NULL , 0 , PDO_STMT_OPTION_FORMAT_DECIMALS , std : : unique_ptr < stmt_option_format_decimals > ( new stmt_option_format_decimals ) } ,
2018-11-28 02:18:38 +01:00
{ NULL , 0 , PDO_STMT_OPTION_DECIMAL_PLACES , std : : unique_ptr < stmt_option_decimal_places > ( new stmt_option_decimal_places ) } ,
2019-05-01 17:03:33 +02:00
{ NULL , 0 , PDO_STMT_OPTION_DATA_CLASSIFICATION , std : : unique_ptr < stmt_option_data_classification > ( new stmt_option_data_classification ) } ,
2017-07-19 23:10:53 +02:00
{ NULL , 0 , SQLSRV_STMT_OPTION_INVALID , std : : unique_ptr < stmt_option_functor > { } } ,
} ;
// boolean connection string
struct pdo_bool_conn_str_func
{
2020-04-21 00:17:21 +02:00
static void func ( _In_ connection_option const * option , _Inout_ zval * value , sqlsrv_conn * /*conn*/ , _Out_ std : : string & conn_str ) ;
2017-07-19 23:10:53 +02:00
} ;
struct pdo_txn_isolation_conn_attr_func
{
2020-04-21 00:17:21 +02:00
static void func ( connection_option const * /*option*/ , _In_ zval * value_z , _Inout_ sqlsrv_conn * conn , std : : string & /*conn_str*/ ) ;
2017-07-19 23:10:53 +02:00
} ;
struct pdo_int_conn_str_func {
2020-04-21 00:17:21 +02:00
static void func ( _In_ connection_option const * option , _In_ zval * value , sqlsrv_conn * /*conn*/ , _Out_ std : : string & conn_str )
2017-07-19 23:10:53 +02:00
{
2021-12-07 01:40:41 +01:00
SQLSRV_ASSERT ( Z_TYPE_P ( value ) = = IS_STRING , " Wrong zval type for this keyword " ) ;
2017-07-19 23:10:53 +02:00
std : : string val_str = Z_STRVAL_P ( value ) ;
conn_str + = option - > odbc_name ;
conn_str + = " ={ " ;
conn_str + = val_str ;
conn_str + = " }; " ;
}
} ;
2021-12-07 01:40:41 +01:00
struct pdo_encrypt_set_func
{
static void func ( _In_ connection_option const * option , _Inout_ zval * value_z , sqlsrv_conn * /*conn*/ , _Out_ std : : string & conn_str )
{
SQLSRV_ASSERT ( Z_TYPE_P ( value_z ) = = IS_STRING , " Wrong zval type for this keyword " ) ;
std : : string val_str = Z_STRVAL_P ( value_z ) ;
std : : string whitespaces ( " \t \f \v \n \r " ) ;
// Trim white spaces
std : : size_t found = val_str . find_last_not_of ( whitespaces ) ;
if ( found ! = std : : string : : npos )
val_str . erase ( found + 1 ) ;
const char TRUE_VALUE_1 [ ] = " true " ;
const char TRUE_VALUE_2 [ ] = " 1 " ;
const char FALSE_VALUE_1 [ ] = " false " ;
const char FALSE_VALUE_2 [ ] = " 0 " ;
// For backward compatibility, convert true/1 to yes and false/0 to no
std : : string attr ;
if ( ! val_str . compare ( TRUE_VALUE_1 ) | | ! val_str . compare ( TRUE_VALUE_2 ) ) {
attr = " yes " ;
} else if ( ! val_str . compare ( FALSE_VALUE_1 ) | | ! val_str . compare ( FALSE_VALUE_2 ) ) {
attr = " no " ;
} else {
// simply pass the attribute value to ODBC driver
attr = val_str ;
}
conn_str + = option - > odbc_name ;
conn_str + = " ={ " ;
conn_str + = attr ;
conn_str + = " }; " ;
}
} ;
2017-07-19 23:10:53 +02:00
template < unsigned int Attr >
struct pdo_int_conn_attr_func {
2020-04-21 00:17:21 +02:00
static void func ( connection_option const * /*option*/ , _In_ zval * value , _Inout_ sqlsrv_conn * conn , std : : string & /*conn_str*/ )
2017-07-19 23:10:53 +02:00
{
try {
SQLSRV_ASSERT ( Z_TYPE_P ( value ) = = IS_STRING , " pdo_int_conn_attr_func: Unexpected zval type. " ) ;
size_t val = static_cast < size_t > ( atoi ( Z_STRVAL_P ( value ) ) ) ;
2020-04-21 00:17:21 +02:00
core : : SQLSetConnectAttr ( conn , Attr , reinterpret_cast < SQLPOINTER > ( val ) , SQL_IS_UINTEGER ) ;
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
throw ;
}
}
} ;
template < unsigned int Attr >
struct pdo_bool_conn_attr_func {
2020-04-21 00:17:21 +02:00
static void func ( connection_option const * /*option*/ , _Inout_ zval * value , _Inout_ sqlsrv_conn * conn , std : : string & /*conn_str*/ )
2017-07-19 23:10:53 +02:00
{
try {
core : : SQLSetConnectAttr ( conn , Attr , reinterpret_cast < SQLPOINTER > ( core_str_zval_is_true ( value ) ) ,
2020-04-21 00:17:21 +02:00
SQL_IS_UINTEGER ) ;
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
throw ;
}
}
} ;
// statement options related functions
void add_stmt_option_key ( _Inout_ sqlsrv_context & ctx , _In_ size_t key , _Inout_ HashTable * options_ht ,
2020-04-21 00:17:21 +02:00
_Inout_ zval * * data ) ;
void validate_stmt_options ( _Inout_ sqlsrv_context & ctx , _Inout_ zval * stmt_options , _Inout_ HashTable * pdo_stmt_options_ht ) ;
2017-07-19 23:10:53 +02:00
} // namespace
// List of all connection options supported by this driver.
const connection_option PDO_CONN_OPTS [ ] = {
{
PDOConnOptionNames : : Server ,
sizeof ( PDOConnOptionNames : : Server ) ,
PDO_CONN_OPTION_SERVER ,
NULL ,
0 ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : APP ,
sizeof ( PDOConnOptionNames : : APP ) ,
SQLSRV_CONN_OPTION_APP ,
ODBCConnOptions : : APP ,
sizeof ( ODBCConnOptions : : APP ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2018-09-06 20:32:04 +02:00
{
PDOConnOptionNames : : AccessToken ,
sizeof ( PDOConnOptionNames : : AccessToken ) ,
SQLSRV_CONN_OPTION_ACCESS_TOKEN ,
ODBCConnOptions : : AccessToken ,
sizeof ( ODBCConnOptions : : AccessToken ) ,
CONN_ATTR_STRING ,
access_token_set_func : : func
} ,
2017-07-19 23:10:53 +02:00
{
PDOConnOptionNames : : ApplicationIntent ,
sizeof ( PDOConnOptionNames : : ApplicationIntent ) ,
SQLSRV_CONN_OPTION_APPLICATION_INTENT ,
ODBCConnOptions : : ApplicationIntent ,
sizeof ( ODBCConnOptions : : ApplicationIntent ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : AttachDBFileName ,
sizeof ( PDOConnOptionNames : : AttachDBFileName ) ,
SQLSRV_CONN_OPTION_ATTACHDBFILENAME ,
ODBCConnOptions : : AttachDBFileName ,
sizeof ( ODBCConnOptions : : AttachDBFileName ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : Authentication ,
sizeof ( PDOConnOptionNames : : Authentication ) ,
SQLSRV_CONN_OPTION_AUTHENTICATION ,
ODBCConnOptions : : Authentication ,
sizeof ( ODBCConnOptions : : Authentication ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : ConnectionPooling ,
sizeof ( PDOConnOptionNames : : ConnectionPooling ) ,
SQLSRV_CONN_OPTION_CONN_POOLING ,
ODBCConnOptions : : ConnectionPooling ,
sizeof ( ODBCConnOptions : : ConnectionPooling ) ,
CONN_ATTR_BOOL ,
conn_null_func : : func
} ,
2019-02-13 13:56:12 +01:00
{
PDOConnOptionNames : : Language ,
sizeof ( PDOConnOptionNames : : Language ) ,
SQLSRV_CONN_OPTION_LANGUAGE ,
ODBCConnOptions : : Language ,
sizeof ( ODBCConnOptions : : Language ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2017-09-12 22:30:14 +02:00
{
PDOConnOptionNames : : Driver ,
sizeof ( PDOConnOptionNames : : Driver ) ,
SQLSRV_CONN_OPTION_DRIVER ,
ODBCConnOptions : : Driver ,
sizeof ( ODBCConnOptions : : Driver ) ,
CONN_ATTR_STRING ,
driver_set_func : : func
} ,
2017-08-29 22:42:15 +02:00
{
2018-02-08 18:00:16 +01:00
PDOConnOptionNames : : ColumnEncryption ,
sizeof ( PDOConnOptionNames : : ColumnEncryption ) ,
SQLSRV_CONN_OPTION_COLUMNENCRYPTION ,
ODBCConnOptions : : ColumnEncryption ,
sizeof ( ODBCConnOptions : : ColumnEncryption ) ,
2017-08-29 22:42:15 +02:00
CONN_ATTR_STRING ,
2018-02-08 18:00:16 +01:00
column_encryption_set_func : : func
2017-08-29 22:42:15 +02:00
} ,
2017-07-19 23:10:53 +02:00
{
PDOConnOptionNames : : ConnectRetryCount ,
sizeof ( PDOConnOptionNames : : ConnectRetryCount ) ,
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT ,
ODBCConnOptions : : ConnectRetryCount ,
sizeof ( ODBCConnOptions : : ConnectRetryCount ) ,
CONN_ATTR_INT ,
pdo_int_conn_str_func : : func
} ,
{
PDOConnOptionNames : : ConnectRetryInterval ,
sizeof ( PDOConnOptionNames : : ConnectRetryInterval ) ,
SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL ,
ODBCConnOptions : : ConnectRetryInterval ,
sizeof ( ODBCConnOptions : : ConnectRetryInterval ) ,
CONN_ATTR_INT ,
pdo_int_conn_str_func : : func
} ,
{
PDOConnOptionNames : : Database ,
sizeof ( PDOConnOptionNames : : Database ) ,
SQLSRV_CONN_OPTION_DATABASE ,
ODBCConnOptions : : Database ,
sizeof ( ODBCConnOptions : : Database ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : Encrypt ,
sizeof ( PDOConnOptionNames : : Encrypt ) ,
SQLSRV_CONN_OPTION_ENCRYPT ,
ODBCConnOptions : : Encrypt ,
sizeof ( ODBCConnOptions : : Encrypt ) ,
2021-12-07 01:40:41 +01:00
CONN_ATTR_MIXED ,
pdo_encrypt_set_func : : func
2017-07-19 23:10:53 +02:00
} ,
{
PDOConnOptionNames : : Failover_Partner ,
sizeof ( PDOConnOptionNames : : Failover_Partner ) ,
SQLSRV_CONN_OPTION_FAILOVER_PARTNER ,
ODBCConnOptions : : Failover_Partner ,
sizeof ( ODBCConnOptions : : Failover_Partner ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2018-05-06 02:08:01 +02:00
{
PDOConnOptionNames : : KeyStoreAuthentication ,
sizeof ( PDOConnOptionNames : : KeyStoreAuthentication ) ,
SQLSRV_CONN_OPTION_KEYSTORE_AUTHENTICATION ,
ODBCConnOptions : : KeyStoreAuthentication ,
sizeof ( ODBCConnOptions : : KeyStoreAuthentication ) ,
CONN_ATTR_STRING ,
ce_akv_str_set_func : : func
} ,
{
PDOConnOptionNames : : KeyStorePrincipalId ,
sizeof ( PDOConnOptionNames : : KeyStorePrincipalId ) ,
SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID ,
ODBCConnOptions : : KeyStorePrincipalId ,
sizeof ( ODBCConnOptions : : KeyStorePrincipalId ) ,
CONN_ATTR_STRING ,
ce_akv_str_set_func : : func
} ,
{
PDOConnOptionNames : : KeyStoreSecret ,
sizeof ( PDOConnOptionNames : : KeyStoreSecret ) ,
SQLSRV_CONN_OPTION_KEYSTORE_SECRET ,
ODBCConnOptions : : KeyStoreSecret ,
sizeof ( ODBCConnOptions : : KeyStoreSecret ) ,
CONN_ATTR_STRING ,
ce_akv_str_set_func : : func
} ,
2017-07-19 23:10:53 +02:00
{
PDOConnOptionNames : : LoginTimeout ,
sizeof ( PDOConnOptionNames : : LoginTimeout ) ,
SQLSRV_CONN_OPTION_LOGIN_TIMEOUT ,
ODBCConnOptions : : LoginTimeout ,
sizeof ( ODBCConnOptions : : LoginTimeout ) ,
CONN_ATTR_INT ,
pdo_int_conn_attr_func < SQL_ATTR_LOGIN_TIMEOUT > : : func
} ,
{
PDOConnOptionNames : : MARS_Option ,
sizeof ( PDOConnOptionNames : : MARS_Option ) ,
SQLSRV_CONN_OPTION_MARS ,
ODBCConnOptions : : MARS_ODBC ,
sizeof ( ODBCConnOptions : : MARS_ODBC ) ,
CONN_ATTR_BOOL ,
pdo_bool_conn_str_func : : func
} ,
{
PDOConnOptionNames : : MultiSubnetFailover ,
sizeof ( PDOConnOptionNames : : MultiSubnetFailover ) ,
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER ,
ODBCConnOptions : : MultiSubnetFailover ,
sizeof ( ODBCConnOptions : : MultiSubnetFailover ) ,
CONN_ATTR_BOOL ,
pdo_bool_conn_str_func : : func
} ,
{
PDOConnOptionNames : : QuotedId ,
sizeof ( PDOConnOptionNames : : QuotedId ) ,
SQLSRV_CONN_OPTION_QUOTED_ID ,
ODBCConnOptions : : QuotedId ,
sizeof ( ODBCConnOptions : : QuotedId ) ,
CONN_ATTR_BOOL ,
pdo_bool_conn_str_func : : func
} ,
{
PDOConnOptionNames : : TraceFile ,
sizeof ( PDOConnOptionNames : : TraceFile ) ,
SQLSRV_CONN_OPTION_TRACE_FILE ,
ODBCConnOptions : : TraceFile ,
sizeof ( ODBCConnOptions : : TraceFile ) ,
CONN_ATTR_STRING ,
str_conn_attr_func < SQL_ATTR_TRACEFILE > : : func
} ,
{
PDOConnOptionNames : : TraceOn ,
sizeof ( PDOConnOptionNames : : TraceOn ) ,
SQLSRV_CONN_OPTION_TRACE_ON ,
ODBCConnOptions : : TraceOn ,
sizeof ( ODBCConnOptions : : TraceOn ) ,
CONN_ATTR_BOOL ,
pdo_bool_conn_attr_func < SQL_ATTR_TRACE > : : func
} ,
{
PDOConnOptionNames : : TransactionIsolation ,
sizeof ( PDOConnOptionNames : : TransactionIsolation ) ,
SQLSRV_CONN_OPTION_TRANS_ISOLATION ,
ODBCConnOptions : : TransactionIsolation ,
sizeof ( ODBCConnOptions : : TransactionIsolation ) ,
CONN_ATTR_INT ,
pdo_txn_isolation_conn_attr_func : : func
} ,
{
PDOConnOptionNames : : TrustServerCertificate ,
sizeof ( PDOConnOptionNames : : TrustServerCertificate ) ,
SQLSRV_CONN_OPTION_TRUST_SERVER_CERT ,
ODBCConnOptions : : TrustServerCertificate ,
sizeof ( ODBCConnOptions : : TrustServerCertificate ) ,
CONN_ATTR_BOOL ,
pdo_bool_conn_str_func : : func
} ,
{
PDOConnOptionNames : : TransparentNetworkIPResolution ,
sizeof ( PDOConnOptionNames : : TransparentNetworkIPResolution ) ,
2018-05-06 02:08:01 +02:00
SQLSRV_CONN_OPTION_TRANSPARENT_NETWORK_IP_RESOLUTION ,
2017-07-19 23:10:53 +02:00
ODBCConnOptions : : TransparentNetworkIPResolution ,
sizeof ( ODBCConnOptions : : TransparentNetworkIPResolution ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
{
PDOConnOptionNames : : WSID ,
sizeof ( PDOConnOptionNames : : WSID ) ,
SQLSRV_CONN_OPTION_WSID ,
ODBCConnOptions : : WSID ,
sizeof ( ODBCConnOptions : : WSID ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2021-12-08 00:29:03 +01:00
{
PDOConnOptionNames : : ComputePool ,
sizeof ( PDOConnOptionNames : : ComputePool ) ,
SQLSRV_CONN_OPTION_COMPUTE_POOL ,
ODBCConnOptions : : ComputePool ,
sizeof ( ODBCConnOptions : : ComputePool ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2022-01-05 17:59:48 +01:00
{
PDOConnOptionNames : : HostNameInCertificate ,
sizeof ( PDOConnOptionNames : : HostNameInCertificate ) ,
SQLSRV_CONN_OPTION_HOSTNAME_IN_CERT ,
ODBCConnOptions : : HostNameInCertificate ,
sizeof ( ODBCConnOptions : : HostNameInCertificate ) ,
CONN_ATTR_STRING ,
conn_str_append_func : : func
} ,
2017-07-19 23:10:53 +02:00
{ NULL , 0 , SQLSRV_CONN_OPTION_INVALID , NULL , 0 , CONN_ATTR_INVALID , NULL } , //terminate the table
} ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
// close the connection
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_close ( _Inout_ pdo_dbh_t * dbh ) ;
2017-07-19 23:10:53 +02:00
// execute queries
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_prepare ( _Inout_ pdo_dbh_t * dbh , _In_reads_ ( sql_len ) const char * sql ,
_Inout_ size_t sql_len , _Inout_ pdo_stmt_t * stmt , _In_ zval * driver_options ) ;
zend_long pdo_sqlsrv_dbh_do ( _Inout_ pdo_dbh_t * dbh , _In_reads_bytes_ ( sql_len ) const char * sql , _In_ size_t sql_len ) ;
// quote a string, meaning put quotes around it and escape any quotes within it
int pdo_sqlsrv_dbh_quote ( _Inout_ pdo_dbh_t * dbh , _In_reads_ ( unquotedlen ) const char * unquoted , _In_ size_t unquotedlen , _Outptr_result_buffer_ ( * quotedlen ) char * * quoted , _Out_ size_t * quotedlen ,
enum pdo_param_type paramtype ) ;
2017-07-19 23:10:53 +02:00
// transaction support functions
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_commit ( _Inout_ pdo_dbh_t * dbh ) ;
int pdo_sqlsrv_dbh_begin ( _Inout_ pdo_dbh_t * dbh ) ;
int pdo_sqlsrv_dbh_rollback ( _Inout_ pdo_dbh_t * dbh ) ;
2017-07-19 23:10:53 +02:00
// attribute functions
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_set_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * val ) ;
int pdo_sqlsrv_dbh_get_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * return_value ) ;
// return the last id generated by an executed SQL statement
char * pdo_sqlsrv_dbh_last_id ( _Inout_ pdo_dbh_t * dbh , _In_z_ const char * name , _Out_ size_t * len ) ;
2017-07-19 23:10:53 +02:00
// return more information
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_return_error ( _In_ pdo_dbh_t * dbh , _In_opt_ pdo_stmt_t * stmt , _Out_ zval * info ) ;
# else
// close the connection
void pdo_sqlsrv_dbh_close ( _Inout_ pdo_dbh_t * dbh ) ;
// execute queries
bool pdo_sqlsrv_dbh_prepare ( _Inout_ pdo_dbh_t * dbh , _In_ zend_string * sql ,
_Inout_ pdo_stmt_t * stmt , _In_ zval * driver_options ) ;
zend_long pdo_sqlsrv_dbh_do ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * sql ) ;
// quote a string, meaning put quotes around it and escape any quotes within it
zend_string * pdo_sqlsrv_dbh_quote ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * unquoted , _In_ enum pdo_param_type paramtype ) ;
// transaction support functions
bool pdo_sqlsrv_dbh_commit ( _Inout_ pdo_dbh_t * dbh ) ;
bool pdo_sqlsrv_dbh_begin ( _Inout_ pdo_dbh_t * dbh ) ;
bool pdo_sqlsrv_dbh_rollback ( _Inout_ pdo_dbh_t * dbh ) ;
// attribute functions
bool pdo_sqlsrv_dbh_set_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * val ) ;
int pdo_sqlsrv_dbh_get_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * return_value ) ;
2017-07-19 23:10:53 +02:00
// return the last id generated by an executed SQL statement
2021-07-29 00:45:04 +02:00
zend_string * pdo_sqlsrv_dbh_last_id ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * name ) ;
// return more information
void pdo_sqlsrv_dbh_return_error ( _In_ pdo_dbh_t * dbh , _In_opt_ pdo_stmt_t * stmt , _Out_ zval * info ) ;
# endif
2017-07-19 23:10:53 +02:00
// additional methods are supported in this function
2020-04-21 00:17:21 +02:00
pdo_sqlsrv_function_entry * pdo_sqlsrv_get_driver_methods ( _Inout_ pdo_dbh_t * dbh , int kind ) ;
2017-07-19 23:10:53 +02:00
struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
pdo_sqlsrv_dbh_close ,
pdo_sqlsrv_dbh_prepare ,
pdo_sqlsrv_dbh_do ,
pdo_sqlsrv_dbh_quote ,
pdo_sqlsrv_dbh_begin ,
pdo_sqlsrv_dbh_commit ,
pdo_sqlsrv_dbh_rollback ,
pdo_sqlsrv_dbh_set_attr ,
pdo_sqlsrv_dbh_last_id ,
pdo_sqlsrv_dbh_return_error ,
pdo_sqlsrv_dbh_get_attr ,
2021-08-13 19:57:19 +02:00
NULL , // check liveness not implemented
2017-07-19 23:10:53 +02:00
pdo_sqlsrv_get_driver_methods ,
2021-08-13 19:57:19 +02:00
NULL , // request shutdown not implemented
# if PHP_VERSION_ID < 80100
NULL // in transaction not implemented
2017-07-19 23:10:53 +02:00
} ;
2021-08-13 19:57:19 +02:00
# else
NULL , // in transaction not implemented
NULL // get_gc not implemented
} ;
# endif
2017-07-19 23:10:53 +02:00
// log a function entry point
# define PDO_LOG_DBH_ENTRY \
{ \
pdo_sqlsrv_dbh * driver_dbh = reinterpret_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ; \
2020-03-25 17:53:18 +01:00
if ( driver_dbh ! = NULL ) driver_dbh - > set_func ( __FUNCTION__ ) ; \
core_sqlsrv_register_severity_checker ( pdo_severity_check ) ; \
LOG ( SEV_NOTICE , " %1!s!: entering " , __FUNCTION__ ) ; \
2017-07-19 23:10:53 +02:00
}
// constructor for the internal object for connections
2020-04-21 00:17:21 +02:00
pdo_sqlsrv_dbh : : pdo_sqlsrv_dbh ( _In_ SQLHANDLE h , _In_ error_callback e , _In_ void * driver ) :
sqlsrv_conn ( h , e , driver , SQLSRV_ENCODING_UTF8 ) ,
2017-07-19 23:10:53 +02:00
stmts ( NULL ) ,
direct_query ( false ) ,
query_timeout ( QUERY_TIMEOUT_INVALID ) ,
client_buffer_max_size ( PDO_SQLSRV_G ( client_buffer_max_size ) ) ,
2018-09-18 01:24:52 +02:00
fetch_numeric ( false ) ,
2018-11-28 02:18:38 +01:00
fetch_datetime ( false ) ,
format_decimals ( false ) ,
2019-10-24 00:12:52 +02:00
decimal_places ( NO_CHANGE_DECIMAL_PLACES ) ,
2021-11-02 16:12:09 +01:00
use_national_characters ( CHARSET_PREFERENCE_NOT_SPECIFIED ) ,
emulate_prepare ( false )
2017-07-19 23:10:53 +02:00
{
if ( client_buffer_max_size < 0 ) {
client_buffer_max_size = sqlsrv_buffered_result_set : : BUFFERED_QUERY_LIMIT_DEFAULT ;
LOG ( SEV_WARNING , INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value. " ) ;
}
}
// pdo_sqlsrv_db_handle_factory
// Maps to PDO::__construct.
// Factory method called by the PDO driver manager to create a SQLSRV PDO connection.
// Does the following things:
// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION.
// (If an error occurs in this function, the PDO specification mandates that
// an exception be thrown, regardless of the error mode setting.)
// 2. Processes the driver options.
// 3. Creates a core_conn object by calling core_sqlsrv_connect.
// 4. Restores the previous error mode on success.
// alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory
// Parameters:
// dbh - The PDO managed structure for the connection.
// driver_options - A HashTable (within the zval) of options to use when creating the connection.
// Return:
// 0 for failure, 1 for success.
2020-04-21 00:17:21 +02:00
int pdo_sqlsrv_db_handle_factory ( _Inout_ pdo_dbh_t * dbh , _In_opt_ zval * driver_options )
2017-07-19 23:10:53 +02:00
{
2020-03-25 17:53:18 +01:00
PDO_LOG_DBH_ENTRY ;
2017-07-19 23:10:53 +02:00
hash_auto_ptr pdo_conn_options_ht ;
pdo_error_mode prev_err_mode = dbh - > error_mode ;
// must be done in all cases so that even a failed connection can query the
// object for errors.
dbh - > methods = & pdo_sqlsrv_dbh_methods ;
dbh - > driver_data = NULL ;
zval * temp_server_z = NULL ;
sqlsrv_malloc_auto_ptr < conn_string_parser > dsn_parser ;
zval server_z ;
ZVAL_UNDEF ( & server_z ) ;
try {
// no matter what the error mode, we want exceptions thrown if the connection fails
// to happen (per the PDO spec)
dbh - > error_mode = PDO_ERRMODE_EXCEPTION ;
g_pdo_henv_cp - > set_driver ( dbh ) ;
g_pdo_henv_ncp - > set_driver ( dbh ) ;
CHECK_CUSTOM_ERROR ( driver_options & & Z_TYPE_P ( driver_options ) ! = IS_ARRAY , * g_pdo_henv_cp , SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) {
throw core : : CoreException ( ) ;
}
// throws PDOException if the ATTR_PERSISTENT is in connection options
CHECK_CUSTOM_ERROR ( dbh - > is_persistent , * g_pdo_henv_cp , PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) {
dbh - > refcount - - ;
throw pdo : : PDOException ( ) ;
}
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE ( pdo_conn_options_ht ) ;
core : : sqlsrv_zend_hash_init ( * g_pdo_henv_cp , pdo_conn_options_ht , 10 /* # of buckets */ ,
2020-04-21 00:17:21 +02:00
ZVAL_PTR_DTOR , 0 /*persistent*/ ) ;
2017-07-19 23:10:53 +02:00
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
dsn_parser = new ( sqlsrv_malloc ( sizeof ( conn_string_parser ) ) ) conn_string_parser ( * g_pdo_henv_cp , dbh - > data_source ,
static_cast < int > ( dbh - > data_source_len ) , pdo_conn_options_ht ) ;
2020-04-21 00:17:21 +02:00
dsn_parser - > parse_conn_string ( ) ;
2017-07-19 23:10:53 +02:00
// Extract the server name
temp_server_z = zend_hash_index_find ( pdo_conn_options_ht , PDO_CONN_OPTION_SERVER ) ;
CHECK_CUSTOM_ERROR ( ( temp_server_z = = NULL ) , g_pdo_henv_cp , PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) {
throw pdo : : PDOException ( ) ;
}
server_z = * temp_server_z ;
// Add a reference to the option value since we are deleting it from the hashtable
zval_add_ref ( & server_z ) ;
zend_hash_index_del ( pdo_conn_options_ht , PDO_CONN_OPTION_SERVER ) ;
sqlsrv_conn * conn = core_sqlsrv_connect ( * g_pdo_henv_cp , * g_pdo_henv_ncp , core : : allocate_conn < pdo_sqlsrv_dbh > , Z_STRVAL ( server_z ) ,
dbh - > username , dbh - > password , pdo_conn_options_ht , pdo_sqlsrv_handle_dbh_error ,
2020-04-21 00:17:21 +02:00
PDO_CONN_OPTS , dbh , " pdo_sqlsrv_db_handle_factory " ) ;
2017-07-19 23:10:53 +02:00
// Free the string in server_z after being used
zend_string_release ( Z_STR ( server_z ) ) ;
SQLSRV_ASSERT ( conn ! = NULL , " Invalid connection returned. Exception should have been thrown. " ) ;
// set the driver_data and methods to complete creation of the PDO object
dbh - > driver_data = conn ;
dbh - > error_mode = prev_err_mode ; // reset the error mode
dbh - > alloc_own_columns = 1 ; // we do our own memory management for columns
dbh - > native_case = PDO_CASE_NATURAL ; // SQL Server supports mixed case types
}
catch ( core : : CoreException & ) {
if ( Z_TYPE ( server_z ) = = IS_STRING ) {
zend_string_release ( Z_STR ( server_z ) ) ;
}
dbh - > error_mode = prev_err_mode ; // reset the error mode
g_pdo_henv_cp - > last_error ( ) . reset ( ) ; // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL
return 0 ;
}
catch ( . . . ) {
DIE ( " pdo_sqlsrv_db_handle_factory: Unknown exception caught " ) ;
}
return 1 ;
}
// pdo_sqlsrv_dbh_close
// Maps to PDO::__destruct.
// Called when a PDO object is to be destroyed.
// By the time this function is called, PDO has already made sure that
// all statements are disposed and the PDO object is the last item destroyed.
// Parameters:
// dbh - The PDO managed connection object.
// Return:
2021-07-29 00:45:04 +02:00
// Always returns 1 for success. (for PHP_VERSION_ID < 80100)
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-04-21 00:17:21 +02:00
int pdo_sqlsrv_dbh_close ( _Inout_ pdo_dbh_t * dbh )
2021-07-29 00:45:04 +02:00
# else
void pdo_sqlsrv_dbh_close ( _Inout_ pdo_dbh_t * dbh )
# endif
2017-07-19 23:10:53 +02:00
{
LOG ( SEV_NOTICE , " pdo_sqlsrv_dbh_close: entering " ) ;
// if the connection didn't complete properly, driver_data isn't initialized.
if ( dbh - > driver_data = = NULL ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return ;
# endif
2017-07-19 23:10:53 +02:00
}
PDO_RESET_DBH_ERROR ;
// call the core layer close
2020-04-21 00:17:21 +02:00
core_sqlsrv_close ( reinterpret_cast < sqlsrv_conn * > ( dbh - > driver_data ) ) ;
2017-07-19 23:10:53 +02:00
dbh - > driver_data = NULL ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
// always return success that the connection is closed
return 1 ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_prepare
// Called by PDO::prepare and PDOStatement::__construct.
// Creates a statement and prepares it for execution by PDO
// Paramters:
// dbh - The PDO managed connection object.
// sql - SQL query to be prepared.
// sql_len - Length of the sql query
// stmt - The PDO managed statement object.
// driver_options - User provided list of statement options.
// Return:
// 0 for failure, 1 for success.
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_prepare ( _Inout_ pdo_dbh_t * dbh , _In_reads_ ( sql_len ) const char * sql ,
_Inout_ size_t sql_len , _Inout_ pdo_stmt_t * stmt , _In_ zval * driver_options )
# else
bool pdo_sqlsrv_dbh_prepare ( _Inout_ pdo_dbh_t * dbh , _In_ zend_string * sql_zstr , _Inout_ pdo_stmt_t * stmt , _In_ zval * driver_options )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
hash_auto_ptr pdo_stmt_options_ht ;
sqlsrv_malloc_auto_ptr < char > sql_rewrite ;
size_t sql_rewrite_len = 0 ;
sqlsrv_malloc_auto_ptr < pdo_sqlsrv_stmt > driver_stmt ;
hash_auto_ptr placeholders ;
sqlsrv_malloc_auto_ptr < sql_string_parser > sql_parser ;
pdo_sqlsrv_dbh * driver_dbh = reinterpret_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( ( driver_dbh ! = NULL ) , " pdo_sqlsrv_dbh_prepare: dbh->driver_data was null " ) ;
try {
// assign the methods for the statement object. This is necessary even if the
// statement fails so the user can retrieve the error information.
stmt - > methods = & pdo_sqlsrv_stmt_methods ;
2021-11-02 16:12:09 +01:00
// if not emulate_prepare, we support parameterized queries with ?, not names
stmt - > supports_placeholders = ( driver_dbh - > emulate_prepare ) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL ; // the statement options may override this later
2017-07-19 23:10:53 +02:00
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE ( pdo_stmt_options_ht ) ;
core : : sqlsrv_zend_hash_init ( * driver_dbh , pdo_stmt_options_ht , 3 /* # of buckets */ ,
2020-04-21 00:17:21 +02:00
ZVAL_PTR_DTOR , 0 /*persistent*/ ) ;
2017-07-19 23:10:53 +02:00
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
2020-04-21 00:17:21 +02:00
validate_stmt_options ( * driver_dbh , driver_options , pdo_stmt_options_ht ) ;
2017-07-19 23:10:53 +02:00
driver_stmt = static_cast < pdo_sqlsrv_stmt * > ( core_sqlsrv_create_stmt ( driver_dbh , core : : allocate_stmt < pdo_sqlsrv_stmt > ,
pdo_stmt_options_ht , PDO_STMT_OPTS ,
2020-04-21 00:17:21 +02:00
pdo_sqlsrv_handle_stmt_error , stmt ) ) ;
2017-07-19 23:10:53 +02:00
// if the user didn't set anything in the prepare options, then set the buffer limit
// to the value set on the connection.
if ( driver_stmt - > buffered_query_limit = = sqlsrv_buffered_result_set : : BUFFERED_QUERY_LIMIT_INVALID ) {
driver_stmt - > buffered_query_limit = driver_dbh - > client_buffer_max_size ;
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID >= 80100
2021-07-29 00:45:04 +02:00
zend_string * sql_rewrite_zstr = NULL ;
2021-09-07 20:04:53 +02:00
const char * sql = ZSTR_VAL ( sql_zstr ) ;
size_t sql_len = ZSTR_LEN ( sql_zstr ) ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
// rewrite named parameters in the query to positional parameters if we aren't letting PDO do the
// parameter substitution for us
if ( stmt - > supports_placeholders ! = PDO_PLACEHOLDER_NONE ) {
// rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named
// parameters for consistency with the PDO MySQL and PDO ODBC drivers.
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-04-21 00:17:21 +02:00
int zr = pdo_parse_params ( stmt , const_cast < char * > ( sql ) , sql_len , & sql_rewrite , & sql_rewrite_len ) ;
2021-07-29 00:45:04 +02:00
CHECK_ZEND_ERROR ( zr , driver_dbh , PDO_SQLSRV_ERROR_PARAM_PARSE ) {
2017-07-19 23:10:53 +02:00
throw core : : CoreException ( ) ;
}
// if parameter substitution happened, use that query instead of the original
2021-07-29 00:45:04 +02:00
if ( sql_rewrite ! = 0 ) {
2017-07-19 23:10:53 +02:00
sql = sql_rewrite ;
sql_len = sql_rewrite_len ;
}
2021-07-29 00:45:04 +02:00
# else
int zr = pdo_parse_params ( stmt , sql_zstr , & sql_rewrite_zstr ) ;
CHECK_ZEND_ERROR ( zr , driver_dbh , PDO_SQLSRV_ERROR_PARAM_PARSE ) {
throw core : : CoreException ( ) ;
}
// if parameter substitution happened, use that query instead of the original
if ( sql_rewrite_zstr ! = NULL ) {
sql = ZSTR_VAL ( sql_rewrite_zstr ) ;
sql_len = ZSTR_LEN ( sql_rewrite_zstr ) ;
}
# endif
2017-07-19 23:10:53 +02:00
}
if ( ! driver_stmt - > direct_query & & stmt - > supports_placeholders ! = PDO_PLACEHOLDER_NONE ) {
2021-07-29 00:45:04 +02:00
core_sqlsrv_prepare ( driver_stmt , sql , sql_len ) ;
2017-07-19 23:10:53 +02:00
}
else if ( driver_stmt - > direct_query ) {
if ( driver_stmt - > direct_query_subst_string ) {
// we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme
// and we use estrdup below to allocate the new string, which uses emalloc
efree ( reinterpret_cast < void * > ( const_cast < char * > ( driver_stmt - > direct_query_subst_string ) ) ) ;
}
driver_stmt - > direct_query_subst_string = estrdup ( sql ) ;
driver_stmt - > direct_query_subst_string_len = sql_len ;
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID >= 80100
2021-07-29 00:45:04 +02:00
if ( sql_rewrite_zstr ! = NULL ) {
zend_string_release ( sql_rewrite_zstr ) ;
}
# endif
2017-07-19 23:10:53 +02:00
// else if stmt->support_placeholders == PDO_PLACEHOLDER_NONE means that stmt->active_query_string will be
// set to the substituted query
if ( stmt - > supports_placeholders = = PDO_PLACEHOLDER_NONE ) {
2021-07-29 00:45:04 +02:00
// parse placeholders in the sql query into the placeholders ht
2017-07-19 23:10:53 +02:00
ALLOC_HASHTABLE ( placeholders ) ;
2021-07-29 00:45:04 +02:00
core : : sqlsrv_zend_hash_init ( * driver_dbh , placeholders , 5 , ZVAL_PTR_DTOR /* dtor */ , 0 /* persistent */ ) ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
sql_parser = new ( sqlsrv_malloc ( sizeof ( sql_string_parser ) ) ) sql_string_parser ( * driver_dbh , stmt - > query_string ,
static_cast < int > ( stmt - > query_stringlen ) , placeholders ) ;
# else
sql_parser = new ( sqlsrv_malloc ( sizeof ( sql_string_parser ) ) ) sql_string_parser ( * driver_dbh , ZSTR_VAL ( stmt - > query_string ) ,
ZSTR_LEN ( stmt - > query_string ) , placeholders ) ;
# endif
2020-04-21 00:17:21 +02:00
sql_parser - > parse_sql_string ( ) ;
2017-07-19 23:10:53 +02:00
driver_stmt - > placeholders = placeholders ;
placeholders . transferred ( ) ;
}
stmt - > driver_data = driver_stmt ;
driver_stmt . transferred ( ) ;
}
// everything is cleaned up by this point
// catch everything so the exception doesn't spill into the calling PDO code
catch ( core : : CoreException & ) {
if ( driver_stmt ) {
driver_stmt - > ~ pdo_sqlsrv_stmt ( ) ;
}
// in the event that the statement caused an error that was copied to the connection, update the
// connection with the error's SQLSTATE.
if ( driver_dbh - > last_error ( ) ) {
strcpy_s ( dbh - > error_code , sizeof ( dbh - > error_code ) ,
reinterpret_cast < const char * > ( driver_dbh - > last_error ( ) - > sqlstate ) ) ;
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
// catch any errant exception and die
catch ( . . . ) {
DIE ( " pdo_sqlsrv_dbh_prepare: Unknown exception caught. " ) ;
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return true ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_do
// Maps to PDO::exec.
// Execute a SQL statement, such as an insert, update or delete, and return
// the number of rows affected.
// Parameters:
// dbh - the PDO connection object, which contains the ODBC handle
// sql - the query to execute
// sql_len - length of sql query
// Return
// # of rows affected, -1 for an error.
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-04-21 00:17:21 +02:00
zend_long pdo_sqlsrv_dbh_do ( _Inout_ pdo_dbh_t * dbh , _In_reads_bytes_ ( sql_len ) const char * sql , _In_ size_t sql_len )
2021-07-29 00:45:04 +02:00
# else
zend_long pdo_sqlsrv_dbh_do ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * sql )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
pdo_sqlsrv_dbh * driver_dbh = static_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
sqlsrv_malloc_auto_ptr < sqlsrv_stmt > driver_stmt ;
SQLLEN rows = 0 ;
// verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong
// thing to happen here.
SQLSRV_STATIC_ASSERT ( sizeof ( rows ) = = sizeof ( SQLLEN ) ) ;
try {
SQLSRV_ASSERT ( sql ! = NULL , " NULL or empty SQL string passed. " ) ;
SQLSRV_ASSERT ( driver_dbh ! = NULL , " pdo_sqlsrv_dbh_do: driver_data object was NULL. " ) ;
// temp PDO statement used for error handling if something happens
pdo_stmt_t temp_stmt ;
temp_stmt . dbh = dbh ;
// allocate a full driver statement to take advantage of the error handling
driver_stmt = core_sqlsrv_create_stmt ( driver_dbh , core : : allocate_stmt < pdo_sqlsrv_stmt > , NULL /*options_ht*/ ,
2020-04-21 00:17:21 +02:00
NULL /*valid_stmt_opts*/ , pdo_sqlsrv_handle_stmt_error , & temp_stmt ) ;
2017-07-19 23:10:53 +02:00
driver_stmt - > set_func ( __FUNCTION__ ) ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
SQLRETURN execReturn = core_sqlsrv_execute ( driver_stmt , sql , static_cast < int > ( sql_len ) ) ;
# else
SQLRETURN execReturn = core_sqlsrv_execute ( driver_stmt , ZSTR_VAL ( sql ) , ZSTR_LEN ( sql ) ) ;
# endif
2017-07-19 23:10:53 +02:00
// since the user can give us a compound statement, we return the row count for the last set, and since the row count
// isn't guaranteed to be valid until all the results have been fetched, we fetch them all first.
2020-04-21 00:17:21 +02:00
if ( execReturn ! = SQL_NO_DATA & & core_sqlsrv_has_any_result ( driver_stmt ) ) {
2017-07-19 23:10:53 +02:00
SQLRETURN r = SQL_SUCCESS ;
do {
2020-04-21 00:17:21 +02:00
rows = core : : SQLRowCount ( driver_stmt ) ;
2017-07-19 23:10:53 +02:00
2020-04-21 00:17:21 +02:00
r = core : : SQLMoreResults ( driver_stmt ) ;
2017-07-19 23:10:53 +02:00
} while ( r ! = SQL_NO_DATA ) ;
}
// returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases
// naturally, so we override that here with no rows returned.
if ( rows = = - 1 ) {
rows = 0 ;
}
}
catch ( core : : CoreException & ) {
// copy any errors on the statement to the connection so that the user sees them, since the statement is released
// before this method returns
strcpy_s ( dbh - > error_code , sizeof ( dbh - > error_code ) ,
reinterpret_cast < const char * > ( driver_stmt - > last_error ( ) - > sqlstate ) ) ;
driver_dbh - > set_last_error ( driver_stmt - > last_error ( ) ) ;
if ( driver_stmt ) {
driver_stmt - > ~ sqlsrv_stmt ( ) ;
}
return - 1 ;
}
catch ( . . . ) {
DIE ( " pdo_sqlsrv_dbh_do: Unknown exception caught. " ) ;
}
if ( driver_stmt ) {
driver_stmt - > ~ sqlsrv_stmt ( ) ;
}
return rows ;
}
// transaction support functions
// pdo_sqlsrv_dbh_begin
// Maps to PDO::beginTransaction.
// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn
// flag is maintained by PDO so we dont have to worry about it.
// Parameters:
// dbh - The PDO managed connection object.
// Return:
2021-07-29 00:45:04 +02:00
// 0 for failure and 1 for success. (if PHP_VERSION_ID < 80100)
// Return true if currently inside a transaction, false otherwise
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_begin ( _Inout_ pdo_dbh_t * dbh )
# else
bool pdo_sqlsrv_dbh_begin ( _Inout_ pdo_dbh_t * dbh )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
try {
SQLSRV_ASSERT ( dbh ! = NULL , " pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null " ) ;
sqlsrv_conn * driver_conn = reinterpret_cast < sqlsrv_conn * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_conn ! = NULL , " pdo_sqlsrv_dbh_begin: driver_data object was null " ) ;
DEBUG_SQLSRV_ASSERT ( ! dbh - > in_txn , " pdo_sqlsrv_dbh_begin: Already in transaction " ) ;
2020-04-21 00:17:21 +02:00
core_sqlsrv_begin_transaction ( driver_conn ) ;
2017-07-19 23:10:53 +02:00
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return true ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( . . . ) {
DIE ( " pdo_sqlsrv_dbh_begin: Uncaught exception occurred. " ) ;
}
2020-10-15 04:38:33 +02:00
// Should not have reached here but adding this due to compilation warnings
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-10-15 04:38:33 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_commit
// Maps to PDO::commit.
// Commits a transaction. Returns the connection to auto-commit mode.
// PDO throws error if PDO::commit is called on a connection that is not in an active
// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here.
// Parameters:
// dbh - The PDO managed connection object.
// Return:
2021-07-29 00:45:04 +02:00
// 0 for failure and 1 for success. (if PHP_VERSION_ID < 80100)
// Return true for success and false otherwise
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_commit ( _Inout_ pdo_dbh_t * dbh )
# else
bool pdo_sqlsrv_dbh_commit ( _Inout_ pdo_dbh_t * dbh )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
try {
SQLSRV_ASSERT ( dbh ! = NULL , " pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null " ) ;
sqlsrv_conn * driver_conn = reinterpret_cast < sqlsrv_conn * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_conn ! = NULL , " pdo_sqlsrv_dbh_commit: driver_data object was null " ) ;
DEBUG_SQLSRV_ASSERT ( dbh - > in_txn , " pdo_sqlsrv_dbh_commit: Not in transaction " ) ;
2020-04-21 00:17:21 +02:00
core_sqlsrv_commit ( driver_conn ) ;
2017-07-19 23:10:53 +02:00
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return true ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( . . . ) {
DIE ( " pdo_sqlsrv_dbh_commit: Uncaught exception occurred. " ) ;
}
2021-07-29 00:45:04 +02:00
2020-10-15 04:38:33 +02:00
// Should not have reached here but adding this due to compilation warnings
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-10-15 04:38:33 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_rollback
// Maps to PDO::rollback.
// Rolls back a transaction. Returns the connection in auto-commit mode.
// PDO throws error if PDO::rollBack is called on a connection that is not in an active
// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here.
// Parameters:
// dbh - The PDO managed connection object.
// Return:
2021-07-29 00:45:04 +02:00
// 0 for failure and 1 for success. (if PHP_VERSION_ID < 80100)
// Return true for success and false otherwise
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_rollback ( _Inout_ pdo_dbh_t * dbh )
# else
bool pdo_sqlsrv_dbh_rollback ( _Inout_ pdo_dbh_t * dbh )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
try {
SQLSRV_ASSERT ( dbh ! = NULL , " pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null " ) ;
sqlsrv_conn * driver_conn = reinterpret_cast < sqlsrv_conn * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_conn ! = NULL , " pdo_sqlsrv_dbh_rollback: driver_data object was null " ) ;
DEBUG_SQLSRV_ASSERT ( dbh - > in_txn , " pdo_sqlsrv_dbh_rollback: Not in transaction " ) ;
2020-04-21 00:17:21 +02:00
core_sqlsrv_rollback ( driver_conn ) ;
2017-07-19 23:10:53 +02:00
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return true ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( . . . ) {
DIE ( " pdo_sqlsrv_dbh_rollback: Uncaught exception occurred. " ) ;
}
2020-10-15 04:38:33 +02:00
// Should not have reached here but adding this due to compilation warnings
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2020-10-15 04:38:33 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_set_attr
// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object.
// PDO driver manager calls this function directly after calling the factory
// method for PDO, for any attribute which is specified in the PDO constructor.
// Parameters:
// dbh - The PDO connection object maintained by PDO.
// attr - The attribute to be set.
// val - The value of the attribute to be set.
// Return:
2021-07-29 00:45:04 +02:00
// 0 for failure, 1 for success. (if PHP_VERSION_ID < 80100)
// Return true on success and false in case of failure
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_set_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * val )
# else
bool pdo_sqlsrv_dbh_set_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * val )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
pdo_sqlsrv_dbh * driver_dbh = static_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_dbh ! = NULL , " pdo_sqlsrv_dbh_set_attr: driver_data object was NULL. " ) ;
try {
switch ( attr ) {
case SQLSRV_ATTR_ENCODING :
{
zend_long attr_value ;
if ( Z_TYPE_P ( val ) ! = IS_LONG ) {
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_INVALID_ENCODING ) ;
}
attr_value = Z_LVAL_P ( val ) ;
switch ( attr_value ) {
case SQLSRV_ENCODING_DEFAULT :
// when default is applied to a connection, that means use UTF-8 encoding
driver_dbh - > set_encoding ( SQLSRV_ENCODING_UTF8 ) ;
break ;
case SQLSRV_ENCODING_SYSTEM :
case SQLSRV_ENCODING_UTF8 :
driver_dbh - > set_encoding ( static_cast < SQLSRV_ENCODING > ( attr_value ) ) ;
break ;
default :
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_INVALID_ENCODING ) ;
break ;
}
}
break ;
case SQLSRV_ATTR_DIRECT_QUERY :
2021-05-19 01:56:49 +02:00
driver_dbh - > direct_query = zend_is_true ( val ) ;
2017-07-19 23:10:53 +02:00
break ;
case SQLSRV_ATTR_QUERY_TIMEOUT :
if ( Z_TYPE_P ( val ) ! = IS_LONG | | Z_LVAL_P ( val ) < 0 ) {
convert_to_string ( val ) ;
THROW_PDO_ERROR ( driver_dbh , SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE , Z_STRVAL_P ( val ) ) ;
}
driver_dbh - > query_timeout = static_cast < long > ( Z_LVAL_P ( val ) ) ;
break ;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE :
if ( Z_TYPE_P ( val ) ! = IS_LONG | | Z_LVAL_P ( val ) < = 0 ) {
convert_to_string ( val ) ;
THROW_PDO_ERROR ( driver_dbh , SQLSRV_ERROR_INVALID_BUFFER_LIMIT , Z_STRVAL_P ( val ) ) ;
}
driver_dbh - > client_buffer_max_size = Z_LVAL_P ( val ) ;
break ;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE :
2021-05-19 01:56:49 +02:00
driver_dbh - > fetch_numeric = zend_is_true ( val ) ;
2017-07-19 23:10:53 +02:00
break ;
2018-09-18 01:24:52 +02:00
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE :
2021-05-19 01:56:49 +02:00
driver_dbh - > fetch_datetime = zend_is_true ( val ) ;
2018-09-18 01:24:52 +02:00
break ;
2018-11-28 02:18:38 +01:00
case SQLSRV_ATTR_FORMAT_DECIMALS :
2021-05-19 01:56:49 +02:00
driver_dbh - > format_decimals = zend_is_true ( val ) ;
2018-11-28 02:18:38 +01:00
break ;
case SQLSRV_ATTR_DECIMAL_PLACES :
{
// first check if the input is an integer
if ( Z_TYPE_P ( val ) ! = IS_LONG ) {
THROW_PDO_ERROR ( driver_dbh , SQLSRV_ERROR_INVALID_DECIMAL_PLACES ) ;
}
zend_long decimal_places = Z_LVAL_P ( val ) ;
if ( decimal_places < 0 | | decimal_places > SQL_SERVER_MAX_MONEY_SCALE ) {
// ignore decimal_places as this is out of range
decimal_places = NO_CHANGE_DECIMAL_PLACES ;
}
driver_dbh - > decimal_places = static_cast < short > ( decimal_places ) ;
}
break ;
2020-01-23 20:55:33 +01:00
# if PHP_VERSION_ID >= 70200
2019-10-24 00:12:52 +02:00
case PDO_ATTR_DEFAULT_STR_PARAM :
{
if ( Z_TYPE_P ( val ) ! = IS_LONG ) {
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_EXTENDED_STRING_TYPE_INVALID ) ;
}
zend_long value = Z_LVAL_P ( val ) ;
if ( value = = PDO_PARAM_STR_NATL ) {
driver_dbh - > use_national_characters = 1 ;
}
else if ( value = = PDO_PARAM_STR_CHAR ) {
driver_dbh - > use_national_characters = 0 ;
}
else {
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_EXTENDED_STRING_TYPE_INVALID ) ;
}
}
break ;
# endif
2017-07-19 23:10:53 +02:00
// Not supported
case PDO_ATTR_FETCH_TABLE_NAMES :
case PDO_ATTR_FETCH_CATALOG_NAMES :
case PDO_ATTR_PREFETCH :
case PDO_ATTR_MAX_COLUMN_LEN :
case PDO_ATTR_CURSOR_NAME :
case PDO_ATTR_AUTOCOMMIT :
case PDO_ATTR_PERSISTENT :
case PDO_ATTR_TIMEOUT :
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) ;
}
// Read-only
case PDO_ATTR_SERVER_VERSION :
case PDO_ATTR_SERVER_INFO :
case PDO_ATTR_CLIENT_VERSION :
case PDO_ATTR_DRIVER_NAME :
case PDO_ATTR_CONNECTION_STATUS :
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ) ;
}
case PDO_ATTR_EMULATE_PREPARES :
2021-11-02 16:12:09 +01:00
{
driver_dbh - > emulate_prepare = zend_is_true ( val ) ;
if ( driver_dbh - > emulate_prepare & & driver_dbh - > ce_option . enabled ) {
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED ) ;
}
}
break ;
// Statement level only
2017-07-19 23:10:53 +02:00
case PDO_ATTR_CURSOR :
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE :
2019-05-01 17:03:33 +02:00
case SQLSRV_ATTR_DATA_CLASSIFICATION :
2017-07-19 23:10:53 +02:00
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ) ;
}
default :
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ) ;
break ;
}
}
}
catch ( pdo : : PDOException & ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return false ;
# endif
2017-07-19 23:10:53 +02:00
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return true ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_get_attr
// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object.
// Parameters:
// dbh - The PDO connection object maintained by PDO.
// attr - The attribute to get.
// return_value - zval in which to return the attribute value.
// Return:
2021-07-29 00:45:04 +02:00
// 0 for failure, 1 for success. (if PHP_VERSION_ID < 80100)
// There are 3 return states:
// -1 for errors while retrieving a valid attribute
// 0 for attempting to retrieve an attribute which is not supported by the driver
// any other value for success, *return_value must be set to the attribute value
int pdo_sqlsrv_dbh_get_attr ( _Inout_ pdo_dbh_t * dbh , _In_ zend_long attr , _Inout_ zval * return_value )
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
pdo_sqlsrv_dbh * driver_dbh = static_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_dbh ! = NULL , " pdo_sqlsrv_dbh_get_attr: driver_data object was NULL. " ) ;
try {
switch ( attr ) {
// Not supported
case PDO_ATTR_FETCH_TABLE_NAMES :
case PDO_ATTR_FETCH_CATALOG_NAMES :
case PDO_ATTR_PREFETCH :
case PDO_ATTR_MAX_COLUMN_LEN :
case PDO_ATTR_CURSOR_NAME :
case PDO_ATTR_AUTOCOMMIT :
case PDO_ATTR_TIMEOUT :
{
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
// PDO does not throw "not supported" error message for these attributes.
2021-07-29 00:45:04 +02:00
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) ;
# else
return 0 ;
# endif
2017-07-19 23:10:53 +02:00
}
case PDO_ATTR_EMULATE_PREPARES :
2021-11-02 16:12:09 +01:00
{
ZVAL_BOOL ( return_value , driver_dbh - > emulate_prepare ) ;
break ;
}
// Statement level only
2017-07-19 23:10:53 +02:00
case PDO_ATTR_CURSOR :
2019-05-01 17:03:33 +02:00
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE :
case SQLSRV_ATTR_DATA_CLASSIFICATION :
2017-07-19 23:10:53 +02:00
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ) ;
}
case PDO_ATTR_STRINGIFY_FETCHES :
{
// For this attribute, if we dont set the return_value than PDO returns NULL.
ZVAL_BOOL ( return_value , ( dbh - > stringify ? 1 : 0 ) ) ;
break ;
}
case PDO_ATTR_SERVER_INFO :
{
2020-04-21 00:17:21 +02:00
core_sqlsrv_get_server_info ( driver_dbh , return_value ) ;
2017-07-19 23:10:53 +02:00
break ;
}
case PDO_ATTR_SERVER_VERSION :
{
2020-04-21 00:17:21 +02:00
core_sqlsrv_get_server_version ( driver_dbh , return_value ) ;
2017-07-19 23:10:53 +02:00
break ;
}
case PDO_ATTR_CLIENT_VERSION :
{
2020-04-21 00:17:21 +02:00
core_sqlsrv_get_client_info ( driver_dbh , return_value ) ;
2017-07-19 23:10:53 +02:00
//Add the PDO SQLSRV driver's file version
//Declarations below eliminate compiler warnings about string constant to char* conversions
const char * extver = " ExtensionVer " ;
std : : string filever = VER_FILEVERSION_STR ;
2020-07-23 22:46:25 +02:00
add_assoc_string ( return_value , extver , & filever [ 0 ] ) ;
2017-07-19 23:10:53 +02:00
break ;
}
case SQLSRV_ATTR_ENCODING :
{
ZVAL_LONG ( return_value , driver_dbh - > encoding ( ) ) ;
break ;
}
case SQLSRV_ATTR_QUERY_TIMEOUT :
{
ZVAL_LONG ( return_value , ( driver_dbh - > query_timeout = = QUERY_TIMEOUT_INVALID ? 0 : driver_dbh - > query_timeout ) ) ;
break ;
}
case SQLSRV_ATTR_DIRECT_QUERY :
{
ZVAL_BOOL ( return_value , driver_dbh - > direct_query ) ;
break ;
}
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE :
{
ZVAL_LONG ( return_value , driver_dbh - > client_buffer_max_size ) ;
break ;
}
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE :
{
ZVAL_BOOL ( return_value , driver_dbh - > fetch_numeric ) ;
break ;
}
2018-09-18 01:24:52 +02:00
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE :
{
ZVAL_BOOL ( return_value , driver_dbh - > fetch_datetime ) ;
break ;
}
2018-11-28 02:18:38 +01:00
case SQLSRV_ATTR_FORMAT_DECIMALS :
{
ZVAL_BOOL ( return_value , driver_dbh - > format_decimals ) ;
break ;
}
case SQLSRV_ATTR_DECIMAL_PLACES :
{
ZVAL_LONG ( return_value , driver_dbh - > decimal_places ) ;
break ;
}
2020-01-23 20:55:33 +01:00
# if PHP_VERSION_ID >= 70200
2019-10-24 00:12:52 +02:00
case PDO_ATTR_DEFAULT_STR_PARAM :
{
ZVAL_LONG ( return_value , ( driver_dbh - > use_national_characters = = 0 ) ? PDO_PARAM_STR_CHAR : PDO_PARAM_STR_NATL ) ;
break ;
}
# endif
2017-07-19 23:10:53 +02:00
default :
{
THROW_PDO_ERROR ( driver_dbh , PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ) ;
break ;
}
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
return 1 ;
# endif
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 0 ;
2021-07-29 00:45:04 +02:00
# else
return - 1 ;
# endif
2017-07-19 23:10:53 +02:00
}
}
// Called by PDO::errorInfo and PDOStatement::errorInfo.
// Returns the error info.
// Parameters:
// dbh - The PDO managed connection object.
// stmt - The PDO managed statement object.
// info - zval in which to return the error info.
// Return:
// 0 for failure, 1 for success.
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
int pdo_sqlsrv_dbh_return_error ( _In_ pdo_dbh_t * dbh , _In_opt_ pdo_stmt_t * stmt , _Out_ zval * info )
# else
void pdo_sqlsrv_dbh_return_error ( _In_ pdo_dbh_t * dbh , _In_opt_ pdo_stmt_t * stmt , _Out_ zval * info )
# endif
2017-07-19 23:10:53 +02:00
{
SQLSRV_ASSERT ( dbh ! = NULL | | stmt ! = NULL , " Either dbh or stmt must not be NULL to dereference the error. " ) ;
sqlsrv_error * ctx_error = NULL ;
if ( stmt ) {
ctx_error = static_cast < sqlsrv_stmt * > ( stmt - > driver_data ) - > last_error ( ) ;
}
else {
ctx_error = static_cast < sqlsrv_conn * > ( dbh - > driver_data ) - > last_error ( ) ;
}
pdo_sqlsrv_retrieve_context_error ( ctx_error , info ) ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_last_id
// Maps to PDO::lastInsertId.
// Returns the last id generated by an executed SQL statement
// Parameters:
// dbh - The PDO managed connection object.
// name - Table name.
// len - Length of the name.
// Return:
2021-07-29 00:45:04 +02:00
// Returns the last insert id as a string. (if PHP_VERSION_ID < 80100)
// Returning NULL indicates an error condition. The input "name" MIGHT be NULL
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
char * pdo_sqlsrv_dbh_last_id ( _Inout_ pdo_dbh_t * dbh , _In_z_ const char * name , _Out_ size_t * len )
# else
zend_string * pdo_sqlsrv_dbh_last_id ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * name )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
// turn off any error handling for last_id
pdo_error_mode prev_err_mode = dbh - > error_mode ;
dbh - > error_mode = PDO_ERRMODE_SILENT ;
sqlsrv_malloc_auto_ptr < sqlsrv_stmt > driver_stmt ;
pdo_sqlsrv_dbh * driver_dbh = static_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_dbh ! = NULL , " pdo_sqlsrv_dbh_last_id: driver_data object was NULL. " ) ;
2021-04-29 22:41:29 +02:00
char idSTR [ LAST_INSERT_ID_BUFF_LEN ] = { ' \0 ' } ;
char * str = NULL ;
SQLLEN cbID = 0 ;
2017-07-19 23:10:53 +02:00
try {
2021-05-06 03:21:16 +02:00
sqlsrv_malloc_auto_ptr < SQLWCHAR > wsql_string ;
unsigned int wsql_len ;
2017-07-19 23:10:53 +02:00
2021-05-06 03:21:16 +02:00
if ( name = = NULL ) {
wsql_string = utf16_string_from_mbcs_string ( SQLSRV_ENCODING_CHAR , LAST_INSERT_ID_QUERY , sizeof ( LAST_INSERT_ID_QUERY ) , & wsql_len ) ;
} else {
char buffer [ LAST_INSERT_ID_QUERY_MAX_LEN ] = { ' \0 ' } ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-05-06 03:21:16 +02:00
snprintf ( buffer , LAST_INSERT_ID_QUERY_MAX_LEN , SEQUENCE_CURRENT_VALUE_QUERY , name ) ;
2021-07-29 00:45:04 +02:00
# else
snprintf ( buffer , LAST_INSERT_ID_QUERY_MAX_LEN , SEQUENCE_CURRENT_VALUE_QUERY , ZSTR_VAL ( name ) ) ;
# endif
2021-05-06 03:21:16 +02:00
wsql_string = utf16_string_from_mbcs_string ( SQLSRV_ENCODING_CHAR , buffer , sizeof ( buffer ) , & wsql_len ) ;
2017-07-19 23:10:53 +02:00
}
2021-05-06 03:21:16 +02:00
CHECK_CUSTOM_ERROR ( wsql_string = = 0 , driver_stmt , SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE , get_last_error_message ( ) ) {
throw core : : CoreException ( ) ;
2017-07-19 23:10:53 +02:00
}
// temp PDO statement used for error handling if something happens
pdo_stmt_t temp_stmt ;
temp_stmt . dbh = dbh ;
// allocate a full driver statement to take advantage of the error handling
2020-04-21 00:17:21 +02:00
driver_stmt = core_sqlsrv_create_stmt ( driver_dbh , core : : allocate_stmt < pdo_sqlsrv_stmt > , NULL /*options_ht*/ , NULL /*valid_stmt_opts*/ , pdo_sqlsrv_handle_stmt_error , & temp_stmt ) ;
2017-07-19 23:10:53 +02:00
driver_stmt - > set_func ( __FUNCTION__ ) ;
2021-05-06 03:21:16 +02:00
// execute the last insert id query
2020-04-21 00:17:21 +02:00
core : : SQLExecDirectW ( driver_stmt , wsql_string ) ;
core : : SQLFetchScroll ( driver_stmt , SQL_FETCH_NEXT , 0 ) ;
2021-09-07 20:04:53 +02:00
2021-04-29 22:41:29 +02:00
SQLRETURN r = core : : SQLGetData ( driver_stmt , 1 , SQL_C_CHAR , idSTR , LAST_INSERT_ID_BUFF_LEN , & cbID , false ) ;
CHECK_CUSTOM_ERROR ( ( ! SQL_SUCCEEDED ( r ) | | cbID = = SQL_NULL_DATA | | cbID = = SQL_NO_TOTAL ) , driver_stmt ,
PDO_SQLSRV_ERROR_LAST_INSERT_ID ) {
2017-07-19 23:10:53 +02:00
throw core : : CoreException ( ) ;
}
driver_stmt - > ~ sqlsrv_stmt ( ) ;
2021-05-06 03:21:16 +02:00
} catch ( core : : CoreException & ) {
2021-11-18 17:14:55 +01:00
// restore error handling to its previous mode
dbh - > error_mode = prev_err_mode ;
2017-07-19 23:10:53 +02:00
// copy any errors on the statement to the connection so that the user sees them, since the statement is released
// before this method returns
strcpy_s ( dbh - > error_code , sizeof ( dbh - > error_code ) ,
reinterpret_cast < const char * > ( driver_stmt - > last_error ( ) - > sqlstate ) ) ;
driver_dbh - > set_last_error ( driver_stmt - > last_error ( ) ) ;
if ( driver_stmt ) {
driver_stmt - > ~ sqlsrv_stmt ( ) ;
}
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
* len = 0 ;
2021-04-29 22:41:29 +02:00
str = reinterpret_cast < char * > ( sqlsrv_malloc ( 0 , sizeof ( char ) , 1 ) ) ; // return an empty string with a null terminator
str [ 0 ] = ' \0 ' ;
return str ;
2021-07-29 00:45:04 +02:00
# else
2021-11-19 01:38:19 +01:00
return ZSTR_EMPTY_ALLOC ( ) ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
}
// restore error handling to its previous mode
dbh - > error_mode = prev_err_mode ;
2021-09-07 20:04:53 +02:00
// copy the last ID string and return it
2021-04-29 22:41:29 +02:00
str = reinterpret_cast < char * > ( sqlsrv_malloc ( cbID , sizeof ( char ) , 1 ) ) ; // include space for null terminator
strcpy_s ( str , cbID + 1 , idSTR ) ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-07-29 00:45:04 +02:00
* len = static_cast < size_t > ( cbID ) ;
2021-04-29 22:41:29 +02:00
return str ;
2021-07-29 00:45:04 +02:00
# else
zend_string * zstr = zend_string_init ( str , cbID , 0 ) ;
sqlsrv_free ( str ) ;
return zstr ;
# endif
2017-07-19 23:10:53 +02:00
}
// pdo_sqlsrv_dbh_quote
// Maps to PDO::quote. As the name says, this function quotes a string.
// Always returns a valid string unless memory allocation fails.
// Parameters:
// dbh - The PDO managed connection object.
// unquoted - The unquoted string to be quoted.
// unquoted_len - Length of the unquoted string.
// quoted - Buffer for output string.
// quoted_len - Length of the output string.
// Return:
// 0 for failure, 1 for success.
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
int pdo_sqlsrv_dbh_quote ( _Inout_ pdo_dbh_t * dbh , _In_reads_ ( unquoted_len ) const char * unquoted , _In_ size_t unquoted_len , _Outptr_result_buffer_ ( * quoted_len ) char * * quoted , _Out_ size_t * quoted_len ,
2020-04-21 00:17:21 +02:00
enum pdo_param_type paramtype )
2021-07-29 00:45:04 +02:00
# else
zend_string * pdo_sqlsrv_dbh_quote ( _Inout_ pdo_dbh_t * dbh , _In_ const zend_string * unquoted , _In_ enum pdo_param_type paramtype )
# endif
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR ;
2019-10-24 00:12:52 +02:00
bool use_national_char_set = false ;
pdo_sqlsrv_dbh * driver_dbh = static_cast < pdo_sqlsrv_dbh * > ( dbh - > driver_data ) ;
SQLSRV_ASSERT ( driver_dbh ! = NULL , " pdo_sqlsrv_dbh_quote: driver_data object was NULL. " ) ;
2017-07-19 23:10:53 +02:00
// get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from:
// 1. PDO::quote() - object name is PDO
// 2. PDOStatement::execute() - object name is PDOStatement
zend_execute_data * execute_data = EG ( current_execute_data ) ;
zval * object = getThis ( ) ;
// iterate through parents to find "PDOStatement"
bool is_statement = false ;
if ( object ) {
zend_class_entry * curr_class = ( Z_OBJ_P ( object ) ) - > ce ;
while ( curr_class ! = NULL ) {
if ( strcmp ( reinterpret_cast < const char * > ( curr_class - > name - > val ) , " PDOStatement " ) = = 0 ) {
is_statement = true ;
break ;
}
curr_class = curr_class - > parent ;
}
}
// only change the encoding if quote is called from the statement level (which should only be called when a statement
// is prepared with emulate prepared on)
2018-08-29 00:18:01 +02:00
if ( is_statement ) {
pdo_stmt_t * stmt = Z_PDO_STMT_P ( object ) ;
SQLSRV_ASSERT ( stmt ! = NULL , " pdo_sqlsrv_dbh_quote: stmt object was null " ) ;
2017-07-19 23:10:53 +02:00
// set the encoding to be the encoding of the statement otherwise set to be the encoding of the dbh
2018-08-29 00:18:01 +02:00
pdo_sqlsrv_stmt * driver_stmt = reinterpret_cast < pdo_sqlsrv_stmt * > ( stmt - > driver_data ) ;
SQLSRV_ASSERT ( driver_stmt ! = NULL , " pdo_sqlsrv_dbh_quote: driver_data object was null " ) ;
2019-10-24 00:12:52 +02:00
encoding = driver_stmt - > encoding ( ) ;
if ( encoding = = SQLSRV_ENCODING_INVALID | | encoding = = SQLSRV_ENCODING_DEFAULT ) {
pdo_sqlsrv_dbh * stmt_driver_dbh = reinterpret_cast < pdo_sqlsrv_dbh * > ( stmt - > driver_data ) ;
encoding = stmt_driver_dbh - > encoding ( ) ;
2017-07-19 23:10:53 +02:00
}
2019-10-24 00:12:52 +02:00
2017-07-19 23:10:53 +02:00
// get the placeholder at the current position in driver_stmt->placeholders ht
2018-08-29 00:18:01 +02:00
// Normally it's not a good idea to alter the internal pointer in a hashed array
// (see pull request 634 on GitHub) but in this case this is for internal use only
2017-07-19 23:10:53 +02:00
zval * placeholder = NULL ;
2018-08-29 00:18:01 +02:00
if ( ( placeholder = zend_hash_get_current_data ( driver_stmt - > placeholders ) ) ! = NULL & & zend_hash_move_forward ( driver_stmt - > placeholders ) = = SUCCESS & & stmt - > bound_params ! = NULL ) {
2017-07-19 23:10:53 +02:00
pdo_bound_param_data * param = NULL ;
2018-08-29 00:18:01 +02:00
if ( Z_TYPE_P ( placeholder ) = = IS_STRING ) {
param = reinterpret_cast < pdo_bound_param_data * > ( zend_hash_find_ptr ( stmt - > bound_params , Z_STR_P ( placeholder ) ) ) ;
2017-07-19 23:10:53 +02:00
}
2018-08-29 00:18:01 +02:00
else if ( Z_TYPE_P ( placeholder ) = = IS_LONG ) {
param = reinterpret_cast < pdo_bound_param_data * > ( zend_hash_index_find_ptr ( stmt - > bound_params , Z_LVAL_P ( placeholder ) ) ) ;
2017-07-19 23:10:53 +02:00
}
2018-08-29 00:18:01 +02:00
if ( NULL ! = param ) {
SQLSRV_ENCODING param_encoding = static_cast < SQLSRV_ENCODING > ( Z_LVAL ( param - > driver_params ) ) ;
if ( param_encoding ! = SQLSRV_ENCODING_INVALID ) {
2017-07-19 23:10:53 +02:00
encoding = param_encoding ;
}
}
}
}
2019-10-24 00:12:52 +02:00
use_national_char_set = ( driver_dbh - > use_national_characters = = 1 | | encoding = = SQLSRV_ENCODING_UTF8 ) ;
2020-01-23 20:55:33 +01:00
# if PHP_VERSION_ID >= 70200
if ( ( paramtype & PDO_PARAM_STR_NATL ) = = PDO_PARAM_STR_NATL ) {
use_national_char_set = true ;
}
if ( ( paramtype & PDO_PARAM_STR_CHAR ) = = PDO_PARAM_STR_CHAR ) {
use_national_char_set = false ;
2019-10-24 00:12:52 +02:00
}
# endif
2021-09-07 20:04:53 +02:00
if ( encoding = = SQLSRV_ENCODING_BINARY ) {
# if PHP_VERSION_ID < 80100
* quoted_len = ( unquoted_len * 2 ) + 2 ; // each character will be converted to 2 hex digits and prepend '0x' to the result
* quoted = reinterpret_cast < char * > ( sqlsrv_malloc ( * quoted_len , sizeof ( char ) , 1 ) ) ; // include space for null terminator
memset ( * quoted , ' \0 ' , * quoted_len + 1 ) ;
unsigned int pos = 0 ;
( * quoted ) [ pos + + ] = ' 0 ' ;
( * quoted ) [ pos + + ] = ' x ' ;
for ( size_t index = 0 ; index < unquoted_len & & unquoted [ index ] ! = ' \0 ' ; + + index ) {
// On success, snprintf returns the total number of characters written
// On failure, a negative number is returned
// The generated string has a length of at most len - 1, so
2021-04-29 22:41:29 +02:00
// len is 3 (2 hex digits + 1)
2021-08-13 19:57:19 +02:00
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf ( ( char * ) ( * quoted + pos ) , 3 , " %02X " , unquoted [ index ] & 0x000000FF ) ;
2021-09-07 20:04:53 +02:00
if ( n < 0 ) {
// Something went wrong, simply return 0 (failure)
return 0 ;
}
pos + = 2 ;
}
return 1 ;
2021-07-29 00:45:04 +02:00
# else
2021-09-07 20:04:53 +02:00
size_t unquoted_len = ZSTR_LEN ( unquoted ) ;
2021-07-29 00:45:04 +02:00
const char * unquoted_str = ZSTR_VAL ( unquoted ) ;
2021-09-07 20:04:53 +02:00
sqlsrv_malloc_auto_ptr < char > quoted ;
2021-07-29 00:45:04 +02:00
size_t quoted_len = ( unquoted_len * 2 ) + 2 ; // each character will be converted to 2 hex digits and prepend '0x' to the result
2021-09-07 20:04:53 +02:00
quoted = reinterpret_cast < char * > ( sqlsrv_malloc ( quoted_len , sizeof ( char ) , 1 ) ) ; // include space for null terminator
memset ( quoted , ' \0 ' , quoted_len + 1 ) ;
unsigned int pos = 0 ;
quoted [ pos + + ] = ' 0 ' ;
quoted [ pos + + ] = ' x ' ;
char * p = quoted ;
for ( size_t index = 0 ; index < unquoted_len & & unquoted_str [ index ] ! = ' \0 ' ; + + index ) {
// On success, snprintf returns the total number of characters written
// On failure, a negative number is returned
// The generated string has a length of at most len - 1, so
2021-07-29 00:45:04 +02:00
// len is 3 (2 hex digits + 1)
2021-09-07 20:04:53 +02:00
// Requires "& 0x000000FF", or snprintf will translate "0x90" to "0xFFFFFF90"
int n = snprintf ( ( char * ) ( p + pos ) , 3 , " %02X " , unquoted_str [ index ] & 0x000000FF ) ;
if ( n < 0 ) {
// Something went wrong, simply return NULL (failure)
return NULL ;
}
pos + = 2 ;
}
zend_string * zstr = zend_string_init ( quoted , quoted_len , 0 ) ;
return zstr ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
}
else {
2021-05-06 03:21:16 +02:00
// The minimum number of single quotes needed is 2 -- the initial start and end quotes
// Add the letter N before the initial quote if the encoding is UTF8
int quotes_needed = ( use_national_char_set ) ? 3 : 2 ;
char c = ' \' ' ;
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-05-06 03:21:16 +02:00
std : : string tmp_str ( unquoted , unquoted_len ) ; // Copy all unquoted_len characters from unquoted
2021-07-29 00:45:04 +02:00
# else
2021-09-07 20:04:53 +02:00
size_t unquoted_len = ZSTR_LEN ( unquoted ) ;
2021-07-29 00:45:04 +02:00
const char * unquoted_str = ZSTR_VAL ( unquoted ) ;
std : : string tmp_str ( unquoted_str , unquoted_len ) ; // Copy all unquoted_len characters from unquoted
# endif
2021-05-06 03:21:16 +02:00
std : : size_t found = tmp_str . find ( c ) ; // Find the first single quote
while ( found ! = std : : string : : npos ) {
tmp_str . insert ( found + 1 , 1 , c ) ; // Insert an additional single quote
found = tmp_str . find ( c , found + 2 ) ; // Find the next single quote
2017-07-19 23:10:53 +02:00
}
2021-05-06 03:21:16 +02:00
size_t len = tmp_str . length ( ) ;
2021-07-29 00:45:04 +02:00
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2021-05-06 03:21:16 +02:00
* quoted_len = quotes_needed + len ; // The new length should be number of quotes plus the length of tmp_str
* quoted = reinterpret_cast < char * > ( sqlsrv_malloc ( * quoted_len , sizeof ( char ) , 1 ) ) ; // include space for null terminator
2021-09-07 20:04:53 +02:00
memset ( * quoted , ' \0 ' , * quoted_len + 1 ) ;
2017-07-19 23:10:53 +02:00
2021-05-06 03:21:16 +02:00
char * p = * quoted ;
2021-07-29 00:45:04 +02:00
# else
2021-09-07 20:04:53 +02:00
sqlsrv_malloc_auto_ptr < char > quoted ;
2021-07-29 00:45:04 +02:00
size_t quoted_len = quotes_needed + len ; // length returned to the caller should not account for null terminator
2021-09-07 20:04:53 +02:00
quoted = reinterpret_cast < char * > ( sqlsrv_malloc ( quoted_len , sizeof ( char ) , 1 ) ) ; // include space for null terminator
memset ( quoted , ' \0 ' , quoted_len + 1 ) ;
2021-07-29 00:45:04 +02:00
char * p = quoted ;
# endif
2021-05-06 03:21:16 +02:00
size_t pos = 0 ;
if ( use_national_char_set ) { // Insert the letter N if the encoding is UTF8
* ( p + ( pos + + ) ) = ' N ' ;
2017-07-19 23:10:53 +02:00
}
2021-05-06 03:21:16 +02:00
* ( p + ( pos + + ) ) = c ; // Add the initial quote
tmp_str . copy ( p + pos , len , 0 ) ; // Copy tmp_str to *quoted
pos + = len ;
* ( p + pos ) = c ; // Add the end quote
2021-07-29 00:45:04 +02:00
2021-09-07 20:04:53 +02:00
# if PHP_VERSION_ID < 80100
2017-07-19 23:10:53 +02:00
return 1 ;
2021-07-29 00:45:04 +02:00
# else
2021-09-07 20:04:53 +02:00
zend_string * zstr = zend_string_init ( quoted , quoted_len , 0 ) ;
return zstr ;
2021-07-29 00:45:04 +02:00
# endif
2017-07-19 23:10:53 +02:00
}
}
// This method is not implemented by this driver.
2020-04-21 00:17:21 +02:00
pdo_sqlsrv_function_entry * pdo_sqlsrv_get_driver_methods ( _Inout_ pdo_dbh_t * dbh , int kind )
2017-07-19 23:10:53 +02:00
{
PDO_RESET_DBH_ERROR ;
PDO_VALIDATE_CONN ;
PDO_LOG_DBH_ENTRY ;
sqlsrv_conn * driver_conn = reinterpret_cast < sqlsrv_conn * > ( dbh - > driver_data ) ;
2021-05-18 02:37:19 +02:00
// As per documentation, simply return false if the method does not exist
// https://www.php.net/manual/en/function.is-callable.php
// But user can call PDO::errorInfo() to check the error message if necessary
CHECK_CUSTOM_WARNING_AS_ERROR ( true , driver_conn , PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED ) ;
return NULL ; // return NULL for PDO to take care of the rest
2017-07-19 23:10:53 +02:00
}
namespace {
// Maps the PDO driver specific statement option/attribute constants to the core layer
// statement option/attribute constants.
2019-05-01 17:03:33 +02:00
void add_stmt_option_key ( _Inout_ sqlsrv_context & ctx , _In_ size_t key , _Inout_ HashTable * options_ht ,
2020-04-21 00:17:21 +02:00
_Inout_ zval * data )
2017-07-19 23:10:53 +02:00
{
2019-05-01 17:03:33 +02:00
zend_ulong option_key = - 1 ;
switch ( key ) {
case PDO_ATTR_CURSOR :
option_key = SQLSRV_STMT_OPTION_SCROLLABLE ;
break ;
case SQLSRV_ATTR_ENCODING :
option_key = PDO_STMT_OPTION_ENCODING ;
break ;
case SQLSRV_ATTR_QUERY_TIMEOUT :
option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT ;
break ;
case PDO_ATTR_STATEMENT_CLASS :
break ;
case SQLSRV_ATTR_DIRECT_QUERY :
option_key = PDO_STMT_OPTION_DIRECT_QUERY ;
break ;
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE :
option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE ;
break ;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE :
option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE ;
break ;
case PDO_ATTR_EMULATE_PREPARES :
option_key = PDO_STMT_OPTION_EMULATE_PREPARES ;
break ;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE :
option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE ;
break ;
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE :
option_key = PDO_STMT_OPTION_FETCHES_DATETIME_TYPE ;
break ;
case SQLSRV_ATTR_FORMAT_DECIMALS :
option_key = PDO_STMT_OPTION_FORMAT_DECIMALS ;
break ;
case SQLSRV_ATTR_DECIMAL_PLACES :
option_key = PDO_STMT_OPTION_DECIMAL_PLACES ;
break ;
case SQLSRV_ATTR_DATA_CLASSIFICATION :
option_key = PDO_STMT_OPTION_DATA_CLASSIFICATION ;
break ;
default :
CHECK_CUSTOM_ERROR ( true , ctx , PDO_SQLSRV_ERROR_INVALID_STMT_OPTION )
{
throw core : : CoreException ( ) ;
}
break ;
2017-07-19 23:10:53 +02:00
}
// if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it
2019-05-01 17:03:33 +02:00
if ( option_key ! = - 1 ) {
zval_add_ref ( data ) ;
2020-04-21 00:17:21 +02:00
core : : sqlsrv_zend_hash_index_update ( ctx , options_ht , option_key , data ) ;
2017-07-19 23:10:53 +02:00
}
}
// validate_stmt_options
// Iterates through the list of statement options provided by the user and validates them
// against the list of statement options provided by this driver. After validation
// creates a Hashtable of statement options to be sent to the core layer for processing.
// Parameters:
// ctx - The current context.
// stmt_options - The user provided list of statement options.
// pdo_stmt_options_ht - Output hashtable of statement options.
2020-04-21 00:17:21 +02:00
void validate_stmt_options ( _Inout_ sqlsrv_context & ctx , _Inout_ zval * stmt_options , _Inout_ HashTable * pdo_stmt_options_ht )
2017-07-19 23:10:53 +02:00
{
try {
if ( stmt_options ) {
HashTable * options_ht = Z_ARRVAL_P ( stmt_options ) ;
size_t int_key = - 1 ;
zend_string * key = NULL ;
zval * data = NULL ;
ZEND_HASH_FOREACH_KEY_VAL ( options_ht , int_key , key , data ) {
int type = HASH_KEY_NON_EXISTENT ;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG ;
CHECK_CUSTOM_ERROR ( ( type ! = HASH_KEY_IS_LONG ) , ctx , PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core : : CoreException ( ) ;
}
2020-04-21 00:17:21 +02:00
add_stmt_option_key ( ctx , int_key , pdo_stmt_options_ht , data ) ;
2017-07-19 23:10:53 +02:00
} ZEND_HASH_FOREACH_END ( ) ;
}
}
catch ( core : : CoreException & ) {
throw ;
}
}
2020-04-21 00:17:21 +02:00
void pdo_bool_conn_str_func : : func ( _In_ connection_option const * option , _Inout_ zval * value , sqlsrv_conn * /*conn*/ , _Out_ std : : string & conn_str )
2017-07-19 23:10:53 +02:00
{
char const * val_str = " no " ;
if ( core_str_zval_is_true ( value ) ) {
val_str = " yes " ;
}
conn_str + = option - > odbc_name ;
conn_str + = " ={ " ;
conn_str + = val_str ;
conn_str + = " }; " ;
}
void pdo_txn_isolation_conn_attr_func : : func ( connection_option const * /*option*/ , _In_ zval * value_z , _Inout_ sqlsrv_conn * conn ,
2020-04-21 00:17:21 +02:00
std : : string & /*conn_str*/ )
2017-07-19 23:10:53 +02:00
{
try {
SQLSRV_ASSERT ( Z_TYPE_P ( value_z ) = = IS_STRING , " pdo_txn_isolation_conn_attr_func: Unexpected zval type. " ) ;
const char * val = Z_STRVAL_P ( value_z ) ;
size_t val_len = Z_STRLEN_P ( value_z ) ;
zend_long out_val = SQL_TXN_READ_COMMITTED ;
// READ_COMMITTED
if ( ( val_len = = ( sizeof ( PDOTxnIsolationValues : : READ_COMMITTED ) - 1 )
& & ! strcasecmp ( val , PDOTxnIsolationValues : : READ_COMMITTED ) ) ) {
out_val = SQL_TXN_READ_COMMITTED ;
}
// READ_UNCOMMITTED
else if ( ( val_len = = ( sizeof ( PDOTxnIsolationValues : : READ_UNCOMMITTED ) - 1 )
& & ! strcasecmp ( val , PDOTxnIsolationValues : : READ_UNCOMMITTED ) ) ) {
out_val = SQL_TXN_READ_UNCOMMITTED ;
}
// REPEATABLE_READ
else if ( ( val_len = = ( sizeof ( PDOTxnIsolationValues : : REPEATABLE_READ ) - 1 )
& & ! strcasecmp ( val , PDOTxnIsolationValues : : REPEATABLE_READ ) ) ) {
out_val = SQL_TXN_REPEATABLE_READ ;
}
// SERIALIZABLE
else if ( ( val_len = = ( sizeof ( PDOTxnIsolationValues : : SERIALIZABLE ) - 1 )
& & ! strcasecmp ( val , PDOTxnIsolationValues : : SERIALIZABLE ) ) ) {
out_val = SQL_TXN_SERIALIZABLE ;
}
// SNAPSHOT
else if ( ( val_len = = ( sizeof ( PDOTxnIsolationValues : : SNAPSHOT ) - 1 )
& & ! strcasecmp ( val , PDOTxnIsolationValues : : SNAPSHOT ) ) ) {
out_val = SQL_TXN_SS_SNAPSHOT ;
}
else {
CHECK_CUSTOM_ERROR ( true , conn , PDO_SQLSRV_ERROR_INVALID_DSN_VALUE , PDOConnOptionNames : : TransactionIsolation ) {
throw core : : CoreException ( ) ;
}
}
2020-04-21 00:17:21 +02:00
core : : SQLSetConnectAttr ( conn , SQL_COPT_SS_TXN_ISOLATION , reinterpret_cast < SQLPOINTER > ( out_val ) , SQL_IS_UINTEGER ) ;
2017-07-19 23:10:53 +02:00
}
catch ( core : : CoreException & ) {
throw ;
}
}
} // namespace