Simplified implementations of last insert id and quote (#1251)

This commit is contained in:
Jenny Tam 2021-04-29 13:41:29 -07:00 committed by GitHub
parent fcd7b64a4b
commit 7fad440fa1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 62 deletions

View file

@ -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.
@ -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
@ -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 ),
@ -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
@ -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;
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;
}
// 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];
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 {

View file

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