From 0762fa3353131c17448b197bb73a5a15ec96faec Mon Sep 17 00:00:00 2001 From: Hadis Kakanejadi Fard Date: Mon, 9 Jan 2017 15:36:38 -0800 Subject: [PATCH] deleted shared folder inside pdo --- source/pdo_sqlsrv/shared/core_conn.cpp | 774 ------- source/pdo_sqlsrv/shared/core_init.cpp | 175 -- source/pdo_sqlsrv/shared/core_results.cpp | 1391 ------------ source/pdo_sqlsrv/shared/core_sqlsrv.h | 2263 ------------------- source/pdo_sqlsrv/shared/core_stmt.cpp | 2471 --------------------- source/pdo_sqlsrv/shared/core_stream.cpp | 261 --- source/pdo_sqlsrv/shared/core_util.cpp | 400 ---- source/pdo_sqlsrv/shared/msodbcsql.h | 2343 ------------------- source/pdo_sqlsrv/shared/template.rc | 83 - source/pdo_sqlsrv/shared/version.h | 35 - 10 files changed, 10196 deletions(-) delete mode 100644 source/pdo_sqlsrv/shared/core_conn.cpp delete mode 100644 source/pdo_sqlsrv/shared/core_init.cpp delete mode 100644 source/pdo_sqlsrv/shared/core_results.cpp delete mode 100644 source/pdo_sqlsrv/shared/core_sqlsrv.h delete mode 100644 source/pdo_sqlsrv/shared/core_stmt.cpp delete mode 100644 source/pdo_sqlsrv/shared/core_stream.cpp delete mode 100644 source/pdo_sqlsrv/shared/core_util.cpp delete mode 100644 source/pdo_sqlsrv/shared/msodbcsql.h delete mode 100644 source/pdo_sqlsrv/shared/template.rc delete mode 100644 source/pdo_sqlsrv/shared/version.h diff --git a/source/pdo_sqlsrv/shared/core_conn.cpp b/source/pdo_sqlsrv/shared/core_conn.cpp deleted file mode 100644 index 8dde4b5c..00000000 --- a/source/pdo_sqlsrv/shared/core_conn.cpp +++ /dev/null @@ -1,774 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_conn.cpp -// -// Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" - -#include -#include -#include -#include - -#include -#include - -// *** internal variables and constants *** - -namespace { - -// *** internal constants *** -// an arbitrary figure that should be large enough for most connection strings. -const int DEFAULT_CONN_STR_LEN = 2048; - -// length of buffer used to retrieve information for client and server info buffers -const int INFO_BUFFER_LEN = 256; - -// processor architectures -const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" }; - -// ODBC driver name. -const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"}; - -// default options if only the server is specified -const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}"; - -// connection option appended when no user name or password is given -const char CONNECTION_OPTION_NO_CREDENTIALS[] = "Trusted_Connection={Yes};"; - -// connection option appended for MARS when MARS isn't explicitly mentioned -const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};"; - -// *** internal function prototypes *** - -void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd, - HashTable* options_ht, const connection_option valid_conn_opts[], - void* driver,_Inout_ std::string& connection_string TSRMLS_DC ); -void determine_server_version( sqlsrv_conn* conn TSRMLS_DC ); -const char* get_processor_arch( void ); -void get_server_version( sqlsrv_conn* conn, char** server_version, SQLSMALLINT& len TSRMLS_DC ); -connection_option const* get_connection_option( sqlsrv_conn* conn, const char* key, SQLULEN key_len TSRMLS_DC ); -void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC ); - -} - -// core_sqlsrv_connect -// opens a connection and returns a sqlsrv_conn structure. -// Parameters: -// henv_cp - connection pooled env context -// henv_ncp - non connection pooled env context -// server - name of the server we're connecting to -// uid - username -// pwd - password -// options_ht - zend_hash list of options -// err - error callback to put into the connection's context -// valid_conn_opts[] - array of valid driver supported connection options. -// driver - reference to caller -// Return -// A sqlsrv_conn structure. An exception is thrown if an error occurs - -sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp, driver_conn_factory conn_factory, - const char* server, const char* uid, const char* pwd, - HashTable* options_ht, error_callback err, const connection_option valid_conn_opts[], - void* driver, const char* driver_func TSRMLS_DC ) - -{ - SQLRETURN r; - std::string conn_str; - conn_str.reserve( DEFAULT_CONN_STR_LEN ); - sqlsrv_malloc_auto_ptr conn; - sqlsrv_malloc_auto_ptr wconn_string; - unsigned int wconn_len = 0; - - try { - - sqlsrv_context* henv = &henv_cp; // by default use the connection pooling henv - - // check the connection pooling setting to determine which henv to use to allocate the connection handle - // we do this earlier because we have to allocate the connection handle prior to setting attributes on - // it in build_connection_string_and_set_conn_attr. - - if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) { - - zval* option_z = NULL; - int zr = SUCCESS; - - option_z = zend_hash_index_find(options_ht, SQLSRV_CONN_OPTION_CONN_POOLING); - if (option_z) { - - // if the option was found and it's not true, then use the non pooled environment handle - if(( Z_TYPE_P( option_z ) == IS_STRING && !core_str_zval_is_true( option_z )) || !zend_is_true( option_z ) ) { - - henv = &henv_ncp; - } - } - } - - SQLHANDLE temp_conn_h; - core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC ); - - conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC ); - conn->set_func( driver_func ); - - for ( std::size_t i = DRIVER_VERSION::MIN; i <= DRIVER_VERSION::MAX; ++i ) { - conn_str = CONNECTION_STRING_DRIVER_NAME[i]; - build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, - conn_str TSRMLS_CC ); - - // We only support UTF-8 encoding for connection string. - // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); - - wconn_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast(conn_str.length()), &wconn_len); - CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message()) - { - throw core::CoreException(); - } - - SQLSMALLINT output_conn_size; - r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get()), - static_cast( wconn_len ), NULL, - 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); - // clear the connection string from memory to remove sensitive data (such as a password). - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - conn_str.clear(); - - if( !SQL_SUCCEEDED( r )) { - SQLCHAR state[SQL_SQLSTATE_BUFSIZE]; - SQLSMALLINT len; - SQLRETURN r = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len ); - bool missing_driver_error = ( SQL_SUCCEEDED( r ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' && - state[4] == '2' ); - // if it's a IM002, meaning that the correct ODBC driver is not installed - CHECK_CUSTOM_ERROR( missing_driver_error && ( i == DRIVER_VERSION::MAX ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) { - throw core::CoreException(); - } - if ( !missing_driver_error ) { - break; - } - } else { - conn->driver_version = static_cast( i ); - break; - } - - } - CHECK_SQL_ERROR( r, conn ) { - throw core::CoreException(); - } - - CHECK_SQL_WARNING_AS_ERROR( r, conn ) { - throw core::CoreException(); - } - - // determine the version of the server we're connected to. The server version is left in the - // connection upon return. - determine_server_version( conn TSRMLS_CC ); - - } - catch( std::bad_alloc& ) { - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - conn->invalidate(); - DIE( "C++ memory allocation failure building the connection string." ); - } - catch( std::out_of_range const& ex ) { - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - LOG( SEV_ERROR, "C++ exception returned: %1!s!", ex.what() ); - conn->invalidate(); - throw; - } - catch( std::length_error const& ex ) { - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - LOG( SEV_ERROR, "C++ exception returned: %1!s!", ex.what() ); - conn->invalidate(); - throw; - } - catch( core::CoreException& ) { - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - conn->invalidate(); - throw; - } - - sqlsrv_conn* return_conn = conn; - conn.transferred(); - - return return_conn; -} - - - -// core_sqlsrv_begin_transaction -// Begins a transaction on a specified connection. The current transaction -// includes all statements on the specified connection that were executed after -// the call to core_sqlsrv_begin_transaction and before any calls to -// core_sqlsrv_rollback or core_sqlsrv_commit. -// The default transaction mode is auto-commit. This means that all queries -// are automatically committed upon success unless they have been designated -// as part of an explicit transaction by using core_sqlsrv_begin_transaction. -// Parameters: -// sqlsrv_conn*: The connection with which the transaction is associated. - -void core_sqlsrv_begin_transaction( sqlsrv_conn* conn TSRMLS_DC ) -{ - try { - - DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_begin_transaction: connection object was null." ); - - core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast( SQL_AUTOCOMMIT_OFF ), - SQL_IS_UINTEGER TSRMLS_CC ); - } - catch ( core::CoreException& ) { - throw; - } -} - -// core_sqlsrv_commit -// Commits the current transaction on the specified connection and returns the -// connection to the auto-commit mode. The current transaction includes all -// statements on the specified connection that were executed after the call to -// core_sqlsrv_begin_transaction and before any calls to core_sqlsrv_rollback or -// core_sqlsrv_commit. -// Parameters: -// sqlsrv_conn*: The connection on which the transaction is active. - -void core_sqlsrv_commit( sqlsrv_conn* conn TSRMLS_DC ) -{ - try { - - DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_commit: connection object was null." ); - - core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_COMMIT TSRMLS_CC ); - - core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast( SQL_AUTOCOMMIT_ON ), - SQL_IS_UINTEGER TSRMLS_CC ); - } - catch ( core::CoreException& ) { - throw; - } -} - -// core_sqlsrv_rollback -// Rolls back the current transaction on the specified connection and returns -// the connection to the auto-commit mode. The current transaction includes all -// statements on the specified connection that were executed after the call to -// core_sqlsrv_begin_transaction and before any calls to core_sqlsrv_rollback or -// core_sqlsrv_commit. -// Parameters: -// sqlsrv_conn*: The connection on which the transaction is active. - -void core_sqlsrv_rollback( sqlsrv_conn* conn TSRMLS_DC ) -{ - try { - - DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_rollback: connection object was null." ); - - core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC ); - - core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast( SQL_AUTOCOMMIT_ON ), - SQL_IS_UINTEGER TSRMLS_CC ); - - } - catch ( core::CoreException& ) { - throw; - } -} - -// core_sqlsrv_close -// Called when a connection resource is destroyed by the Zend engine. -// Parameters: -// conn - The current active connection. -void core_sqlsrv_close( sqlsrv_conn* conn TSRMLS_DC ) -{ - // if the connection wasn't successful, just return. - if( conn == NULL ) - return; - - try { - - // rollback any transaction in progress (we don't care about the return result) - core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC ); - } - catch( core::CoreException& ) { - LOG( SEV_ERROR, "Transaction rollback failed when closing the connection." ); - } - - // disconnect from the server - SQLRETURN r = SQLDisconnect( conn->handle() ); - if( !SQL_SUCCEEDED( r )) { - LOG( SEV_ERROR, "Disconnect failed when closing the connection." ); - } - - // free the connection handle - conn->invalidate(); - - sqlsrv_free( conn ); -} - -// core_sqlsrv_prepare -// Create a statement object and prepare the SQL query passed in for execution at a later time. -// Parameters: -// stmt - statement to be prepared -// sql - T-SQL command to prepare -// sql_len - length of the T-SQL string - -void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, SQLLEN sql_len TSRMLS_DC ) -{ - try { - - // convert the string from its encoding to UTf-16 - // if the string is empty, we initialize the fields and skip since an empty string is a - // failure case for utf16_string_from_mbcs_string - sqlsrv_malloc_auto_ptr wsql_string; - unsigned int wsql_len = 0; - if( sql_len == 0 || ( sql[0] == '\0' && sql_len == 1 )) { - wsql_string = reinterpret_cast( sqlsrv_malloc( sizeof( wchar_t ))); - wsql_string[0] = L'\0'; - wsql_len = 0; - } - else { - - if (sql_len > INT_MAX) - { - LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded."); - throw core::CoreException(); - } - - SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : - stmt->encoding() ); - wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast( sql ), - static_cast( sql_len ), &wsql_len ); - CHECK_CUSTOM_ERROR( wsql_string == NULL, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, - get_last_error_message() ) { - throw core::CoreException(); - } - } - - // prepare our wide char query string - core::SQLPrepareW( stmt, reinterpret_cast( wsql_string.get() ), wsql_len TSRMLS_CC ); - } - catch( core::CoreException& ) { - - throw; - } -} - -// core_sqlsrv_get_server_version -// Determines the vesrion of the SQL Server we are connected to. Calls a helper function -// get_server_version to get the version of SQL Server. -// Parameters: -// conn - The connection resource by which the client and server are connected. -// *server_version - zval for returning results. - -void core_sqlsrv_get_server_version( sqlsrv_conn* conn, _Out_ zval *server_version TSRMLS_DC ) -{ - try { - - sqlsrv_malloc_auto_ptr buffer; - SQLSMALLINT buffer_len = 0; - - get_server_version( conn, &buffer, buffer_len TSRMLS_CC ); - core::sqlsrv_zval_stringl( server_version, buffer, buffer_len ); - if (NULL != buffer) { - sqlsrv_free( buffer ); - } - buffer.transferred(); - } - - catch( core::CoreException& ) { - throw; - } -} - -// core_sqlsrv_get_server_info -// Returns the Database name, the name of the SQL Server we are connected to -// and the version of the SQL Server. -// Parameters: -// conn - The connection resource by which the client and server are connected. -// *server_info - zval for returning results. - -void core_sqlsrv_get_server_info( sqlsrv_conn* conn, _Out_ zval *server_info TSRMLS_DC ) -{ - try { - - sqlsrv_malloc_auto_ptr buffer; - SQLSMALLINT buffer_len = 0; - - // initialize the array - core::sqlsrv_array_init( *conn, server_info TSRMLS_CC ); - - // Get the database name - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_DATABASE_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, server_info, "CurrentDatabase", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - - // Get the server version - get_server_version( conn, &buffer, buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerVersion", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - - // Get the server name - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_SERVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerName", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - } - - catch( core::CoreException& ) { - throw; - } -} - -// core_sqlsrv_get_client_info -// Returns the ODBC driver's dll name, version and the ODBC version. -// Parameters -// conn - The connection resource by which the client and server are connected. -// *client_info - zval for returning the results. - -void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC ) -{ - try { - - sqlsrv_malloc_auto_ptr buffer; - SQLSMALLINT buffer_len = 0; - - // initialize the array - core::sqlsrv_array_init( *conn, client_info TSRMLS_CC ); - - // Get the ODBC driver's dll name - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, client_info, "DriverDllName", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - - // Get the ODBC driver's ODBC version - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_DRIVER_ODBC_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, client_info, "DriverODBCVer", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - - // Get the OBDC driver's version - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_DRIVER_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - core::sqlsrv_add_assoc_string( *conn, client_info, "DriverVer", buffer, 0 /*duplicate*/ TSRMLS_CC ); - buffer.transferred(); - - } - - catch( core::CoreException& ) { - throw; - } -} - - -// core_is_conn_opt_value_escaped -// determine if connection string value is properly escaped. -// Properly escaped means that any '}' should be escaped by a prior '}'. It is assumed that -// the value will be surrounded by { and } by the caller after it has been validated - -bool core_is_conn_opt_value_escaped( const char* value, size_t value_len ) -{ - // if the value is already quoted, then only analyse the part inside the quotes and return it as - // unquoted since we quote it when adding it to the connection string. - if( value_len > 0 && value[0] == '{' && value[ value_len - 1 ] == '}' ) { - ++value; - value_len -= 2; - } - // check to make sure that all right braces are escaped - size_t i = 0; - while( ( value[i] != '}' || ( value[i] == '}' && value[i+1] == '}' )) && i < value_len ) { - // skip both braces - if( value[i] == '}' ) - ++i; - ++i; - } - if( i < value_len && value[i] == '}' ) { - return false; - } - - return true; -} - - -// *** internal connection functions and classes *** - -namespace { - -connection_option const* get_connection_option( sqlsrv_conn* conn, SQLULEN key, - const connection_option conn_opts[] TSRMLS_DC ) -{ - for( int opt_idx = 0; conn_opts[ opt_idx ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) { - - if( key == conn_opts[ opt_idx ].conn_option_key ) { - - return &conn_opts[ opt_idx ]; - } - } - - SQLSRV_ASSERT( false, "Invalid connection option, should have been validated by the driver layer." ); - return NULL; // avoid a compiler warning -} - -// says what it does, and does what it says -// rather than have attributes and connection strings as ODBC does, we unify them into a hash table -// passed to the connection, and then break them out ourselves and either set attributes or put the -// option in the connection string. - -void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd, - HashTable* options, const connection_option valid_conn_opts[], - void* driver,_Inout_ std::string& connection_string TSRMLS_DC ) -{ - bool credentials_mentioned = false; - bool mars_mentioned = false; - connection_option const* conn_opt; - int zr = SUCCESS; - - try { - - // Add the server name - common_conn_str_append_func( ODBCConnOptions::SERVER, server, strlen( server ), connection_string TSRMLS_CC ); - - // if uid is not present then we use trusted connection. - if(uid == NULL || strlen( uid ) == 0 ) { - - connection_string += "Trusted_Connection={Yes};"; - } - else { - - bool escaped = core_is_conn_opt_value_escaped( uid, strlen( uid )); - CHECK_CUSTOM_ERROR( !escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ) { - throw core::CoreException(); - } - - common_conn_str_append_func( ODBCConnOptions::UID, uid, strlen( uid ), connection_string TSRMLS_CC ); - - // if no password was given, then don't add a password to the connection string. Perhaps the UID - // given doesn't have a password? - if( pwd != NULL ) { - - escaped = core_is_conn_opt_value_escaped( pwd, strlen( pwd )); - CHECK_CUSTOM_ERROR( !escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ) { - - throw core::CoreException(); - } - - common_conn_str_append_func( ODBCConnOptions::PWD, pwd, strlen( pwd ), connection_string TSRMLS_CC ); - } - } - - // if no options were given, then we set MARS the defaults and return immediately. - if( options == NULL || zend_hash_num_elements( options ) == 0 ) { - connection_string += CONNECTION_STRING_DEFAULT_OPTIONS; - return; - } - - // workaround for a bug in ODBC Driver Manager wherein the Driver Manager creates a 0 KB file - // if the TraceFile option is set, even if the "TraceOn" is not present or the "TraceOn" - // flag is set to false. - if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) { - - zval* trace_value = NULL; - trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON); - - if (trace_value == NULL || !zend_is_true(trace_value)) { - - zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE ); - } - } - - zend_string *key = NULL; - zend_ulong index = -1; - zval* data = NULL; - - ZEND_HASH_FOREACH_KEY_VAL( options, index, key, data ) { - int type = HASH_KEY_NON_EXISTENT; - type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - - // The driver layer should ensure a valid key. - DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." ); - - conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC ); - - if( index == SQLSRV_CONN_OPTION_MARS ) { - mars_mentioned = true; - } - - conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC ); - } ZEND_HASH_FOREACH_END(); - - // MARS on if not explicitly turned off - if( !mars_mentioned ) { - connection_string += CONNECTION_OPTION_MARS_ON; - } - - } - catch( core::CoreException& ) { - throw; - } -} - - -// get_server_version -// Helper function which returns the version of the SQL Server we are connected to. - -void get_server_version( sqlsrv_conn* conn, char** server_version, SQLSMALLINT& len TSRMLS_DC ) -{ - try { - - sqlsrv_malloc_auto_ptr buffer; - SQLSMALLINT buffer_len = 0; - - buffer = static_cast( sqlsrv_malloc( INFO_BUFFER_LEN )); - core::SQLGetInfo( conn, SQL_DBMS_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC ); - *server_version = buffer; - len = buffer_len; - buffer.transferred(); - } - - catch( core::CoreException& ) { - throw; - } -} - - -// get_processor_arch -// Calls GetSystemInfo to verify the what architecture of the processor is supported -// and return the string of the processor name. -const char* get_processor_arch( void ) -{ - SYSTEM_INFO sys_info; - GetSystemInfo( &sys_info); - switch( sys_info.wProcessorArchitecture ) { - - case PROCESSOR_ARCHITECTURE_INTEL: - return PROCESSOR_ARCH[0]; - - case PROCESSOR_ARCHITECTURE_AMD64: - return PROCESSOR_ARCH[1]; - - case PROCESSOR_ARCHITECTURE_IA64: - return PROCESSOR_ARCH[2]; - - default: - DIE( "Unknown Windows processor architecture." ); - return NULL; - } -} - - -// some features require a server of a certain version or later -// this function determines the version of the server we're connected to -// and stores it in the connection. Any errors are logged before return. -// Exception is thrown when the server version is either undetermined -// or is invalid (< 2000). - -void determine_server_version( sqlsrv_conn* conn TSRMLS_DC ) -{ - SQLSMALLINT info_len; - char p[ INFO_BUFFER_LEN ]; - core::SQLGetInfo( conn, SQL_DBMS_VER, p, INFO_BUFFER_LEN, &info_len TSRMLS_CC ); - - errno = 0; - char version_major_str[ 3 ]; - SERVER_VERSION version_major; - memcpy_s( version_major_str, sizeof( version_major_str ), p, 2 ); - version_major_str[ 2 ] = '\0'; - version_major = static_cast( atoi( version_major_str )); - - CHECK_CUSTOM_ERROR( version_major == 0 && ( errno == ERANGE || errno == EINVAL ), conn, SQLSRV_ERROR_UNKNOWN_SERVER_VERSION ) - { - throw core::CoreException(); - } - - // SNAC won't connect to versions older than SQL Server 2000, so we know that the version is at least - // that high - conn->server_version = version_major; -} - -void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC ) -{ - // wrap a connection option in a quote. It is presumed that any character that need to be escaped will - // be escaped, such as a closing }. - TSRMLS_C; - - if( val_len > 0 && val[0] == '{' && val[ val_len - 1 ] == '}' ) { - ++val; - val_len -= 2; - } - conn_str += odbc_name; - conn_str += "={"; - conn_str.append( val, val_len ); - conn_str += "};"; -} - -} // namespace - -// simply add the parsed value to the connection string -void conn_str_append_func::func( connection_option const* option, zval* value, sqlsrv_conn* /*conn*/, std::string& conn_str - TSRMLS_DC ) -{ - const char* val_str = Z_STRVAL_P( value ); - size_t val_len = Z_STRLEN_P( value ); - common_conn_str_append_func( option->odbc_name, val_str, val_len, conn_str TSRMLS_CC ); -} - -// do nothing for connection pooling since we handled it earlier when -// deciding which environment handle to use. -void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ - TSRMLS_DC ) -{ - TSRMLS_C; -} - -// helper function to evaluate whether a string value is true or false. -// Values = ("true" or "1") are treated as true values. Everything else is treated as false. -// Returns 1 for true and 0 for false. - -size_t core_str_zval_is_true( zval* value_z ) -{ - SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "core_str_zval_is_true: This function only accepts zval of type string." ); - - char* value_in = Z_STRVAL_P( value_z ); - size_t val_len = Z_STRLEN_P( value_z ); - - // strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8) - size_t last_char = val_len - 1; - while( isspace( value_in[ last_char ] )) { - value_in[ last_char ] = '\0'; - val_len = last_char; - --last_char; - } - - // save adjustments to the value made by stripping whitespace at the end - Z_STRLEN_P( value_z ) = val_len; - - const char VALID_TRUE_VALUE_1[] = "true"; - const char VALID_TRUE_VALUE_2[] = "1"; - - if(( val_len == ( sizeof( VALID_TRUE_VALUE_1 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_1, val_len )) || - ( val_len == ( sizeof( VALID_TRUE_VALUE_2 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_2, val_len )) - ) { - - return 1; // true - } - - return 0; // false -} diff --git a/source/pdo_sqlsrv/shared/core_init.cpp b/source/pdo_sqlsrv/shared/core_init.cpp deleted file mode 100644 index 575d1f69..00000000 --- a/source/pdo_sqlsrv/shared/core_init.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_init.cpp -// -// Contents: common initialization routines shared by PDO and sqlsrv -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" - - -// module global variables (initialized in minit and freed in mshutdown) -HMODULE g_sqlsrv_hmodule = NULL; -OSVERSIONINFO g_osversion; - - -// core_sqlsrv_minit -// Module initialization -// This function is called once per execution by the driver layer's MINIT function. -// The primary responsibility of this function is to allocate the two environment -// handles used by core_sqlsrv_connect to allocate either a pooled or non pooled ODBC -// connection handle. -// Parameters: -// henv_cp - Environment handle for pooled connection. -// henv_ncp - Environment handle for non-pooled connection. -// err - Driver specific error handler which handles any errors during initialization. -void core_sqlsrv_minit( sqlsrv_context** henv_cp, sqlsrv_context** henv_ncp, error_callback err, const char* driver_func TSRMLS_DC ) -{ - SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ) ); - SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long )); - - *henv_cp = *henv_ncp = SQL_NULL_HANDLE; // initialize return values to NULL - - try { - - // get the version of the OS we're running on. For now this governs certain flags used by - // WideCharToMultiByte. It might be relevant to other things in the future. - g_osversion.dwOSVersionInfoSize = sizeof( g_osversion ); - BOOL ver_return = GetVersionEx( &g_osversion ); - if( !ver_return ) { - LOG( SEV_ERROR, "Failed to retrieve Windows version information." ); - throw core::CoreException(); - } - - SQLHANDLE henv = SQL_NULL_HANDLE; - SQLRETURN r; - - // allocate the non pooled environment handle - // we can't use the wrapper in core_sqlsrv.h since we don't have a context on which to base errors, so - // we use the direct ODBC function. - r = ::SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv ); - if( !SQL_SUCCEEDED( r )) { - throw core::CoreException(); - } - - *henv_ncp = new sqlsrv_context( henv, SQL_HANDLE_ENV, err, NULL ); - (*henv_ncp)->set_func( driver_func ); - - // set to ODBC 3 - core::SQLSetEnvAttr( **henv_ncp, SQL_ATTR_ODBC_VERSION, reinterpret_cast( SQL_OV_ODBC3 ), SQL_IS_INTEGER - TSRMLS_CC ); - - // disable connection pooling - core::SQLSetEnvAttr( **henv_ncp, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast( SQL_CP_OFF ), - SQL_IS_UINTEGER TSRMLS_CC ); - - // allocate the pooled envrionment handle - // we can't use the wrapper in core_sqlsrv.h since we don't have a context on which to base errors, so - // we use the direct ODBC function. - r = ::SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv ); - if( !SQL_SUCCEEDED( r )) { - throw core::CoreException(); - } - - *henv_cp = new sqlsrv_context( henv, SQL_HANDLE_ENV, err, NULL ); - (*henv_cp)->set_func( driver_func ); - - // set to ODBC 3 - core::SQLSetEnvAttr( **henv_cp, SQL_ATTR_ODBC_VERSION, reinterpret_cast( SQL_OV_ODBC3 ), SQL_IS_INTEGER TSRMLS_CC); - - // enable connection pooling - core:: SQLSetEnvAttr( **henv_cp, SQL_ATTR_CONNECTION_POOLING, reinterpret_cast( SQL_CP_ONE_PER_HENV ), - SQL_IS_UINTEGER TSRMLS_CC ); - - } - catch( core::CoreException& e ) { - - LOG( SEV_ERROR, "core_sqlsrv_minit: Failed to allocate environment handles." ); - - if( *henv_ncp != NULL ) { - // free the ODBC env handle allocated just above - SQLFreeHandle( SQL_HANDLE_ENV, **henv_ncp ); - delete *henv_ncp; // free the memory for the sqlsrv_context (it comes from the C heap, not PHP's heap) - *henv_ncp = NULL; - } - if( *henv_cp != NULL ) { - // free the ODBC env handle allocated just above - SQLFreeHandle( SQL_HANDLE_ENV, **henv_cp ); - delete *henv_cp; // free the memory for the sqlsrv_context (it comes from the C heap, not PHP's heap) - *henv_cp = NULL; - } - - throw e; // rethrow for the driver to catch - } - catch( std::bad_alloc& e ) { - - LOG( SEV_ERROR, "core_sqlsrv_minit: Failed memory allocation for environment handles." ); - - if( *henv_ncp != NULL ) { - SQLFreeHandle( SQL_HANDLE_ENV, **henv_ncp ); - delete *henv_ncp; - *henv_ncp = NULL; - } - if( *henv_cp ) { - SQLFreeHandle( SQL_HANDLE_ENV, **henv_cp ); - delete *henv_cp; - *henv_cp = NULL; - } - - throw e; // rethrow for the driver to catch - } -} - -// core_sqlsrv_mshutdown -// Module shutdown function -// Free the environment handles allocated in MINIT and unregister our stream wrapper. -// Resource types and constants are automatically released since we don't flag them as -// persistent when they are registered. -// Parameters: -// henv_cp - Pooled environment handle. -// henv_ncp - Non-pooled environment handle. -void core_sqlsrv_mshutdown( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp ) -{ - if( henv_ncp != SQL_NULL_HANDLE ) { - - henv_ncp.invalidate(); - } - delete &henv_ncp; - - if( henv_cp != SQL_NULL_HANDLE ) { - - henv_cp.invalidate(); - } - delete &henv_cp; - - return; -} - - -// DllMain for the extension. - -BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID ) -{ - switch( fdwReason ) { - case DLL_PROCESS_ATTACH: - // store the module handle for use by client_info and server_info - g_sqlsrv_hmodule = hinstDLL; - break; - default: - break; - } - - return TRUE; -} diff --git a/source/pdo_sqlsrv/shared/core_results.cpp b/source/pdo_sqlsrv/shared/core_results.cpp deleted file mode 100644 index 91f05a11..00000000 --- a/source/pdo_sqlsrv/shared/core_results.cpp +++ /dev/null @@ -1,1391 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_results.cpp -// -// Contents: Result sets -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" - -#include -#include -#include - -using namespace core; - -// conversion matrix -// each entry holds a function that can perform the conversion or NULL which means the conversion isn't supported -// this is initialized the first time the buffered result set is created. -sqlsrv_buffered_result_set::conv_matrix_t sqlsrv_buffered_result_set::conv_matrix; - -namespace { - -// *** internal types *** - -#pragma warning(disable:4200) - -// *** internal constants *** - -const int INITIAL_FIELD_STRING_LEN = 256; // base allocation size when retrieving a string field - -// *** internal functions *** - -// return an integral type rounded up to a certain number -template -T align_to( T number ) -{ - DEBUG_SQLSRV_ASSERT( (number + align) > number, "Number to align overflowed" ); - return ((number % align) == 0) ? number : (number + align - (number % align)); -} - -// return a pointer address aligned to a certain address boundary -template -T* align_to( T* ptr ) -{ - size_t p_value = (size_t) ptr; - return align_to( p_value ); -} - -// set the nth bit of the bitstream starting at ptr -void set_bit( void* ptr, unsigned int bit ) -{ - unsigned char* null_bits = reinterpret_cast( ptr ); - null_bits += bit >> 3; - *null_bits |= 1 << ( 7 - ( bit & 0x7 )); -} - -// retrieve the nth bit from the bitstream starting at ptr -bool get_bit( void* ptr, unsigned int bit ) -{ - unsigned char* null_bits = reinterpret_cast( ptr ); - null_bits += bit >> 3; - return ((*null_bits & (1 << ( 7 - ( bit & 0x07 )))) != 0); -} - -// read in LOB field during buffered result creation -SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta, - zend_long mem_used TSRMLS_DC ); - -// dtor for each row in the cache -void cache_row_dtor(zval* data); - -// convert a number to a string using locales -// There is an extra copy here, but given the size is short (usually <20 bytes) and the complications of -// subclassing a new streambuf just to avoid the copy, it's easier to do the copy -template -SQLRETURN number_to_string( Number* number_data, _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - sqlsrv_error_auto_ptr& last_error ) -{ - // get to display size by removing the null terminator from buffer length - size_t display_size = ( buffer_length - sizeof( Char )) / sizeof( Char ); - - std::basic_ostringstream os; - // use the display size to determine the sql type. And if it is a double, set the precision accordingly - // the display sizes are set by the ODBC driver based on the precision of the sql type - // otherwise we can just use the default precision as long will not be truncated - size_t real_display_size = 14; - size_t float_display_size = 24; - size_t real_precision = 7; - size_t float_precision = 15; - // this is the case of sql type float(24) or real - if ( display_size == real_display_size ) { - os.precision( real_precision ); - } - // this is the case of sql type float(53) - else if ( display_size == float_display_size ) { - os.precision( float_precision ); - } - std::locale loc; - os.imbue( loc ); - std::use_facet< std::num_put< Char > >( loc ).put( std::basic_ostream::_Iter( os.rdbuf() ), os, ' ', *number_data ); - std::basic_string& str_num = os.str(); - - if( os.fail() ) { - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( - (SQLCHAR*) "IMSSP", (SQLCHAR*) "Failed to convert number to string", -1 ); - return SQL_ERROR; - } - - if( str_num.size() * sizeof(Char) > (size_t) buffer_length ) { - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( - (SQLCHAR*) "HY090", (SQLCHAR*) "Buffer length too small to hold number as string", -1 ); - return SQL_ERROR; - } - - *out_buffer_length = str_num.size() * sizeof( Char ); // str_num.size() already include the NULL terminator - memcpy_s( buffer, buffer_length, str_num.c_str(), *out_buffer_length ); - - return SQL_SUCCESS; -} - -template -SQLRETURN string_to_number( Char* string_data, SQLLEN str_len, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length, sqlsrv_error_auto_ptr& last_error ) -{ - Number* number_data = reinterpret_cast( buffer ); - std::locale loc; // default locale should match system - std::basic_istringstream is; - is.str( string_data ); - is.imbue( loc ); - std::ios_base::iostate st = 0; - - std::use_facet< std::num_get< Char > >( loc ).get( std::basic_istream::_Iter( is.rdbuf( ) ), - std::basic_istream::_Iter(0), is, st, *number_data ); - - if( st & std::ios_base::failbit ) { - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( - (SQLCHAR*) "22003", (SQLCHAR*) "Numeric value out of range", 103 ); - return SQL_ERROR; - } - - *out_buffer_length = sizeof( Number ); - - return SQL_SUCCESS; -} - -// "closure" for the hash table destructor -struct row_dtor_closure { - - sqlsrv_buffered_result_set* results; - BYTE* row_data; - - row_dtor_closure( sqlsrv_buffered_result_set* st, BYTE* row ) : - results( st ), row_data( row ) - { - } -}; - -sqlsrv_error* odbc_get_diag_rec( sqlsrv_stmt* odbc, SQLSMALLINT record_number ) -{ - SQLWCHAR wsql_state[ SQL_SQLSTATE_BUFSIZE ]; - SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ]; - SQLINTEGER native_code; - SQLSMALLINT wnative_message_len = 0; - - SQLRETURN r = SQLGetDiagRecW( SQL_HANDLE_STMT, odbc->handle(), record_number, wsql_state, &native_code, wnative_message, - SQL_MAX_MESSAGE_LENGTH + 1, &wnative_message_len ); - if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { - return NULL; - } - - // convert the error into the encoding of the context - SQLSRV_ENCODING enc = odbc->encoding(); - if( enc == SQLSRV_ENCODING_DEFAULT ) { - enc = odbc->conn->encoding(); - } - - // convert the error into the encoding of the context - sqlsrv_malloc_auto_ptr sql_state; - SQLLEN sql_state_len = 0; - if (!convert_string_from_utf16( enc, wsql_state, sizeof(wsql_state), (char**)&sql_state, sql_state_len )) { - return NULL; - } - - sqlsrv_malloc_auto_ptr native_message; - SQLLEN native_message_len = 0; - if (!convert_string_from_utf16( enc, wnative_message, wnative_message_len, (char**)&native_message, native_message_len )) { - return NULL; - } - - return new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) sql_state, (SQLCHAR*) native_message, - native_code ); -} - -} // namespace - -// base class result set - -sqlsrv_result_set::sqlsrv_result_set( sqlsrv_stmt* stmt ) : - odbc( stmt ) -{ -} - - -// ODBC result set -// This object simply wraps ODBC function calls - -sqlsrv_odbc_result_set::sqlsrv_odbc_result_set( sqlsrv_stmt* stmt ) : - sqlsrv_result_set( stmt ) -{ -} - -sqlsrv_odbc_result_set::~sqlsrv_odbc_result_set( void ) -{ -} - -SQLRETURN sqlsrv_odbc_result_set::fetch( SQLSMALLINT orientation, SQLLEN offset TSRMLS_DC ) -{ - SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); - return core::SQLFetchScroll( odbc, orientation, offset TSRMLS_CC ); -} - -SQLRETURN sqlsrv_odbc_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC ) -{ - SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); - return core::SQLGetData( odbc, field_index, target_type, buffer, buffer_length, out_buffer_length, handle_warning TSRMLS_CC ); -} - -SQLRETURN sqlsrv_odbc_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) -{ - SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); - return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length, - out_buffer_length TSRMLS_CC ); -} - -sqlsrv_error* sqlsrv_odbc_result_set::get_diag_rec( SQLSMALLINT record_number ) -{ - SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); - return odbc_get_diag_rec( odbc, record_number ); -} - -SQLLEN sqlsrv_odbc_result_set::row_count( TSRMLS_D ) -{ - SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" ); - return core::SQLRowCount( odbc TSRMLS_CC ); -} - - -// Buffered result set -// This class holds a result set in memory - -sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS_DC ) : - sqlsrv_result_set( stmt ), - cache(NULL), - col_count(0), - meta(NULL), - current(0), - last_field_index(-1), - read_so_far(0) -{ - // 10 is an arbitrary number for now for the initial size of the cache - ALLOC_HASHTABLE( cache ); - core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, cache_row_dtor /*dtor*/, 0 /*persistent*/ TSRMLS_CC ); - col_count = core::SQLNumResultCols( stmt TSRMLS_CC ); - // there is no result set to buffer - if( col_count == 0 ) { - return; - } - - SQLULEN null_bytes = ( col_count / 8 ) + 1; // number of bits to reserve at the beginning of each row for NULL flags - meta = static_cast( sqlsrv_malloc( col_count * - sizeof( sqlsrv_buffered_result_set::meta_data ))); - - // set up the conversion matrix if this is the first time we're called - if( conv_matrix.size() == 0 ) { - - conv_matrix[ SQL_C_CHAR ][ SQL_C_CHAR ] = &sqlsrv_buffered_result_set::to_same_string; - conv_matrix[ SQL_C_CHAR ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::system_to_wide_string; - conv_matrix[ SQL_C_CHAR ][ SQL_C_BINARY ] = &sqlsrv_buffered_result_set::to_binary_string; - conv_matrix[ SQL_C_CHAR ][ SQL_C_DOUBLE ] = &sqlsrv_buffered_result_set::string_to_double; - conv_matrix[ SQL_C_CHAR ][ SQL_C_LONG ] = &sqlsrv_buffered_result_set::string_to_long; - conv_matrix[ SQL_C_WCHAR ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::to_same_string; - conv_matrix[ SQL_C_WCHAR ][ SQL_C_BINARY ] = &sqlsrv_buffered_result_set::to_binary_string; - conv_matrix[ SQL_C_WCHAR ][ SQL_C_CHAR ] = &sqlsrv_buffered_result_set::wide_to_system_string; - conv_matrix[ SQL_C_WCHAR ][ SQL_C_DOUBLE ] = &sqlsrv_buffered_result_set::wstring_to_double; - conv_matrix[ SQL_C_WCHAR ][ SQL_C_LONG ] = &sqlsrv_buffered_result_set::wstring_to_long; - conv_matrix[ SQL_C_BINARY ][ SQL_C_BINARY ] = &sqlsrv_buffered_result_set::to_same_string; - conv_matrix[ SQL_C_BINARY ][ SQL_C_CHAR ] = &sqlsrv_buffered_result_set::binary_to_system_string; - conv_matrix[ SQL_C_BINARY ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::binary_to_wide_string; - conv_matrix[ SQL_C_LONG ][ SQL_C_DOUBLE ] = &sqlsrv_buffered_result_set::long_to_double; - conv_matrix[ SQL_C_LONG ][ SQL_C_LONG ] = &sqlsrv_buffered_result_set::to_long; - conv_matrix[ SQL_C_LONG ][ SQL_C_BINARY ] = &sqlsrv_buffered_result_set::to_long; - conv_matrix[ SQL_C_LONG ][ SQL_C_CHAR ] = &sqlsrv_buffered_result_set::long_to_system_string; - conv_matrix[ SQL_C_LONG ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::long_to_wide_string; - conv_matrix[ SQL_C_DOUBLE ][ SQL_C_DOUBLE ] = &sqlsrv_buffered_result_set::to_double; - conv_matrix[ SQL_C_DOUBLE ][ SQL_C_BINARY ] = &sqlsrv_buffered_result_set::to_double; - conv_matrix[ SQL_C_DOUBLE ][ SQL_C_CHAR ] = &sqlsrv_buffered_result_set::double_to_system_string; - conv_matrix[ SQL_C_DOUBLE ][ SQL_C_LONG ] = &sqlsrv_buffered_result_set::double_to_long; - conv_matrix[ SQL_C_DOUBLE ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::double_to_wide_string; - } - - SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : - stmt->encoding()); - - // get the meta data and calculate the size of a row buffer - SQLULEN offset = null_bytes; - for( SQLSMALLINT i = 0; i < col_count; ++i ) { - - core::SQLDescribeCol( stmt, i + 1, NULL, 0, NULL, &meta[i].type, &meta[i].length, &meta[i].scale, NULL TSRMLS_CC ); - - offset = align_to<4>( offset ); - meta[i].offset = offset; - - switch( meta[i].type ) { - - // these types are the display size - case SQL_BIGINT: - case SQL_DECIMAL: - case SQL_GUID: - case SQL_NUMERIC: - core::SQLColAttribute( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, - reinterpret_cast( &meta[i].length ) TSRMLS_CC ); - meta[i].length += sizeof( char ) + sizeof( SQLULEN ); // null terminator space - offset += meta[i].length; - break; - case SQL_CHAR: - case SQL_VARCHAR: - if ( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - offset += sizeof( void* ); - } - else { - // If encoding is set to UTF-8, the following types are not necessarily column size. - // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - meta[i].length *= sizeof( WCHAR ); - meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space - offset += meta[i].length; - } - else { - meta[i].length += sizeof( SQLULEN ) + sizeof( char ); // length plus null terminator space - offset += meta[i].length; - } - } - break; - - // these types are the column size - case SQL_BINARY: - case SQL_SS_UDT: - case SQL_VARBINARY: - // var* field types are length prefixed - if( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - offset += sizeof( void* ); - } - else { - meta[i].length += sizeof( SQLULEN ) + sizeof( char ); // length plus null terminator space - offset += meta[i].length; - } - break; - - case SQL_WCHAR: - case SQL_WVARCHAR: - if( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - offset += sizeof( void* ); - } - else { - meta[i].length *= sizeof( WCHAR ); - meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space - offset += meta[i].length; - } - break; - - // these types are LOBs - case SQL_LONGVARBINARY: - case SQL_LONGVARCHAR: - case SQL_WLONGVARCHAR: - case SQL_SS_XML: - meta[i].length = sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN; - offset += sizeof( void* ); - break; - - // these types are the ISO date size - case SQL_DATETIME: - case SQL_TYPE_DATE: - case SQL_SS_TIME2: - case SQL_SS_TIMESTAMPOFFSET: - case SQL_TYPE_TIMESTAMP: - core::SQLColAttribute( stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, - reinterpret_cast( &meta[i].length ) TSRMLS_CC ); - meta[i].length += sizeof(char) + sizeof( SQLULEN ); // null terminator space - offset += meta[i].length; - break; - - // these types are the native size - case SQL_BIT: - case SQL_INTEGER: - case SQL_SMALLINT: - case SQL_TINYINT: - meta[i].length = sizeof( long ); - offset += meta[i].length; - break; - - case SQL_REAL: - case SQL_FLOAT: - meta[i].length = sizeof( double ); - offset += meta[i].length; - break; - - default: - SQLSRV_ASSERT( false, "Unknown type in sqlsrv_buffered_query::sqlsrv_buffered_query" ); - break; - } - - switch( meta[i].type ) { - - case SQL_BIGINT: - case SQL_DATETIME: - case SQL_DECIMAL: - case SQL_GUID: - case SQL_NUMERIC: - case SQL_TYPE_DATE: - case SQL_SS_TIME2: - case SQL_SS_TIMESTAMPOFFSET: - case SQL_SS_XML: - case SQL_TYPE_TIMESTAMP: - meta[i].c_type = SQL_C_CHAR; - break; - - case SQL_CHAR: - case SQL_VARCHAR: - case SQL_LONGVARCHAR: - // If encoding is set to UTF-8, the following types are not necessarily column size. - // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. - if ( encoding == SQLSRV_ENCODING_UTF8 ) { - meta[i].c_type = SQL_C_WCHAR; - } - else { - meta[i].c_type = SQL_C_CHAR; - } - break; - - case SQL_SS_UDT: - case SQL_LONGVARBINARY: - case SQL_BINARY: - case SQL_VARBINARY: - meta[i].c_type = SQL_C_BINARY; - break; - - case SQL_WLONGVARCHAR: - case SQL_WCHAR: - case SQL_WVARCHAR: - meta[i].c_type = SQL_C_WCHAR; - break; - - case SQL_BIT: - case SQL_INTEGER: - case SQL_SMALLINT: - case SQL_TINYINT: - meta[i].c_type = SQL_C_LONG; - break; - - case SQL_REAL: - case SQL_FLOAT: - meta[i].c_type = SQL_C_DOUBLE; - break; - - default: - SQLSRV_ASSERT( false, "Unknown type in sqlsrv_buffered_query::sqlsrv_buffered_query" ); - break; - } - - } - - // read the data into the cache - // (offset from the above loop has the size of the row buffer necessary) - zend_long mem_used = 0; - unsigned long row_count = 0; - - while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ) != SQL_NO_DATA ) { - - // allocate the row buffer - unsigned char* row = static_cast( sqlsrv_malloc( offset )); - memset( row, 0, offset ); - - // read the fields into the row buffer - for( SQLSMALLINT i = 0; i < col_count; ++i ) { - - SQLLEN out_buffer_temp = SQL_NULL_DATA; - SQLPOINTER buffer; - SQLLEN* out_buffer_length = &out_buffer_temp; - - switch( meta[i].c_type ) { - - case SQL_C_CHAR: - case SQL_C_WCHAR: - case SQL_C_BINARY: - if( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - out_buffer_length = &out_buffer_temp; - SQLPOINTER* lob_addr = reinterpret_cast( &row[ meta[i].offset ] ); - *lob_addr = read_lob_field( stmt, i, meta[i], mem_used TSRMLS_CC ); - // a NULL pointer means NULL field - if( *lob_addr == NULL ) { - *out_buffer_length = SQL_NULL_DATA; - } - else { - *out_buffer_length = **reinterpret_cast( lob_addr ); - mem_used += *out_buffer_length; - } - } - else { - - mem_used += meta[i].length; - CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { - - throw core::CoreException(); - } - - buffer = row + meta[i].offset + sizeof( SQLULEN ); - out_buffer_length = reinterpret_cast( row + meta[i].offset ); - core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length, - false TSRMLS_CC ); - } - break; - - case SQL_C_LONG: - case SQL_C_DOUBLE: - { - mem_used += meta[i].length; - CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { - - throw core::CoreException(); - } - buffer = row + meta[i].offset; - out_buffer_length = &out_buffer_temp; - core::SQLGetData( stmt, i + 1, meta[i].c_type, buffer, meta[i].length, out_buffer_length, - false TSRMLS_CC ); - } - break; - - default: - SQLSRV_ASSERT( false, "Unknown C type" ); - break; - } - - if( *out_buffer_length == SQL_NULL_DATA ) { - unsigned char* null_bits = reinterpret_cast( row ); - set_bit( row, i ); - } - } - - SQLSRV_ASSERT( row_count < LONG_MAX, "Hard maximum of 2 billion rows exceeded in a buffered query" ); - - // add it to the cache - row_dtor_closure cl( this, row ); - sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) TSRMLS_CC ); - } - -} - -sqlsrv_buffered_result_set::~sqlsrv_buffered_result_set( void ) -{ - // free the rows - if( cache ) { - zend_hash_destroy( cache ); - FREE_HASHTABLE( cache ); - cache = NULL; - } - - // free the meta data - if( meta ) { - efree( meta ); - meta = NULL; - } -} - -SQLRETURN sqlsrv_buffered_result_set::fetch( SQLSMALLINT orientation, SQLLEN offset TSRMLS_DC ) -{ - last_error = NULL; - last_field_index = -1; - read_so_far = 0; - - switch( orientation ) { - - case SQL_FETCH_NEXT: - offset = 1; - orientation = SQL_FETCH_RELATIVE; - break; - case SQL_FETCH_PRIOR: - offset = -1; - orientation = SQL_FETCH_RELATIVE; - break; - } - - switch( orientation ) { - - case SQL_FETCH_FIRST: - current = 1; - break; - case SQL_FETCH_LAST: - current = row_count( TSRMLS_C ); - break; - case SQL_FETCH_ABSOLUTE: - current = offset; - break; - case SQL_FETCH_RELATIVE: - current += offset; - break; - default: - SQLSRV_ASSERT( false, "Invalid fetch orientation. Should have been caught before here." ); - break; - } - - // check validity of current row - // the cursor can never get further away than just before the first row - if( current <= 0 && ( offset < 0 || orientation != SQL_FETCH_RELATIVE )) { - current = 0; - return SQL_NO_DATA; - } - - // the cursor can never get further away than just after the last row - if( current > row_count( TSRMLS_C ) || ( current <= 0 && offset > 0 ) /*overflow condition*/ ) { - current = row_count( TSRMLS_C ) + 1; - return SQL_NO_DATA; - } - - return SQL_SUCCESS; -} - -SQLRETURN sqlsrv_buffered_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC ) -{ - last_error = NULL; - field_index--; // convert from 1 based to 0 based - SQLSRV_ASSERT( field_index < column_count(), "Invalid field index requested" ); - - if( field_index != last_field_index ) { - last_field_index = field_index; - read_so_far = 0; - } - - unsigned char* row = get_row(); - - // if the field is null, then return SQL_NULL_DATA - if( get_bit( row, field_index )) { - *out_buffer_length = SQL_NULL_DATA; - return SQL_SUCCESS; - } - - // check to make sure the conversion type is valid - if( conv_matrix.find( meta[ field_index ].c_type ) == conv_matrix.end() || - conv_matrix.find( meta[ field_index ].c_type )->second.find( target_type ) == - conv_matrix.find( meta[ field_index ].c_type )->second.end() ) { - - last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "07006", - (SQLCHAR*) "Restricted data type attribute violation", 0 ); - return SQL_ERROR; - } - - return (( this )->*( conv_matrix[ meta[ field_index ].c_type ][ target_type ] ))( field_index, buffer, buffer_length, - out_buffer_length ); -} - -SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) -{ - SQLSRV_ASSERT( record_number == 1, "Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field" ); - SQLSRV_ASSERT( diag_identifier == SQL_DIAG_SQLSTATE, - "Only SQL_DIAG_SQLSTATE can be fetched by sqlsrv_buffered_result_set::get_diag_field" ); - SQLSRV_ASSERT( buffer_length >= SQL_SQLSTATE_BUFSIZE, - "Buffer not big enough to return SQLSTATE in sqlsrv_buffered_result_set::get_diag_field" ); - - if( last_error == NULL ) { - return SQL_NO_DATA; - } - - SQLSRV_ASSERT( last_error->sqlstate != NULL, - "Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field" ); - - memcpy_s( diag_info_buffer, buffer_length, last_error->sqlstate, min( buffer_length, SQL_SQLSTATE_BUFSIZE )); - - return SQL_SUCCESS; -} - -unsigned char* sqlsrv_buffered_result_set::get_row( void ) -{ - row_dtor_closure* cl_ptr; - cl_ptr = reinterpret_cast(zend_hash_index_find_ptr(cache, static_cast(current - 1))); - SQLSRV_ASSERT(cl_ptr != NULL, "Failed to find row %1!d! in the cache", current); - return cl_ptr->row_data; -} - -sqlsrv_error* sqlsrv_buffered_result_set::get_diag_rec( SQLSMALLINT record_number ) -{ - // we only hold a single error if there is one, otherwise return the ODBC error(s) - if( last_error == NULL ) { - return odbc_get_diag_rec( odbc, record_number ); - } - if( record_number > 1 ) { - return NULL; - } - - return new (sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( last_error->sqlstate, last_error->native_message, last_error->native_code ); -} - -SQLLEN sqlsrv_buffered_result_set::row_count( TSRMLS_D ) -{ - last_error = NULL; - - return zend_hash_num_elements( cache ); -} - -// private functions -template -SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, _Out_ void* buffer, - SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - sqlsrv_error_auto_ptr& out_error ) -{ - // hex characters for the conversion loop below - static char hex_chars[] = "0123456789ABCDEF"; - - SQLSRV_ASSERT( out_error == NULL, "Pending error for sqlsrv_buffered_results_set::binary_to_string" ); - - SQLRETURN r = SQL_ERROR; - - // Set the amount of space necessary for null characters at the end of the data. - SQLSMALLINT extra = sizeof(Char); - - SQLSRV_ASSERT( ((buffer_length - extra) % (extra * 2)) == 0, "Must be multiple of 2 for binary to system string or " - "multiple of 4 for binary to wide string" ); - - // all fields will be treated as ODBC returns varchar(max) fields: - // the entire length of the string is returned the first - // call in out_buffer_len. Successive calls return how much is - // left minus how much has already been read by previous reads - // *2 is for each byte to hex conversion and * extra is for either system or wide string allocation - *out_buffer_length = (*reinterpret_cast( field_data - sizeof( SQLULEN )) - read_so_far) * 2 * extra; - - // copy as much as we can into the buffer - SQLLEN to_copy; - if( buffer_length < *out_buffer_length + extra ) { - to_copy = (buffer_length - extra); - out_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); - r = SQL_SUCCESS_WITH_INFO; - } - else { - r = SQL_SUCCESS; - to_copy = *out_buffer_length; - } - - // if there are bytes to copy as hex - if( to_copy > 0 ) { - // quick hex conversion routine - Char* h = reinterpret_cast( buffer ); - BYTE* b = reinterpret_cast( field_data ); - // to_copy contains the number of bytes to copy, so we divide the number in half (or quarter) - // to get the number of hex digits we can copy - SQLLEN to_copy_hex = to_copy / (2 * extra); - for( int i = 0; i < to_copy_hex; ++i ) { - *h = hex_chars[ (*b & 0xf0) >> 4 ]; - h++; - *h = hex_chars[ (*b++ & 0x0f) ]; - h++; - } - read_so_far += to_copy_hex; - *h = static_cast( 0 ); - } - else { - reinterpret_cast( buffer )[0] = '\0'; - } - - return r; -} - -SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLCHAR* row = get_row(); - SQLCHAR* field_data = NULL; - - if( meta[ field_index ].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - field_data = *reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ); - } - else { - - field_data = &row[ meta[ field_index ].offset ] + sizeof( SQLULEN ); - } - - return binary_to_string( field_data, read_so_far, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLCHAR* row = get_row(); - SQLCHAR* field_data = NULL; - - if( meta[ field_index ].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - field_data = *reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ); - } - else { - - field_data = &row[ meta[ field_index ].offset ] + sizeof( SQLULEN ); - } - - return binary_to_string( field_data, read_so_far, buffer, buffer_length, out_buffer_length, last_error ); -} - - -SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to long" ); - SQLSRV_ASSERT( buffer_length >= sizeof(SQLLEN), "Buffer length must be able to find a long in " - "sqlsrv_buffered_result_set::double_to_long" ); - - unsigned char* row = get_row(); - double* double_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - LONG* long_data = reinterpret_cast( buffer ); - - if( *double_data < double( LONG_MIN ) || *double_data > double( LONG_MAX )) { - last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "22003", - (SQLCHAR*) "Numeric value out of range", 0 ); - return SQL_ERROR; - } - - if( *double_data != floor( *double_data )) { - last_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error( (SQLCHAR*) "01S07", - (SQLCHAR*) "Fractional truncation", 0 ); - return SQL_SUCCESS_WITH_INFO; - } - - *long_data = static_cast( *double_data ); - *out_buffer_length = sizeof( LONG ); - - return SQL_SUCCESS; -} - -SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to system string" ); - SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_system_string" ); - - unsigned char* row = get_row(); - double* double_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - return number_to_string( double_data, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to wide string" ); - SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_wide_string" ); - - unsigned char* row = get_row(); - double* double_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - return number_to_string( double_data, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to long" ); - SQLSRV_ASSERT( buffer_length >= sizeof(double), "Buffer length must be able to find a long in sqlsrv_buffered_result_set::double_to_long" ); - - unsigned char* row = get_row(); - double* double_data = reinterpret_cast( buffer ); - LONG* long_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - *double_data = static_cast( *long_data ); - *out_buffer_length = sizeof( double ); - - return SQL_SUCCESS; -} -SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to system string" ); - SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_system_string" ); - - unsigned char* row = get_row(); - LONG* long_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - return number_to_string( long_data, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to wide string" ); - SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_wide_string" ); - - unsigned char* row = get_row(); - LONG* long_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - return number_to_string( long_data, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to double" ); - SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" ); - - unsigned char* row = get_row(); - char* string_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ); - - return string_to_number( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to double" ); - SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" ); - - unsigned char* row = get_row(); - SQLWCHAR* string_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ) / sizeof( SQLWCHAR ); - - return string_to_number( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to long" ); - SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" ); - - unsigned char* row = get_row(); - char* string_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ); - - return string_to_number( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to long" ); - SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" ); - - unsigned char* row = get_row(); - SQLWCHAR* string_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ) / sizeof( SQLWCHAR ); - - return string_to_number( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error ); -} - -SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::system_to_wide_string" ); - SQLSRV_ASSERT( buffer_length % 2 == 0, "Odd buffer length passed to sqlsrv_buffered_result_set::system_to_wide_string" ); - - SQLRETURN r = SQL_ERROR; - unsigned char* row = get_row(); - - SQLCHAR* field_data = NULL; - SQLULEN field_len = NULL; - - if( meta[ field_index ].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - field_len = **reinterpret_cast( &row[ meta[ field_index ].offset ] ); - field_data = *reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ) + read_so_far; - } - else { - - field_len = *reinterpret_cast( &row[ meta[ field_index ].offset ] ); - field_data = &row[ meta[ field_index ].offset ] + sizeof( SQLULEN ) + read_so_far; - } - - // all fields will be treated as ODBC returns varchar(max) fields: - // the entire length of the string is returned the first - // call in out_buffer_len. Successive calls return how much is - // left minus how much has already been read by previous reads - *out_buffer_length = (*reinterpret_cast( field_data - sizeof( SQLULEN )) - read_so_far) * sizeof(WCHAR); - - // to_copy is the number of characters to copy, not including the null terminator - // supposedly it will never happen that a Windows MBCS will explode to UTF-16 surrogate pair. - SQLLEN to_copy; - - if( (size_t) buffer_length < (field_len - read_so_far + sizeof(char)) * sizeof(WCHAR)) { - - to_copy = (buffer_length - sizeof(WCHAR)) / sizeof(WCHAR); // to_copy is the number of characters - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); - r = SQL_SUCCESS_WITH_INFO; - } - else { - - r = SQL_SUCCESS; - to_copy = field_len - read_so_far; - } - - if( to_copy > 0 ) { - - bool tried_again = false; - do { - if (to_copy > INT_MAX ) { - LOG(SEV_ERROR, "MultiByteToWideChar: Buffer length exceeded."); - throw core::CoreException(); - } - - int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, static_cast(to_copy), - static_cast(buffer), static_cast(to_copy)); - if( ch_space == 0 ) { - - switch( GetLastError() ) { - - case ERROR_NO_UNICODE_TRANSLATION: - // the theory here is the conversion failed because the end of the buffer we provided contained only - // half a character at the end - if( !tried_again ) { - to_copy--; - tried_again = true; - continue; - } - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "IMSSP", (SQLCHAR*) "Invalid Unicode translation", -1 ); - break; - default: - SQLSRV_ASSERT( false, "Severe error translating Unicode" ); - break; - } - - return SQL_ERROR; - } - - ((WCHAR*)buffer)[ to_copy ] = L'\0'; - read_so_far += to_copy; - break; - - } while( true ); - } - else { - reinterpret_cast( buffer )[0] = L'\0'; - } - - return r; -} - -SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::to_same_string" ); - - SQLRETURN r = SQL_ERROR; - unsigned char* row = get_row(); - - // Set the amount of space necessary for null characters at the end of the data. - SQLSMALLINT extra = 0; - - switch( meta[ field_index ].c_type ) { - case SQL_C_WCHAR: - extra = sizeof( SQLWCHAR ); - break; - case SQL_C_BINARY: - extra = 0; - break; - case SQL_C_CHAR: - extra = sizeof( SQLCHAR ); - break; - default: - SQLSRV_ASSERT( false, "Invalid type in get_string_data" ); - break; - } - - SQLCHAR* field_data = NULL; - - if( meta[ field_index ].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - field_data = *reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ); - } - else { - - field_data = &row[ meta[ field_index ].offset ] + sizeof( SQLULEN ); - } - - // all fields will be treated as ODBC returns varchar(max) fields: - // the entire length of the string is returned the first - // call in out_buffer_len. Successive calls return how much is - // left minus how much has already been read by previous reads - *out_buffer_length = *reinterpret_cast( field_data - sizeof( SQLULEN )) - read_so_far; - - // copy as much as we can into the buffer - SQLLEN to_copy; - if( buffer_length < *out_buffer_length + extra ) { - to_copy = buffer_length - extra; - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); - r = SQL_SUCCESS_WITH_INFO; - } - else { - r = SQL_SUCCESS; - to_copy = *out_buffer_length; - } - - SQLSRV_ASSERT( to_copy >= 0, "Negative field length calculated in buffered result set" ); - - if( to_copy > 0 ) { - memcpy_s( buffer, buffer_length, field_data + read_so_far, to_copy ); - read_so_far += to_copy; - } - if( extra ) { - OACR_WARNING_SUPPRESS( 26001, "Buffer length verified above" ); - memcpy_s( reinterpret_cast( buffer ) + to_copy, buffer_length, L"\0", extra ); - } - - return r; -} - -SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::wide_to_system_string" ); - - SQLRETURN r = SQL_ERROR; - unsigned char* row = get_row(); - - SQLCHAR* field_data = NULL; - SQLLEN field_len = NULL; - - // if this is the first time called for this field, just convert the entire string to system first then - // use that to read from instead of converting chunk by chunk. This is because it's impossible to know - // the total length of the string for output_buffer_length without doing the conversion and returning - // SQL_NO_TOTAL is not consistent with what our other conversion functions do (system_to_wide_string and - // to_same_string). - - if( read_so_far == 0 ) { - - if( meta[ field_index ].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - field_len = **reinterpret_cast( &row[ meta[ field_index ].offset ] ); - field_data = *reinterpret_cast( &row[ meta[ field_index ].offset ] ) + sizeof( SQLULEN ) + read_so_far; - } - else { - - field_len = *reinterpret_cast( &row[ meta[ field_index ].offset ] ); - field_data = &row[ meta[ field_index ].offset ] + sizeof( SQLULEN ) + read_so_far; - } - - BOOL default_char_used = FALSE; - char default_char = '?'; - - // allocate enough to handle WC -> DBCS conversion if it happens - temp_string = reinterpret_cast( sqlsrv_malloc( field_len, sizeof(char), sizeof(char))); - temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast(field_len / sizeof(WCHAR)), - (LPSTR) temp_string.get(), static_cast(field_len), &default_char, &default_char_used ); - - if( temp_length == 0 ) { - - switch( GetLastError() ) { - - case ERROR_NO_UNICODE_TRANSLATION: - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "IMSSP", (SQLCHAR*) "Invalid Unicode translation", -1 ); - break; - default: - SQLSRV_ASSERT( false, "Severe error translating Unicode" ); - break; - } - - return SQL_ERROR; - } - } - - *out_buffer_length = (temp_length - read_so_far); - - SQLLEN to_copy = 0; - - if( (size_t) buffer_length < (temp_length - read_so_far + sizeof(char))) { - - to_copy = buffer_length - sizeof(char); - last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) - sqlsrv_error( (SQLCHAR*) "01004", (SQLCHAR*) "String data, right truncated", -1 ); - r = SQL_SUCCESS_WITH_INFO; - } - else { - - to_copy = (temp_length - read_so_far); - r = SQL_SUCCESS; - } - - if( to_copy > 0 ) { - - memcpy_s( buffer, buffer_length, temp_string.get() + read_so_far, to_copy ); - } - SQLSRV_ASSERT( to_copy >= 0, "Invalid field copy length" ); - OACR_WARNING_SUPPRESS( BUFFER_UNDERFLOW, "Buffer length verified above" ); - ((SQLCHAR*) buffer)[ to_copy ] = '\0'; - read_so_far += to_copy; - - return r; -} - - -SQLRETURN sqlsrv_buffered_result_set::to_binary_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - return to_same_string( field_index, buffer, buffer_length, out_buffer_length ); -} - -SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invlid conversion to long" ); - SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer too small for SQL_C_LONG" ); // technically should ignore this - - unsigned char* row = get_row(); - LONG* long_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - memcpy_s( buffer, buffer_length, long_data, sizeof( LONG )); - *out_buffer_length = sizeof( LONG ); - - return SQL_SUCCESS; -} - -SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ) -{ - SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invlid conversion to double" ); - SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer too small for SQL_C_DOUBLE" ); // technically should ignore this - - unsigned char* row = get_row(); - double* double_data = reinterpret_cast( &row[ meta[ field_index ].offset ] ); - - memcpy_s( buffer, buffer_length, double_data, sizeof( double )); - *out_buffer_length = sizeof( double ); - - return SQL_SUCCESS; -} - -namespace { - -// called for each row in the cache when the cache is destroyed in the destructor -void cache_row_dtor( zval* data ) -{ - row_dtor_closure* cl = reinterpret_cast( Z_PTR_P( data ) ); - BYTE* row = cl->row_data; - // don't release this here, since this is called from the destructor of the result_set - sqlsrv_buffered_result_set* result_set = cl->results; - - for( SQLSMALLINT i = 0; i < result_set->column_count(); ++i ) { - - if( result_set->col_meta_data(i).length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { - - void* out_of_row_data = *reinterpret_cast( &row[ result_set->col_meta_data(i).offset ] ); - sqlsrv_free( out_of_row_data ); - } - } - - sqlsrv_free( row ); - sqlsrv_free( cl ); -} - -SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta, - zend_long mem_used TSRMLS_DC ) -{ - SQLSMALLINT extra = 0; - SQLULEN* output_buffer_len = NULL; - - // Set the amount of space necessary for null characters at the end of the data. - switch( meta.c_type ) { - case SQL_C_WCHAR: - extra = sizeof( SQLWCHAR ); - break; - case SQL_C_BINARY: - extra = 0; - break; - case SQL_C_CHAR: - extra = sizeof( SQLCHAR ); - break; - default: - SQLSRV_ASSERT( false, "Invalid type in read_lob_field" ); - break; - } - - SQLLEN already_read = 0; - SQLLEN to_read = INITIAL_FIELD_STRING_LEN; - sqlsrv_malloc_auto_ptr buffer; - buffer = static_cast( sqlsrv_malloc( INITIAL_FIELD_STRING_LEN + extra + sizeof( SQLULEN ))); - SQLRETURN r = SQL_SUCCESS; - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLLEN last_field_len = 0; - bool full_length_returned = false; - - do { - - - output_buffer_len = reinterpret_cast( buffer.get() ); - r = core::SQLGetData( stmt, field_index + 1, meta.c_type, buffer.get() + already_read + sizeof( SQLULEN ), - to_read - already_read + extra, &last_field_len, false /*handle_warning*/ TSRMLS_CC ); - - // if the field is NULL, then return a NULL pointer - if( last_field_len == SQL_NULL_DATA ) { - return NULL; - } - - // if the last read was successful, we're done - if( r == SQL_SUCCESS ) { - // check to make sure we haven't overflown our memory limit - CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { - - throw core::CoreException(); - } - break; - } - // else if it wasn't the truncated warning (01004) then we're done - else if( r == SQL_SUCCESS_WITH_INFO ) { - SQLSMALLINT len; - core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len - TSRMLS_CC ); - - if( !is_truncated_warning( state )) { - break; - } - } - - SQLSRV_ASSERT( SQL_SUCCEEDED( r ), "Unknown SQL error not triggered" ); - - // if the type of the field returns the total to be read, we use that and preallocate the buffer - if( last_field_len != SQL_NO_TOTAL ) { - - CHECK_CUSTOM_ERROR( mem_used + last_field_len > stmt->buffered_query_limit * 1024, stmt, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { - - throw core::CoreException(); - } - - already_read += to_read - already_read; - to_read = last_field_len; - buffer.resize( to_read + extra + sizeof( SQLULEN )); - output_buffer_len = reinterpret_cast( buffer.get() ); - // record the size of the field since we have it available - *output_buffer_len = last_field_len; - full_length_returned = true; - } - // otherwise allocate another chunk of memory to read in - else { - already_read += to_read - already_read; - to_read *= 2; - CHECK_CUSTOM_ERROR( mem_used + to_read > stmt->buffered_query_limit * 1024, stmt, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) { - - throw core::CoreException(); - } - buffer.resize( to_read + extra + sizeof( SQLULEN )); - output_buffer_len = reinterpret_cast( buffer.get() ); - } - - } while( true ); - - SQLSRV_ASSERT( output_buffer_len != NULL, "Output buffer not allocated properly" ); - - // most LOB field types return the total length in the last_field_len, but some field types such as XML - // only return the amount read on the last read - if( !full_length_returned ) { - *output_buffer_len = already_read + last_field_len; - } - - char* return_buffer = buffer; - buffer.transferred(); - return return_buffer; -} - -} diff --git a/source/pdo_sqlsrv/shared/core_sqlsrv.h b/source/pdo_sqlsrv/shared/core_sqlsrv.h deleted file mode 100644 index 1004c234..00000000 --- a/source/pdo_sqlsrv/shared/core_sqlsrv.h +++ /dev/null @@ -1,2263 +0,0 @@ -#ifndef CORE_SQLSRV_H -#define CORE_SQLSRV_H - -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_sqlsrv.h -// -// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server -// -// Microsoft Drivers 4.1 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. -//--------------------------------------------------------------------------------------------------------------------------------- - -//********************************************************************************************************************************* -// Includes -//********************************************************************************************************************************* - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef PHP_WIN32 -#define PHP_SQLSRV_API __declspec(dllexport) -#else -#define PHP_SQLSRV_API -#endif - -// OACR is an internal Microsoft static code analysis tool -#if defined(OACR) -#include -OACR_WARNING_PUSH -OACR_WARNING_DISABLE( ALLOC_SIZE_OVERFLOW, "Third party code." ) -OACR_WARNING_DISABLE( INDEX_NEGATIVE, "Third party code." ) -OACR_WARNING_DISABLE( UNANNOTATED_BUFFER, "Third party code." ) -OACR_WARNING_DISABLE( INDEX_UNDERFLOW, "Third party code." ) -OACR_WARNING_DISABLE( REALLOCLEAK, "Third party code." ) -OACR_WARNING_DISABLE( ALLOC_SIZE_OVERFLOW_WITH_ACCESS, "Third party code." ) -#else -// define to eliminate static analysis hints in the code -#define OACR_WARNING_SUPPRESS( warning, msg ) -#endif - -extern "C" { - -#pragma warning(push) -#pragma warning( disable: 4005 4100 4127 4142 4244 4505 4530 ) - -#ifdef ZTS -#include "TSRM.h" -#endif - -#if _MSC_VER >= 1400 -// typedef and macro to prevent a conflict between php.h and ws2tcpip.h. -// php.h defines this constant as unsigned int which causes a compile error -// in ws2tcpip.h. Fortunately php.h allows an override by defining -// HAVE_SOCKLEN_T. Since ws2tcpip.h isn't included until later, we define -// socklen_t here and override the php.h version. -typedef int socklen_t; -#define HAVE_SOCKLEN_T -#endif - -#include "php.h" -#include "php_globals.h" -#include "php_ini.h" -#include "ext/standard/php_standard.h" -#include "ext/standard/info.h" - -#pragma warning(pop) - -#if ZEND_DEBUG -// debug build causes warning C4505 to pop up from the Zend header files -#pragma warning( disable: 4505 ) -#endif - -} // extern "C" - -#if defined(OACR) -OACR_WARNING_POP -#endif - -#include -#include - -#if !defined(WC_ERR_INVALID_CHARS) -// imported from winnls.h as it isn't included by 5.3.0 -#define WC_ERR_INVALID_CHARS 0x00000080 // error for invalid chars -#endif - -// PHP defines inline as __forceinline, which in debug mode causes a warning to be emitted when -// we use std::copy, which causes compilation to fail since we compile with warnings as errors. -#if defined(ZEND_DEBUG) && defined(inline) -#undef inline -#endif - -#include -#include -#include -#include -#include -#include -#include -// included for SQL Server specific constants -#include "msodbcsql.h" - -//********************************************************************************************************************************* -// Constants and Types -//********************************************************************************************************************************* - -// constants for maximums in SQL Server -const int SS_MAXCOLNAMELEN = 128; -const int SQL_SERVER_MAX_FIELD_SIZE = 8000; -const int SQL_SERVER_MAX_PRECISION = 38; -const int SQL_SERVER_MAX_TYPE_SIZE = 0; -const int SQL_SERVER_MAX_PARAMS = 2100; - -// max size of a date time string when converting from a DateTime object to a string -const int MAX_DATETIME_STRING_LEN = 256; - -// precision and scale for the date time types between servers -const int SQL_SERVER_2005_DEFAULT_DATETIME_PRECISION = 23; -const int SQL_SERVER_2005_DEFAULT_DATETIME_SCALE = 3; -const int SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION = 34; -const int SQL_SERVER_2008_DEFAULT_DATETIME_SCALE = 7; - -// types for conversions on output parameters (though they can be used for input parameters, they are ignored) -enum SQLSRV_PHPTYPE { - MIN_SQLSRV_PHPTYPE = 1, // lowest value for a php type - SQLSRV_PHPTYPE_NULL = 1, - SQLSRV_PHPTYPE_INT, - SQLSRV_PHPTYPE_FLOAT, - SQLSRV_PHPTYPE_STRING, - SQLSRV_PHPTYPE_DATETIME, - SQLSRV_PHPTYPE_STREAM, - MAX_SQLSRV_PHPTYPE, // highest value for a php type - SQLSRV_PHPTYPE_INVALID = MAX_SQLSRV_PHPTYPE // used to see if a type is invalid -}; - -// encodings supported by this extension. These basically translate into the use of SQL_C_CHAR or SQL_C_BINARY when getting -// information as a string or a stream. -enum SQLSRV_ENCODING { - SQLSRV_ENCODING_INVALID, // unknown or invalid encoding. Used to initialize variables. - SQLSRV_ENCODING_DEFAULT, // use what is the connection's default for a statement, use system if a connection - SQLSRV_ENCODING_BINARY, // use SQL_C_BINARY when using SQLGetData - SQLSRV_ENCODING_CHAR, // use SQL_C_CHAR when using SQLGetData - SQLSRV_ENCODING_SYSTEM = SQLSRV_ENCODING_CHAR, - SQLSRV_ENCODING_UTF8 = CP_UTF8, -}; - -// the array keys used when returning a row via sqlsrv_fetch_array and sqlsrv_fetch_object. -enum SQLSRV_FETCH_TYPE { - MIN_SQLSRV_FETCH = 1, // lowest value for fetch type - SQLSRV_FETCH_NUMERIC = 1, // return an array with only numeric indices - SQLSRV_FETCH_ASSOC = 2, // return an array with keys made from the field names - SQLSRV_FETCH_BOTH = 3, // return an array indexed with both numbers and keys - MAX_SQLSRV_FETCH = 3, // highest value for fetch type -}; - -// buffer size of a sql state (including the null character) -const int SQL_SQLSTATE_BUFSIZE = SQL_SQLSTATE_SIZE + 1; - -// buffer size allocated to retrieve data from a PHP stream. This number -// was chosen since PHP doesn't return more than 8k at a time even if -// the amount requested was more. -const int PHP_STREAM_BUFFER_SIZE = 8192; - -// SQL types for parameters encoded in an integer. The type corresponds to the SQL type ODBC constants. -// The size is the column size or precision, and scale is the decimal digits for precise numeric types. - -union sqlsrv_sqltype { - struct typeinfo_t { - int type:9; - int size:14; - int scale:8; - } typeinfo; - - zend_long value; -}; - - -// SQLSRV PHP types (as opposed to the Zend PHP type constants). Contains the type (see SQLSRV_PHPTYPE) -// and the encoding for strings and streams (see SQLSRV_ENCODING) - -union sqlsrv_phptype { - - struct typeinfo_t { - unsigned type:8; - unsigned encoding:16; - } typeinfo; - - zend_long value; -}; - -// static assert for enforcing compile time conditions -template -struct sqlsrv_static_assert; - -template <> -struct sqlsrv_static_assert { static const int value = 1; }; - -#define SQLSRV_STATIC_ASSERT( c ) (sqlsrv_static_assert<(c) != 0>() ) - - -//********************************************************************************************************************************* -// Logging -//********************************************************************************************************************************* -// log_callback -// a driver specific callback for logging messages -// severity - severity of the message: notice, warning, or error -// msg - the message to log in a FormatMessage style formatting -// print_args - args to the message -typedef void (*log_callback)( unsigned int severity TSRMLS_DC, const char* msg, va_list* print_args ); - -// each driver must register a log callback. This should be the first thing a driver does. -void core_sqlsrv_register_logger( log_callback ); - -// a simple wrapper around a PHP error logging function. -void write_to_log( unsigned int severity TSRMLS_DC, const char* msg, ... ); - -// a macro to make it convenient to use the function. -#define LOG( severity, msg, ...) write_to_log( severity TSRMLS_CC, msg, __VA_ARGS__ ) - -// mask for filtering which severities are written to the log -enum logging_severity { - SEV_ERROR = 0x01, - SEV_WARNING = 0x02, - SEV_NOTICE = 0x04, - SEV_ALL = -1, -}; - -// Kill the PHP process and log the message to PHP -void die( const char* msg, ... ); -#define DIE( msg, ... ) { die( msg, __VA_ARGS__ ); } - - -//********************************************************************************************************************************* -// Resource/Memory Management -//********************************************************************************************************************************* - -// the macro max is defined and overrides the call to max in the allocator class -#pragma push_macro( "max" ) -#undef max - -// new memory allocation/free debugging facilities to help us verify that all allocations are being -// released in a timely manner and not just at the end of the script. -// Zend has memory logging and checking, but it can generate a lot of noise for just one extension. -// It's meant for internal use but might be useful for people adding features to our extension. -// To use it, uncomment the #define below and compile in Debug NTS. All allocations and releases -// must be done with sqlsrv_malloc and sqlsrv_free. -// #define SQLSRV_MEM_DEBUG 1 -#if defined( PHP_DEBUG ) && !defined( ZTS ) && defined( SQLSRV_MEM_DEBUG ) - -inline void* sqlsrv_malloc_trace( size_t size, const char* file, int line ) -{ - void* ptr = emalloc( size ); - LOG( SEV_NOTICE, "emalloc returned %4!08x!: %1!d! bytes at %2!s!:%3!d!", size, file, line, ptr ); - return ptr; -} - -inline void* sqlsrv_malloc_trace( size_t element_count, size_t element_size, size_t extra, const char* file, int line ) -{ - OACR_WARNING_SUPPRESS( ALLOC_SIZE_OVERFLOW_IN_ALLOC_WRAPPER, "Overflow verified below" ); - - if(( element_count > 0 && element_size > 0 ) && - ( element_count > element_size * element_count || element_size > element_size * element_count )) { - DIE( "Integer overflow in sqlsrv_malloc" ); - } - - if( element_size * element_count > element_size * element_count + extra ) { - DIE( "Integer overflow in sqlsrv_malloc" ); - } - - if( element_size * element_count + extra == 0 ) { - DIE( "Allocation size must be more than 0" ); - } - - void* ptr = emalloc( element_size * element_count + extra ); - LOG( SEV_NOTICE, "emalloc returned %4!08x!: %1!d! bytes at %2!s!:%3!d!", size, file, line, ptr ); - return ptr; -} - -inline void* sqlsrv_realloc_trace( void* buffer, size_t size, const char* file, int line ) -{ - void* ptr = erealloc( original, size ); - LOG( SEV_NOTICE, "erealloc returned %5!08x! from %4!08x!: %1!d! bytes at %2!s!:%3!d!", size, file, line, ptr, original ); - return ptr; -} - -inline void sqlsrv_free_trace( void* ptr, const char* file, int line ) -{ - LOG( SEV_NOTICE, "efree %1!08x! at %2!s!:%3!d!", ptr, file, line ); - efree( ptr ); -} - -#define sqlsrv_malloc( size ) sqlsrv_malloc_trace( size, __FILE__, __LINE__ ) -#define sqlsrv_malloc( count, size, extra ) sqlsrv_malloc_trace( count, size, extra, __FILE__, __LINE__ ) -#define sqlsrv_realloc( buffer, size ) sqlsrv_realloc_trace( buffer, size, __FILE__, __LINE__ ) -#define sqlsrv_free( ptr ) sqlsrv_free_trace( ptr, __FILE__, __LINE__ ) - -#else - -inline void* sqlsrv_malloc( size_t size ) -{ - return emalloc( size ); -} - -inline void* sqlsrv_malloc( size_t element_count, size_t element_size, size_t extra ) -{ - OACR_WARNING_SUPPRESS( ALLOC_SIZE_OVERFLOW_IN_ALLOC_WRAPPER, "Overflow verified below" ); - - if(( element_count > 0 && element_size > 0 ) && - ( element_count > element_size * element_count || element_size > element_size * element_count )) { - DIE( "Integer overflow in sqlsrv_malloc" ); - } - - if( element_size * element_count > element_size * element_count + extra ) { - DIE( "Integer overflow in sqlsrv_malloc" ); - } - - if( element_size * element_count + extra == 0 ) { - DIE( "Allocation size must be more than 0" ); - } - - return emalloc( element_size * element_count + extra ); -} - -inline void* sqlsrv_realloc( void* buffer, size_t size ) -{ - return erealloc( buffer, size ); -} - -inline void sqlsrv_free( void* ptr ) -{ - efree( ptr ); -} - -#endif - -// trait class that allows us to assign const types to an auto_ptr -template -struct remove_const { - typedef T type; -}; - -template -struct remove_const { - typedef T* type; -}; - -// allocator that uses the zend memory manager to manage memory -// this allows us to use STL classes that still work with Zend objects -template -struct sqlsrv_allocator { - - // typedefs used by the STL classes - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - // conversion typedef (used by list and other STL classes) - template - struct rebind { - typedef sqlsrv_allocator other; - }; - - inline sqlsrv_allocator() {} - inline ~sqlsrv_allocator() {} - inline sqlsrv_allocator( sqlsrv_allocator const& ) {} - template - inline sqlsrv_allocator( sqlsrv_allocator const& ) {} - - // address (doesn't work if the class defines operator&) - inline pointer address( reference r ) - { - return &r; - } - - inline const_pointer address( const_reference r ) - { - return &r; - } - - // memory allocation/deallocation - inline pointer allocate( size_type cnt, - typename std::allocator::const_pointer = 0 ) - { - return reinterpret_cast( sqlsrv_malloc(cnt, sizeof (T), 0)); - } - - inline void deallocate( pointer p, size_type ) - { - sqlsrv_free(p); - } - - // size - inline size_type max_size( void ) const - { - return std::numeric_limits::max() / sizeof(T); - } - - // object construction/destruction - inline void construct( pointer p, const T& t ) - { - new(p) T(t); - } - - inline void destroy(pointer p) - { - p->~T(); - } - - // equality operators - inline bool operator==( sqlsrv_allocator const& ) - { - return true; - } - - inline bool operator!=( sqlsrv_allocator const& a ) - { - return !operator==(a); - } -}; - - -// base class for auto_ptrs that we define below. It provides common operators and functions -// used by all the classes. -template -class sqlsrv_auto_ptr { - -public: - - sqlsrv_auto_ptr( void ) : _ptr( NULL ) - { - } - - ~sqlsrv_auto_ptr( void ) - { - static_cast(this)->reset( NULL ); - } - - // call when ownership is transferred - void transferred( void ) - { - _ptr = NULL; - } - - // explicit function to get the pointer. - T* get( void ) const - { - return _ptr; - } - - // cast operator to allow auto_ptr to be used where a normal const * can be. - operator const T* () const - { - return _ptr; - } - - // cast operator to allow auto_ptr to be used where a normal pointer can be. - operator typename remove_const::type () const - { - return _ptr; - } - - operator bool() const - { - return _ptr != NULL; - } - - // there are a number of places where we allocate a block intended to be accessed as - // an array of elements, so this operator allows us to treat the memory as such. - T& operator[]( int index ) const - { - return _ptr[ index ]; - } - - // there are a number of places where we allocate a block intended to be accessed as - // an array of elements, so this operator allows us to treat the memory as such. - T& operator[]( unsigned int index ) const - { - return _ptr[ index ]; - } - - // there are a number of places where we allocate a block intended to be accessed as - // an array of elements, so this operator allows us to treat the memory as such. - T& operator[]( long index ) const - { - return _ptr[ index ]; - } - - - #ifdef __WIN64 - // there are a number of places where we allocate a block intended to be accessed as - // an array of elements, so this operator allows us to treat the memory as such. - T& operator[](std::size_t index) const - { - return _ptr[index]; - } - #endif - - // there are a number of places where we allocate a block intended to be accessed as - // an array of elements, so this operator allows us to treat the memory as such. - T& operator[]( unsigned short index ) const - { - return _ptr[ index ]; - } - - // access elements of a structure through the auto ptr - T* const operator->( void ) const - { - return _ptr; - } - - // value from reference operator (i.e., i = *(&i); or *i = blah;) - T& operator*() const - { - return *_ptr; - } - - // allow the use of the address-of operator to simulate a **. - // Note: this operator conflicts with storing these within an STL container. If you need - // to do that, then redefine this as getpp and change instances of &auto_ptr to auto_ptr.getpp() - T** operator&( void ) - { - return &_ptr; - } - -protected: - - sqlsrv_auto_ptr( T* ptr ) : - _ptr( ptr ) - { - } - - sqlsrv_auto_ptr( sqlsrv_auto_ptr& src ) - { - if( _ptr ) { - static_cast(this)->reset( src._ptr ); - } - src.transferred(); - } - - // assign a new pointer to the auto_ptr. It will free the previous memory block - // because ownership is deemed finished. - T* operator=( T* ptr ) - { - static_cast( this )->reset( ptr ); - - return ptr; - } - - T* _ptr; -}; - -// an auto_ptr for sqlsrv_malloc/sqlsrv_free. When allocating a chunk of memory using sqlsrv_malloc, wrap that pointer -// in a variable of sqlsrv_malloc_auto_ptr. sqlsrv_malloc_auto_ptr will "own" that block and assure that it is -// freed until the variable is destroyed (out of scope) or ownership is transferred using the function -// "transferred". -// DO NOT CALL sqlsrv_realloc with a sqlsrv_malloc_auto_ptr. Use the resize member function. - -template -class sqlsrv_malloc_auto_ptr : public sqlsrv_auto_ptr > { - -public: - - sqlsrv_malloc_auto_ptr( void ) : - sqlsrv_auto_ptr >( NULL ) - { - } - - sqlsrv_malloc_auto_ptr( const sqlsrv_malloc_auto_ptr& src ) : - sqlsrv_auto_ptr >( src ) - { - } - - // free the original pointer and assign a new pointer. Use NULL to simply free the pointer. - void reset( T* ptr = NULL ) - { - if( _ptr ) - sqlsrv_free( (void*) _ptr ); - _ptr = ptr; - } - - T* operator=( T* ptr ) - { - return sqlsrv_auto_ptr >::operator=( ptr ); - } - - void operator=( sqlsrv_malloc_auto_ptr& src ) - { - T* p = src.get(); - src.transferred(); - this->_ptr = p; - } - - // DO NOT CALL sqlsrv_realloc with a sqlsrv_malloc_auto_ptr. Use the resize member function. - // has the same parameter list as sqlsrv_realloc: new_size is the size in bytes of the newly allocated buffer - void resize( size_t new_size ) - { - _ptr = reinterpret_cast( sqlsrv_realloc( _ptr, new_size )); - } -}; - - -// auto ptr for Zend hash tables. Used to clean up a hash table allocated when -// something caused an early exit from the function. This is used when the hash_table is -// allocated in a zval that itself can't be released. Otherwise, use the zval_auto_ptr. - -class hash_auto_ptr : public sqlsrv_auto_ptr { - -public: - - hash_auto_ptr( void ) : - sqlsrv_auto_ptr( NULL ) - { - } - - // free the original pointer and assign a new pointer. Use NULL to simply free the pointer. - void reset( HashTable* ptr = NULL ) - { - if( _ptr ) { - zend_hash_destroy( _ptr ); - FREE_HASHTABLE( _ptr ); - } - _ptr = ptr; - } - - HashTable* operator=( HashTable* ptr ) - { - return sqlsrv_auto_ptr::operator=( ptr ); - } - -private: - - hash_auto_ptr( HashTable const& hash ); - - hash_auto_ptr( hash_auto_ptr const& hash ); -}; - - -// an auto_ptr for zvals. When allocating a zval, wrap that pointer in a variable of zval_auto_ptr. -// zval_auto_ptr will "own" that zval and assure that it is freed when the variable is destroyed -// (out of scope) or ownership is transferred using the function "transferred". - -class zval_auto_ptr : public sqlsrv_auto_ptr { - -public: - - zval_auto_ptr( void ) - { - } - - // free the original pointer and assign a new pointer. Use NULL to simply free the pointer. - void reset( zval* ptr = NULL ) - { - if( _ptr ) - zval_ptr_dtor(_ptr ); - _ptr = ptr; - } - - zval* operator=( zval* ptr ) - { - return sqlsrv_auto_ptr::operator=( ptr ); - } - - -private: - - zval_auto_ptr( const zval_auto_ptr& src ); -}; - -#pragma pop_macro( "max" ) - - -//********************************************************************************************************************************* -// sqlsrv_error -//********************************************************************************************************************************* - -// *** PHP specific errors *** -// sqlsrv errors are held in a structure of this type used by the driver handle_error functions -// format is a flag that tells the driver error handler functions if there are parameters to use with FormatMessage -// into the error message before returning it. - -// base class which can be instatiated with aggregates (see error constants) -struct sqlsrv_error_const { - - SQLCHAR* sqlstate; - SQLCHAR* native_message; - SQLINTEGER native_code; - bool format; -}; - -// subclass which is used by the core layer to instantiate ODBC errors -struct sqlsrv_error : public sqlsrv_error_const { - - sqlsrv_error( void ) - { - sqlstate = NULL; - native_message = NULL; - native_code = -1; - format = false; - } - - sqlsrv_error( SQLCHAR* sql_state, SQLCHAR* message, SQLINTEGER code, bool printf_format = false ) - { - sqlstate = reinterpret_cast( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); - native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 )); - strcpy_s( reinterpret_cast( sqlstate ), SQL_SQLSTATE_BUFSIZE, reinterpret_cast( sql_state )); - strcpy_s( reinterpret_cast( native_message ), SQL_MAX_MESSAGE_LENGTH + 1, reinterpret_cast( message )); - native_code = code; - format = printf_format; - } - - sqlsrv_error( sqlsrv_error_const const& prototype ) - { - sqlsrv_error( prototype.sqlstate, prototype.native_message, prototype.native_code, prototype.format ); - } - - ~sqlsrv_error( void ) - { - if( sqlstate != NULL ) { - sqlsrv_free( sqlstate ); - } - if( native_message != NULL ) { - sqlsrv_free( native_message ); - } - } -}; - - -// an auto_ptr for sqlsrv_errors. These call the destructor explicitly rather than call delete -class sqlsrv_error_auto_ptr : public sqlsrv_auto_ptr { - -public: - - sqlsrv_error_auto_ptr( void ) : - sqlsrv_auto_ptr( NULL ) - { - } - - sqlsrv_error_auto_ptr( sqlsrv_error_auto_ptr const& src ) : - sqlsrv_auto_ptr( (sqlsrv_error_auto_ptr&) src ) - { - } - - // free the original pointer and assign a new pointer. Use NULL to simply free the pointer. - void reset( sqlsrv_error* ptr = NULL ) - { - if( _ptr ) { - _ptr->~sqlsrv_error(); - sqlsrv_free( (void*) _ptr ); - } - _ptr = ptr; - } - - sqlsrv_error* operator=( sqlsrv_error* ptr ) - { - return sqlsrv_auto_ptr::operator=( ptr ); - } - - // unlike traditional assignment operators, the chained assignment of an auto_ptr doesn't make much - // sense. Only the last one would have anything in it. - void operator=( sqlsrv_error_auto_ptr& src ) - { - sqlsrv_error* p = src.get(); - src.transferred(); - this->_ptr = p; - } -}; - - -//********************************************************************************************************************************* -// Context -//********************************************************************************************************************************* - -class sqlsrv_context; -struct sqlsrv_conn; - -// error_callback -// a driver specific callback for processing errors. -// ctx - the context holding the handles -// sqlsrv_error_code - specific error code to return. -typedef bool (*error_callback)( sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool error TSRMLS_DC, va_list* print_args ); - -// sqlsrv_context -// a context holds relevant information to be passed with a connection and statement objects. - -class sqlsrv_context { - - public: - - sqlsrv_context( SQLSMALLINT type, error_callback e, void* drv, SQLSRV_ENCODING encoding = SQLSRV_ENCODING_INVALID ) : - handle_( SQL_NULL_HANDLE ), - handle_type_( type ), - err_( e ), - name_( NULL ), - driver_( drv ), - last_error_(), - encoding_( encoding ) - { - } - - sqlsrv_context( SQLHANDLE h, SQLSMALLINT t, error_callback e, void* drv, SQLSRV_ENCODING encoding = SQLSRV_ENCODING_INVALID ) : - handle_( h ), - handle_type_( t ), - err_( e ), - name_( NULL ), - driver_( drv ), - last_error_(), - encoding_( encoding ) - { - } - - sqlsrv_context( sqlsrv_context const& ctx ) : - handle_( ctx.handle_ ), - handle_type_( ctx.handle_type_ ), - err_( ctx.err_ ), - name_( ctx.name_ ), - driver_( ctx.driver_ ), - last_error_( ctx.last_error_ ) - { - } - - void set_func( const char* f ) - { - name_ = f; - } - - void set_last_error( sqlsrv_error_auto_ptr& last_error ) - { - last_error_ = last_error; - } - - sqlsrv_error_auto_ptr& last_error( void ) - { - return last_error_; - } - - // since the primary responsibility of a context is to hold an ODBC handle, we - // provide these convenience operators for using them interchangeably - operator SQLHANDLE ( void ) const - { - return handle_; - } - - error_callback error_handler( void ) const - { - return err_; - } - - SQLHANDLE handle( void ) const - { - return handle_; - } - - SQLSMALLINT handle_type( void ) const - { - return handle_type_; - } - - const char* func( void ) const - { - return name_; - } - - void* driver( void ) const - { - return driver_; - } - - void set_driver( void* driver ) - { - this->driver_ = driver; - } - - void invalidate( void ) - { - if( handle_ != SQL_NULL_HANDLE ) { - ::SQLFreeHandle( handle_type_, handle_ ); - - last_error_.reset(); - } - handle_ = SQL_NULL_HANDLE; - } - - bool valid( void ) - { - return handle_ != SQL_NULL_HANDLE; - } - - SQLSRV_ENCODING encoding( void ) const - { - return encoding_; - } - - void set_encoding( SQLSRV_ENCODING e ) - { - encoding_ = e; - } - - private: - SQLHANDLE handle_; // ODBC handle for this context - SQLSMALLINT handle_type_; // type of the ODBC handle - const char* name_; // function name currently executing this context - error_callback err_; // driver error callback if error occurs in core layer - void* driver_; // points back to the driver for PDO - sqlsrv_error_auto_ptr last_error_; // last error that happened on this object - SQLSRV_ENCODING encoding_; // encoding of the context -}; - -const int SQLSRV_OS_VISTA_OR_LATER = 6; // major version for Vista - -// maps an IANA encoding to a code page -struct sqlsrv_encoding { - - const char* iana; - size_t iana_len; - unsigned int code_page; - bool not_for_connection; - - sqlsrv_encoding( const char* iana, unsigned int code_page, bool not_for_conn = false ): - iana( iana ), iana_len( strlen( iana )), code_page( code_page ), not_for_connection( not_for_conn ) - { - } -}; - - -//********************************************************************************************************************************* -// Initialization -//********************************************************************************************************************************* - -// variables set during initialization -extern OSVERSIONINFO g_osversion; // used to determine which OS we're running in -extern HashTable* g_encodings; // encodings supported by this driver - -void core_sqlsrv_minit( sqlsrv_context** henv_cp, sqlsrv_context** henv_ncp, error_callback err, const char* driver_func TSRMLS_DC ); -void core_sqlsrv_mshutdown( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp ); - -// environment context used by sqlsrv_connect for when a connection error occurs. -struct sqlsrv_henv { - - sqlsrv_context ctx; - - sqlsrv_henv( SQLHANDLE handle, error_callback e, void* drv ) : - ctx( handle, SQL_HANDLE_ENV, e, drv ) - { - } -}; - - -//********************************************************************************************************************************* -// Connection -//********************************************************************************************************************************* - -// supported server versions (determined at connection time) -enum SERVER_VERSION { - SERVER_VERSION_UNKNOWN = -1, - SERVER_VERSION_2000 = 8, - SERVER_VERSION_2005, - SERVER_VERSION_2008, // use this for anything 2008 or later -}; - -// supported driver versions. -enum DRIVER_VERSION : size_t { - MIN = 0, - ODBC_DRIVER_13 = MIN, - ODBC_DRIVER_11 = 1, - MAX = ODBC_DRIVER_11, -}; - -// forward decl -struct sqlsrv_stmt; -struct stmt_option; - -// *** connection resource structure *** -// this is the resource structure returned when a connection is made. -struct sqlsrv_conn : public sqlsrv_context { - - // instance variables - SERVER_VERSION server_version; // version of the server that we're connected to - - DRIVER_VERSION driver_version; - - // initialize with default values - sqlsrv_conn( SQLHANDLE h, error_callback e, void* drv, SQLSRV_ENCODING encoding TSRMLS_DC ) : - sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding ) - { - } - - // sqlsrv_conn has no destructor since its allocated using placement new, which requires that the destructor be - // called manually. Instead, we leave it to the allocator to invalidate the handle when an error occurs allocating - // the sqlsrv_conn with a connection. -}; - -enum SQLSRV_STMT_OPTIONS { - - SQLSRV_STMT_OPTION_INVALID, - SQLSRV_STMT_OPTION_QUERY_TIMEOUT, - SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC, - SQLSRV_STMT_OPTION_SCROLLABLE, - SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE, - - // Driver specific connection options - SQLSRV_STMT_OPTION_DRIVER_SPECIFIC = 1000, - -}; - -namespace ODBCConnOptions { - -const char APP[] = "APP"; -const char ApplicationIntent[] = "ApplicationIntent"; -const char AttachDBFileName[] = "AttachDbFileName"; -const char CharacterSet[] = "CharacterSet"; -const char ConnectionPooling[] = "ConnectionPooling"; -const char Database[] = "Database"; -const char Encrypt[] = "Encrypt"; -const char Failover_Partner[] = "Failover_Partner"; -const char LoginTimeout[] = "LoginTimeout"; -const char MARS_ODBC[] = "MARS_Connection"; -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 WSID[] = "WSID"; -const char UID[] = "UID"; -const char PWD[] = "PWD"; -const char SERVER[] = "Server"; - -} - -enum SQLSRV_CONN_OPTIONS { - - SQLSRV_CONN_OPTION_INVALID, - SQLSRV_CONN_OPTION_APP, - SQLSRV_CONN_OPTION_CHARACTERSET, - SQLSRV_CONN_OPTION_CONN_POOLING, - SQLSRV_CONN_OPTION_DATABASE, - SQLSRV_CONN_OPTION_ENCRYPT, - SQLSRV_CONN_OPTION_FAILOVER_PARTNER, - SQLSRV_CONN_OPTION_LOGIN_TIMEOUT, - SQLSRV_CONN_OPTION_MARS, - SQLSRV_CONN_OPTION_QUOTED_ID, - SQLSRV_CONN_OPTION_TRACE_FILE, - SQLSRV_CONN_OPTION_TRACE_ON, - SQLSRV_CONN_OPTION_TRANS_ISOLATION, - SQLSRV_CONN_OPTION_TRUST_SERVER_CERT, - SQLSRV_CONN_OPTION_WSID, - SQLSRV_CONN_OPTION_ATTACHDBFILENAME, - SQLSRV_CONN_OPTION_APPLICATION_INTENT, - SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER, - - // Driver specific connection options - SQLSRV_CONN_OPTION_DRIVER_SPECIFIC = 1000, - -}; - - -#define NO_ATTRIBUTE -1 - -// type of connection attributes -enum CONN_ATTR_TYPE { - CONN_ATTR_INT, - CONN_ATTR_BOOL, - CONN_ATTR_STRING, - CONN_ATTR_INVALID, -}; - -// a connection option that includes the callback function that handles that option (e.g., adds it to the connection string or -// sets an attribute) -struct connection_option { - // the name of the option as passed in by the user - const char * sqlsrv_name; - unsigned int sqlsrv_len; - - unsigned int conn_option_key; - // the name of the option in the ODBC connection string - const char * odbc_name; - unsigned int odbc_len; - enum CONN_ATTR_TYPE value_type; - - // process the connection type - // return whether or not the function was successful in processing the connection option - void (*func)( connection_option const*, zval* value, sqlsrv_conn* conn, std::string& conn_str TSRMLS_DC ); -}; - -// connection attribute functions -template -struct str_conn_attr_func { - - static void func( connection_option const* /*option*/, zval* value, sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC ) - { - try { - core::SQLSetConnectAttr( conn, Attr, reinterpret_cast( Z_STRVAL_P( value )), - static_cast(Z_STRLEN_P( value )) TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } - } -}; - -// simply add the parsed value to the connection string -struct conn_str_append_func { - - static void func( connection_option const* option, zval* value, sqlsrv_conn* /*conn*/, std::string& conn_str TSRMLS_DC ); -}; - -struct conn_null_func { - - static void func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ - TSRMLS_DC ); -}; - -// factory to create a connection (since they are subclassed to instantiate statements) -typedef sqlsrv_conn* (*driver_conn_factory)( SQLHANDLE h, error_callback e, void* drv TSRMLS_DC ); - -// *** connection functions *** -sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp, driver_conn_factory conn_factory, - const char* server, const char* uid, const char* pwd, - HashTable* options_ht, error_callback err, const connection_option driver_conn_opt_list[], - void* driver, const char* driver_func TSRMLS_DC ); -void core_sqlsrv_close( sqlsrv_conn* conn TSRMLS_DC ); -void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, SQLLEN sql_len TSRMLS_DC ); -void core_sqlsrv_begin_transaction( sqlsrv_conn* conn TSRMLS_DC ); -void core_sqlsrv_commit( sqlsrv_conn* conn TSRMLS_DC ); -void core_sqlsrv_rollback( sqlsrv_conn* conn TSRMLS_DC ); -void core_sqlsrv_get_server_info( sqlsrv_conn* conn, _Out_ zval* server_info TSRMLS_DC ); -void core_sqlsrv_get_server_version( sqlsrv_conn* conn, _Out_ zval *server_version TSRMLS_DC ); -void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC ); -bool core_is_conn_opt_value_escaped( const char* value, size_t value_len ); -size_t core_str_zval_is_true( zval* str_zval ); - -//********************************************************************************************************************************* -// Statement -//********************************************************************************************************************************* - -struct stmt_option_functor { - - virtual void operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC ); -}; - -struct stmt_option_query_timeout : public stmt_option_functor { - - virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* opt, zval* value_z TSRMLS_DC ); -}; - -struct stmt_option_send_at_exec : public stmt_option_functor { - - virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* opt, zval* value_z TSRMLS_DC ); -}; - -struct stmt_option_buffered_query_limit : public stmt_option_functor { - - virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* opt, zval* value_z TSRMLS_DC ); -}; - -// used to hold the table for statment options -struct stmt_option { - - const char * name; // name of the statement option - unsigned int name_len; // name length - unsigned int key; - std::unique_ptr func; // callback that actually handles the work of the option - -}; - -// holds the stream param and the encoding that it was assigned -struct sqlsrv_stream { - - zval* stream_z; - SQLSRV_ENCODING encoding; - SQLUSMALLINT field_index; - SQLSMALLINT sql_type; - sqlsrv_stmt* stmt; - std::size_t stmt_index; - - sqlsrv_stream( zval* str_z, SQLSRV_ENCODING enc ) : - stream_z( str_z ), encoding( enc ) - { - } - - sqlsrv_stream() : stream_z( NULL ), encoding( SQLSRV_ENCODING_INVALID ), stmt( NULL ) - { - } -}; - -// close any active stream -void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ); - -extern php_stream_wrapper g_sqlsrv_stream_wrapper; - -// resource constants used when registering the stream type with PHP -#define SQLSRV_STREAM_WRAPPER "sqlsrv" -#define SQLSRV_STREAM "sqlsrv_stream" - -// holds the output parameter information. Strings also need the encoding and other information for -// after processing. Only integer, float, and strings are allowable output parameters. -struct sqlsrv_output_param { - - zval* param_z; - SQLSRV_ENCODING encoding; - SQLUSMALLINT param_num; // used to index into the ind_or_len of the statement - SQLLEN original_buffer_len; // used to make sure the returned length didn't overflow the buffer - bool is_bool; - - // string output param constructor - sqlsrv_output_param( zval* p_z, SQLSRV_ENCODING enc, int num, SQLUINTEGER buffer_len ) : - param_z( p_z ), encoding( enc ), param_num( num ), original_buffer_len( buffer_len ), is_bool( false ) - { - } - - // every other type output parameter constructor - sqlsrv_output_param( zval* p_z, int num, bool is_bool ) : - param_z( p_z ), - param_num( num ), - encoding( SQLSRV_ENCODING_INVALID ), - original_buffer_len( -1 ), - is_bool( is_bool ) - { - } -}; - -// forward decls -struct sqlsrv_result_set; - -// *** Statement resource structure *** -struct sqlsrv_stmt : public sqlsrv_context { - - void free_param_data( TSRMLS_D ); - virtual void new_result_set( TSRMLS_D ); - - sqlsrv_conn* conn; // Connection that created this statement - - bool executed; // Whether the statement has been executed yet (used for error messages) - bool past_fetch_end; // Core_sqlsrv_fetch sets this field when the statement goes beyond the last row - sqlsrv_result_set* current_results; // Current result set - SQLULEN cursor_type; // Type of cursor for the current result set - bool has_rows; // Has_rows is set if there are actual rows in the row set - bool fetch_called; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called - int last_field_index; // last field retrieved by core_sqlsrv_get_field - bool past_next_result_end; // core_sqlsrv_next_result sets this to true when the statement goes beyond the - // last results - unsigned long query_timeout; // maximum allowed statement execution time - zend_long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB) - - // holds output pointers for SQLBindParameter - // We use a deque because it 1) provides the at/[] access in constant time, and 2) grows dynamically without moving - // memory, which is important because we pass the pointer to an element of the deque to SQLBindParameter to hold - std::deque param_ind_ptrs; // output pointers for lengths for calls to SQLBindParameter - zval param_input_strings; // hold all UTF-16 input strings that aren't managed by PHP - zval output_params; // hold all the output parameters - zval param_streams; // track which streams to send data to the server - zval param_datetime_buffers; // datetime strings to be converted back to DateTime objects - bool send_streams_at_exec; // send all stream data right after execution before returning - sqlsrv_stream current_stream; // current stream sending data to the server as an input parameter - unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string - // to the server) - zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals - zval active_stream; // the currently active stream reading data from the database - - sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC ); - virtual ~sqlsrv_stmt( void ); - - // driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants - virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream, bool prefer_number_to_string = false ) = 0; - -}; - -// *** field metadata struct *** -struct field_meta_data { - - sqlsrv_malloc_auto_ptr field_name; - SQLSMALLINT field_name_len; - SQLSMALLINT field_type; - SQLULEN field_size; - SQLULEN field_precision; - SQLSMALLINT field_scale; - SQLSMALLINT field_is_nullable; - - field_meta_data() : field_name_len(0), field_type(0), field_size(0), field_precision(0), - field_scale (0), field_is_nullable(0) - { - } - - ~field_meta_data() - { - } -}; - -// *** statement constants *** -// unknown column size used by core_sqlsrv_bind_param when the user doesn't supply a value -const SQLULEN SQLSRV_UNKNOWN_SIZE = 0xffffffff; -const int SQLSRV_DEFAULT_SIZE = -1; // size given for an output parameter that doesn't really need one (e.g., int) - -// uninitialized query timeout value -const unsigned int QUERY_TIMEOUT_INVALID = 0xffffffff; - -// special buffered query constant -const size_t SQLSRV_CURSOR_BUFFERED = 0xfffffffeUL; // arbitrary number that doesn't map to any other SQL_CURSOR_* constant - -// factory to create a statement -typedef sqlsrv_stmt* (*driver_stmt_factory)( sqlsrv_conn* conn, SQLHANDLE h, error_callback e, void* drv TSRMLS_DC ); - -// *** statement functions *** -sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stmt_factory, HashTable* options_ht, - const stmt_option valid_stmt_opts[], error_callback const err, void* driver TSRMLS_DC ); -void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLINT direction, zval* param_z, - SQLSRV_PHPTYPE php_out_type, SQLSRV_ENCODING encoding, SQLSMALLINT sql_type, SQLULEN column_size, - SQLSMALLINT decimal_digits TSRMLS_DC ); -void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql = NULL, int sql_len = 0 ); -field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT colno TSRMLS_DC ); -bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC ); -void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_phptype, bool prefer_string, - _Out_ void*& field_value, _Out_ SQLLEN* field_length, bool cache_field, - _Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC); -bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC ); -void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params = true, bool throw_on_errors = true ); -void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC ); -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC ); -void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, long timeout TSRMLS_DC ); -void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ); -void core_sqlsrv_set_send_at_exec( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ); -bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC ); -void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ); -void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC ); - - -//********************************************************************************************************************************* -// Result Set -//********************************************************************************************************************************* - -// Abstract the result set so that a result set can either be used as is from ODBC or buffered. -// This is not a complete abstraction of a result set. Only enough is abstracted to allow for -// information and capabilities normally not available when a result set is not buffered -// (e.g., forward only vs buffered means row count is available and cursor movement is possible). -// Otherwise, normal ODBC calls are still valid and should be used to get information about the -// result set (e.g., SQLNumResultCols). - -struct sqlsrv_result_set { - - sqlsrv_stmt* odbc; - - explicit sqlsrv_result_set( sqlsrv_stmt* ); - virtual ~sqlsrv_result_set( void ) { } - - virtual bool cached( int field_index ) = 0; - virtual SQLRETURN fetch( SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC ) = 0; - virtual SQLRETURN get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC )= 0; - virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0; - virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number ) = 0; - virtual SQLLEN row_count( TSRMLS_D ) = 0; -}; - -struct sqlsrv_odbc_result_set : public sqlsrv_result_set { - - explicit sqlsrv_odbc_result_set( sqlsrv_stmt* ); - virtual ~sqlsrv_odbc_result_set( void ); - - virtual bool cached( int field_index ) { return false; } - virtual SQLRETURN fetch( SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC ); - virtual SQLRETURN get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC ); - virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ); - virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number ); - virtual SQLLEN row_count( TSRMLS_D ); - - private: - // prevent invalid instantiations and assignments - sqlsrv_odbc_result_set( void ); - sqlsrv_odbc_result_set( sqlsrv_odbc_result_set& ); - sqlsrv_odbc_result_set& operator=( sqlsrv_odbc_result_set& ); -}; - -struct sqlsrv_buffered_result_set : public sqlsrv_result_set { - - struct meta_data { - SQLSMALLINT type; - SQLSMALLINT c_type; // convenience - SQLULEN offset; // in bytes - SQLULEN length; // in bytes - SQLSMALLINT scale; - - static const SQLULEN SIZE_UNKNOWN = 0; - }; - - // default maximum amount of memory that a buffered query can consume - #define INI_BUFFERED_QUERY_LIMIT_DEFAULT "10240" // default used by the php.ini settings - static const zend_long BUFFERED_QUERY_LIMIT_DEFAULT = 10240; // measured in KB - static const zend_long BUFFERED_QUERY_LIMIT_INVALID = 0; - - explicit sqlsrv_buffered_result_set( sqlsrv_stmt* odbc TSRMLS_DC ); - virtual ~sqlsrv_buffered_result_set( void ); - - virtual bool cached( int field_index ) { return true; } - virtual SQLRETURN fetch( SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC ); - virtual SQLRETURN get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC ); - virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ); - virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number ); - virtual SQLLEN row_count( TSRMLS_D ); - - // buffered result set specific - SQLSMALLINT column_count( void ) - { - return col_count; - } - - struct meta_data& col_meta_data( SQLSMALLINT i ) - { - return meta[i]; - } - - private: - // prevent invalid instantiations and assignments - sqlsrv_buffered_result_set( void ); - sqlsrv_buffered_result_set( sqlsrv_buffered_result_set& ); - sqlsrv_buffered_result_set& operator=( sqlsrv_buffered_result_set& ); - - HashTable* cache; // rows of data kept in index based hash table - SQLSMALLINT col_count; // number of columns in the current result set - meta_data* meta; // metadata for fields in the cache - SQLLEN current; // 1 based, 0 means before first row - sqlsrv_error_auto_ptr last_error; // if an error occurred, it is kept here - SQLUSMALLINT last_field_index; // the last field data retrieved from - SQLLEN read_so_far; // position within string to read from (for partial reads of strings) - sqlsrv_malloc_auto_ptr temp_string; // temp buffer to hold a converted field while in use - SQLLEN temp_length; // number of bytes in the temp conversion buffer - - typedef SQLRETURN (sqlsrv_buffered_result_set::*conv_fn)( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - typedef std::map< SQLINTEGER, std::map< SQLINTEGER, conv_fn > > conv_matrix_t; - - // two dimentional sparse matrix that holds the [from][to] functions that do conversions - static conv_matrix_t conv_matrix; - - // string conversion functions - SQLRETURN binary_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN binary_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN system_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN to_binary_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN to_same_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN wide_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - - // long conversion functions - SQLRETURN to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length ); - SQLRETURN long_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN long_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN long_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - - // double conversion functions - SQLRETURN to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length ); - SQLRETURN double_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN double_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN double_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - - // string to number conversion functions - // Future: See if these can be converted directly to template member functions - SQLRETURN string_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN string_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN wstring_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - SQLRETURN wstring_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length, - _Out_ SQLLEN* out_buffer_length ); - - // utility functions for conversions - unsigned char* get_row( void ); -}; - -//********************************************************************************************************************************* -// Utility -//********************************************************************************************************************************* - -// Simple macro to alleviate unused variable warnings. These are optimized out by the compiler. -// We use this since the unused variables are buried in the PHP_FUNCTION macro. -#define SQLSRV_UNUSED( var ) var; - -// do a heap check in debug mode, but only print errors, not all of the allocations -#define MEMCHECK_SILENT 1 - -// utility functions shared by multiple callers across files -bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLLEN& len); -bool convert_zval_string_from_utf16(SQLSRV_ENCODING encoding, zval* value_z, SQLLEN& len); -bool validate_string(char* string, SQLLEN& len); -bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLLEN& cchOutLen ); -wchar_t* utf16_string_from_mbcs_string( SQLSRV_ENCODING php_encoding, const char* mbcs_string, - unsigned int mbcs_len, _Out_ unsigned int* utf16_len ); - -//********************************************************************************************************************************* -// Error handling routines and Predefined Errors -//********************************************************************************************************************************* - -enum SQLSRV_ERROR_CODES { - - SQLSRV_ERROR_ODBC, - SQLSRV_ERROR_DRIVER_NOT_INSTALLED, - SQLSRV_ERROR_ZEND_HASH, - SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, - SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, - SQLSRV_ERROR_INVALID_PARAMETER_ENCODING, - SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, - SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, - SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, - SQLSRV_ERROR_ZEND_STREAM, - SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, - SQLSRV_ERROR_UNKNOWN_SERVER_VERSION, - SQLSRV_ERROR_FETCH_PAST_END, - SQLSRV_ERROR_STATEMENT_NOT_EXECUTED, - SQLSRV_ERROR_NO_FIELDS, - SQLSRV_ERROR_INVALID_TYPE, - SQLSRV_ERROR_FETCH_NOT_CALLED, - SQLSRV_ERROR_NO_DATA, - SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, - SQLSRV_ERROR_ZEND_HASH_CREATE_FAILED, - SQLSRV_ERROR_NEXT_RESULT_PAST_END, - SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED, - SQLSRV_ERROR_INVALID_OPTION_TYPE_INT, - SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING, - SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE, - SQLSRV_ERROR_INVALID_CONNECTION_KEY, - SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, - SQLSRV_ERROR_INVALID_OPTION_KEY, - SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, - SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE, - SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, - SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, - SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, - SQLSRV_ERROR_DATETIME_CONVERSION_FAILED, - SQLSRV_ERROR_STREAMABLE_TYPES_ONLY, - SQLSRV_ERROR_STREAM_CREATE, - SQLSRV_ERROR_MARS_OFF, - SQLSRV_ERROR_FIELD_INDEX_ERROR, - SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, - SQLSRV_ERROR_INVALID_BUFFER_LIMIT, - - // Driver specific error codes starts from here. - SQLSRV_ERROR_DRIVER_SPECIFIC = 1000, - -}; - -// the message returned by ODBC Driver 11 for SQL Server -static const char* CONNECTION_BUSY_ODBC_ERROR[] = { "[Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command", - "[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for another command" }; - -// SQLSTATE for all internal errors -extern SQLCHAR IMSSP[]; - -// SQLSTATE for all internal warnings -extern SQLCHAR SSPWARN[]; - -// flags passed to sqlsrv_errors to filter its return values -enum error_handling_flags { - SQLSRV_ERR_ERRORS, - SQLSRV_ERR_WARNINGS, - SQLSRV_ERR_ALL -}; - -// *** internal error macros and functions *** -// call to retrieve an error from ODBC. This uses SQLGetDiagRec, so the -// errno is 1 based. It returns it as an array with 3 members: -// 1/SQLSTATE) sqlstate -// 2/code) driver specific error code -// 3/message) driver specific error message -// The fetch type determines if the indices are numeric, associative, or both. -bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, _Out_ sqlsrv_error_auto_ptr& error, - logging_severity severity TSRMLS_DC ); - -// format and return a driver specfic error -void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const const* custom_error, - sqlsrv_error_auto_ptr& formatted_error, logging_severity severity TSRMLS_DC, va_list* args ); - - -// return the message for the HRESULT returned by GetLastError. Some driver errors use this to -// return the Windows error, e.g, when a UTF-8 <-> UTF-16 conversion fails. -const char* get_last_error_message( DWORD last_error = 0 ); - -// a wrapper around FormatMessage that can take variadic args rather than a a va_arg pointer -DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... ); - -// convenience functions that overload either a reference or a pointer so we can use -// either in the CHECK_* functions. -inline bool call_error_handler( sqlsrv_context& ctx, unsigned long sqlsrv_error_code TSRMLS_DC, bool warning, ... ) -{ - va_list print_params; - va_start( print_params, warning ); - bool ignored = ctx.error_handler()( ctx, sqlsrv_error_code, warning TSRMLS_CC, &print_params ); - va_end( print_params ); - return ignored; -} - -inline bool call_error_handler( sqlsrv_context* ctx, unsigned long sqlsrv_error_code TSRMLS_DC, bool warning, ... ) -{ - va_list print_params; - va_start( print_params, warning ); - bool ignored = ctx->error_handler()( *ctx, sqlsrv_error_code, warning TSRMLS_CC, &print_params ); - va_end( print_params ); - return ignored; -} - -// PHP equivalent of ASSERT. C asserts cause a dialog to show and halt the process which -// we don't want on a web server - -#define SQLSRV_ASSERT( condition, msg, ...) if( !(condition)) DIE( msg, __VA_ARGS__ ); - -#if defined( PHP_DEBUG ) - -#define DEBUG_SQLSRV_ASSERT( condition, msg, ... ) \ - if( !(condition)) { \ - DIE (msg, __VA_ARGS__ ); \ - } - -#else - - #define DEBUG_SQLSRV_ASSERT( condition, msg, ... ) ((void)0) - -#endif - -// check to see if the sqlstate is 01004, truncated field retrieved. Used for retrieving large fields. -inline bool is_truncated_warning( SQLCHAR* state ) -{ -#if defined(ZEND_DEBUG) - if( state == NULL || strlen( reinterpret_cast( state )) != 5 ) { \ - DIE( "Incorrect SQLSTATE given to is_truncated_warning." ); \ - } -#endif - return (state[0] == '0' && state[1] == '1' && state[2] == '0' && state [3] == '0' && state [4] == '4'); -} - -// Macros for handling errors. These macros are simplified if statements that take boilerplate -// code down to a single line to avoid distractions in the code. - -#define CHECK_ERROR_EX( unique, condition, context, ssphp, ... ) \ - bool flag##unique = (condition); \ - bool ignored##unique = true; \ - if (flag##unique) { \ - ignored##unique = call_error_handler( context, ssphp TSRMLS_CC, /*warning*/false, __VA_ARGS__ ); \ - } \ - if( !ignored##unique ) - -#define CHECK_ERROR_UNIQUE( unique, condition, context, ssphp, ...) \ - CHECK_ERROR_EX( unique, condition, context, ssphp, __VA_ARGS__ ) - -#define CHECK_ERROR( condition, context, ... ) \ - CHECK_ERROR_UNIQUE( __COUNTER__, condition, context, NULL, __VA_ARGS__ ) - -#define CHECK_CUSTOM_ERROR( condition, context, ssphp, ... ) \ - CHECK_ERROR_UNIQUE( __COUNTER__, condition, context, ssphp, __VA_ARGS__ ) - -#define CHECK_SQL_ERROR( result, context, ... ) \ - SQLSRV_ASSERT( result != SQL_INVALID_HANDLE, "Invalid handle returned." ); \ - CHECK_ERROR( result == SQL_ERROR, context, __VA_ARGS__ ) - -#define CHECK_WARNING_AS_ERROR_UNIQUE( unique, condition, context, ssphp, ... ) \ - bool ignored##unique = true; \ - if( condition ) { \ - ignored##unique = call_error_handler( context, ssphp TSRMLS_CC, /*warning*/true, __VA_ARGS__ ); \ - } \ - if( !ignored##unique ) - -#define CHECK_SQL_WARNING_AS_ERROR( result, context, ... ) \ - CHECK_WARNING_AS_ERROR_UNIQUE( __COUNTER__,( result == SQL_SUCCESS_WITH_INFO ), context, SQLSRV_ERROR_ODBC, __VA_ARGS__ ) - -#define CHECK_SQL_WARNING( result, context, ... ) \ - if( result == SQL_SUCCESS_WITH_INFO ) { \ - (void)call_error_handler( context, NULL TSRMLS_CC, /*warning*/ true, __VA_ARGS__ ); \ - } - -#define CHECK_CUSTOM_WARNING_AS_ERROR( condition, context, ssphp, ... ) \ - CHECK_WARNING_AS_ERROR_UNIQUE( __COUNTER__, condition, context, ssphp, __VA_ARGS__ ) - -#define CHECK_ZEND_ERROR( zr, ctx, error, ... ) \ - CHECK_ERROR_UNIQUE( __COUNTER__, ( zr == FAILURE ), ctx, error, __VA_ARGS__ ) \ - -#define CHECK_SQL_ERROR_OR_WARNING( result, context, ... ) \ - SQLSRV_ASSERT( result != SQL_INVALID_HANDLE, "Invalid handle returned." ); \ - bool ignored = true; \ - if( result == SQL_ERROR ) { \ - ignored = call_error_handler( context, SQLSRV_ERROR_ODBC TSRMLS_CC, false, __VA_ARGS__ ); \ - } \ - else if( result == SQL_SUCCESS_WITH_INFO ) { \ - ignored = call_error_handler( context, SQLSRV_ERROR_ODBC TSRMLS_CC, true TSRMLS_CC, __VA_ARGS__ ); \ - } \ - if( !ignored ) - -// throw an exception after it has been hooked into the custom error handler -#define THROW_CORE_ERROR( ctx, custom, ... ) \ - (void)call_error_handler( ctx, custom TSRMLS_CC, /*warning*/ false, __VA_ARGS__ ); \ - throw core::CoreException(); - -//********************************************************************************************************************************* -// ODBC/Zend function wrappers -//********************************************************************************************************************************* - -namespace core { - - // base exception for the driver - struct CoreException : public std::exception { - - CoreException() - { - } - }; - - inline void check_for_mars_error( sqlsrv_stmt* stmt, SQLRETURN r TSRMLS_DC ) - { - // We check for the 'connection busy' error caused by having MultipleActiveResultSets off - // and return a more helpful message prepended to the ODBC errors if that error occurs - if( !SQL_SUCCEEDED( r )) { - - SQLCHAR err_msg[ SQL_MAX_MESSAGE_LENGTH + 1 ]; - SQLSMALLINT len = 0; - - SQLRETURN r = ::SQLGetDiagField( stmt->handle_type(), stmt->handle(), 1, SQL_DIAG_MESSAGE_TEXT, - err_msg, SQL_MAX_MESSAGE_LENGTH, &len ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - - throw CoreException(); - } - std::size_t driver_version = stmt->conn->driver_version; - if( !strcmp( reinterpret_cast( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) { - - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF ); - } - } - } - - // *** ODBC wrappers *** - - // wrap the ODBC functions to throw exceptions rather than use the return value to signal errors - // some of the signatures have been altered to be more convenient since the return value is no longer - // required to return the status of the call (e.g., SQLNumResultCols). - // These functions take the sqlsrv_context type. However, since the error handling code can alter - // the context to hold the error, they are not passed as const. - - inline SQLRETURN SQLGetDiagField( sqlsrv_context* ctx, SQLSMALLINT record_number, SQLSMALLINT diag_identifier, - _Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) - { - SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier, - diag_info_buffer, buffer_length, out_buffer_length ); - - CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { - throw CoreException(); - } - - return r; - } - - inline void SQLAllocHandle( SQLSMALLINT HandleType, sqlsrv_context& InputHandle, - _Out_writes_(1) SQLHANDLE* OutputHandlePtr TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr ); - CHECK_SQL_ERROR_OR_WARNING( r, InputHandle ) { - throw CoreException(); - } - } - - inline void SQLBindParameter( sqlsrv_stmt* stmt, - SQLUSMALLINT ParameterNumber, - SQLSMALLINT InputOutputType, - SQLSMALLINT ValueType, - SQLSMALLINT ParameterType, - SQLULEN ColumnSize, - SQLSMALLINT DecimalDigits, - _Inout_ SQLPOINTER ParameterValuePtr, - SQLLEN BufferLength, - _Inout_ SQLLEN * StrLen_Or_IndPtr - TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLBindParameter( stmt->handle(), ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize, - DecimalDigits, ParameterValuePtr, BufferLength, StrLen_Or_IndPtr ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - inline void SQLColAttribute( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLUSMALLINT field_identifier, - _Out_ SQLPOINTER field_type_char, SQLSMALLINT buffer_length, - _Out_ SQLSMALLINT* out_buffer_length, _Out_ SQLLEN* field_type_num TSRMLS_DC ) - { - SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char, - buffer_length, out_buffer_length, field_type_num ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - inline void SQLDescribeCol( sqlsrv_stmt* stmt, SQLSMALLINT colno, _Out_ SQLCHAR* col_name, SQLSMALLINT col_name_length, - _Out_ SQLSMALLINT* col_name_length_out, SQLSMALLINT* data_type, _Out_ SQLULEN* col_size, - _Out_ SQLSMALLINT* decimal_digits, _Out_ SQLSMALLINT* nullable TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out, - data_type, col_size, decimal_digits, nullable); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - inline void SQLEndTran( SQLSMALLINT handleType, sqlsrv_conn* conn, SQLSMALLINT completionType TSRMLS_DC ) - { - SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType ); - - CHECK_SQL_ERROR_OR_WARNING( r, conn ) { - throw CoreException(); - } - } - - // SQLExecDirect returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success - inline SQLRETURN SQLExecDirect( sqlsrv_stmt* stmt, char* sql TSRMLS_DC ) - { - SQLRETURN r = ::SQLExecDirect( stmt->handle(), reinterpret_cast( sql ), SQL_NTS ); - - check_for_mars_error( stmt, r TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - - throw CoreException(); - } - return r; - } - - inline SQLRETURN SQLExecDirectW( sqlsrv_stmt* stmt, wchar_t* wsql TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLExecDirectW( stmt->handle(), reinterpret_cast( wsql ), SQL_NTS ); - - check_for_mars_error( stmt, r TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - return r; - } - - // SQLExecute returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success - inline SQLRETURN SQLExecute( sqlsrv_stmt* stmt TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLExecute( stmt->handle() ); - - check_for_mars_error( stmt, r TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - - return r; - } - - inline SQLRETURN SQLFetchScroll( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC ) - { - SQLRETURN r = ::SQLFetchScroll( stmt->handle(), fetch_orientation, fetch_offset ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - return r; - } - - - // wrap SQLFreeHandle and report any errors, but don't actually signal an error to the calling routine - inline void SQLFreeHandle( sqlsrv_context& ctx TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLFreeHandle( ctx.handle_type(), ctx.handle() ); - CHECK_SQL_ERROR_OR_WARNING( r, ctx ) {} - } - - inline SQLRETURN SQLGetData( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLSMALLINT target_type, - _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length, - bool handle_warning TSRMLS_DC ) - { - SQLRETURN r = ::SQLGetData( stmt->handle(), field_index, target_type, buffer, buffer_length, out_buffer_length ); - - if( r == SQL_NO_DATA ) - return r; - - CHECK_SQL_ERROR( r, stmt ) { - throw CoreException(); - } - - if( handle_warning ) { - CHECK_SQL_WARNING_AS_ERROR( r, stmt ) { - throw CoreException(); - } - } - - return r; - } - - - inline void SQLGetInfo( sqlsrv_conn* conn, SQLUSMALLINT info_type, _Out_ SQLPOINTER info_value, SQLSMALLINT buffer_len, - _Out_ SQLSMALLINT* str_len TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len ); - - CHECK_SQL_ERROR_OR_WARNING( r, conn ) { - throw CoreException(); - } - } - - - inline void SQLGetTypeInfo( sqlsrv_stmt* stmt, SQLUSMALLINT data_type TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLGetTypeInfo( stmt->handle(), data_type ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - // SQLMoreResults returns the status code since it returns SQL_NO_DATA when there is no more data in a result set. - inline SQLRETURN SQLMoreResults( sqlsrv_stmt* stmt TSRMLS_DC ) - { - SQLRETURN r = ::SQLMoreResults( stmt->handle() ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - - return r; - } - - inline SQLSMALLINT SQLNumResultCols( sqlsrv_stmt* stmt TSRMLS_DC ) - { - SQLRETURN r; - SQLSMALLINT num_cols; - r = ::SQLNumResultCols( stmt->handle(), &num_cols ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - - return num_cols; - } - - // SQLParamData returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA when there are more - // parameters or when the parameters are all processed. - inline SQLRETURN SQLParamData( sqlsrv_stmt* stmt, _Out_ SQLPOINTER* value_ptr_ptr TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLParamData( stmt->handle(), value_ptr_ptr ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - return r; - } - - inline void SQLPrepareW( sqlsrv_stmt* stmt, SQLWCHAR * sql, SQLINTEGER sql_len TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLPrepareW( stmt->handle(), sql, sql_len ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - inline void SQLPutData( sqlsrv_stmt* stmt, SQLPOINTER data_ptr, SQLLEN strlen_or_ind TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLPutData( stmt->handle(), data_ptr, strlen_or_ind ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - inline SQLLEN SQLRowCount( sqlsrv_stmt* stmt TSRMLS_DC ) - { - SQLRETURN r; - SQLLEN rows_affected; - - r = ::SQLRowCount( stmt->handle(), &rows_affected ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - - return rows_affected; - } - - - inline void SQLSetConnectAttr( sqlsrv_context& ctx, SQLINTEGER attr, SQLPOINTER value_ptr, SQLINTEGER str_len TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLSetConnectAttr( ctx.handle(), attr, value_ptr, str_len ); - - CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { - throw CoreException(); - } - } - - - inline void SQLSetEnvAttr( sqlsrv_context& ctx, SQLINTEGER attr, SQLPOINTER value_ptr, SQLINTEGER str_len TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLSetEnvAttr( ctx.handle(), attr, value_ptr, str_len ); - CHECK_SQL_ERROR_OR_WARNING( r, ctx ) { - throw CoreException(); - } - } - - inline void SQLSetConnectAttr( sqlsrv_conn* conn, SQLINTEGER attribute, SQLPOINTER value_ptr, SQLINTEGER value_len TSRMLS_DC ) - { - SQLRETURN r = ::SQLSetConnectAttr( conn->handle(), attribute, value_ptr, value_len ); - - CHECK_SQL_ERROR_OR_WARNING( r, conn ) { - throw CoreException(); - } - } - - inline void SQLSetStmtAttr( sqlsrv_stmt* stmt, SQLINTEGER attr, SQLPOINTER value_ptr, SQLINTEGER str_len TSRMLS_DC ) - { - SQLRETURN r; - r = ::SQLSetStmtAttr( stmt->handle(), attr, value_ptr, str_len ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw CoreException(); - } - } - - - // *** zend wrappers *** - - //zend_resource_dtor sets the type of destroyed resources to -1 - #define RSRC_INVALID_TYPE -1 - - // wrapper for ZVAL_STRINGL macro. ZVAL_STRINGL always allocates memory when initialzing new string from char string - // so allocated memory inside of value_z should be released before assigning it to the new string - inline void sqlsrv_zval_stringl(zval* value_z, const char* str, const std::size_t str_len) - { - if (Z_TYPE_P(value_z) == IS_STRING && Z_STR_P(value_z) != NULL) { - zend_string* temp_zstr = zend_string_init(str, str_len, 0); - zend_string_release(Z_STR_P(value_z)); - ZVAL_NEW_STR(value_z, temp_zstr); - } - else { - ZVAL_STRINGL(value_z, str, str_len); - } - } - - - // exception thrown when a zend function wrapped here fails. - - // wrappers for the zend functions called by our driver. These functions hook into the error reporting of our driver and throw - // exceptions when an error occurs. They are prefaced with sqlsrv_ because many of the zend functions are - // actually macros that call other functions, so the sqlsrv_ is necessary to differentiate them from the macro system. - // If there is a zend function in the source that isn't found here, it is because it returns void and there is no error - // that can be thrown from it. - - inline void sqlsrv_add_index_zval( sqlsrv_context& ctx, zval* array, zend_ulong index, zval* value TSRMLS_DC) - { - int zr = ::add_index_zval( array, index, value ); - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_add_next_index_zval( sqlsrv_context& ctx, zval* array, zval* value TSRMLS_DC) - { - int zr = ::add_next_index_zval( array, value ); - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_add_assoc_null( sqlsrv_context& ctx, zval* array_z, const char* key TSRMLS_DC ) - { - int zr = ::add_assoc_null( array_z, key ); - CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_add_assoc_long( sqlsrv_context& ctx, zval* array_z, const char* key, zend_long val TSRMLS_DC ) - { - int zr = ::add_assoc_long( array_z, key, val ); - CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_add_assoc_string( sqlsrv_context& ctx, zval* array_z, const char* key, char* val, bool duplicate TSRMLS_DC ) - { - int zr = ::add_assoc_string(array_z, key, val); - CHECK_ZEND_ERROR (zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - if (duplicate == 0) { - sqlsrv_free(val); - } - } - - inline void sqlsrv_array_init( sqlsrv_context& ctx, _Out_ zval* new_array TSRMLS_DC) - { - int zr = ::array_init(new_array); - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_php_stream_from_zval_no_verify( sqlsrv_context& ctx, php_stream*& stream, zval* stream_z TSRMLS_DC ) - { - // this duplicates the macro php_stream_from_zval_no_verify, which we can't use because it has an assignment - php_stream_from_zval_no_verify( stream, stream_z ); - CHECK_CUSTOM_ERROR( stream == NULL, ctx, SQLSRV_ERROR_ZEND_STREAM ) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_get_current_data(sqlsrv_context& ctx, HashTable* ht, _Out_ zval*& output_data TSRMLS_DC) - { - int zr = (output_data = ::zend_hash_get_current_data(ht)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_get_current_data_ptr(sqlsrv_context& ctx, HashTable* ht, _Out_ void*& output_data TSRMLS_DC) - { - int zr = (output_data = ::zend_hash_get_current_data_ptr(ht)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_index_del( sqlsrv_context& ctx, HashTable* ht, zend_ulong index TSRMLS_DC ) - { - int zr = ::zend_hash_index_del( ht, index ); - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_index_update( sqlsrv_context& ctx, HashTable* ht, zend_ulong index, zval* data_z TSRMLS_DC ) - { - int zr = (data_z = ::zend_hash_index_update(ht, index, data_z)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_index_update_ptr(sqlsrv_context& ctx, HashTable* ht, zend_ulong index, void* pData TSRMLS_DC) - { - int zr = (pData = ::zend_hash_index_update_ptr(ht, index, pData)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) { - throw CoreException(); - } - } - - - inline void sqlsrv_zend_hash_index_update_mem(sqlsrv_context& ctx, HashTable* ht, zend_ulong index, void* pData, std::size_t size TSRMLS_DC) - { - int zr = (pData = ::zend_hash_index_update_mem(ht, index, pData, size)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_next_index_insert( sqlsrv_context& ctx, HashTable* ht, zval* data TSRMLS_DC ) - { - int zr = (data = ::zend_hash_next_index_insert(ht, data)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_next_index_insert_mem(sqlsrv_context& ctx, HashTable* ht, void* data, uint data_size TSRMLS_DC) - { - int zr = (data = ::zend_hash_next_index_insert_mem(ht, data, data_size)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_next_index_insert_ptr(sqlsrv_context& ctx, HashTable* ht, void* data TSRMLS_DC) - { - int zr = (data = ::zend_hash_next_index_insert_ptr(ht, data)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR(zr, ctx, SQLSRV_ERROR_ZEND_HASH) { - throw CoreException(); - } - } - - inline void sqlsrv_zend_hash_init(sqlsrv_context& ctx, HashTable* ht, uint32_t initial_size, - dtor_func_t dtor_fn, zend_bool persistent TSRMLS_DC ) - { - ::zend_hash_init(ht, initial_size, NULL, dtor_fn, persistent); - } - - inline void sqlsrv_zend_hash_add( sqlsrv_context& ctx, HashTable* ht, zend_string* key, unsigned int key_len, zval* data, - unsigned int data_size, zval* pDest TSRMLS_DC ) - { - int zr = (pDest = ::zend_hash_add(ht, key, data)) != NULL ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) { - throw CoreException(); - } - } - -template -sqlsrv_stmt* allocate_stmt( sqlsrv_conn* conn, SQLHANDLE h, error_callback e, void* driver TSRMLS_DC ) -{ - return new ( sqlsrv_malloc( sizeof( Statement ))) Statement( conn, h, e, driver TSRMLS_CC ); -} - -template -sqlsrv_conn* allocate_conn( SQLHANDLE h, error_callback e, void* driver TSRMLS_DC ) -{ - return new ( sqlsrv_malloc( sizeof( Connection ))) Connection( h, e, driver TSRMLS_CC ); -} - -} // namespace core - -#endif // CORE_SQLSRV_H diff --git a/source/pdo_sqlsrv/shared/core_stmt.cpp b/source/pdo_sqlsrv/shared/core_stmt.cpp deleted file mode 100644 index 7f332169..00000000 --- a/source/pdo_sqlsrv/shared/core_stmt.cpp +++ /dev/null @@ -1,2471 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_stmt.cpp -// -// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" - -namespace { - -// certain drivers using this layer will call for repeated or out of order field retrievals. To allow this, we cache the -// results of every field request, and if it is out of order, we cache those for preceding fields. -struct field_cache { - - void* value; - SQLLEN len; - sqlsrv_phptype type; - - field_cache( void* field_value, SQLLEN field_len, sqlsrv_phptype t ) - : type( t ) - { - // if the value is NULL, then just record a NULL pointer - if( field_value != NULL ) { - value = sqlsrv_malloc( field_len ); - memcpy_s( value, field_len, field_value, field_len ); - len = field_len; - } - else { - value = NULL; - len = 0; - } - } - - // no destructor because we don't want to release the memory when it goes out of scope, but instead we - // rely on the hash table destructor to free the memory -}; - -const int INITIAL_FIELD_STRING_LEN = 256; // base allocation size when retrieving a string field - -// UTF-8 tags for byte length of characters, used by streams to make sure we don't clip a character in between reads -const unsigned int UTF8_MIDBYTE_MASK = 0xc0; -const unsigned int UTF8_MIDBYTE_TAG = 0x80; -const unsigned int UTF8_2BYTESEQ_TAG1 = 0xc0; -const unsigned int UTF8_2BYTESEQ_TAG2 = 0xd0; -const unsigned int UTF8_3BYTESEQ_TAG = 0xe0; -const unsigned int UTF8_4BYTESEQ_TAG = 0xf0; -const unsigned int UTF8_NBYTESEQ_MASK = 0xf0; - -// constants used to convert from a DateTime object to a string which is sent to the server. -// Using the format defined by the ODBC documentation at http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx -namespace DateTime { - -const char DATETIME_CLASS_NAME[] = "DateTime"; -const size_t DATETIME_CLASS_NAME_LEN = sizeof( DATETIME_CLASS_NAME ) - 1; -const char DATETIMEOFFSET_FORMAT[] = "Y-m-d H:i:s.u P"; -const size_t DATETIMEOFFSET_FORMAT_LEN = sizeof( DATETIMEOFFSET_FORMAT ); -const char DATETIME_FORMAT[] = "Y-m-d H:i:s.u"; -const size_t DATETIME_FORMAT_LEN = sizeof( DATETIME_FORMAT ); -const char DATE_FORMAT[] = "Y-m-d"; -const size_t DATE_FORMAT_LEN = sizeof( DATE_FORMAT ); - -} - -// *** internal functions *** -// Only declarations are put here. Functions contain the documentation they need at their definition sites. -void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC ); -size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_end TSRMLS_DC ); -bool check_for_next_stream_parameter( sqlsrv_stmt* stmt TSRMLS_DC ); -bool convert_input_param_to_utf16( zval* input_param_z, zval* convert_param_z ); -void core_get_field_common(_Inout_ sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype - sqlsrv_php_type, _Out_ void*& field_value, _Out_ SQLLEN* field_len TSRMLS_DC); -// returns the ODBC C type constant that matches the PHP type and encoding given -SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC ); -void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding, - _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ); -// given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate) -void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding, - _Out_ SQLSMALLINT& sql_type TSRMLS_DC ); -void field_cache_dtor( zval* data_z ); -void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC ); -void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type, - _Out_ void*& field_value, _Out_ SQLLEN* field_len TSRMLS_DC ); -stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC ); -bool is_valid_sqlsrv_phptype( sqlsrv_phptype type ); -// assure there is enough space for the output parameter string -void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding, - SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer, - SQLLEN& buffer_len TSRMLS_DC ); -void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC ); -// send all the stream data -void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC ); -// called when a bound output string parameter is to be destroyed -void sqlsrv_output_param_dtor( zval* data ); -// called when a bound stream parameter is to be destroyed. -void sqlsrv_stream_dtor( zval* data ); -bool is_streamable_type( SQLINTEGER sql_type ); - -} - -// constructor for sqlsrv_stmt. Here so that we can use functions declared earlier. -sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC ) : - sqlsrv_context( handle, SQL_HANDLE_STMT, e, drv, SQLSRV_ENCODING_DEFAULT ), - conn( c ), - executed( false ), - past_fetch_end( false ), - current_results( NULL ), - cursor_type( SQL_CURSOR_FORWARD_ONLY ), - has_rows( false ), - fetch_called( false ), - last_field_index( -1 ), - past_next_result_end( false ), - param_ind_ptrs( 10 ), // initially hold 10 elements, which should cover 90% of the cases and only take < 100 byte - send_streams_at_exec( true ), - current_stream( NULL, SQLSRV_ENCODING_DEFAULT ), - current_stream_read( 0 ), - query_timeout( QUERY_TIMEOUT_INVALID ), - buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ) -{ - ZVAL_UNDEF( &active_stream ); - // initialize the input string parameters array (which holds zvals) - core::sqlsrv_array_init( *conn, ¶m_input_strings TSRMLS_CC ); - - // initialize the (input only) stream parameters (which holds sqlsrv_stream structures) - ZVAL_NEW_ARR( ¶m_streams ); - core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( param_streams ), 5 /* # of buckets */, sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC); - - // initialize the (input only) datetime parameters of converted date time objects to strings - array_init( ¶m_datetime_buffers ); - - // initialize the output string parameters (which holds sqlsrv_output_param structures) - ZVAL_NEW_ARR( &output_params ); - core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( output_params ), 5 /* # of buckets */, sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC); - - // initialize the field cache - ZVAL_NEW_ARR( &field_cache ); - core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL(field_cache), 5 /* # of buckets */, field_cache_dtor, 0 /*persistent*/ TSRMLS_CC); -} - -// desctructor for sqlsrv statement. -sqlsrv_stmt::~sqlsrv_stmt( void ) -{ - if( Z_TYPE( active_stream ) != IS_UNDEF ) { - TSRMLS_FETCH(); - close_active_stream( this TSRMLS_CC ); - } - - // delete any current results - if( current_results ) { - current_results->~sqlsrv_result_set(); - efree( current_results ); - current_results = NULL; - } - - invalidate(); - zval_ptr_dtor( ¶m_input_strings ); - zval_ptr_dtor( &output_params ); - zval_ptr_dtor( ¶m_streams ); - zval_ptr_dtor( ¶m_datetime_buffers ); - zval_ptr_dtor( &field_cache ); -} - - -// centralized place to release (without destroying the hash tables -// themselves) all the parameter data that accrues during the -// execution phase. -void sqlsrv_stmt::free_param_data( TSRMLS_D ) -{ - SQLSRV_ASSERT(Z_TYPE( param_input_strings ) == IS_ARRAY && Z_TYPE( param_streams ) == IS_ARRAY, - "sqlsrv_stmt::free_param_data: Param zvals aren't arrays." ); - zend_hash_clean( Z_ARRVAL( param_input_strings )); - zend_hash_clean( Z_ARRVAL( output_params )); - zend_hash_clean( Z_ARRVAL( param_streams )); - zend_hash_clean( Z_ARRVAL( param_datetime_buffers )); - zend_hash_clean( Z_ARRVAL( field_cache )); -} - - -// to be called whenever a new result set is created, such as after an -// execute or next_result. Resets the state variables. - -void sqlsrv_stmt::new_result_set( TSRMLS_D ) -{ - this->fetch_called = false; - this->has_rows = false; - this->past_next_result_end = false; - this->past_fetch_end = false; - this->last_field_index = -1; - - // delete any current results - if( current_results ) { - current_results->~sqlsrv_result_set(); - efree( current_results ); - current_results = NULL; - } - - // create a new result set - if( cursor_type == SQLSRV_CURSOR_BUFFERED ) { - current_results = new (sqlsrv_malloc( sizeof( sqlsrv_buffered_result_set ))) sqlsrv_buffered_result_set( this TSRMLS_CC ); - } - else { - current_results = new (sqlsrv_malloc( sizeof( sqlsrv_odbc_result_set ))) sqlsrv_odbc_result_set( this ); - } -} - -// core_sqlsrv_create_stmt -// Common code to allocate a statement from either driver. Returns a valid driver statement object or -// throws an exception if an error occurs. -// Parameters: -// conn - The connection resource by which the client and server are connected. -// stmt_factory - factory method to create a statement. -// options_ht - A HashTable of user provided options to be set on the statement. -// valid_stmt_opts - An array of valid driver supported statement options. -// err - callback for error handling -// driver - reference to caller -// Return -// Returns the created statement - -sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stmt_factory, HashTable* options_ht, - const stmt_option valid_stmt_opts[], error_callback const err, void* driver TSRMLS_DC ) -{ - sqlsrv_malloc_auto_ptr stmt; - SQLHANDLE stmt_h = SQL_NULL_HANDLE; - - try { - - core::SQLAllocHandle( SQL_HANDLE_STMT, *conn, &stmt_h TSRMLS_CC ); - - stmt = stmt_factory( conn, stmt_h, err, driver TSRMLS_CC ); - - stmt->conn = conn; - - // handle has been set in the constructor of ss_sqlsrv_stmt, so we set it to NULL to prevent a double free - // in the catch block below. - stmt_h = SQL_NULL_HANDLE; - - // process the options array given to core_sqlsrv_prepare. - if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) { - zend_ulong index = -1; - zend_string *key = NULL; - zval* value_z = NULL; - - ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) { - - int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - - // The driver layer should ensure a valid key. - DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." ); - - const stmt_option* stmt_opt = get_stmt_option( stmt->conn, index, valid_stmt_opts TSRMLS_CC ); - - // if the key didn't match, then return the error to the script. - // The driver layer should ensure that the key is valid. - DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." ); - - // perform the actions the statement option needs done. - (*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC ); - } ZEND_HASH_FOREACH_END(); - } - - sqlsrv_stmt* return_stmt = stmt; - stmt.transferred(); - - return return_stmt; - } - catch( core::CoreException& ) - { - if( stmt ) { - - conn->set_last_error( stmt->last_error() ); - stmt->~sqlsrv_stmt(); - } - - // if allocating the handle failed before the statement was allocated, free the handle - if( stmt_h != SQL_NULL_HANDLE) { - ::SQLFreeHandle( SQL_HANDLE_STMT, stmt_h ); - } - - throw; - } - catch( ... ) { - - DIE( "core_sqlsrv_allocate_stmt: Unknown exception caught." ); - } -} - - -// core_sqlsrv_bind_param -// Binds a parameter using SQLBindParameter. It allocates memory and handles other details -// in translating between the driver and ODBC. -// Parameters: -// param_num - number of the parameter, 0 based -// param_z - zval of the parameter -// php_out_type - type to return for output parameter -// sql_type - ODBC constant for the SQL Server type (SQL_UNKNOWN_TYPE = 0 means not known, so infer defaults) -// column_size - length of the field on the server (SQLSRV_UKNOWN_SIZE means not known, so infer defaults) -// decimal_digits - if column_size is valid and the type contains a scale, this contains the scale -// Return: -// Nothing, though an exception is thrown if an error occurs -// The php type of the parameter is taken from the zval. -// The sql type is given as a hint if the driver provides it. - -void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLINT direction, zval* param_z, - SQLSRV_PHPTYPE php_out_type, SQLSRV_ENCODING encoding, SQLSMALLINT sql_type, SQLULEN column_size, - SQLSMALLINT decimal_digits TSRMLS_DC ) -{ - SQLSMALLINT c_type; - SQLPOINTER buffer = NULL; - SQLLEN buffer_len = 0; - - SQLSRV_ASSERT( direction == SQL_PARAM_INPUT || direction == SQL_PARAM_OUTPUT || direction == SQL_PARAM_INPUT_OUTPUT, - "core_sqlsrv_bind_param: Invalid parameter direction." ); - SQLSRV_ASSERT( direction == SQL_PARAM_INPUT || php_out_type != SQLSRV_PHPTYPE_INVALID, - "core_sqlsrv_bind_param: php_out_type not set before calling core_sqlsrv_bind_param." ); - - try { - - // check is only < because params are 0 based - CHECK_CUSTOM_ERROR( param_num >= SQL_SERVER_MAX_PARAMS, stmt, SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, param_num + 1 ) { - throw core::CoreException(); - } - - // resize the statements array of int_ptrs if the parameter isn't already set. - if( stmt->param_ind_ptrs.size() < static_cast(param_num + 1) ) { - stmt->param_ind_ptrs.resize( param_num + 1, SQL_NULL_DATA ); - } - SQLLEN& ind_ptr = stmt->param_ind_ptrs[ param_num ]; - - zval* param_ref = param_z; - if ( Z_ISREF_P( param_z ) ) { - ZVAL_DEREF( param_z ); - } - bool zval_was_null = ( Z_TYPE_P( param_z ) == IS_NULL ); - bool zval_was_bool = ( Z_TYPE_P( param_z ) == IS_TRUE || Z_TYPE_P( param_z ) == IS_FALSE ); - // if the user asks for for a specific type for input and output, make sure the data type we send matches the data we - // type we expect back, since we can only send and receive the same type. Anything can be converted to a string, so - // we always let that match if they want a string back. - if( direction == SQL_PARAM_INPUT_OUTPUT ) { - bool match = false; - switch( php_out_type ) { - case SQLSRV_PHPTYPE_INT: - if( zval_was_null || zval_was_bool ) { - convert_to_long( param_z ); - } - match = Z_TYPE_P( param_z ) == IS_LONG; - break; - case SQLSRV_PHPTYPE_FLOAT: - if( zval_was_null ) { - convert_to_double( param_z ); - } - match = Z_TYPE_P( param_z ) == IS_DOUBLE; - break; - case SQLSRV_PHPTYPE_STRING: - // anything can be converted to a string - convert_to_string( param_z ); - match = true; - break; - case SQLSRV_PHPTYPE_NULL: - case SQLSRV_PHPTYPE_DATETIME: - case SQLSRV_PHPTYPE_STREAM: - SQLSRV_ASSERT( false, "Invalid type for an output parameter." ); - break; - default: - SQLSRV_ASSERT( false, "Unknown SQLSRV_PHPTYPE_* constant given." ); - break; - } - CHECK_CUSTOM_ERROR( !match, stmt, SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, param_num + 1 ) { - throw core::CoreException(); - } - } - - // if it's an output parameter and the user asks for a certain type, we have to convert the zval to that type so - // when the buffer is filled, the type is correct - if( direction == SQL_PARAM_OUTPUT ) { - switch( php_out_type ) { - case SQLSRV_PHPTYPE_INT: - convert_to_long( param_z ); - break; - case SQLSRV_PHPTYPE_FLOAT: - convert_to_double( param_z ); - break; - case SQLSRV_PHPTYPE_STRING: - convert_to_string( param_z ); - break; - case SQLSRV_PHPTYPE_NULL: - case SQLSRV_PHPTYPE_DATETIME: - case SQLSRV_PHPTYPE_STREAM: - SQLSRV_ASSERT( false, "Invalid type for an output parameter" ); - break; - default: - SQLSRV_ASSERT( false, "Uknown SQLSRV_PHPTYPE_* constant given" ); - break; - } - } - - SQLSRV_ASSERT(( Z_TYPE_P( param_z ) != IS_STRING && Z_TYPE_P( param_z ) != IS_RESOURCE ) || - ( encoding == SQLSRV_ENCODING_SYSTEM || encoding == SQLSRV_ENCODING_UTF8 || - encoding == SQLSRV_ENCODING_BINARY ), "core_sqlsrv_bind_param: invalid encoding" ); - - // if the sql type is unknown, then set the default based on the PHP type passed in - if( sql_type == SQL_UNKNOWN_TYPE ) { - default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC ); - } - - // if the size is unknown, then set the default based on the PHP type passed in - if( column_size == SQLSRV_UNKNOWN_SIZE ) { - default_sql_size_and_scale( stmt, static_cast( param_num ), param_z, encoding, column_size, decimal_digits TSRMLS_CC ); - } - - // determine the ODBC C type - c_type = default_c_type( stmt, param_num, param_z, encoding TSRMLS_CC ); - - // set the buffer based on the PHP parameter type - switch( Z_TYPE_P( param_z )) { - - case IS_NULL: - { - SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." ); - ind_ptr = SQL_NULL_DATA; - buffer = NULL; - buffer_len = 0; - } - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - { - // if it is boolean, set the lval to 0 or 1 - convert_to_long( param_z ); - buffer = ¶m_z->value; - buffer_len = sizeof( Z_LVAL_P( param_z )); - ind_ptr = buffer_len; - if( direction != SQL_PARAM_INPUT ) { - // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned - sqlsrv_output_param output_param( param_ref, static_cast( param_num ), zval_was_bool ); - save_output_param_for_later( stmt, output_param TSRMLS_CC ); - } - } - break; - case IS_DOUBLE: - { - buffer = ¶m_z->value; - buffer_len = sizeof( Z_DVAL_P( param_z )); - ind_ptr = buffer_len; - if( direction != SQL_PARAM_INPUT ) { - // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned - sqlsrv_output_param output_param( param_ref, static_cast( param_num ), false ); - save_output_param_for_later( stmt, output_param TSRMLS_CC ); - } - } - break; - case IS_STRING: - buffer = Z_STRVAL_P( param_z ); - buffer_len = Z_STRLEN_P( param_z ); - // if the encoding is UTF-8, translate from UTF-8 to UTF-16 (the type variables should have already been adjusted) - if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ) { - - zval wbuffer_z; - ZVAL_NULL( &wbuffer_z ); - - bool converted = convert_input_param_to_utf16( param_z, &wbuffer_z ); - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, - param_num + 1, get_last_error_message() ) { - throw core::CoreException(); - } - buffer = Z_STRVAL_P( &wbuffer_z ); - buffer_len = Z_STRLEN_P( &wbuffer_z ); - core::sqlsrv_add_index_zval(*stmt, &(stmt->param_input_strings), param_num, &wbuffer_z TSRMLS_CC); - } - ind_ptr = buffer_len; - if( direction != SQL_PARAM_INPUT ) { - // PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion, - // we reallocate the string if it's interned - if ( ZSTR_IS_INTERNED( Z_STR_P( param_z ))) { - core::sqlsrv_zval_stringl( param_z, static_cast(buffer), buffer_len ); - buffer = Z_STRVAL_P( param_z ); - buffer_len = Z_STRLEN_P( param_z ); - } - - // if it's a UTF-8 input output parameter (signified by the C type being SQL_C_WCHAR) - // or if the PHP type is a binary encoded string with a N(VAR)CHAR/NTEXTSQL type, - // convert it to wchar first - if( direction == SQL_PARAM_INPUT_OUTPUT && - ( c_type == SQL_C_WCHAR || - ( c_type == SQL_C_BINARY && - ( sql_type == SQL_WCHAR || - sql_type == SQL_WVARCHAR || - sql_type == SQL_WLONGVARCHAR )))) { - - bool converted = convert_input_param_to_utf16( param_z, param_z ); - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, - param_num + 1, get_last_error_message() ) { - throw core::CoreException(); - } - buffer = Z_STRVAL_P( param_z ); - buffer_len = Z_STRLEN_P( param_z ); - ind_ptr = buffer_len; - } - - // since this is an output string, assure there is enough space to hold the requested size and - // set all the variables necessary (param_z, buffer, buffer_len, and ind_ptr) - resize_output_buffer_if_necessary( stmt, param_z, param_num, encoding, c_type, sql_type, column_size, - buffer, buffer_len TSRMLS_CC ); - - // save the parameter to be adjusted and/or converted after the results are processed - sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast( buffer_len )); - - save_output_param_for_later( stmt, output_param TSRMLS_CC ); - - // For output parameters, if we set the column_size to be same as the buffer_len, - // then if there is a truncation due to the data coming from the server being - // greater than the column_size, we don't get any truncation error. In order to - // avoid this silent truncation, we set the column_size to be "MAX" size for - // string types. This will guarantee that there is no silent truncation for - // output parameters. - if( direction == SQL_PARAM_OUTPUT ) { - - switch( sql_type ) { - - case SQL_VARBINARY: - case SQL_VARCHAR: - case SQL_WVARCHAR: - column_size = SQL_SS_LENGTH_UNLIMITED; - break; - - default: - break; - } - } - } - break; - case IS_RESOURCE: - { - SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." ); - sqlsrv_stream stream_encoding( param_z, encoding ); - HashTable* streams_ht = Z_ARRVAL( stmt->param_streams ); - core::sqlsrv_zend_hash_index_update_mem( *stmt, streams_ht, param_num, &stream_encoding, sizeof(stream_encoding) TSRMLS_CC ); - buffer = reinterpret_cast( param_num ); - Z_TRY_ADDREF_P( param_z ); // so that it doesn't go away while we're using it - buffer_len = 0; - ind_ptr = SQL_DATA_AT_EXEC; - } - break; - case IS_OBJECT: - { - SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." ); - zval function_z; - zval buffer_z; - zval format_z; - zval params[1]; - ZVAL_UNDEF( &function_z ); - ZVAL_UNDEF( &buffer_z ); - ZVAL_UNDEF( &format_z ); - ZVAL_UNDEF( params ); - - bool valid_class_name_found = false; - - zend_class_entry *class_entry = Z_OBJCE_P( param_z TSRMLS_CC ); - - while( class_entry != NULL ) { - - if( class_entry->name->len == DateTime::DATETIME_CLASS_NAME_LEN && class_entry->name != NULL && - stricmp( class_entry->name->val, DateTime::DATETIME_CLASS_NAME ) == 0 ) { - valid_class_name_found = true; - break; - } - - else { - - // Check the parent - class_entry = class_entry->parent; - } - } - - CHECK_CUSTOM_ERROR( !valid_class_name_found, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) { - throw core::CoreException(); - } - - // if the user specifies the 'date' sql type, giving it the normal format will cause a 'date overflow error' - // meaning there is too much information in the character string. If the user specifies the 'datetimeoffset' - // sql type, it lacks the timezone. - if( sql_type == SQL_SS_TIMESTAMPOFFSET ) { - core::sqlsrv_zval_stringl( &format_z, const_cast( DateTime::DATETIMEOFFSET_FORMAT ), - DateTime::DATETIMEOFFSET_FORMAT_LEN ); - } - else if( sql_type == SQL_TYPE_DATE ) { - core::sqlsrv_zval_stringl( &format_z, const_cast( DateTime::DATE_FORMAT ), DateTime::DATE_FORMAT_LEN ); - } - else { - core::sqlsrv_zval_stringl( &format_z, const_cast( DateTime::DATETIME_FORMAT ), DateTime::DATETIME_FORMAT_LEN ); - } - // call the DateTime::format member function to convert the object to a string that SQL Server understands - core::sqlsrv_zval_stringl( &function_z, "format", sizeof( "format" ) - 1 ); - params[0] = format_z; - // This is equivalent to the PHP code: $param_z->format( $format_z ); where param_z is the - // DateTime object and $format_z is the format string. - int zr = call_user_function( EG( function_table ), param_z, &function_z, &buffer_z, 1, params TSRMLS_CC ); - zend_string_release( Z_STR( format_z )); - zend_string_release( Z_STR( function_z )); - CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) { - throw core::CoreException(); - } - buffer = Z_STRVAL( buffer_z ); - zr = add_next_index_zval( &( stmt->param_datetime_buffers ), &buffer_z ); - CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) { - throw core::CoreException(); - } - buffer_len = Z_STRLEN( buffer_z ) - 1; - ind_ptr = buffer_len; - break; - } - case IS_ARRAY: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ); - break; - default: - DIE( "core_sqlsrv_bind_param: Unsupported PHP type. Only string, float, int, and streams (resource) are supported. " - "It is the responsibilty of the driver layer to convert a parameter to one of these types." ); - break; - } - - if( zval_was_null ) { - ind_ptr = SQL_NULL_DATA; - } - - core::SQLBindParameter( stmt, param_num + 1, direction, - c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr TSRMLS_CC ); - } - catch( core::CoreException& e ) { - stmt->free_param_data( TSRMLS_C ); - SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS ); - throw e; - } -} - - -// core_sqlsrv_execute -// Executes the statement previously prepared -// Parameters: -// stmt - the core sqlsrv_stmt structure that contains the ODBC handle -// Return: -// true if there is data, false if there is not - -void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_len ) -{ - SQLRETURN r; - - try { - - // close the stream to release the resource - close_active_stream( stmt TSRMLS_CC ); - - if( sql ) { - - sqlsrv_malloc_auto_ptr wsql_string; - unsigned int wsql_len = 0; - if( sql_len == 0 || ( sql[0] == '\0' && sql_len == 1 )) { - wsql_string = reinterpret_cast( sqlsrv_malloc( sizeof( wchar_t ))); - wsql_string[0] = L'\0'; - wsql_len = 0; - } - else { - SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : - stmt->encoding() ); - wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast( sql ), - sql_len, &wsql_len ); - CHECK_CUSTOM_ERROR( wsql_string == NULL, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE, - get_last_error_message() ) { - throw core::CoreException(); - } - } - r = core::SQLExecDirectW( stmt, wsql_string TSRMLS_CC ); - } - else { - r = core::SQLExecute( stmt TSRMLS_CC ); - } - - // if data is needed (streams were bound) and they should be sent at execute time, then do so now - if( r == SQL_NEED_DATA && stmt->send_streams_at_exec ) { - - send_param_streams( stmt TSRMLS_CC ); - } - - stmt->new_result_set( TSRMLS_C ); - stmt->executed = true; - - // if all the data has been sent and no data was returned then finalize the output parameters - if( stmt->send_streams_at_exec && ( r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) { - - finalize_output_parameters( stmt TSRMLS_CC ); - } - // stream parameters are sent, clean the Hashtable - if ( stmt->send_streams_at_exec ) { - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - } - } - catch( core::CoreException& e ) { - - // if the statement executed but failed in a subsequent operation before returning, - // we need to cancel the statement and deref the output and stream parameters - if ( stmt->send_streams_at_exec ) { - zend_hash_clean( Z_ARRVAL( stmt->output_params )); - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - } - if( stmt->executed ) { - SQLCancel( stmt->handle() ); - // stmt->executed = false; should this be reset if something fails? - } - - throw e; - } -} - - -// core_sqlsrv_fetch -// Moves the cursor according to the parameters (by default, moves to the next row) -// Parameters: -// stmt - the sqlsrv_stmt of the cursor -// fetch_orientation - method to move the cursor -// fetch_offset - if the method has a parameter (such as number of rows to move or literal row number) -// Returns: -// Nothing, exception thrown if an error. stmt->past_fetch_end is set to true if the -// user scrolls past a non-scrollable result set - -bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC ) -{ - // pre-condition check - SQLSRV_ASSERT( fetch_orientation >= SQL_FETCH_NEXT || fetch_orientation <= SQL_FETCH_RELATIVE, - "core_sqlsrv_fetch: Invalid value provided for fetch_orientation parameter." ); - - try { - - // clear the field cache of the previous fetch - zend_hash_clean( Z_ARRVAL( stmt->field_cache )); - - CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR( stmt->past_fetch_end, stmt, SQLSRV_ERROR_FETCH_PAST_END ) { - throw core::CoreException(); - } - - SQLSMALLINT has_fields = core::SQLNumResultCols( stmt TSRMLS_CC ); - - CHECK_CUSTOM_ERROR( has_fields == 0, stmt, SQLSRV_ERROR_NO_FIELDS ) { - throw core::CoreException(); - } - - // close the stream to release the resource - close_active_stream( stmt TSRMLS_CC ); - - // if the statement has rows and is not scrollable but doesn't yet have - // fetch_called, this must be the first time we've called sqlsrv_fetch. - if( stmt->cursor_type == SQL_CURSOR_FORWARD_ONLY && stmt->has_rows && !stmt->fetch_called ) { - stmt->fetch_called = true; - return true; - } - - // move to the record requested. For absolute records, we use a 0 based offset, so +1 since - // SQLFetchScroll uses a 1 based offset, otherwise for relative, just use the fetch_offset provided. - SQLRETURN r = stmt->current_results->fetch( fetch_orientation, - ( fetch_orientation == SQL_FETCH_RELATIVE ) ? fetch_offset : fetch_offset + 1 - TSRMLS_CC ); - if( r == SQL_NO_DATA ) { - // if this is a forward only cursor, mark that we've passed the end so future calls result in an error - if( stmt->cursor_type == SQL_CURSOR_FORWARD_ONLY ) { - stmt->past_fetch_end = true; - } - return false; - } - - // mark that we called fetch (which get_field, et. al. uses) and reset our last field retrieved - stmt->fetch_called = true; - stmt->last_field_index = -1; - stmt->has_rows = true; // since we made it this far, we must have at least one row - } - catch (core::CoreException& e) { - throw e; - } - catch ( ... ) { - DIE( "core_sqlsrv_fetch: Unexpected exception occurred." ); - } - - return true; -} - - -// Retrieves metadata for a field of a prepared statement. -// Parameters: -// colno - the index of the field for which to return the metadata. columns are 0 based in PDO -// Return: -// A field_meta_data* consisting of the field metadata. - -field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT colno TSRMLS_DC ) -{ - // pre-condition check - SQLSRV_ASSERT( colno >= 0, "core_sqlsrv_field_metadata: Invalid column number provided." ); - - sqlsrv_malloc_auto_ptr meta_data; - SQLSMALLINT field_name_len = 0; - - meta_data = new ( sqlsrv_malloc( sizeof( field_meta_data ))) field_meta_data(); - meta_data->field_name = static_cast( sqlsrv_malloc( SS_MAXCOLNAMELEN + 1 )); - - try { - core::SQLDescribeCol( stmt, colno + 1, meta_data->field_name.get(), SS_MAXCOLNAMELEN, &field_name_len, - &(meta_data->field_type), &(meta_data->field_size), &(meta_data->field_scale), - &(meta_data->field_is_nullable) TSRMLS_CC ); - } - catch( core::CoreException& e ) { - throw e; - } - - // depending on field type, we add the values into size or precision/scale. - switch( meta_data->field_type ) { - case SQL_DECIMAL: - case SQL_NUMERIC: - case SQL_TYPE_TIMESTAMP: - case SQL_TYPE_DATE: - case SQL_SS_TIME2: - case SQL_SS_TIMESTAMPOFFSET: - case SQL_BIT: - case SQL_TINYINT: - case SQL_SMALLINT: - case SQL_INTEGER: - case SQL_BIGINT: - case SQL_REAL: - case SQL_FLOAT: - case SQL_DOUBLE: - { - meta_data->field_precision = meta_data->field_size; - meta_data->field_size = 0; - break; - } - default: { - break; - } - } - - // Set the field name lenth - meta_data->field_name_len = field_name_len; - - field_meta_data* result_field_meta_data = meta_data; - meta_data.transferred(); - return result_field_meta_data; -} - - -// core_sqlsrv_get_field -// Return the value of a column from ODBC -// Parameters: -// stmt - the sqlsrv_stmt from which to retrieve the column -// field_index - 0 based index for the column to retrieve -// sqlsrv_php_type_in - sqlsrv_php_type structure that tells what format to return the data in -// field_value - pointer to the data retrieved -// field_len - length of the data in the field_value buffer -// Returns: -// Nothing, excpetion thrown if an error occurs - -void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type_in, bool prefer_string, - _Out_ void*& field_value, _Out_ SQLLEN* field_len, bool cache_field, - _Out_ SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC) -{ - try { - - // close the stream to release the resource - close_active_stream(stmt TSRMLS_CC); - - // if the field has been retrieved before, return the previous result - field_cache* cached = NULL; - if (NULL != ( cached = static_cast( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast( field_index ))))) { - // the field value is NULL - if( cached->value == NULL ) { - field_value = NULL; - *field_len = 0; - if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = SQLSRV_PHPTYPE_NULL; } - } - else { - - field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 ); - memcpy_s( field_value, ( cached->len * sizeof( char )), cached->value, cached->len ); - if( cached->type.typeinfo.type == SQLSRV_PHPTYPE_STRING) { - // prevent the 'string not null terminated' warning - reinterpret_cast( field_value )[ cached->len ] = '\0'; - } - *field_len = cached->len; - if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast(cached->type.typeinfo.type); } - } - return; - } - - sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in; - - SQLLEN sql_field_type = 0; - SQLLEN sql_field_len = 0; - - // Make sure that the statement was executed and not just prepared. - CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { - throw core::CoreException(); - } - - // if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they - // may also be retrieved. - if( cache_field && (field_index - stmt->last_field_index ) >= 2 ) { - sqlsrv_phptype invalid; - invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID; - for( int i = stmt->last_field_index + 1; i < field_index; ++i ) { - SQLSRV_ASSERT((cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), i))) == NULL, - "Field already cached." ); - core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field, - sqlsrv_php_type_out TSRMLS_CC ); - // delete the value returned since we only want it cached, not the actual value - if( field_value ) { - efree( field_value ); - field_value = NULL; - *field_len = 0; - } - } - } - - // If the php type was not specified set the php type to be the default type. - if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { - - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - - // Get the length of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); - - // Get the corresponding php type from the sql type. - sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); - } - - // Verify that we have an acceptable type to convert. - CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) { - throw core::CoreException(); - } - - if( sqlsrv_php_type_out != NULL ) - *sqlsrv_php_type_out = static_cast( sqlsrv_php_type.typeinfo.type ); - - // Retrieve the data - core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - - // if the user wants us to cache the field, we'll do it - if( cache_field ) { - field_cache cache( field_value, *field_len, sqlsrv_php_type ); - core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC ); - } - } - - catch( core::CoreException& e ) { - throw e; - } -} - -// core_sqlsrv_has_any_result -// return if any result set or rows affected message is waiting -// to be consumed and moved over by sqlsrv_next_result. -// Parameters: -// stmt - The statement object on which to check for results. -// Return: -// true if any results are present, false otherwise. - -bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC ) -{ - // Use SQLNumResultCols to determine if we have rows or not. - SQLSMALLINT num_cols = core::SQLNumResultCols( stmt TSRMLS_CC ); - // use SQLRowCount to determine if there is a rows status waiting - SQLLEN rows_affected = core::SQLRowCount( stmt TSRMLS_CC ); - return (num_cols != 0) || (rows_affected > 0); -} - -// core_sqlsrv_next_result -// Advances to the next result set from the last executed query -// Parameters -// stmt - the sqlsrv_stmt structure -// Returns -// Nothing, exception thrown if problem occurs - -void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params, bool throw_on_errors ) -{ - try { - - // make sure that the statement has been executed. - CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR( stmt->past_next_result_end, stmt, SQLSRV_ERROR_NEXT_RESULT_PAST_END ) { - throw core::CoreException(); - } - - close_active_stream( stmt TSRMLS_CC ); - - SQLRETURN r; - if( throw_on_errors ) { - r = core::SQLMoreResults( stmt TSRMLS_CC ); - } - else { - r = SQLMoreResults( stmt->handle() ); - } - - if( r == SQL_NO_DATA ) { - - if( &(stmt->output_params) && finalize_output_params ) { - // if we're finished processing result sets, handle the output parameters - finalize_output_parameters( stmt TSRMLS_CC ); - } - - // mark we are past the end of all results - stmt->past_next_result_end = true; - return; - } - - stmt->new_result_set( TSRMLS_C ); - } - catch( core::CoreException& e ) { - - SQLCancel( stmt->handle() ); - throw e; - } -} - - -// core_sqlsrv_post_param -// Performs any actions post execution for each parameter. For now it cleans up input parameters memory from the statement -// Parameters: -// stmt - the sqlsrv_stmt structure -// param_num - 0 based index of the parameter -// param_z - parameter value itself. -// Returns: -// Nothing, exception thrown if problem occurs - -void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong param_num, zval* param_z TSRMLS_DC ) -{ - SQLSRV_ASSERT( Z_TYPE( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." ); - SQLSRV_ASSERT( Z_TYPE( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." ); - - // if the parameter was an input string, delete it from the array holding input parameter strings - if( zend_hash_index_exists( Z_ARRVAL( stmt->param_input_strings ), param_num )) { - core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_input_strings ), param_num TSRMLS_CC ); - } - - // if the parameter was an input stream, decrement our reference to it and delete it from the array holding input streams - // PDO doesn't need the reference count, but sqlsrv does since the stream can be live after sqlsrv_execute by sending it - // with sqlsrv_send_stream_data. - if( zend_hash_index_exists( Z_ARRVAL( stmt->param_streams ), param_num )) { - sqlsrv_stream* stream_encoding = NULL; - stream_encoding = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->param_streams), param_num)); - core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_streams ), param_num TSRMLS_CC ); - } -} - -//Calls SQLSetStmtAttr to set a cursor. -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC ) -{ - try { - - switch( cursor_type ) { - - case SQL_CURSOR_STATIC: - core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE, - reinterpret_cast( SQL_CURSOR_STATIC ), SQL_IS_UINTEGER TSRMLS_CC ); - break; - - case SQL_CURSOR_DYNAMIC: - core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE, - reinterpret_cast( SQL_CURSOR_DYNAMIC ), SQL_IS_UINTEGER TSRMLS_CC ); - break; - - case SQL_CURSOR_KEYSET_DRIVEN: - core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE, - reinterpret_cast( SQL_CURSOR_KEYSET_DRIVEN ), SQL_IS_UINTEGER TSRMLS_CC ); - break; - - case SQL_CURSOR_FORWARD_ONLY: - core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE, - reinterpret_cast( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER TSRMLS_CC ); - break; - - case SQLSRV_CURSOR_BUFFERED: - core::SQLSetStmtAttr( stmt, SQL_ATTR_CURSOR_TYPE, - reinterpret_cast( SQL_CURSOR_FORWARD_ONLY ), SQL_IS_UINTEGER TSRMLS_CC ); - break; - - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE ); - break; - } - - stmt->cursor_type = cursor_type; - - } - catch( core::CoreException& ) { - throw; - } -} - -void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ) -{ - if( Z_TYPE_P( value_z ) != IS_LONG ) { - - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_BUFFER_LIMIT ); - } - - core_sqlsrv_set_buffered_query_limit( stmt, Z_LVAL_P( value_z ) TSRMLS_CC ); -} - -void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC ) -{ - if( limit <= 0 ) { - - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_BUFFER_LIMIT ); - } - - stmt->buffered_query_limit = limit; -} - - -// Overloaded. Extracts the long value and calls the core_sqlsrv_set_query_timeout -// which accepts timeout parameter as a long. If the zval is not of type long -// than throws error. -void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ) -{ - try { - - // validate the value - if( Z_TYPE_P( value_z ) != IS_LONG || Z_LVAL_P( value_z ) < 0 ) { - - convert_to_string( value_z ); - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( value_z ) ); - } - - core_sqlsrv_set_query_timeout( stmt, static_cast( Z_LVAL_P( value_z )) TSRMLS_CC ); - } - catch( core::CoreException& ) { - throw; - } -} - -// Overloaded. Accepts the timeout as a long. -void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, long timeout TSRMLS_DC ) -{ - try { - - DEBUG_SQLSRV_ASSERT( timeout >= 0 , "core_sqlsrv_set_query_timeout: The value of query timeout cannot be less than 0." ); - - // set the statement attribute - core::SQLSetStmtAttr( stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast( (SQLLEN)timeout ), SQL_IS_UINTEGER TSRMLS_CC ); - - // a query timeout of 0 indicates "no timeout", which means that lock_timeout should also be set to "no timeout" which - // is represented by -1. - long lock_timeout = (( timeout == 0 ) ? -1 : timeout * 1000 /*convert to milliseconds*/ ); - - // set the LOCK_TIMEOUT on the server. - char lock_timeout_sql[ 32 ]; - int written = sprintf_s( lock_timeout_sql, sizeof( lock_timeout_sql ), "SET LOCK_TIMEOUT %d", - lock_timeout ); - - SQLSRV_ASSERT( (written != -1 && written != sizeof( lock_timeout_sql )), - "stmt_option_query_timeout: sprintf_s failed. Shouldn't ever fail." ); - - core::SQLExecDirect( stmt, lock_timeout_sql TSRMLS_CC ); - - stmt->query_timeout = timeout; - } - catch( core::CoreException& ) { - throw; - } -} - -void core_sqlsrv_set_send_at_exec( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ) -{ - TSRMLS_C; - - // zend_is_true does not fail. It either returns true or false. - stmt->send_streams_at_exec = ( zend_is_true( value_z )) ? true : false; -} - - -// core_sqlsrv_send_stream_packet -// send a single packet from a stream parameter to the database using -// ODBC. This will also handle the transition between parameters. It -// returns true if it is not done sending, false if it is finished. -// return_value is what should be returned to the script if it is -// given. Any errors that occur are posted here. -// Parameters: -// stmt - query to send the next packet for -// Returns: -// true if more data remains to be sent, false if all data processed - -bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC ) -{ - SQLRETURN r = SQL_SUCCESS; - - // if there no current parameter to process, get the next one - // (probably because this is the first call to sqlsrv_send_stream_data) - if( stmt->current_stream.stream_z == NULL ) { - - if( check_for_next_stream_parameter( stmt TSRMLS_CC ) == false ) { - - stmt->current_stream = sqlsrv_stream( NULL, SQLSRV_ENCODING_CHAR ); - stmt->current_stream_read = 0; - return false; - } - } - - try { - - // get the stream from the zval we bound - php_stream* param_stream = NULL; - core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, stmt->current_stream.stream_z TSRMLS_CC ); - - // if we're at the end, then release our current parameter - if( php_stream_eof( param_stream )) { - // if no data was actually sent prior, then send a NULL - if( stmt->current_stream_read == 0 ) { - // send an empty string, which is what a 0 length does. - char buff[1]; // temp storage to hand to SQLPutData - core::SQLPutData( stmt, buff, 0 TSRMLS_CC ); - } - stmt->current_stream = sqlsrv_stream( NULL, SQLSRV_ENCODING_CHAR ); - stmt->current_stream_read = 0; - } - // read the data from the stream, send it via SQLPutData and track how much we've sent. - else { - char buffer[ PHP_STREAM_BUFFER_SIZE + 1 ]; - std::size_t buffer_size = sizeof( buffer ) - 3; // -3 to preserve enough space for a cut off UTF-8 character - std::size_t read = php_stream_read( param_stream, buffer, buffer_size ); - - if (read > UINT_MAX) - { - LOG(SEV_ERROR, "PHP stream: buffer length exceeded."); - throw core::CoreException(); - } - - stmt->current_stream_read += static_cast( read ); - if( read > 0 ) { - // if this is a UTF-8 stream, then we will use the UTF-8 encoding to determine if we're in the middle of a character - // then read in the appropriate number more bytes and then retest the string. This way we try at most to convert it - // twice. - // If we support other encondings in the future, we'll simply need to read a single byte and then retry the conversion - // since all other MBCS supported by SQL Server are 2 byte maximum size. - if( stmt->current_stream.encoding == CP_UTF8 ) { - - // the size of wbuffer is set for the worst case of UTF-8 to UTF-16 conversion, which is a - // expansion of 2x the UTF-8 size. - wchar_t wbuffer[ PHP_STREAM_BUFFER_SIZE + 1 ]; - // buffer_size is the # of wchars. Since it set to stmt->param_buffer_size / 2, this is accurate - int wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS, - buffer, static_cast( read ), wbuffer, static_cast( sizeof( wbuffer ) / sizeof( wchar_t ))); - if( wsize == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION ) { - - // this will calculate how many bytes were cut off from the last UTF-8 character and read that many more - // in, then reattempt the conversion. If it fails the second time, then an error is returned. - size_t need_to_read = calc_utf8_missing( stmt, buffer, read TSRMLS_CC ); - // read the missing bytes - size_t new_read = php_stream_read( param_stream, static_cast( buffer ) + read, - need_to_read ); - // if the bytes couldn't be read, then we return an error - CHECK_CUSTOM_ERROR( new_read != need_to_read, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, - get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )) { - throw core::CoreException(); - } - // try the conversion again with the complete character - wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS, - buffer, static_cast( read + new_read ), wbuffer, static_cast( sizeof( wbuffer ) / sizeof( wchar_t ))); - // something else must be wrong if it failed - CHECK_CUSTOM_ERROR( wsize == 0, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, - get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )) { - throw core::CoreException(); - } - } - core::SQLPutData( stmt, wbuffer, wsize * sizeof( wchar_t ) TSRMLS_CC ); - } - else { - core::SQLPutData( stmt, buffer, read TSRMLS_CC ); - } - } - } - - } - catch( core::CoreException& e ) { - stmt->free_param_data( TSRMLS_C ); - SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS ); - SQLCancel( stmt->handle() ); - stmt->current_stream = sqlsrv_stream( NULL, SQLSRV_ENCODING_DEFAULT ); - stmt->current_stream_read = 0; - throw e; - } - - return true; -} - -void stmt_option_functor::operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC ) -{ - TSRMLS_C; - - // This implementation should never get called. - DIE( "Not implemented." ); -} - -void stmt_option_query_timeout:: operator()( sqlsrv_stmt* stmt, stmt_option const* /**/, zval* value_z TSRMLS_DC ) -{ - core_sqlsrv_set_query_timeout( stmt, value_z TSRMLS_CC ); -} - -void stmt_option_send_at_exec:: operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ) -{ - core_sqlsrv_set_send_at_exec( stmt, value_z TSRMLS_CC ); -} - -void stmt_option_buffered_query_limit:: operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC ) -{ - core_sqlsrv_set_buffered_query_limit( stmt, value_z TSRMLS_CC ); -} - - -// internal function to release the active stream. Called by each main API function -// that will alter the statement and cancel any retrieval of data from a stream. -void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ) -{ - // if there is no active stream, return - if( Z_TYPE( stmt->active_stream ) == IS_UNDEF ) { - return; - } - - php_stream* stream = NULL; - - // we use no verify since verify would return immediately and we want to assert, not return. - php_stream_from_zval_no_verify( stream, &( stmt->active_stream )); - - SQLSRV_ASSERT(( stream != NULL ), "close_active_stream: Unknown resource type as our active stream." ); - - php_stream_close( stream ); // this will NULL out the active stream in the statement. We don't check for errors here. - - SQLSRV_ASSERT( Z_TYPE( stmt->active_stream ) == IS_UNDEF, "close_active_stream: Active stream not closed." ); - -} - -// local routines not shared by other files (arranged alphabetically) - -namespace { - -bool is_streamable_type( SQLLEN sql_type ) -{ - switch( sql_type ) { - case SQL_CHAR: - case SQL_WCHAR: - case SQL_BINARY: - case SQL_VARBINARY: - case SQL_VARCHAR: - case SQL_WVARCHAR: - case SQL_SS_XML: - case SQL_LONGVARBINARY: - case SQL_LONGVARCHAR: - case SQL_WLONGVARCHAR: - return true; - } - - return false; -} - -void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC ) -{ - try { - - switch( sql_type ) { - // for types that are fixed in size or for which the size is unknown, return the display size. - case SQL_BIGINT: - case SQL_BIT: - case SQL_INTEGER: - case SQL_SMALLINT: - case SQL_TINYINT: - case SQL_GUID: - case SQL_FLOAT: - case SQL_DOUBLE: - case SQL_REAL: - case SQL_DECIMAL: - case SQL_NUMERIC: - case SQL_TYPE_TIMESTAMP: - case SQL_LONGVARBINARY: - case SQL_LONGVARCHAR: - case SQL_BINARY: - case SQL_CHAR: - case SQL_VARBINARY: - case SQL_VARCHAR: - case SQL_SS_XML: - case SQL_SS_UDT: - case SQL_WLONGVARCHAR: - case SQL_DATETIME: - case SQL_TYPE_DATE: - case SQL_SS_TIME2: - case SQL_SS_TIMESTAMPOFFSET: - { - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &size TSRMLS_CC ); - break; - } - - // for wide char types for which the size is known, return the octet length instead, since it will include the - // the number of bytes necessary for the string, not just the characters - case SQL_WCHAR: - case SQL_WVARCHAR: - { - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &size TSRMLS_CC ); - break; - } - - default: - DIE ( "Unexpected SQL type encountered in calc_string_size." ); - } - } - catch( core::CoreException& e ) { - throw e; - } -} - - -// calculates how many characters were cut off from the end of a buffer when reading -// in UTF-8 encoded text - -size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_end TSRMLS_DC ) -{ - const char* last_char = buffer + buffer_end - 1; - size_t need_to_read = 0; - - // rewind until we are at the byte that starts the cut off character - while( (*last_char & UTF8_MIDBYTE_MASK ) == UTF8_MIDBYTE_TAG ) { - --last_char; - ++need_to_read; - } - - // determine how many bytes we need to read in based on the number of bytes in the character - // (# of high bits set) versus the number of bytes we've already read. - switch( *last_char & UTF8_NBYTESEQ_MASK ) { - case UTF8_2BYTESEQ_TAG1: - case UTF8_2BYTESEQ_TAG2: - need_to_read = 1 - need_to_read; - break; - case UTF8_3BYTESEQ_TAG: - need_to_read = 2 - need_to_read; - break; - case UTF8_4BYTESEQ_TAG: - need_to_read = 3 - need_to_read; - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE, - get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )); - break; - } - - return need_to_read; -} - - -// Caller is responsible for freeing the memory allocated for the field_value. -// The memory allocation has to happen in the core layer because otherwise -// the driver layer would have to calculate size of the field_value -// to decide the amount of memory allocation. -void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype - sqlsrv_php_type, _Out_ void*& field_value, _Out_ SQLLEN* field_len TSRMLS_DC ) -{ - try { - - close_active_stream( stmt TSRMLS_CC ); - - // make sure that fetch is called before trying to retrieve. - CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) { - throw core::CoreException(); - } - - // make sure that fields are not retrieved incorrectly. - CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, - stmt->last_field_index ) { - throw core::CoreException(); - } - - switch( sqlsrv_php_type.typeinfo.type ) { - - case SQLSRV_PHPTYPE_INT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( long ))); - - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( long ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - if( *field_len == SQL_NULL_DATA ) { - field_value = NULL; - break; - } - - field_value = field_value_temp; - field_value_temp.transferred(); - break; - } - - case SQLSRV_PHPTYPE_FLOAT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( double ))); - - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - if( *field_len == SQL_NULL_DATA ) { - field_value = NULL; - break; - } - - field_value = field_value_temp; - field_value_temp.transferred(); - break; - } - - case SQLSRV_PHPTYPE_STRING: - { - get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - break; - } - - // get the date as a string (http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx) and - // convert it to a DateTime object and return the created object - case SQLSRV_PHPTYPE_DATETIME: - { - char field_value_temp[ MAX_DATETIME_STRING_LEN ]; - zval params[1]; - zval field_value_temp_z; - zval function_z; - - ZVAL_UNDEF( &field_value_temp_z ); - ZVAL_UNDEF( &function_z ); - ZVAL_UNDEF( params ); - - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_CHAR, field_value_temp, - MAX_DATETIME_STRING_LEN, field_len, true TSRMLS_CC ); - - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - zval_auto_ptr return_value_z; - return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval )); - ZVAL_UNDEF( return_value_z ); - - if( *field_len == SQL_NULL_DATA ) { - ZVAL_NULL( return_value_z ); - field_value = reinterpret_cast( return_value_z.get()); - return_value_z.transferred(); - break; - } - - // Convert the string date to a DateTime object - core::sqlsrv_zval_stringl( &field_value_temp_z, field_value_temp, *field_len ); - core::sqlsrv_zval_stringl( &function_z, "date_create", sizeof("date_create") - 1 ); - params[0] = field_value_temp_z; - - if( call_user_function( EG( function_table ), NULL, &function_z, return_value_z, 1, - params TSRMLS_CC ) == FAILURE) { - THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED); - } - - field_value = reinterpret_cast( return_value_z.get()); - return_value_z.transferred(); - zend_string_free( Z_STR( field_value_temp_z )); - zend_string_free( Z_STR( function_z )); - break; - } - - // create a stream wrapper around the field and return that object to the PHP script. calls to fread - // on the stream will result in calls to SQLGetData. This is handled in stream.cpp. See that file - // for how these fields are used. - case SQLSRV_PHPTYPE_STREAM: - { - - php_stream* stream = NULL; - sqlsrv_stream* ss = NULL; - SQLLEN sql_type; - - SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) { - throw core::CoreException(); - } - - stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); - - CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { - throw core::CoreException(); - } - - ss = static_cast( stream->abstract ); - ss->stmt = stmt; - ss->field_index = field_index; - ss->sql_type = static_cast( sql_type ); - ss->encoding = static_cast( sqlsrv_php_type.typeinfo.encoding ); - - zval_auto_ptr return_value_z; - return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval )); - ZVAL_UNDEF( return_value_z ); - - // turn our stream into a zval to be returned - php_stream_to_zval( stream, return_value_z ); - - field_value = reinterpret_cast( return_value_z.get()); - return_value_z.transferred(); - break; - } - - case SQLSRV_PHPTYPE_NULL: - field_value = NULL; - *field_len = 0; - break; - - default: - DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" ); - break; - } - - // sucessfully retrieved the field, so update our last retrieved field - if( stmt->last_field_index < field_index ) { - stmt->last_field_index = field_index; - } - } - catch( core::CoreException& e ) { - throw e; - } -} - - -// check_for_next_stream_parameter -// see if there is another stream to be sent. Returns true and sets the stream as current in the statement structure, otherwise -// returns false -bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ) -{ - int stream_index = 0; - SQLRETURN r = SQL_SUCCESS; - sqlsrv_stream* stream_encoding = NULL; - zval* param_z = NULL; - - // get the index into the streams_ht from the parameter data we set in core_sqlsrv_bind_param - r = core::SQLParamData( stmt, reinterpret_cast( &stream_index ) TSRMLS_CC ); - // if no more data, we've exhausted the bound parameters, so return that we're done - if( SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { - - // we're all done, so return false - return false; - } - - HashTable* streams_ht = Z_ARRVAL( stmt->param_streams ); - - // pull out the sqlsrv_encoding struct - stream_encoding = reinterpret_cast(zend_hash_index_find_ptr(streams_ht, stream_index)); - SQLSRV_ASSERT(stream_encoding != NULL, "Stream parameter does not exist"); // if the index isn't in the hash, that's a serious error - - param_z = stream_encoding->stream_z; - - // make the next stream current - stmt->current_stream = sqlsrv_stream( param_z, stream_encoding->encoding ); - stmt->current_stream_read = 0; - - // there are more parameters - return true; -} - - -// utility routine to convert an input parameter from UTF-8 to UTF-16 - -bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z ) -{ - SQLSRV_ASSERT( input_param_z == converted_param_z || Z_TYPE_P( converted_param_z ) == IS_NULL, - "convert_input_param_z called with invalid parameter states" ); - - const char* buffer = Z_STRVAL_P( input_param_z ); - std::size_t buffer_len = Z_STRLEN_P( input_param_z ); - int wchar_size; - - if (buffer_len > INT_MAX) - { - LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded."); - throw core::CoreException(); - } - - // if the string is empty, then just return that the conversion succeeded as - // MultiByteToWideChar will "fail" on an empty string. - if( buffer_len == 0 ) { - core::sqlsrv_zval_stringl( converted_param_z, "", 0 ); - return true; - } - - // if the parameter is an input parameter, calc the size of the necessary buffer from the length of the string - wchar_size = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, - reinterpret_cast( buffer ), static_cast( buffer_len ), NULL, 0 ); - - // if there was a problem determining the size of the string, return false - if( wchar_size == 0 ) { - return false; - } - sqlsrv_malloc_auto_ptr wbuffer; - wbuffer = reinterpret_cast( sqlsrv_malloc( (wchar_size + 1) * sizeof( wchar_t ) )); - // convert the utf-8 string to a wchar string in the new buffer - int r = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast( buffer ), - static_cast( buffer_len ), wbuffer, wchar_size ); - // if there was a problem converting the string, then free the memory and return false - if( r == 0 ) { - return false; - } - - // null terminate the string, set the size within the zval, and return success - wbuffer[ wchar_size ] = L'\0'; - core::sqlsrv_zval_stringl( converted_param_z, reinterpret_cast( wbuffer.get() ), - wchar_size * sizeof( wchar_t ) ); - sqlsrv_free(wbuffer); - wbuffer.transferred(); - - return true; -} - -// returns the ODBC C type constant that matches the PHP type and encoding given - -SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC ) -{ - SQLSMALLINT sql_c_type = SQL_UNKNOWN_TYPE; - int php_type = Z_TYPE_P( param_z ); - - switch( php_type ) { - - case IS_NULL: - switch( encoding ) { - // The c type is set to match to the corresponding sql_type. For NULL cases, if the server type - // is a binary type, than the server expects the sql_type to be binary type as well, otherwise - // an error stating "Implicit conversion not allowed.." is thrown by the server. - // For all other server types, setting the sql_type to sql_char works fine. - case SQLSRV_ENCODING_BINARY: - sql_c_type = SQL_C_BINARY; - break; - default: - sql_c_type = SQL_C_CHAR; - break; - } - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - //ODBC 64-bit long and integer type are 4 byte values. - if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) ) - { - sql_c_type = SQL_C_SBIGINT; - } - else - { - sql_c_type = SQL_C_SLONG; - } - break; - case IS_DOUBLE: - sql_c_type = SQL_C_DOUBLE; - break; - case IS_STRING: - case IS_RESOURCE: - switch( encoding ) { - case SQLSRV_ENCODING_CHAR: - sql_c_type = SQL_C_CHAR; - break; - case SQLSRV_ENCODING_BINARY: - sql_c_type = SQL_C_BINARY; - break; - case CP_UTF8: - sql_c_type = SQL_C_WCHAR; - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_ENCODING, paramno ); - break; - } - break; - // it is assumed that an object is a DateTime since it's the only thing we support. - // verification that it's a real DateTime object occurs in core_sqlsrv_bind_param. - // we convert the DateTime to a string before sending it to the server. - case IS_OBJECT: - sql_c_type = SQL_C_CHAR; - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, paramno ); - break; - } - - return sql_c_type; -} - - -// given a zval and encoding, determine the appropriate sql type - -void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding, - _Out_ SQLSMALLINT& sql_type TSRMLS_DC ) -{ - sql_type = SQL_UNKNOWN_TYPE; - int php_type = Z_TYPE_P(param_z); - switch( php_type ) { - - case IS_NULL: - switch( encoding ) { - // Use the encoding to guess whether the sql_type is binary type or char type. For NULL cases, - // if the server type is a binary type, than the server expects the sql_type to be binary type - // as well, otherwise an error stating "Implicit conversion not allowed.." is thrown by the - // server. For all other server types, setting the sql_type to sql_char works fine. - case SQLSRV_ENCODING_BINARY: - sql_type = SQL_BINARY; - break; - default: - sql_type = SQL_CHAR; - break; - } - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - //ODBC 64-bit long and integer type are 4 byte values. - if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) ) - { - sql_type = SQL_BIGINT; - } - else - { - sql_type = SQL_INTEGER; - } - - break; - case IS_DOUBLE: - sql_type = SQL_FLOAT; - break; - case IS_RESOURCE: - case IS_STRING: - switch( encoding ) { - case SQLSRV_ENCODING_CHAR: - sql_type = SQL_VARCHAR; - break; - case SQLSRV_ENCODING_BINARY: - sql_type = SQL_VARBINARY; - break; - case CP_UTF8: - sql_type = SQL_WVARCHAR; - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_ENCODING, paramno ); - break; - } - break; - // it is assumed that an object is a DateTime since it's the only thing we support. - // verification that it's a real DateTime object occurs in the calling function. - // we convert the DateTime to a string before sending it to the server. - case IS_OBJECT: - // if the user is sending this type to SQL Server 2005 or earlier, make it appear - // as a SQLSRV_SQLTYPE_DATETIME, otherwise it should be SQLSRV_SQLTYPE_TIMESTAMPOFFSET - // since these are the date types of the highest precision for their respective server versions - if( stmt->conn->server_version <= SERVER_VERSION_2005 ) { - sql_type = SQL_TYPE_TIMESTAMP; - } - else { - sql_type = SQL_SS_TIMESTAMPOFFSET; - } - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, paramno ); - break; - } - -} - - -// given a zval and encoding, determine the appropriate column size, and decimal scale (if appropriate) - -void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding, - _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ) -{ - int php_type = Z_TYPE_P( param_z ); - column_size = 0; - decimal_digits = 0; - - switch( php_type ) { - - case IS_NULL: - column_size = 1; - break; - // size is not necessary for these types, they are inferred by ODBC - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - case IS_DOUBLE: - case IS_RESOURCE: - break; - case IS_STRING: - { - size_t char_size = ( encoding == SQLSRV_ENCODING_UTF8 ) ? sizeof( wchar_t ) : sizeof( char ); - SQLULEN byte_len = Z_STRLEN_P( param_z ) * char_size; - if( byte_len > SQL_SERVER_MAX_FIELD_SIZE ) { - column_size = SQL_SERVER_MAX_TYPE_SIZE; - } - else { - column_size = SQL_SERVER_MAX_FIELD_SIZE / char_size; - } - break; - } - // it is assumed that an object is a DateTime since it's the only thing we support. - // verification that it's a real DateTime object occurs in the calling function. - // we convert the DateTime to a string before sending it to the server. - case IS_OBJECT: - // if the user is sending this type to SQL Server 2005 or earlier, make it appear - // as a SQLSRV_SQLTYPE_DATETIME, otherwise it should be SQLSRV_SQLTYPE_TIMESTAMPOFFSET - // since these are the date types of the highest precision for their respective server versions - if( stmt->conn->server_version <= SERVER_VERSION_2005 ) { - column_size = SQL_SERVER_2005_DEFAULT_DATETIME_PRECISION; - decimal_digits = SQL_SERVER_2005_DEFAULT_DATETIME_SCALE; - } - else { - column_size = SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION; - decimal_digits = SQL_SERVER_2008_DEFAULT_DATETIME_SCALE; - } - break; - default: - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, paramno ); - break; - } -} - -void field_cache_dtor( zval* data_z ) -{ - field_cache* cache = static_cast( Z_PTR_P( data_z )); - if( cache->value ) - { - sqlsrv_free( cache->value ); - } - sqlsrv_free( cache ); -} - - -// To be called after all results are processed. ODBC and SQL Server do not guarantee that all output -// parameters will be present until all results are processed (since output parameters can depend on results -// while being processed). This function updates the lengths of output parameter strings from the ind_ptr -// parameters passed to SQLBindParameter. It also converts output strings from UTF-16 to UTF-8 if necessary. -// For integer or float parameters, it sets those to NULL if a NULL was returned by SQL Server - -void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC ) -{ - if( Z_ISUNDEF(stmt->output_params) ) - return; - - bool converted = true; - HashTable* params_ht = Z_ARRVAL( stmt->output_params ); - zend_ulong index = -1; - zend_string* key = NULL; - void* output_param_temp = NULL; - - ZEND_HASH_FOREACH_KEY_PTR( params_ht, index, key, output_param_temp ) { - sqlsrv_output_param* output_param = static_cast( output_param_temp ); - zval* value_z = Z_REFVAL_P( output_param->param_z ); - switch( Z_TYPE_P( value_z )) { - case IS_STRING: - { - // adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter - char* str = Z_STRVAL_P( value_z ); - SQLLEN str_len = stmt->param_ind_ptrs[ output_param->param_num ]; - if( str_len == SQL_NULL_DATA || str_len == 0 ) { - zend_string_release( Z_STR_P( value_z )); - ZVAL_NULL( value_z ); - continue; - } - - // if there was more to output than buffer size to hold it, then throw a truncation error - int null_size = 0; - switch( output_param->encoding ) { - case SQLSRV_ENCODING_UTF8: - null_size = sizeof( wchar_t ); // string isn't yet converted to UTF-8, still UTF-16 - break; - case SQLSRV_ENCODING_SYSTEM: - null_size = 1; - break; - case SQLSRV_ENCODING_BINARY: - null_size = 0; - break; - default: - SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." ); - break; - } - CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt, - SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) { - throw core::CoreException(); - } - - // if it's not in the 8 bit encodings, then it's in UTF-16 - if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) { - bool converted = convert_zval_string_from_utf16(output_param->encoding, value_z, str_len); - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) { - throw core::CoreException(); - } - } - else if( output_param->encoding == SQLSRV_ENCODING_BINARY && str_len < output_param->original_buffer_len ) { - // ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated - // so we do that here if the length of the returned data is less than the original allocation. The - // original allocation null terminates the buffer already. - str[ str_len ] = '\0'; - core::sqlsrv_zval_stringl(value_z, str, str_len); - } - else { - core::sqlsrv_zval_stringl(value_z, str, str_len); - } - } - break; - case IS_LONG: - // for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so - if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) { - ZVAL_NULL( value_z ); - } - else if( output_param->is_bool ) { - convert_to_boolean( value_z ); - } - else - { - ZVAL_LONG( value_z, static_cast( Z_LVAL_P( value_z ))); - } - break; - case IS_DOUBLE: - // for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so - if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) { - ZVAL_NULL( value_z ); - } - break; - default: - DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." ); - break; - } - value_z = NULL; - } ZEND_HASH_FOREACH_END(); - - // empty the hash table since it's been processed - zend_hash_clean( Z_ARRVAL( stmt->output_params )); - return; -} - -void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type, - _Out_ void*& field_value, _Out_ SQLLEN* field_len TSRMLS_DC ) -{ - SQLRETURN r; - SQLSMALLINT c_type; - SQLLEN sql_field_type = 0; - SQLSMALLINT extra = 0; - SQLLEN field_len_temp; - SQLLEN sql_display_size = 0; - char* field_value_temp = NULL; - - try { - - DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING, - "Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" ); - - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { - sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding(); - } - - // Set the C type and account for null characters at the end of the data. - switch( sqlsrv_php_type.typeinfo.encoding ) { - case CP_UTF8: - c_type = SQL_C_WCHAR; - extra = sizeof( SQLWCHAR ); - break; - case SQLSRV_ENCODING_BINARY: - c_type = SQL_C_BINARY; - extra = 0; - break; - default: - c_type = SQL_C_CHAR; - extra = sizeof( SQLCHAR ); - break; - } - - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - - // Calculate the field size. - calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); - - // if this is a large type, then read the first few bytes to get the actual length from SQLGetData - if( sql_display_size == 0 || sql_display_size == INT_MAX || - sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) { - - field_len_temp = INITIAL_FIELD_STRING_LEN; - - field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); - - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ), - &field_len_temp, false /*handle_warning*/ TSRMLS_CC ); - - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - if( field_len_temp == SQL_NULL_DATA ) { - field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - if( r == SQL_SUCCESS_WITH_INFO ) { - - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - - stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); - - if( is_truncated_warning( state )) { - - SQLLEN dummy_field_len; - - // for XML (and possibly other conditions) the field length returned is not the real field length, so - // in every pass, we double the allocation size to retrieve all the contents. - if( field_len_temp == SQL_NO_TOTAL ) { - - // reset the field_len_temp - field_len_temp = INITIAL_FIELD_STRING_LEN; - - do { - SQLLEN initial_field_len = field_len_temp; - // Double the size. - field_len_temp *= 2; - - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - field_len_temp -= initial_field_len; - - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len, - field_len_temp + extra, &dummy_field_len, - false /*handle_warning*/ TSRMLS_CC ); - - // the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL - // so we calculate the actual length of the string with that. - if( dummy_field_len != SQL_NO_TOTAL ) - field_len_temp += dummy_field_len; - else - field_len_temp += initial_field_len; - - if( r == SQL_SUCCESS_WITH_INFO ) { - core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len - TSRMLS_CC ); - } - - } while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state )); - } - else { - - // We got the field_len_temp from SQLGetData call. - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - // We have already recieved INITIAL_FIELD_STRING_LEN size data. - field_len_temp -= INITIAL_FIELD_STRING_LEN; - - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN, - field_len_temp + extra, &dummy_field_len, - true /*handle_warning*/ TSRMLS_CC ); - - if( dummy_field_len == SQL_NULL_DATA ) { - field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - field_len_temp += INITIAL_FIELD_STRING_LEN; - } - - } // if( is_truncation_warning ( state ) ) - else { - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - } - } // if( r == SQL_SUCCESS_WITH_INFO ) - - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) { - - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { - throw core::CoreException(); - } - } - } // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. ) - - else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) { - - // only allow binary retrievals for char and binary types. All others get a string converted - // to the encoding type they asked for. - - // null terminator - if( c_type == SQL_C_CHAR ) { - sql_display_size += sizeof( SQLCHAR ); - } - - // For WCHAR multiply by sizeof(WCHAR) and include the null terminator - else if( c_type == SQL_C_WCHAR ) { - sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR); - } - - field_value_temp = static_cast( sqlsrv_malloc( sql_display_size + extra + 1 )); - - // get the data - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size, - &field_len_temp, true /*handle_warning*/ TSRMLS_CC ); - CHECK_SQL_ERROR( r, stmt ) { - throw core::CoreException(); - } - CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } - - if( field_len_temp == SQL_NULL_DATA ) { - field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) { - - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { - throw core::CoreException(); - } - } - } // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) - - else { - - DIE( "Invalid sql_display_size" ); - return; // to eliminate a warning - } - - field_value = field_value_temp; - *field_len = field_len_temp; - - // prevent a warning in debug mode about strings not being NULL terminated. Even though nulls are not necessary, the PHP - // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. - // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary - // operator to set add 1 to fill the null terminator - field_value_temp[field_len_temp] = '\0'; - } - - catch( core::CoreException& ) { - - field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } - catch ( ... ) { - - field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } - -} - - -// return the option from the stmt_opts array that matches the key. If no option found, -// NULL is returned. - -stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC ) -{ - for( int i = 0; stmt_opts[ i ].key != SQLSRV_STMT_OPTION_INVALID; ++i ) { - - // if we find the key we're looking for, return it - if( key == stmt_opts[ i ].key ) { - return &stmt_opts[ i ]; - } - } - - return NULL; // no option found -} - -// is_fixed_size_type -// returns true if the SQL data type is a fixed length, as opposed to a variable length data type such as varchar or varbinary - -bool is_fixed_size_type( SQLINTEGER sql_type ) -{ - switch( sql_type ) { - - case SQL_BINARY: - case SQL_CHAR: - case SQL_WCHAR: - case SQL_VARCHAR: - case SQL_WVARCHAR: - case SQL_LONGVARCHAR: - case SQL_WLONGVARCHAR: - case SQL_VARBINARY: - case SQL_LONGVARBINARY: - case SQL_SS_XML: - case SQL_SS_UDT: - return false; - } - - return true; -} - -bool is_valid_sqlsrv_phptype( sqlsrv_phptype type ) -{ - switch( type.typeinfo.type ) { - - case SQLSRV_PHPTYPE_NULL: - case SQLSRV_PHPTYPE_INT: - case SQLSRV_PHPTYPE_FLOAT: - case SQLSRV_PHPTYPE_DATETIME: - return true; - case SQLSRV_PHPTYPE_STRING: - case SQLSRV_PHPTYPE_STREAM: - { - if( type.typeinfo.encoding == SQLSRV_ENCODING_BINARY || type.typeinfo.encoding == SQLSRV_ENCODING_CHAR - || type.typeinfo.encoding == CP_UTF8 || type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { - return true; - } - break; - } - } - - return false; -} - - -// verify there is enough space to hold the output string parameter, and allocate it if needed. The param_z -// is updated to have the new buffer with the correct size and its reference is incremented. The output -// string is place in the stmt->output_params. param_z is modified to hold the new buffer, and buffer, buffer_len and -// stmt->param_ind_ptrs are modified to hold the correct values for SQLBindParameter - -void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding, - SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer, - SQLLEN& buffer_len TSRMLS_DC ) -{ - SQLSRV_ASSERT( column_size != SQLSRV_UNKNOWN_SIZE, "column size should be set to a known value." ); - buffer_len = Z_STRLEN_P( param_z ); - SQLLEN expected_len; - SQLLEN buffer_null_extra; - SQLLEN elem_size; - SQLLEN without_null_len; - - // calculate the size of each 'element' represented by column_size. WCHAR is of course 2, - // as is a n(var)char/ntext field being returned as a binary field. - elem_size = (c_type == SQL_C_WCHAR || (c_type == SQL_C_BINARY && (sql_type == SQL_WCHAR || sql_type == SQL_WVARCHAR))) ? 2 : 1; - - // account for the NULL terminator returned by ODBC and needed by Zend to avoid a "String not null terminated" debug warning - expected_len = column_size * elem_size + elem_size; - - // binary fields aren't null terminated, so we need to account for that in our buffer length calcuations - buffer_null_extra = (c_type == SQL_C_BINARY) ? elem_size : 0; - - // this is the size of the string for Zend and for the StrLen parameter to SQLBindParameter - without_null_len = column_size * elem_size; - - // increment to include the null terminator since the Zend length doesn't include the null terminator - buffer_len += elem_size; - - // if the current buffer size is smaller than the necessary size, resize the buffer and set the zval to the new - // length. - if( buffer_len < expected_len ) { - SQLSRV_ASSERT( expected_len >= expected_len - buffer_null_extra, - "Integer overflow/underflow caused a corrupt field length." ); - - // allocate enough space to ALWAYS include the NULL regardless of the type being retrieved since - // we set the last byte(s) to be NULL to avoid the debug build warning from the Zend engine about - // not having a NULL terminator on a string. - zend_string* param_z_string = zend_string_realloc( Z_STR_P(param_z), expected_len, 0 ); - - // A zval string len doesn't include the null. This calculates the length it should be - // regardless of whether the ODBC type contains the NULL or not. - - // null terminate the string to avoid a warning in debug PHP builds - ZSTR_VAL(param_z_string)[without_null_len] = '\0'; - ZVAL_NEW_STR(param_z, param_z_string); - - // buffer_len is the length passed to SQLBindParameter. It must contain the space for NULL in the - // buffer when retrieving anything but SQLSRV_ENC_BINARY/SQL_C_BINARY - buffer_len = Z_STRLEN_P(param_z) - buffer_null_extra; - - // Zend string length doesn't include the null terminator - ZSTR_LEN(Z_STR_P(param_z)) -= elem_size; - } - - buffer = Z_STRVAL_P(param_z); - - // The StrLen_Ind_Ptr parameter of SQLBindParameter should contain the length of the data to send, which - // may be less than the size of the buffer since the output may be more than the input. If it is greater, - // than the error 22001 is returned by ODBC. - if( stmt->param_ind_ptrs[ paramno ] > buffer_len - (elem_size - buffer_null_extra)) { - stmt->param_ind_ptrs[ paramno ] = buffer_len - (elem_size - buffer_null_extra); - } -} - -// output parameters have their reference count incremented so that they do not disappear -// while the query is executed and processed. They are saved in the statement so that -// their reference count may be decremented later (after results are processed) - -void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC ) -{ - HashTable* param_ht = Z_ARRVAL( stmt->output_params ); - zend_ulong paramno = static_cast( param.param_num ); - core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, ¶m, sizeof( sqlsrv_output_param )); - Z_TRY_ADDREF_P( param.param_z ); // we have a reference to the param -} - - -// send all the stream data - -void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC ) -{ - while( core_sqlsrv_send_stream_packet( stmt TSRMLS_CC )) { } -} - - -// called by Zend for each parameter in the sqlsrv_stmt::output_params hash table when it is cleaned/destroyed -void sqlsrv_output_param_dtor( zval* data ) -{ - sqlsrv_output_param *output_param = static_cast( Z_PTR_P( data )); - zval_ptr_dtor( output_param->param_z ); // undo the reference to the string we will no longer hold - sqlsrv_free( output_param ); -} - -// called by Zend for each stream in the sqlsrv_stmt::param_streams hash table when it is cleaned/destroyed -void sqlsrv_stream_dtor( zval* data ) -{ - sqlsrv_stream* stream_encoding = static_cast( Z_PTR_P( data )); - zval_ptr_dtor( stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold - sqlsrv_free( stream_encoding ); -} - -} diff --git a/source/pdo_sqlsrv/shared/core_stream.cpp b/source/pdo_sqlsrv/shared/core_stream.cpp deleted file mode 100644 index 08b01d05..00000000 --- a/source/pdo_sqlsrv/shared/core_stream.cpp +++ /dev/null @@ -1,261 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_stream.cpp -// -// Contents: Implementation of PHP streams for reading SQL Server data -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" -#include - -namespace { - -// close a stream and free the PHP resources used by it - -int sqlsrv_stream_close( php_stream* stream, int /*close_handle*/ TSRMLS_DC ) -{ - sqlsrv_stream* ss = static_cast( stream->abstract ); - SQLSRV_ASSERT( ss != NULL, "sqlsrv_stream_close: sqlsrv_stream* ss was null." ); - - // free the stream resources in the Zend engine - php_stream_free( stream, PHP_STREAM_FREE_RELEASE_STREAM ); - - // UNDEF the stream zval and delete our reference count to it. - ZVAL_UNDEF( &( ss->stmt->active_stream ) ); - - sqlsrv_free( ss ); - stream->abstract = NULL; - - return 0; -} - - -// read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are -// set when sqlsrv_get_field is called by the user specifying which field type they want. - -size_t sqlsrv_stream_read( php_stream* stream, _Out_writes_bytes_(count) char* buf, size_t count TSRMLS_DC ) -{ - SQLLEN read = 0; - SQLSMALLINT c_type = SQL_C_CHAR; - char* get_data_buffer = buf; - sqlsrv_malloc_auto_ptr temp_buf; - - sqlsrv_stream* ss = static_cast( stream->abstract ); - SQLSRV_ASSERT( ss != NULL, "sqlsrv_stream_read: sqlsrv_stream* ss is NULL." ); - - try { - - if( stream->eof ) { - return 0; - }; - - switch( ss->encoding ) { - case SQLSRV_ENCODING_CHAR: - c_type = SQL_C_CHAR; - break; - - case SQLSRV_ENCODING_BINARY: - c_type = SQL_C_BINARY; - break; - - case CP_UTF8: - { - c_type = SQL_C_WCHAR; - count /= 2; // divide the number of bytes we read by 2 since converting to UTF-8 can cause an increase in bytes - if( count > PHP_STREAM_BUFFER_SIZE ) { - count = PHP_STREAM_BUFFER_SIZE; - } - - // use a temporary buffer to retrieve from SQLGetData since we need to translate it to UTF-8 from UTF-16 - temp_buf = static_cast( sqlsrv_malloc( PHP_STREAM_BUFFER_SIZE )); - get_data_buffer = temp_buf; - break; - } - - default: - DIE( "Unknown encoding type when reading from a stream" ); - break; - } - - SQLRETURN r = SQLGetData( ss->stmt->handle(), ss->field_index + 1, c_type, get_data_buffer, count /*BufferLength*/, &read ); - - CHECK_SQL_ERROR( r, ss->stmt ) { - stream->eof = 1; - throw core::CoreException(); - } - - // if the stream returns either no data, NULL data, or returns data < than the count requested then - // we are at the "end of the stream" so we mark it - if( r == SQL_NO_DATA || read == SQL_NULL_DATA || ( static_cast( read ) <= count && read != SQL_NO_TOTAL )) { - stream->eof = 1; - } - - // if ODBC returns the 01004 (truncated string) warning, then we return the count minus the null terminator - // if it's not a binary encoded field - if( r == SQL_SUCCESS_WITH_INFO ) { - - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - - ss->stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); - - if( read == SQL_NO_TOTAL ) { - SQLSRV_ASSERT( is_truncated_warning( state ), "sqlsrv_stream_read: truncation warning was expected but it " - "did not occur." ); - } - - if( is_truncated_warning( state ) ) { - switch( c_type ) { - - // As per SQLGetData documentation, if the length of character data exceeds the BufferLength, - // SQLGetData truncates the data to BufferLength less the length of null-termination character. - case SQL_C_BINARY: - read = count; - break; - case SQL_C_WCHAR: - read = ( count % 2 == 0 ? count - 2 : count - 3 ); - break; - case SQL_C_CHAR: - read = count - 1; - break; - default: - DIE( "sqlsrv_stream_read: should have never reached in this switch case."); - break; - } - } - else { - CHECK_SQL_WARNING( r, ss->stmt ); - } - } - - // if the encoding is UTF-8 - if (c_type == SQL_C_WCHAR) { - - count *= 2; // undo the shift to use the full buffer - - // flags set to 0 by default, which means that any invalid characters are dropped rather than causing - // an error. This happens only on XP. - DWORD flags = 0; - - // convert to UTF-8 - if (g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER) { - // Vista (and later) will detect invalid UTF-16 characters and raise an error. - flags = WC_ERR_INVALID_CHARS; - } - - if ( count > INT_MAX || (read >> 1) > INT_MAX ) - { - LOG(SEV_ERROR, "UTF-16 (wide character) string mapping: buffer length exceeded."); - throw core::CoreException(); - } - - int enc_len = WideCharToMultiByte( ss->encoding, flags, reinterpret_cast( temp_buf.get() ), - static_cast(read >> 1), buf, static_cast(count), NULL, NULL ); - - if( enc_len == 0 ) { - - stream->eof = 1; - THROW_CORE_ERROR( ss->stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ); - } - - read = enc_len; - } - - return read; - } - - catch( core::CoreException& ) { - - return 0; - } - catch( ... ) { - - LOG( SEV_ERROR, "sqlsrv_stream_read: Unknown exception caught." ); - return 0; - } -} - -// function table for stream operations. We only support reading and closing the stream -php_stream_ops sqlsrv_stream_ops = { - NULL, - sqlsrv_stream_read, - sqlsrv_stream_close, - NULL, - SQLSRV_STREAM, - NULL, - NULL, - NULL, - NULL -}; - -// open a stream and return the sqlsrv_stream_ops function table as part of the -// return value. There is only one valid way to open a stream, using sqlsrv_get_field on -// certain field types. A sqlsrv stream may only be opened in read mode. -static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, _In_ const char*, _In_ const char* mode, - int options, _In_ zend_string **, php_stream_context* STREAMS_DC TSRMLS_DC ) -{ - -#if ZEND_DEBUG - SQLSRV_UNUSED( __zend_orig_lineno ); - SQLSRV_UNUSED( __zend_orig_filename ); - SQLSRV_UNUSED( __zend_lineno ); - SQLSRV_UNUSED( __zend_filename ); - SQLSRV_UNUSED( __php_stream_call_depth ); -#endif - - sqlsrv_malloc_auto_ptr ss; - - ss = static_cast( sqlsrv_malloc( sizeof( sqlsrv_stream ))); - memset( ss, 0, sizeof( sqlsrv_stream )); - - // check for valid options - if( options != REPORT_ERRORS ) { - php_stream_wrapper_log_error( wrapper, options TSRMLS_CC, "Invalid option: no options except REPORT_ERRORS may be specified with a sqlsrv stream" ); - return NULL; - } - - // allocate the stream from PHP - php_stream* php_str = php_stream_alloc( &sqlsrv_stream_ops, ss, 0, mode ); - if( php_str != NULL ) { - ss.transferred(); - } - - return php_str; -} - -// information structure that contains PHP stream wrapper info. We supply the minimal -// possible, including the open function and the name only. - -php_stream_wrapper_ops sqlsrv_stream_wrapper_ops = { - sqlsrv_stream_opener, - NULL, - NULL, - NULL, - NULL, - SQLSRV_STREAM_WRAPPER, - NULL, - NULL, - NULL, - NULL -}; - -} - -// structure used by PHP to get the function table for opening, closing, etc. of the stream -php_stream_wrapper g_sqlsrv_stream_wrapper = { - &sqlsrv_stream_wrapper_ops, - NULL, - 0 -}; diff --git a/source/pdo_sqlsrv/shared/core_util.cpp b/source/pdo_sqlsrv/shared/core_util.cpp deleted file mode 100644 index fe3c4d06..00000000 --- a/source/pdo_sqlsrv/shared/core_util.cpp +++ /dev/null @@ -1,400 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: core_util.cpp -// -// Contents: Utility functions used by both connection or statement functions for both the PDO and sqlsrv drivers -// -// Comments: Mostly error handling and some type handling -// -// Microsoft Drivers 4.1 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 "core_sqlsrv.h" - -#include - -namespace { - -// *** internal constants *** -log_callback g_driver_log; -// internal error that says that FormatMessage failed -SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage failed writing an error message."; -// buffer used to hold a formatted log message prior to actually logging it. -char last_err_msg[ 2048 ]; // 2k to hold the error messages - -// routine used by utf16_string_from_mbcs_string -unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string, - unsigned int mbcs_len, - _Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string, - unsigned int utf16_len ); -} - -// SQLSTATE for all internal errors -SQLCHAR IMSSP[] = "IMSSP"; - -// SQLSTATE for all internal warnings -SQLCHAR SSPWARN[] = "01SSP"; - -// write to the php log if the severity and subsystem match the filters currently set in the INI or -// the script (sqlsrv_configure). -void write_to_log( unsigned int severity TSRMLS_DC, const char* msg, ...) -{ - SQLSRV_ASSERT( !(g_driver_log == NULL), "Must register a driver log function." ); - - va_list args; - va_start( args, msg ); - - g_driver_log( severity TSRMLS_CC, msg, &args ); - - va_end( args ); -} - -void core_sqlsrv_register_logger( log_callback driver_logger ) -{ - g_driver_log = driver_logger; -} - - -// convert a string from utf-16 to the encoding and return the new string in the pointer parameter and new -// length in the len parameter. If no errors occurred during convertion, true is returned and the original -// utf-16 string is released by this function if no errors occurred. Otherwise the parameters are not changed -// and false is returned. - -bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLLEN& len) -{ - SQLSRV_ASSERT( string != NULL, "String must be specified" ); - - if (validate_string(*string, len)) { - return true; - } - - char* outString = NULL; - SQLLEN outLen = 0; - - bool result = convert_string_from_utf16( encoding, - reinterpret_cast(*string), int(len / sizeof(wchar_t)), &outString, outLen); - - if (result) - { - sqlsrv_free( *string ); - *string = outString; - len = outLen; - } - - return result; -} - -bool convert_zval_string_from_utf16(SQLSRV_ENCODING encoding, zval* value_z, SQLLEN& len) -{ - char* string = Z_STRVAL_P(value_z); - - if (validate_string(string, len)) { - return true; - } - - char* outString = NULL; - SQLLEN outLen = 0; - - bool result = convert_string_from_utf16(encoding, - reinterpret_cast(string), int(len / sizeof(wchar_t)), &outString, outLen); - - if (result) - { - core::sqlsrv_zval_stringl(value_z, outString, outLen); - sqlsrv_free(outString); - len = outLen; - } - - return result; -} - -bool validate_string(char* string, SQLLEN& len) -{ - SQLSRV_ASSERT(string != NULL, "String must be specified"); - - //for the empty string, we simply returned we converted it - if (len == 0 && string[0] == '\0') { - return true; - } - - if ((len / sizeof(wchar_t)) > INT_MAX) - { - LOG(SEV_ERROR, "UTP-16 (wide character) string mapping: buffer length exceeded."); - throw core::CoreException(); - } - - return false; -} - -bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLLEN& cchOutLen ) -{ - SQLSRV_ASSERT( inString != NULL, "Input string must be specified" ); - SQLSRV_ASSERT( outString != NULL, "Output buffer pointer must be specified" ); - SQLSRV_ASSERT( *outString == NULL, "Output buffer pointer must not be set" ); - - if (cchInLen == 0 && inString[0] == L'\0') { - *outString = reinterpret_cast( sqlsrv_malloc ( 1 ) ); - *outString[0] = '\0'; - cchOutLen = 0; - return true; - } - - // flags set to 0 by default, which means that any invalid characters are dropped rather than causing - // an error. This happens only on XP. - DWORD flags = 0; - if( encoding == CP_UTF8 && g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER ) { - // Vista (and later) will detect invalid UTF-16 characters and raise an error. - flags = WC_ERR_INVALID_CHARS; - } - - // calculate the number of characters needed - cchOutLen = WideCharToMultiByte( encoding, flags, - inString, cchInLen, - NULL, 0, NULL, NULL ); - if( cchOutLen == 0 ) { - return false; - } - - // Create a buffer to fit the encoded string - char* newString = reinterpret_cast( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ )); - int rc = WideCharToMultiByte( encoding, flags, - inString, cchInLen, - newString, static_cast(cchOutLen), NULL, NULL ); - if( rc == 0 ) { - cchOutLen = 0; - sqlsrv_free( newString ); - return false; - } - - *outString = newString; - newString[cchOutLen] = '\0'; // null terminate the encoded string - - return true; -} - -// thin wrapper around convert_string_from_default_encoding that handles -// allocation of the destination string. An empty string passed in returns -// failure since it's a failure case for convert_string_from_default_encoding. -wchar_t* utf16_string_from_mbcs_string( SQLSRV_ENCODING php_encoding, const char* mbcs_string, unsigned int mbcs_len, - unsigned int* utf16_len ) -{ - *utf16_len = (mbcs_len + 1); - wchar_t* utf16_string = reinterpret_cast( sqlsrv_malloc( *utf16_len * sizeof( wchar_t ))); - *utf16_len = convert_string_from_default_encoding( php_encoding, mbcs_string, mbcs_len, - utf16_string, *utf16_len ); - if( *utf16_len == 0 ) { - // we preserve the error and reset it because sqlsrv_free resets the last error - DWORD last_error = GetLastError(); - sqlsrv_free( utf16_string ); - SetLastError( last_error ); - return NULL; - } - - return utf16_string; -} - -// call to retrieve an error from ODBC. This uses SQLGetDiagRec, so the -// errno is 1 based. It returns it as an array with 3 members: -// 1/SQLSTATE) sqlstate -// 2/code) driver specific error code -// 3/message) driver specific error message -// The fetch type determines if the indices are numeric, associative, or both. - -bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, sqlsrv_error_auto_ptr& error, logging_severity severity - TSRMLS_DC ) -{ - SQLHANDLE h = ctx.handle(); - SQLSMALLINT h_type = ctx.handle_type(); - - if( h == NULL ) { - return false; - } - - zval* ssphp_z = NULL; - int zr = SUCCESS; - zval* temp = NULL; - SQLRETURN r = SQL_SUCCESS; - SQLSMALLINT wmessage_len = 0; - SQLWCHAR wsqlstate[ SQL_SQLSTATE_BUFSIZE ]; - SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ]; - SQLSRV_ENCODING enc = ctx.encoding(); - - switch( h_type ) { - - case SQL_HANDLE_STMT: - { - sqlsrv_stmt* stmt = static_cast( &ctx ); - if( stmt->current_results != NULL ) { - - error = stmt->current_results->get_diag_rec( record_number ); - // don't use the CHECK* macros here since it will trigger reentry into the error handling system - if( error == NULL ) { - return false; - } - break; - } - - // convert the error into the encoding of the context - if( enc == SQLSRV_ENCODING_DEFAULT ) { - enc = stmt->conn->encoding(); - } - } - - - default: - - error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); - r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message, - SQL_MAX_MESSAGE_LENGTH + 1, &wmessage_len ); - // don't use the CHECK* macros here since it will trigger reentry into the error handling system - if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { - return false; - } - - SQLLEN sqlstate_len = 0; - convert_string_from_utf16(enc, wsqlstate, sizeof(wsqlstate), (char**)&error->sqlstate, sqlstate_len); - - SQLLEN message_len = 0; - convert_string_from_utf16(enc, wnative_message, wmessage_len, (char**)&error->native_message, message_len); - break; - } - - - // log the error first - LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), error->sqlstate ); - LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), error->native_code ); - LOG( severity, "%1!s!: message = %2!s!", ctx.func(), error->native_message ); - - error->format = false; - - return true; -} - -// format and return a driver specfic error -void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const const* custom_error, - sqlsrv_error_auto_ptr& formatted_error, logging_severity severity TSRMLS_DC, va_list* args ) -{ - // allocate space for the formatted message - formatted_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); - formatted_error->sqlstate = reinterpret_cast( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); - formatted_error->native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 )); - - DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast( custom_error->native_message ), 0, 0, - reinterpret_cast( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, args ); - if( rc == 0 ) { - strcpy_s( reinterpret_cast( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, - reinterpret_cast( INTERNAL_FORMAT_ERROR )); - } - - strcpy_s( reinterpret_cast( formatted_error->sqlstate ), SQL_SQLSTATE_BUFSIZE, - reinterpret_cast( custom_error->sqlstate )); - formatted_error->native_code = custom_error->native_code; - - // log the error - LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), formatted_error->sqlstate ); - LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), formatted_error->native_code ); - LOG( severity, "%1!s!: message = %2!s!", ctx.func(), formatted_error->native_message ); -} - -DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... ) -{ - va_list format_args; - va_start( format_args, format ); - - DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, static_cast(output_buffer), output_len, &format_args ); - - va_end( format_args ); - - return rc; -} - -// return an error message for GetLastError using FormatMessage. -// this function returns the msg pointer so that it may be used within -// another function call such as handle_error -const char* get_last_error_message( DWORD last_error ) -{ - if( last_error == 0 ) { - last_error = GetLastError(); - } - - DWORD r = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), - last_err_msg, sizeof( last_err_msg ), NULL ); - - if( r == 0 ) { - SQLSRV_STATIC_ASSERT( sizeof( INTERNAL_FORMAT_ERROR ) < sizeof( last_err_msg )); - std::copy( INTERNAL_FORMAT_ERROR, INTERNAL_FORMAT_ERROR + sizeof( INTERNAL_FORMAT_ERROR ), last_err_msg ); - } - - return last_err_msg; -} - - -// die -// Terminate the PHP request with an error message -// We use this function rather than php_error directly because we use the FormatMessage syntax in most other -// places within the extension (e.g., LOG), whereas php_error uses the printf format syntax. There were -// places where we were using the FormatMessage syntax inadvertently with DIE which left messages without -// proper information. Rather than convert those messages and try and remember the difference between LOG and -// DIE, it is simpler to make the format syntax common between them. -void die( const char* msg, ... ) -{ - va_list format_args; - va_start( format_args, msg ); - - DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, last_err_msg, sizeof( last_err_msg ), &format_args ); - - va_end( format_args ); - - if( rc == 0 ) { - php_error( E_ERROR, reinterpret_cast( INTERNAL_FORMAT_ERROR )); - } - - php_error( E_ERROR, last_err_msg ); -} - -namespace { - -// convert from the default encoding specified by the "CharacterSet" -// connection option to UTF-16. mbcs_len and utf16_len are sizes in -// bytes. The return is the number of UTF-16 characters in the string -// returned in utf16_out_string. An empty string passed in will result as -// a failure since MBTWC returns 0 for both an empty string and failure -// to convert. -unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string, - unsigned int mbcs_len, _Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string, - unsigned int utf16_len ) -{ - unsigned int win_encoding = CP_ACP; - switch( php_encoding ) { - case SQLSRV_ENCODING_CHAR: - win_encoding = CP_ACP; - break; - // this shouldn't ever be set - case SQLSRV_ENCODING_BINARY: - DIE( "Invalid encoding." ); - break; - default: - win_encoding = php_encoding; - break; - } - unsigned int required_len = MultiByteToWideChar( win_encoding, MB_ERR_INVALID_CHARS, mbcs_in_string, mbcs_len, - utf16_out_string, utf16_len ); - if( required_len == 0 ) { - return 0; - } - utf16_out_string[ required_len ] = '\0'; - - return required_len; -} - -} diff --git a/source/pdo_sqlsrv/shared/msodbcsql.h b/source/pdo_sqlsrv/shared/msodbcsql.h deleted file mode 100644 index 8bcf83d0..00000000 --- a/source/pdo_sqlsrv/shared/msodbcsql.h +++ /dev/null @@ -1,2343 +0,0 @@ -//----------------------------------------------------------------------------- -// File: msodbcsql.h -// -// Copyright: Copyright (c) Microsoft Corporation -// -// Contents: ODBC driver for SQL Server specific definitions. -// -//----------------------------------------------------------------------------- -#ifndef __msodbcsql_h__ -#define __msodbcsql_h__ - -#if !defined(SQLODBC_VER) -#define SQLODBC_VER 1100 -#endif - -#if SQLODBC_VER >= 1100 - -#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 11 for SQL Server" -#define SQLODBC_PRODUCT_NAME_FULL_ANSI "Microsoft ODBC Driver for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 11 for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_ANSI "ODBC Driver for SQL Server" - -#define SQLODBC_FILE_NAME_ANSI "msodbcsql" -#define SQLODBC_FILE_NAME_VER_ANSI "msodbcsql11" -#define SQLODBC_FILE_NAME_FULL_ANSI "msodbcsql11.dll" - -#define SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft ODBC Driver 11 for SQL Server" -#define SQLODBC_PRODUCT_NAME_FULL_UNICODE L"Microsoft ODBC Driver for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE L"ODBC Driver 11 for SQL Server" -#define SQLODBC_PRODUCT_NAME_SHORT_UNICODE L"ODBC Driver for SQL Server" - -#define SQLODBC_FILE_NAME_UNICODE L"msodbcsql" -#define SQLODBC_FILE_NAME_VER_UNICODE L"msodbcsql11" -#define SQLODBC_FILE_NAME_FULL_UNICODE L"msodbcsql11.dll" - -// define the character type agnostic constants -#if defined(_UNICODE) || defined(UNICODE) - -#define SQLODBC_PRODUCT_NAME_FULL_VER SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE -#define SQLODBC_PRODUCT_NAME_FULL SQLODBC_PRODUCT_NAME_FULL_UNICODE -#define SQLODBC_PRODUCT_NAME_SHORT_VER SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE -#define SQLODBC_PRODUCT_NAME_SHORT SQLODBC_PRODUCT_NAME_SHORT_UNICODE - -#define SQLODBC_FILE_NAME SQLODBC_FILE_NAME_UNICODE -#define SQLODBC_FILE_NAME_VER SQLODBC_FILE_NAME_VER_UNICODE -#define SQLODBC_FILE_NAME_FULL SQLODBC_FILE_NAME_FULL_UNICODE - -#else // _UNICODE || UNICODE - -#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_FILE_NAME SQLODBC_FILE_NAME_ANSI -#define SQLODBC_FILE_NAME_VER SQLODBC_FILE_NAME_VER_ANSI -#define SQLODBC_FILE_NAME_FULL SQLODBC_FILE_NAME_FULL_ANSI - -#endif // _UNICODE || UNICODE - -#define SQLODBC_DRIVER_NAME SQLODBC_PRODUCT_NAME_SHORT_VER - -#endif // SQLODBC_VER - -#ifndef __sqlncli_h__ - -#if !defined(SQLNCLI_VER) -#define SQLNCLI_VER 1100 -#endif - -#if SQLNCLI_VER >= 1100 - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_ANSI "Microsoft SQL Server Native Client 11.0" -#define SQLNCLI_PRODUCT_NAME_FULL_ANSI "Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_ANSI "SQL Server Native Client 11.0" -#define SQLNCLI_PRODUCT_NAME_SHORT_ANSI "SQL Server Native Client" - -#define SQLNCLI_FILE_NAME_ANSI "sqlncli" -#define SQLNCLI_FILE_NAME_VER_ANSI "sqlncli11" -#define SQLNCLI_FILE_NAME_FULL_ANSI "sqlncli11.dll" - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft SQL Server Native Client 11.0" -#define SQLNCLI_PRODUCT_NAME_FULL_UNICODE L"Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_UNICODE L"SQL Server Native Client 11.0" -#define SQLNCLI_PRODUCT_NAME_SHORT_UNICODE L"SQL Server Native Client" - -#define SQLNCLI_FILE_NAME_UNICODE L"sqlncli" -#define SQLNCLI_FILE_NAME_VER_UNICODE L"sqlncli11" -#define SQLNCLI_FILE_NAME_FULL_UNICODE L"sqlncli11.dll" - -#elif SQLNCLI_VER >= 1000 - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_ANSI "Microsoft SQL Server Native Client 10.0" -#define SQLNCLI_PRODUCT_NAME_FULL_ANSI "Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_ANSI "SQL Server Native Client 10.0" -#define SQLNCLI_PRODUCT_NAME_SHORT_ANSI "SQL Server Native Client" - -#define SQLNCLI_FILE_NAME_ANSI "sqlncli" -#define SQLNCLI_FILE_NAME_VER_ANSI "sqlncli10" -#define SQLNCLI_FILE_NAME_FULL_ANSI "sqlncli10.dll" - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft SQL Server Native Client 10.0" -#define SQLNCLI_PRODUCT_NAME_FULL_UNICODE L"Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_UNICODE L"SQL Server Native Client 10.0" -#define SQLNCLI_PRODUCT_NAME_SHORT_UNICODE L"SQL Server Native Client" - -#define SQLNCLI_FILE_NAME_UNICODE L"sqlncli" -#define SQLNCLI_FILE_NAME_VER_UNICODE L"sqlncli10" -#define SQLNCLI_FILE_NAME_FULL_UNICODE L"sqlncli10.dll" - -#else - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_ANSI "Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_FULL_ANSI "Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_ANSI "SQL Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_ANSI "SQL Native Client" - -#define SQLNCLI_FILE_NAME_ANSI "sqlncli" -#define SQLNCLI_FILE_NAME_VER_ANSI "sqlncli" -#define SQLNCLI_FILE_NAME_FULL_ANSI "sqlncli.dll" - -#define SQLNCLI_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_FULL_UNICODE L"Microsoft SQL Server Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_VER_UNICODE L"SQL Native Client" -#define SQLNCLI_PRODUCT_NAME_SHORT_UNICODE L"SQL Native Client" - -#define SQLNCLI_FILE_NAME_UNICODE L"sqlncli" -#define SQLNCLI_FILE_NAME_VER_UNICODE L"sqlncli" -#define SQLNCLI_FILE_NAME_FULL_UNICODE L"sqlncli.dll" - -#endif // SQLNCLI_VER >= 1100 - -// define the character type agnostic constants -#if defined(_UNICODE) || defined(UNICODE) - -#define SQLNCLI_PRODUCT_NAME_FULL_VER SQLNCLI_PRODUCT_NAME_FULL_VER_UNICODE -#define SQLNCLI_PRODUCT_NAME_FULL SQLNCLI_PRODUCT_NAME_FULL_UNICODE -#define SQLNCLI_PRODUCT_NAME_SHORT_VER SQLNCLI_PRODUCT_NAME_SHORT_VER_UNICODE -#define SQLNCLI_PRODUCT_NAME_SHORT SQLNCLI_PRODUCT_NAME_SHORT_UNICODE - -#define SQLNCLI_FILE_NAME SQLNCLI_FILE_NAME_UNICODE -#define SQLNCLI_FILE_NAME_VER SQLNCLI_FILE_NAME_VER_UNICODE -#define SQLNCLI_FILE_NAME_FULL SQLNCLI_FILE_NAME_FULL_UNICODE - - -#else // _UNICODE || UNICODE - -#define SQLNCLI_PRODUCT_NAME_FULL_VER SQLNCLI_PRODUCT_NAME_FULL_VER_ANSI -#define SQLNCLI_PRODUCT_NAME_FULL SQLNCLI_PRODUCT_NAME_FULL_ANSI -#define SQLNCLI_PRODUCT_NAME_SHORT_VER SQLNCLI_PRODUCT_NAME_SHORT_VER_ANSI -#define SQLNCLI_PRODUCT_NAME_SHORT SQLNCLI_PRODUCT_NAME_SHORT_ANSI - -#define SQLNCLI_FILE_NAME SQLNCLI_FILE_NAME_ANSI -#define SQLNCLI_FILE_NAME_VER SQLNCLI_FILE_NAME_VER_ANSI -#define SQLNCLI_FILE_NAME_FULL SQLNCLI_FILE_NAME_FULL_ANSI - -#endif // _UNICODE || UNICODE - -#define SQLNCLI_DRIVER_NAME SQLNCLI_PRODUCT_NAME_SHORT_VER - - -#ifdef ODBCVER - -#ifdef __cplusplus -extern "C" { -#endif - -// max SQL Server identifier length -#define SQL_MAX_SQLSERVERNAME 128 - -// SQLSetConnectAttr driver specific defines. -// Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver usage. -// Connection attributes -#define SQL_COPT_SS_BASE 1200 -#define SQL_COPT_SS_REMOTE_PWD (SQL_COPT_SS_BASE+1) // dbrpwset SQLSetConnectOption only -#define SQL_COPT_SS_USE_PROC_FOR_PREP (SQL_COPT_SS_BASE+2) // Use create proc for SQLPrepare -#define SQL_COPT_SS_INTEGRATED_SECURITY (SQL_COPT_SS_BASE+3) // Force integrated security on login -#define SQL_COPT_SS_PRESERVE_CURSORS (SQL_COPT_SS_BASE+4) // Preserve server cursors after SQLTransact -#define SQL_COPT_SS_USER_DATA (SQL_COPT_SS_BASE+5) // dbgetuserdata/dbsetuserdata -#define SQL_COPT_SS_ENLIST_IN_DTC SQL_ATTR_ENLIST_IN_DTC // Enlist in a DTC transaction -#define SQL_COPT_SS_ENLIST_IN_XA SQL_ATTR_ENLIST_IN_XA // Enlist in a XA transaction -#define SQL_COPT_SS_FALLBACK_CONNECT (SQL_COPT_SS_BASE+10) // Enables FallBack connections -#define SQL_COPT_SS_PERF_DATA (SQL_COPT_SS_BASE+11) // Used to access SQL Server ODBC driver performance data -#define SQL_COPT_SS_PERF_DATA_LOG (SQL_COPT_SS_BASE+12) // Used to set the logfile name for the Performance data -#define SQL_COPT_SS_PERF_QUERY_INTERVAL (SQL_COPT_SS_BASE+13) // Used to set the query logging threshold in milliseconds. -#define SQL_COPT_SS_PERF_QUERY_LOG (SQL_COPT_SS_BASE+14) // Used to set the logfile name for saving queryies. -#define SQL_COPT_SS_PERF_QUERY (SQL_COPT_SS_BASE+15) // Used to start and stop query logging. -#define SQL_COPT_SS_PERF_DATA_LOG_NOW (SQL_COPT_SS_BASE+16) // Used to make a statistics log entry to disk. -#define SQL_COPT_SS_QUOTED_IDENT (SQL_COPT_SS_BASE+17) // Enable/Disable Quoted Identifiers -#define SQL_COPT_SS_ANSI_NPW (SQL_COPT_SS_BASE+18) // Enable/Disable ANSI NULL, Padding and Warnings -#define SQL_COPT_SS_BCP (SQL_COPT_SS_BASE+19) // Allow BCP usage on connection -#define SQL_COPT_SS_TRANSLATE (SQL_COPT_SS_BASE+20) // Perform code page translation -#define SQL_COPT_SS_ATTACHDBFILENAME (SQL_COPT_SS_BASE+21) // File name to be attached as a database -#define SQL_COPT_SS_CONCAT_NULL (SQL_COPT_SS_BASE+22) // Enable/Disable CONCAT_NULL_YIELDS_NULL -#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_FAILOVER_PARTNER (SQL_COPT_SS_BASE+25) // Failover partner server -#define SQL_COPT_SS_OLDPWD (SQL_COPT_SS_BASE+26) // Old Password, used when changing password during login -#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 -#define SQL_COPT_SS_SERVER_SPN (SQL_COPT_SS_BASE+29) // Server SPN -#define SQL_COPT_SS_FAILOVER_PARTNER_SPN (SQL_COPT_SS_BASE+30) // Failover partner server SPN -#define SQL_COPT_SS_INTEGRATED_AUTHENTICATION_METHOD (SQL_COPT_SS_BASE+31) // The integrated authentication method used for the connection -#define SQL_COPT_SS_MUTUALLY_AUTHENTICATED (SQL_COPT_SS_BASE+32) // Used to decide if the connection is mutually authenticated -#define SQL_COPT_SS_CLIENT_CONNECTION_ID (SQL_COPT_SS_BASE+33) // Post connection attribute used to get the ConnectionID -// Define old names -#define SQL_REMOTE_PWD SQL_COPT_SS_REMOTE_PWD -#define SQL_USE_PROCEDURE_FOR_PREPARE SQL_COPT_SS_USE_PROC_FOR_PREP -#define SQL_INTEGRATED_SECURITY SQL_COPT_SS_INTEGRATED_SECURITY -#define SQL_PRESERVE_CURSORS SQL_COPT_SS_PRESERVE_CURSORS - -// SQLSetStmtAttr SQL Server Native Client driver 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_CURRENT_COMMAND (SQL_SOPT_SS_BASE+1) // dbcurcmd SQLGetStmtOption only -#define SQL_SOPT_SS_HIDDEN_COLUMNS (SQL_SOPT_SS_BASE+2) // Expose FOR BROWSE hidden columns -#define SQL_SOPT_SS_NOBROWSETABLE (SQL_SOPT_SS_BASE+3) // Set NOBROWSETABLE option -#define SQL_SOPT_SS_REGIONALIZE (SQL_SOPT_SS_BASE+4) // Regionalize output character conversions -#define SQL_SOPT_SS_CURSOR_OPTIONS (SQL_SOPT_SS_BASE+5) // Server cursor options -#define SQL_SOPT_SS_NOCOUNT_STATUS (SQL_SOPT_SS_BASE+6) // Real vs. Not Real row count indicator -#define SQL_SOPT_SS_DEFER_PREPARE (SQL_SOPT_SS_BASE+7) // Defer prepare until necessary -#define SQL_SOPT_SS_QUERYNOTIFICATION_TIMEOUT (SQL_SOPT_SS_BASE+8) // Notification timeout -#define SQL_SOPT_SS_QUERYNOTIFICATION_MSGTEXT (SQL_SOPT_SS_BASE+9) // Notification message text -#define SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS (SQL_SOPT_SS_BASE+10)// SQL service broker name -#define SQL_SOPT_SS_PARAM_FOCUS (SQL_SOPT_SS_BASE+11)// Direct subsequent calls to parameter related methods to set properties on constituent columns/parameters of container types -#define SQL_SOPT_SS_NAME_SCOPE (SQL_SOPT_SS_BASE+12)// Sets name scope for subsequent catalog function calls -#define SQL_SOPT_SS_MAX_USED SQL_SOPT_SS_NAME_SCOPE -// Define old names -#define SQL_TEXTPTR_LOGGING SQL_SOPT_SS_TEXTPTR_LOGGING -#define SQL_COPT_SS_BASE_EX 1240 -#define SQL_COPT_SS_BROWSE_CONNECT (SQL_COPT_SS_BASE_EX+1) // Browse connect mode of operation -#define SQL_COPT_SS_BROWSE_SERVER (SQL_COPT_SS_BASE_EX+2) // Single Server browse request. -#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_BROWSE_CACHE_DATA (SQL_COPT_SS_BASE_EX+5) // Determines if we should cache browse info. Used when returned buffer is greater then ODBC limit (32K) -#define SQL_COPT_SS_RESET_CONNECTION (SQL_COPT_SS_BASE_EX+6) // When this option is set, we will perform connection reset on next packet -#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_EX_MAX_USED SQL_COPT_SS_MULTISUBNET_FAILOVER - -// SQLColAttributes driver specific defines. -// SQLSetDescField/SQLGetDescField driver specific defines. -// Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver 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_DESC_BASE_COLUMN_NAME_OLD (SQL_CA_SS_BASE+13) // This is defined at another location. -#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_UDT_ASSEMBLY_TYPE_NAME (SQL_CA_SS_BASE+21) // Qualified name of the assembly containing the UDT class -#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 - -#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+36) - -// Defines returned by SQL_ATTR_CURSOR_TYPE/SQL_CURSOR_TYPE -#define SQL_CURSOR_FAST_FORWARD_ONLY 8 // Only returned by SQLGetStmtAttr/Option -// Defines for use with SQL_COPT_SS_USE_PROC_FOR_PREP -#define SQL_UP_OFF 0L // Procedures won't be used for prepare -#define SQL_UP_ON 1L // Procedures will be used for prepare -#define SQL_UP_ON_DROP 2L // Temp procedures will be explicitly dropped -#define SQL_UP_DEFAULT SQL_UP_ON -// 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_PRESERVE_CURSORS -#define SQL_PC_OFF 0L // Cursors are closed on SQLTransact -#define SQL_PC_ON 1L // Cursors remain open on SQLTransact -#define SQL_PC_DEFAULT SQL_PC_OFF -// Defines for use with SQL_COPT_SS_USER_DATA -#define SQL_UD_NOTSET NULL // No user data pointer set -// 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_COPT_SS_FALLBACK_CONNECT - Pre-Connect Option only -#define SQL_FB_OFF 0L // FallBack connections are disabled -#define SQL_FB_ON 1L // FallBack connections are enabled -#define SQL_FB_DEFAULT SQL_FB_OFF -// Defines for use with SQL_COPT_SS_BCP - Pre-Connect Option only -#define SQL_BCP_OFF 0L // BCP is not allowed on connection -#define SQL_BCP_ON 1L // BCP is allowed on connection -#define SQL_BCP_DEFAULT SQL_BCP_OFF -// Defines for use with SQL_COPT_SS_QUOTED_IDENT -#define SQL_QI_OFF 0L // Quoted identifiers are enable -#define SQL_QI_ON 1L // Quoted identifiers are disabled -#define SQL_QI_DEFAULT SQL_QI_ON -// Defines for use with SQL_COPT_SS_ANSI_NPW - Pre-Connect Option only -#define SQL_AD_OFF 0L // ANSI NULLs, Padding and Warnings are enabled -#define SQL_AD_ON 1L // ANSI NULLs, Padding and Warnings are disabled -#define SQL_AD_DEFAULT SQL_AD_ON -// Defines for use with SQL_COPT_SS_CONCAT_NULL - Pre-Connect Option only -#define SQL_CN_OFF 0L // CONCAT_NULL_YIELDS_NULL is off -#define SQL_CN_ON 1L // CONCAT_NULL_YIELDS_NULL is on -#define SQL_CN_DEFAULT SQL_CN_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_HIDDEN_COLUMNS -#define SQL_HC_OFF 0L // FOR BROWSE columns are hidden -#define SQL_HC_ON 1L // FOR BROWSE columns are exposed -#define SQL_HC_DEFAULT SQL_HC_OFF -// 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_REGIONALIZE -#define SQL_RE_OFF 0L // No regionalization occurs on output character conversions -#define SQL_RE_ON 1L // Regionalization occurs on output character conversions -#define SQL_RE_DEFAULT SQL_RE_OFF -// Defines for use with SQL_SOPT_SS_CURSOR_OPTIONS -#define SQL_CO_OFF 0L // Clear all cursor options -#define SQL_CO_FFO 1L // Fast-forward cursor will be used -#define SQL_CO_AF 2L // Autofetch on cursor open -#define SQL_CO_FFO_AF (SQL_CO_FFO|SQL_CO_AF) // Fast-forward cursor with autofetch -#define SQL_CO_FIREHOSE_AF 4L // Auto fetch on fire-hose cursors -#define SQL_CO_DEFAULT SQL_CO_OFF -//SQL_SOPT_SS_NOCOUNT_STATUS -#define SQL_NC_OFF 0L -#define SQL_NC_ON 1L -//SQL_SOPT_SS_DEFER_PREPARE -#define SQL_DP_OFF 0L -#define SQL_DP_ON 1L -//SQL_SOPT_SS_NAME_SCOPE -#define SQL_SS_NAME_SCOPE_TABLE 0L -#define SQL_SS_NAME_SCOPE_TABLE_TYPE 1L -#define SQL_SS_NAME_SCOPE_EXTENDED 2L -#define SQL_SS_NAME_SCOPE_SPARSE_COLUMN_SET 3L -#define SQL_SS_NAME_SCOPE_DEFAULT SQL_SS_NAME_SCOPE_TABLE -//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_BROWSE_CONNECT -#define SQL_MORE_INFO_NO 0L -#define SQL_MORE_INFO_YES 1L -//SQL_COPT_SS_BROWSE_CACHE_DATA -#define SQL_CACHE_DATA_NO 0L -#define SQL_CACHE_DATA_YES 1L -//SQL_COPT_SS_RESET_CONNECTION -#define SQL_RESET_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 SQL Server Native Client driver 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 -#define SQL_C_SS_TIME2 (SQL_C_TYPES_EXTENDED+0) -#define SQL_C_SS_TIMESTAMPOFFSET (SQL_C_TYPES_EXTENDED+1) - -#ifndef SQLNCLI_NO_BCP -// Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application -// and you want to exclude the BCP-related definitions in this header file. - -// SQL Server Data Type defines. -// New types for SQL 6.0 and later servers -#define SQLTEXT 0x23 -#define SQLVARBINARY 0x25 -#define SQLINTN 0x26 -#define SQLVARCHAR 0x27 -#define SQLBINARY 0x2d -#define SQLIMAGE 0x22 -#define SQLCHARACTER 0x2f -#define SQLINT1 0x30 -#define SQLBIT 0x32 -#define SQLINT2 0x34 -#define SQLINT4 0x38 -#define SQLMONEY 0x3c -#define SQLDATETIME 0x3d -#define SQLFLT8 0x3e -#define SQLFLTN 0x6d -#define SQLMONEYN 0x6e -#define SQLDATETIMN 0x6f -#define SQLFLT4 0x3b -#define SQLMONEY4 0x7a -#define SQLDATETIM4 0x3a -// New types for SQL 6.0 and later servers -#define SQLDECIMAL 0x6a -#define SQLNUMERIC 0x6c -// New types for SQL 7.0 and later servers -#define SQLUNIQUEID 0x24 -#define SQLBIGCHAR 0xaf -#define SQLBIGVARCHAR 0xa7 -#define SQLBIGBINARY 0xad -#define SQLBIGVARBINARY 0xa5 -#define SQLBITN 0x68 -#define SQLNCHAR 0xef -#define SQLNVARCHAR 0xe7 -#define SQLNTEXT 0x63 -// New types for SQL 2000 and later servers -#define SQLINT8 0x7f -#define SQLVARIANT 0x62 -// New types for SQL 2005 and later servers -#define SQLUDT 0xf0 -#define SQLXML 0xf1 -// New types for SQL 2008 and later servers -#define SQLTABLE 0xf3 -#define SQLDATEN 0x28 -#define SQLTIMEN 0x29 -#define SQLDATETIME2N 0x2a -#define SQLDATETIMEOFFSETN 0x2b -// Define old names -#define SQLDECIMALN 0x6a -#define SQLNUMERICN 0x6c -#endif // SQLNCLI_NO_BCP - -// 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 -// SQLGetInfo driver specific defines. -// Microsoft has 1151 thru 1200 reserved for Microsoft SQL Server Native Client driver usage. -#define SQL_INFO_SS_FIRST 1199 -#define SQL_INFO_SS_NETLIB_NAMEW (SQL_INFO_SS_FIRST+0) // dbprocinfo -#define SQL_INFO_SS_NETLIB_NAMEA (SQL_INFO_SS_FIRST+1) // dbprocinfo -#define SQL_INFO_SS_MAX_USED SQL_INFO_SS_NETLIB_NAMEA -#ifdef UNICODE -#define SQL_INFO_SS_NETLIB_NAME SQL_INFO_SS_NETLIB_NAMEW -#else -#define SQL_INFO_SS_NETLIB_NAME SQL_INFO_SS_NETLIB_NAMEA -#endif - -// SQLGetDiagField driver specific defines. -// Microsoft has -1150 thru -1199 reserved for Microsoft SQL Server Native Client driver 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 SQL Server Native Client driver 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 -// Internal server datatypes - used when binding to SQL_C_BINARY -#ifndef MAXNUMERICLEN // Resolve ODS/DBLib conflicts -// DB-Library datatypes -#define DBMAXCHAR (8000+1) // Max length of DBVARBINARY and DBVARCHAR, etc. +1 for zero byte -#define MAXNAME (SQL_MAX_SQLSERVERNAME+1) // Max server identifier length including zero byte -#ifdef UNICODE -typedef wchar_t DBCHAR; -#else -typedef char DBCHAR; - -#endif -typedef short SQLSMALLINT; - -typedef unsigned short SQLUSMALLINT; - -typedef unsigned char DBBINARY; - -typedef unsigned char DBTINYINT; - -typedef short DBSMALLINT; - -typedef unsigned short DBUSMALLINT; - -typedef double DBFLT8; - -typedef unsigned char DBBIT; - -typedef unsigned char DBBOOL; - -typedef float DBFLT4; - -typedef DBFLT4 DBREAL; - -typedef UINT DBUBOOL; - -typedef struct dbmoney - { - LONG mnyhigh; - ULONG mnylow; - } DBMONEY; - -typedef struct dbdatetime - { - LONG dtdays; - ULONG dttime; - } DBDATETIME; - -typedef struct dbdatetime4 - { - USHORT numdays; - USHORT nummins; - } DBDATETIM4; - -typedef LONG DBMONEY4; - -#include // 8-byte structure packing - -// New Date Time Structures -// New Structure for TIME2 -typedef struct tagSS_TIME2_STRUCT -{ - SQLUSMALLINT hour; - SQLUSMALLINT minute; - SQLUSMALLINT second; - SQLUINTEGER fraction; -} SQL_SS_TIME2_STRUCT; -// New Structure for TIMESTAMPOFFSET -typedef struct tagSS_TIMESTAMPOFFSET_STRUCT -{ - SQLSMALLINT year; - SQLUSMALLINT month; - SQLUSMALLINT day; - SQLUSMALLINT hour; - SQLUSMALLINT minute; - SQLUSMALLINT second; - SQLUINTEGER fraction; - SQLSMALLINT timezone_hour; - SQLSMALLINT timezone_minute; -} SQL_SS_TIMESTAMPOFFSET_STRUCT; - -typedef struct tagDBTIME2 -{ - USHORT hour; - USHORT minute; - USHORT second; - ULONG fraction; -} DBTIME2; - -typedef struct tagDBTIMESTAMPOFFSET -{ - SHORT year; - USHORT month; - USHORT day; - USHORT hour; - USHORT minute; - USHORT second; - ULONG fraction; - SHORT timezone_hour; - SHORT timezone_minute; -} DBTIMESTAMPOFFSET; - -#include // restore original structure packing - -// Money value *10,000 -#define DBNUM_PREC_TYPE BYTE -#define DBNUM_SCALE_TYPE BYTE -#define DBNUM_VAL_TYPE BYTE - -#if (ODBCVER < 0x0300) -#define MAXNUMERICLEN 16 -typedef struct dbnumeric // Internal representation of NUMERIC data type -{ - DBNUM_PREC_TYPE precision; // Precision - DBNUM_SCALE_TYPE scale; // Scale - BYTE sign; // Sign (1 if positive, 0 if negative) - DBNUM_VAL_TYPE val[MAXNUMERICLEN];// Value -} DBNUMERIC; -typedef DBNUMERIC DBDECIMAL;// Internal representation of DECIMAL data type -#else // Use ODBC 3.0 definitions since same as DBLib -#define MAXNUMERICLEN SQL_MAX_NUMERIC_LEN -typedef SQL_NUMERIC_STRUCT DBNUMERIC; -typedef SQL_NUMERIC_STRUCT DBDECIMAL; -#endif // ODCBVER -#endif // MAXNUMERICLEN - -#ifndef INT -typedef int INT; -typedef LONG DBINT; -typedef DBINT * LPDBINT; -#ifndef _LPCBYTE_DEFINED -#define _LPCBYTE_DEFINED -typedef BYTE const* LPCBYTE; -#endif //_LPCBYTE_DEFINED -#endif // INT -/************************************************************************** -This struct is a global used for gathering statistical data on the driver. -Access to this structure is controlled via the pStatCrit; -***************************************************************************/ -typedef struct sqlperf -{ - // Application Profile Statistics - DWORD TimerResolution; - DWORD SQLidu; - DWORD SQLiduRows; - DWORD SQLSelects; - DWORD SQLSelectRows; - DWORD Transactions; - DWORD SQLPrepares; - DWORD ExecDirects; - DWORD SQLExecutes; - DWORD CursorOpens; - DWORD CursorSize; - DWORD CursorUsed; - LDOUBLE PercentCursorUsed; - LDOUBLE AvgFetchTime; - LDOUBLE AvgCursorSize; - LDOUBLE AvgCursorUsed; - DWORD SQLFetchTime; - DWORD SQLFetchCount; - DWORD CurrentStmtCount; - DWORD MaxOpenStmt; - DWORD SumOpenStmt; - // Connection Statistics - DWORD CurrentConnectionCount; - DWORD MaxConnectionsOpened; - DWORD SumConnectionsOpened; - DWORD SumConnectiontime; - LDOUBLE AvgTimeOpened; - // Network Statistics - DWORD ServerRndTrips; - DWORD BuffersSent; - DWORD BuffersRec; - DWORD BytesSent; - DWORD BytesRec; - // Time Statistics; - DWORD msExecutionTime; - DWORD msNetWorkServerTime; -} SQLPERF; -// The following are options for SQL_COPT_SS_PERF_DATA and SQL_COPT_SS_PERF_QUERY -#define SQL_PERF_START 1 // Starts the driver sampling performance data. -#define SQL_PERF_STOP 2 // Stops the counters from sampling performance data. -// The following are defines for SQL_COPT_SS_PERF_DATA_LOG -#define SQL_SS_DL_DEFAULT TEXT("STATS.LOG") -// The following are defines for SQL_COPT_SS_PERF_QUERY_LOG -#define SQL_SS_QL_DEFAULT TEXT("QUERY.LOG") -// The following are defines for SQL_COPT_SS_PERF_QUERY_INTERVAL -#define SQL_SS_QI_DEFAULT 30000 // 30,000 milliseconds - -#ifndef SQLNCLI_NO_BCP -// Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application -// and you want to exclude the BCP-related definitions in this header file. - -// ODBC BCP prototypes and defines -// Return codes -#define SUCCEED 1 -#define FAIL 0 -#define SUCCEED_ABORT 2 -#define SUCCEED_ASYNC 3 -// Transfer directions -#define DB_IN 1 // Transfer from client to server -#define DB_OUT 2 // Transfer from server to client -// bcp_control option -#define BCPMAXERRS 1 // Sets max errors allowed -#define BCPFIRST 2 // Sets first row to be copied out -#define BCPLAST 3 // Sets number of rows to be copied out -#define BCPBATCH 4 // Sets input batch size -#define BCPKEEPNULLS 5 // Sets to insert NULLs for empty input values -#define BCPABORT 6 // Sets to have bcpexec return SUCCEED_ABORT -#define BCPODBC 7 // Sets ODBC canonical character output -#define BCPKEEPIDENTITY 8 // Sets IDENTITY_INSERT on -#if SQLNCLI_VER < 1000 -#define BCP6xFILEFMT 9 // DEPRECATED: Sets 6x file format on -#endif -#define BCPHINTSA 10 // Sets server BCP hints (ANSI string) -#define BCPHINTSW 11 // Sets server BCP hints (UNICODE string) -#define BCPFILECP 12 // Sets clients code page for the file -#define BCPUNICODEFILE 13 // Sets that the file contains unicode header -#define BCPTEXTFILE 14 // Sets BCP mode to expect a text file and to detect Unicode or ANSI automatically -#define BCPFILEFMT 15 // Sets file format version -#define BCPFMTXML 16 // Sets the format file type to xml -#define BCPFIRSTEX 17 // Starting Row for BCP operation (64 bit) -#define BCPLASTEX 18 // Ending Row for BCP operation (64 bit) -#define BCPROWCOUNT 19 // Total Number of Rows Copied (64 bit) -#define BCPDELAYREADFMT 20 // Delay reading format file unil bcp_exec -// BCPFILECP values -// Any valid code page that is installed on the client can be passed plus: -#define BCPFILECP_ACP 0 // Data in file is in Windows code page -#define BCPFILECP_OEMCP 1 // Data in file is in OEM code page (default) -#define BCPFILECP_RAW (-1)// Data in file is in Server code page (no conversion) -// bcp_collen definition -#define SQL_VARLEN_DATA (-10) // Use default length for column -// BCP column format properties -#define BCP_FMT_TYPE 0x01 -#define BCP_FMT_INDICATOR_LEN 0x02 -#define BCP_FMT_DATA_LEN 0x03 -#define BCP_FMT_TERMINATOR 0x04 -#define BCP_FMT_SERVER_COL 0x05 -#define BCP_FMT_COLLATION 0x06 -#define BCP_FMT_COLLATION_ID 0x07 -// bcp_setbulkmode properties -#define BCP_OUT_CHARACTER_MODE 0x01 -#define BCP_OUT_WIDE_CHARACTER_MODE 0x02 -#define BCP_OUT_NATIVE_TEXT_MODE 0x03 -#define BCP_OUT_NATIVE_MODE 0x04 - - - -// BCP functions -DBINT SQL_API bcp_batch (HDBC); -RETCODE SQL_API bcp_bind (HDBC, LPCBYTE, INT, DBINT, LPCBYTE, INT, INT, INT); -RETCODE SQL_API bcp_colfmt (HDBC, INT, BYTE, INT, DBINT, LPCBYTE, INT, INT); -RETCODE SQL_API bcp_collen (HDBC, DBINT, INT); -RETCODE SQL_API bcp_colptr (HDBC, LPCBYTE, INT); -RETCODE SQL_API bcp_columns (HDBC, INT); -RETCODE SQL_API bcp_control (HDBC, INT, void *); -DBINT SQL_API bcp_done (HDBC); -RETCODE SQL_API bcp_exec (HDBC, LPDBINT); -RETCODE SQL_API bcp_getcolfmt (HDBC, INT, INT, void *, INT, INT *); -RETCODE SQL_API bcp_initA (HDBC, LPCSTR, LPCSTR, LPCSTR, INT); -RETCODE SQL_API bcp_initW (HDBC, LPCWSTR, LPCWSTR, LPCWSTR, INT); -RETCODE SQL_API bcp_moretext (HDBC, DBINT, LPCBYTE); -RETCODE SQL_API bcp_readfmtA (HDBC, LPCSTR); -RETCODE SQL_API bcp_readfmtW (HDBC, LPCWSTR); -RETCODE SQL_API bcp_sendrow (HDBC); -RETCODE SQL_API bcp_setbulkmode (HDBC, INT, _In_reads_bytes_(cbField) void*, INT cbField, _In_reads_bytes_(cbRow) void *, INT cbRow); -RETCODE SQL_API bcp_setcolfmt (HDBC, INT, INT, void *, INT); -RETCODE SQL_API bcp_writefmtA (HDBC, LPCSTR); -RETCODE SQL_API bcp_writefmtW (HDBC, LPCWSTR); -CHAR* SQL_API dbprtypeA (INT); -WCHAR* SQL_API dbprtypeW (INT); -CHAR* SQL_API bcp_gettypenameA (INT, DBBOOL); -WCHAR* SQL_API bcp_gettypenameW (INT, DBBOOL); - -#ifdef UNICODE -#define bcp_init bcp_initW -#define bcp_readfmt bcp_readfmtW -#define bcp_writefmt bcp_writefmtW -#define dbprtype dbprtypeW -#define bcp_gettypename bcp_gettypenameW -#define BCPHINTS BCPHINTSW -#else -#define bcp_init bcp_initA -#define bcp_readfmt bcp_readfmtA -#define bcp_writefmt bcp_writefmtA -#define dbprtype dbprtypeA -#define bcp_gettypename bcp_gettypenameA -#define BCPHINTS BCPHINTSA -#endif // UNICODE - -#endif // SQLNCLI_NO_BCP - -// The following options have been deprecated -#define SQL_FAST_CONNECT (SQL_COPT_SS_BASE+0) -// Defines for use with SQL_FAST_CONNECT - only useable before connecting -#define SQL_FC_OFF 0L // Fast connect is off -#define SQL_FC_ON 1L // Fast connect is on -#define SQL_FC_DEFAULT SQL_FC_OFF -#define SQL_COPT_SS_ANSI_OEM (SQL_COPT_SS_BASE+6) -#define SQL_AO_OFF 0L -#define SQL_AO_ON 1L -#define SQL_AO_DEFAULT SQL_AO_OFF -#define SQL_CA_SS_BASE_COLUMN_NAME SQL_DESC_BASE_COLUMN_NAME - - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // ODBCVER - - - -#ifdef __cplusplus -extern "C" { -#endif -#include - -//The following facilitates opening a handle to a SQL filestream -typedef enum _SQL_FILESTREAM_DESIRED_ACCESS { - SQL_FILESTREAM_READ = 0, - SQL_FILESTREAM_WRITE = 1, - SQL_FILESTREAM_READWRITE = 2 -} SQL_FILESTREAM_DESIRED_ACCESS; -#define SQL_FILESTREAM_OPEN_FLAG_ASYNC 0x00000001L -#define SQL_FILESTREAM_OPEN_FLAG_NO_BUFFERING 0x00000002L -#define SQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGH 0x00000004L -#define SQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCAN 0x00000008L -#define SQL_FILESTREAM_OPEN_FLAG_RANDOM_ACCESS 0x00000010L - - -HANDLE __stdcall OpenSqlFilestream ( - LPCWSTR FilestreamPath, - SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess, - ULONG OpenOptions, - _In_reads_bytes_(FilestreamTransactionContextLength) - LPBYTE FilestreamTransactionContext, - SSIZE_T FilestreamTransactionContextLength, - PLARGE_INTEGER AllocationSize); -#define FSCTL_SQL_FILESTREAM_FETCH_OLD_CONTENT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 2392, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#ifdef __cplusplus -} // extern "C" -#endif - - -#endif //__sqlncli_h__ - -#define SQL_COPT_SS_CONNECT_RETRY_COUNT (SQL_COPT_SS_BASE+34) // Post connection attribute used to get ConnectRetryCount -#define SQL_COPT_SS_CONNECT_RETRY_INTERVAL (SQL_COPT_SS_BASE+35) // Post connection attribute used to get ConnectRetryInterval -#ifdef SQL_COPT_SS_MAX_USED -#undef SQL_COPT_SS_MAX_USED -#endif // SQL_COPT_SS_MAX_USED -#define SQL_COPT_SS_MAX_USED SQL_COPT_SS_CONNECT_RETRY_INTERVAL - - -#ifndef _SQLUSERINSTANCE_H_ -#define _SQLUSERINSTANCE_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -// Recommended buffer size to store a LocalDB connection string -#define LOCALDB_MAX_SQLCONNECTION_BUFFER_SIZE 260 - -// type definition for LocalDBCreateInstance function -typedef HRESULT __cdecl FnLocalDBCreateInstance ( - // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) - _In_z_ PCWSTR wszVersion, - // I the instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags -); - -// type definition for pointer to LocalDBCreateInstance function -typedef FnLocalDBCreateInstance* PFnLocalDBCreateInstance; - -// type definition for LocalDBStartInstance function -typedef HRESULT __cdecl FnLocalDBStartInstance ( - // I the LocalDB instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags, - // O the buffer to store the connection string to the LocalDB instance - _Out_writes_opt_z_(*lpcchSqlConnection) LPWSTR wszSqlConnection, - // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. - _Inout_opt_ LPDWORD lpcchSqlConnection -); - -// type definition for pointer to LocalDBStartInstance function -typedef FnLocalDBStartInstance* PFnLocalDBStartInstance; - -// Flags for the LocalDBFormatMessage function -#define LOCALDB_TRUNCATE_ERR_MESSAGE 0x0001L - -// type definition for LocalDBFormatMessage function -typedef HRESULT __cdecl FnLocalDBFormatMessage( - // I the LocalDB error code - _In_ HRESULT hrLocalDB, - // I Available flags: - // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, - // the error message will be truncated to fit into the buffer - _In_ DWORD dwFlags, - // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) - _In_ DWORD dwLanguageId, - // O the buffer to store the LocalDB error message - _Out_writes_z_(*lpcchMessage) LPWSTR wszMessage, - // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. If the function succeeds - // contains the number of characters in the message, excluding the trailing null - _Inout_ LPDWORD lpcchMessage -); - -// type definition for function pointer to LocalDBFormatMessage function -typedef FnLocalDBFormatMessage* PFnLocalDBFormatMessage; - - -// MessageId: LOCALDB_ERROR_NOT_INSTALLED -// -// MessageText: -// -// LocalDB is not installed. -// -#define LOCALDB_ERROR_NOT_INSTALLED ((HRESULT)0x89C50116L) - -//--------------------------------------------------------------------- -// Function: LocalDBCreateInstance -// -// Description: This function will create the new LocalDB instance. -// -// Available Flags: -// No flags available. Reserved for future use. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_VERSION, if the version parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INVALID_OPERATION, if the user tries to create a default instance -// LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH -// LOCALDB_ERROR_VERSION_REQUESTED_NOT_INSTALLED, if the specified service level is not installed -// LOCALDB_ERROR_INSTANCE_FOLDER_ALREADY_EXISTS, if the instance folder already exists and is not empty -// LOCALDB_ERROR_INSTANCE_EXISTS_WITH_LOWER_VERSION, if the specified instance already exists but with lower version -// LOCALDB_ERROR_CANNOT_CREATE_INSTANCE_FOLDER, if a folder cannot be created under %userprofile% -// LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified -// LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created -// LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. -// LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted -// -FnLocalDBCreateInstance LocalDBCreateInstance; - -//--------------------------------------------------------------------- -// Function: LocalDBStartInstance -// -// Description: This function will start the given LocalDB instance. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_CONNECTION, if the wszSqlConnection parameter is NULL -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the buffer wszSqlConnection is too small -// LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH - -// LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified -// LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created -// LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. -// LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted -// -FnLocalDBStartInstance LocalDBStartInstance; - -// type definition for LocalDBStopInstance function -typedef HRESULT __cdecl FnLocalDBStopInstance ( - // I the LocalDB instance name - _In_z_ PCWSTR pInstanceName, - // I Available flags: - // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately - // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option - _In_ DWORD dwFlags, - // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately - // without waiting for LocalDB instance to stop - _In_ ULONG ulTimeout -); - -// type definition for pointer to LocalDBStopInstance function -typedef FnLocalDBStopInstance* PFnLocalDBStopInstance; - -// Flags for the StopLocalDBInstance function -#define LOCALDB_SHUTDOWN_KILL_PROCESS 0x0001L -#define LOCALDB_SHUTDOWN_WITH_NOWAIT 0x0002L - -//--------------------------------------------------------------------- -// Function: LocalDBStopInstance -// -// Description: This function will shutdown the given LocalDB instance. -// If the flag LOCALDB_SHUTDOWN_KILL_PROCESS is set, the LocalDB instance will be killed immediately. -// IF the flag LOCALDB_SHUTDOWN_WITH_NOWAIT is set, the LocalDB instance will shutdown with NOWAIT option. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_WAIT_TIMEOUT - if this function has not finished in given time -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBStopInstance LocalDBStopInstance; - -// type definition for LocalDBDeleteInstance function -typedef HRESULT __cdecl FnLocalDBDeleteInstance ( - // I the LocalDB instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags -); - -// type definition for pointer to LocalDBDeleteInstance function -typedef FnLocalDBDeleteInstance* PFnLocalDBDeleteInstance; - -//--------------------------------------------------------------------- -// Function: LocalDBDeleteInstance -// -// Description: This function will remove the given LocalDB instance. If the given instance is running this function will -// fail with the error code LOCALDB_ERROR_INSTANCE_BUSY. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INSTANCE_BUSY, if the given instance is running -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBDeleteInstance LocalDBDeleteInstance; - -// Function: LocalDBFormatMessage -// -// Description: This function will return the localized textual description for the given LocalDB error -// -// Available Flags: -// LOCALDB_TRUNCATE_ERR_MESSAGE - the error message should be truncated to fit into the provided buffer -// -// Return Value: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_UNKNOWN_HRESULT, if the given HRESULT is unknown -// LOCALDB_ERROR_UNKNOWN_LANGUAGE_ID, if the given language id is unknown (0 is recommended for the // default language) -// LOCALDB_ERROR_UNKNOWN_ERROR_CODE, if the LocalDB error code is unknown -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the input buffer is too short and LOCALDB_TRUNCATE_ERR_MESSAGE flag -// is not set -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBFormatMessage LocalDBFormatMessage; - -#define MAX_LOCALDB_INSTANCE_NAME_LENGTH 128 -#define MAX_LOCALDB_PARENT_INSTANCE_LENGTH MAX_INSTANCE_NAME - -typedef WCHAR TLocalDBInstanceName[MAX_LOCALDB_INSTANCE_NAME_LENGTH + 1]; -typedef TLocalDBInstanceName* PTLocalDBInstanceName; - -// type definition for LocalDBGetInstances function -typedef HRESULT __cdecl FnLocalDBGetInstances( - // O buffer for a LocalDB instance names - _Out_ PTLocalDBInstanceName pInstanceNames, - // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, - // has the number of existing LocalDB instances - _Inout_ LPDWORD lpdwNumberOfInstances -); - -// type definition for pointer to LocalDBGetInstances function -typedef FnLocalDBGetInstances* PFnLocalDBGetInstances; - -// Function: LocalDBGetInstances -// -// Description: This function returns names for all existing Local DB instances -// -// Usage Example: -// DWORD dwN = 0; -// LocalDBGetInstances(NULL, &dwN); - -// PTLocalDBInstanceName insts = (PTLocalDBInstanceName) malloc(dwN * sizeof(TLocalDBInstanceName)); -// LocalDBGetInstances(insts, &dwN); - -// for (int i = 0; i < dwN; i++) -// wprintf(L"%s\n", insts[i]); -// -// Return values: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBGetInstances LocalDBGetInstances; - -// SID string format: S - Revision(1B) - Authority ID (6B) {- Sub authority ID (4B)} * max 15 sub-authorities = 1 + 1 + 3 + 1 + 15 + (1 + 10) * 15 -#define MAX_STRING_SID_LENGTH 186 - -#pragma pack(push) -#pragma pack(8) - -// DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetInstanceInfo in sqluserinstance.cpp file. -// -typedef struct _LocalDBInstanceInfo -{ - DWORD cbLocalDBInstanceInfoSize; - TLocalDBInstanceName wszInstanceName; - BOOL bExists; - BOOL bConfigurationCorrupted; - BOOL bIsRunning; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwBuild; - DWORD dwRevision; - FILETIME ftLastStartDateUTC; - WCHAR wszConnection[LOCALDB_MAX_SQLCONNECTION_BUFFER_SIZE]; - BOOL bIsShared; - TLocalDBInstanceName wszSharedInstanceName; - WCHAR wszOwnerSID[MAX_STRING_SID_LENGTH + 1]; - BOOL bIsAutomatic; -} LocalDBInstanceInfo; - -#pragma pack(pop) - -typedef LocalDBInstanceInfo* PLocalDBInstanceInfo; - -// type definition for LocalDBGetInstanceInfo function -typedef HRESULT __cdecl FnLocalDBGetInstanceInfo( - // I the LocalDB instance name - _In_z_ PCWSTR wszInstanceName, - // O instance information - _Out_ PLocalDBInstanceInfo pInfo, - // I Size of LocalDBInstanceInfo structure in bytes - _In_ DWORD cbInfo); - -// type definition for pointer to LocalDBGetInstances function -typedef FnLocalDBGetInstanceInfo* PFnLocalDBGetInstanceInfo; - -// Function: LocalDBGetInstanceInfo -// -// Description: This function returns information about the given instance. -// -// Return values: -// S_OK, if the function succeeds -// -// ERROR_INVALID_PARAMETER, if some of the parameters is invalid -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBGetInstanceInfo LocalDBGetInstanceInfo; - -// Version has format: Major.Minor[.Build[.Revision]]. Each of components is 32bit integer which is at most 40 digits and 3 dots -// -#define MAX_LOCALDB_VERSION_LENGTH 43 - -typedef WCHAR TLocalDBVersion[MAX_LOCALDB_VERSION_LENGTH + 1]; -typedef TLocalDBVersion* PTLocalDBVersion; - -// type definition for LocalDBGetVersions function -typedef HRESULT __cdecl FnLocalDBGetVersions( - // O buffer for installed LocalDB versions - _Out_ PTLocalDBVersion pVersions, - // I/O on input has the number slots for versions in the pVersions buffer. On output, - // has the number of existing LocalDB versions - _Inout_ LPDWORD lpdwNumberOfVersions -); - -// type definition for pointer to LocalDBGetVersions function -typedef FnLocalDBGetVersions* PFnLocalDBGetVersions; - -// Function: LocalDBGetVersions -// -// Description: This function returns all installed LocalDB versions. Returned versions will be in format Major.Minor -// -// Usage Example: -// DWORD dwN = 0; -// LocalDBGetVersions(NULL, &dwN); - -// PTLocalDBVersion versions = (PTLocalDBVersion) malloc(dwN * sizeof(TLocalDBVersion)); -// LocalDBGetVersions(insts, &dwN); - -// for (int i = 0; i < dwN; i++) -// wprintf(L"%s\n", insts[i]); -// -// Return values: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurs. -// -FnLocalDBGetVersions LocalDBGetVersions; - -#pragma pack(push) -#pragma pack(8) - -// DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetVersionInfo in sqluserinstance.cpp file. -// -typedef struct _LocalDBVersionInfo -{ - DWORD cbLocalDBVersionInfoSize; - TLocalDBVersion wszVersion; - BOOL bExists; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwBuild; - DWORD dwRevision; -} LocalDBVersionInfo; - -#pragma pack(pop) - -typedef LocalDBVersionInfo* PLocalDBVersionInfo; - -// type definition for LocalDBGetVersionInfo function -typedef HRESULT __cdecl FnLocalDBGetVersionInfo( - // I LocalDB version string - _In_z_ PCWSTR wszVersion, - // O version information - _Out_ PLocalDBVersionInfo pVersionInfo, - // I Size of LocalDBVersionInfo structure in bytes - _In_ DWORD cbVersionInfo -); - -// type definition for pointer to LocalDBGetVersionInfo function -typedef FnLocalDBGetVersionInfo* PFnLocalDBGetVersionInfo; - -// Function: LocalDBGetVersionInfo -// -// Description: This function returns information about the given LocalDB version -// -// Return values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INTERNAL_ERROR, if some internal error occurred -// LOCALDB_ERROR_INVALID_PARAMETER, if a input parameter is invalid -// -FnLocalDBGetVersionInfo LocalDBGetVersionInfo; - -typedef HRESULT __cdecl FnLocalDBStartTracing(); -typedef FnLocalDBStartTracing* PFnLocalDBStartTracing; - -// Function: LocalDBStartTracing -// -// Description: This function will write in registry that Tracing sessions should be started for the current user. -// -// Return values: -// S_OK - on success -// Propper HRESULT in case of failure -// -FnLocalDBStartTracing LocalDBStartTracing; - -typedef HRESULT __cdecl FnLocalDBStopTracing(); -typedef FnLocalDBStopTracing* PFnFnLocalDBStopTracing; - -// Function: LocalDBStopTracing -// -// Description: This function will write in registry that Tracing sessions should be stopped for the current user. -// -// Return values: -// S_OK - on success -// Propper HRESULT in case of failure -// -FnLocalDBStopTracing LocalDBStopTracing; - -// type definition for LocalDBShareInstance function -typedef HRESULT __cdecl FnLocalDBShareInstance( - // I the SID of the LocalDB instance owner - _In_opt_ PSID pOwnerSID, - // I the private name of LocalDB instance which should be shared - _In_z_ PCWSTR wszPrivateLocalDBInstanceName, - // I the public shared name - _In_z_ PCWSTR wszSharedName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags); - -// type definition for pointer to LocalDBShareInstance function -typedef FnLocalDBShareInstance* PFnLocalDBShareInstance; - -// Function: LocalDBShareInstance -// -// Description: This function will share the given private instance of the given user with the given shared name. -// This function has to be executed elevated. -// -// Return values: -// HRESULT -// -FnLocalDBShareInstance LocalDBShareInstance; - -// type definition for LocalDBUnshareInstance function -typedef HRESULT __cdecl FnLocalDBUnshareInstance( - // I the LocalDB instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags); - -// type definition for pointer to LocalDBUnshareInstance function -typedef FnLocalDBUnshareInstance* PFnLocalDBUnshareInstance; - -// Function: LocalDBUnshareInstance -// -// Description: This function unshares the given LocalDB instance. -// If a shared name is given then that shared instance will be unshared. -// If a private name is given then we will check if the caller -// shares a private instance with the given private name and unshare it. -// -// Return values: -// HRESULT -// -FnLocalDBUnshareInstance LocalDBUnshareInstance; - -#ifdef __cplusplus -} // extern "C" -#endif - -#if defined(LOCALDB_DEFINE_PROXY_FUNCTIONS) -//--------------------------------------------------------------------- -// The following section is enabled only if the constant LOCALDB_DEFINE_PROXY_FUNCTIONS -// is defined. It provides an implementation of proxies for each of the LocalDB APIs. -// The proxy implementations use a common function to bind to entry points in the -// latest installed SqlUserInstance DLL, and then forward the requests. -// -// The current implementation loads the SqlUserInstance DLL on the first call into -// a proxy function. There is no provision for unloading the DLL. Note that if the -// process includes multiple binaries (EXE and one or more DLLs), each of them could -// load a separate instance of the SqlUserInstance DLL. -// -// For future consideration: allow the SqlUserInstance DLL to be unloaded dynamically. -// -// WARNING: these functions must not be called in DLL initialization, since a deadlock -// could result loading dependent DLLs. -//--------------------------------------------------------------------- - -// This macro provides the body for each proxy function. -// -#define LOCALDB_PROXY(LocalDBFn) static Fn##LocalDBFn* pfn##LocalDBFn = NULL; if (!pfn##LocalDBFn) {HRESULT hr = LocalDBGetPFn(#LocalDBFn, (FARPROC *)&pfn##LocalDBFn); if (FAILED(hr)) return hr;} return (*pfn##LocalDBFn) - -// Structure and function to parse the "Installed Versions" registry subkeys -// -typedef struct { - DWORD dwComponent[2]; - WCHAR wszKeyName[256]; -} Version; - -// The following algorithm is intended to match, in part, the .NET Version class. -// A maximum of two components are allowed, which must be separated with a period. -// Valid: "11", "11.0" -// Invalid: "", ".0", "11.", "11.0." -// -static BOOL ParseVersion(Version * pVersion) -{ - pVersion->dwComponent[0] = 0; - pVersion->dwComponent[1] = 0; - WCHAR * pwch = pVersion->wszKeyName; - - for (int i = 0; i<2; i++) - { - LONGLONG llVal = 0; - BOOL fHaveDigit = FALSE; - - while (*pwch >= L'0' && *pwch <= L'9') - { - llVal = llVal * 10 + (*pwch++ - L'0'); - fHaveDigit = TRUE; - - if (llVal > 0x7fffffff) - { - return FALSE; - } - } - - if (!fHaveDigit) - return FALSE; - - pVersion->dwComponent[i] = (DWORD) llVal; - - if (*pwch == L'\0') - return TRUE; - - if (*pwch != L'.') - return FALSE; - - pwch++; - } - // If we get here, the version string was terminated with L'.', which is not valid - // - return FALSE; -} - -#include - -// This function loads the correct LocalDB API DLL (if required) and returns a pointer to a procedure. -// Note that the first-loaded API DLL for the process will be used until process termination: installation of -// a new version of the API will not be recognized after first load. -// -static HRESULT LocalDBGetPFn(LPCSTR szLocalDBFn, FARPROC *pfnLocalDBFn) -{ - static volatile HMODULE hLocalDBDll = NULL; - - if (!hLocalDBDll) - { - LONG ec; - HKEY hkeyVersions = NULL; - HKEY hkeyVersion = NULL; - Version verHigh = {0}; - Version verCurrent; - DWORD cchKeyName; - DWORD dwValueType; - WCHAR wszLocalDBDll[MAX_PATH+1]; - DWORD cbLocalDBDll = sizeof(wszLocalDBDll) - sizeof(WCHAR); // to deal with RegQueryValueEx null-termination quirk - HMODULE hLocalDBDllTemp = NULL; - - if (ERROR_SUCCESS != (ec = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Microsoft SQL Server Local DB\\Installed Versions", 0, KEY_READ, &hkeyVersions))) - { - goto Cleanup; - } - - for (int i = 0; ; i++) - { - cchKeyName = 256; - if (ERROR_SUCCESS != (ec = RegEnumKeyExW(hkeyVersions, i, verCurrent.wszKeyName, &cchKeyName, 0, NULL, NULL, NULL))) - { - if (ERROR_NO_MORE_ITEMS == ec) - { - break; - } - goto Cleanup; - } - - if (!ParseVersion(&verCurrent)) - { - continue; // invalid version syntax - } - - if (verCurrent.dwComponent[0] > verHigh.dwComponent[0] || - (verCurrent.dwComponent[0] == verHigh.dwComponent[0] && verCurrent.dwComponent[1] > verHigh.dwComponent[1])) - { - verHigh = verCurrent; - } - } - if (!verHigh.wszKeyName[0]) - { - // ec must be ERROR_NO_MORE_ITEMS here - // - assert(ec == ERROR_NO_MORE_ITEMS); - - // We will change the error code to ERROR_FILE_NOT_FOUND in order to indicate that - // LocalDB instalation is not found. Registry key "SOFTWARE\\Microsoft\\Microsoft SQL Server Local DB\\Installed Versions" exists - // but it is empty. - // - ec = ERROR_FILE_NOT_FOUND; - goto Cleanup; - } - - if (ERROR_SUCCESS != (ec = RegOpenKeyExW(hkeyVersions, verHigh.wszKeyName, 0, KEY_READ, &hkeyVersion))) - { - goto Cleanup; - } - if (ERROR_SUCCESS != (ec = RegQueryValueExW(hkeyVersion, L"InstanceAPIPath", NULL, &dwValueType, (PBYTE) wszLocalDBDll, &cbLocalDBDll))) - { - goto Cleanup; - } - if (dwValueType != REG_SZ) - { - ec = ERROR_INVALID_DATA; - goto Cleanup; - } - // Ensure string value null-terminated - // Note that we left a spare character in the output buffer for RegQueryValueEx for this purpose - // - wszLocalDBDll[cbLocalDBDll/sizeof(WCHAR)] = L'\0'; - - hLocalDBDllTemp = LoadLibraryW(wszLocalDBDll); - if (NULL == hLocalDBDllTemp) - { - ec = GetLastError(); - goto Cleanup; - } - if (NULL == InterlockedCompareExchangePointer((volatile PVOID *)&hLocalDBDll, hLocalDBDllTemp, NULL)) - { - // We were the winner: we gave away our DLL handle - // - hLocalDBDllTemp = NULL; - } - ec = ERROR_SUCCESS; -Cleanup: - if (hLocalDBDllTemp) - FreeLibrary(hLocalDBDllTemp); - if (hkeyVersion) - RegCloseKey(hkeyVersion); - if (hkeyVersions) - RegCloseKey(hkeyVersions); - - // Error code ERROR_FILE_NOT_FOUND can occure if registry hive with installed LocalDB versions is missing. - // In that case we should return the LocalDB specific error code - // - if (ec == ERROR_FILE_NOT_FOUND) - return LOCALDB_ERROR_NOT_INSTALLED; - - if (ec != ERROR_SUCCESS) - return HRESULT_FROM_WIN32(ec); - } - - FARPROC pfn = GetProcAddress(hLocalDBDll, szLocalDBFn); - - if (!pfn) - { - return HRESULT_FROM_WIN32(GetLastError()); - } - *pfnLocalDBFn = pfn; - return S_OK; -} - -// The following proxy functions forward calls to the latest LocalDB API DLL. -// - -HRESULT __cdecl -LocalDBCreateInstance ( - // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) - _In_z_ PCWSTR wszVersion, - // I the instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags -) -{ - LOCALDB_PROXY(LocalDBCreateInstance)(wszVersion, pInstanceName, dwFlags); -} - -HRESULT __cdecl -LocalDBStartInstance( - // I the instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags, - // O the buffer to store the connection string to the LocalDB instance - _Out_writes_z__opt(*lpcchSqlConnection) LPWSTR wszSqlConnection, - // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. - _Inout_opt_ LPDWORD lpcchSqlConnection -) -{ - LOCALDB_PROXY(LocalDBStartInstance)(pInstanceName, dwFlags, wszSqlConnection, lpcchSqlConnection); -} - -HRESULT __cdecl -LocalDBStopInstance ( - // I the instance name - _In_z_ PCWSTR pInstanceName, - // I Available flags: - // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately - // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option - _In_ DWORD dwFlags, - // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately - // without waiting for LocalDB instance to stop - _In_ ULONG ulTimeout -) -{ - LOCALDB_PROXY(LocalDBStopInstance)(pInstanceName, dwFlags, ulTimeout); -} - -HRESULT __cdecl -LocalDBDeleteInstance ( - // I the instance name - _In_z_ PCWSTR pInstanceName, - // reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags -) -{ - LOCALDB_PROXY(LocalDBDeleteInstance)(pInstanceName, dwFlags); -} - -HRESULT __cdecl -LocalDBFormatMessage( - // I the LocalDB error code - _In_ HRESULT hrLocalDB, - // I Available flags: - // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, - // the error message will be truncated to fit into the buffer - _In_ DWORD dwFlags, - // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) - _In_ DWORD dwLanguageId, - // O the buffer to store the LocalDB error message - _Out_writes_z_(*lpcchMessage) LPWSTR wszMessage, - // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. If the function succeeds - // contains the number of characters in the message, excluding the trailing null - _Inout_ LPDWORD lpcchMessage -) -{ - LOCALDB_PROXY(LocalDBFormatMessage)(hrLocalDB, dwFlags, dwLanguageId, wszMessage, lpcchMessage); -} - -HRESULT __cdecl -LocalDBGetInstances( - // O buffer with instance names - _Out_ PTLocalDBInstanceName pInstanceNames, - // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, - // has the number of existing LocalDB instances - _Inout_ LPDWORD lpdwNumberOfInstances -) -{ - LOCALDB_PROXY(LocalDBGetInstances)(pInstanceNames, lpdwNumberOfInstances); -} - -HRESULT __cdecl -LocalDBGetInstanceInfo( - // I the instance name - _In_z_ PCWSTR wszInstanceName, - // O instance information - _Out_ PLocalDBInstanceInfo pInfo, - // I Size of LocalDBInstanceInfo structure in bytes - _In_ DWORD cbInfo -) -{ - LOCALDB_PROXY(LocalDBGetInstanceInfo)(wszInstanceName, pInfo, cbInfo); -} - -HRESULT __cdecl -LocalDBStartTracing() -{ - LOCALDB_PROXY(LocalDBStartTracing)(); -} - -HRESULT __cdecl -LocalDBStopTracing() -{ - LOCALDB_PROXY(LocalDBStopTracing)(); -} - -HRESULT __cdecl -LocalDBShareInstance( - // I the SID of the LocalDB instance owner - _In_opt_ PSID pOwnerSID, - // I the private name of LocalDB instance which should be shared - _In_z_ PCWSTR wszLocalDBInstancePrivateName, - // I the public shared name - _In_z_ PCWSTR wszSharedName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags) -{ - LOCALDB_PROXY(LocalDBShareInstance)(pOwnerSID, wszLocalDBInstancePrivateName, wszSharedName, dwFlags); -} - -HRESULT __cdecl -LocalDBGetVersions( - // O buffer for installed LocalDB versions - _Out_ PTLocalDBVersion pVersions, - // I/O on input has the number slots for versions in the pVersions buffer. On output, - // has the number of existing LocalDB versions - _Inout_ LPDWORD lpdwNumberOfVersions -) -{ - LOCALDB_PROXY(LocalDBGetVersions)(pVersions, lpdwNumberOfVersions); -} - -HRESULT __cdecl -LocalDBUnshareInstance( - // I the LocalDB instance name - _In_z_ PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - _In_ DWORD dwFlags) -{ - LOCALDB_PROXY(LocalDBUnshareInstance)(pInstanceName, dwFlags); -} - -HRESULT __cdecl -LocalDBGetVersionInfo( - // I LocalDB version string - _In_z_ PCWSTR wszVersion, - // O version information - _Out_ PLocalDBVersionInfo pVersionInfo, - // I Size of LocalDBVersionInfo structure in bytes - _In_ DWORD cbVersionInfo) -{ - LOCALDB_PROXY(LocalDBGetVersionInfo)(wszVersion, pVersionInfo, cbVersionInfo); -} - -#endif - -#endif // _SQLUSERINSTANCE_H_ - -//----------------------------------------------------------------------------- -// File: sqluserinstancemsgs.mc -// -// Copyright: Copyright (c) Microsoft Corporation -//----------------------------------------------------------------------------- -#ifndef _LOCALDB_MESSAGES_H_ -#define _LOCALDB_MESSAGES_H_ -// Header section -// -// Section with the LocalDB messages -// -// -// Values are 32 bit values laid out as follows: -// -// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 -// +-+-+-+-+-+---------------------+-------------------------------+ -// |S|R|C|N|r| Facility | Code | -// +-+-+-+-+-+---------------------+-------------------------------+ -// -// where -// -// S - Severity - indicates success/fail -// -// 0 - Success -// 1 - Fail (COERROR) -// -// R - reserved portion of the facility code, corresponds to NT's -// second severity bit. -// -// C - reserved portion of the facility code, corresponds to NT's -// C field. -// -// N - reserved portion of the facility code. Used to indicate a -// mapped NT status value. -// -// r - reserved portion of the facility code. Reserved for internal -// use. Used to indicate HRESULT values that are not status -// values, but are instead message ids for display strings. -// -// Facility - is the facility code -// -// Code - is the facility's status code -// -// -// Define the facility codes -// -#define FACILITY_LOCALDB 0x9C5 - - -// -// Define the severity codes -// -#define LOCALDB_SEVERITY_SUCCESS 0x0 -#define LOCALDB_SEVERITY_ERROR 0x2 - - -// -// MessageId: LOCALDB_ERROR_CANNOT_CREATE_INSTANCE_FOLDER -// -// MessageText: -// -// Cannot create folder for the LocalDB instance at: %%LOCALAPPDATA%%\Microsoft\Microsoft SQL Server Local DB\Instances\. -// -#define LOCALDB_ERROR_CANNOT_CREATE_INSTANCE_FOLDER ((HRESULT)0x89C50100L) - -// -// MessageId: LOCALDB_ERROR_INVALID_PARAMETER -// -// MessageText: -// -// The parameter for the LocalDB Instance API method is incorrect. Consult the API documentation. -// -#define LOCALDB_ERROR_INVALID_PARAMETER ((HRESULT)0x89C50101L) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_EXISTS_WITH_LOWER_VERSION -// -// MessageText: -// -// Unable to create the LocalDB instance with specified version. An instance with the same name already exists, but it has lower version than the specified version. -// -#define LOCALDB_ERROR_INSTANCE_EXISTS_WITH_LOWER_VERSION ((HRESULT)0x89C50102L) - -// -// MessageId: LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER -// -// MessageText: -// -// Cannot access the user profile folder for local application data (%%LOCALAPPDATA%%). -// -#define LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER ((HRESULT)0x89C50103L) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG -// -// MessageText: -// -// The full path length of the LocalDB instance folder is longer than MAX_PATH. The instance must be stored in folder: %%LOCALAPPDATA%%\Microsoft\Microsoft SQL Server Local DB\Instances\. -// -#define LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG ((HRESULT)0x89C50104L) - -// -// MessageId: LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER -// -// MessageText: -// -// Cannot access LocalDB instance folder: %%LOCALAPPDATA%%\Microsoft\Microsoft SQL Server Local DB\Instances\. -// -#define LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER ((HRESULT)0x89C50105L) - -// -// MessageId: LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY -// -// MessageText: -// -// Unexpected error occurred while trying to access the LocalDB instance registry configuration. See the Windows Application event log for error details. -// -#define LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY ((HRESULT)0x89C50106L) - -// -// MessageId: LOCALDB_ERROR_UNKNOWN_INSTANCE -// -// MessageText: -// -// The specified LocalDB instance does not exist. -// -#define LOCALDB_ERROR_UNKNOWN_INSTANCE ((HRESULT)0x89C50107L) - -// -// MessageId: LOCALDB_ERROR_INTERNAL_ERROR -// -// MessageText: -// -// Unexpected error occurred inside a LocalDB instance API method call. See the Windows Application event log for error details. -// -#define LOCALDB_ERROR_INTERNAL_ERROR ((HRESULT)0x89C50108L) - -// -// MessageId: LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY -// -// MessageText: -// -// Unexpected error occurred while trying to modify the registry configuration for the LocalDB instance. See the Windows Application event log for error details. -// -#define LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY ((HRESULT)0x89C50109L) - -// -// MessageId: LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED -// -// MessageText: -// -// Error occurred during LocalDB instance startup: SQL Server process failed to start. -// -#define LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED ((HRESULT)0x89C5010AL) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT -// -// MessageText: -// -// LocalDB instance is corrupted. See the Windows Application event log for error details. -// -#define LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT ((HRESULT)0x89C5010BL) - -// -// MessageId: LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS -// -// MessageText: -// -// Error occurred during LocalDB instance startup: unable to create the SQL Server process. -// -#define LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS ((HRESULT)0x89C5010CL) - -// -// MessageId: LOCALDB_ERROR_UNKNOWN_VERSION -// -// MessageText: -// -// The specified LocalDB version is not available on this computer. -// -#define LOCALDB_ERROR_UNKNOWN_VERSION ((HRESULT)0x89C5010DL) - -// -// MessageId: LOCALDB_ERROR_UNKNOWN_LANGUAGE_ID -// -// MessageText: -// -// Error getting the localized error message. The language specified by 'Language ID' parameter is unknown. -// -#define LOCALDB_ERROR_UNKNOWN_LANGUAGE_ID ((HRESULT)0x89C5010EL) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_STOP_FAILED -// -// MessageText: -// -// Stop operation for LocalDB instance failed to complete within the specified time. -// -#define LOCALDB_ERROR_INSTANCE_STOP_FAILED ((HRESULT)0x89C5010FL) - -// -// MessageId: LOCALDB_ERROR_UNKNOWN_ERROR_CODE -// -// MessageText: -// -// Error getting the localized error message. The specified error code is unknown. -// -#define LOCALDB_ERROR_UNKNOWN_ERROR_CODE ((HRESULT)0x89C50110L) - -// -// MessageId: LOCALDB_ERROR_VERSION_REQUESTED_NOT_INSTALLED -// -// MessageText: -// -// The LocalDB version available on this workstation is lower than the requested LocalDB version. -// -#define LOCALDB_ERROR_VERSION_REQUESTED_NOT_INSTALLED ((HRESULT)0x89C50111L) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_BUSY -// -// MessageText: -// -// Requested operation on LocalDB instance cannot be performed because specified instance is currently in use. Stop the instance and try again. -// -#define LOCALDB_ERROR_INSTANCE_BUSY ((HRESULT)0x89C50112L) - -// -// MessageId: LOCALDB_ERROR_INVALID_OPERATION -// -// MessageText: -// -// Default LocalDB instances cannot be created, stopped or deleted manually. -// -#define LOCALDB_ERROR_INVALID_OPERATION ((HRESULT)0x89C50113L) - -// -// MessageId: LOCALDB_ERROR_INSUFFICIENT_BUFFER -// -// MessageText: -// -// The buffer passed to the LocalDB instance API method has insufficient size. -// -#define LOCALDB_ERROR_INSUFFICIENT_BUFFER ((HRESULT)0x89C50114L) - -// -// MessageId: LOCALDB_ERROR_WAIT_TIMEOUT -// -// MessageText: -// -// Timeout occurred inside the LocalDB instance API method. -// -#define LOCALDB_ERROR_WAIT_TIMEOUT ((HRESULT)0x89C50115L) - -// MessageId=0x0116 message id is reserved. This message ID will be used for error LOCALDB_ERROR_NOT_INSTALLED. -// This message is specific since it has to be present in SqlUserIntsnace.h because it can be returned by discovery API. -// -// -// MessageId: LOCALDB_ERROR_XEVENT_FAILED -// -// MessageText: -// -// Failed to start XEvent engine within the LocalDB Instance API. -// -#define LOCALDB_ERROR_XEVENT_FAILED ((HRESULT)0x89C50117L) - -// -// MessageId: LOCALDB_ERROR_AUTO_INSTANCE_CREATE_FAILED -// -// MessageText: -// -// Cannot create an automatic instance. See the Windows Application event log for error details. -// -#define LOCALDB_ERROR_AUTO_INSTANCE_CREATE_FAILED ((HRESULT)0x89C50118L) - -// -// MessageId: LOCALDB_ERROR_SHARED_NAME_TAKEN -// -// MessageText: -// -// Cannot create a shared instance. The specified shared instance name is already in use. -// -#define LOCALDB_ERROR_SHARED_NAME_TAKEN ((HRESULT)0x89C50119L) - -// -// MessageId: LOCALDB_ERROR_CALLER_IS_NOT_OWNER -// -// MessageText: -// -// API caller is not LocalDB instance owner. -// -#define LOCALDB_ERROR_CALLER_IS_NOT_OWNER ((HRESULT)0x89C5011AL) - -// -// MessageId: LOCALDB_ERROR_INVALID_INSTANCE_NAME -// -// MessageText: -// -// Specified LocalDB instance name is invalid. -// -#define LOCALDB_ERROR_INVALID_INSTANCE_NAME ((HRESULT)0x89C5011BL) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_ALREADY_SHARED -// -// MessageText: -// -// The specified LocalDB instance is already shared with different shared name. -// -#define LOCALDB_ERROR_INSTANCE_ALREADY_SHARED ((HRESULT)0x89C5011CL) - -// -// MessageId: LOCALDB_ERROR_INSTANCE_NOT_SHARED -// -// MessageText: -// -// The specified LocalDB instance is not shared. -// -#define LOCALDB_ERROR_INSTANCE_NOT_SHARED ((HRESULT)0x89C5011DL) - -// -// MessageId: LOCALDB_ERROR_ADMIN_RIGHTS_REQUIRED -// -// MessageText: -// -// Administrator privileges are required in order to execute this operation. -// -#define LOCALDB_ERROR_ADMIN_RIGHTS_REQUIRED ((HRESULT)0x89C5011EL) - -// -// MessageId: LOCALDB_ERROR_TOO_MANY_SHARED_INSTANCES -// -// MessageText: -// -// There are too many shared instance and we cannot generate unique User Instance Name. Unshare some of the existing shared instances. -// -#define LOCALDB_ERROR_TOO_MANY_SHARED_INSTANCES ((HRESULT)0x89C5011FL) - -// -// MessageId: LOCALDB_ERROR_CANNOT_GET_LOCAL_APP_DATA_PATH -// -// MessageText: -// -// Cannot get a local application data path. Most probably a user profile is not loaded. If LocalDB is executed under IIS, make sure that profile loading is enabled for the current user. -// -#define LOCALDB_ERROR_CANNOT_GET_LOCAL_APP_DATA_PATH ((HRESULT)0x89C50120L) - -// -// MessageId: LOCALDB_ERROR_CANNOT_LOAD_RESOURCES -// -// MessageText: -// -// Cannot load resources for this DLL. Resources for this DLL should be stored in a subfolder Resources, with the same file name as this DLL and the extension ".RLL". -// -#define LOCALDB_ERROR_CANNOT_LOAD_RESOURCES ((HRESULT)0x89C50121L) - - // Detailed error descriptions -// -// MessageId: LOCALDB_EDETAIL_DATADIRECTORY_IS_MISSING -// -// MessageText: -// -// The "DataDirectory" registry value is missing in the LocalDB instance registry key: %1 -// -#define LOCALDB_EDETAIL_DATADIRECTORY_IS_MISSING ((HRESULT)0x89C50200L) - -// -// MessageId: LOCALDB_EDETAIL_CANNOT_ACCESS_INSTANCE_FOLDER -// -// MessageText: -// -// Cannot access LocalDB instance folder: %1 -// -#define LOCALDB_EDETAIL_CANNOT_ACCESS_INSTANCE_FOLDER ((HRESULT)0x89C50201L) - -// -// MessageId: LOCALDB_EDETAIL_DATADIRECTORY_IS_TOO_LONG -// -// MessageText: -// -// The "DataDirectory" registry value is too long in the LocalDB instance registry key: %1 -// -#define LOCALDB_EDETAIL_DATADIRECTORY_IS_TOO_LONG ((HRESULT)0x89C50202L) - -// -// MessageId: LOCALDB_EDETAIL_PARENT_INSTANCE_IS_MISSING -// -// MessageText: -// -// The "Parent Instance" registry value is missing in the LocalDB instance registry key: %1 -// -#define LOCALDB_EDETAIL_PARENT_INSTANCE_IS_MISSING ((HRESULT)0x89C50203L) - -// -// MessageId: LOCALDB_EDETAIL_PARENT_INSTANCE_IS_TOO_LONG -// -// MessageText: -// -// The "Parent Instance" registry value is too long in the LocalDB instance registry key: %1 -// -#define LOCALDB_EDETAIL_PARENT_INSTANCE_IS_TOO_LONG ((HRESULT)0x89C50204L) - -// -// MessageId: LOCALDB_EDETAIL_DATA_DIRECTORY_INVALID -// -// MessageText: -// -// Data directory for LocalDB instance is invalid: %1 -// -#define LOCALDB_EDETAIL_DATA_DIRECTORY_INVALID ((HRESULT)0x89C50205L) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_ASSERT -// -// MessageText: -// -// LocalDB instance API: XEvent engine assert: %1 in %2:%3 (%4) -// -#define LOCALDB_EDETAIL_XEVENT_ASSERT ((HRESULT)0x89C50206L) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_ERROR -// -// MessageText: -// -// LocalDB instance API: XEvent error: %1 -// -#define LOCALDB_EDETAIL_XEVENT_ERROR ((HRESULT)0x89C50207L) - -// -// MessageId: LOCALDB_EDETAIL_INSTALLATION_CORRUPTED -// -// MessageText: -// -// LocalDB installation is corrupted. Reinstall the LocalDB. -// -#define LOCALDB_EDETAIL_INSTALLATION_CORRUPTED ((HRESULT)0x89C50208L) - -// -// MessageId: LOCALDB_EDETAIL_CANNOT_GET_PROGRAM_FILES_LOCATION -// -// MessageText: -// -// LocalDB XEvent error: cannot determine %ProgramFiles% folder location. -// -#define LOCALDB_EDETAIL_CANNOT_GET_PROGRAM_FILES_LOCATION ((HRESULT)0x89C50209L) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_CANNOT_INITIALIZE -// -// MessageText: -// -// LocalDB XEvent error: Cannot initialize XEvent engine. -// -#define LOCALDB_EDETAIL_XEVENT_CANNOT_INITIALIZE ((HRESULT)0x89C5020AL) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_CANNOT_FIND_CONF_FILE -// -// MessageText: -// -// LocalDB XEvent error: Cannot find XEvents configuration file: %1 -// -#define LOCALDB_EDETAIL_XEVENT_CANNOT_FIND_CONF_FILE ((HRESULT)0x89C5020BL) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_CANNOT_CONFIGURE -// -// MessageText: -// -// LocalDB XEvent error: Cannot configure XEvents engine with the configuration file: %1 -// HRESULT returned: %2 -// -#define LOCALDB_EDETAIL_XEVENT_CANNOT_CONFIGURE ((HRESULT)0x89C5020CL) - -// -// MessageId: LOCALDB_EDETAIL_XEVENT_CONF_FILE_NAME_TOO_LONG -// -// MessageText: -// -// LocalDB XEvent error: XEvents engine configuration file too long -// -#define LOCALDB_EDETAIL_XEVENT_CONF_FILE_NAME_TOO_LONG ((HRESULT)0x89C5020DL) - -// -// MessageId: LOCALDB_EDETAIL_COINITIALIZEEX_FAILED -// -// MessageText: -// -// CoInitializeEx API failed. HRESULT returned: %1 -// -#define LOCALDB_EDETAIL_COINITIALIZEEX_FAILED ((HRESULT)0x89C5020EL) - -// -// MessageId: LOCALDB_EDETAIL_PARENT_INSTANCE_VERSION_INVALID -// -// MessageText: -// -// LocalDB parent instance version is invalid: %1 -// -#define LOCALDB_EDETAIL_PARENT_INSTANCE_VERSION_INVALID ((HRESULT)0x89C5020FL) - -// -// MessageId: LOCALDB_EDETAIL_WINAPI_ERROR -// -// MessageText: -// -// Windows API call %1 returned error code: %2. Windows system error message is: %3Reported at line: %4. %5 -// -#define LOCALDB_EDETAIL_WINAPI_ERROR ((HRESULT)0xC9C50210L) - -// -// MessageId: LOCALDB_EDETAIL_UNEXPECTED_RESULT -// -// MessageText: -// -// Function %1 returned %2 at line %3. -// -#define LOCALDB_EDETAIL_UNEXPECTED_RESULT ((HRESULT)0x89C50211L) - -// -#endif // _LOCALDB_MESSAGES_H_ - -#endif //__msodbcsql_h__ diff --git a/source/pdo_sqlsrv/shared/template.rc b/source/pdo_sqlsrv/shared/template.rc deleted file mode 100644 index fa9d72b7..00000000 --- a/source/pdo_sqlsrv/shared/template.rc +++ /dev/null @@ -1,83 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------------------- -// File: template.rc -// -// Contents: Version resource -// -// Microsoft Drivers 4.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. -//--------------------------------------------------------------------------------------------------------------------------------- - -#ifdef APSTUDIO_INVOKED -# error dont edit with MSVC -#endif - -#include "winresrc.h" -#include "main/php_version.h" -#include "version.h" - -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifndef THANKS_GUYS -# define THANKS_GUYS "" -#endif - -#ifdef WANT_LOGO -0 ICON win32\build\php.ico -#endif - -#define XSTRVER4(maj, min, rel, build) #maj "." #min "." #rel "." #build -#define XSTRVER3(maj, min, rel) #maj "." #min "." #rel -#define STRVER4(maj, min, rel, build) XSTRVER4(maj, min, rel, build) -#define STRVER3(maj, min, rel) XSTRVER3(maj, min, rel) - -//Version -VS_VERSION_INFO VERSIONINFO - FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR, SQLVERSION_RELEASE, SQLVERSION_BUILD - PRODUCTVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_RELEASE,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "This product includes PHP software that is freely available from http://www.php.net/software/. Copyright © 2001-2016 The PHP Group. All rights reserved.\0" - VALUE "CompanyName", "Microsoft Corp.\0" - VALUE "FileDescription", "Microsoft Drivers for PHP for SQL Server\0" - VALUE "FileVersion", STRVER4(SQLVERSION_MAJOR,SQLVERSION_MINOR, SQLVERSION_RELEASE, SQLVERSION_BUILD) - VALUE "InternalName", FILE_NAME "\0" - VALUE "LegalCopyright", "Copyright Microsoft Corporation.\0" - VALUE "OriginalFilename", FILE_NAME "\0" - VALUE "ProductName", "Microsoft Drivers for PHP for SQL Server\0" - VALUE "ProductVersion", STRVER3(SQLVERSION_MAJOR,SQLVERSION_MINOR, SQLVERSION_RELEASE) - VALUE "URL", "http://www.microsoft.com\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#ifdef MC_INCLUDE -#include MC_INCLUDE -#endif - diff --git a/source/pdo_sqlsrv/shared/version.h b/source/pdo_sqlsrv/shared/version.h deleted file mode 100644 index 3bbc3261..00000000 --- a/source/pdo_sqlsrv/shared/version.h +++ /dev/null @@ -1,35 +0,0 @@ -//--------------------------------------------------------------------------------------------------------------------------------- -// File: version.h -// Contents: Version number constants -// -// Microsoft Drivers 4.1 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 - -#define SQLVERSION_MAJOR 4 -#define SQLVERSION_MINOR 1 -#define SQLVERSION_RELEASE 4 -#define SQLVERSION_BUILD 0 - -#define VER_FILEVERSION_STR STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_RELEASE ) "." STRINGIFY( SQLVERSION_BUILD ) -#define _FILEVERSION SQLVERSION_MAJOR,SQLVERSION_MINOR,SQLVERSION_RELEASE,SQLVERSION_BUILD -#define PHP_SQLSRV_VERSION STRINGIFY( SQLVERSION_MAJOR ) "." STRINGIFY( SQLVERSION_MINOR ) "." STRINGIFY( SQLVERSION_RELEASE ) -#define PHP_PDO_SQLSRV_VERSION PHP_SQLSRV_VERSION - - - -