changed line endings

This commit is contained in:
David Puglielli 2017-07-19 14:10:53 -07:00
parent a1dd674c44
commit ca4a2d9b7d
3 changed files with 1762 additions and 1762 deletions

View file

@ -1,1642 +1,1642 @@
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
// file: pdo_dbh.cpp // file: pdo_dbh.cpp
// //
// Contents: Implements the PDO object for PDO_SQLSRV // Contents: Implements the PDO object for PDO_SQLSRV
// //
// Microsoft Drivers 4.3 for PHP for SQL Server // Microsoft Drivers 4.3 for PHP for SQL Server
// Copyright(c) Microsoft Corporation // Copyright(c) Microsoft Corporation
// All rights reserved. // All rights reserved.
// MIT License // MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), // 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, // 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 : // 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 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, // 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 // 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 // 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. // IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------------------
#include "php_pdo_sqlsrv.h" #include "php_pdo_sqlsrv.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
typedef const zend_function_entry pdo_sqlsrv_function_entry; typedef const zend_function_entry pdo_sqlsrv_function_entry;
// *** internal variables and constants *** // *** internal variables and constants ***
namespace { namespace {
const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;";
const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer
const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s"; const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s";
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes 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. // List of PDO supported connection options.
namespace PDOConnOptionNames { namespace PDOConnOptionNames {
const char Server[] = "Server"; const char Server[] = "Server";
const char APP[] = "APP"; const char APP[] = "APP";
const char ApplicationIntent[] = "ApplicationIntent"; const char ApplicationIntent[] = "ApplicationIntent";
const char AttachDBFileName[] = "AttachDbFileName"; const char AttachDBFileName[] = "AttachDbFileName";
const char ConnectionPooling[] = "ConnectionPooling"; const char ConnectionPooling[] = "ConnectionPooling";
const char Authentication[] = "Authentication"; const char Authentication[] = "Authentication";
#ifdef _WIN32 #ifdef _WIN32
const char ConnectRetryCount[] = "ConnectRetryCount"; const char ConnectRetryCount[] = "ConnectRetryCount";
const char ConnectRetryInterval[] = "ConnectRetryInterval"; const char ConnectRetryInterval[] = "ConnectRetryInterval";
#endif // _WIN32 #endif // _WIN32
const char Database[] = "Database"; const char Database[] = "Database";
const char Encrypt[] = "Encrypt"; const char Encrypt[] = "Encrypt";
const char Failover_Partner[] = "Failover_Partner"; const char Failover_Partner[] = "Failover_Partner";
const char LoginTimeout[] = "LoginTimeout"; const char LoginTimeout[] = "LoginTimeout";
const char MARS_Option[] = "MultipleActiveResultSets"; const char MARS_Option[] = "MultipleActiveResultSets";
const char MultiSubnetFailover[] = "MultiSubnetFailover"; const char MultiSubnetFailover[] = "MultiSubnetFailover";
const char QuotedId[] = "QuotedId"; const char QuotedId[] = "QuotedId";
const char TraceFile[] = "TraceFile"; const char TraceFile[] = "TraceFile";
const char TraceOn[] = "TraceOn"; const char TraceOn[] = "TraceOn";
const char TrustServerCertificate[] = "TrustServerCertificate"; const char TrustServerCertificate[] = "TrustServerCertificate";
const char TransactionIsolation[] = "TransactionIsolation"; const char TransactionIsolation[] = "TransactionIsolation";
const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution"; const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution";
const char WSID[] = "WSID"; const char WSID[] = "WSID";
} }
enum PDO_CONN_OPTIONS { enum PDO_CONN_OPTIONS {
PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC,
}; };
enum PDO_STMT_OPTIONS { enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC, PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC,
PDO_STMT_OPTION_DIRECT_QUERY, PDO_STMT_OPTION_DIRECT_QUERY,
PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE,
PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE,
PDO_STMT_OPTION_EMULATE_PREPARES, PDO_STMT_OPTION_EMULATE_PREPARES,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE,
}; };
// List of all the statement options supported by this driver. // List of all the statement options supported by this driver.
const stmt_option PDO_STMT_OPTS[] = { 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_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, 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_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_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_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_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_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 ) }, { NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_numeric>( new stmt_option_fetch_numeric ) },
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} }, { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
}; };
// boolean connection string // boolean connection string
struct pdo_bool_conn_str_func struct pdo_bool_conn_str_func
{ {
static void func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC ); static void func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC );
}; };
struct pdo_txn_isolation_conn_attr_func struct pdo_txn_isolation_conn_attr_func
{ {
static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC ); static void func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC );
}; };
#ifdef _WIN32 #ifdef _WIN32
struct pdo_int_conn_str_func { struct pdo_int_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC ) static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC )
{ {
TSRMLS_C; TSRMLS_C;
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" ) SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
std::string val_str = Z_STRVAL_P( value ); std::string val_str = Z_STRVAL_P( value );
conn_str += option->odbc_name; conn_str += option->odbc_name;
conn_str += "={"; conn_str += "={";
conn_str += val_str; conn_str += val_str;
conn_str += "};"; conn_str += "};";
} }
}; };
#endif // _WIN32 #endif // _WIN32
template <unsigned int Attr> template <unsigned int Attr>
struct pdo_int_conn_attr_func { struct pdo_int_conn_attr_func {
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC ) static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
{ {
try { try {
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); 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 )) ); size_t val = static_cast<size_t>( atoi( Z_STRVAL_P( value )) );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER TSRMLS_CC ); core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER TSRMLS_CC );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;
} }
} }
}; };
template <unsigned int Attr> template <unsigned int Attr>
struct pdo_bool_conn_attr_func { struct pdo_bool_conn_attr_func {
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC ) static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
{ {
try { try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( core_str_zval_is_true( value )), core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( core_str_zval_is_true( value )),
SQL_IS_UINTEGER TSRMLS_CC ); SQL_IS_UINTEGER TSRMLS_CC );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;
} }
} }
}; };
// statement options related functions // statement options related functions
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval** data TSRMLS_DC ); _Inout_ zval** data TSRMLS_DC );
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ); void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC );
} // namespace } // namespace
// List of all connection options supported by this driver. // List of all connection options supported by this driver.
const connection_option PDO_CONN_OPTS[] = { const connection_option PDO_CONN_OPTS[] = {
{ {
PDOConnOptionNames::Server, PDOConnOptionNames::Server,
sizeof( PDOConnOptionNames::Server ), sizeof( PDOConnOptionNames::Server ),
PDO_CONN_OPTION_SERVER, PDO_CONN_OPTION_SERVER,
NULL, NULL,
0, 0,
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::APP, PDOConnOptionNames::APP,
sizeof( PDOConnOptionNames::APP ), sizeof( PDOConnOptionNames::APP ),
SQLSRV_CONN_OPTION_APP, SQLSRV_CONN_OPTION_APP,
ODBCConnOptions::APP, ODBCConnOptions::APP,
sizeof( ODBCConnOptions::APP ), sizeof( ODBCConnOptions::APP ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::ApplicationIntent, PDOConnOptionNames::ApplicationIntent,
sizeof( PDOConnOptionNames::ApplicationIntent ), sizeof( PDOConnOptionNames::ApplicationIntent ),
SQLSRV_CONN_OPTION_APPLICATION_INTENT, SQLSRV_CONN_OPTION_APPLICATION_INTENT,
ODBCConnOptions::ApplicationIntent, ODBCConnOptions::ApplicationIntent,
sizeof( ODBCConnOptions::ApplicationIntent ), sizeof( ODBCConnOptions::ApplicationIntent ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::AttachDBFileName, PDOConnOptionNames::AttachDBFileName,
sizeof( PDOConnOptionNames::AttachDBFileName ), sizeof( PDOConnOptionNames::AttachDBFileName ),
SQLSRV_CONN_OPTION_ATTACHDBFILENAME, SQLSRV_CONN_OPTION_ATTACHDBFILENAME,
ODBCConnOptions::AttachDBFileName, ODBCConnOptions::AttachDBFileName,
sizeof( ODBCConnOptions::AttachDBFileName ), sizeof( ODBCConnOptions::AttachDBFileName ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::Authentication, PDOConnOptionNames::Authentication,
sizeof( PDOConnOptionNames::Authentication ), sizeof( PDOConnOptionNames::Authentication ),
SQLSRV_CONN_OPTION_AUTHENTICATION, SQLSRV_CONN_OPTION_AUTHENTICATION,
ODBCConnOptions::Authentication, ODBCConnOptions::Authentication,
sizeof( ODBCConnOptions::Authentication ), sizeof( ODBCConnOptions::Authentication ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::ConnectionPooling, PDOConnOptionNames::ConnectionPooling,
sizeof( PDOConnOptionNames::ConnectionPooling ), sizeof( PDOConnOptionNames::ConnectionPooling ),
SQLSRV_CONN_OPTION_CONN_POOLING, SQLSRV_CONN_OPTION_CONN_POOLING,
ODBCConnOptions::ConnectionPooling, ODBCConnOptions::ConnectionPooling,
sizeof( ODBCConnOptions::ConnectionPooling ), sizeof( ODBCConnOptions::ConnectionPooling ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
conn_null_func::func conn_null_func::func
}, },
#ifdef _WIN32 #ifdef _WIN32
{ {
PDOConnOptionNames::ConnectRetryCount, PDOConnOptionNames::ConnectRetryCount,
sizeof( PDOConnOptionNames::ConnectRetryCount ), sizeof( PDOConnOptionNames::ConnectRetryCount ),
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT, SQLSRV_CONN_OPTION_CONN_RETRY_COUNT,
ODBCConnOptions::ConnectRetryCount, ODBCConnOptions::ConnectRetryCount,
sizeof( ODBCConnOptions::ConnectRetryCount ), sizeof( ODBCConnOptions::ConnectRetryCount ),
CONN_ATTR_INT, CONN_ATTR_INT,
pdo_int_conn_str_func::func pdo_int_conn_str_func::func
}, },
{ {
PDOConnOptionNames::ConnectRetryInterval, PDOConnOptionNames::ConnectRetryInterval,
sizeof( PDOConnOptionNames::ConnectRetryInterval ), sizeof( PDOConnOptionNames::ConnectRetryInterval ),
SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL, SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL,
ODBCConnOptions::ConnectRetryInterval, ODBCConnOptions::ConnectRetryInterval,
sizeof( ODBCConnOptions::ConnectRetryInterval ), sizeof( ODBCConnOptions::ConnectRetryInterval ),
CONN_ATTR_INT, CONN_ATTR_INT,
pdo_int_conn_str_func::func pdo_int_conn_str_func::func
}, },
#endif // _WIN32 #endif // _WIN32
{ {
PDOConnOptionNames::Database, PDOConnOptionNames::Database,
sizeof( PDOConnOptionNames::Database ), sizeof( PDOConnOptionNames::Database ),
SQLSRV_CONN_OPTION_DATABASE, SQLSRV_CONN_OPTION_DATABASE,
ODBCConnOptions::Database, ODBCConnOptions::Database,
sizeof( ODBCConnOptions::Database ), sizeof( ODBCConnOptions::Database ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::Encrypt, PDOConnOptionNames::Encrypt,
sizeof( PDOConnOptionNames::Encrypt ), sizeof( PDOConnOptionNames::Encrypt ),
SQLSRV_CONN_OPTION_ENCRYPT, SQLSRV_CONN_OPTION_ENCRYPT,
ODBCConnOptions::Encrypt, ODBCConnOptions::Encrypt,
sizeof( ODBCConnOptions::Encrypt ), sizeof( ODBCConnOptions::Encrypt ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func pdo_bool_conn_str_func::func
}, },
{ {
PDOConnOptionNames::Failover_Partner, PDOConnOptionNames::Failover_Partner,
sizeof( PDOConnOptionNames::Failover_Partner ), sizeof( PDOConnOptionNames::Failover_Partner ),
SQLSRV_CONN_OPTION_FAILOVER_PARTNER, SQLSRV_CONN_OPTION_FAILOVER_PARTNER,
ODBCConnOptions::Failover_Partner, ODBCConnOptions::Failover_Partner,
sizeof( ODBCConnOptions::Failover_Partner ), sizeof( ODBCConnOptions::Failover_Partner ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::LoginTimeout, PDOConnOptionNames::LoginTimeout,
sizeof( PDOConnOptionNames::LoginTimeout ), sizeof( PDOConnOptionNames::LoginTimeout ),
SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, SQLSRV_CONN_OPTION_LOGIN_TIMEOUT,
ODBCConnOptions::LoginTimeout, ODBCConnOptions::LoginTimeout,
sizeof( ODBCConnOptions::LoginTimeout ), sizeof( ODBCConnOptions::LoginTimeout ),
CONN_ATTR_INT, CONN_ATTR_INT,
pdo_int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func pdo_int_conn_attr_func<SQL_ATTR_LOGIN_TIMEOUT>::func
}, },
{ {
PDOConnOptionNames::MARS_Option, PDOConnOptionNames::MARS_Option,
sizeof( PDOConnOptionNames::MARS_Option ), sizeof( PDOConnOptionNames::MARS_Option ),
SQLSRV_CONN_OPTION_MARS, SQLSRV_CONN_OPTION_MARS,
ODBCConnOptions::MARS_ODBC, ODBCConnOptions::MARS_ODBC,
sizeof( ODBCConnOptions::MARS_ODBC ), sizeof( ODBCConnOptions::MARS_ODBC ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func pdo_bool_conn_str_func::func
}, },
{ {
PDOConnOptionNames::MultiSubnetFailover, PDOConnOptionNames::MultiSubnetFailover,
sizeof( PDOConnOptionNames::MultiSubnetFailover ), sizeof( PDOConnOptionNames::MultiSubnetFailover ),
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER,
ODBCConnOptions::MultiSubnetFailover, ODBCConnOptions::MultiSubnetFailover,
sizeof( ODBCConnOptions::MultiSubnetFailover ), sizeof( ODBCConnOptions::MultiSubnetFailover ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func pdo_bool_conn_str_func::func
}, },
{ {
PDOConnOptionNames::QuotedId, PDOConnOptionNames::QuotedId,
sizeof( PDOConnOptionNames::QuotedId ), sizeof( PDOConnOptionNames::QuotedId ),
SQLSRV_CONN_OPTION_QUOTED_ID, SQLSRV_CONN_OPTION_QUOTED_ID,
ODBCConnOptions::QuotedId, ODBCConnOptions::QuotedId,
sizeof( ODBCConnOptions::QuotedId ), sizeof( ODBCConnOptions::QuotedId ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func pdo_bool_conn_str_func::func
}, },
{ {
PDOConnOptionNames::TraceFile, PDOConnOptionNames::TraceFile,
sizeof( PDOConnOptionNames::TraceFile ), sizeof( PDOConnOptionNames::TraceFile ),
SQLSRV_CONN_OPTION_TRACE_FILE, SQLSRV_CONN_OPTION_TRACE_FILE,
ODBCConnOptions::TraceFile, ODBCConnOptions::TraceFile,
sizeof( ODBCConnOptions::TraceFile ), sizeof( ODBCConnOptions::TraceFile ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
str_conn_attr_func<SQL_ATTR_TRACEFILE>::func str_conn_attr_func<SQL_ATTR_TRACEFILE>::func
}, },
{ {
PDOConnOptionNames::TraceOn, PDOConnOptionNames::TraceOn,
sizeof( PDOConnOptionNames::TraceOn ), sizeof( PDOConnOptionNames::TraceOn ),
SQLSRV_CONN_OPTION_TRACE_ON, SQLSRV_CONN_OPTION_TRACE_ON,
ODBCConnOptions::TraceOn, ODBCConnOptions::TraceOn,
sizeof( ODBCConnOptions::TraceOn ), sizeof( ODBCConnOptions::TraceOn ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_attr_func<SQL_ATTR_TRACE>::func pdo_bool_conn_attr_func<SQL_ATTR_TRACE>::func
}, },
{ {
PDOConnOptionNames::TransactionIsolation, PDOConnOptionNames::TransactionIsolation,
sizeof( PDOConnOptionNames::TransactionIsolation ), sizeof( PDOConnOptionNames::TransactionIsolation ),
SQLSRV_CONN_OPTION_TRANS_ISOLATION, SQLSRV_CONN_OPTION_TRANS_ISOLATION,
ODBCConnOptions::TransactionIsolation, ODBCConnOptions::TransactionIsolation,
sizeof( ODBCConnOptions::TransactionIsolation ), sizeof( ODBCConnOptions::TransactionIsolation ),
CONN_ATTR_INT, CONN_ATTR_INT,
pdo_txn_isolation_conn_attr_func::func pdo_txn_isolation_conn_attr_func::func
}, },
{ {
PDOConnOptionNames::TrustServerCertificate, PDOConnOptionNames::TrustServerCertificate,
sizeof( PDOConnOptionNames::TrustServerCertificate ), sizeof( PDOConnOptionNames::TrustServerCertificate ),
SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, SQLSRV_CONN_OPTION_TRUST_SERVER_CERT,
ODBCConnOptions::TrustServerCertificate, ODBCConnOptions::TrustServerCertificate,
sizeof( ODBCConnOptions::TrustServerCertificate ), sizeof( ODBCConnOptions::TrustServerCertificate ),
CONN_ATTR_BOOL, CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func pdo_bool_conn_str_func::func
}, },
{ {
PDOConnOptionNames::TransparentNetworkIPResolution, PDOConnOptionNames::TransparentNetworkIPResolution,
sizeof(PDOConnOptionNames::TransparentNetworkIPResolution), sizeof(PDOConnOptionNames::TransparentNetworkIPResolution),
SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION, SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION,
ODBCConnOptions::TransparentNetworkIPResolution, ODBCConnOptions::TransparentNetworkIPResolution,
sizeof(ODBCConnOptions::TransparentNetworkIPResolution), sizeof(ODBCConnOptions::TransparentNetworkIPResolution),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ {
PDOConnOptionNames::WSID, PDOConnOptionNames::WSID,
sizeof( PDOConnOptionNames::WSID ), sizeof( PDOConnOptionNames::WSID ),
SQLSRV_CONN_OPTION_WSID, SQLSRV_CONN_OPTION_WSID,
ODBCConnOptions::WSID, ODBCConnOptions::WSID,
sizeof( ODBCConnOptions::WSID ), sizeof( ODBCConnOptions::WSID ),
CONN_ATTR_STRING, CONN_ATTR_STRING,
conn_str_append_func::func conn_str_append_func::func
}, },
{ NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table { NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table
}; };
// close the connection // close the connection
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
// execute queries // execute queries
int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql, int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql,
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC ); _Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC );
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC ); zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC );
// transaction support functions // transaction support functions
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC );
// attribute functions // attribute functions
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC ); int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC );
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ); int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC );
// return more information // return more information
int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
_Out_ zval *info TSRMLS_DC); _Out_ zval *info TSRMLS_DC);
// return the last id generated by an executed SQL statement // return the last id generated by an executed SQL statement
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC ); char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC );
// additional methods are supported in this function // additional methods are supported in this function
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC ); pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC );
// quote a string, meaning put quotes around it and escape any quotes within it // 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, int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen,
enum pdo_param_type paramtype TSRMLS_DC ); enum pdo_param_type paramtype TSRMLS_DC );
struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = { struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
pdo_sqlsrv_dbh_close, pdo_sqlsrv_dbh_close,
pdo_sqlsrv_dbh_prepare, pdo_sqlsrv_dbh_prepare,
pdo_sqlsrv_dbh_do, pdo_sqlsrv_dbh_do,
pdo_sqlsrv_dbh_quote, pdo_sqlsrv_dbh_quote,
pdo_sqlsrv_dbh_begin, pdo_sqlsrv_dbh_begin,
pdo_sqlsrv_dbh_commit, pdo_sqlsrv_dbh_commit,
pdo_sqlsrv_dbh_rollback, pdo_sqlsrv_dbh_rollback,
pdo_sqlsrv_dbh_set_attr, pdo_sqlsrv_dbh_set_attr,
pdo_sqlsrv_dbh_last_id, pdo_sqlsrv_dbh_last_id,
pdo_sqlsrv_dbh_return_error, pdo_sqlsrv_dbh_return_error,
pdo_sqlsrv_dbh_get_attr, pdo_sqlsrv_dbh_get_attr,
NULL, // check liveness not implemented NULL, // check liveness not implemented
pdo_sqlsrv_get_driver_methods, pdo_sqlsrv_get_driver_methods,
NULL, // request shutdown not implemented NULL, // request shutdown not implemented
NULL // in transaction not implemented NULL // in transaction not implemented
}; };
// log a function entry point // log a function entry point
#ifndef _WIN32 #ifndef _WIN32
#define PDO_LOG_DBH_ENTRY \ #define PDO_LOG_DBH_ENTRY \
{ \ { \
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); \ pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); \
driver_dbh->set_func( __FUNCTION__ ); \ driver_dbh->set_func( __FUNCTION__ ); \
int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \ int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \
char func[length+1]; \ char func[length+1]; \
strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \ strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \
strcat_s( func, length+1, ": entering" ); \ strcat_s( func, length+1, ": entering" ); \
LOG( SEV_NOTICE, func ); \ LOG( SEV_NOTICE, func ); \
} }
#else #else
#define PDO_LOG_DBH_ENTRY \ #define PDO_LOG_DBH_ENTRY \
{ \ { \
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); \ pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); \
driver_dbh->set_func( __FUNCTION__ ); \ driver_dbh->set_func( __FUNCTION__ ); \
LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \ LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \
} }
#endif #endif
// constructor for the internal object for connections // constructor for the internal object for connections
pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC ) : pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver TSRMLS_DC ) :
sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ), sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ),
stmts( NULL ), stmts( NULL ),
direct_query( false ), direct_query( false ),
query_timeout( QUERY_TIMEOUT_INVALID ), query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )), client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )),
fetch_numeric( false ) fetch_numeric( false )
{ {
if( client_buffer_max_size < 0 ) { if( client_buffer_max_size < 0 ) {
client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT; 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." ); LOG( SEV_WARNING, INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value." );
} }
} }
// pdo_sqlsrv_db_handle_factory // pdo_sqlsrv_db_handle_factory
// Maps to PDO::__construct. // Maps to PDO::__construct.
// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. // Factory method called by the PDO driver manager to create a SQLSRV PDO connection.
// Does the following things: // Does the following things:
// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. // 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION.
// (If an error occurs in this function, the PDO specification mandates that // (If an error occurs in this function, the PDO specification mandates that
// an exception be thrown, regardless of the error mode setting.) // an exception be thrown, regardless of the error mode setting.)
// 2. Processes the driver options. // 2. Processes the driver options.
// 3. Creates a core_conn object by calling core_sqlsrv_connect. // 3. Creates a core_conn object by calling core_sqlsrv_connect.
// 4. Restores the previous error mode on success. // 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 // alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory
// Parameters: // Parameters:
// dbh - The PDO managed structure for the connection. // dbh - The PDO managed structure for the connection.
// driver_options - A HashTable (within the zval) of options to use when creating the connection. // driver_options - A HashTable (within the zval) of options to use when creating the connection.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options TSRMLS_DC) int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options TSRMLS_DC)
{ {
LOG( SEV_NOTICE, "pdo_sqlsrv_db_handle_factory: entering" ); LOG( SEV_NOTICE, "pdo_sqlsrv_db_handle_factory: entering" );
hash_auto_ptr pdo_conn_options_ht; hash_auto_ptr pdo_conn_options_ht;
pdo_error_mode prev_err_mode = dbh->error_mode; pdo_error_mode prev_err_mode = dbh->error_mode;
// must be done in all cases so that even a failed connection can query the // must be done in all cases so that even a failed connection can query the
// object for errors. // object for errors.
dbh->methods = &pdo_sqlsrv_dbh_methods; dbh->methods = &pdo_sqlsrv_dbh_methods;
dbh->driver_data = NULL; dbh->driver_data = NULL;
zval* temp_server_z = NULL; zval* temp_server_z = NULL;
sqlsrv_malloc_auto_ptr<conn_string_parser> dsn_parser; sqlsrv_malloc_auto_ptr<conn_string_parser> dsn_parser;
zval server_z; zval server_z;
ZVAL_UNDEF( &server_z ); ZVAL_UNDEF( &server_z );
try { try {
// no matter what the error mode, we want exceptions thrown if the connection fails // no matter what the error mode, we want exceptions thrown if the connection fails
// to happen (per the PDO spec) // to happen (per the PDO spec)
dbh->error_mode = PDO_ERRMODE_EXCEPTION; dbh->error_mode = PDO_ERRMODE_EXCEPTION;
g_pdo_henv_cp->set_driver( dbh ); g_pdo_henv_cp->set_driver( dbh );
g_pdo_henv_ncp->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 ) { 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(); throw core::CoreException();
} }
// throws PDOException if the ATTR_PERSISTENT is in connection options // 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 ) { CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) {
dbh->refcount--; dbh->refcount--;
throw pdo::PDOException(); throw pdo::PDOException();
} }
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_conn_options_ht ); ALLOC_HASHTABLE( pdo_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */,
ZVAL_INTERNAL_DTOR, 0 /*persistent*/ TSRMLS_CC ); ZVAL_INTERNAL_DTOR, 0 /*persistent*/ TSRMLS_CC );
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error. // 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, 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 ); static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht );
dsn_parser->parse_conn_string( TSRMLS_C ); dsn_parser->parse_conn_string( TSRMLS_C );
// Extract the server name // Extract the server name
temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); 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 ) { CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) {
throw pdo::PDOException(); throw pdo::PDOException();
} }
server_z = *temp_server_z; server_z = *temp_server_z;
// Add a reference to the option value since we are deleting it from the hashtable // Add a reference to the option value since we are deleting it from the hashtable
zval_add_ref( &server_z ); zval_add_ref( &server_z );
zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); 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 ), 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, dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error,
PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC ); PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC );
// Free the string in server_z after being used // Free the string in server_z after being used
zend_string_release( Z_STR( server_z )); zend_string_release( Z_STR( server_z ));
SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); 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 // set the driver_data and methods to complete creation of the PDO object
dbh->driver_data = conn; dbh->driver_data = conn;
dbh->error_mode = prev_err_mode; // reset the error mode dbh->error_mode = prev_err_mode; // reset the error mode
dbh->alloc_own_columns = 1; // we do our own memory management for columns 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 dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
if ( Z_TYPE( server_z ) == IS_STRING ) { if ( Z_TYPE( server_z ) == IS_STRING ) {
zend_string_release( Z_STR( server_z )); zend_string_release( Z_STR( server_z ));
} }
dbh->error_mode = prev_err_mode; // reset the error mode 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 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; return 0;
} }
catch( ... ) { catch( ... ) {
DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" ); DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" );
} }
return 1; return 1;
} }
// pdo_sqlsrv_dbh_close // pdo_sqlsrv_dbh_close
// Maps to PDO::__destruct. // Maps to PDO::__destruct.
// Called when a PDO object is to be destroyed. // Called when a PDO object is to be destroyed.
// By the time this function is called, PDO has already made sure that // 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. // all statements are disposed and the PDO object is the last item destroyed.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// Return: // Return:
// Always returns 1 for success. // Always returns 1 for success.
int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
{ {
LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" ); LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" );
// if the connection didn't complete properly, driver_data isn't initialized. // if the connection didn't complete properly, driver_data isn't initialized.
if( dbh->driver_data == NULL ) { if( dbh->driver_data == NULL ) {
return 1; return 1;
} }
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
// call the core layer close // call the core layer close
core_sqlsrv_close( reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ) TSRMLS_CC ); core_sqlsrv_close( reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ) TSRMLS_CC );
dbh->driver_data = NULL; dbh->driver_data = NULL;
// always return success that the connection is closed // always return success that the connection is closed
return 1; return 1;
} }
// pdo_sqlsrv_dbh_prepare // pdo_sqlsrv_dbh_prepare
// Called by PDO::prepare and PDOStatement::__construct. // Called by PDO::prepare and PDOStatement::__construct.
// Creates a statement and prepares it for execution by PDO // Creates a statement and prepares it for execution by PDO
// Paramters: // Paramters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// sql - SQL query to be prepared. // sql - SQL query to be prepared.
// sql_len - Length of the sql query // sql_len - Length of the sql query
// stmt - The PDO managed statement object. // stmt - The PDO managed statement object.
// driver_options - User provided list of statement options. // driver_options - User provided list of statement options.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql, int pdo_sqlsrv_dbh_prepare( _Inout_ pdo_dbh_t *dbh, _In_reads_(sql_len) const char *sql,
_Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC ) _Inout_ size_t sql_len, _Inout_ pdo_stmt_t *stmt, _In_ zval *driver_options TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
hash_auto_ptr pdo_stmt_options_ht; hash_auto_ptr pdo_stmt_options_ht;
sqlsrv_malloc_auto_ptr<char> sql_rewrite; sqlsrv_malloc_auto_ptr<char> sql_rewrite;
size_t sql_rewrite_len = 0; size_t sql_rewrite_len = 0;
sqlsrv_malloc_auto_ptr<pdo_sqlsrv_stmt> driver_stmt; sqlsrv_malloc_auto_ptr<pdo_sqlsrv_stmt> driver_stmt;
hash_auto_ptr placeholders; hash_auto_ptr placeholders;
sqlsrv_malloc_auto_ptr<sql_string_parser> sql_parser; sqlsrv_malloc_auto_ptr<sql_string_parser> sql_parser;
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); 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"); SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null");
try { try {
// assign the methods for the statement object. This is necessary even if the // assign the methods for the statement object. This is necessary even if the
// statement fails so the user can retrieve the error information. // statement fails so the user can retrieve the error information.
stmt->methods = &pdo_sqlsrv_stmt_methods; stmt->methods = &pdo_sqlsrv_stmt_methods;
stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names
// Initialize the options array to be passed to the core layer // Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_stmt_options_ht ); ALLOC_HASHTABLE( pdo_stmt_options_ht );
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC ); ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
// Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error. // Either of g_pdo_henv_cp or g_pdo_henv_ncp can be used to propogate the error.
validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC ); validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC );
driver_stmt = static_cast<pdo_sqlsrv_stmt*>( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, driver_stmt = static_cast<pdo_sqlsrv_stmt*>( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>,
pdo_stmt_options_ht, PDO_STMT_OPTS, pdo_stmt_options_ht, PDO_STMT_OPTS,
pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC )); pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC ));
// if the user didn't set anything in the prepare options, then set the buffer limit // if the user didn't set anything in the prepare options, then set the buffer limit
// to the value set on the connection. // to the value set on the connection.
if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { 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; driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size;
} }
// if the user didn't set anything in the prepare options, then set the query timeout // if the user didn't set anything in the prepare options, then set the query timeout
// to the value set on the connection. // to the value set on the connection.
if(( driver_stmt->query_timeout == QUERY_TIMEOUT_INVALID ) && ( driver_dbh->query_timeout != QUERY_TIMEOUT_INVALID )) { if(( driver_stmt->query_timeout == QUERY_TIMEOUT_INVALID ) && ( driver_dbh->query_timeout != QUERY_TIMEOUT_INVALID )) {
core_sqlsrv_set_query_timeout( driver_stmt, driver_dbh->query_timeout TSRMLS_CC ); core_sqlsrv_set_query_timeout( driver_stmt, driver_dbh->query_timeout TSRMLS_CC );
} }
// rewrite named parameters in the query to positional parameters if we aren't letting PDO do the // rewrite named parameters in the query to positional parameters if we aren't letting PDO do the
// parameter substitution for us // parameter substitution for us
if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { 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 // 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. // parameters for consistency with the PDO MySQL and PDO ODBC drivers.
int zr = pdo_parse_params( stmt, const_cast<char*>( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC ); int zr = pdo_parse_params( stmt, const_cast<char*>( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC );
CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) { CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) {
throw core::CoreException(); throw core::CoreException();
} }
// if parameter substitution happened, use that query instead of the original // if parameter substitution happened, use that query instead of the original
if( sql_rewrite != 0) { if( sql_rewrite != 0) {
sql = sql_rewrite; sql = sql_rewrite;
sql_len = sql_rewrite_len; sql_len = sql_rewrite_len;
} }
} }
if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) {
core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC ); core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC );
} }
else if( driver_stmt->direct_query ) { else if( driver_stmt->direct_query ) {
if( driver_stmt->direct_query_subst_string ) { if( driver_stmt->direct_query_subst_string ) {
// we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme // 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 // 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 ))); 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 = estrdup( sql );
driver_stmt->direct_query_subst_string_len = sql_len; driver_stmt->direct_query_subst_string_len = sql_len;
} }
// else if stmt->support_placeholders == PDO_PLACEHOLDER_NONE means that stmt->active_query_string will be // else if stmt->support_placeholders == PDO_PLACEHOLDER_NONE means that stmt->active_query_string will be
// set to the substituted query // set to the substituted query
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) { if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
// parse placeholders in the sql query into the placeholders ht // parse placeholders in the sql query into the placeholders ht
ALLOC_HASHTABLE( placeholders ); ALLOC_HASHTABLE( placeholders );
core::sqlsrv_zend_hash_init( *driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */ TSRMLS_CC ); core::sqlsrv_zend_hash_init( *driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */ TSRMLS_CC );
sql_parser = new ( sqlsrv_malloc( sizeof( sql_string_parser ))) sql_string_parser( *driver_dbh, stmt->query_string, sql_parser = new ( sqlsrv_malloc( sizeof( sql_string_parser ))) sql_string_parser( *driver_dbh, stmt->query_string,
static_cast<int>(stmt->query_stringlen), placeholders ); static_cast<int>(stmt->query_stringlen), placeholders );
sql_parser->parse_sql_string( TSRMLS_C ); sql_parser->parse_sql_string( TSRMLS_C );
driver_stmt->placeholders = placeholders; driver_stmt->placeholders = placeholders;
placeholders.transferred(); placeholders.transferred();
} }
stmt->driver_data = driver_stmt; stmt->driver_data = driver_stmt;
driver_stmt.transferred(); driver_stmt.transferred();
} }
// everything is cleaned up by this point // everything is cleaned up by this point
// catch everything so the exception doesn't spill into the calling PDO code // catch everything so the exception doesn't spill into the calling PDO code
catch( core::CoreException& ) { catch( core::CoreException& ) {
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~pdo_sqlsrv_stmt(); driver_stmt->~pdo_sqlsrv_stmt();
} }
// in the event that the statement caused an error that was copied to the connection, update the // in the event that the statement caused an error that was copied to the connection, update the
// connection with the error's SQLSTATE. // connection with the error's SQLSTATE.
if( driver_dbh->last_error() ) { if( driver_dbh->last_error() ) {
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_dbh->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_dbh->last_error()->sqlstate ));
} }
return 0; return 0;
} }
// catch any errant exception and die // catch any errant exception and die
catch(...) { catch(...) {
DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." ); DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." );
} }
return 1; return 1;
} }
// pdo_sqlsrv_dbh_do // pdo_sqlsrv_dbh_do
// Maps to PDO::exec. // Maps to PDO::exec.
// Execute a SQL statement, such as an insert, update or delete, and return // Execute a SQL statement, such as an insert, update or delete, and return
// the number of rows affected. // the number of rows affected.
// Parameters: // Parameters:
// dbh - the PDO connection object, which contains the ODBC handle // dbh - the PDO connection object, which contains the ODBC handle
// sql - the query to execute // sql - the query to execute
// sql_len - length of sql query // sql_len - length of sql query
// Return // Return
// # of rows affected, -1 for an error. // # of rows affected, -1 for an error.
zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC ) zend_long pdo_sqlsrv_dbh_do( _Inout_ pdo_dbh_t *dbh, _In_reads_bytes_(sql_len) const char *sql, _In_ size_t sql_len TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt; sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt;
SQLLEN rows = 0; 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 // 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. // thing to happen here.
SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN ));
try { try {
SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." );
SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL.");
// temp PDO statement used for error handling if something happens // temp PDO statement used for error handling if something happens
pdo_stmt_t temp_stmt; pdo_stmt_t temp_stmt;
temp_stmt.dbh = dbh; temp_stmt.dbh = dbh;
// allocate a full driver statement to take advantage of the error handling // 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*/, driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, NULL /*options_ht*/,
NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC );
driver_stmt->set_func( __FUNCTION__ ); driver_stmt->set_func( __FUNCTION__ );
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast<int>( sql_len ) ); SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast<int>( sql_len ) );
// since the user can give us a compound statement, we return the row count for the last set, and since the row count // 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. // isn't guaranteed to be valid until all the results have been fetched, we fetch them all first.
if ( execReturn != SQL_NO_DATA && core_sqlsrv_has_any_result( driver_stmt TSRMLS_CC )) { if ( execReturn != SQL_NO_DATA && core_sqlsrv_has_any_result( driver_stmt TSRMLS_CC )) {
SQLRETURN r = SQL_SUCCESS; SQLRETURN r = SQL_SUCCESS;
do { do {
rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); rows = core::SQLRowCount( driver_stmt TSRMLS_CC );
r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); r = core::SQLMoreResults( driver_stmt TSRMLS_CC );
} while ( r != SQL_NO_DATA ); } 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 // 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. // naturally, so we override that here with no rows returned.
if( rows == -1 ) { if( rows == -1 ) {
rows = 0; rows = 0;
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
// copy any errors on the statement to the connection so that the user sees them, since the statement is released // copy any errors on the statement to the connection so that the user sees them, since the statement is released
// before this method returns // before this method returns
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate ));
driver_dbh->set_last_error( driver_stmt->last_error() ); driver_dbh->set_last_error( driver_stmt->last_error() );
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
return -1; return -1;
} }
catch( ... ) { catch( ... ) {
DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." ); DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." );
} }
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
return rows; return rows;
} }
// transaction support functions // transaction support functions
// pdo_sqlsrv_dbh_begin // pdo_sqlsrv_dbh_begin
// Maps to PDO::beginTransaction. // Maps to PDO::beginTransaction.
// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn // 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. // flag is maintained by PDO so we dont have to worry about it.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// Return: // Return:
// 0 for failure and 1 for success. // 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); 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_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); 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" ); DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" );
core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC ); core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC );
return 1; return 1;
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
return 0; return 0;
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred.");
} }
} }
// pdo_sqlsrv_dbh_commit // pdo_sqlsrv_dbh_commit
// Maps to PDO::commit. // Maps to PDO::commit.
// Commits a transaction. Returns the connection to auto-commit mode. // 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 // 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 // transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here. // to worry about it here.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// Return: // Return:
// 0 for failure and 1 for success. // 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); 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_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); 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" ); DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" );
core_sqlsrv_commit( driver_conn TSRMLS_CC ); core_sqlsrv_commit( driver_conn TSRMLS_CC );
return 1; return 1;
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
return 0; return 0;
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred.");
} }
} }
// pdo_sqlsrv_dbh_rollback // pdo_sqlsrv_dbh_rollback
// Maps to PDO::rollback. // Maps to PDO::rollback.
// Rolls back a transaction. Returns the connection in auto-commit mode. // 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 // 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 // transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have
// to worry about it here. // to worry about it here.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// Return: // Return:
// 0 for failure and 1 for success. // 0 for failure and 1 for success.
int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
try { try {
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); 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_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); 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" ); DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" );
core_sqlsrv_rollback( driver_conn TSRMLS_CC ); core_sqlsrv_rollback( driver_conn TSRMLS_CC );
return 1; return 1;
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
return 0; return 0;
} }
catch( ... ) { catch( ... ) {
DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred.");
} }
} }
// pdo_sqlsrv_dbh_set_attr // pdo_sqlsrv_dbh_set_attr
// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. // Maps to PDO::setAttribute. Sets an attribute on the PDO connection object.
// PDO driver manager calls this function directly after calling the factory // PDO driver manager calls this function directly after calling the factory
// method for PDO, for any attribute which is specified in the PDO constructor. // method for PDO, for any attribute which is specified in the PDO constructor.
// Parameters: // Parameters:
// dbh - The PDO connection object maintained by PDO. // dbh - The PDO connection object maintained by PDO.
// attr - The attribute to be set. // attr - The attribute to be set.
// val - The value of the attribute to be set. // val - The value of the attribute to be set.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC ) int pdo_sqlsrv_dbh_set_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); 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."); SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_set_attr: driver_data object was NULL.");
try { try {
switch( attr ) { switch( attr ) {
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
{ {
zend_long attr_value; zend_long attr_value;
if( Z_TYPE_P( val ) != IS_LONG ) { if( Z_TYPE_P( val ) != IS_LONG ) {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING );
} }
attr_value = Z_LVAL_P( val ); attr_value = Z_LVAL_P( val );
switch( attr_value ) { switch( attr_value ) {
case SQLSRV_ENCODING_DEFAULT: case SQLSRV_ENCODING_DEFAULT:
// when default is applied to a connection, that means use UTF-8 encoding // when default is applied to a connection, that means use UTF-8 encoding
driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 ); driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 );
break; break;
case SQLSRV_ENCODING_SYSTEM: case SQLSRV_ENCODING_SYSTEM:
case SQLSRV_ENCODING_UTF8: case SQLSRV_ENCODING_UTF8:
driver_dbh->set_encoding( static_cast<SQLSRV_ENCODING>( attr_value )); driver_dbh->set_encoding( static_cast<SQLSRV_ENCODING>( attr_value ));
break; break;
default: default:
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING );
break; break;
} }
} }
break; break;
case SQLSRV_ATTR_DIRECT_QUERY: case SQLSRV_ATTR_DIRECT_QUERY:
driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false; driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false;
break; break;
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) {
convert_to_string( val ); convert_to_string( val );
THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( 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 ) ); driver_dbh->query_timeout = static_cast<long>( Z_LVAL_P( val ) );
break; break;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) {
convert_to_string( val ); convert_to_string( val );
THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( 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 ); driver_dbh->client_buffer_max_size = Z_LVAL_P( val );
break; break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false; driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false;
break; break;
// Not supported // Not supported
case PDO_ATTR_FETCH_TABLE_NAMES: case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES: case PDO_ATTR_FETCH_CATALOG_NAMES:
case PDO_ATTR_PREFETCH: case PDO_ATTR_PREFETCH:
case PDO_ATTR_MAX_COLUMN_LEN: case PDO_ATTR_MAX_COLUMN_LEN:
case PDO_ATTR_CURSOR_NAME: case PDO_ATTR_CURSOR_NAME:
case PDO_ATTR_AUTOCOMMIT: case PDO_ATTR_AUTOCOMMIT:
case PDO_ATTR_PERSISTENT: case PDO_ATTR_PERSISTENT:
case PDO_ATTR_TIMEOUT: case PDO_ATTR_TIMEOUT:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR );
} }
// Read-only // Read-only
case PDO_ATTR_SERVER_VERSION: case PDO_ATTR_SERVER_VERSION:
case PDO_ATTR_SERVER_INFO: case PDO_ATTR_SERVER_INFO:
case PDO_ATTR_CLIENT_VERSION: case PDO_ATTR_CLIENT_VERSION:
case PDO_ATTR_DRIVER_NAME: case PDO_ATTR_DRIVER_NAME:
case PDO_ATTR_CONNECTION_STATUS: case PDO_ATTR_CONNECTION_STATUS:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR );
} }
// Statement level only // Statement level only
case PDO_ATTR_EMULATE_PREPARES: case PDO_ATTR_EMULATE_PREPARES:
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
} }
default: default:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR );
break; break;
} }
} }
} }
catch( pdo::PDOException& ) { catch( pdo::PDOException& ) {
return 0; return 0;
} }
return 1; return 1;
} }
// pdo_sqlsrv_dbh_get_attr // pdo_sqlsrv_dbh_get_attr
// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object. // Maps to PDO::getAttribute. Gets an attribute on the PDO connection object.
// Parameters: // Parameters:
// dbh - The PDO connection object maintained by PDO. // dbh - The PDO connection object maintained by PDO.
// attr - The attribute to get. // attr - The attribute to get.
// return_value - zval in which to return the attribute value. // return_value - zval in which to return the attribute value.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ) int pdo_sqlsrv_dbh_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); 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."); SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_get_attr: driver_data object was NULL.");
try { try {
switch( attr ) { switch( attr ) {
// Not supported // Not supported
case PDO_ATTR_FETCH_TABLE_NAMES: case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES: case PDO_ATTR_FETCH_CATALOG_NAMES:
case PDO_ATTR_PREFETCH: case PDO_ATTR_PREFETCH:
case PDO_ATTR_MAX_COLUMN_LEN: case PDO_ATTR_MAX_COLUMN_LEN:
case PDO_ATTR_CURSOR_NAME: case PDO_ATTR_CURSOR_NAME:
case PDO_ATTR_AUTOCOMMIT: case PDO_ATTR_AUTOCOMMIT:
case PDO_ATTR_TIMEOUT: case PDO_ATTR_TIMEOUT:
{ {
// PDO does not throw "not supported" error message for these attributes. // PDO does not throw "not supported" error message for these attributes.
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR );
} }
// Statement level only // Statement level only
case PDO_ATTR_EMULATE_PREPARES: case PDO_ATTR_EMULATE_PREPARES:
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR );
} }
case PDO_ATTR_STRINGIFY_FETCHES: case PDO_ATTR_STRINGIFY_FETCHES:
{ {
// For this attribute, if we dont set the return_value than PDO returns NULL. // For this attribute, if we dont set the return_value than PDO returns NULL.
ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) ); ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) );
break; break;
} }
case PDO_ATTR_SERVER_INFO: case PDO_ATTR_SERVER_INFO:
{ {
core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC ); core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC );
break; break;
} }
case PDO_ATTR_SERVER_VERSION: case PDO_ATTR_SERVER_VERSION:
{ {
core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC ); core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC );
break; break;
} }
case PDO_ATTR_CLIENT_VERSION: case PDO_ATTR_CLIENT_VERSION:
{ {
core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC ); core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC );
//Add the PDO SQLSRV driver's file version //Add the PDO SQLSRV driver's file version
//Declarations below eliminate compiler warnings about string constant to char* conversions //Declarations below eliminate compiler warnings about string constant to char* conversions
const char* extver = "ExtensionVer"; const char* extver = "ExtensionVer";
std::string filever = VER_FILEVERSION_STR; std::string filever = VER_FILEVERSION_STR;
core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/ core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/
TSRMLS_CC ); TSRMLS_CC );
break; break;
} }
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
{ {
ZVAL_LONG( return_value, driver_dbh->encoding() ); ZVAL_LONG( return_value, driver_dbh->encoding() );
break; break;
} }
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
{ {
ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout ));
break; break;
} }
case SQLSRV_ATTR_DIRECT_QUERY: case SQLSRV_ATTR_DIRECT_QUERY:
{ {
ZVAL_BOOL( return_value, driver_dbh->direct_query ); ZVAL_BOOL( return_value, driver_dbh->direct_query );
break; break;
} }
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
{ {
ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size );
break; break;
} }
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
{ {
ZVAL_BOOL( return_value, driver_dbh->fetch_numeric ); ZVAL_BOOL( return_value, driver_dbh->fetch_numeric );
break; break;
} }
default: default:
{ {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR );
break; break;
} }
} }
return 1; return 1;
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
return 0; return 0;
} }
} }
// Called by PDO::errorInfo and PDOStatement::errorInfo. // Called by PDO::errorInfo and PDOStatement::errorInfo.
// Returns the error info. // Returns the error info.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// stmt - The PDO managed statement object. // stmt - The PDO managed statement object.
// info - zval in which to return the error info. // info - zval in which to return the error info.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt,
_Out_ zval *info TSRMLS_DC) _Out_ zval *info TSRMLS_DC)
{ {
SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." ); SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." );
sqlsrv_error* ctx_error = NULL; sqlsrv_error* ctx_error = NULL;
if( stmt ) { if( stmt ) {
ctx_error = static_cast<sqlsrv_stmt*>( stmt->driver_data )->last_error(); ctx_error = static_cast<sqlsrv_stmt*>( stmt->driver_data )->last_error();
} }
else { else {
ctx_error = static_cast<sqlsrv_conn*>( dbh->driver_data )->last_error(); ctx_error = static_cast<sqlsrv_conn*>( dbh->driver_data )->last_error();
} }
pdo_sqlsrv_retrieve_context_error( ctx_error, info ); pdo_sqlsrv_retrieve_context_error( ctx_error, info );
return 1; return 1;
} }
// pdo_sqlsrv_dbh_last_id // pdo_sqlsrv_dbh_last_id
// Maps to PDO::lastInsertId. // Maps to PDO::lastInsertId.
// Returns the last id generated by an executed SQL statement // Returns the last id generated by an executed SQL statement
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// name - Table name. // name - Table name.
// len - Length of the name. // len - Length of the name.
// Return: // Return:
// Returns the last insert id as a string. // Returns the last insert id as a string.
char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC ) char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name, _Out_ size_t* len TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
// turn off any error handling for last_id // turn off any error handling for last_id
pdo_error_mode prev_err_mode = dbh->error_mode; pdo_error_mode prev_err_mode = dbh->error_mode;
dbh->error_mode = PDO_ERRMODE_SILENT; dbh->error_mode = PDO_ERRMODE_SILENT;
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt; sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt;
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data ); 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." ); SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." );
sqlsrv_malloc_auto_ptr<char> id_str; sqlsrv_malloc_auto_ptr<char> id_str;
id_str = reinterpret_cast<char*>( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN )); id_str = reinterpret_cast<char*>( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN ));
try { try {
char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ]; char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ];
if( name == NULL ) { if( name == NULL ) {
strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY ); strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY );
} }
else { else {
char* quoted_table = NULL; char* quoted_table = NULL;
size_t quoted_len = 0; size_t quoted_len = 0;
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL TSRMLS_CC ); int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL TSRMLS_CC );
SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name."); SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name.");
snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table ); snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table );
sqlsrv_free( quoted_table ); sqlsrv_free( quoted_table );
} }
// temp PDO statement used for error handling if something happens // temp PDO statement used for error handling if something happens
pdo_stmt_t temp_stmt; pdo_stmt_t temp_stmt;
temp_stmt.dbh = dbh; temp_stmt.dbh = dbh;
// allocate a full driver statement to take advantage of the error handling // allocate a full driver statement to take advantage of the error handling
driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt<pdo_sqlsrv_stmt>, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC );
driver_stmt->set_func( __FUNCTION__ ); driver_stmt->set_func( __FUNCTION__ );
sqlsrv_malloc_auto_ptr<SQLWCHAR> wsql_string; sqlsrv_malloc_auto_ptr<SQLWCHAR> wsql_string;
unsigned int wsql_len; unsigned int wsql_len;
wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast<const char*>( last_insert_id_query ), strlen(last_insert_id_query), &wsql_len ); wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast<const char*>( last_insert_id_query ), strlen(last_insert_id_query), &wsql_len );
CHECK_CUSTOM_ERROR( wsql_string == 0, driver_stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message() ) { CHECK_CUSTOM_ERROR( wsql_string == 0, driver_stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, get_last_error_message() ) {
throw core::CoreException(); throw core::CoreException();
} }
// execute the last insert id query // execute the last insert id query
core::SQLExecDirectW( driver_stmt, wsql_string TSRMLS_CC ); core::SQLExecDirectW( driver_stmt, wsql_string TSRMLS_CC );
core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ); core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC );
SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN, SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN,
reinterpret_cast<SQLLEN*>( len ), false TSRMLS_CC ); reinterpret_cast<SQLLEN*>( len ), false TSRMLS_CC );
CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt, CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt,
PDO_SQLSRV_ERROR_LAST_INSERT_ID ) { PDO_SQLSRV_ERROR_LAST_INSERT_ID ) {
throw core::CoreException(); throw core::CoreException();
} }
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
// copy any errors on the statement to the connection so that the user sees them, since the statement is released // copy any errors on the statement to the connection so that the user sees them, since the statement is released
// before this method returns // before this method returns
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate )); reinterpret_cast<const char*>( driver_stmt->last_error()->sqlstate ));
driver_dbh->set_last_error( driver_stmt->last_error() ); driver_dbh->set_last_error( driver_stmt->last_error() );
if( driver_stmt ) { if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt(); driver_stmt->~sqlsrv_stmt();
} }
strcpy_s( id_str.get(), 1, "" ); strcpy_s( id_str.get(), 1, "" );
*len = 0; *len = 0;
} }
char* ret_id_str = id_str.get(); char* ret_id_str = id_str.get();
id_str.transferred(); id_str.transferred();
// restore error handling to its previous mode // restore error handling to its previous mode
dbh->error_mode = prev_err_mode; dbh->error_mode = prev_err_mode;
return ret_id_str; return ret_id_str;
} }
// pdo_sqlsrv_dbh_quote // pdo_sqlsrv_dbh_quote
// Maps to PDO::quote. As the name says, this function quotes a string. // Maps to PDO::quote. As the name says, this function quotes a string.
// Always returns a valid string unless memory allocation fails. // Always returns a valid string unless memory allocation fails.
// Parameters: // Parameters:
// dbh - The PDO managed connection object. // dbh - The PDO managed connection object.
// unquoted - The unquoted string to be quoted. // unquoted - The unquoted string to be quoted.
// unquoted_len - Length of the unquoted string. // unquoted_len - Length of the unquoted string.
// quoted - Buffer for output string. // quoted - Buffer for output string.
// quoted_len - Length of the output string. // quoted_len - Length of the output string.
// Return: // Return:
// 0 for failure, 1 for success. // 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const char* unquoted, _In_ size_t unquoted_len, _Outptr_result_buffer_(*quoted_len) char **quoted, _Out_ size_t* quoted_len, int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const char* unquoted, _In_ size_t unquoted_len, _Outptr_result_buffer_(*quoted_len) char **quoted, _Out_ size_t* quoted_len,
enum pdo_param_type /*paramtype*/ TSRMLS_DC ) enum pdo_param_type /*paramtype*/ TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR; SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR;
// get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from: // get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from:
// 1. PDO::quote() - object name is PDO // 1. PDO::quote() - object name is PDO
// 2. PDOStatement::execute() - object name is PDOStatement // 2. PDOStatement::execute() - object name is PDOStatement
zend_execute_data* execute_data = EG( current_execute_data ); zend_execute_data* execute_data = EG( current_execute_data );
zval *object = getThis(); zval *object = getThis();
// iterate through parents to find "PDOStatement" // iterate through parents to find "PDOStatement"
bool is_statement = false; bool is_statement = false;
if ( object ) { if ( object ) {
zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce; zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce;
while ( curr_class != NULL ) { while ( curr_class != NULL ) {
if ( strcmp( reinterpret_cast<const char*>( curr_class->name->val ), "PDOStatement" ) == 0 ) { if ( strcmp( reinterpret_cast<const char*>( curr_class->name->val ), "PDOStatement" ) == 0 ) {
is_statement = true; is_statement = true;
break; break;
} }
curr_class = curr_class->parent; 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 // 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) // is prepared with emulate prepared on)
if ( is_statement ) { if ( is_statement ) {
pdo_stmt_t *stmt = Z_PDO_STMT_P( object ); pdo_stmt_t *stmt = Z_PDO_STMT_P( object );
// set the encoding to be the encoding of the statement otherwise set to be the encoding of the dbh // set the encoding to be the encoding of the statement otherwise set to be the encoding of the dbh
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) { if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) {
encoding = driver_stmt->encoding(); encoding = driver_stmt->encoding();
} }
else { else {
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( stmt->driver_data ); pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( stmt->driver_data );
encoding = driver_dbh->encoding(); encoding = driver_dbh->encoding();
} }
// get the placeholder at the current position in driver_stmt->placeholders ht // get the placeholder at the current position in driver_stmt->placeholders ht
zval* placeholder = NULL; zval* placeholder = NULL;
if (( placeholder = zend_hash_get_current_data( driver_stmt->placeholders )) != NULL && zend_hash_move_forward( driver_stmt->placeholders ) == SUCCESS && stmt->bound_params != NULL ) { if (( placeholder = zend_hash_get_current_data( driver_stmt->placeholders )) != NULL && zend_hash_move_forward( driver_stmt->placeholders ) == SUCCESS && stmt->bound_params != NULL ) {
pdo_bound_param_data* param = NULL; pdo_bound_param_data* param = NULL;
if ( Z_TYPE_P( placeholder ) == IS_STRING ) { 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 ))); param = reinterpret_cast<pdo_bound_param_data*>( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder )));
} }
else if ( Z_TYPE_P( placeholder ) == IS_LONG) { 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 ))); param = reinterpret_cast<pdo_bound_param_data*>( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder )));
} }
if ( NULL != param ) { if ( NULL != param ) {
SQLSRV_ENCODING param_encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params )); SQLSRV_ENCODING param_encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
if ( param_encoding != SQLSRV_ENCODING_INVALID ) { if ( param_encoding != SQLSRV_ENCODING_INVALID ) {
encoding = param_encoding; encoding = param_encoding;
} }
} }
} }
} }
if ( encoding == SQLSRV_ENCODING_BINARY ) { if ( encoding == SQLSRV_ENCODING_BINARY ) {
// convert from char* to hex digits using os // convert from char* to hex digits using os
std::basic_ostringstream<char> os; std::basic_ostringstream<char> os;
for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
// if unquoted is < 0 or > 255, that means this is a non-ascii character. Translation from non-ascii to binary is not supported. // if unquoted is < 0 or > 255, that means this is a non-ascii character. Translation from non-ascii to binary is not supported.
// return an empty terminated string for now // return an empty terminated string for now
if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) { if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) {
*quoted_len = 0; *quoted_len = 0;
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); *quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 ));
( *quoted )[ 0 ] = '\0'; ( *quoted )[ 0 ] = '\0';
return 1; return 1;
} }
// when an int is < 16 and is appended to os, its hex representation which starts // when an int is < 16 and is appended to os, its hex representation which starts
// with '0' does not get appended properly (the starting '0' does not get appended) // with '0' does not get appended properly (the starting '0' does not get appended)
// thus append '0' first // thus append '0' first
if (( int )unquoted[index] < 16 ) { if (( int )unquoted[index] < 16 ) {
os << '0'; os << '0';
} }
os << std::hex << ( int )unquoted[ index ]; os << std::hex << ( int )unquoted[ index ];
} }
std::basic_string<char> str_hex = os.str(); std::basic_string<char> str_hex = os.str();
// each character is represented by 2 digits of hex // each character is represented by 2 digits of hex
size_t unquoted_str_len = unquoted_len * 2; // length returned should not account for null terminator size_t unquoted_str_len = unquoted_len * 2; // length returned should not account for null terminator
char* unquoted_str = reinterpret_cast<char*>( sqlsrv_malloc( unquoted_str_len, sizeof( char ), 1 )); // include space for null terminator char* unquoted_str = reinterpret_cast<char*>( sqlsrv_malloc( unquoted_str_len, sizeof( char ), 1 )); // include space for null terminator
strcpy_s( unquoted_str, unquoted_str_len + 1 /* include null terminator*/, str_hex.c_str() ); strcpy_s( unquoted_str, unquoted_str_len + 1 /* include null terminator*/, str_hex.c_str() );
// include length of '0x' in the binary string // include length of '0x' in the binary string
*quoted_len = unquoted_str_len + 2; *quoted_len = unquoted_str_len + 2;
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); *quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 ));
unsigned int out_current = 0; unsigned int out_current = 0;
// insert '0x' // insert '0x'
( *quoted )[ out_current++ ] = '0'; ( *quoted )[ out_current++ ] = '0';
( *quoted )[ out_current++ ] = 'x'; ( *quoted )[ out_current++ ] = 'x';
for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) { for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) {
( *quoted )[ out_current++ ] = unquoted_str[ index ]; ( *quoted )[ out_current++ ] = unquoted_str[ index ];
} }
// null terminator // null terminator
( *quoted )[ out_current ] = '\0'; ( *quoted )[ out_current ] = '\0';
sqlsrv_free( unquoted_str ); sqlsrv_free( unquoted_str );
return 1; return 1;
} }
else { else {
// count the number of quotes needed // count the number of quotes needed
unsigned int quotes_needed = 2; // the initial start and end quotes of course unsigned int quotes_needed = 2; // the initial start and end quotes of course
// include the N proceeding the initial quote if encoding is UTF8 // include the N proceeding the initial quote if encoding is UTF8
if ( encoding == SQLSRV_ENCODING_UTF8 ) { if ( encoding == SQLSRV_ENCODING_UTF8 ) {
quotes_needed = 3; quotes_needed = 3;
} }
for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
if ( unquoted[ index ] == '\'' ) { if ( unquoted[ index ] == '\'' ) {
++quotes_needed; ++quotes_needed;
} }
} }
*quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator. *quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator.
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator. *quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator.
unsigned int out_current = 0; unsigned int out_current = 0;
// insert N if the encoding is UTF8 // insert N if the encoding is UTF8
if ( encoding == SQLSRV_ENCODING_UTF8 ) { if ( encoding == SQLSRV_ENCODING_UTF8 ) {
( *quoted )[ out_current++ ] = 'N'; ( *quoted )[ out_current++ ] = 'N';
} }
// insert initial quote // insert initial quote
( *quoted )[ out_current++ ] = '\''; ( *quoted )[ out_current++ ] = '\'';
for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
if ( unquoted[ index ] == '\'' ) { if ( unquoted[ index ] == '\'' ) {
( *quoted )[ out_current++ ] = '\''; ( *quoted )[ out_current++ ] = '\'';
( *quoted )[ out_current++ ] = '\''; ( *quoted )[ out_current++ ] = '\'';
} }
else { else {
( *quoted )[ out_current++ ] = unquoted[ index ]; ( *quoted )[ out_current++ ] = unquoted[ index ];
} }
} }
// trailing quote and null terminator // trailing quote and null terminator
( *quoted )[ out_current++ ] = '\''; ( *quoted )[ out_current++ ] = '\'';
( *quoted )[ out_current ] = '\0'; ( *quoted )[ out_current ] = '\0';
return 1; return 1;
} }
} }
// This method is not implemented by this driver. // This method is not implemented by this driver.
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC ) pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( _Inout_ pdo_dbh_t *dbh, int kind TSRMLS_DC )
{ {
PDO_RESET_DBH_ERROR; PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN; PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY; PDO_LOG_DBH_ENTRY;
sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data ); sqlsrv_conn* driver_conn = reinterpret_cast<sqlsrv_conn*>( dbh->driver_data );
SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_get_driver_methods: driver_data object was NULL." ); SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_get_driver_methods: driver_data object was NULL." );
CHECK_CUSTOM_ERROR( true, driver_conn, PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED ) { CHECK_CUSTOM_ERROR( true, driver_conn, PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED ) {
return NULL; return NULL;
} }
return NULL; // to avoid a compiler warning return NULL; // to avoid a compiler warning
} }
namespace { namespace {
// Maps the PDO driver specific statement option/attribute constants to the core layer // Maps the PDO driver specific statement option/attribute constants to the core layer
// statement option/attribute constants. // statement option/attribute constants.
void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht,
_Inout_ zval* data TSRMLS_DC ) _Inout_ zval* data TSRMLS_DC )
{ {
zend_ulong option_key = -1; zend_ulong option_key = -1;
switch( key ) { switch( key ) {
case PDO_ATTR_CURSOR: case PDO_ATTR_CURSOR:
option_key = SQLSRV_STMT_OPTION_SCROLLABLE; option_key = SQLSRV_STMT_OPTION_SCROLLABLE;
break; break;
case SQLSRV_ATTR_ENCODING: case SQLSRV_ATTR_ENCODING:
option_key = PDO_STMT_OPTION_ENCODING; option_key = PDO_STMT_OPTION_ENCODING;
break; break;
case SQLSRV_ATTR_QUERY_TIMEOUT: case SQLSRV_ATTR_QUERY_TIMEOUT:
option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT; option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT;
break; break;
case PDO_ATTR_STATEMENT_CLASS: case PDO_ATTR_STATEMENT_CLASS:
break; break;
case SQLSRV_ATTR_DIRECT_QUERY: case SQLSRV_ATTR_DIRECT_QUERY:
option_key = PDO_STMT_OPTION_DIRECT_QUERY; option_key = PDO_STMT_OPTION_DIRECT_QUERY;
break; break;
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE; option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE;
break; break;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE; option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE;
break; break;
case PDO_ATTR_EMULATE_PREPARES: case PDO_ATTR_EMULATE_PREPARES:
option_key = PDO_STMT_OPTION_EMULATE_PREPARES; option_key = PDO_STMT_OPTION_EMULATE_PREPARES;
break; break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE; option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE;
break; break;
default: default:
CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core::CoreException(); throw core::CoreException();
} }
break; break;
} }
// if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it // if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it
if( option_key != -1 ) { if( option_key != -1 ) {
zval_add_ref( data ); zval_add_ref( data );
core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC ); core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC );
} }
} }
// validate_stmt_options // validate_stmt_options
// Iterates through the list of statement options provided by the user and validates them // 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 // 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. // creates a Hashtable of statement options to be sent to the core layer for processing.
// Parameters: // Parameters:
// ctx - The current context. // ctx - The current context.
// stmt_options - The user provided list of statement options. // stmt_options - The user provided list of statement options.
// pdo_stmt_options_ht - Output hashtable of statement options. // pdo_stmt_options_ht - Output hashtable of statement options.
void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ) void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC )
{ {
try { try {
if( stmt_options ) { if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options ); HashTable* options_ht = Z_ARRVAL_P( stmt_options );
size_t int_key = -1; size_t int_key = -1;
zend_string *key = NULL; zend_string *key = NULL;
zval* data = NULL; zval* data = NULL;
ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) { ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) {
int type = HASH_KEY_NON_EXISTENT; int type = HASH_KEY_NON_EXISTENT;
int result = 0; int result = 0;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; 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 ) { CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core::CoreException(); throw core::CoreException();
} }
add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC );
} ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END();
} }
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;
} }
} }
void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC ) void pdo_bool_conn_str_func::func( _In_ connection_option const* option, _Inout_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str TSRMLS_DC )
{ {
TSRMLS_C; TSRMLS_C;
char const* val_str = "no"; char const* val_str = "no";
if( core_str_zval_is_true( value ) ) { if( core_str_zval_is_true( value ) ) {
val_str = "yes"; val_str = "yes";
} }
conn_str += option->odbc_name; conn_str += option->odbc_name;
conn_str += "={"; conn_str += "={";
conn_str += val_str; conn_str += val_str;
conn_str += "};"; conn_str += "};";
} }
void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn,
std::string& /*conn_str*/ TSRMLS_DC ) std::string& /*conn_str*/ TSRMLS_DC )
{ {
try { try {
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); 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 ); const char* val = Z_STRVAL_P( value_z );
size_t val_len = Z_STRLEN_P( value_z ); size_t val_len = Z_STRLEN_P( value_z );
zend_long out_val = SQL_TXN_READ_COMMITTED; zend_long out_val = SQL_TXN_READ_COMMITTED;
// READ_COMMITTED // READ_COMMITTED
if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) {
out_val = SQL_TXN_READ_COMMITTED; out_val = SQL_TXN_READ_COMMITTED;
} }
// READ_UNCOMMITTED // READ_UNCOMMITTED
else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) {
out_val = SQL_TXN_READ_UNCOMMITTED; out_val = SQL_TXN_READ_UNCOMMITTED;
} }
// REPEATABLE_READ // REPEATABLE_READ
else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) {
out_val = SQL_TXN_REPEATABLE_READ; out_val = SQL_TXN_REPEATABLE_READ;
} }
// SERIALIZABLE // SERIALIZABLE
else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) {
out_val = SQL_TXN_SERIALIZABLE; out_val = SQL_TXN_SERIALIZABLE;
} }
// SNAPSHOT // SNAPSHOT
else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 )
&& !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) {
out_val = SQL_TXN_SS_SNAPSHOT; out_val = SQL_TXN_SS_SNAPSHOT;
} }
else { else {
CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) {
throw core::CoreException(); throw core::CoreException();
} }
} }
core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER TSRMLS_CC ); core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast<SQLPOINTER>( out_val ), SQL_IS_UINTEGER TSRMLS_CC );
} }
catch( core::CoreException& ) { catch( core::CoreException& ) {
throw; throw;
} }
} }
} // namespace } // namespace

View file

@ -1,60 +1,60 @@
--TEST-- --TEST--
Provide name in lastInsertId to retrieve the last sequence number Provide name in lastInsertId to retrieve the last sequence number
--SKIPIF-- --SKIPIF--
--FILE-- --FILE--
<?php <?php
include 'pdo_tools.inc'; include 'pdo_tools.inc';
require_once("autonomous_setup.php"); require_once("autonomous_setup.php");
try{ try{
$database = "tempdb"; $database = "tempdb";
$conn = new PDO("sqlsrv:Server=$serverName;Database=$database", $username, $password); $conn = new PDO("sqlsrv:Server=$serverName;Database=$database", $username, $password);
// sequence is only supported in SQL server 2012 and up (or version 11 and up) // sequence is only supported in SQL server 2012 and up (or version 11 and up)
// Output Done once the server version is found to be < 11 // Output Done once the server version is found to be < 11
$version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION)); $version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION));
if ($version_arr[0] < 11) { if ($version_arr[0] < 11) {
echo "Done\n"; echo "Done\n";
} }
else { else {
$tableName1 = GetTempTableName('tab1'); $tableName1 = GetTempTableName('tab1');
$tableName2 = GetTempTableName('tab2'); $tableName2 = GetTempTableName('tab2');
$sequenceName = 'sequence1'; $sequenceName = 'sequence1';
$stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName"); $stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName");
$sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; $sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))"; $sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE"; $sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )"); $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )");
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )"); $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )");
$ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )"); $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )");
$ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )"); $ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )");
// return the last sequence number is sequence name is provided // return the last sequence number is sequence name is provided
$lastSeq = $conn->lastInsertId($sequenceName); $lastSeq = $conn->lastInsertId($sequenceName);
// defaults to $tableName2 -- because it returns the last inserted id value // defaults to $tableName2 -- because it returns the last inserted id value
$lastRow = $conn->lastInsertId(); $lastRow = $conn->lastInsertId();
if ($lastSeq == 3 && $lastRow == 1) { if ($lastSeq == 3 && $lastRow == 1) {
echo "Done\n"; echo "Done\n";
} }
else { else {
echo "sequence value or identity does not match as expected\n"; echo "sequence value or identity does not match as expected\n";
} }
$stmt = $conn->query("DROP TABLE $tableName1"); $stmt = $conn->query("DROP TABLE $tableName1");
$stmt = $conn->query("DROP TABLE $tableName2"); $stmt = $conn->query("DROP TABLE $tableName2");
$stmt = $conn->query("DROP SEQUENCE $sequenceName"); $stmt = $conn->query("DROP SEQUENCE $sequenceName");
$stmt = null; $stmt = null;
} }
$conn = null; $conn = null;
} }
catch (Exception $e){ catch (Exception $e){
echo "Exception $e\n"; echo "Exception $e\n";
} }
?> ?>
--EXPECT-- --EXPECT--
Done Done

View file

@ -1,62 +1,62 @@
--TEST-- --TEST--
LastInsertId returns the last sequences operating on the same table LastInsertId returns the last sequences operating on the same table
--SKIPIF-- --SKIPIF--
--FILE-- --FILE--
<?php <?php
include 'pdo_tools.inc'; include 'pdo_tools.inc';
require_once("autonomous_setup.php"); require_once("autonomous_setup.php");
try{ try{
$database = "tempdb"; $database = "tempdb";
$conn = new PDO("sqlsrv:Server=$serverName;Database=$database", $username, $password); $conn = new PDO("sqlsrv:Server=$serverName;Database=$database", $username, $password);
// sequence is only supported in SQL server 2012 and up (or version 11 and up) // sequence is only supported in SQL server 2012 and up (or version 11 and up)
// Output Done once the server version is found to be < 11 // Output Done once the server version is found to be < 11
$version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION)); $version_arr = explode(".", $conn->getAttribute(PDO::ATTR_SERVER_VERSION));
if ($version_arr[0] < 11) { if ($version_arr[0] < 11) {
echo "Done\n"; echo "Done\n";
} }
else { else {
$tableName = GetTempTableName('tab'); $tableName = GetTempTableName('tab');
$sequence1 = 'sequence1'; $sequence1 = 'sequence1';
$sequence2 = 'sequenceNeg1'; $sequence2 = 'sequenceNeg1';
$stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1"); $stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1");
$stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2"); $stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2");
$sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; $sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100"; $sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200"; $sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200";
$stmt = $conn->query($sql); $stmt = $conn->query($sql);
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )");
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )");
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )");
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )");
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )");
$ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )"); $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )");
// return the last sequence number of 'sequence1' // return the last sequence number of 'sequence1'
$lastSeq1 = $conn->lastInsertId($sequence1); $lastSeq1 = $conn->lastInsertId($sequence1);
// return the last sequence number of 'sequenceNeg1' // return the last sequence number of 'sequenceNeg1'
$lastSeq2 = $conn->lastInsertId($sequence2); $lastSeq2 = $conn->lastInsertId($sequence2);
// providing a table name in lastInsertId should return an empty string // providing a table name in lastInsertId should return an empty string
$lastSeq3 = $conn->lastInsertId($tableName); $lastSeq3 = $conn->lastInsertId($tableName);
if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") { if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") {
echo "Done\n"; echo "Done\n";
} }
$stmt = $conn->query("DROP TABLE $tableName"); $stmt = $conn->query("DROP TABLE $tableName");
$stmt = $conn->query("DROP SEQUENCE $sequence1"); $stmt = $conn->query("DROP SEQUENCE $sequence1");
$stmt = $conn->query("DROP SEQUENCE $sequence2"); $stmt = $conn->query("DROP SEQUENCE $sequence2");
$stmt = null; $stmt = null;
} }
$conn = null; $conn = null;
} }
catch (Exception $e){ catch (Exception $e){
echo "Exception $e\n"; echo "Exception $e\n";
} }
?> ?>
--EXPECT-- --EXPECT--
Done Done