implemented emulate prepare and added tests
This commit is contained in:
parent
012b16a64f
commit
7aaa16d49e
|
@ -589,6 +589,8 @@ int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
|
|||
sqlsrv_malloc_auto_ptr<char> sql_rewrite;
|
||||
size_t sql_rewrite_len = 0;
|
||||
sqlsrv_malloc_auto_ptr<pdo_sqlsrv_stmt> driver_stmt;
|
||||
hash_auto_ptr placeholders;
|
||||
sqlsrv_malloc_auto_ptr<sql_string_parser> sql_parser;
|
||||
|
||||
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
|
||||
SQLSRV_ASSERT(( driver_dbh != NULL ), "pdo_sqlsrv_dbh_prepare: dbh->driver_data was null");
|
||||
|
@ -660,8 +662,17 @@ int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
|
|||
}
|
||||
// else if stmt->support_placeholders == PDO_PLACEHOLDER_NONE means that stmt->active_query_string will be
|
||||
// set to the substituted query
|
||||
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
|
||||
// parse placeholders in the sql query into the placeholders ht
|
||||
ALLOC_HASHTABLE( placeholders );
|
||||
core::sqlsrv_zend_hash_init( *driver_dbh, placeholders, 5, ZVAL_PTR_DTOR /* dtor */, 0 /* persistent */ TSRMLS_CC );
|
||||
sql_parser = new ( sqlsrv_malloc( sizeof( sql_string_parser ))) sql_string_parser( *driver_dbh, stmt->query_string,
|
||||
static_cast<int>(stmt->query_stringlen), placeholders );
|
||||
sql_parser->parse_sql_string( TSRMLS_C );
|
||||
driver_stmt->placeholders = placeholders;
|
||||
placeholders.transferred();
|
||||
}
|
||||
|
||||
|
||||
stmt->driver_data = driver_stmt;
|
||||
driver_stmt.transferred();
|
||||
}
|
||||
|
@ -1279,13 +1290,67 @@ int pdo_sqlsrv_dbh_quote( pdo_dbh_t* dbh, const char* unquoted, size_t unquoted_
|
|||
PDO_VALIDATE_CONN;
|
||||
PDO_LOG_DBH_ENTRY;
|
||||
|
||||
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
|
||||
SQLSRV_ENCODING encoding = driver_dbh->bind_param_encoding;
|
||||
SQLSRV_ENCODING encoding = SQLSRV_ENCODING_CHAR;
|
||||
|
||||
// get the current object in PHP; this distinguishes pdo_sqlsrv_dbh_quote being called from:
|
||||
// 1. PDO::quote() - object name is PDO
|
||||
// 2. PDO::execute() - object name is PDOStatement
|
||||
zend_execute_data* execute_data = EG( current_execute_data );
|
||||
zval *object = getThis();
|
||||
|
||||
// iterate through parents to find "PDOStatement"
|
||||
bool is_statement = false;
|
||||
zend_class_entry* curr_class = ( Z_OBJ_P( object ))->ce;
|
||||
while ( curr_class != NULL ) {
|
||||
if ( strcmp( reinterpret_cast<const char*>( curr_class->name->val ), "PDOStatement" ) == 0 ) {
|
||||
is_statement = true;
|
||||
break;
|
||||
}
|
||||
curr_class = curr_class->parent;
|
||||
}
|
||||
|
||||
// only change the encoding if quote is called from the statement level (which should only be called when a statement
|
||||
// is prepared with emulate prepared on)
|
||||
if ( is_statement ) {
|
||||
pdo_stmt_t *stmt = Z_PDO_STMT_P( object );
|
||||
// set the encoding to be the encoding of the statement otherwise set to be the encoding of the dbh
|
||||
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
||||
if ( driver_stmt->encoding() != SQLSRV_ENCODING_INVALID ) {
|
||||
encoding = driver_stmt->encoding();
|
||||
}
|
||||
else {
|
||||
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( stmt->driver_data );
|
||||
encoding = driver_dbh->encoding();
|
||||
}
|
||||
// get the placeholder at the current position in driver_stmt->placeholders ht
|
||||
zval* placeholder = NULL;
|
||||
if (( placeholder = zend_hash_get_current_data( driver_stmt->placeholders )) != NULL && zend_hash_move_forward( driver_stmt->placeholders ) == SUCCESS ) {
|
||||
pdo_bound_param_data* param = NULL;
|
||||
if ( Z_TYPE_P( placeholder ) == IS_STRING ) {
|
||||
param = reinterpret_cast<pdo_bound_param_data*>( zend_hash_find_ptr( stmt->bound_params, Z_STR_P( placeholder )));
|
||||
}
|
||||
else if ( Z_TYPE_P( placeholder ) == IS_LONG) {
|
||||
param = reinterpret_cast<pdo_bound_param_data*>( zend_hash_index_find_ptr( stmt->bound_params, Z_LVAL_P( placeholder )));
|
||||
}
|
||||
if ( NULL != param ) {
|
||||
SQLSRV_ENCODING param_encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
|
||||
if ( param_encoding != SQLSRV_ENCODING_INVALID ) {
|
||||
encoding = param_encoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( encoding == SQLSRV_ENCODING_BINARY ) {
|
||||
// convert from char* to hex digits using os
|
||||
std::basic_ostringstream<char> os;
|
||||
for ( size_t index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
|
||||
// when an int is < 16 and is appended to os, its hex representation which starts
|
||||
// with '0' does not get appended properly (the starting '0' does not get appended)
|
||||
// thus append '0' first
|
||||
if (( int )unquoted[index] < 16 ) {
|
||||
os << '0';
|
||||
}
|
||||
os << std::hex << ( int )unquoted[ index ];
|
||||
}
|
||||
std::basic_string<char> str_hex = os.str();
|
||||
|
|
|
@ -24,17 +24,27 @@
|
|||
// Constructor
|
||||
conn_string_parser:: conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht )
|
||||
{
|
||||
this->conn_str = dsn;
|
||||
this->orig_str = dsn;
|
||||
this->len = len;
|
||||
this->conn_options_ht = conn_options_ht;
|
||||
this->element_ht = conn_options_ht;
|
||||
this->pos = -1;
|
||||
this->ctx = &ctx;
|
||||
this->current_key = 0;
|
||||
this->current_key_name = NULL;
|
||||
}
|
||||
|
||||
sql_string_parser:: sql_string_parser( sqlsrv_context& ctx, const char* sql_str, int len, _Inout_ HashTable* placeholders_ht )
|
||||
{
|
||||
this->orig_str = sql_str;
|
||||
this->len = len;
|
||||
this->element_ht = placeholders_ht;
|
||||
this->pos = -1;
|
||||
this->ctx = &ctx;
|
||||
this->current_key = 0;
|
||||
}
|
||||
|
||||
// Move to the next character
|
||||
inline bool conn_string_parser::next( void )
|
||||
inline bool string_parser::next( void )
|
||||
{
|
||||
// if already at the end then return false
|
||||
if( this->is_eos() ) {
|
||||
|
@ -55,7 +65,7 @@ inline bool conn_string_parser::next( void )
|
|||
}
|
||||
|
||||
// Check for end of string.
|
||||
inline bool conn_string_parser::is_eos( void )
|
||||
inline bool string_parser::is_eos( void )
|
||||
{
|
||||
if( this->pos == len )
|
||||
{
|
||||
|
@ -68,7 +78,7 @@ inline bool conn_string_parser::is_eos( void )
|
|||
}
|
||||
|
||||
// Check for white space.
|
||||
inline bool conn_string_parser::is_white_space( char c )
|
||||
inline bool string_parser::is_white_space( char c )
|
||||
{
|
||||
if( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) {
|
||||
return true;
|
||||
|
@ -98,7 +108,7 @@ bool conn_string_parser::discard_white_spaces()
|
|||
return false;
|
||||
}
|
||||
|
||||
while( this->is_white_space( this->conn_str[ pos ] )) {
|
||||
while( this->is_white_space( this->orig_str[ pos ] )) {
|
||||
|
||||
if( !next() )
|
||||
return false;
|
||||
|
@ -107,8 +117,8 @@ bool conn_string_parser::discard_white_spaces()
|
|||
return true;
|
||||
}
|
||||
|
||||
// Add a key-value pair to the hashtable of connection options.
|
||||
void conn_string_parser::add_key_value_pair( const char* value, int len TSRMLS_DC )
|
||||
// Add a key-value pair to the hashtable
|
||||
void string_parser::add_key_value_pair( const char* value, int len TSRMLS_DC )
|
||||
{
|
||||
zval value_z;
|
||||
ZVAL_UNDEF( &value_z );
|
||||
|
@ -122,7 +132,15 @@ void conn_string_parser::add_key_value_pair( const char* value, int len TSRMLS_D
|
|||
ZVAL_STRINGL( &value_z, const_cast<char*>( value ), len );
|
||||
}
|
||||
|
||||
core::sqlsrv_zend_hash_index_update( *ctx, this->conn_options_ht, this->current_key, &value_z TSRMLS_CC );
|
||||
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z TSRMLS_CC );
|
||||
}
|
||||
|
||||
// Add a key-value pair to the hashtable with int value
|
||||
void sql_string_parser::add_key_int_value_pair( unsigned int value TSRMLS_DC ) {
|
||||
zval value_z;
|
||||
ZVAL_LONG( &value_z, value );
|
||||
|
||||
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z TSRMLS_CC );
|
||||
}
|
||||
|
||||
// Validate a given DSN keyword.
|
||||
|
@ -151,6 +169,76 @@ void conn_string_parser::validate_key(const char *key, int key_len TSRMLS_DC )
|
|||
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ) );
|
||||
}
|
||||
|
||||
inline bool sql_string_parser::is_placeholder_char( char c )
|
||||
{
|
||||
switch ( c ) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'G':
|
||||
case 'H':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
case 'P':
|
||||
case 'Q':
|
||||
case 'R':
|
||||
case 'S':
|
||||
case 'T':
|
||||
case 'U':
|
||||
case 'V':
|
||||
case 'W':
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
case '_':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'n':
|
||||
case 'o':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'r':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'u':
|
||||
case 'v':
|
||||
case 'w':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z': return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Primary function which parses the connection string/DSN.
|
||||
void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
||||
{
|
||||
|
@ -180,7 +268,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
start_pos = this->pos;
|
||||
|
||||
// read the key name
|
||||
while( this->conn_str[ pos ] != '=' ) {
|
||||
while( this->orig_str[ pos ] != '=' ) {
|
||||
|
||||
if( !next() ) {
|
||||
|
||||
|
@ -188,7 +276,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
}
|
||||
}
|
||||
|
||||
this->validate_key( &( this->conn_str[ start_pos ] ), ( pos - start_pos ) TSRMLS_CC );
|
||||
this->validate_key( &( this->orig_str[ start_pos ] ), ( pos - start_pos ) TSRMLS_CC );
|
||||
|
||||
state = Value;
|
||||
|
||||
|
@ -197,13 +285,13 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
|
||||
case Value:
|
||||
{
|
||||
SQLSRV_ASSERT(( this->conn_str[ pos ] == '=' ), "conn_string_parser:: parse_conn_string: "
|
||||
SQLSRV_ASSERT(( this->orig_str[ pos ] == '=' ), "conn_string_parser:: parse_conn_string: "
|
||||
"Equal was expected" );
|
||||
|
||||
next(); // skip "="
|
||||
|
||||
// if EOS encountered after 0 or more spaces OR semi-colon encountered.
|
||||
if( !discard_white_spaces() || this->conn_str[ pos ] == ';' ) {
|
||||
if( !discard_white_spaces() || this->orig_str[ pos ] == ';' ) {
|
||||
|
||||
add_key_value_pair( NULL, 0 TSRMLS_CC );
|
||||
|
||||
|
@ -213,13 +301,13 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
}
|
||||
else {
|
||||
|
||||
// this->conn_str[ pos ] == ';'
|
||||
// this->orig_str[ pos ] == ';'
|
||||
state = NextKeyValuePair;
|
||||
}
|
||||
}
|
||||
|
||||
// if LCB
|
||||
else if( this->conn_str[ pos ] == '{' ) {
|
||||
else if( this->orig_str[ pos ] == '{' ) {
|
||||
|
||||
start_pos = this->pos; // starting character is LCB
|
||||
state = ValueContent1;
|
||||
|
@ -237,7 +325,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
|
||||
case ValueContent1:
|
||||
{
|
||||
while ( this->conn_str[ pos ] != '}' ) {
|
||||
while ( this->orig_str[ pos ] != '}' ) {
|
||||
|
||||
if ( ! next() ) {
|
||||
|
||||
|
@ -253,7 +341,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
|
||||
case ValueContent2:
|
||||
{
|
||||
while( this->conn_str[ pos ] != ';' ) {
|
||||
while( this->orig_str[ pos ] != ';' ) {
|
||||
|
||||
if( ! next() ) {
|
||||
|
||||
|
@ -261,13 +349,13 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
}
|
||||
}
|
||||
|
||||
if( !this->is_eos() && this->conn_str[ pos ] == ';' ) {
|
||||
if( !this->is_eos() && this->orig_str[ pos ] == ';' ) {
|
||||
|
||||
// semi-colon encountered, so go to next key-value pair
|
||||
state = NextKeyValuePair;
|
||||
}
|
||||
|
||||
add_key_value_pair( &( this->conn_str[ start_pos ] ), this->pos - start_pos TSRMLS_CC );
|
||||
add_key_value_pair( &( this->orig_str[ start_pos ] ), this->pos - start_pos TSRMLS_CC );
|
||||
|
||||
SQLSRV_ASSERT((( state == NextKeyValuePair ) || ( this->is_eos() )),
|
||||
"conn_string_parser::parse_conn_string: Invalid state encountered " );
|
||||
|
@ -282,14 +370,14 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
if( !next() ) {
|
||||
|
||||
// EOS
|
||||
add_key_value_pair( &( this->conn_str[ start_pos ] ), this->pos - start_pos TSRMLS_CC );
|
||||
add_key_value_pair( &( this->orig_str[ start_pos ] ), this->pos - start_pos TSRMLS_CC );
|
||||
break;
|
||||
}
|
||||
|
||||
SQLSRV_ASSERT( !this->is_eos(), "conn_string_parser::parse_conn_string: Unexpected EOS encountered" );
|
||||
|
||||
// if second RCB encountered than go back to ValueContent1
|
||||
if( this->conn_str[ pos ] == '}' ) {
|
||||
if( this->orig_str[ pos ] == '}' ) {
|
||||
|
||||
if( !next() ) {
|
||||
|
||||
|
@ -304,20 +392,20 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
int end_pos = this->pos;
|
||||
|
||||
// discard any trailing white-spaces.
|
||||
if( this->is_white_space( this->conn_str[ pos ] )) {
|
||||
if( this->is_white_space( this->orig_str[ pos ] )) {
|
||||
|
||||
if( ! this->discard_white_spaces() ) {
|
||||
|
||||
//EOS
|
||||
add_key_value_pair( &( this->conn_str[ start_pos ] ), end_pos - start_pos TSRMLS_CC );
|
||||
add_key_value_pair( &( this->orig_str[ start_pos ] ), end_pos - start_pos TSRMLS_CC );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if semi-colon than go to next key-value pair
|
||||
if ( this->conn_str[ pos ] == ';' ) {
|
||||
if ( this->orig_str[ pos ] == ';' ) {
|
||||
|
||||
add_key_value_pair( &( this->conn_str[ start_pos ] ), end_pos - start_pos TSRMLS_CC );
|
||||
add_key_value_pair( &( this->orig_str[ start_pos ] ), end_pos - start_pos TSRMLS_CC );
|
||||
state = NextKeyValuePair;
|
||||
break;
|
||||
}
|
||||
|
@ -328,7 +416,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
}
|
||||
case NextKeyValuePair:
|
||||
{
|
||||
SQLSRV_ASSERT(( this->conn_str[ pos ] == ';' ),
|
||||
SQLSRV_ASSERT(( this->orig_str[ pos ] == ';' ),
|
||||
"conn_string_parser::parse_conn_string: semi-colon was expected." );
|
||||
|
||||
// Call next() to skip the semi-colon.
|
||||
|
@ -338,7 +426,7 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
break;
|
||||
}
|
||||
|
||||
if( this->conn_str[ pos ] == ';' ) {
|
||||
if( this->orig_str[ pos ] == ';' ) {
|
||||
|
||||
// a second semi-colon is error case.
|
||||
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING, this->pos );
|
||||
|
@ -360,3 +448,47 @@ void conn_string_parser:: parse_conn_string( TSRMLS_D )
|
|||
}
|
||||
}
|
||||
|
||||
// Primary function which parses out the named placeholders from a sql string.
|
||||
void sql_string_parser::parse_sql_string( TSRMLS_D ) {
|
||||
try {
|
||||
while ( !this->is_eos() ) {
|
||||
int start_pos = -1;
|
||||
|
||||
// skip until a '"', '\'', ':' or '?'
|
||||
char sym;
|
||||
while ( this->orig_str[pos] != '"' && this->orig_str[pos] != '\'' && this->orig_str[pos] != ':' && this->orig_str[pos] != '?' && !this->is_eos() ) {
|
||||
next();
|
||||
}
|
||||
sym = this->orig_str[pos];
|
||||
// if '"' or '\'', skip until the next '"' or '\'' respectively
|
||||
if ( sym == '"' || sym == '\'' ) {
|
||||
next();
|
||||
while ( this->orig_str[pos] != sym && !this->is_eos() ) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
// if ':', store placeholder in the placeholders hashtable
|
||||
else if ( sym == ':' ) {
|
||||
start_pos = this->pos;
|
||||
next();
|
||||
// keep going until the next space or line break
|
||||
// while (!is_white_space(this->orig_str[pos]) && !this->is_eos()) {
|
||||
while ( is_placeholder_char( this->orig_str[pos] )) {
|
||||
next();
|
||||
}
|
||||
add_key_value_pair( &( this->orig_str[start_pos] ), this->pos - start_pos TSRMLS_CC );
|
||||
this->current_key++;
|
||||
}
|
||||
// if '?', don't need to parse anymore since the position of the bound_param is already stored in the bound_params ht
|
||||
else if ( sym == '?' ) {
|
||||
next();
|
||||
// add dummy value to placeholders ht to keep count of the number of placeholders
|
||||
add_key_int_value_pair( this->current_key );
|
||||
this->current_key++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( pdo::PDOException& ) {
|
||||
throw;
|
||||
}
|
||||
}
|
|
@ -486,9 +486,10 @@ int pdo_sqlsrv_stmt_describe_col(pdo_stmt_t *stmt, int colno TSRMLS_DC)
|
|||
// *stmt - pointer to current statement
|
||||
// Return:
|
||||
// 1 for success.
|
||||
int pdo_sqlsrv_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
|
||||
int pdo_sqlsrv_stmt_dtor( pdo_stmt_t *stmt TSRMLS_DC )
|
||||
{
|
||||
sqlsrv_stmt* driver_stmt = reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data );
|
||||
//sqlsrv_stmt* driver_stmt = reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data );
|
||||
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
||||
|
||||
LOG( SEV_NOTICE, "pdo_sqlsrv_stmt_dtor: entering" );
|
||||
|
||||
|
@ -498,7 +499,13 @@ int pdo_sqlsrv_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
|
|||
return 1;
|
||||
}
|
||||
|
||||
driver_stmt->~sqlsrv_stmt();
|
||||
if ( driver_stmt->placeholders != NULL ) {
|
||||
zend_hash_destroy( driver_stmt->placeholders );
|
||||
FREE_HASHTABLE( driver_stmt->placeholders );
|
||||
driver_stmt->placeholders = NULL;
|
||||
}
|
||||
|
||||
(( sqlsrv_stmt* )driver_stmt )->~sqlsrv_stmt();
|
||||
|
||||
sqlsrv_free( driver_stmt );
|
||||
|
||||
|
@ -547,6 +554,8 @@ int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
|
|||
// if the user is using prepare emulation (PDO::ATTR_EMULATE_PREPARES), set the query to the
|
||||
// subtituted query provided by PDO
|
||||
if( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
|
||||
// reset the placeholders hashtable internal in case the user reexecutes a statement
|
||||
zend_hash_internal_pointer_reset(driver_stmt->placeholders);
|
||||
|
||||
query = stmt->active_query_string;
|
||||
query_len = static_cast<unsigned int>( stmt->active_query_stringlen );
|
||||
|
|
|
@ -125,12 +125,28 @@ PHP_MINFO_FUNCTION(pdo_sqlsrv);
|
|||
|
||||
extern zend_module_entry g_pdo_sqlsrv_module_entry; // describes the extension to PHP
|
||||
|
||||
// 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(char c);
|
||||
void add_key_value_pair(const char* value, int len TSRMLS_DC);
|
||||
};
|
||||
|
||||
//*********************************************************************************************************************************
|
||||
// PDO DSN Parser
|
||||
//*********************************************************************************************************************************
|
||||
|
||||
// Parser class used to parse DSN connection string.
|
||||
class conn_string_parser
|
||||
class conn_string_parser : private string_parser
|
||||
{
|
||||
enum States
|
||||
{
|
||||
|
@ -144,26 +160,31 @@ class conn_string_parser
|
|||
};
|
||||
|
||||
private:
|
||||
const char* conn_str;
|
||||
sqlsrv_context* ctx;
|
||||
int len;
|
||||
int pos;
|
||||
unsigned int current_key;
|
||||
const char* current_key_name;
|
||||
HashTable* conn_options_ht;
|
||||
inline bool next( void );
|
||||
inline bool is_eos( void );
|
||||
inline bool is_white_space( char c );
|
||||
bool discard_white_spaces( void );
|
||||
int discard_trailing_white_spaces( const char* str, int len );
|
||||
void validate_key( const char *key, int key_len TSRMLS_DC );
|
||||
void add_key_value_pair( const char* value, int len TSRMLS_DC );
|
||||
bool discard_white_spaces(void);
|
||||
int discard_trailing_white_spaces(const char* str, int len);
|
||||
void validate_key(const char *key, int key_len TSRMLS_DC);
|
||||
|
||||
public:
|
||||
conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht );
|
||||
void parse_conn_string( TSRMLS_D );
|
||||
};
|
||||
|
||||
//*********************************************************************************************************************************
|
||||
// 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(unsigned int value TSRMLS_DC);
|
||||
sql_string_parser(sqlsrv_context& ctx, const char* sql_str, int len, _Inout_ HashTable* placeholder_ht);
|
||||
void parse_sql_string(TSRMLS_D);
|
||||
};
|
||||
|
||||
//*********************************************************************************************************************************
|
||||
// Connection
|
||||
//*********************************************************************************************************************************
|
||||
|
@ -228,6 +249,7 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
|
|||
direct_query( false ),
|
||||
direct_query_subst_string( NULL ),
|
||||
direct_query_subst_string_len( 0 ),
|
||||
placeholders(NULL),
|
||||
bound_column_param_types( NULL ),
|
||||
fetch_numeric( false )
|
||||
{
|
||||
|
@ -245,6 +267,7 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
|
|||
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
|
||||
|
||||
// meta data for current result set
|
||||
std::vector<field_meta_data*, sqlsrv_allocator< field_meta_data* > > current_meta_data;
|
||||
|
|
69
test/pdo_sqlsrv/pdo_092_emulate_prepare_statement_utf8.phpt
Normal file
69
test/pdo_sqlsrv/pdo_092_emulate_prepare_statement_utf8.phpt
Normal file
|
@ -0,0 +1,69 @@
|
|||
--TEST--
|
||||
Test emulate prepare utf8 encoding set at the statement level
|
||||
--SKIPIF--
|
||||
--FILE--
|
||||
|
||||
<?php
|
||||
require_once("autonomous_setup.php");
|
||||
|
||||
$pdo_options = [];
|
||||
$pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_ENCODING] = PDO::SQLSRV_ENCODING_UTF8;
|
||||
$database = "tempdb";
|
||||
|
||||
$connection = new \PDO("sqlsrv:server=$serverName;Database=$database", $username, $password, $pdo_options);
|
||||
|
||||
$pdo_options = array();
|
||||
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = TRUE;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_DIRECT_QUERY] = TRUE;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_ENCODING] = PDO::SQLSRV_ENCODING_UTF8;
|
||||
$pdo_options[PDO::ATTR_CURSOR] = PDO::CURSOR_SCROLL;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE] = PDO::SQLSRV_CURSOR_BUFFERED;
|
||||
|
||||
// Drop
|
||||
try {
|
||||
$st = $connection->prepare("DROP TABLE TEST", $pdo_options);
|
||||
$st->execute();
|
||||
}
|
||||
catch(\Exception $e) {}
|
||||
|
||||
// Recreate
|
||||
$st = $connection->prepare("CREATE TABLE TEST([id] [int] IDENTITY(1,1) NOT NULL, [name] nvarchar(max))", $pdo_options);
|
||||
$st->execute();
|
||||
|
||||
$prefix = '가각';
|
||||
$name = '가각ácasa';
|
||||
$name2 = '가각sample2';
|
||||
|
||||
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = FALSE;
|
||||
$st = $connection->prepare("INSERT INTO TEST(name) VALUES(:p0)", $pdo_options);
|
||||
$st->execute(['p0' => $name]);
|
||||
|
||||
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = TRUE;
|
||||
$st = $connection->prepare("INSERT INTO TEST(name) VALUES(:p0)", $pdo_options);
|
||||
$st->execute(['p0' => $name2]);
|
||||
|
||||
$statement = $connection->prepare("SELECT * FROM TEST WHERE NAME LIKE :p0", $pdo_options);
|
||||
$statement->execute(['p0' => "$prefix%"]);
|
||||
foreach ($statement as $row) {
|
||||
echo "\n" . 'FOUND: ' . $row['name'];
|
||||
}
|
||||
|
||||
$pdo_options = array();
|
||||
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = FALSE;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_DIRECT_QUERY] = TRUE;
|
||||
$pdo_options[PDO::SQLSRV_ATTR_ENCODING] = PDO::SQLSRV_ENCODING_UTF8;
|
||||
$statement = $connection->prepare("SELECT * FROM TEST WHERE NAME LIKE :p0", $pdo_options);
|
||||
$statement->execute(['p0' => "$prefix%"]);
|
||||
foreach ($statement as $row) {
|
||||
echo "\n" . 'FOUND: ' . $row['name'];
|
||||
}
|
||||
$stmt = NULL;
|
||||
$connection = NULL;
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
FOUND: 가각ácasa
|
||||
FOUND: 가각sample2
|
||||
FOUND: 가각ácasa
|
||||
FOUND: 가각sample2
|
259
test/pdo_sqlsrv/pdo_140_emulate_prepare_mix_binary.phpt
Normal file
259
test/pdo_sqlsrv/pdo_140_emulate_prepare_mix_binary.phpt
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue