From 28bd83954c133d623e7eb6a487ed2e84936b4ae8 Mon Sep 17 00:00:00 2001 From: ulvii Date: Mon, 12 Dec 2016 15:39:50 -0800 Subject: [PATCH] Fixing the issue with buffered result sets, when connection option is set to utf8. See issue #192 --- pdo_sqlsrv/core_results.cpp | 41 +++++++++++++++++++++++++++++++------ sqlsrv/core_results.cpp | 41 +++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/pdo_sqlsrv/core_results.cpp b/pdo_sqlsrv/core_results.cpp index a5471ac8a..d01b2820d 100644 --- a/pdo_sqlsrv/core_results.cpp +++ b/pdo_sqlsrv/core_results.cpp @@ -314,6 +314,9 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS conv_matrix[ SQL_C_DOUBLE ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::double_to_wide_string; } + SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : + stmt->encoding()); + // get the meta data and calculate the size of a row buffer SQLULEN offset = null_bytes; for( SQLSMALLINT i = 0; i < col_count; ++i ) { @@ -335,13 +338,30 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS meta[i].length += sizeof( char ) + sizeof( SQLULEN ); // null terminator space offset += meta[i].length; break; + case SQL_CHAR: + case SQL_VARCHAR: + if ( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { + offset += sizeof( void* ); + } + else { + // If encoding is set to UTF-8, the following types are not necessarily column size. + // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. + if ( encoding == SQLSRV_ENCODING( CP_UTF8 )) { + meta[i].length *= sizeof( WCHAR ); + meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space + offset += meta[i].length; + } + else { + meta[i].length += sizeof( SQLULEN ) + sizeof( char ); // length plus null terminator space + offset += meta[i].length; + } + } + break; // these types are the column size case SQL_BINARY: - case SQL_CHAR: case SQL_SS_UDT: case SQL_VARBINARY: - case SQL_VARCHAR: // var* field types are length prefixed if( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { offset += sizeof( void* ); @@ -408,21 +428,30 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS switch( meta[i].type ) { case SQL_BIGINT: - case SQL_CHAR: case SQL_DATETIME: case SQL_DECIMAL: case SQL_GUID: case SQL_NUMERIC: - case SQL_LONGVARCHAR: case SQL_TYPE_DATE: case SQL_SS_TIME2: case SQL_SS_TIMESTAMPOFFSET: case SQL_SS_XML: case SQL_TYPE_TIMESTAMP: - case SQL_VARCHAR: + meta[i].c_type = SQL_C_CHAR; break; - + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + // If encoding is set to UTF-8, the following types are not necessarily column size. + // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. + if ( encoding == SQLSRV_ENCODING( CP_UTF8 )) { + meta[i].c_type = SQL_C_WCHAR; + } + else { + meta[i].c_type = SQL_C_CHAR; + } + break; case SQL_SS_UDT: case SQL_LONGVARBINARY: case SQL_BINARY: diff --git a/sqlsrv/core_results.cpp b/sqlsrv/core_results.cpp index a5471ac8a..d01b2820d 100644 --- a/sqlsrv/core_results.cpp +++ b/sqlsrv/core_results.cpp @@ -314,6 +314,9 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS conv_matrix[ SQL_C_DOUBLE ][ SQL_C_WCHAR ] = &sqlsrv_buffered_result_set::double_to_wide_string; } + SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() : + stmt->encoding()); + // get the meta data and calculate the size of a row buffer SQLULEN offset = null_bytes; for( SQLSMALLINT i = 0; i < col_count; ++i ) { @@ -335,13 +338,30 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS meta[i].length += sizeof( char ) + sizeof( SQLULEN ); // null terminator space offset += meta[i].length; break; + case SQL_CHAR: + case SQL_VARCHAR: + if ( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { + offset += sizeof( void* ); + } + else { + // If encoding is set to UTF-8, the following types are not necessarily column size. + // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. + if ( encoding == SQLSRV_ENCODING( CP_UTF8 )) { + meta[i].length *= sizeof( WCHAR ); + meta[i].length += sizeof( SQLULEN ) + sizeof( WCHAR ); // length plus null terminator space + offset += meta[i].length; + } + else { + meta[i].length += sizeof( SQLULEN ) + sizeof( char ); // length plus null terminator space + offset += meta[i].length; + } + } + break; // these types are the column size case SQL_BINARY: - case SQL_CHAR: case SQL_SS_UDT: case SQL_VARBINARY: - case SQL_VARCHAR: // var* field types are length prefixed if( meta[i].length == sqlsrv_buffered_result_set::meta_data::SIZE_UNKNOWN ) { offset += sizeof( void* ); @@ -408,21 +428,30 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS switch( meta[i].type ) { case SQL_BIGINT: - case SQL_CHAR: case SQL_DATETIME: case SQL_DECIMAL: case SQL_GUID: case SQL_NUMERIC: - case SQL_LONGVARCHAR: case SQL_TYPE_DATE: case SQL_SS_TIME2: case SQL_SS_TIMESTAMPOFFSET: case SQL_SS_XML: case SQL_TYPE_TIMESTAMP: - case SQL_VARCHAR: + meta[i].c_type = SQL_C_CHAR; break; - + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + // If encoding is set to UTF-8, the following types are not necessarily column size. + // We need to call SQLGetData with c_type SQL_C_WCHAR and set the size accordingly. + if ( encoding == SQLSRV_ENCODING( CP_UTF8 )) { + meta[i].c_type = SQL_C_WCHAR; + } + else { + meta[i].c_type = SQL_C_CHAR; + } + break; case SQL_SS_UDT: case SQL_LONGVARBINARY: case SQL_BINARY: