php-sqlsrv/source/pdo_sqlsrv/php_pdo_sqlsrv_int.h

418 lines
17 KiB
C++

#ifndef PHP_PDO_SQLSRV_INT_H
#define PHP_PDO_SQLSRV_INT_H
//---------------------------------------------------------------------------------------------------------------------------------
// File: php_pdo_sqlsrv_int.h
//
// Contents: Internal declarations for the extension
//
// Microsoft Drivers 5.10 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 "version.h"
extern "C" {
#include "pdo/php_pdo.h"
#include "pdo/php_pdo_driver.h"
}
#include <vector>
#include <map>
//*********************************************************************************************************************************
// Global variables
//*********************************************************************************************************************************
// henv context for creating connections
extern sqlsrv_context* g_pdo_henv_cp;
extern sqlsrv_context* g_pdo_henv_ncp;
// used for getting the version information
extern HMODULE g_sqlsrv_hmodule;
// macros used to access the global variables. Use these to make global variable access agnostic to threads
#ifdef ZTS
#define PDO_SQLSRV_G(v) TSRMG(pdo_sqlsrv_globals_id, zend_pdo_sqlsrv_globals *, v)
#else
#define PDO_SQLSRV_G(v) pdo_sqlsrv_globals.v
#endif
// INI settings and constants
// (these are defined as macros to allow concatenation as we do below)
#define INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE "client_buffer_max_kb_size"
#define INI_PDO_SQLSRV_LOG "log_severity"
#define INI_PDO_SQLSRV_MORE_ERRORS "report_additional_errors"
#define INI_PREFIX "pdo_sqlsrv."
#ifndef _WIN32
#define INI_PDO_SET_LOCALE_INFO "set_locale_info"
#endif
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_LOG , "0", PHP_INI_ALL, OnUpdateLong, pdo_log_severity,
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE , INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong,
client_buffer_max_size, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SQLSRV_MORE_ERRORS, "1", PHP_INI_ALL, OnUpdateLong, report_additional_errors, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)
#ifndef _WIN32
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SET_LOCALE_INFO, "2", PHP_INI_ALL, OnUpdateLong, set_locale_info,
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)
#endif
PHP_INI_END()
//*********************************************************************************************************************************
// Constants and Types
//*********************************************************************************************************************************
// sqlsrv driver specific PDO attributes
enum PDO_SQLSRV_ATTR {
// The custom attributes for this driver:
SQLSRV_ATTR_ENCODING = PDO_ATTR_DRIVER_SPECIFIC,
SQLSRV_ATTR_QUERY_TIMEOUT,
SQLSRV_ATTR_DIRECT_QUERY,
SQLSRV_ATTR_CURSOR_SCROLL_TYPE,
SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE,
SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_DATETIME_TYPE,
SQLSRV_ATTR_FORMAT_DECIMALS,
SQLSRV_ATTR_DECIMAL_PLACES,
SQLSRV_ATTR_DATA_CLASSIFICATION
};
// valid set of values for TransactionIsolation connection option
namespace PDOTxnIsolationValues {
const char READ_UNCOMMITTED[] = "READ_UNCOMMITTED";
const char READ_COMMITTED[] = "READ_COMMITTED";
const char REPEATABLE_READ[] = "REPEATABLE_READ";
const char SERIALIZABLE[] = "SERIALIZABLE";
const char SNAPSHOT[] = "SNAPSHOT";
}
//*********************************************************************************************************************************
// Initialization
//*********************************************************************************************************************************
// Basic string parser
class string_parser
{
protected:
const char* orig_str;
sqlsrv_context* ctx;
int len;
int pos;
unsigned int current_key;
HashTable* element_ht;
inline bool next(void);
inline bool is_eos(void);
inline bool is_white_space( _In_ char c );
bool discard_white_spaces(void);
void add_key_value_pair( _In_reads_(len) const char* value, _In_ int len );
};
//*********************************************************************************************************************************
// PDO DSN Parser
//*********************************************************************************************************************************
// Parser class used to parse DSN connection string.
class conn_string_parser : private string_parser
{
enum States
{
FirstKeyValuePair,
Key,
Value,
ValueContent1,
ValueContent2,
RCBEncountered,
NextKeyValuePair,
};
private:
const char* current_key_name;
int discard_trailing_white_spaces( _In_reads_(len) const char* str, _Inout_ int len );
void validate_key( _In_reads_(key_len) const char *key, _Inout_ int key_len);
public:
conn_string_parser( _In_ sqlsrv_context& ctx, _In_ const char* dsn, _In_ int len, _In_ HashTable* conn_options_ht );
void parse_conn_string( void );
};
//*********************************************************************************************************************************
// PDO Query Parser
//*********************************************************************************************************************************
// Parser class used to parse DSN named placeholders.
class sql_string_parser : private string_parser
{
private:
bool is_placeholder_char(char);
public:
void add_key_int_value_pair( _In_ unsigned int value );
sql_string_parser(_In_ sqlsrv_context& ctx, _In_ const char* sql_str, _In_ int len, _In_ HashTable* placeholder_ht);
void parse_sql_string(void);
};
//*********************************************************************************************************************************
// Connection
//*********************************************************************************************************************************
extern const connection_option PDO_CONN_OPTS[];
int pdo_sqlsrv_db_handle_factory( _Inout_ pdo_dbh_t *dbh, _In_opt_ zval *driver_options);
// a core layer pdo dbh object. This object inherits and overrides the statement factory
struct pdo_sqlsrv_dbh : public sqlsrv_conn {
zval* stmts;
bool direct_query;
long query_timeout;
zend_long client_buffer_max_size;
bool fetch_numeric;
bool fetch_datetime;
bool format_decimals;
short decimal_places;
short use_national_characters;
bool emulate_prepare;
pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver );
};
//*********************************************************************************************************************************
// Statement
//*********************************************************************************************************************************
struct stmt_option_encoding : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_pdo_scrollable : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_direct_query : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_cursor_scroll_type : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_emulate_prepares : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_fetch_numeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
struct stmt_option_fetch_datetime : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
extern struct pdo_stmt_methods pdo_sqlsrv_stmt_methods;
// a core layer pdo stmt object. This object inherits and overrides the callbacks necessary
struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
pdo_sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_ void* drv ) :
sqlsrv_stmt( c, handle, e, drv ),
direct_query( false ),
direct_query_subst_string( NULL ),
direct_query_subst_string_len( 0 ),
placeholders(NULL),
bound_column_param_types( NULL ),
fetch_numeric( false ),
fetch_datetime( false )
{
pdo_sqlsrv_dbh* db = static_cast<pdo_sqlsrv_dbh*>( c );
direct_query = db->direct_query;
fetch_numeric = db->fetch_numeric;
fetch_datetime = db->fetch_datetime;
format_decimals = db->format_decimals;
decimal_places = db->decimal_places;
query_timeout = db->query_timeout;
}
virtual ~pdo_sqlsrv_stmt( void );
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
// for PDO, everything is a string, so we return SQLSRV_PHPTYPE_STRING for all SQL types
virtual sqlsrv_phptype sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_to_stream );
bool direct_query; // flag set if the query should be executed directly or prepared
const char* direct_query_subst_string; // if the query is direct, hold the substitution string if using named parameters
size_t direct_query_subst_string_len; // length of query string used for direct queries
HashTable* placeholders; // hashtable of named placeholders to keep track of params ordering in emulate prepare
pdo_param_type* bound_column_param_types;
bool fetch_numeric;
bool fetch_datetime;
};
//*********************************************************************************************************************************
// Error Handling Functions
//*********************************************************************************************************************************
// represents the mapping between an error_code and the corresponding error message.
struct pdo_error {
unsigned int error_code;
sqlsrv_error_const sqlsrv_error;
};
// called when an error occurs in the core layer. These routines are set as the error_callback in a
// context. The context is passed to this function since it contains the function
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ int warning,
_In_opt_ va_list* print_args );
// common routine to transfer a sqlsrv_context's error to a PDO zval
void pdo_sqlsrv_retrieve_context_error( _In_ sqlsrv_error const* last_error, _Out_ zval* pdo_zval );
// reset the errors from the last operation
inline void pdo_reset_dbh_error( _Inout_ pdo_dbh_t* dbh )
{
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), "00000" ); // 00000 means no error
// release the last statement from the dbh so that error handling won't have a statement passed to it
if( dbh->query_stmt ) {
dbh->query_stmt = NULL;
zval_ptr_dtor( &dbh->query_stmt_zval );
}
// if the driver isn't valid, just return (PDO calls close sometimes more than once?)
if( dbh->driver_data == NULL ) {
return;
}
// reset the last error on the sqlsrv_context
sqlsrv_context* ctx = static_cast<sqlsrv_conn*>( dbh->driver_data );
if( ctx->last_error() ) {
ctx->last_error().reset();
}
}
#define PDO_LOG_NOTICE(message) \
core_sqlsrv_register_severity_checker(pdo_severity_check); \
LOG(SEV_NOTICE, message);
#define PDO_RESET_DBH_ERROR pdo_reset_dbh_error( dbh );
inline void pdo_reset_stmt_error( _Inout_ pdo_stmt_t* stmt )
{
strcpy_s( stmt->error_code, sizeof( stmt->error_code ), "00000" ); // 00000 means no error
// if the driver isn't valid, just return (PDO calls close sometimes more than once?)
if( stmt->driver_data == NULL ) {
return;
}
// reset the last error on the sqlsrv_context
sqlsrv_context* ctx = static_cast<sqlsrv_stmt*>( stmt->driver_data );
if( ctx->last_error() ) {
ctx->last_error().reset();
}
}
#define PDO_RESET_STMT_ERROR pdo_reset_stmt_error( stmt );
// validate the driver objects
#define PDO_VALIDATE_CONN if( dbh->driver_data == NULL ) { DIE( "Invalid driver data in PDO object." ); }
#define PDO_VALIDATE_STMT if( stmt->driver_data == NULL ) { DIE( "Invalid driver data in PDOStatement object." ); }
//*********************************************************************************************************************************
// Utility Functions
//*********************************************************************************************************************************
// List of PDO specific error messages.
enum PDO_ERROR_CODES {
PDO_SQLSRV_ERROR_INVALID_DBH_ATTR = SQLSRV_ERROR_DRIVER_SPECIFIC,
PDO_SQLSRV_ERROR_INVALID_STMT_ATTR,
PDO_SQLSRV_ERROR_INVALID_ENCODING,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM,
PDO_SQLSRV_ERROR_PDO_STMT_UNSUPPORTED,
PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR,
PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR,
PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR,
PDO_SQLSRV_ERROR_INVALID_STMT_OPTION,
PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE,
PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED,
PDO_SQLSRV_ERROR_PARAM_PARSE,
PDO_SQLSRV_ERROR_LAST_INSERT_ID,
PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA,
PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING,
PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING,
PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION,
PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE,
PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY,
PDO_SQLSRV_ERROR_INVALID_DSN_STRING,
PDO_SQLSRV_ERROR_INVALID_DSN_KEY,
PDO_SQLSRV_ERROR_INVALID_DSN_VALUE,
PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED,
PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY,
PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING,
SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN,
PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE,
PDO_SQLSRV_ERROR_DQ_ATTR_AT_PREPARE_ONLY,
PDO_SQLSRV_ERROR_INVALID_COLUMN_INDEX,
PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE,
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
PDO_SQLSRV_ERROR_CE_DIRECT_QUERY_UNSUPPORTED,
PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED,
PDO_SQLSRV_ERROR_EXTENDED_STRING_TYPE_INVALID
};
extern pdo_error PDO_ERRORS[];
#define THROW_PDO_ERROR( ctx, custom, ... ) \
call_error_handler( ctx, custom, false, ## __VA_ARGS__ ); \
throw pdo::PDOException();
namespace pdo {
// an error which occurred in our PDO driver, NOT an exception thrown by PDO
struct PDOException : public core::CoreException {
PDOException() : CoreException()
{
}
};
} // namespace pdo
// check the global variable of pdo_sqlsrv severity whether the message qualifies to be logged with the LOG macro
bool pdo_severity_check(_In_ unsigned int severity);
#endif /* PHP_PDO_SQLSRV_INT_H */