From 53318fbbdd18d02d793481a8808827ef2799dec2 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 14:05:14 -0700 Subject: [PATCH 01/10] new PR for lastInsertID --- source/pdo_sqlsrv/pdo_dbh.cpp | 3284 ++++++++++++++++----------------- 1 file changed, 1642 insertions(+), 1642 deletions(-) diff --git a/source/pdo_sqlsrv/pdo_dbh.cpp b/source/pdo_sqlsrv/pdo_dbh.cpp index da2bc727..7ea59d3c 100644 --- a/source/pdo_sqlsrv/pdo_dbh.cpp +++ b/source/pdo_sqlsrv/pdo_dbh.cpp @@ -1,1642 +1,1642 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// file: pdo_dbh.cpp -// -// Contents: Implements the PDO object for PDO_SQLSRV -// -// Microsoft Drivers 4.3 for PHP for SQL Server -// Copyright(c) Microsoft Corporation -// All rights reserved. -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//--------------------------------------------------------------------------------------------------------------------------------- - -#include "php_pdo_sqlsrv.h" - -#include -#include - -typedef const zend_function_entry pdo_sqlsrv_function_entry; - -// *** internal variables and constants *** - -namespace { - -const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; -const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer -const char TABLE_LAST_INSERT_ID_QUERY[] = "SELECT IDENT_CURRENT(%s)"; -const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( TABLE_LAST_INSERT_ID_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes - -// List of PDO supported connection options. -namespace PDOConnOptionNames { - -const char Server[] = "Server"; -const char APP[] = "APP"; -const char ApplicationIntent[] = "ApplicationIntent"; -const char AttachDBFileName[] = "AttachDbFileName"; -const char ConnectionPooling[] = "ConnectionPooling"; -const char Authentication[] = "Authentication"; -#ifdef _WIN32 -const char ConnectRetryCount[] = "ConnectRetryCount"; -const char ConnectRetryInterval[] = "ConnectRetryInterval"; -#endif // _WIN32 -const char Database[] = "Database"; -const char Encrypt[] = "Encrypt"; -const char Failover_Partner[] = "Failover_Partner"; -const char LoginTimeout[] = "LoginTimeout"; -const char MARS_Option[] = "MultipleActiveResultSets"; -const char MultiSubnetFailover[] = "MultiSubnetFailover"; -const char QuotedId[] = "QuotedId"; -const char TraceFile[] = "TraceFile"; -const char TraceOn[] = "TraceOn"; -const char TrustServerCertificate[] = "TrustServerCertificate"; -const char TransactionIsolation[] = "TransactionIsolation"; -const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution"; -const char WSID[] = "WSID"; - -} - -enum PDO_CONN_OPTIONS { - - PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, - -}; - -enum PDO_STMT_OPTIONS { - - PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC, - PDO_STMT_OPTION_DIRECT_QUERY, - PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, - PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, - PDO_STMT_OPTION_EMULATE_PREPARES, - PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, -}; - -// List of all the statement options supported by this driver. -const stmt_option PDO_STMT_OPTS[] = { - - { NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr( new stmt_option_query_timeout ) }, - { NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr( new stmt_option_pdo_scrollable ) }, - { NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr( new stmt_option_encoding ) }, - { NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, std::unique_ptr( new stmt_option_direct_query ) }, - { NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, std::unique_ptr( new stmt_option_cursor_scroll_type ) }, - { NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr( new stmt_option_buffered_query_limit ) }, - { NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr( new stmt_option_emulate_prepares ) }, - { NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr( new stmt_option_fetch_numeric ) }, - - { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr{} }, -}; - -// boolean connection string -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 ); -}; - -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 ); -}; - -#ifdef _WIN32 -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 ) - { - TSRMLS_C; - SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" ) - - std::string val_str = Z_STRVAL_P( value ); - - conn_str += option->odbc_name; - conn_str += "={"; - conn_str += val_str; - conn_str += "};"; - } -}; -#endif // _WIN32 - -template -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 ) - { - try { - - SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); - - size_t val = static_cast( atoi( Z_STRVAL_P( value )) ); - core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( val ), SQL_IS_UINTEGER TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } - } -}; - -template -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 ) - { - try { - - core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( core_str_zval_is_true( value )), - SQL_IS_UINTEGER TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } - } -}; - -// statement options related functions -void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, - _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 ); - -} // namespace - - -// List of all connection options supported by this driver. -const connection_option PDO_CONN_OPTS[] = { - { - PDOConnOptionNames::Server, - sizeof( PDOConnOptionNames::Server ), - PDO_CONN_OPTION_SERVER, - NULL, - 0, - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::APP, - sizeof( PDOConnOptionNames::APP ), - SQLSRV_CONN_OPTION_APP, - ODBCConnOptions::APP, - sizeof( ODBCConnOptions::APP ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::ApplicationIntent, - sizeof( PDOConnOptionNames::ApplicationIntent ), - SQLSRV_CONN_OPTION_APPLICATION_INTENT, - ODBCConnOptions::ApplicationIntent, - sizeof( ODBCConnOptions::ApplicationIntent ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::AttachDBFileName, - sizeof( PDOConnOptionNames::AttachDBFileName ), - SQLSRV_CONN_OPTION_ATTACHDBFILENAME, - ODBCConnOptions::AttachDBFileName, - sizeof( ODBCConnOptions::AttachDBFileName ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::Authentication, - sizeof( PDOConnOptionNames::Authentication ), - SQLSRV_CONN_OPTION_AUTHENTICATION, - ODBCConnOptions::Authentication, - sizeof( ODBCConnOptions::Authentication ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::ConnectionPooling, - sizeof( PDOConnOptionNames::ConnectionPooling ), - SQLSRV_CONN_OPTION_CONN_POOLING, - ODBCConnOptions::ConnectionPooling, - sizeof( ODBCConnOptions::ConnectionPooling ), - CONN_ATTR_BOOL, - conn_null_func::func - }, -#ifdef _WIN32 - { - PDOConnOptionNames::ConnectRetryCount, - sizeof( PDOConnOptionNames::ConnectRetryCount ), - SQLSRV_CONN_OPTION_CONN_RETRY_COUNT, - ODBCConnOptions::ConnectRetryCount, - sizeof( ODBCConnOptions::ConnectRetryCount ), - CONN_ATTR_INT, - pdo_int_conn_str_func::func - }, - { - PDOConnOptionNames::ConnectRetryInterval, - sizeof( PDOConnOptionNames::ConnectRetryInterval ), - SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL, - ODBCConnOptions::ConnectRetryInterval, - sizeof( ODBCConnOptions::ConnectRetryInterval ), - CONN_ATTR_INT, - pdo_int_conn_str_func::func - }, -#endif // _WIN32 - { - PDOConnOptionNames::Database, - sizeof( PDOConnOptionNames::Database ), - SQLSRV_CONN_OPTION_DATABASE, - ODBCConnOptions::Database, - sizeof( ODBCConnOptions::Database ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::Encrypt, - sizeof( PDOConnOptionNames::Encrypt ), - SQLSRV_CONN_OPTION_ENCRYPT, - ODBCConnOptions::Encrypt, - sizeof( ODBCConnOptions::Encrypt ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::Failover_Partner, - sizeof( PDOConnOptionNames::Failover_Partner ), - SQLSRV_CONN_OPTION_FAILOVER_PARTNER, - ODBCConnOptions::Failover_Partner, - sizeof( ODBCConnOptions::Failover_Partner ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::LoginTimeout, - sizeof( PDOConnOptionNames::LoginTimeout ), - SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, - ODBCConnOptions::LoginTimeout, - sizeof( ODBCConnOptions::LoginTimeout ), - CONN_ATTR_INT, - pdo_int_conn_attr_func::func - }, - { - PDOConnOptionNames::MARS_Option, - sizeof( PDOConnOptionNames::MARS_Option ), - SQLSRV_CONN_OPTION_MARS, - ODBCConnOptions::MARS_ODBC, - sizeof( ODBCConnOptions::MARS_ODBC ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::MultiSubnetFailover, - sizeof( PDOConnOptionNames::MultiSubnetFailover ), - SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, - ODBCConnOptions::MultiSubnetFailover, - sizeof( ODBCConnOptions::MultiSubnetFailover ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::QuotedId, - sizeof( PDOConnOptionNames::QuotedId ), - SQLSRV_CONN_OPTION_QUOTED_ID, - ODBCConnOptions::QuotedId, - sizeof( ODBCConnOptions::QuotedId ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::TraceFile, - sizeof( PDOConnOptionNames::TraceFile ), - SQLSRV_CONN_OPTION_TRACE_FILE, - ODBCConnOptions::TraceFile, - sizeof( ODBCConnOptions::TraceFile ), - CONN_ATTR_STRING, - str_conn_attr_func::func - }, - { - PDOConnOptionNames::TraceOn, - sizeof( PDOConnOptionNames::TraceOn ), - SQLSRV_CONN_OPTION_TRACE_ON, - ODBCConnOptions::TraceOn, - sizeof( ODBCConnOptions::TraceOn ), - CONN_ATTR_BOOL, - pdo_bool_conn_attr_func::func - }, - { - PDOConnOptionNames::TransactionIsolation, - sizeof( PDOConnOptionNames::TransactionIsolation ), - SQLSRV_CONN_OPTION_TRANS_ISOLATION, - ODBCConnOptions::TransactionIsolation, - sizeof( ODBCConnOptions::TransactionIsolation ), - CONN_ATTR_INT, - pdo_txn_isolation_conn_attr_func::func - }, - { - PDOConnOptionNames::TrustServerCertificate, - sizeof( PDOConnOptionNames::TrustServerCertificate ), - SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, - ODBCConnOptions::TrustServerCertificate, - sizeof( ODBCConnOptions::TrustServerCertificate ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::TransparentNetworkIPResolution, - sizeof(PDOConnOptionNames::TransparentNetworkIPResolution), - SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION, - ODBCConnOptions::TransparentNetworkIPResolution, - sizeof(ODBCConnOptions::TransparentNetworkIPResolution), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::WSID, - sizeof( PDOConnOptionNames::WSID ), - SQLSRV_CONN_OPTION_WSID, - ODBCConnOptions::WSID, - sizeof( ODBCConnOptions::WSID ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table -}; - - -// close the connection -int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); - -// execute queries -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 ); -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 -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_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); - -// 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_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ); - -// return more information -int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, - _Out_ zval *info TSRMLS_DC); - -// 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 ); - -// 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 ); - -// quote a string, meaning put quotes around it and escape any quotes within it -int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen, - enum pdo_param_type paramtype TSRMLS_DC ); - -struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = { - - pdo_sqlsrv_dbh_close, - pdo_sqlsrv_dbh_prepare, - pdo_sqlsrv_dbh_do, - pdo_sqlsrv_dbh_quote, - pdo_sqlsrv_dbh_begin, - pdo_sqlsrv_dbh_commit, - pdo_sqlsrv_dbh_rollback, - pdo_sqlsrv_dbh_set_attr, - pdo_sqlsrv_dbh_last_id, - pdo_sqlsrv_dbh_return_error, - pdo_sqlsrv_dbh_get_attr, - NULL, // check liveness not implemented - pdo_sqlsrv_get_driver_methods, - NULL, // request shutdown not implemented - NULL // in transaction not implemented -}; - - -// log a function entry point -#ifndef _WIN32 -#define PDO_LOG_DBH_ENTRY \ -{ \ - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ - driver_dbh->set_func( __FUNCTION__ ); \ - int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \ - char func[length+1]; \ - strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \ - strcat_s( func, length+1, ": entering" ); \ - LOG( SEV_NOTICE, func ); \ -} -#else -#define PDO_LOG_DBH_ENTRY \ -{ \ - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ - driver_dbh->set_func( __FUNCTION__ ); \ - LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \ -} -#endif - -// 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 ) : - sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ), - stmts( NULL ), - direct_query( false ), - query_timeout( QUERY_TIMEOUT_INVALID ), - client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )), - fetch_numeric( false ) -{ - if( client_buffer_max_size < 0 ) { - client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT; - LOG( SEV_WARNING, INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value." ); - } -} - -// pdo_sqlsrv_db_handle_factory -// Maps to PDO::__construct. -// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. -// Does the following things: -// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. -// (If an error occurs in this function, the PDO specification mandates that -// an exception be thrown, regardless of the error mode setting.) -// 2. Processes the driver options. -// 3. Creates a core_conn object by calling core_sqlsrv_connect. -// 4. Restores the previous error mode on success. -// alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory -// Parameters: -// dbh - The PDO managed structure for the connection. -// driver_options - A HashTable (within the zval) of options to use when creating the connection. -// Return: -// 0 for failure, 1 for success. -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" ); - - hash_auto_ptr pdo_conn_options_ht; - pdo_error_mode prev_err_mode = dbh->error_mode; - - // must be done in all cases so that even a failed connection can query the - // object for errors. - dbh->methods = &pdo_sqlsrv_dbh_methods; - dbh->driver_data = NULL; - zval* temp_server_z = NULL; - sqlsrv_malloc_auto_ptr dsn_parser; - zval server_z; - ZVAL_UNDEF( &server_z ); - - try { - - // no matter what the error mode, we want exceptions thrown if the connection fails - // to happen (per the PDO spec) - dbh->error_mode = PDO_ERRMODE_EXCEPTION; - - g_pdo_henv_cp->set_driver( dbh ); - g_pdo_henv_ncp->set_driver( dbh ); - - CHECK_CUSTOM_ERROR( driver_options && Z_TYPE_P( driver_options ) != IS_ARRAY, *g_pdo_henv_cp, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) { - throw core::CoreException(); - } - // throws PDOException if the ATTR_PERSISTENT is in connection options - CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) { - dbh->refcount--; - throw pdo::PDOException(); - } - - // Initialize the options array to be passed to the core layer - ALLOC_HASHTABLE( pdo_conn_options_ht ); - - core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, - 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. - dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, - static_cast( dbh->data_source_len ), pdo_conn_options_ht ); - dsn_parser->parse_conn_string( TSRMLS_C ); - - // Extract the server name - temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); - - CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) { - - throw pdo::PDOException(); - } - - server_z = *temp_server_z; - - // Add a reference to the option value since we are deleting it from the hashtable - zval_add_ref( &server_z ); - zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); - - sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn, Z_STRVAL( server_z ), - dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, - PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC ); - - // Free the string in server_z after being used - zend_string_release( Z_STR( server_z )); - - SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); - - // set the driver_data and methods to complete creation of the PDO object - dbh->driver_data = conn; - dbh->error_mode = prev_err_mode; // reset the error mode - dbh->alloc_own_columns = 1; // we do our own memory management for columns - dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types - - } - catch( core::CoreException& ) { - - if ( Z_TYPE( server_z ) == IS_STRING ) { - zend_string_release( Z_STR( server_z )); - } - dbh->error_mode = prev_err_mode; // reset the error mode - g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL - - return 0; - } - catch( ... ) { - - DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" ); - } - - return 1; -} - -// pdo_sqlsrv_dbh_close -// Maps to PDO::__destruct. -// Called when a PDO object is to be destroyed. -// By the time this function is called, PDO has already made sure that -// all statements are disposed and the PDO object is the last item destroyed. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// Always returns 1 for success. -int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" ); - - // if the connection didn't complete properly, driver_data isn't initialized. - if( dbh->driver_data == NULL ) { - - return 1; - } - - PDO_RESET_DBH_ERROR; - - // call the core layer close - core_sqlsrv_close( reinterpret_cast( dbh->driver_data ) TSRMLS_CC ); - dbh->driver_data = NULL; - - // always return success that the connection is closed - return 1; -} - -// pdo_sqlsrv_dbh_prepare -// Called by PDO::prepare and PDOStatement::__construct. -// Creates a statement and prepares it for execution by PDO -// Paramters: -// dbh - The PDO managed connection object. -// sql - SQL query to be prepared. -// sql_len - Length of the sql query -// stmt - The PDO managed statement object. -// driver_options - User provided list of statement options. -// Return: -// 0 for failure, 1 for success. -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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - hash_auto_ptr pdo_stmt_options_ht; - sqlsrv_malloc_auto_ptr sql_rewrite; - size_t sql_rewrite_len = 0; - sqlsrv_malloc_auto_ptr driver_stmt; - hash_auto_ptr placeholders; - sqlsrv_malloc_auto_ptr sql_parser; - - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); - SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null"); - - try { - - // assign the methods for the statement object. This is necessary even if the - // statement fails so the user can retrieve the error information. - stmt->methods = &pdo_sqlsrv_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names - - // Initialize the options array to be passed to the core layer - ALLOC_HASHTABLE( pdo_stmt_options_ht ); - core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, - 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. - validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC ); - - driver_stmt = static_cast( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, - pdo_stmt_options_ht, PDO_STMT_OPTS, - pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC )); - - // if the user didn't set anything in the prepare options, then set the buffer limit - // to the value set on the connection. - if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { - - driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size; - } - - // if the user didn't set anything in the prepare options, then set the query timeout - // to the value set on the connection. - 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 ); - } - - // rewrite named parameters in the query to positional parameters if we aren't letting PDO do the - // parameter substitution for us - if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { - - // rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named - // parameters for consistency with the PDO MySQL and PDO ODBC drivers. - int zr = pdo_parse_params( stmt, const_cast( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC ); - - CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) { - throw core::CoreException(); - } - // if parameter substitution happened, use that query instead of the original - if( sql_rewrite != 0) { - sql = sql_rewrite; - sql_len = sql_rewrite_len; - } - } - - if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { - - core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC ); - } - else if( driver_stmt->direct_query ) { - - if( driver_stmt->direct_query_subst_string ) { - // we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme - // and we use estrdup below to allocate the new string, which uses emalloc - efree( reinterpret_cast( const_cast( driver_stmt->direct_query_subst_string ))); - } - driver_stmt->direct_query_subst_string = estrdup( sql ); - 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 - // set to the substituted query - if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) { - // parse placeholders in the sql query into the placeholders ht - ALLOC_HASHTABLE( placeholders ); - 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, - static_cast(stmt->query_stringlen), placeholders ); - sql_parser->parse_sql_string( TSRMLS_C ); - driver_stmt->placeholders = placeholders; - placeholders.transferred(); - } - - stmt->driver_data = driver_stmt; - driver_stmt.transferred(); - } - // everything is cleaned up by this point - // catch everything so the exception doesn't spill into the calling PDO code - catch( core::CoreException& ) { - - if( driver_stmt ) { - - driver_stmt->~pdo_sqlsrv_stmt(); - } - - // in the event that the statement caused an error that was copied to the connection, update the - // connection with the error's SQLSTATE. - if( driver_dbh->last_error() ) { - - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_dbh->last_error()->sqlstate )); - } - - return 0; - } - - // catch any errant exception and die - catch(...) { - - DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." ); - } - - return 1; -} - - -// pdo_sqlsrv_dbh_do -// Maps to PDO::exec. -// Execute a SQL statement, such as an insert, update or delete, and return -// the number of rows affected. -// Parameters: -// dbh - the PDO connection object, which contains the ODBC handle -// sql - the query to execute -// sql_len - length of sql query -// Return -// # of rows affected, -1 for an error. -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_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - - sqlsrv_malloc_auto_ptr driver_stmt; - SQLLEN rows = 0; - - // verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong - // thing to happen here. - SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); - - try { - - SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); - - // temp PDO statement used for error handling if something happens - pdo_stmt_t temp_stmt; - temp_stmt.dbh = dbh; - // allocate a full driver statement to take advantage of the error handling - driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, - NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); - driver_stmt->set_func( __FUNCTION__ ); - - SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( 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 - // 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 )) { - - SQLRETURN r = SQL_SUCCESS; - - do { - - rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); - - r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); - - } while ( r != SQL_NO_DATA ); - } - - // returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases - // naturally, so we override that here with no rows returned. - if( rows == -1 ) { - rows = 0; - } - } - catch( core::CoreException& ) { - - // copy any errors on the statement to the connection so that the user sees them, since the statement is released - // before this method returns - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_stmt->last_error()->sqlstate )); - driver_dbh->set_last_error( driver_stmt->last_error() ); - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - return -1; - } - catch( ... ) { - - DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." ); - } - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - return rows; -} - - -// transaction support functions - -// pdo_sqlsrv_dbh_begin -// Maps to PDO::beginTransaction. -// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn -// flag is maintained by PDO so we dont have to worry about it. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" ); - - core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); - } -} - - - -// pdo_sqlsrv_dbh_commit -// Maps to PDO::commit. -// Commits a transaction. Returns the connection to auto-commit mode. -// PDO throws error if PDO::commit is called on a connection that is not in an active -// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have -// to worry about it here. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" ); - - core_sqlsrv_commit( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); - } -} - -// pdo_sqlsrv_dbh_rollback -// Maps to PDO::rollback. -// Rolls back a transaction. Returns the connection in auto-commit mode. -// PDO throws error if PDO::rollBack is called on a connection that is not in an active -// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have -// to worry about it here. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" ); - - core_sqlsrv_rollback( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); - } -} - -// pdo_sqlsrv_dbh_set_attr -// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. -// PDO driver manager calls this function directly after calling the factory -// method for PDO, for any attribute which is specified in the PDO constructor. -// Parameters: -// dbh - The PDO connection object maintained by PDO. -// attr - The attribute to be set. -// val - The value of the attribute to be set. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_set_attr: driver_data object was NULL."); - - try { - - switch( attr ) { - - case SQLSRV_ATTR_ENCODING: - { - zend_long attr_value; - if( Z_TYPE_P( val ) != IS_LONG ) { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); - } - attr_value = Z_LVAL_P( val ); - switch( attr_value ) { - - case SQLSRV_ENCODING_DEFAULT: - // when default is applied to a connection, that means use UTF-8 encoding - driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 ); - break; - case SQLSRV_ENCODING_SYSTEM: - case SQLSRV_ENCODING_UTF8: - driver_dbh->set_encoding( static_cast( attr_value )); - break; - default: - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); - break; - } - } - break; - - case SQLSRV_ATTR_DIRECT_QUERY: - driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false; - break; - - case SQLSRV_ATTR_QUERY_TIMEOUT: - if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { - convert_to_string( val ); - THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val )); - } - driver_dbh->query_timeout = static_cast( Z_LVAL_P( val ) ); - break; - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { - convert_to_string( val ); - THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val )); - } - driver_dbh->client_buffer_max_size = Z_LVAL_P( val ); - break; - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false; - break; - - // Not supported - case PDO_ATTR_FETCH_TABLE_NAMES: - case PDO_ATTR_FETCH_CATALOG_NAMES: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_MAX_COLUMN_LEN: - case PDO_ATTR_CURSOR_NAME: - case PDO_ATTR_AUTOCOMMIT: - case PDO_ATTR_PERSISTENT: - case PDO_ATTR_TIMEOUT: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); - } - - // Read-only - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_SERVER_INFO: - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_DRIVER_NAME: - case PDO_ATTR_CONNECTION_STATUS: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); - } - - // Statement level only - case PDO_ATTR_EMULATE_PREPARES: - case PDO_ATTR_CURSOR: - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); - } - - default: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); - break; - } - } - } - catch( pdo::PDOException& ) { - - return 0; - } - - return 1; -} - - -// pdo_sqlsrv_dbh_get_attr -// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object. -// Parameters: -// dbh - The PDO connection object maintained by PDO. -// attr - The attribute to get. -// return_value - zval in which to return the attribute value. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_get_attr: driver_data object was NULL."); - - try { - - switch( attr ) { - - // Not supported - case PDO_ATTR_FETCH_TABLE_NAMES: - case PDO_ATTR_FETCH_CATALOG_NAMES: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_MAX_COLUMN_LEN: - case PDO_ATTR_CURSOR_NAME: - case PDO_ATTR_AUTOCOMMIT: - case PDO_ATTR_TIMEOUT: - { - // PDO does not throw "not supported" error message for these attributes. - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); - } - - // Statement level only - case PDO_ATTR_EMULATE_PREPARES: - case PDO_ATTR_CURSOR: - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); - } - - case PDO_ATTR_STRINGIFY_FETCHES: - { - // For this attribute, if we dont set the return_value than PDO returns NULL. - ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) ); - break; - } - - case PDO_ATTR_SERVER_INFO: - { - core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC ); - break; - } - - case PDO_ATTR_SERVER_VERSION: - { - core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC ); - break; - } - - case PDO_ATTR_CLIENT_VERSION: - { - core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC ); - - //Add the PDO SQLSRV driver's file version - //Declarations below eliminate compiler warnings about string constant to char* conversions - const char* extver = "ExtensionVer"; - std::string filever = VER_FILEVERSION_STR; - core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/ - TSRMLS_CC ); - break; - } - - case SQLSRV_ATTR_ENCODING: - { - ZVAL_LONG( return_value, driver_dbh->encoding() ); - break; - } - - case SQLSRV_ATTR_QUERY_TIMEOUT: - { - ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); - break; - } - - case SQLSRV_ATTR_DIRECT_QUERY: - { - ZVAL_BOOL( return_value, driver_dbh->direct_query ); - break; - } - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - { - ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); - break; - } - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - { - ZVAL_BOOL( return_value, driver_dbh->fetch_numeric ); - break; - } - - default: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); - break; - } - } - - return 1; - } - catch( core::CoreException& ) { - return 0; - } -} - -// Called by PDO::errorInfo and PDOStatement::errorInfo. -// Returns the error info. -// Parameters: -// dbh - The PDO managed connection object. -// stmt - The PDO managed statement object. -// info - zval in which to return the error info. -// Return: -// 0 for failure, 1 for success. -int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, - _Out_ zval *info TSRMLS_DC) -{ - SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." ); - - sqlsrv_error* ctx_error = NULL; - if( stmt ) { - ctx_error = static_cast( stmt->driver_data )->last_error(); - } - else { - ctx_error = static_cast( dbh->driver_data )->last_error(); - } - - pdo_sqlsrv_retrieve_context_error( ctx_error, info ); - - return 1; -} - -// pdo_sqlsrv_dbh_last_id -// Maps to PDO::lastInsertId. -// Returns the last id generated by an executed SQL statement -// Parameters: -// dbh - The PDO managed connection object. -// name - Table name. -// len - Length of the name. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - // turn off any error handling for last_id - pdo_error_mode prev_err_mode = dbh->error_mode; - dbh->error_mode = PDO_ERRMODE_SILENT; - - sqlsrv_malloc_auto_ptr driver_stmt; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." ); - - sqlsrv_malloc_auto_ptr id_str; - id_str = reinterpret_cast( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN )); - - try { - - char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ]; - if( name == NULL ) { - strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY ); - } - else { - char* quoted_table = NULL; - size_t quoted_len = 0; - int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), "ed_table, "ed_len, PDO_PARAM_NULL TSRMLS_CC ); - SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name."); - snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, TABLE_LAST_INSERT_ID_QUERY, quoted_table ); - sqlsrv_free( quoted_table ); - } - - // temp PDO statement used for error handling if something happens - pdo_stmt_t temp_stmt; - temp_stmt.dbh = dbh; - - // allocate a full driver statement to take advantage of the error handling - driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); - driver_stmt->set_func( __FUNCTION__ ); - - - sqlsrv_malloc_auto_ptr wsql_string; - unsigned int wsql_len; - wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast( 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() ) { - throw core::CoreException(); - } - - // execute the last insert id query - core::SQLExecDirectW( driver_stmt, wsql_string 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, - reinterpret_cast( len ), false TSRMLS_CC ); - - CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt, - PDO_SQLSRV_ERROR_LAST_INSERT_ID ) { - throw core::CoreException(); - } - - driver_stmt->~sqlsrv_stmt(); - } - catch( core::CoreException& ) { - - // copy any errors on the statement to the connection so that the user sees them, since the statement is released - // before this method returns - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_stmt->last_error()->sqlstate )); - driver_dbh->set_last_error( driver_stmt->last_error() ); - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - strcpy_s( id_str.get(), 1, "" ); - *len = 0; - } - - char* ret_id_str = id_str.get(); - id_str.transferred(); - - // restore error handling to its previous mode - dbh->error_mode = prev_err_mode; - - return ret_id_str; -} - -// pdo_sqlsrv_dbh_quote -// Maps to PDO::quote. As the name says, this function quotes a string. -// Always returns a valid string unless memory allocation fails. -// Parameters: -// dbh - The PDO managed connection object. -// unquoted - The unquoted string to be quoted. -// unquoted_len - Length of the unquoted string. -// quoted - Buffer for output string. -// quoted_len - Length of the output string. -// Return: -// 0 for failure, 1 for success. -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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR; - - // get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from: - // 1. PDO::quote() - object name is PDO - // 2. PDOStatement::execute() - object name is PDOStatement - zend_execute_data* execute_data = EG( current_execute_data ); - zval *object = getThis(); - - // iterate through parents to find "PDOStatement" - bool is_statement = false; - if ( object ) { - zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce; - while ( curr_class != NULL ) { - if ( strcmp( reinterpret_cast( curr_class->name->val ), "PDOStatement" ) == 0 ) { - is_statement = true; - break; - } - curr_class = curr_class->parent; - } - } - // only change the encoding if quote is called from the statement level (which should only be called when a statement - // is prepared with emulate prepared on) - if ( is_statement ) { - 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 - pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast( stmt->driver_data ); - if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) { - encoding = driver_stmt->encoding(); - } - else { - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( stmt->driver_data ); - encoding = driver_dbh->encoding(); - } - // get the placeholder at the current position in driver_stmt->placeholders ht - 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 ) { - pdo_bound_param_data* param = NULL; - if ( Z_TYPE_P( placeholder ) == IS_STRING ) { - param = reinterpret_cast( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder ))); - } - else if ( Z_TYPE_P( placeholder ) == IS_LONG) { - param = reinterpret_cast( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder ))); - } - if ( NULL != param ) { - SQLSRV_ENCODING param_encoding = static_cast( Z_LVAL( param->driver_params )); - if ( param_encoding != SQLSRV_ENCODING_INVALID ) { - encoding = param_encoding; - } - } - } - } - - if ( encoding == SQLSRV_ENCODING_BINARY ) { - // convert from char* to hex digits using os - std::basic_ostringstream os; - 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. - // return an empty terminated string for now - if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) { - *quoted_len = 0; - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); - ( *quoted )[ 0 ] = '\0'; - return 1; - } - // 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) - // thus append '0' first - if (( int )unquoted[index] < 16 ) { - os << '0'; - } - os << std::hex << ( int )unquoted[ index ]; - } - std::basic_string str_hex = os.str(); - // 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 - char* unquoted_str = reinterpret_cast( 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() ); - // include length of '0x' in the binary string - *quoted_len = unquoted_str_len + 2; - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); - unsigned int out_current = 0; - // insert '0x' - ( *quoted )[ out_current++ ] = '0'; - ( *quoted )[ out_current++ ] = 'x'; - for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) { - ( *quoted )[ out_current++ ] = unquoted_str[ index ]; - } - // null terminator - ( *quoted )[ out_current ] = '\0'; - sqlsrv_free( unquoted_str ); - return 1; - } - else { - // count the number of quotes needed - unsigned int quotes_needed = 2; // the initial start and end quotes of course - // include the N proceeding the initial quote if encoding is UTF8 - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - quotes_needed = 3; - } - for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { - if ( unquoted[ index ] == '\'' ) { - ++quotes_needed; - } - } - - *quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator. - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator. - unsigned int out_current = 0; - - // insert N if the encoding is UTF8 - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - ( *quoted )[ out_current++ ] = 'N'; - } - // insert initial quote - ( *quoted )[ out_current++ ] = '\''; - - for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { - if ( unquoted[ index ] == '\'' ) { - ( *quoted )[ out_current++ ] = '\''; - ( *quoted )[ out_current++ ] = '\''; - } - else { - ( *quoted )[ out_current++ ] = unquoted[ index ]; - } - } - - // trailing quote and null terminator - ( *quoted )[ out_current++ ] = '\''; - ( *quoted )[ out_current ] = '\0'; - - return 1; - } -} - -// 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_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - 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 ) { - return NULL; - } - - return NULL; // to avoid a compiler warning -} - -namespace { - -// Maps the PDO driver specific statement option/attribute constants to the core layer -// statement option/attribute constants. -void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, - _Inout_ zval* data TSRMLS_DC ) -{ - zend_ulong option_key = -1; - switch( key ) { - - case PDO_ATTR_CURSOR: - option_key = SQLSRV_STMT_OPTION_SCROLLABLE; - break; - - case SQLSRV_ATTR_ENCODING: - option_key = PDO_STMT_OPTION_ENCODING; - break; - - case SQLSRV_ATTR_QUERY_TIMEOUT: - option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT; - break; - - case PDO_ATTR_STATEMENT_CLASS: - break; - - case SQLSRV_ATTR_DIRECT_QUERY: - option_key = PDO_STMT_OPTION_DIRECT_QUERY; - break; - - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE; - break; - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE; - break; - - case PDO_ATTR_EMULATE_PREPARES: - option_key = PDO_STMT_OPTION_EMULATE_PREPARES; - break; - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE; - break; - - default: - CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { - throw core::CoreException(); - } - break; - } - - // if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it - if( option_key != -1 ) { - zval_add_ref( data ); - core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC ); - } -} - - -// validate_stmt_options -// Iterates through the list of statement options provided by the user and validates them -// against the list of statement options provided by this driver. After validation -// creates a Hashtable of statement options to be sent to the core layer for processing. -// Parameters: -// ctx - The current context. -// stmt_options - The user provided list of statement options. -// pdo_stmt_options_ht - Output hashtable of statement options. -void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ) -{ - try { - - if( stmt_options ) { - - HashTable* options_ht = Z_ARRVAL_P( stmt_options ); - size_t int_key = -1; - zend_string *key = NULL; - zval* data = NULL; - - ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) { - int type = HASH_KEY_NON_EXISTENT; - int result = 0; - type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { - throw core::CoreException(); - } - - add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); - } ZEND_HASH_FOREACH_END(); - } - } - catch( core::CoreException& ) { - - 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 ) -{ - TSRMLS_C; - char const* val_str = "no"; - - if( core_str_zval_is_true( value ) ) { - - val_str = "yes"; - } - - conn_str += option->odbc_name; - conn_str += "={"; - conn_str += val_str; - conn_str += "};"; -} - -void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, - std::string& /*conn_str*/ TSRMLS_DC ) -{ - try { - - SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); - const char* val = Z_STRVAL_P( value_z ); - size_t val_len = Z_STRLEN_P( value_z ); - zend_long out_val = SQL_TXN_READ_COMMITTED; - - // READ_COMMITTED - if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { - - out_val = SQL_TXN_READ_COMMITTED; - } - - // READ_UNCOMMITTED - else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { - - out_val = SQL_TXN_READ_UNCOMMITTED; - } - - // REPEATABLE_READ - else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { - - out_val = SQL_TXN_REPEATABLE_READ; - } - - // SERIALIZABLE - else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { - - out_val = SQL_TXN_SERIALIZABLE; - } - - // SNAPSHOT - else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { - - out_val = SQL_TXN_SS_SNAPSHOT; - } - - else { - - CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { - - throw core::CoreException(); - } - } - - core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast( out_val ), SQL_IS_UINTEGER TSRMLS_CC ); - - } - catch( core::CoreException& ) { - - throw; - } -} - -} // namespace +//--------------------------------------------------------------------------------------------------------------------------------- +// file: pdo_dbh.cpp +// +// Contents: Implements the PDO object for PDO_SQLSRV +// +// Microsoft Drivers 4.3 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + +#include "php_pdo_sqlsrv.h" + +#include +#include + +typedef const zend_function_entry pdo_sqlsrv_function_entry; + +// *** internal variables and constants *** + +namespace { + +const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; +const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer +const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s"; +const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes + +// List of PDO supported connection options. +namespace PDOConnOptionNames { + +const char Server[] = "Server"; +const char APP[] = "APP"; +const char ApplicationIntent[] = "ApplicationIntent"; +const char AttachDBFileName[] = "AttachDbFileName"; +const char ConnectionPooling[] = "ConnectionPooling"; +const char Authentication[] = "Authentication"; +#ifdef _WIN32 +const char ConnectRetryCount[] = "ConnectRetryCount"; +const char ConnectRetryInterval[] = "ConnectRetryInterval"; +#endif // _WIN32 +const char Database[] = "Database"; +const char Encrypt[] = "Encrypt"; +const char Failover_Partner[] = "Failover_Partner"; +const char LoginTimeout[] = "LoginTimeout"; +const char MARS_Option[] = "MultipleActiveResultSets"; +const char MultiSubnetFailover[] = "MultiSubnetFailover"; +const char QuotedId[] = "QuotedId"; +const char TraceFile[] = "TraceFile"; +const char TraceOn[] = "TraceOn"; +const char TrustServerCertificate[] = "TrustServerCertificate"; +const char TransactionIsolation[] = "TransactionIsolation"; +const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution"; +const char WSID[] = "WSID"; + +} + +enum PDO_CONN_OPTIONS { + + PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, + +}; + +enum PDO_STMT_OPTIONS { + + PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC, + PDO_STMT_OPTION_DIRECT_QUERY, + PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, + PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, + PDO_STMT_OPTION_EMULATE_PREPARES, + PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, +}; + +// List of all the statement options supported by this driver. +const stmt_option PDO_STMT_OPTS[] = { + + { NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr( new stmt_option_query_timeout ) }, + { NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr( new stmt_option_pdo_scrollable ) }, + { NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr( new stmt_option_encoding ) }, + { NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, std::unique_ptr( new stmt_option_direct_query ) }, + { NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, std::unique_ptr( new stmt_option_cursor_scroll_type ) }, + { NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr( new stmt_option_buffered_query_limit ) }, + { NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr( new stmt_option_emulate_prepares ) }, + { NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr( new stmt_option_fetch_numeric ) }, + + { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr{} }, +}; + +// boolean connection string +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 ); +}; + +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 ); +}; + +#ifdef _WIN32 +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 ) + { + TSRMLS_C; + SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" ) + + std::string val_str = Z_STRVAL_P( value ); + + conn_str += option->odbc_name; + conn_str += "={"; + conn_str += val_str; + conn_str += "};"; + } +}; +#endif // _WIN32 + +template +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 ) + { + try { + + SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); + + size_t val = static_cast( atoi( Z_STRVAL_P( value )) ); + core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( val ), SQL_IS_UINTEGER TSRMLS_CC ); + } + catch( core::CoreException& ) { + throw; + } + } +}; + +template +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 ) + { + try { + + core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( core_str_zval_is_true( value )), + SQL_IS_UINTEGER TSRMLS_CC ); + } + catch( core::CoreException& ) { + throw; + } + } +}; + +// statement options related functions +void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, + _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 ); + +} // namespace + + +// List of all connection options supported by this driver. +const connection_option PDO_CONN_OPTS[] = { + { + PDOConnOptionNames::Server, + sizeof( PDOConnOptionNames::Server ), + PDO_CONN_OPTION_SERVER, + NULL, + 0, + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::APP, + sizeof( PDOConnOptionNames::APP ), + SQLSRV_CONN_OPTION_APP, + ODBCConnOptions::APP, + sizeof( ODBCConnOptions::APP ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::ApplicationIntent, + sizeof( PDOConnOptionNames::ApplicationIntent ), + SQLSRV_CONN_OPTION_APPLICATION_INTENT, + ODBCConnOptions::ApplicationIntent, + sizeof( ODBCConnOptions::ApplicationIntent ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::AttachDBFileName, + sizeof( PDOConnOptionNames::AttachDBFileName ), + SQLSRV_CONN_OPTION_ATTACHDBFILENAME, + ODBCConnOptions::AttachDBFileName, + sizeof( ODBCConnOptions::AttachDBFileName ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::Authentication, + sizeof( PDOConnOptionNames::Authentication ), + SQLSRV_CONN_OPTION_AUTHENTICATION, + ODBCConnOptions::Authentication, + sizeof( ODBCConnOptions::Authentication ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::ConnectionPooling, + sizeof( PDOConnOptionNames::ConnectionPooling ), + SQLSRV_CONN_OPTION_CONN_POOLING, + ODBCConnOptions::ConnectionPooling, + sizeof( ODBCConnOptions::ConnectionPooling ), + CONN_ATTR_BOOL, + conn_null_func::func + }, +#ifdef _WIN32 + { + PDOConnOptionNames::ConnectRetryCount, + sizeof( PDOConnOptionNames::ConnectRetryCount ), + SQLSRV_CONN_OPTION_CONN_RETRY_COUNT, + ODBCConnOptions::ConnectRetryCount, + sizeof( ODBCConnOptions::ConnectRetryCount ), + CONN_ATTR_INT, + pdo_int_conn_str_func::func + }, + { + PDOConnOptionNames::ConnectRetryInterval, + sizeof( PDOConnOptionNames::ConnectRetryInterval ), + SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL, + ODBCConnOptions::ConnectRetryInterval, + sizeof( ODBCConnOptions::ConnectRetryInterval ), + CONN_ATTR_INT, + pdo_int_conn_str_func::func + }, +#endif // _WIN32 + { + PDOConnOptionNames::Database, + sizeof( PDOConnOptionNames::Database ), + SQLSRV_CONN_OPTION_DATABASE, + ODBCConnOptions::Database, + sizeof( ODBCConnOptions::Database ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::Encrypt, + sizeof( PDOConnOptionNames::Encrypt ), + SQLSRV_CONN_OPTION_ENCRYPT, + ODBCConnOptions::Encrypt, + sizeof( ODBCConnOptions::Encrypt ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::Failover_Partner, + sizeof( PDOConnOptionNames::Failover_Partner ), + SQLSRV_CONN_OPTION_FAILOVER_PARTNER, + ODBCConnOptions::Failover_Partner, + sizeof( ODBCConnOptions::Failover_Partner ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::LoginTimeout, + sizeof( PDOConnOptionNames::LoginTimeout ), + SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, + ODBCConnOptions::LoginTimeout, + sizeof( ODBCConnOptions::LoginTimeout ), + CONN_ATTR_INT, + pdo_int_conn_attr_func::func + }, + { + PDOConnOptionNames::MARS_Option, + sizeof( PDOConnOptionNames::MARS_Option ), + SQLSRV_CONN_OPTION_MARS, + ODBCConnOptions::MARS_ODBC, + sizeof( ODBCConnOptions::MARS_ODBC ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::MultiSubnetFailover, + sizeof( PDOConnOptionNames::MultiSubnetFailover ), + SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, + ODBCConnOptions::MultiSubnetFailover, + sizeof( ODBCConnOptions::MultiSubnetFailover ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::QuotedId, + sizeof( PDOConnOptionNames::QuotedId ), + SQLSRV_CONN_OPTION_QUOTED_ID, + ODBCConnOptions::QuotedId, + sizeof( ODBCConnOptions::QuotedId ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::TraceFile, + sizeof( PDOConnOptionNames::TraceFile ), + SQLSRV_CONN_OPTION_TRACE_FILE, + ODBCConnOptions::TraceFile, + sizeof( ODBCConnOptions::TraceFile ), + CONN_ATTR_STRING, + str_conn_attr_func::func + }, + { + PDOConnOptionNames::TraceOn, + sizeof( PDOConnOptionNames::TraceOn ), + SQLSRV_CONN_OPTION_TRACE_ON, + ODBCConnOptions::TraceOn, + sizeof( ODBCConnOptions::TraceOn ), + CONN_ATTR_BOOL, + pdo_bool_conn_attr_func::func + }, + { + PDOConnOptionNames::TransactionIsolation, + sizeof( PDOConnOptionNames::TransactionIsolation ), + SQLSRV_CONN_OPTION_TRANS_ISOLATION, + ODBCConnOptions::TransactionIsolation, + sizeof( ODBCConnOptions::TransactionIsolation ), + CONN_ATTR_INT, + pdo_txn_isolation_conn_attr_func::func + }, + { + PDOConnOptionNames::TrustServerCertificate, + sizeof( PDOConnOptionNames::TrustServerCertificate ), + SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, + ODBCConnOptions::TrustServerCertificate, + sizeof( ODBCConnOptions::TrustServerCertificate ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::TransparentNetworkIPResolution, + sizeof(PDOConnOptionNames::TransparentNetworkIPResolution), + SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION, + ODBCConnOptions::TransparentNetworkIPResolution, + sizeof(ODBCConnOptions::TransparentNetworkIPResolution), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::WSID, + sizeof( PDOConnOptionNames::WSID ), + SQLSRV_CONN_OPTION_WSID, + ODBCConnOptions::WSID, + sizeof( ODBCConnOptions::WSID ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table +}; + + +// close the connection +int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); + +// execute queries +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 ); +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 +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_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); + +// 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_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ); + +// return more information +int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, + _Out_ zval *info TSRMLS_DC); + +// 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 ); + +// 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 ); + +// quote a string, meaning put quotes around it and escape any quotes within it +int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen, + enum pdo_param_type paramtype TSRMLS_DC ); + +struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = { + + pdo_sqlsrv_dbh_close, + pdo_sqlsrv_dbh_prepare, + pdo_sqlsrv_dbh_do, + pdo_sqlsrv_dbh_quote, + pdo_sqlsrv_dbh_begin, + pdo_sqlsrv_dbh_commit, + pdo_sqlsrv_dbh_rollback, + pdo_sqlsrv_dbh_set_attr, + pdo_sqlsrv_dbh_last_id, + pdo_sqlsrv_dbh_return_error, + pdo_sqlsrv_dbh_get_attr, + NULL, // check liveness not implemented + pdo_sqlsrv_get_driver_methods, + NULL, // request shutdown not implemented + NULL // in transaction not implemented +}; + + +// log a function entry point +#ifndef _WIN32 +#define PDO_LOG_DBH_ENTRY \ +{ \ + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ + driver_dbh->set_func( __FUNCTION__ ); \ + int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \ + char func[length+1]; \ + strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \ + strcat_s( func, length+1, ": entering" ); \ + LOG( SEV_NOTICE, func ); \ +} +#else +#define PDO_LOG_DBH_ENTRY \ +{ \ + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ + driver_dbh->set_func( __FUNCTION__ ); \ + LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \ +} +#endif + +// 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 ) : + sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ), + stmts( NULL ), + direct_query( false ), + query_timeout( QUERY_TIMEOUT_INVALID ), + client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )), + fetch_numeric( false ) +{ + if( client_buffer_max_size < 0 ) { + client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT; + LOG( SEV_WARNING, INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value." ); + } +} + +// pdo_sqlsrv_db_handle_factory +// Maps to PDO::__construct. +// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. +// Does the following things: +// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. +// (If an error occurs in this function, the PDO specification mandates that +// an exception be thrown, regardless of the error mode setting.) +// 2. Processes the driver options. +// 3. Creates a core_conn object by calling core_sqlsrv_connect. +// 4. Restores the previous error mode on success. +// alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory +// Parameters: +// dbh - The PDO managed structure for the connection. +// driver_options - A HashTable (within the zval) of options to use when creating the connection. +// Return: +// 0 for failure, 1 for success. +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" ); + + hash_auto_ptr pdo_conn_options_ht; + pdo_error_mode prev_err_mode = dbh->error_mode; + + // must be done in all cases so that even a failed connection can query the + // object for errors. + dbh->methods = &pdo_sqlsrv_dbh_methods; + dbh->driver_data = NULL; + zval* temp_server_z = NULL; + sqlsrv_malloc_auto_ptr dsn_parser; + zval server_z; + ZVAL_UNDEF( &server_z ); + + try { + + // no matter what the error mode, we want exceptions thrown if the connection fails + // to happen (per the PDO spec) + dbh->error_mode = PDO_ERRMODE_EXCEPTION; + + g_pdo_henv_cp->set_driver( dbh ); + g_pdo_henv_ncp->set_driver( dbh ); + + CHECK_CUSTOM_ERROR( driver_options && Z_TYPE_P( driver_options ) != IS_ARRAY, *g_pdo_henv_cp, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) { + throw core::CoreException(); + } + // throws PDOException if the ATTR_PERSISTENT is in connection options + CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) { + dbh->refcount--; + throw pdo::PDOException(); + } + + // Initialize the options array to be passed to the core layer + ALLOC_HASHTABLE( pdo_conn_options_ht ); + + core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, + 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. + dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, + static_cast( dbh->data_source_len ), pdo_conn_options_ht ); + dsn_parser->parse_conn_string( TSRMLS_C ); + + // Extract the server name + temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); + + CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) { + + throw pdo::PDOException(); + } + + server_z = *temp_server_z; + + // Add a reference to the option value since we are deleting it from the hashtable + zval_add_ref( &server_z ); + zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); + + sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn, Z_STRVAL( server_z ), + dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, + PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC ); + + // Free the string in server_z after being used + zend_string_release( Z_STR( server_z )); + + SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); + + // set the driver_data and methods to complete creation of the PDO object + dbh->driver_data = conn; + dbh->error_mode = prev_err_mode; // reset the error mode + dbh->alloc_own_columns = 1; // we do our own memory management for columns + dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types + + } + catch( core::CoreException& ) { + + if ( Z_TYPE( server_z ) == IS_STRING ) { + zend_string_release( Z_STR( server_z )); + } + dbh->error_mode = prev_err_mode; // reset the error mode + g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL + + return 0; + } + catch( ... ) { + + DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" ); + } + + return 1; +} + +// pdo_sqlsrv_dbh_close +// Maps to PDO::__destruct. +// Called when a PDO object is to be destroyed. +// By the time this function is called, PDO has already made sure that +// all statements are disposed and the PDO object is the last item destroyed. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// Always returns 1 for success. +int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" ); + + // if the connection didn't complete properly, driver_data isn't initialized. + if( dbh->driver_data == NULL ) { + + return 1; + } + + PDO_RESET_DBH_ERROR; + + // call the core layer close + core_sqlsrv_close( reinterpret_cast( dbh->driver_data ) TSRMLS_CC ); + dbh->driver_data = NULL; + + // always return success that the connection is closed + return 1; +} + +// pdo_sqlsrv_dbh_prepare +// Called by PDO::prepare and PDOStatement::__construct. +// Creates a statement and prepares it for execution by PDO +// Paramters: +// dbh - The PDO managed connection object. +// sql - SQL query to be prepared. +// sql_len - Length of the sql query +// stmt - The PDO managed statement object. +// driver_options - User provided list of statement options. +// Return: +// 0 for failure, 1 for success. +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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + hash_auto_ptr pdo_stmt_options_ht; + sqlsrv_malloc_auto_ptr sql_rewrite; + size_t sql_rewrite_len = 0; + sqlsrv_malloc_auto_ptr driver_stmt; + hash_auto_ptr placeholders; + sqlsrv_malloc_auto_ptr sql_parser; + + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); + SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null"); + + try { + + // assign the methods for the statement object. This is necessary even if the + // statement fails so the user can retrieve the error information. + stmt->methods = &pdo_sqlsrv_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names + + // Initialize the options array to be passed to the core layer + ALLOC_HASHTABLE( pdo_stmt_options_ht ); + core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, + 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. + validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC ); + + driver_stmt = static_cast( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, + pdo_stmt_options_ht, PDO_STMT_OPTS, + pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC )); + + // if the user didn't set anything in the prepare options, then set the buffer limit + // to the value set on the connection. + if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { + + driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size; + } + + // if the user didn't set anything in the prepare options, then set the query timeout + // to the value set on the connection. + 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 ); + } + + // rewrite named parameters in the query to positional parameters if we aren't letting PDO do the + // parameter substitution for us + if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { + + // rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named + // parameters for consistency with the PDO MySQL and PDO ODBC drivers. + int zr = pdo_parse_params( stmt, const_cast( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC ); + + CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) { + throw core::CoreException(); + } + // if parameter substitution happened, use that query instead of the original + if( sql_rewrite != 0) { + sql = sql_rewrite; + sql_len = sql_rewrite_len; + } + } + + if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { + + core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC ); + } + else if( driver_stmt->direct_query ) { + + if( driver_stmt->direct_query_subst_string ) { + // we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme + // and we use estrdup below to allocate the new string, which uses emalloc + efree( reinterpret_cast( const_cast( driver_stmt->direct_query_subst_string ))); + } + driver_stmt->direct_query_subst_string = estrdup( sql ); + 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 + // set to the substituted query + if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) { + // parse placeholders in the sql query into the placeholders ht + ALLOC_HASHTABLE( placeholders ); + 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, + static_cast(stmt->query_stringlen), placeholders ); + sql_parser->parse_sql_string( TSRMLS_C ); + driver_stmt->placeholders = placeholders; + placeholders.transferred(); + } + + stmt->driver_data = driver_stmt; + driver_stmt.transferred(); + } + // everything is cleaned up by this point + // catch everything so the exception doesn't spill into the calling PDO code + catch( core::CoreException& ) { + + if( driver_stmt ) { + + driver_stmt->~pdo_sqlsrv_stmt(); + } + + // in the event that the statement caused an error that was copied to the connection, update the + // connection with the error's SQLSTATE. + if( driver_dbh->last_error() ) { + + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_dbh->last_error()->sqlstate )); + } + + return 0; + } + + // catch any errant exception and die + catch(...) { + + DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." ); + } + + return 1; +} + + +// pdo_sqlsrv_dbh_do +// Maps to PDO::exec. +// Execute a SQL statement, such as an insert, update or delete, and return +// the number of rows affected. +// Parameters: +// dbh - the PDO connection object, which contains the ODBC handle +// sql - the query to execute +// sql_len - length of sql query +// Return +// # of rows affected, -1 for an error. +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_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + + sqlsrv_malloc_auto_ptr driver_stmt; + SQLLEN rows = 0; + + // verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong + // thing to happen here. + SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); + + try { + + SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); + + // temp PDO statement used for error handling if something happens + pdo_stmt_t temp_stmt; + temp_stmt.dbh = dbh; + // allocate a full driver statement to take advantage of the error handling + driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, + NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); + driver_stmt->set_func( __FUNCTION__ ); + + SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( 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 + // 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 )) { + + SQLRETURN r = SQL_SUCCESS; + + do { + + rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); + + r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); + + } while ( r != SQL_NO_DATA ); + } + + // returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases + // naturally, so we override that here with no rows returned. + if( rows == -1 ) { + rows = 0; + } + } + catch( core::CoreException& ) { + + // copy any errors on the statement to the connection so that the user sees them, since the statement is released + // before this method returns + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_stmt->last_error()->sqlstate )); + driver_dbh->set_last_error( driver_stmt->last_error() ); + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + return -1; + } + catch( ... ) { + + DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." ); + } + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + return rows; +} + + +// transaction support functions + +// pdo_sqlsrv_dbh_begin +// Maps to PDO::beginTransaction. +// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn +// flag is maintained by PDO so we dont have to worry about it. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" ); + + core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); + } +} + + + +// pdo_sqlsrv_dbh_commit +// Maps to PDO::commit. +// Commits a transaction. Returns the connection to auto-commit mode. +// PDO throws error if PDO::commit is called on a connection that is not in an active +// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have +// to worry about it here. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" ); + + core_sqlsrv_commit( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); + } +} + +// pdo_sqlsrv_dbh_rollback +// Maps to PDO::rollback. +// Rolls back a transaction. Returns the connection in auto-commit mode. +// PDO throws error if PDO::rollBack is called on a connection that is not in an active +// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have +// to worry about it here. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" ); + + core_sqlsrv_rollback( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); + } +} + +// pdo_sqlsrv_dbh_set_attr +// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. +// PDO driver manager calls this function directly after calling the factory +// method for PDO, for any attribute which is specified in the PDO constructor. +// Parameters: +// dbh - The PDO connection object maintained by PDO. +// attr - The attribute to be set. +// val - The value of the attribute to be set. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_set_attr: driver_data object was NULL."); + + try { + + switch( attr ) { + + case SQLSRV_ATTR_ENCODING: + { + zend_long attr_value; + if( Z_TYPE_P( val ) != IS_LONG ) { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); + } + attr_value = Z_LVAL_P( val ); + switch( attr_value ) { + + case SQLSRV_ENCODING_DEFAULT: + // when default is applied to a connection, that means use UTF-8 encoding + driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 ); + break; + case SQLSRV_ENCODING_SYSTEM: + case SQLSRV_ENCODING_UTF8: + driver_dbh->set_encoding( static_cast( attr_value )); + break; + default: + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); + break; + } + } + break; + + case SQLSRV_ATTR_DIRECT_QUERY: + driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false; + break; + + case SQLSRV_ATTR_QUERY_TIMEOUT: + if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { + convert_to_string( val ); + THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val )); + } + driver_dbh->query_timeout = static_cast( Z_LVAL_P( val ) ); + break; + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { + convert_to_string( val ); + THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val )); + } + driver_dbh->client_buffer_max_size = Z_LVAL_P( val ); + break; + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false; + break; + + // Not supported + case PDO_ATTR_FETCH_TABLE_NAMES: + case PDO_ATTR_FETCH_CATALOG_NAMES: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_MAX_COLUMN_LEN: + case PDO_ATTR_CURSOR_NAME: + case PDO_ATTR_AUTOCOMMIT: + case PDO_ATTR_PERSISTENT: + case PDO_ATTR_TIMEOUT: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); + } + + // Read-only + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_DRIVER_NAME: + case PDO_ATTR_CONNECTION_STATUS: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); + } + + // Statement level only + case PDO_ATTR_EMULATE_PREPARES: + case PDO_ATTR_CURSOR: + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); + } + + default: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); + break; + } + } + } + catch( pdo::PDOException& ) { + + return 0; + } + + return 1; +} + + +// pdo_sqlsrv_dbh_get_attr +// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object. +// Parameters: +// dbh - The PDO connection object maintained by PDO. +// attr - The attribute to get. +// return_value - zval in which to return the attribute value. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_get_attr: driver_data object was NULL."); + + try { + + switch( attr ) { + + // Not supported + case PDO_ATTR_FETCH_TABLE_NAMES: + case PDO_ATTR_FETCH_CATALOG_NAMES: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_MAX_COLUMN_LEN: + case PDO_ATTR_CURSOR_NAME: + case PDO_ATTR_AUTOCOMMIT: + case PDO_ATTR_TIMEOUT: + { + // PDO does not throw "not supported" error message for these attributes. + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); + } + + // Statement level only + case PDO_ATTR_EMULATE_PREPARES: + case PDO_ATTR_CURSOR: + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); + } + + case PDO_ATTR_STRINGIFY_FETCHES: + { + // For this attribute, if we dont set the return_value than PDO returns NULL. + ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) ); + break; + } + + case PDO_ATTR_SERVER_INFO: + { + core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC ); + break; + } + + case PDO_ATTR_SERVER_VERSION: + { + core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC ); + break; + } + + case PDO_ATTR_CLIENT_VERSION: + { + core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC ); + + //Add the PDO SQLSRV driver's file version + //Declarations below eliminate compiler warnings about string constant to char* conversions + const char* extver = "ExtensionVer"; + std::string filever = VER_FILEVERSION_STR; + core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/ + TSRMLS_CC ); + break; + } + + case SQLSRV_ATTR_ENCODING: + { + ZVAL_LONG( return_value, driver_dbh->encoding() ); + break; + } + + case SQLSRV_ATTR_QUERY_TIMEOUT: + { + ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); + break; + } + + case SQLSRV_ATTR_DIRECT_QUERY: + { + ZVAL_BOOL( return_value, driver_dbh->direct_query ); + break; + } + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + { + ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); + break; + } + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + { + ZVAL_BOOL( return_value, driver_dbh->fetch_numeric ); + break; + } + + default: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); + break; + } + } + + return 1; + } + catch( core::CoreException& ) { + return 0; + } +} + +// Called by PDO::errorInfo and PDOStatement::errorInfo. +// Returns the error info. +// Parameters: +// dbh - The PDO managed connection object. +// stmt - The PDO managed statement object. +// info - zval in which to return the error info. +// Return: +// 0 for failure, 1 for success. +int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, + _Out_ zval *info TSRMLS_DC) +{ + SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." ); + + sqlsrv_error* ctx_error = NULL; + if( stmt ) { + ctx_error = static_cast( stmt->driver_data )->last_error(); + } + else { + ctx_error = static_cast( dbh->driver_data )->last_error(); + } + + pdo_sqlsrv_retrieve_context_error( ctx_error, info ); + + return 1; +} + +// pdo_sqlsrv_dbh_last_id +// Maps to PDO::lastInsertId. +// Returns the last id generated by an executed SQL statement +// Parameters: +// dbh - The PDO managed connection object. +// name - Table name. +// len - Length of the name. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + // turn off any error handling for last_id + pdo_error_mode prev_err_mode = dbh->error_mode; + dbh->error_mode = PDO_ERRMODE_SILENT; + + sqlsrv_malloc_auto_ptr driver_stmt; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." ); + + sqlsrv_malloc_auto_ptr id_str; + id_str = reinterpret_cast( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN )); + + try { + + char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ]; + if( name == NULL ) { + strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY ); + } + else { + char* quoted_table = NULL; + size_t quoted_len = 0; + int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), "ed_table, "ed_len, PDO_PARAM_NULL TSRMLS_CC ); + SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name."); + snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table ); + sqlsrv_free( quoted_table ); + } + + // temp PDO statement used for error handling if something happens + pdo_stmt_t temp_stmt; + temp_stmt.dbh = dbh; + + // allocate a full driver statement to take advantage of the error handling + driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); + driver_stmt->set_func( __FUNCTION__ ); + + + sqlsrv_malloc_auto_ptr wsql_string; + unsigned int wsql_len; + wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast( 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() ) { + throw core::CoreException(); + } + + // execute the last insert id query + core::SQLExecDirectW( driver_stmt, wsql_string 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, + reinterpret_cast( len ), false TSRMLS_CC ); + + CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt, + PDO_SQLSRV_ERROR_LAST_INSERT_ID ) { + throw core::CoreException(); + } + + driver_stmt->~sqlsrv_stmt(); + } + catch( core::CoreException& ) { + + // copy any errors on the statement to the connection so that the user sees them, since the statement is released + // before this method returns + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_stmt->last_error()->sqlstate )); + driver_dbh->set_last_error( driver_stmt->last_error() ); + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + strcpy_s( id_str.get(), 1, "" ); + *len = 0; + } + + char* ret_id_str = id_str.get(); + id_str.transferred(); + + // restore error handling to its previous mode + dbh->error_mode = prev_err_mode; + + return ret_id_str; +} + +// pdo_sqlsrv_dbh_quote +// Maps to PDO::quote. As the name says, this function quotes a string. +// Always returns a valid string unless memory allocation fails. +// Parameters: +// dbh - The PDO managed connection object. +// unquoted - The unquoted string to be quoted. +// unquoted_len - Length of the unquoted string. +// quoted - Buffer for output string. +// quoted_len - Length of the output string. +// Return: +// 0 for failure, 1 for success. +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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR; + + // get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from: + // 1. PDO::quote() - object name is PDO + // 2. PDOStatement::execute() - object name is PDOStatement + zend_execute_data* execute_data = EG( current_execute_data ); + zval *object = getThis(); + + // iterate through parents to find "PDOStatement" + bool is_statement = false; + if ( object ) { + zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce; + while ( curr_class != NULL ) { + if ( strcmp( reinterpret_cast( curr_class->name->val ), "PDOStatement" ) == 0 ) { + is_statement = true; + break; + } + curr_class = curr_class->parent; + } + } + // only change the encoding if quote is called from the statement level (which should only be called when a statement + // is prepared with emulate prepared on) + if ( is_statement ) { + 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 + pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast( stmt->driver_data ); + if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) { + encoding = driver_stmt->encoding(); + } + else { + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( stmt->driver_data ); + encoding = driver_dbh->encoding(); + } + // get the placeholder at the current position in driver_stmt->placeholders ht + 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 ) { + pdo_bound_param_data* param = NULL; + if ( Z_TYPE_P( placeholder ) == IS_STRING ) { + param = reinterpret_cast( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder ))); + } + else if ( Z_TYPE_P( placeholder ) == IS_LONG) { + param = reinterpret_cast( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder ))); + } + if ( NULL != param ) { + SQLSRV_ENCODING param_encoding = static_cast( Z_LVAL( param->driver_params )); + if ( param_encoding != SQLSRV_ENCODING_INVALID ) { + encoding = param_encoding; + } + } + } + } + + if ( encoding == SQLSRV_ENCODING_BINARY ) { + // convert from char* to hex digits using os + std::basic_ostringstream os; + 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. + // return an empty terminated string for now + if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) { + *quoted_len = 0; + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); + ( *quoted )[ 0 ] = '\0'; + return 1; + } + // 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) + // thus append '0' first + if (( int )unquoted[index] < 16 ) { + os << '0'; + } + os << std::hex << ( int )unquoted[ index ]; + } + std::basic_string str_hex = os.str(); + // 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 + char* unquoted_str = reinterpret_cast( 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() ); + // include length of '0x' in the binary string + *quoted_len = unquoted_str_len + 2; + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); + unsigned int out_current = 0; + // insert '0x' + ( *quoted )[ out_current++ ] = '0'; + ( *quoted )[ out_current++ ] = 'x'; + for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) { + ( *quoted )[ out_current++ ] = unquoted_str[ index ]; + } + // null terminator + ( *quoted )[ out_current ] = '\0'; + sqlsrv_free( unquoted_str ); + return 1; + } + else { + // count the number of quotes needed + unsigned int quotes_needed = 2; // the initial start and end quotes of course + // include the N proceeding the initial quote if encoding is UTF8 + if ( encoding == SQLSRV_ENCODING_UTF8 ) { + quotes_needed = 3; + } + for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { + if ( unquoted[ index ] == '\'' ) { + ++quotes_needed; + } + } + + *quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator. + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator. + unsigned int out_current = 0; + + // insert N if the encoding is UTF8 + if ( encoding == SQLSRV_ENCODING_UTF8 ) { + ( *quoted )[ out_current++ ] = 'N'; + } + // insert initial quote + ( *quoted )[ out_current++ ] = '\''; + + for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { + if ( unquoted[ index ] == '\'' ) { + ( *quoted )[ out_current++ ] = '\''; + ( *quoted )[ out_current++ ] = '\''; + } + else { + ( *quoted )[ out_current++ ] = unquoted[ index ]; + } + } + + // trailing quote and null terminator + ( *quoted )[ out_current++ ] = '\''; + ( *quoted )[ out_current ] = '\0'; + + return 1; + } +} + +// 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_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + 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 ) { + return NULL; + } + + return NULL; // to avoid a compiler warning +} + +namespace { + +// Maps the PDO driver specific statement option/attribute constants to the core layer +// statement option/attribute constants. +void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, + _Inout_ zval* data TSRMLS_DC ) +{ + zend_ulong option_key = -1; + switch( key ) { + + case PDO_ATTR_CURSOR: + option_key = SQLSRV_STMT_OPTION_SCROLLABLE; + break; + + case SQLSRV_ATTR_ENCODING: + option_key = PDO_STMT_OPTION_ENCODING; + break; + + case SQLSRV_ATTR_QUERY_TIMEOUT: + option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT; + break; + + case PDO_ATTR_STATEMENT_CLASS: + break; + + case SQLSRV_ATTR_DIRECT_QUERY: + option_key = PDO_STMT_OPTION_DIRECT_QUERY; + break; + + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE; + break; + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE; + break; + + case PDO_ATTR_EMULATE_PREPARES: + option_key = PDO_STMT_OPTION_EMULATE_PREPARES; + break; + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE; + break; + + default: + CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { + throw core::CoreException(); + } + break; + } + + // if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it + if( option_key != -1 ) { + zval_add_ref( data ); + core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC ); + } +} + + +// validate_stmt_options +// Iterates through the list of statement options provided by the user and validates them +// against the list of statement options provided by this driver. After validation +// creates a Hashtable of statement options to be sent to the core layer for processing. +// Parameters: +// ctx - The current context. +// stmt_options - The user provided list of statement options. +// pdo_stmt_options_ht - Output hashtable of statement options. +void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ) +{ + try { + + if( stmt_options ) { + + HashTable* options_ht = Z_ARRVAL_P( stmt_options ); + size_t int_key = -1; + zend_string *key = NULL; + zval* data = NULL; + + ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) { + int type = HASH_KEY_NON_EXISTENT; + int result = 0; + type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; + CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { + throw core::CoreException(); + } + + add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); + } ZEND_HASH_FOREACH_END(); + } + } + catch( core::CoreException& ) { + + 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 ) +{ + TSRMLS_C; + char const* val_str = "no"; + + if( core_str_zval_is_true( value ) ) { + + val_str = "yes"; + } + + conn_str += option->odbc_name; + conn_str += "={"; + conn_str += val_str; + conn_str += "};"; +} + +void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, + std::string& /*conn_str*/ TSRMLS_DC ) +{ + try { + + SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); + const char* val = Z_STRVAL_P( value_z ); + size_t val_len = Z_STRLEN_P( value_z ); + zend_long out_val = SQL_TXN_READ_COMMITTED; + + // READ_COMMITTED + if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { + + out_val = SQL_TXN_READ_COMMITTED; + } + + // READ_UNCOMMITTED + else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { + + out_val = SQL_TXN_READ_UNCOMMITTED; + } + + // REPEATABLE_READ + else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { + + out_val = SQL_TXN_REPEATABLE_READ; + } + + // SERIALIZABLE + else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { + + out_val = SQL_TXN_SERIALIZABLE; + } + + // SNAPSHOT + else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { + + out_val = SQL_TXN_SS_SNAPSHOT; + } + + else { + + CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { + + throw core::CoreException(); + } + } + + core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast( out_val ), SQL_IS_UINTEGER TSRMLS_CC ); + + } + catch( core::CoreException& ) { + + throw; + } +} + +} // namespace From a1dd674c449eef88e42e0db4209866b0781228c9 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 14:08:48 -0700 Subject: [PATCH 02/10] Tests added --- .../pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 60 ++++++++++++++++++ .../pdo_278_lastinsertid_seq_2.phpt | 62 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt create mode 100644 test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt new file mode 100644 index 00000000..fe7a7972 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -0,0 +1,60 @@ +--TEST-- +Provide name in lastInsertId to retrieve the last sequence number +--SKIPIF-- +--FILE-- +getAttribute(PDO::ATTR_SERVER_VERSION)); + if ($version_arr[0] < 11) { + echo "Done\n"; + } + else { + $tableName1 = GetTempTableName('tab1'); + $tableName2 = GetTempTableName('tab2'); + $sequenceName = 'sequence1'; + + $stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName"); + $sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; + $stmt = $conn->query($sql); + $sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))"; + $stmt = $conn->query($sql); + + $sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE"; + $stmt = $conn->query($sql); + + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )"); + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )"); + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )"); + $ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )"); + // return the last sequence number is sequence name is provided + $lastSeq = $conn->lastInsertId($sequenceName); + // defaults to $tableName2 -- because it returns the last inserted id value + $lastRow = $conn->lastInsertId(); + + if ($lastSeq == 3 && $lastRow == 1) { + echo "Done\n"; + } + else { + echo "sequence value or identity does not match as expected\n"; + } + $stmt = $conn->query("DROP TABLE $tableName1"); + $stmt = $conn->query("DROP TABLE $tableName2"); + $stmt = $conn->query("DROP SEQUENCE $sequenceName"); + $stmt = null; + } + $conn = null; +} +catch (Exception $e){ + echo "Exception $e\n"; +} + +?> +--EXPECT-- +Done \ No newline at end of file diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt new file mode 100644 index 00000000..08cb7b92 --- /dev/null +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt @@ -0,0 +1,62 @@ +--TEST-- +LastInsertId returns the last sequences operating on the same table +--SKIPIF-- +--FILE-- +getAttribute(PDO::ATTR_SERVER_VERSION)); + if ($version_arr[0] < 11) { + echo "Done\n"; + } + else { + $tableName = GetTempTableName('tab'); + $sequence1 = 'sequence1'; + $sequence2 = 'sequenceNeg1'; + $stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1"); + $stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2"); + $sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; + $stmt = $conn->query($sql); + $sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100"; + $stmt = $conn->query($sql); + + $sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200"; + $stmt = $conn->query($sql); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )"); + // return the last sequence number of 'sequence1' + $lastSeq1 = $conn->lastInsertId($sequence1); + + // return the last sequence number of 'sequenceNeg1' + $lastSeq2 = $conn->lastInsertId($sequence2); + + // providing a table name in lastInsertId should return an empty string + $lastSeq3 = $conn->lastInsertId($tableName); + + if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") { + echo "Done\n"; + } + $stmt = $conn->query("DROP TABLE $tableName"); + $stmt = $conn->query("DROP SEQUENCE $sequence1"); + $stmt = $conn->query("DROP SEQUENCE $sequence2"); + $stmt = null; + } + $conn = null; +} + catch (Exception $e){ + echo "Exception $e\n"; +} + +?> +--EXPECT-- +Done \ No newline at end of file From ca4a2d9b7de753828c871bcb173aaf8ccde84ef0 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 14:10:53 -0700 Subject: [PATCH 03/10] changed line endings --- source/pdo_sqlsrv/pdo_dbh.cpp | 3284 ++++++++--------- .../pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 118 +- .../pdo_278_lastinsertid_seq_2.phpt | 122 +- 3 files changed, 1762 insertions(+), 1762 deletions(-) diff --git a/source/pdo_sqlsrv/pdo_dbh.cpp b/source/pdo_sqlsrv/pdo_dbh.cpp index 7ea59d3c..6dcd7c76 100644 --- a/source/pdo_sqlsrv/pdo_dbh.cpp +++ b/source/pdo_sqlsrv/pdo_dbh.cpp @@ -1,1642 +1,1642 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// file: pdo_dbh.cpp -// -// Contents: Implements the PDO object for PDO_SQLSRV -// -// Microsoft Drivers 4.3 for PHP for SQL Server -// Copyright(c) Microsoft Corporation -// All rights reserved. -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//--------------------------------------------------------------------------------------------------------------------------------- - -#include "php_pdo_sqlsrv.h" - -#include -#include - -typedef const zend_function_entry pdo_sqlsrv_function_entry; - -// *** internal variables and constants *** - -namespace { - -const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; -const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer -const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s"; -const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes - -// List of PDO supported connection options. -namespace PDOConnOptionNames { - -const char Server[] = "Server"; -const char APP[] = "APP"; -const char ApplicationIntent[] = "ApplicationIntent"; -const char AttachDBFileName[] = "AttachDbFileName"; -const char ConnectionPooling[] = "ConnectionPooling"; -const char Authentication[] = "Authentication"; -#ifdef _WIN32 -const char ConnectRetryCount[] = "ConnectRetryCount"; -const char ConnectRetryInterval[] = "ConnectRetryInterval"; -#endif // _WIN32 -const char Database[] = "Database"; -const char Encrypt[] = "Encrypt"; -const char Failover_Partner[] = "Failover_Partner"; -const char LoginTimeout[] = "LoginTimeout"; -const char MARS_Option[] = "MultipleActiveResultSets"; -const char MultiSubnetFailover[] = "MultiSubnetFailover"; -const char QuotedId[] = "QuotedId"; -const char TraceFile[] = "TraceFile"; -const char TraceOn[] = "TraceOn"; -const char TrustServerCertificate[] = "TrustServerCertificate"; -const char TransactionIsolation[] = "TransactionIsolation"; -const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution"; -const char WSID[] = "WSID"; - -} - -enum PDO_CONN_OPTIONS { - - PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, - -}; - -enum PDO_STMT_OPTIONS { - - PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC, - PDO_STMT_OPTION_DIRECT_QUERY, - PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, - PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, - PDO_STMT_OPTION_EMULATE_PREPARES, - PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, -}; - -// List of all the statement options supported by this driver. -const stmt_option PDO_STMT_OPTS[] = { - - { NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr( new stmt_option_query_timeout ) }, - { NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr( new stmt_option_pdo_scrollable ) }, - { NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr( new stmt_option_encoding ) }, - { NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, std::unique_ptr( new stmt_option_direct_query ) }, - { NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, std::unique_ptr( new stmt_option_cursor_scroll_type ) }, - { NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr( new stmt_option_buffered_query_limit ) }, - { NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr( new stmt_option_emulate_prepares ) }, - { NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr( new stmt_option_fetch_numeric ) }, - - { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr{} }, -}; - -// boolean connection string -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 ); -}; - -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 ); -}; - -#ifdef _WIN32 -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 ) - { - TSRMLS_C; - SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" ) - - std::string val_str = Z_STRVAL_P( value ); - - conn_str += option->odbc_name; - conn_str += "={"; - conn_str += val_str; - conn_str += "};"; - } -}; -#endif // _WIN32 - -template -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 ) - { - try { - - SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); - - size_t val = static_cast( atoi( Z_STRVAL_P( value )) ); - core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( val ), SQL_IS_UINTEGER TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } - } -}; - -template -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 ) - { - try { - - core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( core_str_zval_is_true( value )), - SQL_IS_UINTEGER TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } - } -}; - -// statement options related functions -void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, - _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 ); - -} // namespace - - -// List of all connection options supported by this driver. -const connection_option PDO_CONN_OPTS[] = { - { - PDOConnOptionNames::Server, - sizeof( PDOConnOptionNames::Server ), - PDO_CONN_OPTION_SERVER, - NULL, - 0, - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::APP, - sizeof( PDOConnOptionNames::APP ), - SQLSRV_CONN_OPTION_APP, - ODBCConnOptions::APP, - sizeof( ODBCConnOptions::APP ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::ApplicationIntent, - sizeof( PDOConnOptionNames::ApplicationIntent ), - SQLSRV_CONN_OPTION_APPLICATION_INTENT, - ODBCConnOptions::ApplicationIntent, - sizeof( ODBCConnOptions::ApplicationIntent ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::AttachDBFileName, - sizeof( PDOConnOptionNames::AttachDBFileName ), - SQLSRV_CONN_OPTION_ATTACHDBFILENAME, - ODBCConnOptions::AttachDBFileName, - sizeof( ODBCConnOptions::AttachDBFileName ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::Authentication, - sizeof( PDOConnOptionNames::Authentication ), - SQLSRV_CONN_OPTION_AUTHENTICATION, - ODBCConnOptions::Authentication, - sizeof( ODBCConnOptions::Authentication ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::ConnectionPooling, - sizeof( PDOConnOptionNames::ConnectionPooling ), - SQLSRV_CONN_OPTION_CONN_POOLING, - ODBCConnOptions::ConnectionPooling, - sizeof( ODBCConnOptions::ConnectionPooling ), - CONN_ATTR_BOOL, - conn_null_func::func - }, -#ifdef _WIN32 - { - PDOConnOptionNames::ConnectRetryCount, - sizeof( PDOConnOptionNames::ConnectRetryCount ), - SQLSRV_CONN_OPTION_CONN_RETRY_COUNT, - ODBCConnOptions::ConnectRetryCount, - sizeof( ODBCConnOptions::ConnectRetryCount ), - CONN_ATTR_INT, - pdo_int_conn_str_func::func - }, - { - PDOConnOptionNames::ConnectRetryInterval, - sizeof( PDOConnOptionNames::ConnectRetryInterval ), - SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL, - ODBCConnOptions::ConnectRetryInterval, - sizeof( ODBCConnOptions::ConnectRetryInterval ), - CONN_ATTR_INT, - pdo_int_conn_str_func::func - }, -#endif // _WIN32 - { - PDOConnOptionNames::Database, - sizeof( PDOConnOptionNames::Database ), - SQLSRV_CONN_OPTION_DATABASE, - ODBCConnOptions::Database, - sizeof( ODBCConnOptions::Database ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::Encrypt, - sizeof( PDOConnOptionNames::Encrypt ), - SQLSRV_CONN_OPTION_ENCRYPT, - ODBCConnOptions::Encrypt, - sizeof( ODBCConnOptions::Encrypt ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::Failover_Partner, - sizeof( PDOConnOptionNames::Failover_Partner ), - SQLSRV_CONN_OPTION_FAILOVER_PARTNER, - ODBCConnOptions::Failover_Partner, - sizeof( ODBCConnOptions::Failover_Partner ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::LoginTimeout, - sizeof( PDOConnOptionNames::LoginTimeout ), - SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, - ODBCConnOptions::LoginTimeout, - sizeof( ODBCConnOptions::LoginTimeout ), - CONN_ATTR_INT, - pdo_int_conn_attr_func::func - }, - { - PDOConnOptionNames::MARS_Option, - sizeof( PDOConnOptionNames::MARS_Option ), - SQLSRV_CONN_OPTION_MARS, - ODBCConnOptions::MARS_ODBC, - sizeof( ODBCConnOptions::MARS_ODBC ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::MultiSubnetFailover, - sizeof( PDOConnOptionNames::MultiSubnetFailover ), - SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, - ODBCConnOptions::MultiSubnetFailover, - sizeof( ODBCConnOptions::MultiSubnetFailover ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::QuotedId, - sizeof( PDOConnOptionNames::QuotedId ), - SQLSRV_CONN_OPTION_QUOTED_ID, - ODBCConnOptions::QuotedId, - sizeof( ODBCConnOptions::QuotedId ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::TraceFile, - sizeof( PDOConnOptionNames::TraceFile ), - SQLSRV_CONN_OPTION_TRACE_FILE, - ODBCConnOptions::TraceFile, - sizeof( ODBCConnOptions::TraceFile ), - CONN_ATTR_STRING, - str_conn_attr_func::func - }, - { - PDOConnOptionNames::TraceOn, - sizeof( PDOConnOptionNames::TraceOn ), - SQLSRV_CONN_OPTION_TRACE_ON, - ODBCConnOptions::TraceOn, - sizeof( ODBCConnOptions::TraceOn ), - CONN_ATTR_BOOL, - pdo_bool_conn_attr_func::func - }, - { - PDOConnOptionNames::TransactionIsolation, - sizeof( PDOConnOptionNames::TransactionIsolation ), - SQLSRV_CONN_OPTION_TRANS_ISOLATION, - ODBCConnOptions::TransactionIsolation, - sizeof( ODBCConnOptions::TransactionIsolation ), - CONN_ATTR_INT, - pdo_txn_isolation_conn_attr_func::func - }, - { - PDOConnOptionNames::TrustServerCertificate, - sizeof( PDOConnOptionNames::TrustServerCertificate ), - SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, - ODBCConnOptions::TrustServerCertificate, - sizeof( ODBCConnOptions::TrustServerCertificate ), - CONN_ATTR_BOOL, - pdo_bool_conn_str_func::func - }, - { - PDOConnOptionNames::TransparentNetworkIPResolution, - sizeof(PDOConnOptionNames::TransparentNetworkIPResolution), - SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION, - ODBCConnOptions::TransparentNetworkIPResolution, - sizeof(ODBCConnOptions::TransparentNetworkIPResolution), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { - PDOConnOptionNames::WSID, - sizeof( PDOConnOptionNames::WSID ), - SQLSRV_CONN_OPTION_WSID, - ODBCConnOptions::WSID, - sizeof( ODBCConnOptions::WSID ), - CONN_ATTR_STRING, - conn_str_append_func::func - }, - { NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table -}; - - -// close the connection -int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); - -// execute queries -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 ); -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 -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_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); - -// 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_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ); - -// return more information -int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, - _Out_ zval *info TSRMLS_DC); - -// 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 ); - -// 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 ); - -// quote a string, meaning put quotes around it and escape any quotes within it -int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen, - enum pdo_param_type paramtype TSRMLS_DC ); - -struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = { - - pdo_sqlsrv_dbh_close, - pdo_sqlsrv_dbh_prepare, - pdo_sqlsrv_dbh_do, - pdo_sqlsrv_dbh_quote, - pdo_sqlsrv_dbh_begin, - pdo_sqlsrv_dbh_commit, - pdo_sqlsrv_dbh_rollback, - pdo_sqlsrv_dbh_set_attr, - pdo_sqlsrv_dbh_last_id, - pdo_sqlsrv_dbh_return_error, - pdo_sqlsrv_dbh_get_attr, - NULL, // check liveness not implemented - pdo_sqlsrv_get_driver_methods, - NULL, // request shutdown not implemented - NULL // in transaction not implemented -}; - - -// log a function entry point -#ifndef _WIN32 -#define PDO_LOG_DBH_ENTRY \ -{ \ - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ - driver_dbh->set_func( __FUNCTION__ ); \ - int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \ - char func[length+1]; \ - strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \ - strcat_s( func, length+1, ": entering" ); \ - LOG( SEV_NOTICE, func ); \ -} -#else -#define PDO_LOG_DBH_ENTRY \ -{ \ - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ - driver_dbh->set_func( __FUNCTION__ ); \ - LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \ -} -#endif - -// 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 ) : - sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ), - stmts( NULL ), - direct_query( false ), - query_timeout( QUERY_TIMEOUT_INVALID ), - client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )), - fetch_numeric( false ) -{ - if( client_buffer_max_size < 0 ) { - client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT; - LOG( SEV_WARNING, INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value." ); - } -} - -// pdo_sqlsrv_db_handle_factory -// Maps to PDO::__construct. -// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. -// Does the following things: -// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. -// (If an error occurs in this function, the PDO specification mandates that -// an exception be thrown, regardless of the error mode setting.) -// 2. Processes the driver options. -// 3. Creates a core_conn object by calling core_sqlsrv_connect. -// 4. Restores the previous error mode on success. -// alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory -// Parameters: -// dbh - The PDO managed structure for the connection. -// driver_options - A HashTable (within the zval) of options to use when creating the connection. -// Return: -// 0 for failure, 1 for success. -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" ); - - hash_auto_ptr pdo_conn_options_ht; - pdo_error_mode prev_err_mode = dbh->error_mode; - - // must be done in all cases so that even a failed connection can query the - // object for errors. - dbh->methods = &pdo_sqlsrv_dbh_methods; - dbh->driver_data = NULL; - zval* temp_server_z = NULL; - sqlsrv_malloc_auto_ptr dsn_parser; - zval server_z; - ZVAL_UNDEF( &server_z ); - - try { - - // no matter what the error mode, we want exceptions thrown if the connection fails - // to happen (per the PDO spec) - dbh->error_mode = PDO_ERRMODE_EXCEPTION; - - g_pdo_henv_cp->set_driver( dbh ); - g_pdo_henv_ncp->set_driver( dbh ); - - CHECK_CUSTOM_ERROR( driver_options && Z_TYPE_P( driver_options ) != IS_ARRAY, *g_pdo_henv_cp, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) { - throw core::CoreException(); - } - // throws PDOException if the ATTR_PERSISTENT is in connection options - CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) { - dbh->refcount--; - throw pdo::PDOException(); - } - - // Initialize the options array to be passed to the core layer - ALLOC_HASHTABLE( pdo_conn_options_ht ); - - core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, - 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. - dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, - static_cast( dbh->data_source_len ), pdo_conn_options_ht ); - dsn_parser->parse_conn_string( TSRMLS_C ); - - // Extract the server name - temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); - - CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) { - - throw pdo::PDOException(); - } - - server_z = *temp_server_z; - - // Add a reference to the option value since we are deleting it from the hashtable - zval_add_ref( &server_z ); - zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); - - sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn, Z_STRVAL( server_z ), - dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, - PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC ); - - // Free the string in server_z after being used - zend_string_release( Z_STR( server_z )); - - SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); - - // set the driver_data and methods to complete creation of the PDO object - dbh->driver_data = conn; - dbh->error_mode = prev_err_mode; // reset the error mode - dbh->alloc_own_columns = 1; // we do our own memory management for columns - dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types - - } - catch( core::CoreException& ) { - - if ( Z_TYPE( server_z ) == IS_STRING ) { - zend_string_release( Z_STR( server_z )); - } - dbh->error_mode = prev_err_mode; // reset the error mode - g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL - - return 0; - } - catch( ... ) { - - DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" ); - } - - return 1; -} - -// pdo_sqlsrv_dbh_close -// Maps to PDO::__destruct. -// Called when a PDO object is to be destroyed. -// By the time this function is called, PDO has already made sure that -// all statements are disposed and the PDO object is the last item destroyed. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// Always returns 1 for success. -int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" ); - - // if the connection didn't complete properly, driver_data isn't initialized. - if( dbh->driver_data == NULL ) { - - return 1; - } - - PDO_RESET_DBH_ERROR; - - // call the core layer close - core_sqlsrv_close( reinterpret_cast( dbh->driver_data ) TSRMLS_CC ); - dbh->driver_data = NULL; - - // always return success that the connection is closed - return 1; -} - -// pdo_sqlsrv_dbh_prepare -// Called by PDO::prepare and PDOStatement::__construct. -// Creates a statement and prepares it for execution by PDO -// Paramters: -// dbh - The PDO managed connection object. -// sql - SQL query to be prepared. -// sql_len - Length of the sql query -// stmt - The PDO managed statement object. -// driver_options - User provided list of statement options. -// Return: -// 0 for failure, 1 for success. -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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - hash_auto_ptr pdo_stmt_options_ht; - sqlsrv_malloc_auto_ptr sql_rewrite; - size_t sql_rewrite_len = 0; - sqlsrv_malloc_auto_ptr driver_stmt; - hash_auto_ptr placeholders; - sqlsrv_malloc_auto_ptr sql_parser; - - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); - SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null"); - - try { - - // assign the methods for the statement object. This is necessary even if the - // statement fails so the user can retrieve the error information. - stmt->methods = &pdo_sqlsrv_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names - - // Initialize the options array to be passed to the core layer - ALLOC_HASHTABLE( pdo_stmt_options_ht ); - core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, - 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. - validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC ); - - driver_stmt = static_cast( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, - pdo_stmt_options_ht, PDO_STMT_OPTS, - pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC )); - - // if the user didn't set anything in the prepare options, then set the buffer limit - // to the value set on the connection. - if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { - - driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size; - } - - // if the user didn't set anything in the prepare options, then set the query timeout - // to the value set on the connection. - 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 ); - } - - // rewrite named parameters in the query to positional parameters if we aren't letting PDO do the - // parameter substitution for us - if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { - - // rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named - // parameters for consistency with the PDO MySQL and PDO ODBC drivers. - int zr = pdo_parse_params( stmt, const_cast( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC ); - - CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) { - throw core::CoreException(); - } - // if parameter substitution happened, use that query instead of the original - if( sql_rewrite != 0) { - sql = sql_rewrite; - sql_len = sql_rewrite_len; - } - } - - if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { - - core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC ); - } - else if( driver_stmt->direct_query ) { - - if( driver_stmt->direct_query_subst_string ) { - // we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme - // and we use estrdup below to allocate the new string, which uses emalloc - efree( reinterpret_cast( const_cast( driver_stmt->direct_query_subst_string ))); - } - driver_stmt->direct_query_subst_string = estrdup( sql ); - 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 - // set to the substituted query - if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) { - // parse placeholders in the sql query into the placeholders ht - ALLOC_HASHTABLE( placeholders ); - 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, - static_cast(stmt->query_stringlen), placeholders ); - sql_parser->parse_sql_string( TSRMLS_C ); - driver_stmt->placeholders = placeholders; - placeholders.transferred(); - } - - stmt->driver_data = driver_stmt; - driver_stmt.transferred(); - } - // everything is cleaned up by this point - // catch everything so the exception doesn't spill into the calling PDO code - catch( core::CoreException& ) { - - if( driver_stmt ) { - - driver_stmt->~pdo_sqlsrv_stmt(); - } - - // in the event that the statement caused an error that was copied to the connection, update the - // connection with the error's SQLSTATE. - if( driver_dbh->last_error() ) { - - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_dbh->last_error()->sqlstate )); - } - - return 0; - } - - // catch any errant exception and die - catch(...) { - - DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." ); - } - - return 1; -} - - -// pdo_sqlsrv_dbh_do -// Maps to PDO::exec. -// Execute a SQL statement, such as an insert, update or delete, and return -// the number of rows affected. -// Parameters: -// dbh - the PDO connection object, which contains the ODBC handle -// sql - the query to execute -// sql_len - length of sql query -// Return -// # of rows affected, -1 for an error. -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_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - - sqlsrv_malloc_auto_ptr driver_stmt; - SQLLEN rows = 0; - - // verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong - // thing to happen here. - SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); - - try { - - SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); - - // temp PDO statement used for error handling if something happens - pdo_stmt_t temp_stmt; - temp_stmt.dbh = dbh; - // allocate a full driver statement to take advantage of the error handling - driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, - NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); - driver_stmt->set_func( __FUNCTION__ ); - - SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( 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 - // 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 )) { - - SQLRETURN r = SQL_SUCCESS; - - do { - - rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); - - r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); - - } while ( r != SQL_NO_DATA ); - } - - // returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases - // naturally, so we override that here with no rows returned. - if( rows == -1 ) { - rows = 0; - } - } - catch( core::CoreException& ) { - - // copy any errors on the statement to the connection so that the user sees them, since the statement is released - // before this method returns - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_stmt->last_error()->sqlstate )); - driver_dbh->set_last_error( driver_stmt->last_error() ); - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - return -1; - } - catch( ... ) { - - DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." ); - } - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - return rows; -} - - -// transaction support functions - -// pdo_sqlsrv_dbh_begin -// Maps to PDO::beginTransaction. -// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn -// flag is maintained by PDO so we dont have to worry about it. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" ); - - core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); - } -} - - - -// pdo_sqlsrv_dbh_commit -// Maps to PDO::commit. -// Commits a transaction. Returns the connection to auto-commit mode. -// PDO throws error if PDO::commit is called on a connection that is not in an active -// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have -// to worry about it here. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" ); - - core_sqlsrv_commit( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); - } -} - -// pdo_sqlsrv_dbh_rollback -// Maps to PDO::rollback. -// Rolls back a transaction. Returns the connection in auto-commit mode. -// PDO throws error if PDO::rollBack is called on a connection that is not in an active -// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have -// to worry about it here. -// Parameters: -// dbh - The PDO managed connection object. -// Return: -// 0 for failure and 1 for success. -int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - try { - SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - - SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); - - DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" ); - - core_sqlsrv_rollback( driver_conn TSRMLS_CC ); - - return 1; - } - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); - } -} - -// pdo_sqlsrv_dbh_set_attr -// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. -// PDO driver manager calls this function directly after calling the factory -// method for PDO, for any attribute which is specified in the PDO constructor. -// Parameters: -// dbh - The PDO connection object maintained by PDO. -// attr - The attribute to be set. -// val - The value of the attribute to be set. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_set_attr: driver_data object was NULL."); - - try { - - switch( attr ) { - - case SQLSRV_ATTR_ENCODING: - { - zend_long attr_value; - if( Z_TYPE_P( val ) != IS_LONG ) { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); - } - attr_value = Z_LVAL_P( val ); - switch( attr_value ) { - - case SQLSRV_ENCODING_DEFAULT: - // when default is applied to a connection, that means use UTF-8 encoding - driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 ); - break; - case SQLSRV_ENCODING_SYSTEM: - case SQLSRV_ENCODING_UTF8: - driver_dbh->set_encoding( static_cast( attr_value )); - break; - default: - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); - break; - } - } - break; - - case SQLSRV_ATTR_DIRECT_QUERY: - driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false; - break; - - case SQLSRV_ATTR_QUERY_TIMEOUT: - if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { - convert_to_string( val ); - THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val )); - } - driver_dbh->query_timeout = static_cast( Z_LVAL_P( val ) ); - break; - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { - convert_to_string( val ); - THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val )); - } - driver_dbh->client_buffer_max_size = Z_LVAL_P( val ); - break; - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false; - break; - - // Not supported - case PDO_ATTR_FETCH_TABLE_NAMES: - case PDO_ATTR_FETCH_CATALOG_NAMES: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_MAX_COLUMN_LEN: - case PDO_ATTR_CURSOR_NAME: - case PDO_ATTR_AUTOCOMMIT: - case PDO_ATTR_PERSISTENT: - case PDO_ATTR_TIMEOUT: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); - } - - // Read-only - case PDO_ATTR_SERVER_VERSION: - case PDO_ATTR_SERVER_INFO: - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_DRIVER_NAME: - case PDO_ATTR_CONNECTION_STATUS: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); - } - - // Statement level only - case PDO_ATTR_EMULATE_PREPARES: - case PDO_ATTR_CURSOR: - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); - } - - default: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); - break; - } - } - } - catch( pdo::PDOException& ) { - - return 0; - } - - return 1; -} - - -// pdo_sqlsrv_dbh_get_attr -// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object. -// Parameters: -// dbh - The PDO connection object maintained by PDO. -// attr - The attribute to get. -// return_value - zval in which to return the attribute value. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_get_attr: driver_data object was NULL."); - - try { - - switch( attr ) { - - // Not supported - case PDO_ATTR_FETCH_TABLE_NAMES: - case PDO_ATTR_FETCH_CATALOG_NAMES: - case PDO_ATTR_PREFETCH: - case PDO_ATTR_MAX_COLUMN_LEN: - case PDO_ATTR_CURSOR_NAME: - case PDO_ATTR_AUTOCOMMIT: - case PDO_ATTR_TIMEOUT: - { - // PDO does not throw "not supported" error message for these attributes. - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); - } - - // Statement level only - case PDO_ATTR_EMULATE_PREPARES: - case PDO_ATTR_CURSOR: - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); - } - - case PDO_ATTR_STRINGIFY_FETCHES: - { - // For this attribute, if we dont set the return_value than PDO returns NULL. - ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) ); - break; - } - - case PDO_ATTR_SERVER_INFO: - { - core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC ); - break; - } - - case PDO_ATTR_SERVER_VERSION: - { - core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC ); - break; - } - - case PDO_ATTR_CLIENT_VERSION: - { - core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC ); - - //Add the PDO SQLSRV driver's file version - //Declarations below eliminate compiler warnings about string constant to char* conversions - const char* extver = "ExtensionVer"; - std::string filever = VER_FILEVERSION_STR; - core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/ - TSRMLS_CC ); - break; - } - - case SQLSRV_ATTR_ENCODING: - { - ZVAL_LONG( return_value, driver_dbh->encoding() ); - break; - } - - case SQLSRV_ATTR_QUERY_TIMEOUT: - { - ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); - break; - } - - case SQLSRV_ATTR_DIRECT_QUERY: - { - ZVAL_BOOL( return_value, driver_dbh->direct_query ); - break; - } - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - { - ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); - break; - } - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - { - ZVAL_BOOL( return_value, driver_dbh->fetch_numeric ); - break; - } - - default: - { - THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); - break; - } - } - - return 1; - } - catch( core::CoreException& ) { - return 0; - } -} - -// Called by PDO::errorInfo and PDOStatement::errorInfo. -// Returns the error info. -// Parameters: -// dbh - The PDO managed connection object. -// stmt - The PDO managed statement object. -// info - zval in which to return the error info. -// Return: -// 0 for failure, 1 for success. -int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, - _Out_ zval *info TSRMLS_DC) -{ - SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." ); - - sqlsrv_error* ctx_error = NULL; - if( stmt ) { - ctx_error = static_cast( stmt->driver_data )->last_error(); - } - else { - ctx_error = static_cast( dbh->driver_data )->last_error(); - } - - pdo_sqlsrv_retrieve_context_error( ctx_error, info ); - - return 1; -} - -// pdo_sqlsrv_dbh_last_id -// Maps to PDO::lastInsertId. -// Returns the last id generated by an executed SQL statement -// Parameters: -// dbh - The PDO managed connection object. -// name - Table name. -// len - Length of the name. -// Return: -// 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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - // turn off any error handling for last_id - pdo_error_mode prev_err_mode = dbh->error_mode; - dbh->error_mode = PDO_ERRMODE_SILENT; - - sqlsrv_malloc_auto_ptr driver_stmt; - - pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); - SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." ); - - sqlsrv_malloc_auto_ptr id_str; - id_str = reinterpret_cast( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN )); - - try { - - char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ]; - if( name == NULL ) { - strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY ); - } - else { - char* quoted_table = NULL; - size_t quoted_len = 0; - int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), "ed_table, "ed_len, PDO_PARAM_NULL TSRMLS_CC ); - SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name."); - snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table ); - sqlsrv_free( quoted_table ); - } - - // temp PDO statement used for error handling if something happens - pdo_stmt_t temp_stmt; - temp_stmt.dbh = dbh; - - // allocate a full driver statement to take advantage of the error handling - driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); - driver_stmt->set_func( __FUNCTION__ ); - - - sqlsrv_malloc_auto_ptr wsql_string; - unsigned int wsql_len; - wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast( 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() ) { - throw core::CoreException(); - } - - // execute the last insert id query - core::SQLExecDirectW( driver_stmt, wsql_string 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, - reinterpret_cast( len ), false TSRMLS_CC ); - - CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt, - PDO_SQLSRV_ERROR_LAST_INSERT_ID ) { - throw core::CoreException(); - } - - driver_stmt->~sqlsrv_stmt(); - } - catch( core::CoreException& ) { - - // copy any errors on the statement to the connection so that the user sees them, since the statement is released - // before this method returns - strcpy_s( dbh->error_code, sizeof( dbh->error_code ), - reinterpret_cast( driver_stmt->last_error()->sqlstate )); - driver_dbh->set_last_error( driver_stmt->last_error() ); - - if( driver_stmt ) { - driver_stmt->~sqlsrv_stmt(); - } - - strcpy_s( id_str.get(), 1, "" ); - *len = 0; - } - - char* ret_id_str = id_str.get(); - id_str.transferred(); - - // restore error handling to its previous mode - dbh->error_mode = prev_err_mode; - - return ret_id_str; -} - -// pdo_sqlsrv_dbh_quote -// Maps to PDO::quote. As the name says, this function quotes a string. -// Always returns a valid string unless memory allocation fails. -// Parameters: -// dbh - The PDO managed connection object. -// unquoted - The unquoted string to be quoted. -// unquoted_len - Length of the unquoted string. -// quoted - Buffer for output string. -// quoted_len - Length of the output string. -// Return: -// 0 for failure, 1 for success. -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 ) -{ - PDO_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR; - - // get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from: - // 1. PDO::quote() - object name is PDO - // 2. PDOStatement::execute() - object name is PDOStatement - zend_execute_data* execute_data = EG( current_execute_data ); - zval *object = getThis(); - - // iterate through parents to find "PDOStatement" - bool is_statement = false; - if ( object ) { - zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce; - while ( curr_class != NULL ) { - if ( strcmp( reinterpret_cast( curr_class->name->val ), "PDOStatement" ) == 0 ) { - is_statement = true; - break; - } - curr_class = curr_class->parent; - } - } - // only change the encoding if quote is called from the statement level (which should only be called when a statement - // is prepared with emulate prepared on) - if ( is_statement ) { - 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 - pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast( stmt->driver_data ); - if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) { - encoding = driver_stmt->encoding(); - } - else { - pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( stmt->driver_data ); - encoding = driver_dbh->encoding(); - } - // get the placeholder at the current position in driver_stmt->placeholders ht - 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 ) { - pdo_bound_param_data* param = NULL; - if ( Z_TYPE_P( placeholder ) == IS_STRING ) { - param = reinterpret_cast( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder ))); - } - else if ( Z_TYPE_P( placeholder ) == IS_LONG) { - param = reinterpret_cast( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder ))); - } - if ( NULL != param ) { - SQLSRV_ENCODING param_encoding = static_cast( Z_LVAL( param->driver_params )); - if ( param_encoding != SQLSRV_ENCODING_INVALID ) { - encoding = param_encoding; - } - } - } - } - - if ( encoding == SQLSRV_ENCODING_BINARY ) { - // convert from char* to hex digits using os - std::basic_ostringstream os; - 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. - // return an empty terminated string for now - if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) { - *quoted_len = 0; - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); - ( *quoted )[ 0 ] = '\0'; - return 1; - } - // 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) - // thus append '0' first - if (( int )unquoted[index] < 16 ) { - os << '0'; - } - os << std::hex << ( int )unquoted[ index ]; - } - std::basic_string str_hex = os.str(); - // 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 - char* unquoted_str = reinterpret_cast( 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() ); - // include length of '0x' in the binary string - *quoted_len = unquoted_str_len + 2; - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); - unsigned int out_current = 0; - // insert '0x' - ( *quoted )[ out_current++ ] = '0'; - ( *quoted )[ out_current++ ] = 'x'; - for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) { - ( *quoted )[ out_current++ ] = unquoted_str[ index ]; - } - // null terminator - ( *quoted )[ out_current ] = '\0'; - sqlsrv_free( unquoted_str ); - return 1; - } - else { - // count the number of quotes needed - unsigned int quotes_needed = 2; // the initial start and end quotes of course - // include the N proceeding the initial quote if encoding is UTF8 - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - quotes_needed = 3; - } - for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { - if ( unquoted[ index ] == '\'' ) { - ++quotes_needed; - } - } - - *quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator. - *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator. - unsigned int out_current = 0; - - // insert N if the encoding is UTF8 - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - ( *quoted )[ out_current++ ] = 'N'; - } - // insert initial quote - ( *quoted )[ out_current++ ] = '\''; - - for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { - if ( unquoted[ index ] == '\'' ) { - ( *quoted )[ out_current++ ] = '\''; - ( *quoted )[ out_current++ ] = '\''; - } - else { - ( *quoted )[ out_current++ ] = unquoted[ index ]; - } - } - - // trailing quote and null terminator - ( *quoted )[ out_current++ ] = '\''; - ( *quoted )[ out_current ] = '\0'; - - return 1; - } -} - -// 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_RESET_DBH_ERROR; - PDO_VALIDATE_CONN; - PDO_LOG_DBH_ENTRY; - - sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); - 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 ) { - return NULL; - } - - return NULL; // to avoid a compiler warning -} - -namespace { - -// Maps the PDO driver specific statement option/attribute constants to the core layer -// statement option/attribute constants. -void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, - _Inout_ zval* data TSRMLS_DC ) -{ - zend_ulong option_key = -1; - switch( key ) { - - case PDO_ATTR_CURSOR: - option_key = SQLSRV_STMT_OPTION_SCROLLABLE; - break; - - case SQLSRV_ATTR_ENCODING: - option_key = PDO_STMT_OPTION_ENCODING; - break; - - case SQLSRV_ATTR_QUERY_TIMEOUT: - option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT; - break; - - case PDO_ATTR_STATEMENT_CLASS: - break; - - case SQLSRV_ATTR_DIRECT_QUERY: - option_key = PDO_STMT_OPTION_DIRECT_QUERY; - break; - - case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: - option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE; - break; - - case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: - option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE; - break; - - case PDO_ATTR_EMULATE_PREPARES: - option_key = PDO_STMT_OPTION_EMULATE_PREPARES; - break; - - case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: - option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE; - break; - - default: - CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { - throw core::CoreException(); - } - break; - } - - // if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it - if( option_key != -1 ) { - zval_add_ref( data ); - core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC ); - } -} - - -// validate_stmt_options -// Iterates through the list of statement options provided by the user and validates them -// against the list of statement options provided by this driver. After validation -// creates a Hashtable of statement options to be sent to the core layer for processing. -// Parameters: -// ctx - The current context. -// stmt_options - The user provided list of statement options. -// pdo_stmt_options_ht - Output hashtable of statement options. -void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ) -{ - try { - - if( stmt_options ) { - - HashTable* options_ht = Z_ARRVAL_P( stmt_options ); - size_t int_key = -1; - zend_string *key = NULL; - zval* data = NULL; - - ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) { - int type = HASH_KEY_NON_EXISTENT; - int result = 0; - type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { - throw core::CoreException(); - } - - add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); - } ZEND_HASH_FOREACH_END(); - } - } - catch( core::CoreException& ) { - - 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 ) -{ - TSRMLS_C; - char const* val_str = "no"; - - if( core_str_zval_is_true( value ) ) { - - val_str = "yes"; - } - - conn_str += option->odbc_name; - conn_str += "={"; - conn_str += val_str; - conn_str += "};"; -} - -void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, - std::string& /*conn_str*/ TSRMLS_DC ) -{ - try { - - SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); - const char* val = Z_STRVAL_P( value_z ); - size_t val_len = Z_STRLEN_P( value_z ); - zend_long out_val = SQL_TXN_READ_COMMITTED; - - // READ_COMMITTED - if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { - - out_val = SQL_TXN_READ_COMMITTED; - } - - // READ_UNCOMMITTED - else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { - - out_val = SQL_TXN_READ_UNCOMMITTED; - } - - // REPEATABLE_READ - else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { - - out_val = SQL_TXN_REPEATABLE_READ; - } - - // SERIALIZABLE - else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { - - out_val = SQL_TXN_SERIALIZABLE; - } - - // SNAPSHOT - else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) - && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { - - out_val = SQL_TXN_SS_SNAPSHOT; - } - - else { - - CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { - - throw core::CoreException(); - } - } - - core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast( out_val ), SQL_IS_UINTEGER TSRMLS_CC ); - - } - catch( core::CoreException& ) { - - throw; - } -} - -} // namespace +//--------------------------------------------------------------------------------------------------------------------------------- +// file: pdo_dbh.cpp +// +// Contents: Implements the PDO object for PDO_SQLSRV +// +// Microsoft Drivers 4.3 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + +#include "php_pdo_sqlsrv.h" + +#include +#include + +typedef const zend_function_entry pdo_sqlsrv_function_entry; + +// *** internal variables and constants *** + +namespace { + +const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;"; +const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer +const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT CURRENT_VALUE FROM SYS.SEQUENCES WHERE NAME=%s"; +const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes + +// List of PDO supported connection options. +namespace PDOConnOptionNames { + +const char Server[] = "Server"; +const char APP[] = "APP"; +const char ApplicationIntent[] = "ApplicationIntent"; +const char AttachDBFileName[] = "AttachDbFileName"; +const char ConnectionPooling[] = "ConnectionPooling"; +const char Authentication[] = "Authentication"; +#ifdef _WIN32 +const char ConnectRetryCount[] = "ConnectRetryCount"; +const char ConnectRetryInterval[] = "ConnectRetryInterval"; +#endif // _WIN32 +const char Database[] = "Database"; +const char Encrypt[] = "Encrypt"; +const char Failover_Partner[] = "Failover_Partner"; +const char LoginTimeout[] = "LoginTimeout"; +const char MARS_Option[] = "MultipleActiveResultSets"; +const char MultiSubnetFailover[] = "MultiSubnetFailover"; +const char QuotedId[] = "QuotedId"; +const char TraceFile[] = "TraceFile"; +const char TraceOn[] = "TraceOn"; +const char TrustServerCertificate[] = "TrustServerCertificate"; +const char TransactionIsolation[] = "TransactionIsolation"; +const char TransparentNetworkIPResolution[] = "TransparentNetworkIPResolution"; +const char WSID[] = "WSID"; + +} + +enum PDO_CONN_OPTIONS { + + PDO_CONN_OPTION_SERVER = SQLSRV_CONN_OPTION_DRIVER_SPECIFIC, + +}; + +enum PDO_STMT_OPTIONS { + + PDO_STMT_OPTION_ENCODING = SQLSRV_STMT_OPTION_DRIVER_SPECIFIC, + PDO_STMT_OPTION_DIRECT_QUERY, + PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, + PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, + PDO_STMT_OPTION_EMULATE_PREPARES, + PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, +}; + +// List of all the statement options supported by this driver. +const stmt_option PDO_STMT_OPTS[] = { + + { NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr( new stmt_option_query_timeout ) }, + { NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr( new stmt_option_pdo_scrollable ) }, + { NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr( new stmt_option_encoding ) }, + { NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, std::unique_ptr( new stmt_option_direct_query ) }, + { NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, std::unique_ptr( new stmt_option_cursor_scroll_type ) }, + { NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr( new stmt_option_buffered_query_limit ) }, + { NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr( new stmt_option_emulate_prepares ) }, + { NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr( new stmt_option_fetch_numeric ) }, + + { NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr{} }, +}; + +// boolean connection string +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 ); +}; + +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 ); +}; + +#ifdef _WIN32 +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 ) + { + TSRMLS_C; + SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" ) + + std::string val_str = Z_STRVAL_P( value ); + + conn_str += option->odbc_name; + conn_str += "={"; + conn_str += val_str; + conn_str += "};"; + } +}; +#endif // _WIN32 + +template +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 ) + { + try { + + SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." ); + + size_t val = static_cast( atoi( Z_STRVAL_P( value )) ); + core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( val ), SQL_IS_UINTEGER TSRMLS_CC ); + } + catch( core::CoreException& ) { + throw; + } + } +}; + +template +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 ) + { + try { + + core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( core_str_zval_is_true( value )), + SQL_IS_UINTEGER TSRMLS_CC ); + } + catch( core::CoreException& ) { + throw; + } + } +}; + +// statement options related functions +void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, + _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 ); + +} // namespace + + +// List of all connection options supported by this driver. +const connection_option PDO_CONN_OPTS[] = { + { + PDOConnOptionNames::Server, + sizeof( PDOConnOptionNames::Server ), + PDO_CONN_OPTION_SERVER, + NULL, + 0, + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::APP, + sizeof( PDOConnOptionNames::APP ), + SQLSRV_CONN_OPTION_APP, + ODBCConnOptions::APP, + sizeof( ODBCConnOptions::APP ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::ApplicationIntent, + sizeof( PDOConnOptionNames::ApplicationIntent ), + SQLSRV_CONN_OPTION_APPLICATION_INTENT, + ODBCConnOptions::ApplicationIntent, + sizeof( ODBCConnOptions::ApplicationIntent ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::AttachDBFileName, + sizeof( PDOConnOptionNames::AttachDBFileName ), + SQLSRV_CONN_OPTION_ATTACHDBFILENAME, + ODBCConnOptions::AttachDBFileName, + sizeof( ODBCConnOptions::AttachDBFileName ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::Authentication, + sizeof( PDOConnOptionNames::Authentication ), + SQLSRV_CONN_OPTION_AUTHENTICATION, + ODBCConnOptions::Authentication, + sizeof( ODBCConnOptions::Authentication ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::ConnectionPooling, + sizeof( PDOConnOptionNames::ConnectionPooling ), + SQLSRV_CONN_OPTION_CONN_POOLING, + ODBCConnOptions::ConnectionPooling, + sizeof( ODBCConnOptions::ConnectionPooling ), + CONN_ATTR_BOOL, + conn_null_func::func + }, +#ifdef _WIN32 + { + PDOConnOptionNames::ConnectRetryCount, + sizeof( PDOConnOptionNames::ConnectRetryCount ), + SQLSRV_CONN_OPTION_CONN_RETRY_COUNT, + ODBCConnOptions::ConnectRetryCount, + sizeof( ODBCConnOptions::ConnectRetryCount ), + CONN_ATTR_INT, + pdo_int_conn_str_func::func + }, + { + PDOConnOptionNames::ConnectRetryInterval, + sizeof( PDOConnOptionNames::ConnectRetryInterval ), + SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL, + ODBCConnOptions::ConnectRetryInterval, + sizeof( ODBCConnOptions::ConnectRetryInterval ), + CONN_ATTR_INT, + pdo_int_conn_str_func::func + }, +#endif // _WIN32 + { + PDOConnOptionNames::Database, + sizeof( PDOConnOptionNames::Database ), + SQLSRV_CONN_OPTION_DATABASE, + ODBCConnOptions::Database, + sizeof( ODBCConnOptions::Database ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::Encrypt, + sizeof( PDOConnOptionNames::Encrypt ), + SQLSRV_CONN_OPTION_ENCRYPT, + ODBCConnOptions::Encrypt, + sizeof( ODBCConnOptions::Encrypt ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::Failover_Partner, + sizeof( PDOConnOptionNames::Failover_Partner ), + SQLSRV_CONN_OPTION_FAILOVER_PARTNER, + ODBCConnOptions::Failover_Partner, + sizeof( ODBCConnOptions::Failover_Partner ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::LoginTimeout, + sizeof( PDOConnOptionNames::LoginTimeout ), + SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, + ODBCConnOptions::LoginTimeout, + sizeof( ODBCConnOptions::LoginTimeout ), + CONN_ATTR_INT, + pdo_int_conn_attr_func::func + }, + { + PDOConnOptionNames::MARS_Option, + sizeof( PDOConnOptionNames::MARS_Option ), + SQLSRV_CONN_OPTION_MARS, + ODBCConnOptions::MARS_ODBC, + sizeof( ODBCConnOptions::MARS_ODBC ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::MultiSubnetFailover, + sizeof( PDOConnOptionNames::MultiSubnetFailover ), + SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, + ODBCConnOptions::MultiSubnetFailover, + sizeof( ODBCConnOptions::MultiSubnetFailover ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::QuotedId, + sizeof( PDOConnOptionNames::QuotedId ), + SQLSRV_CONN_OPTION_QUOTED_ID, + ODBCConnOptions::QuotedId, + sizeof( ODBCConnOptions::QuotedId ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::TraceFile, + sizeof( PDOConnOptionNames::TraceFile ), + SQLSRV_CONN_OPTION_TRACE_FILE, + ODBCConnOptions::TraceFile, + sizeof( ODBCConnOptions::TraceFile ), + CONN_ATTR_STRING, + str_conn_attr_func::func + }, + { + PDOConnOptionNames::TraceOn, + sizeof( PDOConnOptionNames::TraceOn ), + SQLSRV_CONN_OPTION_TRACE_ON, + ODBCConnOptions::TraceOn, + sizeof( ODBCConnOptions::TraceOn ), + CONN_ATTR_BOOL, + pdo_bool_conn_attr_func::func + }, + { + PDOConnOptionNames::TransactionIsolation, + sizeof( PDOConnOptionNames::TransactionIsolation ), + SQLSRV_CONN_OPTION_TRANS_ISOLATION, + ODBCConnOptions::TransactionIsolation, + sizeof( ODBCConnOptions::TransactionIsolation ), + CONN_ATTR_INT, + pdo_txn_isolation_conn_attr_func::func + }, + { + PDOConnOptionNames::TrustServerCertificate, + sizeof( PDOConnOptionNames::TrustServerCertificate ), + SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, + ODBCConnOptions::TrustServerCertificate, + sizeof( ODBCConnOptions::TrustServerCertificate ), + CONN_ATTR_BOOL, + pdo_bool_conn_str_func::func + }, + { + PDOConnOptionNames::TransparentNetworkIPResolution, + sizeof(PDOConnOptionNames::TransparentNetworkIPResolution), + SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION, + ODBCConnOptions::TransparentNetworkIPResolution, + sizeof(ODBCConnOptions::TransparentNetworkIPResolution), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { + PDOConnOptionNames::WSID, + sizeof( PDOConnOptionNames::WSID ), + SQLSRV_CONN_OPTION_WSID, + ODBCConnOptions::WSID, + sizeof( ODBCConnOptions::WSID ), + CONN_ATTR_STRING, + conn_str_append_func::func + }, + { NULL, 0, SQLSRV_CONN_OPTION_INVALID, NULL, 0 , CONN_ATTR_INVALID, NULL }, //terminate the table +}; + + +// close the connection +int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); + +// execute queries +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 ); +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 +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_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ); + +// 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_get_attr( _Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC ); + +// return more information +int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, + _Out_ zval *info TSRMLS_DC); + +// 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 ); + +// 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 ); + +// quote a string, meaning put quotes around it and escape any quotes within it +int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquotedlen) const char* unquoted, _In_ size_t unquotedlen, _Outptr_result_buffer_(*quotedlen) char **quoted, _Out_ size_t* quotedlen, + enum pdo_param_type paramtype TSRMLS_DC ); + +struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = { + + pdo_sqlsrv_dbh_close, + pdo_sqlsrv_dbh_prepare, + pdo_sqlsrv_dbh_do, + pdo_sqlsrv_dbh_quote, + pdo_sqlsrv_dbh_begin, + pdo_sqlsrv_dbh_commit, + pdo_sqlsrv_dbh_rollback, + pdo_sqlsrv_dbh_set_attr, + pdo_sqlsrv_dbh_last_id, + pdo_sqlsrv_dbh_return_error, + pdo_sqlsrv_dbh_get_attr, + NULL, // check liveness not implemented + pdo_sqlsrv_get_driver_methods, + NULL, // request shutdown not implemented + NULL // in transaction not implemented +}; + + +// log a function entry point +#ifndef _WIN32 +#define PDO_LOG_DBH_ENTRY \ +{ \ + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ + driver_dbh->set_func( __FUNCTION__ ); \ + int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \ + char func[length+1]; \ + strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \ + strcat_s( func, length+1, ": entering" ); \ + LOG( SEV_NOTICE, func ); \ +} +#else +#define PDO_LOG_DBH_ENTRY \ +{ \ + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); \ + driver_dbh->set_func( __FUNCTION__ ); \ + LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \ +} +#endif + +// 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 ) : + sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ), + stmts( NULL ), + direct_query( false ), + query_timeout( QUERY_TIMEOUT_INVALID ), + client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )), + fetch_numeric( false ) +{ + if( client_buffer_max_size < 0 ) { + client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT; + LOG( SEV_WARNING, INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE " set to a invalid value. Resetting to default value." ); + } +} + +// pdo_sqlsrv_db_handle_factory +// Maps to PDO::__construct. +// Factory method called by the PDO driver manager to create a SQLSRV PDO connection. +// Does the following things: +// 1.Sets the error handling temporarily to PDO_ERRMODE_EXCEPTION. +// (If an error occurs in this function, the PDO specification mandates that +// an exception be thrown, regardless of the error mode setting.) +// 2. Processes the driver options. +// 3. Creates a core_conn object by calling core_sqlsrv_connect. +// 4. Restores the previous error mode on success. +// alloc_own_columns is set to 1 to tell the PDO driver manager that we manage memory +// Parameters: +// dbh - The PDO managed structure for the connection. +// driver_options - A HashTable (within the zval) of options to use when creating the connection. +// Return: +// 0 for failure, 1 for success. +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" ); + + hash_auto_ptr pdo_conn_options_ht; + pdo_error_mode prev_err_mode = dbh->error_mode; + + // must be done in all cases so that even a failed connection can query the + // object for errors. + dbh->methods = &pdo_sqlsrv_dbh_methods; + dbh->driver_data = NULL; + zval* temp_server_z = NULL; + sqlsrv_malloc_auto_ptr dsn_parser; + zval server_z; + ZVAL_UNDEF( &server_z ); + + try { + + // no matter what the error mode, we want exceptions thrown if the connection fails + // to happen (per the PDO spec) + dbh->error_mode = PDO_ERRMODE_EXCEPTION; + + g_pdo_henv_cp->set_driver( dbh ); + g_pdo_henv_ncp->set_driver( dbh ); + + CHECK_CUSTOM_ERROR( driver_options && Z_TYPE_P( driver_options ) != IS_ARRAY, *g_pdo_henv_cp, SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ) { + throw core::CoreException(); + } + // throws PDOException if the ATTR_PERSISTENT is in connection options + CHECK_CUSTOM_ERROR( dbh->is_persistent, *g_pdo_henv_cp, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ) { + dbh->refcount--; + throw pdo::PDOException(); + } + + // Initialize the options array to be passed to the core layer + ALLOC_HASHTABLE( pdo_conn_options_ht ); + + core::sqlsrv_zend_hash_init( *g_pdo_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, + 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. + dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_pdo_henv_cp, dbh->data_source, + static_cast( dbh->data_source_len ), pdo_conn_options_ht ); + dsn_parser->parse_conn_string( TSRMLS_C ); + + // Extract the server name + temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); + + CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_pdo_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) { + + throw pdo::PDOException(); + } + + server_z = *temp_server_z; + + // Add a reference to the option value since we are deleting it from the hashtable + zval_add_ref( &server_z ); + zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER ); + + sqlsrv_conn* conn = core_sqlsrv_connect( *g_pdo_henv_cp, *g_pdo_henv_ncp, core::allocate_conn, Z_STRVAL( server_z ), + dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error, + PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC ); + + // Free the string in server_z after being used + zend_string_release( Z_STR( server_z )); + + SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." ); + + // set the driver_data and methods to complete creation of the PDO object + dbh->driver_data = conn; + dbh->error_mode = prev_err_mode; // reset the error mode + dbh->alloc_own_columns = 1; // we do our own memory management for columns + dbh->native_case = PDO_CASE_NATURAL;// SQL Server supports mixed case types + + } + catch( core::CoreException& ) { + + if ( Z_TYPE( server_z ) == IS_STRING ) { + zend_string_release( Z_STR( server_z )); + } + dbh->error_mode = prev_err_mode; // reset the error mode + g_pdo_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL + + return 0; + } + catch( ... ) { + + DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" ); + } + + return 1; +} + +// pdo_sqlsrv_dbh_close +// Maps to PDO::__destruct. +// Called when a PDO object is to be destroyed. +// By the time this function is called, PDO has already made sure that +// all statements are disposed and the PDO object is the last item destroyed. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// Always returns 1 for success. +int pdo_sqlsrv_dbh_close( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + LOG( SEV_NOTICE, "pdo_sqlsrv_dbh_close: entering" ); + + // if the connection didn't complete properly, driver_data isn't initialized. + if( dbh->driver_data == NULL ) { + + return 1; + } + + PDO_RESET_DBH_ERROR; + + // call the core layer close + core_sqlsrv_close( reinterpret_cast( dbh->driver_data ) TSRMLS_CC ); + dbh->driver_data = NULL; + + // always return success that the connection is closed + return 1; +} + +// pdo_sqlsrv_dbh_prepare +// Called by PDO::prepare and PDOStatement::__construct. +// Creates a statement and prepares it for execution by PDO +// Paramters: +// dbh - The PDO managed connection object. +// sql - SQL query to be prepared. +// sql_len - Length of the sql query +// stmt - The PDO managed statement object. +// driver_options - User provided list of statement options. +// Return: +// 0 for failure, 1 for success. +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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + hash_auto_ptr pdo_stmt_options_ht; + sqlsrv_malloc_auto_ptr sql_rewrite; + size_t sql_rewrite_len = 0; + sqlsrv_malloc_auto_ptr driver_stmt; + hash_auto_ptr placeholders; + sqlsrv_malloc_auto_ptr sql_parser; + + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( dbh->driver_data ); + SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null"); + + try { + + // assign the methods for the statement object. This is necessary even if the + // statement fails so the user can retrieve the error information. + stmt->methods = &pdo_sqlsrv_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names + + // Initialize the options array to be passed to the core layer + ALLOC_HASHTABLE( pdo_stmt_options_ht ); + core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, + 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. + validate_stmt_options( *driver_dbh, driver_options, pdo_stmt_options_ht TSRMLS_CC ); + + driver_stmt = static_cast( core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, + pdo_stmt_options_ht, PDO_STMT_OPTS, + pdo_sqlsrv_handle_stmt_error, stmt TSRMLS_CC )); + + // if the user didn't set anything in the prepare options, then set the buffer limit + // to the value set on the connection. + if( driver_stmt->buffered_query_limit== sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) { + + driver_stmt->buffered_query_limit = driver_dbh->client_buffer_max_size; + } + + // if the user didn't set anything in the prepare options, then set the query timeout + // to the value set on the connection. + 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 ); + } + + // rewrite named parameters in the query to positional parameters if we aren't letting PDO do the + // parameter substitution for us + if( stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { + + // rewrite the query to map named parameters to positional parameters. We do this rather than use the ODBC named + // parameters for consistency with the PDO MySQL and PDO ODBC drivers. + int zr = pdo_parse_params( stmt, const_cast( sql ), sql_len, &sql_rewrite, &sql_rewrite_len TSRMLS_CC ); + + CHECK_ZEND_ERROR( zr, driver_dbh, PDO_SQLSRV_ERROR_PARAM_PARSE) { + throw core::CoreException(); + } + // if parameter substitution happened, use that query instead of the original + if( sql_rewrite != 0) { + sql = sql_rewrite; + sql_len = sql_rewrite_len; + } + } + + if( !driver_stmt->direct_query && stmt->supports_placeholders != PDO_PLACEHOLDER_NONE ) { + + core_sqlsrv_prepare( driver_stmt, sql, sql_len TSRMLS_CC ); + } + else if( driver_stmt->direct_query ) { + + if( driver_stmt->direct_query_subst_string ) { + // we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme + // and we use estrdup below to allocate the new string, which uses emalloc + efree( reinterpret_cast( const_cast( driver_stmt->direct_query_subst_string ))); + } + driver_stmt->direct_query_subst_string = estrdup( sql ); + 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 + // set to the substituted query + if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) { + // parse placeholders in the sql query into the placeholders ht + ALLOC_HASHTABLE( placeholders ); + 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, + static_cast(stmt->query_stringlen), placeholders ); + sql_parser->parse_sql_string( TSRMLS_C ); + driver_stmt->placeholders = placeholders; + placeholders.transferred(); + } + + stmt->driver_data = driver_stmt; + driver_stmt.transferred(); + } + // everything is cleaned up by this point + // catch everything so the exception doesn't spill into the calling PDO code + catch( core::CoreException& ) { + + if( driver_stmt ) { + + driver_stmt->~pdo_sqlsrv_stmt(); + } + + // in the event that the statement caused an error that was copied to the connection, update the + // connection with the error's SQLSTATE. + if( driver_dbh->last_error() ) { + + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_dbh->last_error()->sqlstate )); + } + + return 0; + } + + // catch any errant exception and die + catch(...) { + + DIE( "pdo_sqlsrv_dbh_prepare: Unknown exception caught." ); + } + + return 1; +} + + +// pdo_sqlsrv_dbh_do +// Maps to PDO::exec. +// Execute a SQL statement, such as an insert, update or delete, and return +// the number of rows affected. +// Parameters: +// dbh - the PDO connection object, which contains the ODBC handle +// sql - the query to execute +// sql_len - length of sql query +// Return +// # of rows affected, -1 for an error. +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_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + + sqlsrv_malloc_auto_ptr driver_stmt; + SQLLEN rows = 0; + + // verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong + // thing to happen here. + SQLSRV_STATIC_ASSERT( sizeof( rows ) == sizeof( SQLLEN )); + + try { + + SQLSRV_ASSERT( sql != NULL, "NULL or empty SQL string passed." ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_do: driver_data object was NULL."); + + // temp PDO statement used for error handling if something happens + pdo_stmt_t temp_stmt; + temp_stmt.dbh = dbh; + // allocate a full driver statement to take advantage of the error handling + driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, + NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); + driver_stmt->set_func( __FUNCTION__ ); + + SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast( 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 + // 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 )) { + + SQLRETURN r = SQL_SUCCESS; + + do { + + rows = core::SQLRowCount( driver_stmt TSRMLS_CC ); + + r = core::SQLMoreResults( driver_stmt TSRMLS_CC ); + + } while ( r != SQL_NO_DATA ); + } + + // returning -1 forces PDO to return false, which signals an error occurred. SQLRowCount returns -1 for a number of cases + // naturally, so we override that here with no rows returned. + if( rows == -1 ) { + rows = 0; + } + } + catch( core::CoreException& ) { + + // copy any errors on the statement to the connection so that the user sees them, since the statement is released + // before this method returns + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_stmt->last_error()->sqlstate )); + driver_dbh->set_last_error( driver_stmt->last_error() ); + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + return -1; + } + catch( ... ) { + + DIE( "pdo_sqlsrv_dbh_do: Unknown exception caught." ); + } + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + return rows; +} + + +// transaction support functions + +// pdo_sqlsrv_dbh_begin +// Maps to PDO::beginTransaction. +// Begins a transaction. Turns off auto-commit mode. The pdo_dbh_t::in_txn +// flag is maintained by PDO so we dont have to worry about it. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_begin( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_begin: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_begin: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( !dbh->in_txn, "pdo_sqlsrv_dbh_begin: Already in transaction" ); + + core_sqlsrv_begin_transaction( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_begin: Uncaught exception occurred."); + } +} + + + +// pdo_sqlsrv_dbh_commit +// Maps to PDO::commit. +// Commits a transaction. Returns the connection to auto-commit mode. +// PDO throws error if PDO::commit is called on a connection that is not in an active +// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have +// to worry about it here. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_commit( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_commit: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_commit: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_commit: Not in transaction" ); + + core_sqlsrv_commit( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_commit: Uncaught exception occurred."); + } +} + +// pdo_sqlsrv_dbh_rollback +// Maps to PDO::rollback. +// Rolls back a transaction. Returns the connection in auto-commit mode. +// PDO throws error if PDO::rollBack is called on a connection that is not in an active +// transaction. The pdo_dbh_t::in_txn flag is maintained by PDO so we dont have +// to worry about it here. +// Parameters: +// dbh - The PDO managed connection object. +// Return: +// 0 for failure and 1 for success. +int pdo_sqlsrv_dbh_rollback( _Inout_ pdo_dbh_t *dbh TSRMLS_DC ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + try { + SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_dbh_rollback: pdo_dbh_t object was null" ); + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + + SQLSRV_ASSERT( driver_conn != NULL, "pdo_sqlsrv_dbh_rollback: driver_data object was null" ); + + DEBUG_SQLSRV_ASSERT( dbh->in_txn, "pdo_sqlsrv_dbh_rollback: Not in transaction" ); + + core_sqlsrv_rollback( driver_conn TSRMLS_CC ); + + return 1; + } + catch( core::CoreException& ) { + + return 0; + } + catch( ... ) { + + DIE ("pdo_sqlsrv_dbh_rollback: Uncaught exception occurred."); + } +} + +// pdo_sqlsrv_dbh_set_attr +// Maps to PDO::setAttribute. Sets an attribute on the PDO connection object. +// PDO driver manager calls this function directly after calling the factory +// method for PDO, for any attribute which is specified in the PDO constructor. +// Parameters: +// dbh - The PDO connection object maintained by PDO. +// attr - The attribute to be set. +// val - The value of the attribute to be set. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_set_attr: driver_data object was NULL."); + + try { + + switch( attr ) { + + case SQLSRV_ATTR_ENCODING: + { + zend_long attr_value; + if( Z_TYPE_P( val ) != IS_LONG ) { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); + } + attr_value = Z_LVAL_P( val ); + switch( attr_value ) { + + case SQLSRV_ENCODING_DEFAULT: + // when default is applied to a connection, that means use UTF-8 encoding + driver_dbh->set_encoding( SQLSRV_ENCODING_UTF8 ); + break; + case SQLSRV_ENCODING_SYSTEM: + case SQLSRV_ENCODING_UTF8: + driver_dbh->set_encoding( static_cast( attr_value )); + break; + default: + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING ); + break; + } + } + break; + + case SQLSRV_ATTR_DIRECT_QUERY: + driver_dbh->direct_query = ( zend_is_true( val ) ) ? true : false; + break; + + case SQLSRV_ATTR_QUERY_TIMEOUT: + if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) < 0 ) { + convert_to_string( val ); + THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val )); + } + driver_dbh->query_timeout = static_cast( Z_LVAL_P( val ) ); + break; + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + if( Z_TYPE_P( val ) != IS_LONG || Z_LVAL_P( val ) <= 0 ) { + convert_to_string( val ); + THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, Z_STRVAL_P( val )); + } + driver_dbh->client_buffer_max_size = Z_LVAL_P( val ); + break; + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false; + break; + + // Not supported + case PDO_ATTR_FETCH_TABLE_NAMES: + case PDO_ATTR_FETCH_CATALOG_NAMES: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_MAX_COLUMN_LEN: + case PDO_ATTR_CURSOR_NAME: + case PDO_ATTR_AUTOCOMMIT: + case PDO_ATTR_PERSISTENT: + case PDO_ATTR_TIMEOUT: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); + } + + // Read-only + case PDO_ATTR_SERVER_VERSION: + case PDO_ATTR_SERVER_INFO: + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_DRIVER_NAME: + case PDO_ATTR_CONNECTION_STATUS: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ); + } + + // Statement level only + case PDO_ATTR_EMULATE_PREPARES: + case PDO_ATTR_CURSOR: + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); + } + + default: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); + break; + } + } + } + catch( pdo::PDOException& ) { + + return 0; + } + + return 1; +} + + +// pdo_sqlsrv_dbh_get_attr +// Maps to PDO::getAttribute. Gets an attribute on the PDO connection object. +// Parameters: +// dbh - The PDO connection object maintained by PDO. +// attr - The attribute to get. +// return_value - zval in which to return the attribute value. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_get_attr: driver_data object was NULL."); + + try { + + switch( attr ) { + + // Not supported + case PDO_ATTR_FETCH_TABLE_NAMES: + case PDO_ATTR_FETCH_CATALOG_NAMES: + case PDO_ATTR_PREFETCH: + case PDO_ATTR_MAX_COLUMN_LEN: + case PDO_ATTR_CURSOR_NAME: + case PDO_ATTR_AUTOCOMMIT: + case PDO_ATTR_TIMEOUT: + { + // PDO does not throw "not supported" error message for these attributes. + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ); + } + + // Statement level only + case PDO_ATTR_EMULATE_PREPARES: + case PDO_ATTR_CURSOR: + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ); + } + + case PDO_ATTR_STRINGIFY_FETCHES: + { + // For this attribute, if we dont set the return_value than PDO returns NULL. + ZVAL_BOOL(return_value, ( dbh->stringify ? 1 : 0 ) ); + break; + } + + case PDO_ATTR_SERVER_INFO: + { + core_sqlsrv_get_server_info( driver_dbh, return_value TSRMLS_CC ); + break; + } + + case PDO_ATTR_SERVER_VERSION: + { + core_sqlsrv_get_server_version( driver_dbh, return_value TSRMLS_CC ); + break; + } + + case PDO_ATTR_CLIENT_VERSION: + { + core_sqlsrv_get_client_info( driver_dbh, return_value TSRMLS_CC ); + + //Add the PDO SQLSRV driver's file version + //Declarations below eliminate compiler warnings about string constant to char* conversions + const char* extver = "ExtensionVer"; + std::string filever = VER_FILEVERSION_STR; + core::sqlsrv_add_assoc_string( *driver_dbh, return_value, extver, &filever[0], 1 /*duplicate*/ + TSRMLS_CC ); + break; + } + + case SQLSRV_ATTR_ENCODING: + { + ZVAL_LONG( return_value, driver_dbh->encoding() ); + break; + } + + case SQLSRV_ATTR_QUERY_TIMEOUT: + { + ZVAL_LONG( return_value, ( driver_dbh->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_dbh->query_timeout )); + break; + } + + case SQLSRV_ATTR_DIRECT_QUERY: + { + ZVAL_BOOL( return_value, driver_dbh->direct_query ); + break; + } + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + { + ZVAL_LONG( return_value, driver_dbh->client_buffer_max_size ); + break; + } + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + { + ZVAL_BOOL( return_value, driver_dbh->fetch_numeric ); + break; + } + + default: + { + THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ); + break; + } + } + + return 1; + } + catch( core::CoreException& ) { + return 0; + } +} + +// Called by PDO::errorInfo and PDOStatement::errorInfo. +// Returns the error info. +// Parameters: +// dbh - The PDO managed connection object. +// stmt - The PDO managed statement object. +// info - zval in which to return the error info. +// Return: +// 0 for failure, 1 for success. +int pdo_sqlsrv_dbh_return_error( _In_ pdo_dbh_t *dbh, _In_opt_ pdo_stmt_t *stmt, + _Out_ zval *info TSRMLS_DC) +{ + SQLSRV_ASSERT( dbh != NULL || stmt != NULL, "Either dbh or stmt must not be NULL to dereference the error." ); + + sqlsrv_error* ctx_error = NULL; + if( stmt ) { + ctx_error = static_cast( stmt->driver_data )->last_error(); + } + else { + ctx_error = static_cast( dbh->driver_data )->last_error(); + } + + pdo_sqlsrv_retrieve_context_error( ctx_error, info ); + + return 1; +} + +// pdo_sqlsrv_dbh_last_id +// Maps to PDO::lastInsertId. +// Returns the last id generated by an executed SQL statement +// Parameters: +// dbh - The PDO managed connection object. +// name - Table name. +// len - Length of the name. +// Return: +// 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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + // turn off any error handling for last_id + pdo_error_mode prev_err_mode = dbh->error_mode; + dbh->error_mode = PDO_ERRMODE_SILENT; + + sqlsrv_malloc_auto_ptr driver_stmt; + + pdo_sqlsrv_dbh* driver_dbh = static_cast( dbh->driver_data ); + SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." ); + + sqlsrv_malloc_auto_ptr id_str; + id_str = reinterpret_cast( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN )); + + try { + + char last_insert_id_query[ LAST_INSERT_ID_QUERY_MAX_LEN ]; + if( name == NULL ) { + strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY ); + } + else { + char* quoted_table = NULL; + size_t quoted_len = 0; + int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), "ed_table, "ed_len, PDO_PARAM_NULL TSRMLS_CC ); + SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name."); + snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table ); + sqlsrv_free( quoted_table ); + } + + // temp PDO statement used for error handling if something happens + pdo_stmt_t temp_stmt; + temp_stmt.dbh = dbh; + + // allocate a full driver statement to take advantage of the error handling + driver_stmt = core_sqlsrv_create_stmt( driver_dbh, core::allocate_stmt, NULL /*options_ht*/, NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC ); + driver_stmt->set_func( __FUNCTION__ ); + + + sqlsrv_malloc_auto_ptr wsql_string; + unsigned int wsql_len; + wsql_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_CHAR, reinterpret_cast( 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() ) { + throw core::CoreException(); + } + + // execute the last insert id query + core::SQLExecDirectW( driver_stmt, wsql_string 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, + reinterpret_cast( len ), false TSRMLS_CC ); + + CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt, + PDO_SQLSRV_ERROR_LAST_INSERT_ID ) { + throw core::CoreException(); + } + + driver_stmt->~sqlsrv_stmt(); + } + catch( core::CoreException& ) { + + // copy any errors on the statement to the connection so that the user sees them, since the statement is released + // before this method returns + strcpy_s( dbh->error_code, sizeof( dbh->error_code ), + reinterpret_cast( driver_stmt->last_error()->sqlstate )); + driver_dbh->set_last_error( driver_stmt->last_error() ); + + if( driver_stmt ) { + driver_stmt->~sqlsrv_stmt(); + } + + strcpy_s( id_str.get(), 1, "" ); + *len = 0; + } + + char* ret_id_str = id_str.get(); + id_str.transferred(); + + // restore error handling to its previous mode + dbh->error_mode = prev_err_mode; + + return ret_id_str; +} + +// pdo_sqlsrv_dbh_quote +// Maps to PDO::quote. As the name says, this function quotes a string. +// Always returns a valid string unless memory allocation fails. +// Parameters: +// dbh - The PDO managed connection object. +// unquoted - The unquoted string to be quoted. +// unquoted_len - Length of the unquoted string. +// quoted - Buffer for output string. +// quoted_len - Length of the output string. +// Return: +// 0 for failure, 1 for success. +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 ) +{ + PDO_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR; + + // get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from: + // 1. PDO::quote() - object name is PDO + // 2. PDOStatement::execute() - object name is PDOStatement + zend_execute_data* execute_data = EG( current_execute_data ); + zval *object = getThis(); + + // iterate through parents to find "PDOStatement" + bool is_statement = false; + if ( object ) { + zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce; + while ( curr_class != NULL ) { + if ( strcmp( reinterpret_cast( curr_class->name->val ), "PDOStatement" ) == 0 ) { + is_statement = true; + break; + } + curr_class = curr_class->parent; + } + } + // only change the encoding if quote is called from the statement level (which should only be called when a statement + // is prepared with emulate prepared on) + if ( is_statement ) { + 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 + pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast( stmt->driver_data ); + if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) { + encoding = driver_stmt->encoding(); + } + else { + pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast( stmt->driver_data ); + encoding = driver_dbh->encoding(); + } + // get the placeholder at the current position in driver_stmt->placeholders ht + 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 ) { + pdo_bound_param_data* param = NULL; + if ( Z_TYPE_P( placeholder ) == IS_STRING ) { + param = reinterpret_cast( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder ))); + } + else if ( Z_TYPE_P( placeholder ) == IS_LONG) { + param = reinterpret_cast( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder ))); + } + if ( NULL != param ) { + SQLSRV_ENCODING param_encoding = static_cast( Z_LVAL( param->driver_params )); + if ( param_encoding != SQLSRV_ENCODING_INVALID ) { + encoding = param_encoding; + } + } + } + } + + if ( encoding == SQLSRV_ENCODING_BINARY ) { + // convert from char* to hex digits using os + std::basic_ostringstream os; + 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. + // return an empty terminated string for now + if (( int )unquoted[ index ] < 0 || ( int )unquoted[ index ] > 255) { + *quoted_len = 0; + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); + ( *quoted )[ 0 ] = '\0'; + return 1; + } + // 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) + // thus append '0' first + if (( int )unquoted[index] < 16 ) { + os << '0'; + } + os << std::hex << ( int )unquoted[ index ]; + } + std::basic_string str_hex = os.str(); + // 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 + char* unquoted_str = reinterpret_cast( 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() ); + // include length of '0x' in the binary string + *quoted_len = unquoted_str_len + 2; + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); + unsigned int out_current = 0; + // insert '0x' + ( *quoted )[ out_current++ ] = '0'; + ( *quoted )[ out_current++ ] = 'x'; + for ( size_t index = 0; index < unquoted_str_len && unquoted_str[ index ] != '\0'; ++index ) { + ( *quoted )[ out_current++ ] = unquoted_str[ index ]; + } + // null terminator + ( *quoted )[ out_current ] = '\0'; + sqlsrv_free( unquoted_str ); + return 1; + } + else { + // count the number of quotes needed + unsigned int quotes_needed = 2; // the initial start and end quotes of course + // include the N proceeding the initial quote if encoding is UTF8 + if ( encoding == SQLSRV_ENCODING_UTF8 ) { + quotes_needed = 3; + } + for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { + if ( unquoted[ index ] == '\'' ) { + ++quotes_needed; + } + } + + *quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator. + *quoted = reinterpret_cast( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator. + unsigned int out_current = 0; + + // insert N if the encoding is UTF8 + if ( encoding == SQLSRV_ENCODING_UTF8 ) { + ( *quoted )[ out_current++ ] = 'N'; + } + // insert initial quote + ( *quoted )[ out_current++ ] = '\''; + + for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) { + if ( unquoted[ index ] == '\'' ) { + ( *quoted )[ out_current++ ] = '\''; + ( *quoted )[ out_current++ ] = '\''; + } + else { + ( *quoted )[ out_current++ ] = unquoted[ index ]; + } + } + + // trailing quote and null terminator + ( *quoted )[ out_current++ ] = '\''; + ( *quoted )[ out_current ] = '\0'; + + return 1; + } +} + +// 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_RESET_DBH_ERROR; + PDO_VALIDATE_CONN; + PDO_LOG_DBH_ENTRY; + + sqlsrv_conn* driver_conn = reinterpret_cast( dbh->driver_data ); + 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 ) { + return NULL; + } + + return NULL; // to avoid a compiler warning +} + +namespace { + +// Maps the PDO driver specific statement option/attribute constants to the core layer +// statement option/attribute constants. +void add_stmt_option_key( _Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ HashTable* options_ht, + _Inout_ zval* data TSRMLS_DC ) +{ + zend_ulong option_key = -1; + switch( key ) { + + case PDO_ATTR_CURSOR: + option_key = SQLSRV_STMT_OPTION_SCROLLABLE; + break; + + case SQLSRV_ATTR_ENCODING: + option_key = PDO_STMT_OPTION_ENCODING; + break; + + case SQLSRV_ATTR_QUERY_TIMEOUT: + option_key = SQLSRV_STMT_OPTION_QUERY_TIMEOUT; + break; + + case PDO_ATTR_STATEMENT_CLASS: + break; + + case SQLSRV_ATTR_DIRECT_QUERY: + option_key = PDO_STMT_OPTION_DIRECT_QUERY; + break; + + case SQLSRV_ATTR_CURSOR_SCROLL_TYPE: + option_key = PDO_STMT_OPTION_CURSOR_SCROLL_TYPE; + break; + + case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE: + option_key = PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE; + break; + + case PDO_ATTR_EMULATE_PREPARES: + option_key = PDO_STMT_OPTION_EMULATE_PREPARES; + break; + + case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE: + option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE; + break; + + default: + CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { + throw core::CoreException(); + } + break; + } + + // if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it + if( option_key != -1 ) { + zval_add_ref( data ); + core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC ); + } +} + + +// validate_stmt_options +// Iterates through the list of statement options provided by the user and validates them +// against the list of statement options provided by this driver. After validation +// creates a Hashtable of statement options to be sent to the core layer for processing. +// Parameters: +// ctx - The current context. +// stmt_options - The user provided list of statement options. +// pdo_stmt_options_ht - Output hashtable of statement options. +void validate_stmt_options( _Inout_ sqlsrv_context& ctx, _Inout_ zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC ) +{ + try { + + if( stmt_options ) { + + HashTable* options_ht = Z_ARRVAL_P( stmt_options ); + size_t int_key = -1; + zend_string *key = NULL; + zval* data = NULL; + + ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) { + int type = HASH_KEY_NON_EXISTENT; + int result = 0; + type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; + CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) { + throw core::CoreException(); + } + + add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); + } ZEND_HASH_FOREACH_END(); + } + } + catch( core::CoreException& ) { + + 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 ) +{ + TSRMLS_C; + char const* val_str = "no"; + + if( core_str_zval_is_true( value ) ) { + + val_str = "yes"; + } + + conn_str += option->odbc_name; + conn_str += "={"; + conn_str += val_str; + conn_str += "};"; +} + +void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, + std::string& /*conn_str*/ TSRMLS_DC ) +{ + try { + + SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." ); + const char* val = Z_STRVAL_P( value_z ); + size_t val_len = Z_STRLEN_P( value_z ); + zend_long out_val = SQL_TXN_READ_COMMITTED; + + // READ_COMMITTED + if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::READ_COMMITTED ))) { + + out_val = SQL_TXN_READ_COMMITTED; + } + + // READ_UNCOMMITTED + else if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_UNCOMMITTED ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::READ_UNCOMMITTED ))) { + + out_val = SQL_TXN_READ_UNCOMMITTED; + } + + // REPEATABLE_READ + else if(( val_len == ( sizeof( PDOTxnIsolationValues::REPEATABLE_READ ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::REPEATABLE_READ ))) { + + out_val = SQL_TXN_REPEATABLE_READ; + } + + // SERIALIZABLE + else if(( val_len == ( sizeof( PDOTxnIsolationValues::SERIALIZABLE ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::SERIALIZABLE ))) { + + out_val = SQL_TXN_SERIALIZABLE; + } + + // SNAPSHOT + else if(( val_len == ( sizeof( PDOTxnIsolationValues::SNAPSHOT ) - 1 ) + && !strcasecmp( val, PDOTxnIsolationValues::SNAPSHOT ))) { + + out_val = SQL_TXN_SS_SNAPSHOT; + } + + else { + + CHECK_CUSTOM_ERROR( true, conn, PDO_SQLSRV_ERROR_INVALID_DSN_VALUE, PDOConnOptionNames::TransactionIsolation ) { + + throw core::CoreException(); + } + } + + core::SQLSetConnectAttr( conn, SQL_COPT_SS_TXN_ISOLATION, reinterpret_cast( out_val ), SQL_IS_UINTEGER TSRMLS_CC ); + + } + catch( core::CoreException& ) { + + throw; + } +} + +} // namespace diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index fe7a7972..5b8dd8b9 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -1,60 +1,60 @@ ---TEST-- -Provide name in lastInsertId to retrieve the last sequence number ---SKIPIF-- ---FILE-- -getAttribute(PDO::ATTR_SERVER_VERSION)); - if ($version_arr[0] < 11) { - echo "Done\n"; - } - else { - $tableName1 = GetTempTableName('tab1'); - $tableName2 = GetTempTableName('tab2'); - $sequenceName = 'sequence1'; - - $stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName"); - $sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; - $stmt = $conn->query($sql); - $sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))"; - $stmt = $conn->query($sql); - - $sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE"; - $stmt = $conn->query($sql); - - $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )"); - $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )"); - $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )"); - $ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )"); - // return the last sequence number is sequence name is provided - $lastSeq = $conn->lastInsertId($sequenceName); - // defaults to $tableName2 -- because it returns the last inserted id value - $lastRow = $conn->lastInsertId(); - - if ($lastSeq == 3 && $lastRow == 1) { - echo "Done\n"; - } - else { - echo "sequence value or identity does not match as expected\n"; - } - $stmt = $conn->query("DROP TABLE $tableName1"); - $stmt = $conn->query("DROP TABLE $tableName2"); - $stmt = $conn->query("DROP SEQUENCE $sequenceName"); - $stmt = null; - } - $conn = null; -} -catch (Exception $e){ - echo "Exception $e\n"; -} - -?> ---EXPECT-- +--TEST-- +Provide name in lastInsertId to retrieve the last sequence number +--SKIPIF-- +--FILE-- +getAttribute(PDO::ATTR_SERVER_VERSION)); + if ($version_arr[0] < 11) { + echo "Done\n"; + } + else { + $tableName1 = GetTempTableName('tab1'); + $tableName2 = GetTempTableName('tab2'); + $sequenceName = 'sequence1'; + + $stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName"); + $sql = "CREATE TABLE $tableName1 (seqnum INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; + $stmt = $conn->query($sql); + $sql = "CREATE TABLE $tableName2 (ID INT IDENTITY(1,2), SomeValue char(10))"; + $stmt = $conn->query($sql); + + $sql = "CREATE SEQUENCE $sequenceName AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100 CYCLE"; + $stmt = $conn->query($sql); + + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 20 )"); + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 40 )"); + $ret = $conn->exec("INSERT INTO $tableName1 VALUES( NEXT VALUE FOR $sequenceName, 60 )"); + $ret = $conn->exec("INSERT INTO $tableName2 VALUES( '20' )"); + // return the last sequence number is sequence name is provided + $lastSeq = $conn->lastInsertId($sequenceName); + // defaults to $tableName2 -- because it returns the last inserted id value + $lastRow = $conn->lastInsertId(); + + if ($lastSeq == 3 && $lastRow == 1) { + echo "Done\n"; + } + else { + echo "sequence value or identity does not match as expected\n"; + } + $stmt = $conn->query("DROP TABLE $tableName1"); + $stmt = $conn->query("DROP TABLE $tableName2"); + $stmt = $conn->query("DROP SEQUENCE $sequenceName"); + $stmt = null; + } + $conn = null; +} +catch (Exception $e){ + echo "Exception $e\n"; +} + +?> +--EXPECT-- Done \ No newline at end of file diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt index 08cb7b92..75ae1687 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt @@ -1,62 +1,62 @@ ---TEST-- -LastInsertId returns the last sequences operating on the same table ---SKIPIF-- ---FILE-- -getAttribute(PDO::ATTR_SERVER_VERSION)); - if ($version_arr[0] < 11) { - echo "Done\n"; - } - else { - $tableName = GetTempTableName('tab'); - $sequence1 = 'sequence1'; - $sequence2 = 'sequenceNeg1'; - $stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1"); - $stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2"); - $sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; - $stmt = $conn->query($sql); - $sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100"; - $stmt = $conn->query($sql); - - $sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200"; - $stmt = $conn->query($sql); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )"); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )"); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )"); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )"); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )"); - $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )"); - // return the last sequence number of 'sequence1' - $lastSeq1 = $conn->lastInsertId($sequence1); - - // return the last sequence number of 'sequenceNeg1' - $lastSeq2 = $conn->lastInsertId($sequence2); - - // providing a table name in lastInsertId should return an empty string - $lastSeq3 = $conn->lastInsertId($tableName); - - if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") { - echo "Done\n"; - } - $stmt = $conn->query("DROP TABLE $tableName"); - $stmt = $conn->query("DROP SEQUENCE $sequence1"); - $stmt = $conn->query("DROP SEQUENCE $sequence2"); - $stmt = null; - } - $conn = null; -} - catch (Exception $e){ - echo "Exception $e\n"; -} - -?> ---EXPECT-- +--TEST-- +LastInsertId returns the last sequences operating on the same table +--SKIPIF-- +--FILE-- +getAttribute(PDO::ATTR_SERVER_VERSION)); + if ($version_arr[0] < 11) { + echo "Done\n"; + } + else { + $tableName = GetTempTableName('tab'); + $sequence1 = 'sequence1'; + $sequence2 = 'sequenceNeg1'; + $stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1"); + $stmt = $conn->query("IF OBJECT_ID('$sequence2', 'SO') IS NOT NULL DROP SEQUENCE $sequence2"); + $sql = "CREATE TABLE $tableName (ID INT IDENTITY(1,1), SeqNumInc INTEGER NOT NULL PRIMARY KEY, SomeNumber INT)"; + $stmt = $conn->query($sql); + $sql = "CREATE SEQUENCE $sequence1 AS INTEGER START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 100"; + $stmt = $conn->query($sql); + + $sql = "CREATE SEQUENCE $sequence2 AS INTEGER START WITH 200 INCREMENT BY -1 MINVALUE 101 MAXVALUE 200"; + $stmt = $conn->query($sql); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 20 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 180 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 40 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 160 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence1, 60 )"); + $ret = $conn->exec("INSERT INTO $tableName VALUES( NEXT VALUE FOR $sequence2, 140 )"); + // return the last sequence number of 'sequence1' + $lastSeq1 = $conn->lastInsertId($sequence1); + + // return the last sequence number of 'sequenceNeg1' + $lastSeq2 = $conn->lastInsertId($sequence2); + + // providing a table name in lastInsertId should return an empty string + $lastSeq3 = $conn->lastInsertId($tableName); + + if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") { + echo "Done\n"; + } + $stmt = $conn->query("DROP TABLE $tableName"); + $stmt = $conn->query("DROP SEQUENCE $sequence1"); + $stmt = $conn->query("DROP SEQUENCE $sequence2"); + $stmt = null; + } + $conn = null; +} + catch (Exception $e){ + echo "Exception $e\n"; +} + +?> +--EXPECT-- Done \ No newline at end of file From 9817f4d598ed33cab7490cdf0bbf02faab34eaf3 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 14:43:28 -0700 Subject: [PATCH 04/10] Tests fixed, version updated --- source/shared/version.h | 116 +++++++++--------- .../pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 6 +- .../pdo_278_lastinsertid_seq_2.phpt | 6 +- 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/source/shared/version.h b/source/shared/version.h index ba205053..b26accd7 100644 --- a/source/shared/version.h +++ b/source/shared/version.h @@ -1,58 +1,58 @@ -#ifndef VERSION_H -#define VERSION_H -//--------------------------------------------------------------------------------------------------------------------------------- -// File: version.h -// Contents: Version number constants -// -// Microsoft Drivers 4.3 for PHP for SQL Server -// Copyright(c) Microsoft Corporation -// All rights reserved. -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//--------------------------------------------------------------------------------------------------------------------------------- - -// helper macros to stringify the a macro value -#define STRINGIFY(a) TOSTRING(a) -#define TOSTRING(a) #a - - -// Increase Major number with backward incompatible breaking changes. -// Increase Minor with backward compatible new functionalities and API changes. -// Increase Patch for backward compatible fixes. -#define SQLVERSION_MAJOR 4 -#define SQLVERSION_MINOR 3 -#define SQLVERSION_PATCH 0 -#define SQLVERSION_BUILD 0 - -// Semantic versioning pre-release -// for stable releases should be empty -// "-RC" for release candidates -// "-preview" for ETP -#define SEMVER_PRERELEASE -// Semantic versioning build metadata, build meta data is not counted in precedence order. -#define SEMVER_BUILDMETA - -#if SQLVERSION_BUILD > 0 -#undef SEMVER_BUILDMETA -#define SEMVER_BUILDMETA "+" STRINGIFY( SQLVERSION_BUILD ) -#endif - -// Main version, dot separated 3 digits, Major.Minor.Patch -#define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH ) - -// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases) -#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA -#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD - -// PECL package version macros (can't have '-' or '+') -#define PHP_SQLSRV_VERSION VER_APIVERSION_STR SEMVER_PRERELEASE -#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION - -#endif // VERSION_H +#ifndef VERSION_H +#define VERSION_H +//--------------------------------------------------------------------------------------------------------------------------------- +// File: version.h +// Contents: Version number constants +// +// Microsoft Drivers 4.3 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + +// helper macros to stringify the a macro value +#define STRINGIFY(a) TOSTRING(a) +#define TOSTRING(a) #a + + +// Increase Major number with backward incompatible breaking changes. +// Increase Minor with backward compatible new functionalities and API changes. +// Increase Patch for backward compatible fixes. +#define SQLVERSION_MAJOR 5 +#define SQLVERSION_MINOR 0 +#define SQLVERSION_PATCH 0 +#define SQLVERSION_BUILD 0 + +// Semantic versioning pre-release +// for stable releases should be empty +// "-RC" for release candidates +// "-preview" for ETP +#define SEMVER_PRERELEASE +// Semantic versioning build metadata, build meta data is not counted in precedence order. +#define SEMVER_BUILDMETA + +#if SQLVERSION_BUILD > 0 +#undef SEMVER_BUILDMETA +#define SEMVER_BUILDMETA "+" STRINGIFY( SQLVERSION_BUILD ) +#endif + +// Main version, dot separated 3 digits, Major.Minor.Patch +#define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH ) + +// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases) +#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA +#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD + +// PECL package version macros (can't have '-' or '+') +#define PHP_SQLSRV_VERSION VER_APIVERSION_STR SEMVER_PRERELEASE +#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION + +#endif // VERSION_H diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index 5b8dd8b9..1d60fb61 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -3,11 +3,11 @@ Provide name in lastInsertId to retrieve the last sequence number --SKIPIF-- --FILE-- Date: Wed, 19 Jul 2017 15:13:23 -0700 Subject: [PATCH 05/10] Tests fixed --- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 2 +- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index 1d60fb61..b0503733 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -7,7 +7,7 @@ require_once("MsSetup.inc"); try{ $database = "tempdb"; - $conn = new PDO("sqlsrv:Server=$serverName;Database=$databaseName", $username, $password); + $conn = new PDO("sqlsrv:Server=$serverName;Database=$databaseName", $uid, $pwd); // sequence is only supported in SQL server 2012 and up (or version 11 and up) // Output Done once the server version is found to be < 11 diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt index cbc0c25d..3689a78a 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt @@ -7,7 +7,7 @@ require_once("MsSetup.inc"); try{ $database = "tempdb"; - $conn = new PDO("sqlsrv:Server=$server;Database=$databaseName", $username, $password); + $conn = new PDO("sqlsrv:Server=$server;Database=$databaseName", $uid, $pwd); // sequence is only supported in SQL server 2012 and up (or version 11 and up) // Output Done once the server version is found to be < 11 From 1904877d8ccc87fa381efef1f5d2c4f7e6c8f146 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 15:55:57 -0700 Subject: [PATCH 06/10] Tests fixed --- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 1 + test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt | 1 + 2 files changed, 2 insertions(+) diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index b0503733..daf69dfe 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -3,6 +3,7 @@ Provide name in lastInsertId to retrieve the last sequence number --SKIPIF-- --FILE-- Date: Wed, 19 Jul 2017 16:08:20 -0700 Subject: [PATCH 07/10] Tests fixed --- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index daf69dfe..ae10db95 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -8,7 +8,7 @@ require_once("MsSetup.inc"); try{ $database = "tempdb"; - $conn = new PDO("sqlsrv:Server=$serverName;Database=$databaseName", $uid, $pwd); + $conn = new PDO("sqlsrv:Server=$server;Database=$databaseName", $uid, $pwd); // sequence is only supported in SQL server 2012 and up (or version 11 and up) // Output Done once the server version is found to be < 11 From c1e7280dc6c6f30af4a952a342fa086c677372b7 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Thu, 20 Jul 2017 11:44:46 -0700 Subject: [PATCH 08/10] Version updated --- source/pdo_sqlsrv/config.w32 | 2 +- source/pdo_sqlsrv/pdo_dbh.cpp | 2 +- source/pdo_sqlsrv/pdo_init.cpp | 2 +- source/pdo_sqlsrv/pdo_parser.cpp | 2 +- source/pdo_sqlsrv/pdo_stmt.cpp | 2 +- source/pdo_sqlsrv/pdo_util.cpp | 2 +- source/pdo_sqlsrv/php_pdo_sqlsrv.h | 2 +- source/pdo_sqlsrv/template.rc | 2 +- source/shared/FormattedPrint.cpp | 2 +- source/shared/FormattedPrint.h | 2 +- source/shared/StringFunctions.cpp | 2 +- source/shared/StringFunctions.h | 2 +- source/shared/core_conn.cpp | 2 +- source/shared/core_init.cpp | 2 +- source/shared/core_results.cpp | 2 +- source/shared/core_sqlsrv.h | 2 +- source/shared/core_stmt.cpp | 2 +- source/shared/core_stream.cpp | 2 +- source/shared/core_util.cpp | 2 +- source/shared/globalization.h | 2 +- source/shared/interlockedatomic.h | 2 +- source/shared/interlockedatomic_gcc.h | 2 +- source/shared/interlockedslist.h | 2 +- source/shared/localization.hpp | 2 +- source/shared/localizationimpl.cpp | 2 +- source/shared/msodbcsql.h | 810 +++++++++--------- source/shared/sal_def.h | 2 +- source/shared/typedefs_for_linux.h | 2 +- source/shared/version.h | 116 +-- source/shared/xplat.h | 2 +- source/shared/xplat_intsafe.h | 2 +- source/shared/xplat_winerror.h | 2 +- source/shared/xplat_winnls.h | 2 +- source/sqlsrv/config.w32 | 2 +- source/sqlsrv/conn.cpp | 2 +- source/sqlsrv/init.cpp | 2 +- source/sqlsrv/php_sqlsrv.h | 2 +- source/sqlsrv/stmt.cpp | 2 +- source/sqlsrv/template.rc | 2 +- source/sqlsrv/util.cpp | 2 +- test/functional/pdo_sqlsrv/MsSetup.inc | 2 +- .../pdo_278_lastinsertid_seq_2.phpt | 1 + 42 files changed, 503 insertions(+), 502 deletions(-) diff --git a/source/pdo_sqlsrv/config.w32 b/source/pdo_sqlsrv/config.w32 index dea793d4..14eb7205 100644 --- a/source/pdo_sqlsrv/config.w32 +++ b/source/pdo_sqlsrv/config.w32 @@ -3,7 +3,7 @@ // // Contents: JScript build configuration used by buildconf.bat // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/pdo_dbh.cpp b/source/pdo_sqlsrv/pdo_dbh.cpp index 6dcd7c76..81352cfe 100644 --- a/source/pdo_sqlsrv/pdo_dbh.cpp +++ b/source/pdo_sqlsrv/pdo_dbh.cpp @@ -3,7 +3,7 @@ // // Contents: Implements the PDO object for PDO_SQLSRV // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/pdo_init.cpp b/source/pdo_sqlsrv/pdo_init.cpp index c8076e11..afa47b3c 100644 --- a/source/pdo_sqlsrv/pdo_init.cpp +++ b/source/pdo_sqlsrv/pdo_init.cpp @@ -3,7 +3,7 @@ // // Contents: initialization routines for PDO_SQLSRV // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/pdo_parser.cpp b/source/pdo_sqlsrv/pdo_parser.cpp index 56292a31..e7194160 100644 --- a/source/pdo_sqlsrv/pdo_parser.cpp +++ b/source/pdo_sqlsrv/pdo_parser.cpp @@ -5,7 +5,7 @@ // // Copyright Microsoft Corporation // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/pdo_stmt.cpp b/source/pdo_sqlsrv/pdo_stmt.cpp index 7edb17dc..85d726fd 100644 --- a/source/pdo_sqlsrv/pdo_stmt.cpp +++ b/source/pdo_sqlsrv/pdo_stmt.cpp @@ -3,7 +3,7 @@ // // Contents: Implements the PDOStatement object for the PDO_SQLSRV // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/pdo_util.cpp b/source/pdo_sqlsrv/pdo_util.cpp index 3e05ff1c..3f08c0b3 100644 --- a/source/pdo_sqlsrv/pdo_util.cpp +++ b/source/pdo_sqlsrv/pdo_util.cpp @@ -3,7 +3,7 @@ // // Contents: Utility functions used by both connection or statement functions // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/php_pdo_sqlsrv.h b/source/pdo_sqlsrv/php_pdo_sqlsrv.h index 36b0eddf..73ca9ac2 100644 --- a/source/pdo_sqlsrv/php_pdo_sqlsrv.h +++ b/source/pdo_sqlsrv/php_pdo_sqlsrv.h @@ -6,7 +6,7 @@ // // Contents: Declarations for the extension // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/pdo_sqlsrv/template.rc b/source/pdo_sqlsrv/template.rc index 63326055..9ccfc835 100644 --- a/source/pdo_sqlsrv/template.rc +++ b/source/pdo_sqlsrv/template.rc @@ -3,7 +3,7 @@ // // Contents: Version resource // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/FormattedPrint.cpp b/source/shared/FormattedPrint.cpp index ba5520bd..5ddfdfa6 100644 --- a/source/shared/FormattedPrint.cpp +++ b/source/shared/FormattedPrint.cpp @@ -6,7 +6,7 @@ // Contents: Contains functions for handling Windows format strings // and UTF-16 on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/FormattedPrint.h b/source/shared/FormattedPrint.h index d789afa7..c5ee154e 100644 --- a/source/shared/FormattedPrint.h +++ b/source/shared/FormattedPrint.h @@ -4,7 +4,7 @@ // Contents: Contains functions for handling Windows format strings // and UTF-16 on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/StringFunctions.cpp b/source/shared/StringFunctions.cpp index ca2ec89b..d8e1caf2 100644 --- a/source/shared/StringFunctions.cpp +++ b/source/shared/StringFunctions.cpp @@ -3,7 +3,7 @@ // // Contents: Contains functions for handling UTF-16 on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/StringFunctions.h b/source/shared/StringFunctions.h index 323d308a..1cd765cf 100644 --- a/source/shared/StringFunctions.h +++ b/source/shared/StringFunctions.h @@ -3,7 +3,7 @@ // // Contents: Contains functions for handling UTF-16 on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index 4b488b16..0cc17a64 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -3,7 +3,7 @@ // // Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_init.cpp b/source/shared/core_init.cpp index 89f75a84..29bb9ec7 100644 --- a/source/shared/core_init.cpp +++ b/source/shared/core_init.cpp @@ -3,7 +3,7 @@ // // Contents: common initialization routines shared by PDO and sqlsrv // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_results.cpp b/source/shared/core_results.cpp index e28cbbed..2130875c 100644 --- a/source/shared/core_results.cpp +++ b/source/shared/core_results.cpp @@ -3,7 +3,7 @@ // // Contents: Result sets // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 6f904f72..8bc799c6 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -6,7 +6,7 @@ // // Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 54c44fe5..349dbfc0 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -3,7 +3,7 @@ // // Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_stream.cpp b/source/shared/core_stream.cpp index b56e58ca..c19b14f9 100644 --- a/source/shared/core_stream.cpp +++ b/source/shared/core_stream.cpp @@ -3,7 +3,7 @@ // // Contents: Implementation of PHP streams for reading SQL Server data // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/core_util.cpp b/source/shared/core_util.cpp index 61f66bdb..da64d42a 100644 --- a/source/shared/core_util.cpp +++ b/source/shared/core_util.cpp @@ -5,7 +5,7 @@ // // Comments: Mostly error handling and some type handling // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/globalization.h b/source/shared/globalization.h index cd8b744f..0781d607 100644 --- a/source/shared/globalization.h +++ b/source/shared/globalization.h @@ -4,7 +4,7 @@ // Contents: Contains functions for handling Windows format strings // and UTF-16 on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/interlockedatomic.h b/source/shared/interlockedatomic.h index 30941ee0..2642e5a5 100644 --- a/source/shared/interlockedatomic.h +++ b/source/shared/interlockedatomic.h @@ -4,7 +4,7 @@ // Contents: Contains a portable abstraction for interlocked, atomic // operations on int32_t and pointer types. // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/interlockedatomic_gcc.h b/source/shared/interlockedatomic_gcc.h index e4e4205b..8f54ad46 100644 --- a/source/shared/interlockedatomic_gcc.h +++ b/source/shared/interlockedatomic_gcc.h @@ -4,7 +4,7 @@ // Contents: Contains a portable abstraction for interlocked, atomic // operations on int32_t and pointer types. // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/interlockedslist.h b/source/shared/interlockedslist.h index 0a262aeb..1af301b0 100644 --- a/source/shared/interlockedslist.h +++ b/source/shared/interlockedslist.h @@ -4,7 +4,7 @@ // Contents: Contains a portable abstraction for interlocked, singly // linked list. // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/localization.hpp b/source/shared/localization.hpp index a5310650..c2709a46 100644 --- a/source/shared/localization.hpp +++ b/source/shared/localization.hpp @@ -3,7 +3,7 @@ // // Contents: Contains portable classes for localization // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/localizationimpl.cpp b/source/shared/localizationimpl.cpp index 381b9cc5..59a58fa9 100644 --- a/source/shared/localizationimpl.cpp +++ b/source/shared/localizationimpl.cpp @@ -5,7 +5,7 @@ // Must be included in one c/cpp file per binary // A build error will occur if this inclusion policy is not followed // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/msodbcsql.h b/source/shared/msodbcsql.h index e2a4bf0d..9c6e0d41 100644 --- a/source/shared/msodbcsql.h +++ b/source/shared/msodbcsql.h @@ -1,405 +1,405 @@ -#ifndef __msodbcsql_h__ -#define __msodbcsql_h__ - -//--------------------------------------------------------------------------------------------------------------------------------- -// File: msodbcsql.h -// -// Contents: Routines that use statement handles. This is a subset of the header file msodbcsql.h in the ODBC Driver. -// -// Contents: This SDK is not supported under any Microsoft standard support -// program or service. The information is provided AS IS without -// warranty of any kind. Microsoft disclaims all implied -// warranties including, without limitation, any implied -// warranties of merchantability or of fitness for a particular -// purpose. The entire risk arising out of the use of this SDK -// remains with you. In no event shall Microsoft, its authors, or -// anyone else involved in the creation, production, or delivery -// of this SDK be liable for any damages whatsoever (including, -// without limitation, damages for loss of business profits, -// business interruption, loss of business information, or other -// pecuniary loss) arising out of the use of or inability to use -// this SDK, even if Microsoft has been advised of the possibility -// of such damages. -// Microsoft Drivers 4.3 for PHP for SQL Server -// Copyright(c) Microsoft Corporation -// All rights reserved. -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//--------------------------------------------------------------------------------------------------------------------------------- - - -#if !defined(SQLODBC_VER) -#define SQLODBC_VER 1300 -#endif - -#if SQLODBC_VER >= 1300 - -#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 13 for SQL Server" -#define SQLODBC_PRODUCT_NAME_FULL_ANSI "Microsoft ODBC Driver for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 13 for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_ANSI "ODBC Driver for SQL Server" - -#endif // SQLODBC_VER >= 1300 - -#define SQLODBC_PRODUCT_NAME_FULL_VER SQLODBC_PRODUCT_NAME_FULL_VER_ANSI -#define SQLODBC_PRODUCT_NAME_FULL SQLODBC_PRODUCT_NAME_FULL_ANSI -#define SQLODBC_PRODUCT_NAME_SHORT_VER SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI -#define SQLODBC_PRODUCT_NAME_SHORT SQLODBC_PRODUCT_NAME_SHORT_ANSI - -#define SQLODBC_DRIVER_NAME SQLODBC_PRODUCT_NAME_SHORT_VER - -// max SQL Server identifier length -#define SQL_MAX_SQLSERVERNAME 128 - - -// SQLSetConnectAttr driver specific defines. -// Microsoft has 1200 thru 1249 reserved for Microsoft ODBC Driver for SQL Server usage. -// Connection attributes - -#define SQL_COPT_SS_BASE 1200 -#define SQL_COPT_SS_INTEGRATED_SECURITY (SQL_COPT_SS_BASE+3) // Force integrated security on login -#define SQL_COPT_SS_TRANSLATE (SQL_COPT_SS_BASE+20) // Perform code page translation -#define SQL_COPT_SS_ENCRYPT (SQL_COPT_SS_BASE+23) // Allow strong encryption for data -#define SQL_COPT_SS_MARS_ENABLED (SQL_COPT_SS_BASE+24) // Multiple active result set per connection -#define SQL_COPT_SS_TXN_ISOLATION (SQL_COPT_SS_BASE+27) // Used to set/get any driver-specific or ODBC-defined TXN iso level -#define SQL_COPT_SS_TRUST_SERVER_CERTIFICATE (SQL_COPT_SS_BASE+28) // Trust server certificate - -// SQLSetStmtAttr Microsoft ODBC Driver for SQL Server specific defines. -// Statement attributes - -#define SQL_SOPT_SS_BASE 1225 -#define SQL_SOPT_SS_TEXTPTR_LOGGING (SQL_SOPT_SS_BASE+0) // Text pointer logging -#define SQL_SOPT_SS_NOBROWSETABLE (SQL_SOPT_SS_BASE+3) // Set NOBROWSETABLE option -#define SQL_SOPT_SS_COLUMN_ENCRYPTION (SQL_SOPT_SS_BASE+13)// Sets the column encryption mode -// Define old names -#define SQL_TEXTPTR_LOGGING SQL_SOPT_SS_TEXTPTR_LOGGING -#define SQL_COPT_SS_BASE_EX 1240 -#define SQL_COPT_SS_WARN_ON_CP_ERROR (SQL_COPT_SS_BASE_EX+3) // Issues warning when data from the server had a loss during code page conversion. -#define SQL_COPT_SS_CONNECTION_DEAD (SQL_COPT_SS_BASE_EX+4) // dbdead SQLGetConnectOption only. It will try to ping the server. Expensive connection check -#define SQL_COPT_SS_APPLICATION_INTENT (SQL_COPT_SS_BASE_EX+7) // Application Intent -#define SQL_COPT_SS_MULTISUBNET_FAILOVER (SQL_COPT_SS_BASE_EX+8) // Multi-subnet Failover -#define SQL_COPT_SS_TNIR (SQL_COPT_SS_BASE_EX+9) // Transparent Network IP Resolution -#define SQL_COPT_SS_COLUMN_ENCRYPTION (SQL_COPT_SS_BASE_EX+10)// Column Encryption Enabled or Disabled -#define SQL_COPT_SS_CEKEYSTOREPROVIDER (SQL_COPT_SS_BASE_EX+11)// Load a keystore provider or read the list of loaded keystore providers -#define SQL_COPT_SS_CEKEYSTOREDATA (SQL_COPT_SS_BASE_EX+12)// Communicate with loaded keystore providers -#define SQL_COPT_SS_TRUSTEDCMKPATHS (SQL_COPT_SS_BASE_EX+13)// List of trusted CMK paths -#define SQL_COPT_SS_CEKCACHETTL (SQL_COPT_SS_BASE_EX+14)// Symmetric Key Cache TTL -#define SQL_COPT_SS_AUTHENTICATION (SQL_COPT_SS_BASE_EX+15)// The authentication method used for the connection - -// SQLColAttributes driver specific defines. -// SQLSetDescField/SQLGetDescField driver specific defines. -// Microsoft has 1200 thru 1249 reserved for Microsoft ODBC Driver for SQL Server usage. - -#define SQL_CA_SS_BASE 1200 -#define SQL_CA_SS_COLUMN_SSTYPE (SQL_CA_SS_BASE+0) // dbcoltype/dbalttype -#define SQL_CA_SS_COLUMN_UTYPE (SQL_CA_SS_BASE+1) // dbcolutype/dbaltutype -#define SQL_CA_SS_NUM_ORDERS (SQL_CA_SS_BASE+2) // dbnumorders -#define SQL_CA_SS_COLUMN_ORDER (SQL_CA_SS_BASE+3) // dbordercol -#define SQL_CA_SS_COLUMN_VARYLEN (SQL_CA_SS_BASE+4) // dbvarylen -#define SQL_CA_SS_NUM_COMPUTES (SQL_CA_SS_BASE+5) // dbnumcompute -#define SQL_CA_SS_COMPUTE_ID (SQL_CA_SS_BASE+6) // dbnextrow status return -#define SQL_CA_SS_COMPUTE_BYLIST (SQL_CA_SS_BASE+7) // dbbylist -#define SQL_CA_SS_COLUMN_ID (SQL_CA_SS_BASE+8) // dbaltcolid -#define SQL_CA_SS_COLUMN_OP (SQL_CA_SS_BASE+9) // dbaltop -#define SQL_CA_SS_COLUMN_SIZE (SQL_CA_SS_BASE+10) // dbcollen -#define SQL_CA_SS_COLUMN_HIDDEN (SQL_CA_SS_BASE+11) // Column is hidden (FOR BROWSE) -#define SQL_CA_SS_COLUMN_KEY (SQL_CA_SS_BASE+12) // Column is key column (FOR BROWSE) -#define SQL_CA_SS_COLUMN_COLLATION (SQL_CA_SS_BASE+14) // Column collation (only for chars) -#define SQL_CA_SS_VARIANT_TYPE (SQL_CA_SS_BASE+15) -#define SQL_CA_SS_VARIANT_SQL_TYPE (SQL_CA_SS_BASE+16) -#define SQL_CA_SS_VARIANT_SERVER_TYPE (SQL_CA_SS_BASE+17) - -// XML, CLR UDT, and table valued parameter related metadata -#define SQL_CA_SS_UDT_CATALOG_NAME (SQL_CA_SS_BASE+18) // UDT catalog name -#define SQL_CA_SS_UDT_SCHEMA_NAME (SQL_CA_SS_BASE+19) // UDT schema name -#define SQL_CA_SS_UDT_TYPE_NAME (SQL_CA_SS_BASE+20) // UDT type name -#define SQL_CA_SS_XML_SCHEMACOLLECTION_CATALOG_NAME (SQL_CA_SS_BASE+22) // Name of the catalog that contains XML Schema collection -#define SQL_CA_SS_XML_SCHEMACOLLECTION_SCHEMA_NAME (SQL_CA_SS_BASE+23) // Name of the schema that contains XML Schema collection -#define SQL_CA_SS_XML_SCHEMACOLLECTION_NAME (SQL_CA_SS_BASE+24) // Name of the XML Schema collection -#define SQL_CA_SS_CATALOG_NAME (SQL_CA_SS_BASE+25) // Catalog name -#define SQL_CA_SS_SCHEMA_NAME (SQL_CA_SS_BASE+26) // Schema name -#define SQL_CA_SS_TYPE_NAME (SQL_CA_SS_BASE+27) // Type name - -// table valued parameter related metadata -#define SQL_CA_SS_COLUMN_COMPUTED (SQL_CA_SS_BASE+29) // column is computed -#define SQL_CA_SS_COLUMN_IN_UNIQUE_KEY (SQL_CA_SS_BASE+30) // column is part of a unique key -#define SQL_CA_SS_COLUMN_SORT_ORDER (SQL_CA_SS_BASE+31) // column sort order -#define SQL_CA_SS_COLUMN_SORT_ORDINAL (SQL_CA_SS_BASE+32) // column sort ordinal -#define SQL_CA_SS_COLUMN_HAS_DEFAULT_VALUE (SQL_CA_SS_BASE+33) // column has default value for all rows of the table valued parameter - -// sparse column related metadata -#define SQL_CA_SS_IS_COLUMN_SET (SQL_CA_SS_BASE+34) // column is a column-set column for sparse columns - -// Legacy datetime related metadata -#define SQL_CA_SS_SERVER_TYPE (SQL_CA_SS_BASE+35) // column type to send on the wire for datetime types - -// force column encryption -#define SQL_CA_SS_FORCE_ENCRYPT (SQL_CA_SS_BASE+36) // indicate mandatory encryption for this parameter - -#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+37) -// Defines for use with SQL_COPT_SS_INTEGRATED_SECURITY - Pre-Connect Option only -#define SQL_IS_OFF 0L // Integrated security isn't used -#define SQL_IS_ON 1L // Integrated security is used -#define SQL_IS_DEFAULT SQL_IS_OFF -// Defines for use with SQL_COPT_SS_TRANSLATE -#define SQL_XL_OFF 0L // Code page translation is not performed -#define SQL_XL_ON 1L // Code page translation is performed -#define SQL_XL_DEFAULT SQL_XL_ON -// Defines for use with SQL_SOPT_SS_TEXTPTR_LOGGING -#define SQL_TL_OFF 0L // No logging on text pointer ops -#define SQL_TL_ON 1L // Logging occurs on text pointer ops -#define SQL_TL_DEFAULT SQL_TL_ON -// Defines for use with SQL_SOPT_SS_NOBROWSETABLE -#define SQL_NB_OFF 0L // NO_BROWSETABLE is off -#define SQL_NB_ON 1L // NO_BROWSETABLE is on -#define SQL_NB_DEFAULT SQL_NB_OFF -// Defines for use with SQL_SOPT_SS_COLUMN_ENCRYPTION -#define SQL_CE_DISABLED 0L // Disabled -#define SQL_CE_RESULTSETONLY 1L // Decryption Only (resultsets and return values) -#define SQL_CE_ENABLED 3L // Enabled (both encryption and decryption) - // Defines for use with SQL_COPT_SS_COLUMN_ENCRYPTION -#define SQL_COLUMN_ENCRYPTION_DISABLE 0L -#define SQL_COLUMN_ENCRYPTION_ENABLE 1L -#define SQL_COLUMN_ENCRYPTION_DEFAULT SQL_COLUMN_ENCRYPTION_DISABLE - // Defines for use with SQL_COPT_SS_CEKCACHETTL -#define SQL_CEKCACHETTL_DEFAULT 7200L // TTL value in seconds (2 hours) -// SQL_COPT_SS_ENCRYPT -#define SQL_EN_OFF 0L -#define SQL_EN_ON 1L -// SQL_COPT_SS_TRUST_SERVER_CERTIFICATE -#define SQL_TRUST_SERVER_CERTIFICATE_NO 0L -#define SQL_TRUST_SERVER_CERTIFICATE_YES 1L -// SQL_COPT_SS_WARN_ON_CP_ERROR -#define SQL_WARN_NO 0L -#define SQL_WARN_YES 1L -// SQL_COPT_SS_MARS_ENABLED -#define SQL_MARS_ENABLED_NO 0L -#define SQL_MARS_ENABLED_YES 1L -// SQL_TXN_ISOLATION_OPTION bitmasks -#define SQL_TXN_SS_SNAPSHOT 0x00000020L - -// The following are defines for SQL_CA_SS_COLUMN_SORT_ORDER -#define SQL_SS_ORDER_UNSPECIFIED 0L -#define SQL_SS_DESCENDING_ORDER 1L -#define SQL_SS_ASCENDING_ORDER 2L -#define SQL_SS_ORDER_DEFAULT SQL_SS_ORDER_UNSPECIFIED - -// Driver specific SQL data type defines. -// Microsoft has -150 thru -199 reserved for Microsoft ODBC Driver for SQL Server usage. - -#define SQL_SS_VARIANT (-150) -#define SQL_SS_UDT (-151) -#define SQL_SS_XML (-152) -#define SQL_SS_TABLE (-153) -#define SQL_SS_TIME2 (-154) -#define SQL_SS_TIMESTAMPOFFSET (-155) - -// Local types to be used with SQL_CA_SS_SERVER_TYPE -#define SQL_SS_TYPE_DEFAULT 0L -#define SQL_SS_TYPE_SMALLDATETIME 1L -#define SQL_SS_TYPE_DATETIME 2L - -// Extended C Types range 4000 and above. Range of -100 thru 200 is reserved by Driver Manager. -#define SQL_C_TYPES_EXTENDED 0x04000L - -// SQL_SS_LENGTH_UNLIMITED is used to describe the max length of -// VARCHAR(max), VARBINARY(max), NVARCHAR(max), and XML columns - -#define SQL_SS_LENGTH_UNLIMITED 0 - -// User Data Type definitions. -// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_UTYPE. - -#define SQLudtBINARY 3 -#define SQLudtBIT 16 -#define SQLudtBITN 0 -#define SQLudtCHAR 1 -#define SQLudtDATETIM4 22 -#define SQLudtDATETIME 12 -#define SQLudtDATETIMN 15 -#define SQLudtDECML 24 -#define SQLudtDECMLN 26 -#define SQLudtFLT4 23 -#define SQLudtFLT8 8 -#define SQLudtFLTN 14 -#define SQLudtIMAGE 20 -#define SQLudtINT1 5 -#define SQLudtINT2 6 -#define SQLudtINT4 7 -#define SQLudtINTN 13 -#define SQLudtMONEY 11 -#define SQLudtMONEY4 21 -#define SQLudtMONEYN 17 -#define SQLudtNUM 10 -#define SQLudtNUMN 25 -#define SQLudtSYSNAME 18 -#define SQLudtTEXT 19 -#define SQLudtTIMESTAMP 80 -#define SQLudtUNIQUEIDENTIFIER 0 -#define SQLudtVARBINARY 4 -#define SQLudtVARCHAR 2 -#define MIN_USER_DATATYPE 256 - -// Aggregate operator types. -// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_OP. - -#define SQLAOPSTDEV 0x30 // Standard deviation -#define SQLAOPSTDEVP 0x31 // Standard deviation population -#define SQLAOPVAR 0x32 // Variance -#define SQLAOPVARP 0x33 // Variance population -#define SQLAOPCNT 0x4b // Count -#define SQLAOPSUM 0x4d // Sum -#define SQLAOPAVG 0x4f // Average -#define SQLAOPMIN 0x51 // Min -#define SQLAOPMAX 0x52 // Max -#define SQLAOPANY 0x53 // Any -#define SQLAOPNOOP 0x56 // None - -// SQLGetDiagField driver specific defines. -// Microsoft has -1150 thru -1199 reserved for Microsoft ODBC Driver for SQL Server usage. - -#define SQL_DIAG_SS_BASE (-1150) -#define SQL_DIAG_SS_MSGSTATE (SQL_DIAG_SS_BASE) -#define SQL_DIAG_SS_SEVERITY (SQL_DIAG_SS_BASE-1) -#define SQL_DIAG_SS_SRVNAME (SQL_DIAG_SS_BASE-2) -#define SQL_DIAG_SS_PROCNAME (SQL_DIAG_SS_BASE-3) -#define SQL_DIAG_SS_LINE (SQL_DIAG_SS_BASE-4) - -// SQLGetDiagField/SQL_DIAG_DYNAMIC_FUNCTION_CODE driver specific defines. -// Microsoft has -200 thru -299 reserved for Microsoft ODBC Driver for SQL Server usage. - -#define SQL_DIAG_DFC_SS_BASE (-200) -#define SQL_DIAG_DFC_SS_ALTER_DATABASE (SQL_DIAG_DFC_SS_BASE-0) -#define SQL_DIAG_DFC_SS_CHECKPOINT (SQL_DIAG_DFC_SS_BASE-1) -#define SQL_DIAG_DFC_SS_CONDITION (SQL_DIAG_DFC_SS_BASE-2) -#define SQL_DIAG_DFC_SS_CREATE_DATABASE (SQL_DIAG_DFC_SS_BASE-3) -#define SQL_DIAG_DFC_SS_CREATE_DEFAULT (SQL_DIAG_DFC_SS_BASE-4) -#define SQL_DIAG_DFC_SS_CREATE_PROCEDURE (SQL_DIAG_DFC_SS_BASE-5) -#define SQL_DIAG_DFC_SS_CREATE_RULE (SQL_DIAG_DFC_SS_BASE-6) -#define SQL_DIAG_DFC_SS_CREATE_TRIGGER (SQL_DIAG_DFC_SS_BASE-7) -#define SQL_DIAG_DFC_SS_CURSOR_DECLARE (SQL_DIAG_DFC_SS_BASE-8) -#define SQL_DIAG_DFC_SS_CURSOR_OPEN (SQL_DIAG_DFC_SS_BASE-9) -#define SQL_DIAG_DFC_SS_CURSOR_FETCH (SQL_DIAG_DFC_SS_BASE-10) -#define SQL_DIAG_DFC_SS_CURSOR_CLOSE (SQL_DIAG_DFC_SS_BASE-11) -#define SQL_DIAG_DFC_SS_DEALLOCATE_CURSOR (SQL_DIAG_DFC_SS_BASE-12) -#define SQL_DIAG_DFC_SS_DBCC (SQL_DIAG_DFC_SS_BASE-13) -#define SQL_DIAG_DFC_SS_DISK (SQL_DIAG_DFC_SS_BASE-14) -#define SQL_DIAG_DFC_SS_DROP_DATABASE (SQL_DIAG_DFC_SS_BASE-15) -#define SQL_DIAG_DFC_SS_DROP_DEFAULT (SQL_DIAG_DFC_SS_BASE-16) -#define SQL_DIAG_DFC_SS_DROP_PROCEDURE (SQL_DIAG_DFC_SS_BASE-17) -#define SQL_DIAG_DFC_SS_DROP_RULE (SQL_DIAG_DFC_SS_BASE-18) -#define SQL_DIAG_DFC_SS_DROP_TRIGGER (SQL_DIAG_DFC_SS_BASE-19) -#define SQL_DIAG_DFC_SS_DUMP_DATABASE (SQL_DIAG_DFC_SS_BASE-20) -#define SQL_DIAG_DFC_SS_BACKUP_DATABASE (SQL_DIAG_DFC_SS_BASE-20) -#define SQL_DIAG_DFC_SS_DUMP_TABLE (SQL_DIAG_DFC_SS_BASE-21) -#define SQL_DIAG_DFC_SS_DUMP_TRANSACTION (SQL_DIAG_DFC_SS_BASE-22) -#define SQL_DIAG_DFC_SS_BACKUP_TRANSACTION (SQL_DIAG_DFC_SS_BASE-22) -#define SQL_DIAG_DFC_SS_GOTO (SQL_DIAG_DFC_SS_BASE-23) -#define SQL_DIAG_DFC_SS_INSERT_BULK (SQL_DIAG_DFC_SS_BASE-24) -#define SQL_DIAG_DFC_SS_KILL (SQL_DIAG_DFC_SS_BASE-25) -#define SQL_DIAG_DFC_SS_LOAD_DATABASE (SQL_DIAG_DFC_SS_BASE-26) -#define SQL_DIAG_DFC_SS_RESTORE_DATABASE (SQL_DIAG_DFC_SS_BASE-26) -#define SQL_DIAG_DFC_SS_LOAD_HEADERONLY (SQL_DIAG_DFC_SS_BASE-27) -#define SQL_DIAG_DFC_SS_RESTORE_HEADERONLY (SQL_DIAG_DFC_SS_BASE-27) -#define SQL_DIAG_DFC_SS_LOAD_TABLE (SQL_DIAG_DFC_SS_BASE-28) -#define SQL_DIAG_DFC_SS_LOAD_TRANSACTION (SQL_DIAG_DFC_SS_BASE-29) -#define SQL_DIAG_DFC_SS_RESTORE_TRANSACTION (SQL_DIAG_DFC_SS_BASE-29) -#define SQL_DIAG_DFC_SS_PRINT (SQL_DIAG_DFC_SS_BASE-30) -#define SQL_DIAG_DFC_SS_RAISERROR (SQL_DIAG_DFC_SS_BASE-31) -#define SQL_DIAG_DFC_SS_READTEXT (SQL_DIAG_DFC_SS_BASE-32) -#define SQL_DIAG_DFC_SS_RECONFIGURE (SQL_DIAG_DFC_SS_BASE-33) -#define SQL_DIAG_DFC_SS_RETURN (SQL_DIAG_DFC_SS_BASE-34) -#define SQL_DIAG_DFC_SS_SELECT_INTO (SQL_DIAG_DFC_SS_BASE-35) -#define SQL_DIAG_DFC_SS_SET (SQL_DIAG_DFC_SS_BASE-36) -#define SQL_DIAG_DFC_SS_SET_IDENTITY_INSERT (SQL_DIAG_DFC_SS_BASE-37) -#define SQL_DIAG_DFC_SS_SET_ROW_COUNT (SQL_DIAG_DFC_SS_BASE-38) -#define SQL_DIAG_DFC_SS_SET_STATISTICS (SQL_DIAG_DFC_SS_BASE-39) -#define SQL_DIAG_DFC_SS_SET_TEXTSIZE (SQL_DIAG_DFC_SS_BASE-40) -#define SQL_DIAG_DFC_SS_SETUSER (SQL_DIAG_DFC_SS_BASE-41) -#define SQL_DIAG_DFC_SS_SHUTDOWN (SQL_DIAG_DFC_SS_BASE-42) -#define SQL_DIAG_DFC_SS_TRANS_BEGIN (SQL_DIAG_DFC_SS_BASE-43) -#define SQL_DIAG_DFC_SS_TRANS_COMMIT (SQL_DIAG_DFC_SS_BASE-44) -#define SQL_DIAG_DFC_SS_TRANS_PREPARE (SQL_DIAG_DFC_SS_BASE-45) -#define SQL_DIAG_DFC_SS_TRANS_ROLLBACK (SQL_DIAG_DFC_SS_BASE-46) -#define SQL_DIAG_DFC_SS_TRANS_SAVE (SQL_DIAG_DFC_SS_BASE-47) -#define SQL_DIAG_DFC_SS_TRUNCATE_TABLE (SQL_DIAG_DFC_SS_BASE-48) -#define SQL_DIAG_DFC_SS_UPDATE_STATISTICS (SQL_DIAG_DFC_SS_BASE-49) -#define SQL_DIAG_DFC_SS_UPDATETEXT (SQL_DIAG_DFC_SS_BASE-50) -#define SQL_DIAG_DFC_SS_USE (SQL_DIAG_DFC_SS_BASE-51) -#define SQL_DIAG_DFC_SS_WAITFOR (SQL_DIAG_DFC_SS_BASE-52) -#define SQL_DIAG_DFC_SS_WRITETEXT (SQL_DIAG_DFC_SS_BASE-53) -#define SQL_DIAG_DFC_SS_DENY (SQL_DIAG_DFC_SS_BASE-54) -#define SQL_DIAG_DFC_SS_SET_XCTLVL (SQL_DIAG_DFC_SS_BASE-55) -#define SQL_DIAG_DFC_SS_MERGE (SQL_DIAG_DFC_SS_BASE-56) - -// Severity codes for SQL_DIAG_SS_SEVERITY -#define EX_ANY 0 -#define EX_INFO 10 -#define EX_MAXISEVERITY EX_INFO -#define EX_MISSING 11 -#define EX_TYPE 12 -#define EX_DEADLOCK 13 -#define EX_PERMIT 14 -#define EX_SYNTAX 15 -#define EX_USER 16 -#define EX_RESOURCE 17 -#define EX_INTOK 18 -#define MAXUSEVERITY EX_INTOK -#define EX_LIMIT 19 -#define EX_CMDFATAL 20 -#define MINFATALERR EX_CMDFATAL -#define EX_DBFATAL 21 -#define EX_TABCORRUPT 22 -#define EX_DBCORRUPT 23 -#define EX_HARDWARE 24 -#define EX_CONTROL 25 - -// Data is defined to be past the end of the structure header. -// This is accepted by MSVC, GCC, and C99 standard but former emits -// unnecessary warning, hence it has to be disabled. - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4200) -#endif - -// Communication between the driver and application via the CEKeystoreData structure -typedef struct CEKeystoreData -{ - wchar_t *name; - unsigned int dataSize; - char data[]; -} CEKEYSTOREDATA; - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -// The following constants are for the Azure Key Vault configuration interface -#define AKV_CONFIG_FLAGS 0 -#define AKVCFG_AUTHMODE 0x0000000F -#define AKVCFG_AUTHMODE_ACCESSTOKEN 0 -#define AKVCFG_AUTHMODE_CLIENTKEY 1 -#define AKVCFG_AUTHMODE_PASSWORD 2 -#define AKVCFG_AUTHMODE_INTEGRATED 3 -#define AKVCFG_AUTHMODE_CERTIFICATE 4 -#define AKVCFG_NOAUTORENEW 0x00000010 - -#define AKV_CONFIG_PRINCIPALID 1 -#define AKV_CONFIG_AUTHSECRET 2 -#define AKV_CONFIG_ACCESSTOKEN 3 -#define AKV_CONFIG_TOKENEXPIRY 4 -#define AKV_CONFIG_MAXRETRIES 5 -#define AKV_CONFIG_RETRYTIMEOUT 6 -#define AKV_CONFIG_RETRYWAIT 7 - -#define AKV_CONFIG_RESET 255 -#endif // __msodbcsql_h__ - +#ifndef __msodbcsql_h__ +#define __msodbcsql_h__ + +//--------------------------------------------------------------------------------------------------------------------------------- +// File: msodbcsql.h +// +// Contents: Routines that use statement handles. This is a subset of the header file msodbcsql.h in the ODBC Driver. +// +// Contents: This SDK is not supported under any Microsoft standard support +// program or service. The information is provided AS IS without +// warranty of any kind. Microsoft disclaims all implied +// warranties including, without limitation, any implied +// warranties of merchantability or of fitness for a particular +// purpose. The entire risk arising out of the use of this SDK +// remains with you. In no event shall Microsoft, its authors, or +// anyone else involved in the creation, production, or delivery +// of this SDK be liable for any damages whatsoever (including, +// without limitation, damages for loss of business profits, +// business interruption, loss of business information, or other +// pecuniary loss) arising out of the use of or inability to use +// this SDK, even if Microsoft has been advised of the possibility +// of such damages. +// Microsoft Drivers 5.0 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + + +#if !defined(SQLODBC_VER) +#define SQLODBC_VER 1300 +#endif + +#if SQLODBC_VER >= 1300 + +#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 13 for SQL Server" +#define SQLODBC_PRODUCT_NAME_FULL_ANSI "Microsoft ODBC Driver for SQL Server" +#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 13 for SQL Server" +#define SQLODBC_PRODUCT_NAME_SHORT_ANSI "ODBC Driver for SQL Server" + +#endif // SQLODBC_VER >= 1300 + +#define SQLODBC_PRODUCT_NAME_FULL_VER SQLODBC_PRODUCT_NAME_FULL_VER_ANSI +#define SQLODBC_PRODUCT_NAME_FULL SQLODBC_PRODUCT_NAME_FULL_ANSI +#define SQLODBC_PRODUCT_NAME_SHORT_VER SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI +#define SQLODBC_PRODUCT_NAME_SHORT SQLODBC_PRODUCT_NAME_SHORT_ANSI + +#define SQLODBC_DRIVER_NAME SQLODBC_PRODUCT_NAME_SHORT_VER + +// max SQL Server identifier length +#define SQL_MAX_SQLSERVERNAME 128 + + +// SQLSetConnectAttr driver specific defines. +// Microsoft has 1200 thru 1249 reserved for Microsoft ODBC Driver for SQL Server usage. +// Connection attributes + +#define SQL_COPT_SS_BASE 1200 +#define SQL_COPT_SS_INTEGRATED_SECURITY (SQL_COPT_SS_BASE+3) // Force integrated security on login +#define SQL_COPT_SS_TRANSLATE (SQL_COPT_SS_BASE+20) // Perform code page translation +#define SQL_COPT_SS_ENCRYPT (SQL_COPT_SS_BASE+23) // Allow strong encryption for data +#define SQL_COPT_SS_MARS_ENABLED (SQL_COPT_SS_BASE+24) // Multiple active result set per connection +#define SQL_COPT_SS_TXN_ISOLATION (SQL_COPT_SS_BASE+27) // Used to set/get any driver-specific or ODBC-defined TXN iso level +#define SQL_COPT_SS_TRUST_SERVER_CERTIFICATE (SQL_COPT_SS_BASE+28) // Trust server certificate + +// SQLSetStmtAttr Microsoft ODBC Driver for SQL Server specific defines. +// Statement attributes + +#define SQL_SOPT_SS_BASE 1225 +#define SQL_SOPT_SS_TEXTPTR_LOGGING (SQL_SOPT_SS_BASE+0) // Text pointer logging +#define SQL_SOPT_SS_NOBROWSETABLE (SQL_SOPT_SS_BASE+3) // Set NOBROWSETABLE option +#define SQL_SOPT_SS_COLUMN_ENCRYPTION (SQL_SOPT_SS_BASE+13)// Sets the column encryption mode +// Define old names +#define SQL_TEXTPTR_LOGGING SQL_SOPT_SS_TEXTPTR_LOGGING +#define SQL_COPT_SS_BASE_EX 1240 +#define SQL_COPT_SS_WARN_ON_CP_ERROR (SQL_COPT_SS_BASE_EX+3) // Issues warning when data from the server had a loss during code page conversion. +#define SQL_COPT_SS_CONNECTION_DEAD (SQL_COPT_SS_BASE_EX+4) // dbdead SQLGetConnectOption only. It will try to ping the server. Expensive connection check +#define SQL_COPT_SS_APPLICATION_INTENT (SQL_COPT_SS_BASE_EX+7) // Application Intent +#define SQL_COPT_SS_MULTISUBNET_FAILOVER (SQL_COPT_SS_BASE_EX+8) // Multi-subnet Failover +#define SQL_COPT_SS_TNIR (SQL_COPT_SS_BASE_EX+9) // Transparent Network IP Resolution +#define SQL_COPT_SS_COLUMN_ENCRYPTION (SQL_COPT_SS_BASE_EX+10)// Column Encryption Enabled or Disabled +#define SQL_COPT_SS_CEKEYSTOREPROVIDER (SQL_COPT_SS_BASE_EX+11)// Load a keystore provider or read the list of loaded keystore providers +#define SQL_COPT_SS_CEKEYSTOREDATA (SQL_COPT_SS_BASE_EX+12)// Communicate with loaded keystore providers +#define SQL_COPT_SS_TRUSTEDCMKPATHS (SQL_COPT_SS_BASE_EX+13)// List of trusted CMK paths +#define SQL_COPT_SS_CEKCACHETTL (SQL_COPT_SS_BASE_EX+14)// Symmetric Key Cache TTL +#define SQL_COPT_SS_AUTHENTICATION (SQL_COPT_SS_BASE_EX+15)// The authentication method used for the connection + +// SQLColAttributes driver specific defines. +// SQLSetDescField/SQLGetDescField driver specific defines. +// Microsoft has 1200 thru 1249 reserved for Microsoft ODBC Driver for SQL Server usage. + +#define SQL_CA_SS_BASE 1200 +#define SQL_CA_SS_COLUMN_SSTYPE (SQL_CA_SS_BASE+0) // dbcoltype/dbalttype +#define SQL_CA_SS_COLUMN_UTYPE (SQL_CA_SS_BASE+1) // dbcolutype/dbaltutype +#define SQL_CA_SS_NUM_ORDERS (SQL_CA_SS_BASE+2) // dbnumorders +#define SQL_CA_SS_COLUMN_ORDER (SQL_CA_SS_BASE+3) // dbordercol +#define SQL_CA_SS_COLUMN_VARYLEN (SQL_CA_SS_BASE+4) // dbvarylen +#define SQL_CA_SS_NUM_COMPUTES (SQL_CA_SS_BASE+5) // dbnumcompute +#define SQL_CA_SS_COMPUTE_ID (SQL_CA_SS_BASE+6) // dbnextrow status return +#define SQL_CA_SS_COMPUTE_BYLIST (SQL_CA_SS_BASE+7) // dbbylist +#define SQL_CA_SS_COLUMN_ID (SQL_CA_SS_BASE+8) // dbaltcolid +#define SQL_CA_SS_COLUMN_OP (SQL_CA_SS_BASE+9) // dbaltop +#define SQL_CA_SS_COLUMN_SIZE (SQL_CA_SS_BASE+10) // dbcollen +#define SQL_CA_SS_COLUMN_HIDDEN (SQL_CA_SS_BASE+11) // Column is hidden (FOR BROWSE) +#define SQL_CA_SS_COLUMN_KEY (SQL_CA_SS_BASE+12) // Column is key column (FOR BROWSE) +#define SQL_CA_SS_COLUMN_COLLATION (SQL_CA_SS_BASE+14) // Column collation (only for chars) +#define SQL_CA_SS_VARIANT_TYPE (SQL_CA_SS_BASE+15) +#define SQL_CA_SS_VARIANT_SQL_TYPE (SQL_CA_SS_BASE+16) +#define SQL_CA_SS_VARIANT_SERVER_TYPE (SQL_CA_SS_BASE+17) + +// XML, CLR UDT, and table valued parameter related metadata +#define SQL_CA_SS_UDT_CATALOG_NAME (SQL_CA_SS_BASE+18) // UDT catalog name +#define SQL_CA_SS_UDT_SCHEMA_NAME (SQL_CA_SS_BASE+19) // UDT schema name +#define SQL_CA_SS_UDT_TYPE_NAME (SQL_CA_SS_BASE+20) // UDT type name +#define SQL_CA_SS_XML_SCHEMACOLLECTION_CATALOG_NAME (SQL_CA_SS_BASE+22) // Name of the catalog that contains XML Schema collection +#define SQL_CA_SS_XML_SCHEMACOLLECTION_SCHEMA_NAME (SQL_CA_SS_BASE+23) // Name of the schema that contains XML Schema collection +#define SQL_CA_SS_XML_SCHEMACOLLECTION_NAME (SQL_CA_SS_BASE+24) // Name of the XML Schema collection +#define SQL_CA_SS_CATALOG_NAME (SQL_CA_SS_BASE+25) // Catalog name +#define SQL_CA_SS_SCHEMA_NAME (SQL_CA_SS_BASE+26) // Schema name +#define SQL_CA_SS_TYPE_NAME (SQL_CA_SS_BASE+27) // Type name + +// table valued parameter related metadata +#define SQL_CA_SS_COLUMN_COMPUTED (SQL_CA_SS_BASE+29) // column is computed +#define SQL_CA_SS_COLUMN_IN_UNIQUE_KEY (SQL_CA_SS_BASE+30) // column is part of a unique key +#define SQL_CA_SS_COLUMN_SORT_ORDER (SQL_CA_SS_BASE+31) // column sort order +#define SQL_CA_SS_COLUMN_SORT_ORDINAL (SQL_CA_SS_BASE+32) // column sort ordinal +#define SQL_CA_SS_COLUMN_HAS_DEFAULT_VALUE (SQL_CA_SS_BASE+33) // column has default value for all rows of the table valued parameter + +// sparse column related metadata +#define SQL_CA_SS_IS_COLUMN_SET (SQL_CA_SS_BASE+34) // column is a column-set column for sparse columns + +// Legacy datetime related metadata +#define SQL_CA_SS_SERVER_TYPE (SQL_CA_SS_BASE+35) // column type to send on the wire for datetime types + +// force column encryption +#define SQL_CA_SS_FORCE_ENCRYPT (SQL_CA_SS_BASE+36) // indicate mandatory encryption for this parameter + +#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+37) +// Defines for use with SQL_COPT_SS_INTEGRATED_SECURITY - Pre-Connect Option only +#define SQL_IS_OFF 0L // Integrated security isn't used +#define SQL_IS_ON 1L // Integrated security is used +#define SQL_IS_DEFAULT SQL_IS_OFF +// Defines for use with SQL_COPT_SS_TRANSLATE +#define SQL_XL_OFF 0L // Code page translation is not performed +#define SQL_XL_ON 1L // Code page translation is performed +#define SQL_XL_DEFAULT SQL_XL_ON +// Defines for use with SQL_SOPT_SS_TEXTPTR_LOGGING +#define SQL_TL_OFF 0L // No logging on text pointer ops +#define SQL_TL_ON 1L // Logging occurs on text pointer ops +#define SQL_TL_DEFAULT SQL_TL_ON +// Defines for use with SQL_SOPT_SS_NOBROWSETABLE +#define SQL_NB_OFF 0L // NO_BROWSETABLE is off +#define SQL_NB_ON 1L // NO_BROWSETABLE is on +#define SQL_NB_DEFAULT SQL_NB_OFF +// Defines for use with SQL_SOPT_SS_COLUMN_ENCRYPTION +#define SQL_CE_DISABLED 0L // Disabled +#define SQL_CE_RESULTSETONLY 1L // Decryption Only (resultsets and return values) +#define SQL_CE_ENABLED 3L // Enabled (both encryption and decryption) + // Defines for use with SQL_COPT_SS_COLUMN_ENCRYPTION +#define SQL_COLUMN_ENCRYPTION_DISABLE 0L +#define SQL_COLUMN_ENCRYPTION_ENABLE 1L +#define SQL_COLUMN_ENCRYPTION_DEFAULT SQL_COLUMN_ENCRYPTION_DISABLE + // Defines for use with SQL_COPT_SS_CEKCACHETTL +#define SQL_CEKCACHETTL_DEFAULT 7200L // TTL value in seconds (2 hours) +// SQL_COPT_SS_ENCRYPT +#define SQL_EN_OFF 0L +#define SQL_EN_ON 1L +// SQL_COPT_SS_TRUST_SERVER_CERTIFICATE +#define SQL_TRUST_SERVER_CERTIFICATE_NO 0L +#define SQL_TRUST_SERVER_CERTIFICATE_YES 1L +// SQL_COPT_SS_WARN_ON_CP_ERROR +#define SQL_WARN_NO 0L +#define SQL_WARN_YES 1L +// SQL_COPT_SS_MARS_ENABLED +#define SQL_MARS_ENABLED_NO 0L +#define SQL_MARS_ENABLED_YES 1L +// SQL_TXN_ISOLATION_OPTION bitmasks +#define SQL_TXN_SS_SNAPSHOT 0x00000020L + +// The following are defines for SQL_CA_SS_COLUMN_SORT_ORDER +#define SQL_SS_ORDER_UNSPECIFIED 0L +#define SQL_SS_DESCENDING_ORDER 1L +#define SQL_SS_ASCENDING_ORDER 2L +#define SQL_SS_ORDER_DEFAULT SQL_SS_ORDER_UNSPECIFIED + +// Driver specific SQL data type defines. +// Microsoft has -150 thru -199 reserved for Microsoft ODBC Driver for SQL Server usage. + +#define SQL_SS_VARIANT (-150) +#define SQL_SS_UDT (-151) +#define SQL_SS_XML (-152) +#define SQL_SS_TABLE (-153) +#define SQL_SS_TIME2 (-154) +#define SQL_SS_TIMESTAMPOFFSET (-155) + +// Local types to be used with SQL_CA_SS_SERVER_TYPE +#define SQL_SS_TYPE_DEFAULT 0L +#define SQL_SS_TYPE_SMALLDATETIME 1L +#define SQL_SS_TYPE_DATETIME 2L + +// Extended C Types range 4000 and above. Range of -100 thru 200 is reserved by Driver Manager. +#define SQL_C_TYPES_EXTENDED 0x04000L + +// SQL_SS_LENGTH_UNLIMITED is used to describe the max length of +// VARCHAR(max), VARBINARY(max), NVARCHAR(max), and XML columns + +#define SQL_SS_LENGTH_UNLIMITED 0 + +// User Data Type definitions. +// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_UTYPE. + +#define SQLudtBINARY 3 +#define SQLudtBIT 16 +#define SQLudtBITN 0 +#define SQLudtCHAR 1 +#define SQLudtDATETIM4 22 +#define SQLudtDATETIME 12 +#define SQLudtDATETIMN 15 +#define SQLudtDECML 24 +#define SQLudtDECMLN 26 +#define SQLudtFLT4 23 +#define SQLudtFLT8 8 +#define SQLudtFLTN 14 +#define SQLudtIMAGE 20 +#define SQLudtINT1 5 +#define SQLudtINT2 6 +#define SQLudtINT4 7 +#define SQLudtINTN 13 +#define SQLudtMONEY 11 +#define SQLudtMONEY4 21 +#define SQLudtMONEYN 17 +#define SQLudtNUM 10 +#define SQLudtNUMN 25 +#define SQLudtSYSNAME 18 +#define SQLudtTEXT 19 +#define SQLudtTIMESTAMP 80 +#define SQLudtUNIQUEIDENTIFIER 0 +#define SQLudtVARBINARY 4 +#define SQLudtVARCHAR 2 +#define MIN_USER_DATATYPE 256 + +// Aggregate operator types. +// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_OP. + +#define SQLAOPSTDEV 0x30 // Standard deviation +#define SQLAOPSTDEVP 0x31 // Standard deviation population +#define SQLAOPVAR 0x32 // Variance +#define SQLAOPVARP 0x33 // Variance population +#define SQLAOPCNT 0x4b // Count +#define SQLAOPSUM 0x4d // Sum +#define SQLAOPAVG 0x4f // Average +#define SQLAOPMIN 0x51 // Min +#define SQLAOPMAX 0x52 // Max +#define SQLAOPANY 0x53 // Any +#define SQLAOPNOOP 0x56 // None + +// SQLGetDiagField driver specific defines. +// Microsoft has -1150 thru -1199 reserved for Microsoft ODBC Driver for SQL Server usage. + +#define SQL_DIAG_SS_BASE (-1150) +#define SQL_DIAG_SS_MSGSTATE (SQL_DIAG_SS_BASE) +#define SQL_DIAG_SS_SEVERITY (SQL_DIAG_SS_BASE-1) +#define SQL_DIAG_SS_SRVNAME (SQL_DIAG_SS_BASE-2) +#define SQL_DIAG_SS_PROCNAME (SQL_DIAG_SS_BASE-3) +#define SQL_DIAG_SS_LINE (SQL_DIAG_SS_BASE-4) + +// SQLGetDiagField/SQL_DIAG_DYNAMIC_FUNCTION_CODE driver specific defines. +// Microsoft has -200 thru -299 reserved for Microsoft ODBC Driver for SQL Server usage. + +#define SQL_DIAG_DFC_SS_BASE (-200) +#define SQL_DIAG_DFC_SS_ALTER_DATABASE (SQL_DIAG_DFC_SS_BASE-0) +#define SQL_DIAG_DFC_SS_CHECKPOINT (SQL_DIAG_DFC_SS_BASE-1) +#define SQL_DIAG_DFC_SS_CONDITION (SQL_DIAG_DFC_SS_BASE-2) +#define SQL_DIAG_DFC_SS_CREATE_DATABASE (SQL_DIAG_DFC_SS_BASE-3) +#define SQL_DIAG_DFC_SS_CREATE_DEFAULT (SQL_DIAG_DFC_SS_BASE-4) +#define SQL_DIAG_DFC_SS_CREATE_PROCEDURE (SQL_DIAG_DFC_SS_BASE-5) +#define SQL_DIAG_DFC_SS_CREATE_RULE (SQL_DIAG_DFC_SS_BASE-6) +#define SQL_DIAG_DFC_SS_CREATE_TRIGGER (SQL_DIAG_DFC_SS_BASE-7) +#define SQL_DIAG_DFC_SS_CURSOR_DECLARE (SQL_DIAG_DFC_SS_BASE-8) +#define SQL_DIAG_DFC_SS_CURSOR_OPEN (SQL_DIAG_DFC_SS_BASE-9) +#define SQL_DIAG_DFC_SS_CURSOR_FETCH (SQL_DIAG_DFC_SS_BASE-10) +#define SQL_DIAG_DFC_SS_CURSOR_CLOSE (SQL_DIAG_DFC_SS_BASE-11) +#define SQL_DIAG_DFC_SS_DEALLOCATE_CURSOR (SQL_DIAG_DFC_SS_BASE-12) +#define SQL_DIAG_DFC_SS_DBCC (SQL_DIAG_DFC_SS_BASE-13) +#define SQL_DIAG_DFC_SS_DISK (SQL_DIAG_DFC_SS_BASE-14) +#define SQL_DIAG_DFC_SS_DROP_DATABASE (SQL_DIAG_DFC_SS_BASE-15) +#define SQL_DIAG_DFC_SS_DROP_DEFAULT (SQL_DIAG_DFC_SS_BASE-16) +#define SQL_DIAG_DFC_SS_DROP_PROCEDURE (SQL_DIAG_DFC_SS_BASE-17) +#define SQL_DIAG_DFC_SS_DROP_RULE (SQL_DIAG_DFC_SS_BASE-18) +#define SQL_DIAG_DFC_SS_DROP_TRIGGER (SQL_DIAG_DFC_SS_BASE-19) +#define SQL_DIAG_DFC_SS_DUMP_DATABASE (SQL_DIAG_DFC_SS_BASE-20) +#define SQL_DIAG_DFC_SS_BACKUP_DATABASE (SQL_DIAG_DFC_SS_BASE-20) +#define SQL_DIAG_DFC_SS_DUMP_TABLE (SQL_DIAG_DFC_SS_BASE-21) +#define SQL_DIAG_DFC_SS_DUMP_TRANSACTION (SQL_DIAG_DFC_SS_BASE-22) +#define SQL_DIAG_DFC_SS_BACKUP_TRANSACTION (SQL_DIAG_DFC_SS_BASE-22) +#define SQL_DIAG_DFC_SS_GOTO (SQL_DIAG_DFC_SS_BASE-23) +#define SQL_DIAG_DFC_SS_INSERT_BULK (SQL_DIAG_DFC_SS_BASE-24) +#define SQL_DIAG_DFC_SS_KILL (SQL_DIAG_DFC_SS_BASE-25) +#define SQL_DIAG_DFC_SS_LOAD_DATABASE (SQL_DIAG_DFC_SS_BASE-26) +#define SQL_DIAG_DFC_SS_RESTORE_DATABASE (SQL_DIAG_DFC_SS_BASE-26) +#define SQL_DIAG_DFC_SS_LOAD_HEADERONLY (SQL_DIAG_DFC_SS_BASE-27) +#define SQL_DIAG_DFC_SS_RESTORE_HEADERONLY (SQL_DIAG_DFC_SS_BASE-27) +#define SQL_DIAG_DFC_SS_LOAD_TABLE (SQL_DIAG_DFC_SS_BASE-28) +#define SQL_DIAG_DFC_SS_LOAD_TRANSACTION (SQL_DIAG_DFC_SS_BASE-29) +#define SQL_DIAG_DFC_SS_RESTORE_TRANSACTION (SQL_DIAG_DFC_SS_BASE-29) +#define SQL_DIAG_DFC_SS_PRINT (SQL_DIAG_DFC_SS_BASE-30) +#define SQL_DIAG_DFC_SS_RAISERROR (SQL_DIAG_DFC_SS_BASE-31) +#define SQL_DIAG_DFC_SS_READTEXT (SQL_DIAG_DFC_SS_BASE-32) +#define SQL_DIAG_DFC_SS_RECONFIGURE (SQL_DIAG_DFC_SS_BASE-33) +#define SQL_DIAG_DFC_SS_RETURN (SQL_DIAG_DFC_SS_BASE-34) +#define SQL_DIAG_DFC_SS_SELECT_INTO (SQL_DIAG_DFC_SS_BASE-35) +#define SQL_DIAG_DFC_SS_SET (SQL_DIAG_DFC_SS_BASE-36) +#define SQL_DIAG_DFC_SS_SET_IDENTITY_INSERT (SQL_DIAG_DFC_SS_BASE-37) +#define SQL_DIAG_DFC_SS_SET_ROW_COUNT (SQL_DIAG_DFC_SS_BASE-38) +#define SQL_DIAG_DFC_SS_SET_STATISTICS (SQL_DIAG_DFC_SS_BASE-39) +#define SQL_DIAG_DFC_SS_SET_TEXTSIZE (SQL_DIAG_DFC_SS_BASE-40) +#define SQL_DIAG_DFC_SS_SETUSER (SQL_DIAG_DFC_SS_BASE-41) +#define SQL_DIAG_DFC_SS_SHUTDOWN (SQL_DIAG_DFC_SS_BASE-42) +#define SQL_DIAG_DFC_SS_TRANS_BEGIN (SQL_DIAG_DFC_SS_BASE-43) +#define SQL_DIAG_DFC_SS_TRANS_COMMIT (SQL_DIAG_DFC_SS_BASE-44) +#define SQL_DIAG_DFC_SS_TRANS_PREPARE (SQL_DIAG_DFC_SS_BASE-45) +#define SQL_DIAG_DFC_SS_TRANS_ROLLBACK (SQL_DIAG_DFC_SS_BASE-46) +#define SQL_DIAG_DFC_SS_TRANS_SAVE (SQL_DIAG_DFC_SS_BASE-47) +#define SQL_DIAG_DFC_SS_TRUNCATE_TABLE (SQL_DIAG_DFC_SS_BASE-48) +#define SQL_DIAG_DFC_SS_UPDATE_STATISTICS (SQL_DIAG_DFC_SS_BASE-49) +#define SQL_DIAG_DFC_SS_UPDATETEXT (SQL_DIAG_DFC_SS_BASE-50) +#define SQL_DIAG_DFC_SS_USE (SQL_DIAG_DFC_SS_BASE-51) +#define SQL_DIAG_DFC_SS_WAITFOR (SQL_DIAG_DFC_SS_BASE-52) +#define SQL_DIAG_DFC_SS_WRITETEXT (SQL_DIAG_DFC_SS_BASE-53) +#define SQL_DIAG_DFC_SS_DENY (SQL_DIAG_DFC_SS_BASE-54) +#define SQL_DIAG_DFC_SS_SET_XCTLVL (SQL_DIAG_DFC_SS_BASE-55) +#define SQL_DIAG_DFC_SS_MERGE (SQL_DIAG_DFC_SS_BASE-56) + +// Severity codes for SQL_DIAG_SS_SEVERITY +#define EX_ANY 0 +#define EX_INFO 10 +#define EX_MAXISEVERITY EX_INFO +#define EX_MISSING 11 +#define EX_TYPE 12 +#define EX_DEADLOCK 13 +#define EX_PERMIT 14 +#define EX_SYNTAX 15 +#define EX_USER 16 +#define EX_RESOURCE 17 +#define EX_INTOK 18 +#define MAXUSEVERITY EX_INTOK +#define EX_LIMIT 19 +#define EX_CMDFATAL 20 +#define MINFATALERR EX_CMDFATAL +#define EX_DBFATAL 21 +#define EX_TABCORRUPT 22 +#define EX_DBCORRUPT 23 +#define EX_HARDWARE 24 +#define EX_CONTROL 25 + +// Data is defined to be past the end of the structure header. +// This is accepted by MSVC, GCC, and C99 standard but former emits +// unnecessary warning, hence it has to be disabled. + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4200) +#endif + +// Communication between the driver and application via the CEKeystoreData structure +typedef struct CEKeystoreData +{ + wchar_t *name; + unsigned int dataSize; + char data[]; +} CEKEYSTOREDATA; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// The following constants are for the Azure Key Vault configuration interface +#define AKV_CONFIG_FLAGS 0 +#define AKVCFG_AUTHMODE 0x0000000F +#define AKVCFG_AUTHMODE_ACCESSTOKEN 0 +#define AKVCFG_AUTHMODE_CLIENTKEY 1 +#define AKVCFG_AUTHMODE_PASSWORD 2 +#define AKVCFG_AUTHMODE_INTEGRATED 3 +#define AKVCFG_AUTHMODE_CERTIFICATE 4 +#define AKVCFG_NOAUTORENEW 0x00000010 + +#define AKV_CONFIG_PRINCIPALID 1 +#define AKV_CONFIG_AUTHSECRET 2 +#define AKV_CONFIG_ACCESSTOKEN 3 +#define AKV_CONFIG_TOKENEXPIRY 4 +#define AKV_CONFIG_MAXRETRIES 5 +#define AKV_CONFIG_RETRYTIMEOUT 6 +#define AKV_CONFIG_RETRYWAIT 7 + +#define AKV_CONFIG_RESET 255 +#endif // __msodbcsql_h__ + diff --git a/source/shared/sal_def.h b/source/shared/sal_def.h index 8116f9c1..13fd73ab 100644 --- a/source/shared/sal_def.h +++ b/source/shared/sal_def.h @@ -3,7 +3,7 @@ // // Contents: Contains the minimal definitions to build on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/typedefs_for_linux.h b/source/shared/typedefs_for_linux.h index 9906c9d3..531d637d 100644 --- a/source/shared/typedefs_for_linux.h +++ b/source/shared/typedefs_for_linux.h @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------------------------------------------------------------- // File: typedefs_for_linux.h // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/version.h b/source/shared/version.h index b26accd7..142038aa 100644 --- a/source/shared/version.h +++ b/source/shared/version.h @@ -1,58 +1,58 @@ -#ifndef VERSION_H -#define VERSION_H -//--------------------------------------------------------------------------------------------------------------------------------- -// File: version.h -// Contents: Version number constants -// -// Microsoft Drivers 4.3 for PHP for SQL Server -// Copyright(c) Microsoft Corporation -// All rights reserved. -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), -// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : -// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//--------------------------------------------------------------------------------------------------------------------------------- - -// helper macros to stringify the a macro value -#define STRINGIFY(a) TOSTRING(a) -#define TOSTRING(a) #a - - -// Increase Major number with backward incompatible breaking changes. -// Increase Minor with backward compatible new functionalities and API changes. -// Increase Patch for backward compatible fixes. -#define SQLVERSION_MAJOR 5 -#define SQLVERSION_MINOR 0 -#define SQLVERSION_PATCH 0 -#define SQLVERSION_BUILD 0 - -// Semantic versioning pre-release -// for stable releases should be empty -// "-RC" for release candidates -// "-preview" for ETP -#define SEMVER_PRERELEASE -// Semantic versioning build metadata, build meta data is not counted in precedence order. -#define SEMVER_BUILDMETA - -#if SQLVERSION_BUILD > 0 -#undef SEMVER_BUILDMETA -#define SEMVER_BUILDMETA "+" STRINGIFY( SQLVERSION_BUILD ) -#endif - -// Main version, dot separated 3 digits, Major.Minor.Patch -#define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH ) - -// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases) -#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA -#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD - -// PECL package version macros (can't have '-' or '+') -#define PHP_SQLSRV_VERSION VER_APIVERSION_STR SEMVER_PRERELEASE -#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION - -#endif // VERSION_H +#ifndef VERSION_H +#define VERSION_H +//--------------------------------------------------------------------------------------------------------------------------------- +// File: version.h +// Contents: Version number constants +// +// Microsoft Drivers 4.3 for PHP for SQL Server +// Copyright(c) Microsoft Corporation +// All rights reserved. +// MIT License +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), +// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//--------------------------------------------------------------------------------------------------------------------------------- + +// helper macros to stringify the a macro value +#define STRINGIFY(a) TOSTRING(a) +#define TOSTRING(a) #a + + +// Increase Major number with backward incompatible breaking changes. +// Increase Minor with backward compatible new functionalities and API changes. +// Increase Patch for backward compatible fixes. +#define SQLVERSION_MAJOR 5 +#define SQLVERSION_MINOR 0 +#define SQLVERSION_PATCH 0 +#define SQLVERSION_BUILD 0 + +// Semantic versioning pre-release +// for stable releases should be empty +// "-RC" for release candidates +// "-preview" for ETP +#define SEMVER_PRERELEASE +// Semantic versioning build metadata, build meta data is not counted in precedence order. +#define SEMVER_BUILDMETA + +#if SQLVERSION_BUILD > 0 +#undef SEMVER_BUILDMETA +#define SEMVER_BUILDMETA "+" STRINGIFY( SQLVERSION_BUILD ) +#endif + +// Main version, dot separated 3 digits, Major.Minor.Patch +#define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH ) + +// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases) +#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA +#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD + +// PECL package version macros (can't have '-' or '+') +#define PHP_SQLSRV_VERSION VER_APIVERSION_STR SEMVER_PRERELEASE +#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION + +#endif // VERSION_H diff --git a/source/shared/xplat.h b/source/shared/xplat.h index b29689c5..2032a164 100644 --- a/source/shared/xplat.h +++ b/source/shared/xplat.h @@ -3,7 +3,7 @@ // // Contents: include for definition of Windows types for non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/xplat_intsafe.h b/source/shared/xplat_intsafe.h index 1fd43551..56f2eed8 100644 --- a/source/shared/xplat_intsafe.h +++ b/source/shared/xplat_intsafe.h @@ -4,7 +4,7 @@ // Contents: This module defines helper functions to prevent // integer overflow bugs. // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/xplat_winerror.h b/source/shared/xplat_winerror.h index f3f0878b..a6078c6e 100644 --- a/source/shared/xplat_winerror.h +++ b/source/shared/xplat_winerror.h @@ -3,7 +3,7 @@ // // Contents: Contains the minimal definitions to build on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/shared/xplat_winnls.h b/source/shared/xplat_winnls.h index a91dd554..d80367f9 100644 --- a/source/shared/xplat_winnls.h +++ b/source/shared/xplat_winnls.h @@ -3,7 +3,7 @@ // // Contents: Contains the minimal definitions to build on non-Windows platforms // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/config.w32 b/source/sqlsrv/config.w32 index 811602fe..81b3d019 100644 --- a/source/sqlsrv/config.w32 +++ b/source/sqlsrv/config.w32 @@ -3,7 +3,7 @@ // // Contents: JScript build configuration used by buildconf.bat // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/conn.cpp b/source/sqlsrv/conn.cpp index 8da98f30..2e2f6f99 100644 --- a/source/sqlsrv/conn.cpp +++ b/source/sqlsrv/conn.cpp @@ -3,7 +3,7 @@ // // Contents: Routines that use connection handles // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/init.cpp b/source/sqlsrv/init.cpp index f6626122..12a02f0e 100644 --- a/source/sqlsrv/init.cpp +++ b/source/sqlsrv/init.cpp @@ -2,7 +2,7 @@ // File: init.cpp // Contents: initialization routines for the extension // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/php_sqlsrv.h b/source/sqlsrv/php_sqlsrv.h index 82168296..1147f539 100644 --- a/source/sqlsrv/php_sqlsrv.h +++ b/source/sqlsrv/php_sqlsrv.h @@ -8,7 +8,7 @@ // // Comments: Also contains "internal" declarations shared across source files. // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/stmt.cpp b/source/sqlsrv/stmt.cpp index 1155782b..015dddfc 100644 --- a/source/sqlsrv/stmt.cpp +++ b/source/sqlsrv/stmt.cpp @@ -3,7 +3,7 @@ // // Contents: Routines that use statement handles // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/template.rc b/source/sqlsrv/template.rc index 5502389a..2dd474e3 100644 --- a/source/sqlsrv/template.rc +++ b/source/sqlsrv/template.rc @@ -3,7 +3,7 @@ // // Contents: Version resource // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/source/sqlsrv/util.cpp b/source/sqlsrv/util.cpp index 47ff0a01..cd04101c 100644 --- a/source/sqlsrv/util.cpp +++ b/source/sqlsrv/util.cpp @@ -5,7 +5,7 @@ // // Comments: Mostly error handling and some type handling // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License diff --git a/test/functional/pdo_sqlsrv/MsSetup.inc b/test/functional/pdo_sqlsrv/MsSetup.inc index 96343778..2aa68b27 100644 --- a/test/functional/pdo_sqlsrv/MsSetup.inc +++ b/test/functional/pdo_sqlsrv/MsSetup.inc @@ -40,4 +40,4 @@ $marsMode = true; $dsnMode = true; $traceEnabled = false; -?> +?> \ No newline at end of file diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt index 41befa50..80e2d2db 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt @@ -47,6 +47,7 @@ try{ if ($lastSeq1 == 3 && $lastSeq2 == 198 && $lastSeq3 == "") { echo "Done\n"; } + $stmt = $conn->query("DROP TABLE $tableName"); $stmt = $conn->query("DROP SEQUENCE $sequence1"); $stmt = $conn->query("DROP SEQUENCE $sequence2"); From 3f20e0cab0aa5b4f5ce19fa53e1199697f5641ee Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Thu, 20 Jul 2017 16:39:58 -0700 Subject: [PATCH 09/10] updated version.h --- source/shared/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/shared/version.h b/source/shared/version.h index 142038aa..7237b2a8 100644 --- a/source/shared/version.h +++ b/source/shared/version.h @@ -4,7 +4,7 @@ // File: version.h // Contents: Version number constants // -// Microsoft Drivers 4.3 for PHP for SQL Server +// Microsoft Drivers 5.0 for PHP for SQL Server // Copyright(c) Microsoft Corporation // All rights reserved. // MIT License @@ -35,7 +35,7 @@ // for stable releases should be empty // "-RC" for release candidates // "-preview" for ETP -#define SEMVER_PRERELEASE +#define SEMVER_PRERELEASE "-preview" // Semantic versioning build metadata, build meta data is not counted in precedence order. #define SEMVER_BUILDMETA From 8a9d2d66fc81ff74539002f742ff2d6a8b5e0ad2 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Thu, 20 Jul 2017 17:07:02 -0700 Subject: [PATCH 10/10] Tests fixed to use temp tables --- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt | 4 ++-- test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt index ae10db95..c0d35ed7 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt @@ -17,8 +17,8 @@ try{ echo "Done\n"; } else { - $tableName1 = GetTempTableName('tab1'); - $tableName2 = GetTempTableName('tab2'); + $tableName1 = GetTempTableName('tab1', false); + $tableName2 = GetTempTableName('tab2', false); $sequenceName = 'sequence1'; $stmt = $conn->query("IF OBJECT_ID('$sequenceName', 'SO') IS NOT NULL DROP SEQUENCE $sequenceName"); diff --git a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt index 80e2d2db..6782d6c5 100644 --- a/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt +++ b/test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq_2.phpt @@ -17,7 +17,7 @@ try{ echo "Done\n"; } else { - $tableName = GetTempTableName('tab'); + $tableName = GetTempTableName('tab', false); $sequence1 = 'sequence1'; $sequence2 = 'sequenceNeg1'; $stmt = $conn->query("IF OBJECT_ID('$sequence1', 'SO') IS NOT NULL DROP SEQUENCE $sequence1");