Skip to content

Commit

Permalink
Merge 591021d into fcd7b64
Browse files Browse the repository at this point in the history
  • Loading branch information
yitam authored Apr 29, 2021
2 parents fcd7b64 + 591021d commit e9ca8a6
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 62 deletions.
94 changes: 36 additions & 58 deletions source/pdo_sqlsrv/pdo_dbh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace {

const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;";
const size_t LAST_INSERT_ID_BUFF_LEN = 50; // size of the buffer to hold the string value of the last inserted id, which may be an int, bigint, decimal(p,0) or numeric(p,0)
const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT current_value FROM sys.sequences WHERE name=%s";
const char SEQUENCE_CURRENT_VALUE_QUERY[] = "SELECT current_value FROM sys.sequences WHERE name=N'%s'";
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( SEQUENCE_CURRENT_VALUE_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes

// List of PDO supported connection options.
Expand Down Expand Up @@ -1360,22 +1360,18 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
SQLSRV_ASSERT( driver_dbh != NULL, "pdo_sqlsrv_dbh_last_id: driver_data object was NULL." );

sqlsrv_malloc_auto_ptr<char> id_str;
id_str = reinterpret_cast<char*>( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN ));
char idSTR[LAST_INSERT_ID_BUFF_LEN] = { '\0' };
char* str = NULL;
SQLLEN cbID = 0;

try {

char last_insert_id_query[LAST_INSERT_ID_QUERY_MAX_LEN] = {'\0'};
if( name == NULL ) {
strcpy_s( last_insert_id_query, sizeof( last_insert_id_query ), LAST_INSERT_ID_QUERY );
strcpy_s(last_insert_id_query, sizeof(last_insert_id_query), LAST_INSERT_ID_QUERY);
}
else {
char* quoted_table = NULL;
size_t quoted_len = 0;
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strnlen_s( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL );
SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name.");
snprintf( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, quoted_table );
sqlsrv_free( quoted_table );
snprintf(last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, SEQUENCE_CURRENT_VALUE_QUERY, name);
}

// temp PDO statement used for error handling if something happens
Expand All @@ -1397,20 +1393,17 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,

// execute the last insert id query
core::SQLExecDirectW( driver_stmt, wsql_string );

core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 );
SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN,
reinterpret_cast<SQLLEN*>( len ), false );

CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt,
PDO_SQLSRV_ERROR_LAST_INSERT_ID ) {
SQLRETURN r = core::SQLGetData(driver_stmt, 1, SQL_C_CHAR, idSTR, LAST_INSERT_ID_BUFF_LEN, &cbID, false);
CHECK_CUSTOM_ERROR((!SQL_SUCCEEDED(r) || cbID == SQL_NULL_DATA || cbID == SQL_NO_TOTAL), driver_stmt,
PDO_SQLSRV_ERROR_LAST_INSERT_ID) {
throw core::CoreException();
}

driver_stmt->~sqlsrv_stmt();
}
catch( core::CoreException& ) {

// copy any errors on the statement to the connection so that the user sees them, since the statement is released
// before this method returns
strcpy_s( dbh->error_code, sizeof( dbh->error_code ),
Expand All @@ -1420,18 +1413,20 @@ char * pdo_sqlsrv_dbh_last_id( _Inout_ pdo_dbh_t *dbh, _In_z_ const char *name,
if( driver_stmt ) {
driver_stmt->~sqlsrv_stmt();
}

strcpy_s( id_str.get(), 1, "" );
*len = 0;
str = reinterpret_cast<char*>(sqlsrv_malloc(0, sizeof(char), 1)); // return an empty string with a null terminator
str[0] = '\0';
return str;
}

char* ret_id_str = id_str.get();
id_str.transferred();

// restore error handling to its previous mode
dbh->error_mode = prev_err_mode;

return ret_id_str;
// copy the last ID string and return it
*len = static_cast<size_t>(cbID);
str = reinterpret_cast<char*>(sqlsrv_malloc(cbID, sizeof(char), 1)); // include space for null terminator
strcpy_s(str, cbID + 1, idSTR);
return str;
}

// pdo_sqlsrv_dbh_quote
Expand Down Expand Up @@ -1523,44 +1518,27 @@ int pdo_sqlsrv_dbh_quote( _Inout_ pdo_dbh_t* dbh, _In_reads_(unquoted_len) const
}
#endif

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 ) {
// if unquoted is < 0 or > 255, that means this is a non-ascii character. Translation from non-ascii to binary is not supported.
// return an empty terminated string for now
if (( int )unquoted[index] < 0 || ( int )unquoted[index] > 255) {
*quoted_len = 0;
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 ));
( *quoted )[0] = '\0';
return 1;
}
// when an int is < 16 and is appended to os, its hex representation which starts
// with '0' does not get appended properly (the starting '0' does not get appended)
// thus append '0' first
if (( int )unquoted[index] < 16 ) {
os << '0';
if ( encoding == SQLSRV_ENCODING_BINARY ) {
*quoted_len = (unquoted_len * 2) + 2; // each character will be converted to 2 hex digits and prepend '0x' to the result
*quoted = reinterpret_cast<char*>(sqlsrv_malloc(*quoted_len, sizeof(char), 1)); // include space for null terminator
memset(*quoted, '\0', *quoted_len + 1);

unsigned int pos = 0;
(*quoted)[pos++] = '0';
(*quoted)[pos++] = 'x';

for (size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index) {
// On success, snprintf returns the total number of characters written
// On failure, a negative number is returned
// The generated string has a length of at most len - 1, so
// len is 3 (2 hex digits + 1)
int n = snprintf((char*)(*quoted + pos), 3, "%02X", unquoted[index]);
if (n < 0) {
// Something went wrong, simply return 0 (failure)
return 0;
}
os << std::hex << ( int )unquoted[index];
pos += 2;
}
std::basic_string<char> str_hex = os.str();
// each character is represented by 2 digits of hex
size_t unquoted_str_len = unquoted_len * 2; // length returned should not account for null terminator
char* unquoted_str = reinterpret_cast<char*>( sqlsrv_malloc( unquoted_str_len, sizeof( char ), 1 )); // include space for null terminator
strcpy_s( unquoted_str, unquoted_str_len + 1 /* include null terminator*/, str_hex.c_str() );
// include length of '0x' in the binary string
*quoted_len = unquoted_str_len + 2;
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 ));
unsigned int out_current = 0;
// insert '0x'
( *quoted )[out_current++] = '0';
( *quoted )[out_current++] = 'x';
for ( size_t index = 0; index < unquoted_str_len && unquoted_str[index] != '\0'; ++index ) {
( *quoted )[out_current++] = unquoted_str[index];
}
// null terminator
( *quoted )[out_current] = '\0';
sqlsrv_free( unquoted_str );
return 1;
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@ try {
$tbname = "watchdog";
createTable( $cnn, $tbname, array( "system_encoding" => "nvarchar(128)", "utf8_encoding" => "nvarchar(128)", "binary_encoding" => "varbinary(max)"));

$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);

$inputs = array("system_encoding" => $system_param,
"utf8_encoding" => new BindParamOp( 2, $utf8_param, "PDO::PARAM_STR", 0, "PDO::SQLSRV_ENCODING_UTF8" ),
"binary_encoding" => new BindParamOp( 3, $binary_param, "PDO::PARAM_LOB", 0, "PDO::SQLSRV_ENCODING_BINARY" ));
$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);

insertRow($cnn, $tbname, $inputs, "prepareBindParam");
$st->execute();

$data = selectAll($cnn, $tbname);
var_dump($data);
Expand Down

0 comments on commit e9ca8a6

Please sign in to comment.