From 53318fbbdd18d02d793481a8808827ef2799dec2 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Wed, 19 Jul 2017 14:05:14 -0700 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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"); From 3e8b44546f9ecef1db1c93150a11118cc40d3e3a Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 21 Jul 2017 16:56:16 -0700 Subject: [PATCH 11/17] test fixes for PHP 7.2 --- test/functional/sqlsrv/TC21_Connection.phpt | 4 ++-- test/functional/sqlsrv/sqlsrv_close.phpt | 7 ++++--- .../sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt | 2 +- .../sqlsrv/sqlsrv_input_param_unknown_encoding.phpt | 5 ++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/functional/sqlsrv/TC21_Connection.phpt b/test/functional/sqlsrv/TC21_Connection.phpt index ed0b57c4..0a6872a7 100644 --- a/test/functional/sqlsrv/TC21_Connection.phpt +++ b/test/functional/sqlsrv/TC21_Connection.phpt @@ -35,8 +35,8 @@ function ConnectionTest() // Valid connection attempt => no errors are expected Trace("\nValid connection attempt (to $server) ....\n"); $conn2 = Connect(); - $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); - if(count($errors) != 0) + $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); + if (!empty($errors) && count($errors) != 0) { die("No errors were expected on valid connection attempts."); } diff --git a/test/functional/sqlsrv/sqlsrv_close.phpt b/test/functional/sqlsrv/sqlsrv_close.phpt index ad0d9dab..8841141e 100644 --- a/test/functional/sqlsrv/sqlsrv_close.phpt +++ b/test/functional/sqlsrv/sqlsrv_close.phpt @@ -14,9 +14,10 @@ using an already closed connection. $conn2 = Connect(); $stmt1 = sqlsrv_query($conn2, "IF OBJECT_ID('PHPTable', 'U') IS NOT NULL DROP TABLE [PHPTable]"); $errors = sqlsrv_errors(); - $count = count($errors); - $error = $errors[0]; - $value = $error['message']; + if (!empty($errors) && count($errors) != 0) + { + die("No errors were expected on valid connection attempts."); + } $stmt2 = sqlsrv_query($conn2, "CREATE TABLE [PHPTable] ([c1_int] int, [c2_bit] bit, [c3_tinyint] tinyint, [c4_smallint] smallint, [c5_bigint] bigint, [c6_float] float, [c7_real] real, [c8_decimal] decimal(28,4), [c9_numeric] numeric(32,0), [c10_money] money, [c11_smallmoney] smallmoney, [c12_char] char(512), [c13_varchar] varchar(512), [c14_varchar(max)] varchar(max), [c15_text] text, [c16_nchar] nchar(512), [c17_nvarchar] nvarchar(512), [c18_nvarchar(max)] nvarchar(max), [c19_ntext] ntext, [c20_binary] binary(512), [c21_varbinary] varbinary(512), [c22_varbinary(max)] varbinary(max), [c23_image] image, [c24_uniqueidentifier] uniqueidentifier, [c25_timestamp] timestamp, [c26_datetime] datetime, [c27_smalldatetime] smalldatetime)"); sqlsrv_free_stmt($stmt2); $stmt3 = sqlsrv_query($conn2, "INSERT INTO [PHPTable] ([c1_int], [c2_bit], [c3_tinyint], [c4_smallint], [c5_bigint], [c6_float], [c7_real], [c8_decimal], [c9_numeric], [c10_money], [c11_smallmoney], [c12_char], [c13_varchar], [c14_varchar(max)], [c15_text], [c16_nchar], [c17_nvarchar], [c18_nvarchar(max)], [c19_ntext], [c20_binary], [c21_varbinary], [c22_varbinary(max)], [c23_image], [c24_uniqueidentifier], [c26_datetime], [c27_smalldatetime]) VALUES (-1, null, 255, 29430, -1023675178, 1, 3.4E+38, 0.6199, 0, -922337203685477.5808, 0.1695, '', null, '|Cb*vzu£ruZCzAAbÃ,@ +©¢ß_ýzÖäv,uåh,ÜÐo:üaðzÐ*OO/AC£ZUüßå>UßZa:ßðßbUO£vz@ö_z:+î<*ßäÄТBýð >Z>*oü_h+îZðaÜÜßC*ã*@~Ö:ÖzvýüÃÖîÃßÜbbOuªO¢Üýö~CäA_~_AÃbßhßýÖbäOZbZBÖauîßüߢ/oð|oO.ö/BråýÖüöZr:_z:<ö©<+äߢÜ~rb¢©rUß~£ðÖrä,BýAZ¢¢>Zzý©ýåÃß_hß ¢©ðhzÐA+åî,ÄZbv,+ß@ahbðÖ¢ýu*CÜBßöåuÄoäÐ_~U¢+îZ+î¢z/ĪßO|Öo>z@©UBߪÖ.ã_u@Bü.î/_ä*¢zÐß*+OoÄ©üa*/Aða_|z@übbBð¢¢+ZCuzoÃrð/Ö./ªÃO~uo£ö>åîü_ߣv©Ð/Ö_ß._ÖÃOÖãý~Üoåðvå>åÖüÃå,uUÖ*AövÃz©åßZ¢ã@üîC>zCß©å++Oä©ÜãðAz>ðãÄåÄå:<ýCýv£*Üb~+bC.B:©|Bã£îAåUz ªhOuß/£*ÃÖ+C@@îÃv_ürBß+b ¢bß~öuÖö Уî+~£:b.ÃZ:~zÐrÜ*ZCUÜÖhîo+ßÖÐ_¢oOÜUßåüzÐa.©ÜäAÐb,oå<ãBöîÜ<.v£z©o£Z,@>,î+ü+u+b*AC£Ð+|£o Bã A.|bBÄÖ>C~£r~ýv@><å~ß.:Z¢îðB@>özÃo+<ßîZ@ª|U/,b©äZv:öB££ððßAC©b bCAÃuhvAýýöý ¢C_ÐöðäbZ@u~vßåÄ+:äÐ>ªÖZ:rÃr~oß*~ÄOýäzä@uöÃ~oO+|îÃ|ooAa>:rªU,B_+/>~üäb|h.ÐÖCÐðCvßhzO|Ä©@©Ã¢Ch.zh©~.ÄäC.Ð/vîÜ_b*.vÜ£ÜßzAvO@î:ߪªÄ/Ü:©./:ÜOîÖZßîý~*rä*Ö@**|h@ uüöߣr*Ü¢©ýß äªå|Ö:v_zBªÐvöî/BCvîC|rüö¢ª|rüÃrbAåð:ßßÃvßîå b,v/ @_Äðrz.ð¢Cãà .,Ãubýüzz~¢ £ ¢äu*hå:h>Cã~@_O©ã~:|<ö+Or:*ruhZå£BZÖî>|:åvî|,<a|CÄ._ÃÃaZîb_,öbåäh£bv©©aÜ:h./ÃÖ@Ð @uß<.voüßZÐvh*zåaÖ+~/ÜßâB|v|~h_ßb/ßb,Ä,îZå+v> ÜC|Ãýhhå', 'Cã Ãî.rhr:bð<ߪbýÃoî/.CÜßoýåh,©OÃ.ö_,Ãaå©UbbUä¢ÖBöh:ÐãüÃÐ|îÐ.zýßÜåubäb£ZÃZur*b@ð@Ußrvu:ã/Ußãßv:+UZZ*¢|åbzãÜ:¢Ü*©AürO>ß_|/,¢¢uß>.ãäübÜozö+Öå./ýu©åoßãbvä/ÐÄðUöUß<**¢Ößzåå©vßÖîhh/oöÖbA©+|a@ bUbýCö.bb+<ä~vðoß:B@ª~bbbÃBoßC>|CîaÄ<Öüýªrü+bzÄ|ª/Z|_ÐZa@@.,î ð r/öãZv å£vB¢@ÄýÖü@|+ßöA+¢ýOü |Bvv¢O£+åZ*B.ðv££<ß_©/,Ü|@v,|uZb+_bOß+î_ +Burv.aÖÄü>+.£CÄ<ÃCðUߣavÐäãBãZ~Zrz*o~ZCU¢ãvßÄöåö: ©ß¢b_/uð__O©å©îbr*Ü_î,ý©¢ü,r|Üüå¢>¢ß©hÖîð£Ö|büö~å£Ð_O', N'vßö:|ba+Ö|zA,AC,b~å¢v£/¢£Aß ðA/ÃÄb+ýЩOäîvaýÐö|_ÖU©özzO¢+zß_UÃ:Ähªzz©h©.,aÄý¢/åÃ:v~Ã+<,.îÖ¢.㢢ußZäh£Uå¢|U_Z~Oýð.CBüCÜ*£rå.î>U|Ü,ãÃhß bã_hÐîrüßÜ@O ãÜ_ä Aý.îr~Ã.ÄÄäýªU~<ЪªbýÜö.@äCäª_ðüü*Cãðo¢ü:zO>BãOZªãCOðß*Uba, :ãßOßîAUhÖ||ý¢¢©~:zÃÃãUÖ/ÖAOaäÜðÜZ@ª>aÃrßðoäZ_Ozo©©ªÃÄ¢ª~bÐAääuvü£ÜÃCaO|ÖBåÖÃ,<å|Ða*OOub©îA:v©*öÄåå£Ö>Oh>BäªßäðO|äýå>:.B¢+üäBåå:£ªo@ªªU*UBÜÄÜã+<£Ã_~Öu£ß,<ªßÃbZ@å@ðZå_~+v¢£.©Ä Üý>u, ZÄ.ü*CCa/©OßUßUîB~BBO C~Uh©oßßrãårªß@ @.:+ãü:ª/îbäîîCzoöªãÐu:üO¢:*ðbr/,Übâß>oC£ÄÜböðzÄ~b£u©~zö>rr>C©,ã:Cã/©*:/Ðb@ÃåuÄo£*ý|uÐOÖÐhªZ©ýüzb/<ß :z,h.ÐöbÃ_ÐoÖÐCrª/ÐîÃ+:Z*/ß©ö<@£Ü*_©ðöAÖä,~*öAar.ª£ouª¢BäBÜüðvCuaB,.r/v©.ýbzßB* .ªv*ß*.OÄ/ßßobß~v**䢪ýü ýÖU_ ZÖÖ:_ aÜ.©ÃuÄÖ¢/>*o:A:Ö>ÃߪzAߪo¢+obbÄ>>~<ð©o.åU å<,BBZãavÖÐÐÄÖ~b_~*ª~+ßä©å|@b Zö*üh*rb_bzAßüÜÖåÐ*ª¢ß_£Ö@Ã_.rÐ öð:Ü+ЪUä:bvZ_|ãbîaßo:Ä:.Är¢©££ *ýBå>ý,hCBÐoÖv+röä:>OðoªÐªh@@*A*bÐv@+ýzuBÜ©ZUz@ Üa£~O>ÐvÃAbªC*rÖß©~î,ãîU rÄ~üßOBÖäãîÐðý._.ãÖðu_öÄ:ß_auö*ä| Zo*ýah C©Üå>A£b_î>Ao~ÄðUÖUÜ/A/übý.©ÖvªÖÖ| *o,î~£AÖOüvzz,+©az£böåÃüZ.äÜ ªðO~ý|rÐ rð©Ãß.ýrBv,~ª/C>,<:.ªOßU.~AbÐ ãoöÃBÄ.vbvüB ~,Öa hhäãå*aU< åååbr.|oß~ürO,rÃÖ@uubß_*. _@Üåbªz/aÖ@Z𩪩©î+ðü:¢rª>Avvb*@©ääb,ð>*îÐ,:o:~|¢,o¢ý>+bå|Z~~AOö,a¢bß|vÖ.AaubOAhî<¢åîÄ_O<Ü', N'¢îÜä|:boß@ÖbC.rö>~à ÜC¢_.Ö£ßrhCÖýîÃv@ãßrzªÜîÃýã©*ãuB|oåßßßöUÖ~©ä:u.+ /ßÄ~h/î*Ää/îý/*îAOãAå:Äî_/Äðð::|: ða@@/Ðü:Bö@ Üh ü/b~ /üCßAÄÄü.v,ä:©@*+>ÐBb.*Ü,r©~ªüA,üCîüîÄ<Ðaabã_£åB¢ü~ðz>Zaª .ãAh*:Uaî Ö_:£aaBA+@UÐz|ü£+Bb<ãå*|z£Ãå©~~_Ö|*b@ðîz,U£bB©@:ª B:å£|_aräãh,©ÜvZ~¢ðo ä£î,ÐuB<.zZBÖZ.:hUÜ ÜähbA:ö£©.ðAUA ßoZä<ÜöU/hZBUA@B~rðb:ß©.BУ Ä~UäÐö~zã+z_ߢÃîÖ*êh *ß öäA>äÜÐCª_äå/*z:rUrî+UãBÖ©ÐbbO+u.@Üb_ßüª|.uBßa ý+oöbhðöÃAðÐrAÃb.o¢<î~Uî,vª*öAÖvÐ*ЩîUzB>|bAªBÜ|ä*ßðr_..hU_ß@~ý©Z£aãzAOoä_ü >rZz>b<ªußbOö @£Ä>|ð|.ÖüBövãO¢b©~bîööUz@:+übÖB/ u_ߢ/ªÖ.ªä.~Ä:OBßOýZ+vbZ*£OÄÄ@zß@¢AÖÃ:öãÄOrA|îßaOäu~Bhüö>@ZÜ.AÄB_~bB...*uh~rbaÄ/ão@ãZÐÄ+©BvoOO.©ýãÐ~Ö:å+ü|Öä>ßãðv>äßßäÃ.zoUObÐîÐü>ЪC|Ü+.+ÖßîUÖ+*UoOªzÃChzv~uÄzu£AåýbußÃÐ<,:Bå@b.åßß~Ðh©ßª<ßZOoÄb_oå*ÖZC/:/CðuAUÜrO å@¢ðåuO<Öý,_z©,|ТУªÃ+CZaÐ~~uABýãß_îuýC~ß+/båßUã:@h:_ÐOh>£å>~î@üZ|<ää£>£¢bß öb,r>ÖCÃå¢:B£,,~C¢aîä:Ü ,üßA>ã>ðär/U©ýßßßb©C+uoÃäor/ZZ|C+ãÜ/bOäðözÐ_.ö>h,:ßO¢ÐO¢äÃubÖÐrv|ä<¢vö£ü åöÃÖªvð©bbß>,<¢£o|,ößOrC:ý|:åå/ßãäÖß<äU/z,©Ã+ÜO+ãüB,va@ÃÖ~z¢Ä:Ürªa©@Ðãozzö£,>>ЩßrªðÜb|ß bbî_¢Äãªää.|/ãrÃÜvÖÃußÜa.+|o|ªCýüUCUÖhCu@.A|bröããoüÄ_/ߪu:Av£_Cbð/Cßv¢uuÄZC_+Cãaz_¢zä.O.B¢/ZüÜvA£ov©äOª,z©ßvzvuCªaöCîßu@vÄÄ£rövðabårãooCðUýrªðBã, ©üßohßöÖ.o<üO+.åUu,Av/äãO£ãaî ¢åvåvzBäöbvbÖa£üb¢ßЩä/+buBýü<ýðãb_~ÄzBCÜ+Z ÜOÃäOu~uuO£Ö/åßArCî:b U©özo|Z¢|vÃî/Z:ÜZãÖßoî@ä©Übbo/b~ß@@~ü*zUaß+ÖÃU@_UÜð@.>hbüb¢,>r_oz+:ªåÐîÐß*U|~oªÜ+öýýîÃß@rîroý*B¢£öÖ_zvðr¢ÜaýZrªv¢Ð|U¢|ð.ü_ÐÖ:vªð¢.bühOOC*ªÐaß©CÐ+|v_b*bð*Bã.ßÜbߪoö,uv::ß,îßo~+|~©î@.üðßB_Ã>£ÖZ||ð,£ÄÄ@BBßУ/ZÖÜÄvãhä@,£uozÃ~üßAC*UUOÄääoß.ªuv*ßÜîÜ,ðhÃA_Ð/_,ã>haÃ_BA@ªý.üýbCÜ/öß<îÃO.Cü£rÄ/Zz< Z~@ÐUÜUzßüüo£ðÖz<Ðððßüª~|/ãßoÄB>©Äãrîu|Ä©,,/hßýZühß~_,@zýðoßö/hbå<<ðB_.UOuv.~o/Ö:|ãAå@övr@rübÖÖuUz|© br.Ä|. hCÐß<,*,ªCr+_uÐ|*/O.:AOZªÜ/,*/zzCªCbÜ+<©,+ªCBrhC ¢bZ+ýåBö:uÄüüAOßåvðB£ßüü@ab/bO.£zbÐ+aðCÐAhzoÜÃ.*/Zrðra©ßB äüß©åvßb£îZßßühOb><ßOãªÐýÖü<:Z.îZÃbhö*vA*ÐA@b¢>Ußaöo*vha+b©hz ª*>+_CåaUÄU/<:ßb©+vÐÃ/ U|a|bÖå©CÐAC©a©B+*ßÜUîîÜÃrZ,zä|¢oaUß@_ßU+ACÃ: hbýZå+î£B*©uåä:>+vO,ü£Öå|ã__.uÜr *bu,aýrÐ>ðîhZ©üÜå©ä@b+åäbCß_Ä©äЪöuðvvöCÐbåZ_åã üB*ÃÜ<,ßörZ<<£üåuo.:aßýboÖ Öª~zoZ<~¢ OüC|.ýbü<¢bCab,:+zhCZ@ao£r+åCrab |bOzî~ãîbÃhv¢ª@ðÄîo/@A£üüðu¢OävªýÜ/auo<Ðzrrãäåa~ãü.Zã£ß@.r ý*uÄu._Zäo>Ã_//h ãßCbð£Üu:ýÖ üðªaã<+b<ý/ ~h.*våã.@>âBUbÄ:>Ü.ÜöîUî©Ãöbbãã*ß>Üu/ü|o*ß:uýð~/£¢ýuA_¢UO:~Oå*:£ãvzÖZ>a@rv/h/AîÖC@..ä¢ Uä@bý,,vý+ß+Ä¢ãhoª:AÖC>ßo_ãb*ðAßuðbåÖ:Ahßý/Ü¢£rCZ,vß,~|C+Äî/_*îb aoß|ö,Ã| UÖÃÐO>*ÄÜü©u,¢+ö@A|~~:B+/ª>,_*ý|Ub*ß/¢_,Bã|ð,C¢@/¢.*hüÐÜ©zz~aöo.üCü v|>î>O>Öåoýzýüüðö£.v*~hh ö+o©hüZoZUäC@äü<üî@ ýªbüu< ÃÃüÃ.+||ß bÐrÃoÃÄBß.ß/ß+âß* zr:£¢~:z|:ÐözOoîo ¢ßð ä.b@Uîªaß|uuv¢:@zO@.ý A~B*_Ä_£OÐbÃAZbÖ@C_Ou*ªAüZh<>,Ãß>ߪ>:ÖO©¢ýåÐ.~ªO:_©,äßC©ýb+U|boÜbZOuÃ~zÖöå|u:å¢,ÃüAüÐZðÃÃð>+', 0x673D69FE556ADF245AF7DA561AD24A053FF2DA447CBCED68EB2B0944D8E3922D9EF8DE088F120EFE46E8C681A7E81741F8F6E1CF375BA5AA50011329609A4B7665ACB822F441762CE3DBFD94F66BC00213C74CE102C2F5425234B88DFFE49DAC9C4F1BFB30F54DB780B4EB5B5D11B4E4411016994E6A74A3B675CFBF88E82DFF3E51F7A650C5D11E7994614DD799E5417FD34CEECE045D32DA7845BA258D2E9787EB1D0B190D947C3EF71FBA0827CB1C908E3FB49C3F3139F4C5CBD87F17F8EB66CAC5EB7CDA28723FD0AFD6E4A329B3AC282A974E79FB48BAE3165342BBFC27FE34533E61E0A0B566CD75CB8F5A55519B182C57EC619DECCE7D3098C3D42F83D488D8B04FEB51CB88743180042FB85F8EC5B82A8FA53A9C96666FE4AF07F2919EF1F70782A401B0C660CE77301DE89FC4D109FD1B05F42E4D3B1AE97B6D1C80819FE11237B1DDEAC308642FD6C26C787DD01FDEFEF47699FDC8BA468A183459444DF6903BD66813F31F3A13A1C4588ACEA16BAB4D3D828D2948156EF79660AAD30AEB89CCDB3DA7680CEBDFBA62F738703078E8AD890B06CB6867F2C0F91C5DBF8F4096D9CFBDF264C3424152A3D3C751DCFDC71E77FD49FFE4BD64744AE66EF08E202A22615FA1728584E946C9B42795A0A1E4FB553E4792F696, 0x597E19DDD451C483A8A4EF7C5C5498F21303352F96CED4A901739A4FD8972BCA6B9019066A0C7278F0A4ECFBB2689F00A5547FF361C47B8AA970DB61B674AA11792411CC3A91880790BD3F, 0xD8F799D399A983173869A6158E5CFA3011C17BF9DA828D56D3C613CC7862A38589A8D179428D1CCB27F940BA6BB6AB348DD67FFBC4F70656694200D395B79FD5C6208BE58B81EE83A4B0D2E141D2EB1C1ADE3FA1E2A8C192A27A0FFF8C4935705A6AF44A2BC6A90CE7FB56132425149656E4B9B777FDF21E5FF8431171EEF75B7A6287D6DEA0F1533532BAE751AD6E7C6578A8A88F7EF85C78D6EB3282979AF804FA98C19E3DB8B6F3658144430BCCB18AC7ABF9464ECBFC6A2A5E07B5BED19E98C1E25614ACDE27C0F2B52AFCB3186C0939A8BBECA1B2B62F9C5773DFB0003494E3AC8229769CDEE883385399A1FFB712F2FD7B1615B1F70E2C2778556A480FF2809619079542939CCAD268685F1C8D9C9F61ECB2841D7E31B474FD4E23887E3A2961180AE5B07CAF69DE4C42498FAF0F99DCB5095C5C6D6218E8522145457941542E3EFD0382B6EFE43C48A5D13BE825C4EDB547D375DECCF3B5F2FD5D5B6A24650BEE1B50B10B1625C5251F13C791DCFF2D5C5790316EDE4F720A4D90B89288D2243FC64CCDD412E19E210D4FBEB92B2416FD30FE1294349D0FCBDEA7BE866D339C4DEF88CB9032584976F10F608E91B9DE297ABB6289C0900B293C01F66B2317B4DAB371C9A35985CAD7B49CA2243C803925C9940CB1D0128D5F7CA1533062DE52DA5A85DB20EF3ACFF4F08BD524EDE6ECBBC8D0A18E0B0209A0E36A26B7329974D649C8CAB6A519368EC518C1453D697196B59768923DC4CE577C3B3C841318C15616FEDA251C3516AF9A12C9F241C4627AF890029A31E29985F9723A6ADCB17AAD566B2F3E8E4C7A7F81B9F72AFADB16B7D8C18C831527243465552BA9FBACD2E9981C9CD59424ECAC16821878EA6E8A1D5B82921A25F7972F46FAD83820D084D5E60E92DE3F4122B4D5DF6D930EAC4AF7CB0D4E781AB7C4A0856C8E9A4BB38B46FE4391DBDCABDE1A5FDD45458A03209AB0E16D13810CBB2E4AF159D2B2886821542694CB1A6E1A1F2F33183C727E16814BF4EA694577C4F2876290EB520B68D77523F0636E557F41C74DE34EF37FBCCBDAD786BD9EFFDCDC62A243B8DE2ECD24CB41C74E8EB312F675A8A1F79475FE08A5C4117A84AA3975471300CB12C6639880BF83B2B86D3D27C59048F55DE448ECA43A12191D52A0200751077AB246E4AE450977D5F24B503787E9B4F361431763CB1427D7B4497B2D1F62AA6666C3747EAC9AAF546C73FA58EADA92339DACBFC439A4AF9D1E0CF9FB8D49B690A0E9D1C4753647C693C2F6581ACEEDD9B7479072A3E62CC213D75F9FA0B4C623BC69B70009CDBE6764F83077AD5683501F204346B943ECBABC0EB09DD6A726DFFCD29BB017ECD0E988280EABB6B514BD530F808070806A797DFD43FA1D2091BCD1507ADABBD338325059BD9035410307F9F34AC627A56F7B68213319A8D5CC689651D9653A3A0180D64D3016C2D9443BF93C002AD6CB952CE1FF7299A01F8A7A8D55FAE3689ECCBFF1CAEFDA7558B88FDDF71F6CD4A192CBBCAC843BC767E0B7C3B5886DDF60D35B7041380BDBBAB68846E3BCA8FF1DA6ECC22E27BB1A90F789E93816BFD04BAF7D23C4FB936986F001F5FBF5E4AB37B7A50F9CAAB0381841DF49AE21A3A3F7B8FA333E843B2213823C3E26C51E9B13DEB28071B033A0FF440B821C49BA644C5DFE42FAB16CB37C83FB196E711253224436D06F223246855A34FA79B805C9C8D8F1C8C6DCA06271FF03EEB419328D32B860D813E9F4292BB8BDA07C723AC47D325195804A8C46A3D35F701F44916B27A6C7C0582DB5DFE3C79D0D1F00E5FEC43F7081C89C0DE82B19343F89840AE7275178D6AA796C507D71B2B8AC7F27A2EE61ABDFD30FA28A99DF943EF875555FBDB5E6CD870F534BD3200456EA553BACF2925128D82B1318AEEE3CCAE42D57081C53FAF699591BDF0D600CB30A4CA044A4E84B66232C8692A2CD1F99D28866A5C8FC3A975A44139976721B781E600D624DF826FCE2F1E4EFCC6854CB39820C84F967EA0E1109E960D5B2EFB1110E7341C0BD4BDA626AC3D7174ACBF1EF485FF466977EB776FE50440EC6272F0132DEBDF5120A7C3631228FA312F7C63EF8708C52FB4B7A90BCCBC26C07CB9B4393D6E46C0DFCDC3DE3BD3474EF136307890615559ADD05E7642D3847480F2F65864B103C125A30FC934F6F3E1705A0A867BAE201B646A8B6D0FD07E0C395F5D38ECEEEA30327F52FD519C24B6EC7D05F8C90DF8D2C2EAF3FA3596B14524F39FE599BB04916FCA540C5B32B9C835D48092DA85433B567A65F1EAE00A9BCE0DC86E0C3F1D21687488FEA9FC7790213BC451161E7AAFB0548F2F5502A67FCFC84E5D7A5BCC06C75F3A65FC44816A02D5C0BF2BA975AF8318ADA7BFFDDC65CE193EE1BE2A15D57FCD99B6E414A37BC3252C7F31F74416429EDA51AE1446D982D497E305AFABE8BFFB3F6E1FBECB6E7531D48AD15D23C6C5289F1F4FB4275B76B0341F7CB066238943BF46B7E5F683D36BEC7C3FCC0825A0D12DED008C990BC3E3F75F8AABE1B4CBB1018D9DDD1EEF221B4BE3C7BB1BBAB80E6EAB828912FCAAB382D3EE9709277A27B08EBA7A97D3C24EBAF540B53418C84CA77C4D4622429E14E2EA4A3AF09B1214699FFA9CF04D768AE943CE2DBBF57F7A0BA1FF8CEA34056DF0A587BD89875E2FE32C4B941E3B4C59F5B25CEE846058ED6AD03F5E9B852A359A9CAC5AB5C90161E59E4A8FA9DB5BD055959737D716E9804CAA42A9480F945DF21609819EE1A43CA0FAF8E615C9438F746EF8F5FDF25E83A31C0E1B765819E609EC9D83D1DA3163F15A86EC1DF37A7356C334E9169E600FC785DE82E388B658D112EC293BBFBDAA2D8504936B7265BFA19AB4B32320DF7B8C8DCC5C0BBE8068943E0B0B174147A7159ACB8189DCA445101ED148D3F335B03370ACBDE3519E6EF4ABFF2D776AA850431DAD98554DE1AFD9873BECED3794A1085AC7969489B3B3FC6C9CEDDD86DDB57670F5C6559D5AE58EFCD46C15A181BD075E252C9301CBC959F99BF8B103C252A986766C31DE4B6AFFE21797B5579E4AEAEFD68F51B12274B53C6C6C0162348B2A71B29F3B157B3902BA2CA0EA2403F25B410DD12D42F0F85401A70561EF699DAD9C7D43950F1FC564AD7CA794718A91386A10B054635DE770CA3B397E75C723508F035DB6C06D4C31486C8FD65963E28705CCF7DEA9493A3E85C2DAD3E656FED64947057E2CEA680CC48F0C06DE01176648B268A8DF54A745C91F20FD82785AF17DA98D025E745A2B64FB0A2A3D335084AB748B23EE4606F4AFCFA50DB96A7A5C241A9934841507D4583D43D45A8D0895CEAC421DED270327A298629EDF4C61FA70BDF612AA2BF0966CA6CBA10222AA12B71F4F1D185C6F0D177181FBA269050BDB59639C86E5FB92ABBBB1465B62257FEE4FE7B00D44D2244EF90B389A04F403A1BFD5A8CF0D9BC113BC616CAB28A1A55C120D6271450E86873F5D9DEAC2F8A6B7AD949B8C7CB0E0B426AC40E36579264755EC87F791826CB14F218FBFFDE650C3CAA4FD841628DF612CAE5AA8BDA311C6275684D660AF262B37EA4D4B2120B6404B8D18B07B92313D0D26DAE327B3F3AC0FB988A7517A532B7ED25C3BDB943FFBF81A6D308A615A851547D949C442F373E6913B67A80EEEC139FD319946055168E6EEEB1D56C311AE6EF39219A109573BEED8454A58D843F93BABF69D2FB14F1FE66062D557D62D238F8CF8A47CC520209605BA7DB017100E9AF1E1BACC748EBDB4723330A681D408AB21BACBEFAF8C667BD60719F6C16FCF50543A65F472570B0E3AE00A91EF8A1421B1C29E5C442EFB658B3EF66D64C6F30263D042476E1438DABFCDA4BF81C49C5BF2E844FEC1631C254EF0693350AD9973FCF83E1F37DCC39D99C3AC768A3BE3B6A2AA7A7C81F430F0C41CF86DAAF57A122430C299968D142624A7650DC695AB131CB290A6232CF208B73D4B1AD455F294499A5259695A1025964447DA8F333A3E71D550F864C3EF074EC9D259320941CEB4D0C42C3351A9B2927B1FDED5177974031CA8FF050A3F75BB5B1AAF77C6E38D028925BE7F0DFB103B9E55E9B4002227406E56155042C9BAA14E70E8470A3ADB8782DC5BE1CB04F52188C366A445C1AB28E36F1960DE3CBFBF7B0D209D02D931638A622133D645755950F3D78F63066B7CC5CF63AB216AB9561C9C76BDD4581EBF920329D3A077529DE9C01FDA55305C64A1A3D7437596321FA8CB1739CEB774F8D527ED5FEC87DC4E6CC5B1614ADF00EC40021C2080CB3A0BC10A2AD55018DF224B16F53D42FC8819D788D6BFD4D0D4D9CA3AE9CCAB447819EDE19EE25D06EE53E69F1623D9C3995389F258F7E48A26F035511B253F0286F6AEEB4F516D95CCD0AA82C945BAA0F006919632A8FB702563CAAEBD0BFBC914E4182F84EE7437F5E435885D51B0E1359451048398DFDE0B2EC5EB96CEE8C0D4E17DE53D9B1C118269A04E736322C3A2D96928AAE7357C143A2456288891370B74A37BE3DBB39A784F8229F8815A980C0238CD55C015FDABF1D0F376C5E8F9C879430ECFB6DFD725CEA06B551B7382DF8C5A165FD3D13B5EA5CFF20AB023A25688518F4E5BD845225197871FBAA8807E4E13EA6BD2A9F49807A019E6EDDC65AAFE04DCC9F44483B404E3FE3C5A1BA40D3F83BCAC493683EE5CF736ABBE6E448531DC11A5CBC689F9802666EEABB83DEC0C1CA76A1FB961C780EE6CFD1DE805A6AF790A7DFA0E3178F9C31C12DFA54294B94AAFA1B6EA65787A5A14ED598CEBA820B03F32BDBFF20DDB3E604CAD5EE7EBABC843D34853FFB50382039FE9541F7CE763E21F660FD6F95891D6788A28CB2B7239418139086FDCC08A2F3BBC5224871EE365397CC8859BF14A7AA68D772AEAB6E82DA0128E33DF04055EDBD0AB4D067D4F59A6B391BEA9B3A59FFE9DC94F495F4474D424747490AF86356769A9BB45FD74293E1D8427253973FC6CFE45413AC6157C9C2678D1F8DAC9072D5C473009FA4A011990112BDB1D5F9AA08807926A44FA91542D2F2F74923053548D09C8E233E272A44427AAA4E9A17EC6DE2170C326ACE851A5EBDCCB2A8C775FBDD75E1386B87C9594FE261D4FFC6A8BA058A660BF902DE011673C5815A779B7B04AF712535B4025B4DB3E3886CB01B971511AD1D606A02DA96DF21F8C148FFD746E6D04F55828551870BBE451F569565572D6B48CA7D1A9AAD968E55920A9A05B30B4186E0CC42928C5B9555ABBCBB404ABBA093DCB70DC8CB35A2BEF42FBF7D70B89214140E858BEBD9BB6EE7C4EEF0DB4D0BD6690629FBFE79BAAB2A849E5C9C18E4F329A72C3B3615A702FB9EE00646B1E2A624E8146A8F96169A53D960673A5FD2CFE0FBC73421CF9F545E26FF5F99B46479A1F837B3047A64929192E85DB440C79CCB10264BEAAE855549BF0B6896B6009F74E7C642271ECB659734A38EE7429F1E5E6394888D198FF273E16A446EFBFBDA9148E8CD7A8575AD45C0B3B50BF66E061FED0A332278125A58D3CF45B486F69C38C8FD9E5AD2A3D9EEE743CD66D9E77C73632481F0C8B819368D74A7B52FF8BEC9177D43F9CFAC4D4931453B3445F47FEB52D2506275AB6F0FE468CC5EFC23BFEE85034E5BA483A3B6D738BE827DE5F198F5BB625CA75B546B0D331567E0EF4D0E864CAE41A52347CF2F9A8A3DECED91FE77336840C0624629AEE34D716E3CD6E8EAB0FC3036AFF3D174932D0DD1177E20A116EFAC860C4D23068493C2D6D660F2C4A2523CF061C8C74097CC2C34385896642CCF584C2962DE62D82AB11B30FB9E323A492FC54230404D276CEE3F21EFCD2CFC5DA90608A69A4073D9183616AC0F9C2EF9FAEF29D121FC8F0A24BD739E41E95C851FD251972A94AEEE51691809367B5C521127F5DE032B3BAC77FD81DDCDD5972ED9918D3C4A96CF65403A7FB8E485D3A860208C1AF47E82F396A5202D12D1217CD1E92FA5EE1F077B588F9DC8AB83C8F229D97DAC487D3A5B0F99A6A576A2536F1803E7FA552EBA401811C164E0B94813DC2E033953753353AFD49555796204B53C2EAFA79BCD9CFB1F3C9336BCD57B565192C8479E191BA3D0944B809864C7B60D0B28B109A0770F91081A17E1FC97D9593F03E699BC1722130BD707811B62BA75DC29B0ACC94CB11AB8B696FF03C2D3FD71275861E01D50F4069EFEC8708E75A55FA205D3E72D90F352CCE00680220F49030A0FF4095894BFD9BBA62A4EE8A92A40F09F61BD00164497650E143C4974E38FB4C4667E819B060D45F92B8665A56A22B6C68F82AD8C03F3CB5DA44A80828F47C39B992343ABBE5498537706EABD31BFA3F28683E460E127C172B324F7BC8DC36AE4C3CA0B47DF0A073AEA5A15FD090D2BE5A68EFB0B6262BA7261D0F89A5DDCC5B39C05FA67B35C137C7CA9768612C369A5F81215FFC85E81600F99E50E126CDD0D9462610312C4314A7B7F8FB9EC335F9D6752A24BC6C6EA3D849A571A7604690FCC68F2B062659233E0CB8449CFC873DC3947A7BFCA9147CE77247F786D17D376157FEC8440AF3B01BF6E6AFB41A16DBB267AE02E5DAA453A2023393EE61AC6C9F70E3350DBEF9B890CA5005B264C8CD5B6C352F237A4196A3AB2631C76FC1BA5A7A103336D1DE036DF5622B8AAE913D496638823C7D49252F4C816298B7E130F8A5AB38548596798E31B12E08261C0509E8E851374F8DED30FB13EA407B6AB6043122D4F358E8C3DFCEF3FABC1B1D09308C58BFDF8FF9B917CE076971AC2902D70B29D6F33701A1002481784B832115DE0AA9C412BAD6743779CD35518F522395E123B0F13606097AB7AFAB0E817D94ACF18B670254F01C24B5AF8979DC5F80D26DD25A6F60249804A0596B0B3B65A5FC28986FF3EB2130F1078C3B967C9F9E582ED8A0BF13353CCCE352620B8510F94FF98D4143FF8CE73C916957E3E9018800BB02DC52C4E6E8F35B931088CF357BF692FB9CEF5E5A68A1F1641DB9DD58D3E1DA51507234967B02A44DE85C6C5E7D6BED6140FD6498A250AA3A6160D93F2602BF3EBE6DE4F9B1FC709A4B10720C35A2E6B1CC7C07E68D31AEFFD80A351A397527D5AD3D901F86F3AF548CDA4D91DE9E04CE7A2762AA0AD8B17CBEE511B7B75650D5F8AC6EF802D72AD8DCB359A2E41D1139F576A012027DDAC48C235091D1E79D267977A9323F0CCB92FC99DD477F0E88A1C3E7B240B63EC37AD19A7BDF8688F6E52633397C63EF0AF8BB0D1578A083558D9AAEDDFA354C603AB42E50A490EC1C6392F8214BD51C3DD4DCC995B9294ADA5E44FE34D7BB0CBFD9D2FEB8359E6657DEE0A1D5A85209A88E70F91D6BB6A7116954025F92A744B896485768D663F8467661C6D8776521FC3B45B656C1EF44132BDD4EE18C06C5279BF94F600A90D22A78B2A6F9FFCEAA042206245CE0FDE6A6F4344D091516BA7FAC9F4C7E25B1F19A739BEE1F484A135D234ABF85D26846AE02987BC653C90551F8374D60823BEE601499E9450FD76EAA10EFD5DA57349D4458AE6B8D463B36AA968C1710FCB2DAF75C4F71488EF19E3B39C1A573F33AFAC3AA0917E3D05129E9533EBE73C05DAD6E04EE7E17C344B5570030538485C2B75B814376E3AAF7170B2587E540332A6ADC14476F04BCE3005553B0D3900DA553908762E27CE6B9B31DC772E23B5F7523D5C577603E63A29EAE43119FEA1376FD802454B8D047FDE55A84413D4A00D4F5EE9CFDDA8635253CE4C44439C529D90A407562070ABC439DD79205BA1265549D7E2BB15D15AC774B0F27D40BF3C42030BB46FCC49ECDEB32496002F42F6B1A7F7FA6CBA9687BE4A40E16F7E2D847AA1361D4FECB78800EA0308F13B5A9732BA40D649C5D0876C84F6248C677D6085A01B5FF825A7C4F06FFA81B78E9583F0A8AFB53D573AB5D1AFB0CBF692FB986CA282511FFB9F0C127BE6DACA4ED0DEEE7334E3B7C8232E65668E4974DBCD5D736115140B4463F409A2A2C17160EA82DED1DC835D17E0A47F54EBB86D569836AD88666939BCD3AA0F3FDA152561BD057C0B6AA2FE4A005B817094B658AB87B30A6B13E8FA814922FE32741D3DCB7F1E97E63B61EBF53863880AE7C8281C5164B7260AB94DD40BFBC147A4CBD5E21EB5F1751F716F26C71533B060EF9980FAE1E10F6516C1C5D1C3DB40A0A4506C8DA5EC32D7B2FDDFF5915FF4869C731B62414DC3EAC57C07F42BA45B962F8523C4C722BB0367A848E3011AD3B7CC3F3450BFDD5F568D81C729E43DAC0EA149C60C3B30BE7AB818FB9805DFE8ABA84F28EC6F0BD65126CC37455A911E29FC3A41B854C77B9B80BA98A7E1C651AE981C809514BF9E967274E56FF8791F26FAD19BCB9D3056E0D48C6CB5732F6F607B89AD3E1189153D9BAB45E0E20CA3079A71AD102B861068C59FC08752E823430484F46FCBE01470BC5E2FF2EA76FEC7E9651A01815F35F478447871C88DEAB28701DB5D18333BB95C3C54CC561E7D4FA1F99DEDCE21C2A54FFFF68668A2A82E737C6AE89AA0AEB62A60A4440B689D5AC728D0CEF0FDE4663F360D193DB451BA942559DAB394B5014AE7C36D7D7D99E7619CBF3FD2D93E769704A963929578750FEAB0E50EC3A63273CF84083977E9C29C26B7A1F411E7BBE7E342AFEB516EB46E4D5D92CDB4D89DF58CCC0255353C201BE7C66CDDD780FD02F5FE2B80F6472161CF99EC1956F5E84D19AB6844E466B1A62294038A02D797CD5F90D8B5AC1CFE44ABA1859B5006C4F2DE1EC32C18B91D8F4C855E6143965EF8021EB9C61D33A1FCAC239843E016561BCF11ACE3136AB4A672486927BE9C0115438D99314DFED5AB7DC67FF420DAAE362E439231E97AE4976A1253D4C9793E10228300527C25CDD13C01664CEBBF85979933F87B0ACF2BC8A901C73AAB00E103886FD29DDA5CB90975C0F5E71497ACAA8DBB3A9680C305535737A9, 0x93253B2062C47CEDE8BD3D7AEF0549D944B411, '08d2a6e7-72e4-4c2c-b1c1-4cd363f653c1', '2479-02-01 02:49:22.037', '1963-09-15 02:54')"); diff --git a/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt b/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt index 11009693..8fed9d3c 100644 --- a/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt +++ b/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt @@ -72,7 +72,7 @@ function FetchField($stmt, $idx, $numFields, $errorExpected) function PrintError($errorExpected = true) { $errors = sqlsrv_errors(SQLSRV_ERR_ALL); - if (count($errors) > 0) + if (!empty($errors) && count($errors) > 0) { $e = $errors[0]; var_dump($e['message']); diff --git a/test/functional/sqlsrv/sqlsrv_input_param_unknown_encoding.phpt b/test/functional/sqlsrv/sqlsrv_input_param_unknown_encoding.phpt index 4b76ca31..65e90fc6 100644 --- a/test/functional/sqlsrv/sqlsrv_input_param_unknown_encoding.phpt +++ b/test/functional/sqlsrv/sqlsrv_input_param_unknown_encoding.phpt @@ -52,7 +52,7 @@ sqlsrv_close($conn); ?> --EXPECTREGEX-- -Notice\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 26 +(Warning|Notice)\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' (\(this will throw an Error in a future version of PHP\) )?in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 26 Array \( \[0\] => Array @@ -67,7 +67,7 @@ Array \) -Notice\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 33 +(Warning|Notice)\: Use of undefined constant SQLSRV_ENC_UNKNOWN - assumed \'SQLSRV_ENC_UNKNOWN\' (\(this will throw an Error in a future version of PHP\) )?in .+(\/|\\)sqlsrv_input_param_unknown_encoding\.php on line 33 Array \( \[0\] => Array @@ -81,4 +81,3 @@ Array \) \) - From e3fd38c87304183bbaac325eaee7ffda18f9c758 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 24 Jul 2017 15:12:03 -0700 Subject: [PATCH 12/17] removed redundant count() as per review comment --- test/functional/sqlsrv/TC21_Connection.phpt | 2 +- test/functional/sqlsrv/sqlsrv_close.phpt | 2 +- test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/sqlsrv/TC21_Connection.phpt b/test/functional/sqlsrv/TC21_Connection.phpt index 0a6872a7..0d3b53dc 100644 --- a/test/functional/sqlsrv/TC21_Connection.phpt +++ b/test/functional/sqlsrv/TC21_Connection.phpt @@ -36,7 +36,7 @@ function ConnectionTest() Trace("\nValid connection attempt (to $server) ....\n"); $conn2 = Connect(); $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS); - if (!empty($errors) && count($errors) != 0) + if (!empty($errors)) { die("No errors were expected on valid connection attempts."); } diff --git a/test/functional/sqlsrv/sqlsrv_close.phpt b/test/functional/sqlsrv/sqlsrv_close.phpt index 8841141e..2c8e2d10 100644 --- a/test/functional/sqlsrv/sqlsrv_close.phpt +++ b/test/functional/sqlsrv/sqlsrv_close.phpt @@ -14,7 +14,7 @@ using an already closed connection. $conn2 = Connect(); $stmt1 = sqlsrv_query($conn2, "IF OBJECT_ID('PHPTable', 'U') IS NOT NULL DROP TABLE [PHPTable]"); $errors = sqlsrv_errors(); - if (!empty($errors) && count($errors) != 0) + if (!empty($errors)) { die("No errors were expected on valid connection attempts."); } diff --git a/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt b/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt index 8fed9d3c..333c87d5 100644 --- a/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt +++ b/test/functional/sqlsrv/sqlsrv_fetch_field_twice_data_types.phpt @@ -72,7 +72,7 @@ function FetchField($stmt, $idx, $numFields, $errorExpected) function PrintError($errorExpected = true) { $errors = sqlsrv_errors(SQLSRV_ERR_ALL); - if (!empty($errors) && count($errors) > 0) + if (!empty($errors)) { $e = $errors[0]; var_dump($e['message']); From 5a3f2ba2010035d146710d9811c966cbab6e0de5 Mon Sep 17 00:00:00 2001 From: David Puglielli Date: Tue, 25 Jul 2017 11:44:01 -0700 Subject: [PATCH 13/17] Update version.h Fixed the location of the dash, since pecl does not like dashes it gets included in VER_FILEVERSION_STR instead of SEMVER_PRERELEASE. --- source/shared/version.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/shared/version.h b/source/shared/version.h index 7237b2a8..98eebfae 100644 --- a/source/shared/version.h +++ b/source/shared/version.h @@ -35,7 +35,7 @@ // for stable releases should be empty // "-RC" for release candidates // "-preview" for ETP -#define SEMVER_PRERELEASE "-preview" +#define SEMVER_PRERELEASE "preview" // Semantic versioning build metadata, build meta data is not counted in precedence order. #define SEMVER_BUILDMETA @@ -47,8 +47,10 @@ // Main version, dot separated 3 digits, Major.Minor.Patch #define VER_APIVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_PATCH ) -// Remove "-" if SEMVER_PRERELEASE is empty (for stable releases) -#define VER_FILEVERSION_STR VER_APIVERSION_STR SEMVER_PRERELEASE SEMVER_BUILDMETA +// For preview release, we want the following: +// #define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA +// because pecl doesn't like dashes. However, if SEMVER_PRERELEASE is empty, the "-" must be removed +#define VER_FILEVERSION_STR VER_APIVERSION_STR "-" SEMVER_PRERELEASE SEMVER_BUILDMETA #define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_PATCH,SQLVERSION_BUILD // PECL package version macros (can't have '-' or '+') From a0896f2da9318bee32c4d8c665dd6cdda7e54e5d Mon Sep 17 00:00:00 2001 From: Hadis Fard Date: Wed, 26 Jul 2017 15:29:52 -0700 Subject: [PATCH 14/17] Fixed the AppVeyor badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75b2a438..c78c480b 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Thank you for taking time to take our February survey. Let us know how we are do |-------------------------|--------------------------| ------------------ | [![av-image][]][av-site]| [![tv-image][]][tv-site] |[![Coverage Status][]][coveralls-site] -[av-image]: https://ci.appveyor.com/api/projects/status/github/Microsoft/msphpsql?branch=dev&svg=true -[av-site]: https://ci.appveyor.com/project/Microsoft-PHPSQL/msphpsql +[av-image]: https://ci.appveyor.com/api/projects/status/xhp4nq9ouljnhxqf/branch/dev?svg=true +[av-site]: https://ci.appveyor.com/project/Microsoft-PHPSQL/msphpsql-frhmr/branch/dev [tv-image]: https://travis-ci.org/Microsoft/msphpsql.svg?branch=dev [tv-site]: https://travis-ci.org/Microsoft/msphpsql/ [Coverage Status]: https://coveralls.io/repos/github/Microsoft/msphpsql/badge.svg?branch=dev From ecf316fb0f16d81c3956a18e202f6f19d8340397 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 31 Jul 2017 13:13:10 -0700 Subject: [PATCH 15/17] added to changelog for 5.0.0-preveiw release --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaeeb198..645b27c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## Windows/Linux/MAC 5.0.0-preview - 2017-07-31 +Updated PECL release packages. Here is the list of updates: + +### Added +- Added support for PHP 7.2 Beta 1 + +### Changed +- Implementation of PDO::lastInsertId($name) to return the last inserted sequence number if the sequence name is supplied to the function ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid)) + +### Deprecated +- No longer support Ubuntu 15 +- Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row + +### Limitation +- No support for inout / output params when using sql_variant type + +### Known Issues +- When pooling is enabled in Linux or MAC + - unixODBC <= 2.3.4 (Linux and MAC) might not return proper diagnostics information, such as error messages, warnings and informative messages + - due to this unixODBC bug, fetch large data (such as xml, binary) as streams as a workaround. See the examples [here](https://github.com/Microsoft/msphpsql/wiki/Connection-Pooling-on-Linux-and-Mac) + ## Windows/Linux/MAC 4.3.0 - 2017-07-06 Production Ready release for SQLSRV and PDO_SQLSRV drivers on Sierra, El Capitan, Debian 8, Ubuntu 15, Ubuntu 16, CentOS 7, and Windows. Here is the changlog since the last Production Ready release. From 408f9a29e1309a223b776844eacda14f1e764368 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 31 Jul 2017 13:16:56 -0700 Subject: [PATCH 16/17] changed the word deprecated to removed --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645b27c4..d94b5729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Updated PECL release packages. Here is the list of updates: ### Changed - Implementation of PDO::lastInsertId($name) to return the last inserted sequence number if the sequence name is supplied to the function ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid)) -### Deprecated +### Removed - No longer support Ubuntu 15 - Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row From 9009af253c3107a6989cc16ff89f0d7202b3e97b Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 31 Jul 2017 13:22:00 -0700 Subject: [PATCH 17/17] added link to lastInsertId wiki --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94b5729..be5eb651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ Updated PECL release packages. Here is the list of updates: ### Removed - No longer support Ubuntu 15 -- Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row +- Supplying tablename into PDO::lastInsertId($name) no longer return the last inserted row ([lastInsertId](https://github.com/Microsoft/msphpsql/wiki/Features#lastinsertid)) ### Limitation - No support for inout / output params when using sql_variant type