Merge pull request #371 from v-kaywon/emulate_prepare

implemented emulate prepare and added tests
This commit is contained in:
v-kaywon 2017-05-02 11:20:46 -07:00 committed by GitHub
commit fb3663bca4
10 changed files with 652 additions and 55 deletions

View file

@ -420,7 +420,6 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( SQLHANDLE h, error_callback e, void* driver TSRM
direct_query( false ),
query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )),
bind_param_encoding( SQLSRV_ENCODING_CHAR ),
fetch_numeric( false )
{
if( client_buffer_max_size < 0 ) {
@ -589,6 +588,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 +661,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 +1289,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. PDOStatement::execute() - object name is PDOStatement
zend_execute_data* execute_data = EG( current_execute_data );
zval *object = getThis();
// iterate through parents to find "PDOStatement"
bool is_statement = false;
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();

View file

@ -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;
@ -91,14 +101,14 @@ int conn_string_parser::discard_trailing_white_spaces( const char* str, int len
}
// Discard white spaces.
bool conn_string_parser::discard_white_spaces()
bool string_parser::discard_white_spaces()
{
if( this->is_eos() ) {
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,15 @@ 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 )
{
// placeholder only accepts numbers, upper and lower case alphabets and underscore
if (( c >= '0' && c <= '9' ) || ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' ) || c == '_' ) {
return true;
}
return false;
}
// Primary function which parses the connection string/DSN.
void conn_string_parser:: parse_conn_string( TSRMLS_D )
{
@ -180,7 +207,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 +215,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 +224,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 +240,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 +264,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 +280,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 +288,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 +309,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 +331,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 +355,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 +365,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 +387,57 @@ 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 string 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 );
discard_white_spaces();
// if an '=' is right after a placeholder, it means the placeholder is for output parameters
// and emulate prepare does not support output parameters
if (this->orig_str[pos] == '=') {
THROW_PDO_ERROR(this->ctx, PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED);
}
this->current_key++;
}
// if '?', store long placeholder into the placeholders hashtable
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 );
discard_white_spaces();
if (this->orig_str[pos] == '=') {
THROW_PDO_ERROR(this->ctx, PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED);
}
this->current_key++;
}
}
}
catch ( pdo::PDOException& ) {
throw;
}
}

View file

@ -486,9 +486,9 @@ 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 );
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
LOG( SEV_NOTICE, "pdo_sqlsrv_stmt_dtor: entering" );
@ -498,7 +498,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 +553,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 );
@ -1106,10 +1114,9 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
// since the param isn't reliable, we don't do anything here
case PDO_PARAM_EVT_ALLOC:
// if emulate prepare is on, set the bind_param_encoding so it can be used in PDO::quote when binding parameters on the client side
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( stmt->dbh->driver_data );
driver_dbh->bind_param_encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && (param->param_type & PDO_PARAM_INPUT_OUTPUT )) {
sqlsrv_stmt* driver_stmt = reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data );
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED );
}
break;
case PDO_PARAM_EVT_FREE:

View file

@ -373,6 +373,10 @@ pdo_error PDO_ERRORS[] = {
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
{ IMSSP, (SQLCHAR*) "Memory limit of %1!d! KB exceeded for buffered query", -71, true }
},
{
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
{ IMSSP, (SQLCHAR*) "Statement with emulate prepare on does not support output or input_output parameters.", -72, false }
},
{ UINT_MAX, {} }
};

View file

@ -125,12 +125,29 @@ 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);
bool discard_white_spaces(void);
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 +161,30 @@ 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 );
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
//*********************************************************************************************************************************
@ -178,7 +199,6 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
bool direct_query;
long query_timeout;
zend_long client_buffer_max_size;
SQLSRV_ENCODING bind_param_encoding;
bool fetch_numeric;
pdo_sqlsrv_dbh( SQLHANDLE h, error_callback e, void* driver TSRMLS_DC );
@ -228,6 +248,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 +266,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;
@ -367,7 +389,7 @@ enum PDO_ERROR_CODES {
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,
};
extern pdo_error PDO_ERRORS[];

View file

@ -0,0 +1,30 @@
--TEST--
Tests error returned when binding input/output parameter with emulate prepare
--SKIPIF--
--FILE--
<?php
require_once("autonomous_setup.php");
$database = 'tempdb';
$dsn = "sqlsrv:Server=$serverName ; Database = $database";
try {
$dbh = new PDO($dsn, $username, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->query("IF OBJECT_ID('sp_ReverseString', 'P') IS NOT NULL DROP PROCEDURE sp_ReverseString");
$dbh->query("CREATE PROCEDURE sp_ReverseString @String as VARCHAR(2048) OUTPUT as SELECT @String = REVERSE(@String)");
$stmt = $dbh->prepare("EXEC sp_ReverseString ?", array(PDO::ATTR_EMULATE_PREPARES => true));
$string = "123456789";
$stmt->bindParam(1, $string, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 2048);
$stmt->execute();
print "Result: ".$string;
//free the statement and connection
$stmt = null;
$dbh = null;
}
catch(PDOException $e) {
print("Error: " . $e->getMessage() . "\n");
}
?>
--EXPECT--
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.

View file

@ -0,0 +1,52 @@
--TEST--
Tests error returned when binding output parameter with emulate prepare
--SKIPIF--
--FILE--
<?php
require_once("autonomous_setup.php");
$database = "tempdb";
$dsn = "sqlsrv:Server=$serverName ; Database = $database";
try {
$conn = new PDO($dsn, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$count = 0;
$query = "select ? = count(* ) from cd_info";
$stmt = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
$stmt->bindParam( 1, $count, PDO::PARAM_STR, 10 );
$stmt->execute();
echo "Result: ".$count."\n";
$query = "select bigint_type, int_type, money_type from [test_types] where int_type < 0";
$stmt1 = $conn->prepare($query);
$stmt1->execute();
$row = $stmt1->fetch( PDO::FETCH_ASSOC );
print_r($row);
$int = 0;
$bigint = 100;
$query = "select ? = bigint_type, ? = int_type, ? = money_type from [test_types] where int_type < 0";
$stmt2 = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
$stmt2->bindparam( 1, $bigint, PDO::PARAM_STR, 256 );
$stmt2->bindParam( 2, $int, PDO::PARAM_INT, 4 );
$stmt2->bindParam( 3, $money, PDO::PARAM_STR, 1024 );
$stmt2->execute();
echo "Big integer: ".$bigint."\n";
echo "Integer: ".$int."\n";
echo "Money: ".$money."\n";
//free the statement and connection
$stmt = null;
$stmt1 = null;
$stmt2 = null;
$conn = null;
}
catch(PDOException $e) {
print("Error: " . $e->getMessage() . "\n");
}
?>
--EXPECT--
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.

View 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

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,82 @@
--TEST--
Test emulate prepare with mix bound param encodings and positional placeholders (i.e., using '?' as placeholders)
--SKIPIF--
--FILE--
<?php
require_once("autonomous_setup.php");
$connection_options['pdo'] = array();
$connection_options['pdo'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
$database = "tempdb";
$cnn = new PDO("sqlsrv:Server=$serverName;Database=$database", $username, $password, $connection_options['pdo']);
// Drop
try {
$pdo_options = array();
$pdo_options[PDO::ATTR_EMULATE_PREPARES] = TRUE;
$pdo_options[PDO::SQLSRV_ATTR_DIRECT_QUERY] = TRUE;
$pdo_options[PDO::ATTR_CURSOR] = PDO::CURSOR_SCROLL;
$pdo_options[PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE] = PDO::SQLSRV_CURSOR_BUFFERED;
$st = $cnn->prepare('DROP TABLE WATCHDOG', $pdo_options);
$st->execute();
}
catch(\Exception $e) {}
$tablescript = <<<EOF
CREATE TABLE [dbo].[watchdog](
[system_encoding] [nvarchar](128),
[utf8_encoding] [nvarchar](128),
[binary_encoding] [varbinary](max))
EOF;
// Recreate
$st = $cnn->prepare($tablescript, $pdo_options);
$st->execute();
$query = <<<EOF
INSERT INTO [watchdog] ([system_encoding], [utf8_encoding], [binary_encoding]) VALUES
(?, ?, ?)
EOF;
/** @var MyStatement */
$st = $cnn->prepare($query, $pdo_options);
$system_param = 'system encoded string';
$utf8_param = '가각ácasa';
$binary_param = fopen('php://memory', 'a');
fwrite($binary_param, 'asdgasdgasdgsadg');
rewind($binary_param);
$st->bindParam(1, $system_param, PDO::PARAM_STR);
$st->bindParam(2, $utf8_param, PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
$st->bindParam(3, $binary_param, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
$st->execute();
$st = $cnn->query("SELECT * FROM [watchdog]");
var_dump($st->fetchAll());
$st = NULL;
$cnn = NULL;
?>
--EXPECT--
array(1) {
[0]=>
array(6) {
["system_encoding"]=>
string(21) "system encoded string"
[0]=>
string(21) "system encoded string"
["utf8_encoding"]=>
string(12) "가각ácasa"
[1]=>
string(12) "가각ácasa"
["binary_encoding"]=>
string(16) "asdgasdgasdgsadg"
[2]=>
string(16) "asdgasdgasdgsadg"
}
}