Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save meta data for the fetched result set #855

Merged
merged 10 commits into from
Oct 5, 2018
9 changes: 7 additions & 2 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_FLOAT:
Expand All @@ -1386,6 +1387,7 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_TYPE_DATE:
Expand All @@ -1400,10 +1402,13 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
}
break;
case SQL_BIGINT:
case SQL_CHAR:
case SQL_DECIMAL:
case SQL_GUID:
case SQL_NUMERIC:
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
break;
case SQL_CHAR:
case SQL_GUID:
case SQL_WCHAR:
case SQL_VARCHAR:
case SQL_WVARCHAR:
Expand Down
2 changes: 0 additions & 2 deletions source/pdo_sqlsrv/php_pdo_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,6 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
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;
pdo_param_type* bound_column_param_types;
bool fetch_numeric;
bool fetch_datetime;
Expand Down
4 changes: 4 additions & 0 deletions source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,7 @@ struct sqlsrv_output_param {

// forward decls
struct sqlsrv_result_set;
struct field_meta_data;

// *** parameter metadata struct ***
struct param_meta_data
Expand Down Expand Up @@ -1427,6 +1428,9 @@ struct sqlsrv_stmt : public sqlsrv_context {

std::vector<param_meta_data> param_descriptions;

// meta data for current result set
std::vector<field_meta_data*, sqlsrv_allocator<field_meta_data*>> current_meta_data;

sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC );
virtual ~sqlsrv_stmt( void );

Expand Down
97 changes: 60 additions & 37 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ void send_param_streams( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
void sqlsrv_output_param_dtor( _Inout_ zval* data );
// called when a bound stream parameter is to be destroyed.
void sqlsrv_stream_dtor( _Inout_ zval* data );
bool is_streamable_type( _In_ SQLINTEGER sql_type );

}

Expand Down Expand Up @@ -997,22 +996,24 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
efree( field_value );
field_value = NULL;
*field_len = 0;
}
}
}

// If the php type was not specified set the php type to be the default type.
if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) {

// Get the SQL type of the field.
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC );
}
}
}

// Get the length of the field.
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC );
// If the php type was not specified set the php type to be the default type.
if (sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID) {
SQLSRV_ASSERT(stmt->current_meta_data.size() > field_index, "core_sqlsrv_get_field - meta data vector not in sync" );
sql_field_type = stmt->current_meta_data[field_index]->field_type;
if (stmt->current_meta_data[field_index]->field_precision > 0) {
sql_field_len = stmt->current_meta_data[field_index]->field_precision;
}
else {
sql_field_len = stmt->current_meta_data[field_index]->field_size;
}

// Get the corresponding php type from the sql type.
sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast<SQLINTEGER>( sql_field_type ), static_cast<SQLUINTEGER>( sql_field_len ), prefer_string );
}
// Get the corresponding php type from the sql type.
sqlsrv_php_type = stmt->sql_type_to_php_type(static_cast<SQLINTEGER>(sql_field_type), static_cast<SQLUINTEGER>(sql_field_len), prefer_string);
}

// Verify that we have an acceptable type to convert.
CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) {
Expand Down Expand Up @@ -1441,7 +1442,7 @@ void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )

namespace {

bool is_streamable_type( _In_ SQLLEN sql_type )
bool is_streamable_type( _In_ SQLSMALLINT sql_type )
{
switch( sql_type ) {
case SQL_CHAR:
Expand All @@ -1460,6 +1461,25 @@ bool is_streamable_type( _In_ SQLLEN sql_type )
return false;
}

bool is_a_numeric_type(_In_ SQLSMALLINT sql_type)
{
switch (sql_type) {
case SQL_BIGINT:
case SQL_BIT:
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_TINYINT:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_REAL:
case SQL_DECIMAL:
case SQL_NUMERIC:
return true;
}

return false;
}

void calc_string_size( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_index, _In_ SQLLEN sql_type, _Inout_ SQLLEN& size TSRMLS_DC )
{
try {
Expand Down Expand Up @@ -1693,12 +1713,10 @@ void core_get_field_common( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
{
php_stream* stream = NULL;
sqlsrv_stream* ss = NULL;
SQLLEN sql_type;
SQLSMALLINT sql_type;

SQLRETURN r = SQLColAttributeW( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type );
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
}
SQLSRV_ASSERT(stmt->current_meta_data.size() > field_index, "core_get_field_common - meta data vector not in sync" );
sql_type = stmt->current_meta_data[field_index]->field_type;

CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) {
throw core::CoreException();
Expand Down Expand Up @@ -2208,9 +2226,30 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING,
"Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" );

col_cache* cached = NULL;
if ( NULL != ( cached = static_cast< col_cache* >( zend_hash_index_find_ptr( Z_ARRVAL( stmt->col_cache ), static_cast< zend_ulong >( field_index ))))) {
sql_field_type = cached->sql_type;
sql_display_size = cached->display_size;
}
else {
SQLSRV_ASSERT(stmt->current_meta_data.size() > field_index, "get_field_as_string - meta data vector not in sync" );
sql_field_type = stmt->current_meta_data[field_index]->field_type;

// Calculate the field size.
calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC );

col_cache cache( sql_field_type, sql_display_size );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->col_cache ), field_index, &cache, sizeof( col_cache ) TSRMLS_CC );
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this part been moved up?

Copy link
Contributor Author

@yitam yitam Oct 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we need to get the sql_field_type first before we can decide whether to use CHAR encoding for the numeric types

// Determine the correct encoding
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) {
sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding();
}
// For numbers, no need to convert
if (is_a_numeric_type(sql_field_type)) {
sqlsrv_php_type.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}

// Set the C type and account for null characters at the end of the data.
switch( sqlsrv_php_type.typeinfo.encoding ) {
Expand All @@ -2228,22 +2267,6 @@ void get_field_as_string( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_ind
break;
}

col_cache* cached = NULL;
if ( NULL != ( cached = static_cast< col_cache* >( zend_hash_index_find_ptr( Z_ARRVAL( stmt->col_cache ), static_cast< zend_ulong >( field_index ))))) {
sql_field_type = cached->sql_type;
sql_display_size = cached->display_size;
}
else {
// Get the SQL type of the field. unixODBC 2.3.1 requires wide calls to support pooling
core::SQLColAttributeW( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC );

// Calculate the field size.
calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC );

col_cache cache( sql_field_type, sql_display_size );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->col_cache ), field_index, &cache, sizeof( col_cache ) TSRMLS_CC );
}

// if this is a large type, then read the first few bytes to get the actual length from SQLGetData
if( sql_display_size == 0 || sql_display_size == INT_MAX ||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_MAX - 1 ) {
Expand Down
Loading