1491 lines
58 KiB
C++
1491 lines
58 KiB
C++
//---------------------------------------------------------------------------------------------------------------------------------
|
|
// File: pdo_stmt.cpp
|
|
//
|
|
// Contents: Implements the PDOStatement object for the PDO_SQLSRV
|
|
//
|
|
// Microsoft Drivers 5.6 for PHP for SQL Server
|
|
// Copyright(c) Microsoft Corporation
|
|
// All rights reserved.
|
|
// MIT License
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
|
|
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
|
|
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//---------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
extern "C" {
|
|
#include "php_pdo_sqlsrv.h"
|
|
}
|
|
|
|
#include "php_pdo_sqlsrv_int.h"
|
|
|
|
// *** internal variables and constants ***
|
|
|
|
namespace {
|
|
|
|
// Maps to the list of PDO::FETCH_ORI_* constants
|
|
SQLSMALLINT odbc_fetch_orientation[] =
|
|
{
|
|
SQL_FETCH_NEXT, // PDO_FETCH_ORI_NEXT
|
|
SQL_FETCH_PRIOR, // PDO_FETCH_ORI_PRIOR
|
|
SQL_FETCH_FIRST, // PDO_FETCH_ORI_FIRST
|
|
SQL_FETCH_LAST, // PDO_FETCH_ORI_LAST
|
|
SQL_FETCH_ABSOLUTE, // PDO_FETCH_ORI_ABS
|
|
SQL_FETCH_RELATIVE // PDO_FETCH_ORI_REL
|
|
};
|
|
|
|
// max length of a field type
|
|
const int SQL_SERVER_IDENT_SIZE_MAX = 128;
|
|
|
|
inline SQLSMALLINT pdo_fetch_ori_to_odbc_fetch_ori ( _In_ enum pdo_fetch_orientation ori )
|
|
{
|
|
SQLSRV_ASSERT( ori >= PDO_FETCH_ORI_NEXT && ori <= PDO_FETCH_ORI_REL, "Fetch orientation out of range.");
|
|
#ifdef _WIN32
|
|
OACR_WARNING_SUPPRESS( 26001, "Buffer length verified above" );
|
|
OACR_WARNING_SUPPRESS( 26000, "Buffer length verified above" );
|
|
#endif
|
|
return odbc_fetch_orientation[ori];
|
|
}
|
|
|
|
// Returns SQLSRV data type for a given PDO type. See pdo_param_type
|
|
// for list of supported pdo types.
|
|
SQLSRV_PHPTYPE pdo_type_to_sqlsrv_php_type( _Inout_ sqlsrv_stmt* driver_stmt, _In_ enum pdo_param_type pdo_type TSRMLS_DC )
|
|
{
|
|
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>(driver_stmt);
|
|
SQLSRV_ASSERT(pdo_stmt != NULL, "pdo_type_to_sqlsrv_php_type: pdo_stmt object was null");
|
|
|
|
switch( pdo_type ) {
|
|
|
|
case PDO_PARAM_BOOL:
|
|
case PDO_PARAM_INT:
|
|
return SQLSRV_PHPTYPE_INT;
|
|
|
|
case PDO_PARAM_STR:
|
|
return SQLSRV_PHPTYPE_STRING;
|
|
|
|
case PDO_PARAM_NULL:
|
|
return SQLSRV_PHPTYPE_NULL;
|
|
|
|
case PDO_PARAM_LOB:
|
|
if (pdo_stmt->fetch_datetime) {
|
|
return SQLSRV_PHPTYPE_DATETIME;
|
|
} else {
|
|
// TODO: This will eventually be changed to SQLSRV_PHPTYPE_STREAM when output streaming is implemented.
|
|
return SQLSRV_PHPTYPE_STRING;
|
|
}
|
|
case PDO_PARAM_STMT:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_PDO_STMT_UNSUPPORTED );
|
|
break;
|
|
|
|
default:
|
|
DIE( "pdo_type_to_sqlsrv_php_type: Unexpected pdo_param_type encountered" );
|
|
}
|
|
|
|
return SQLSRV_PHPTYPE_INVALID; // to prevent compiler warning
|
|
}
|
|
|
|
// Returns a pdo type for a given SQL type. See pdo_param_type
|
|
// for list of supported pdo types.
|
|
inline pdo_param_type sql_type_to_pdo_type( _In_ SQLSMALLINT sql_type )
|
|
{
|
|
pdo_param_type return_type = PDO_PARAM_STR;
|
|
|
|
switch( sql_type ) {
|
|
|
|
case SQL_BIT:
|
|
case SQL_INTEGER:
|
|
case SQL_SMALLINT:
|
|
case SQL_TINYINT:
|
|
case SQL_BIGINT:
|
|
case SQL_BINARY:
|
|
case SQL_CHAR:
|
|
case SQL_DECIMAL:
|
|
case SQL_DOUBLE:
|
|
case SQL_FLOAT:
|
|
case SQL_GUID:
|
|
case SQL_LONGVARBINARY:
|
|
case SQL_LONGVARCHAR:
|
|
case SQL_NUMERIC:
|
|
case SQL_REAL:
|
|
case SQL_SS_TIME2:
|
|
case SQL_SS_TIMESTAMPOFFSET:
|
|
case SQL_SS_UDT:
|
|
case SQL_SS_VARIANT:
|
|
case SQL_SS_XML:
|
|
case SQL_TYPE_DATE:
|
|
case SQL_TYPE_TIMESTAMP:
|
|
case SQL_VARBINARY:
|
|
case SQL_VARCHAR:
|
|
case SQL_WCHAR:
|
|
case SQL_WLONGVARCHAR:
|
|
case SQL_WVARCHAR:
|
|
return_type = PDO_PARAM_STR;
|
|
break;
|
|
|
|
default: {
|
|
DIE( "sql_type_to_pdo_type: Invalid SQL type provided." );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return return_type;
|
|
}
|
|
|
|
// Calls core_sqlsrv_set_scrollable function to set cursor.
|
|
// PDO supports two cursor types: PDO_CURSOR_FWDONLY, PDO_CURSOR_SCROLL.
|
|
void set_stmt_cursors( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
if( Z_TYPE_P( value_z ) != IS_LONG ) {
|
|
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
|
|
}
|
|
|
|
zend_long pdo_cursor_type = Z_LVAL_P( value_z );
|
|
long odbc_cursor_type = -1;
|
|
|
|
switch( pdo_cursor_type ) {
|
|
|
|
case PDO_CURSOR_FWDONLY:
|
|
odbc_cursor_type = SQL_CURSOR_FORWARD_ONLY;
|
|
break;
|
|
|
|
case PDO_CURSOR_SCROLL:
|
|
odbc_cursor_type = SQL_CURSOR_STATIC;
|
|
break;
|
|
|
|
default:
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
|
|
}
|
|
|
|
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC );
|
|
}
|
|
|
|
void set_stmt_cursor_scroll_type( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
if( Z_TYPE_P( value_z ) != IS_LONG ) {
|
|
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
|
|
}
|
|
|
|
if( stmt->cursor_type == SQL_CURSOR_FORWARD_ONLY ) {
|
|
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE );
|
|
}
|
|
|
|
long odbc_cursor_type = static_cast<long>( Z_LVAL_P( value_z ) );
|
|
|
|
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC );
|
|
|
|
return;
|
|
}
|
|
|
|
// Sets the statement encoding. Default encoding on the statement
|
|
// implies use the connection's encoding.
|
|
void set_stmt_encoding( _Inout_ sqlsrv_stmt* stmt, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
// validate the value
|
|
if( Z_TYPE_P( value_z ) != IS_LONG ) {
|
|
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_ENCODING );
|
|
}
|
|
|
|
zend_long attr_value = Z_LVAL_P( value_z );
|
|
|
|
switch( attr_value ) {
|
|
|
|
// when the default encoding is applied to a statement, it means use the creating connection's encoding
|
|
case SQLSRV_ENCODING_DEFAULT:
|
|
case SQLSRV_ENCODING_BINARY:
|
|
case SQLSRV_ENCODING_SYSTEM:
|
|
case SQLSRV_ENCODING_UTF8:
|
|
stmt->set_encoding( static_cast<SQLSRV_ENCODING>( attr_value ));
|
|
break;
|
|
|
|
default:
|
|
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_ENCODING );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// internal helper function to free meta data structures allocated
|
|
void meta_data_free( _Inout_ field_meta_data* meta )
|
|
{
|
|
if( meta->field_name ) {
|
|
meta->field_name.reset();
|
|
}
|
|
sqlsrv_free( meta );
|
|
}
|
|
|
|
zval convert_to_zval( _In_ SQLSRV_PHPTYPE sqlsrv_php_type, _Inout_ void** in_val, _In_opt_ SQLLEN field_len )
|
|
{
|
|
zval out_zval;
|
|
ZVAL_UNDEF(&out_zval);
|
|
|
|
switch (sqlsrv_php_type) {
|
|
|
|
case SQLSRV_PHPTYPE_INT:
|
|
case SQLSRV_PHPTYPE_FLOAT:
|
|
{
|
|
if (*in_val == NULL) {
|
|
ZVAL_NULL(&out_zval);
|
|
}
|
|
else {
|
|
|
|
if (sqlsrv_php_type == SQLSRV_PHPTYPE_INT) {
|
|
ZVAL_LONG(&out_zval, **(reinterpret_cast<int**>(in_val)));
|
|
}
|
|
else {
|
|
ZVAL_DOUBLE(&out_zval, **(reinterpret_cast<double**>(in_val)));
|
|
}
|
|
}
|
|
|
|
if (*in_val) {
|
|
sqlsrv_free(*in_val);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SQLSRV_PHPTYPE_STRING:
|
|
case SQLSRV_PHPTYPE_STREAM: // TODO: this will be moved when output streaming is implemented
|
|
{
|
|
if (*in_val == NULL) {
|
|
|
|
ZVAL_NULL(&out_zval);
|
|
}
|
|
else {
|
|
|
|
ZVAL_STRINGL(&out_zval, reinterpret_cast<char*>(*in_val), field_len);
|
|
sqlsrv_free(*in_val);
|
|
}
|
|
break;
|
|
}
|
|
case SQLSRV_PHPTYPE_DATETIME:
|
|
if (*in_val == NULL) {
|
|
|
|
ZVAL_NULL(&out_zval);
|
|
}
|
|
else {
|
|
|
|
out_zval = *(reinterpret_cast<zval*>(*in_val));
|
|
sqlsrv_free(*in_val);
|
|
}
|
|
break;
|
|
case SQLSRV_PHPTYPE_NULL:
|
|
ZVAL_NULL(&out_zval);
|
|
break;
|
|
default:
|
|
DIE("Unknown php type");
|
|
break;
|
|
}
|
|
|
|
return out_zval;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orientation ori,
|
|
_In_ zend_long offset TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
|
|
_Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
|
|
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
|
|
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC );
|
|
|
|
struct pdo_stmt_methods pdo_sqlsrv_stmt_methods = {
|
|
|
|
pdo_sqlsrv_stmt_dtor,
|
|
pdo_sqlsrv_stmt_execute,
|
|
pdo_sqlsrv_stmt_fetch,
|
|
pdo_sqlsrv_stmt_describe_col,
|
|
pdo_sqlsrv_stmt_get_col_data,
|
|
pdo_sqlsrv_stmt_param_hook,
|
|
pdo_sqlsrv_stmt_set_attr,
|
|
pdo_sqlsrv_stmt_get_attr,
|
|
pdo_sqlsrv_stmt_get_col_meta,
|
|
pdo_sqlsrv_stmt_next_rowset,
|
|
pdo_sqlsrv_stmt_close_cursor
|
|
|
|
};
|
|
|
|
void stmt_option_pdo_scrollable:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
set_stmt_cursors( stmt, value_z TSRMLS_CC );
|
|
}
|
|
|
|
void stmt_option_encoding:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
set_stmt_encoding( stmt, value_z TSRMLS_CC );
|
|
}
|
|
|
|
void stmt_option_direct_query:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
|
|
pdo_stmt->direct_query = ( zend_is_true( value_z )) ? true : false;
|
|
}
|
|
|
|
void stmt_option_cursor_scroll_type:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
set_stmt_cursor_scroll_type( stmt, value_z TSRMLS_CC );
|
|
}
|
|
|
|
void stmt_option_emulate_prepares:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
pdo_stmt_t *pdo_stmt = static_cast<pdo_stmt_t*>( stmt->driver() );
|
|
pdo_stmt->supports_placeholders = ( zend_is_true( value_z )) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL;
|
|
}
|
|
|
|
void stmt_option_fetch_numeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
|
|
pdo_stmt->fetch_numeric = ( zend_is_true( value_z )) ? true : false;
|
|
}
|
|
|
|
void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z TSRMLS_DC )
|
|
{
|
|
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
|
|
pdo_stmt->fetch_datetime = ( zend_is_true( value_z )) ? true : false;
|
|
}
|
|
|
|
// log a function entry point
|
|
#ifndef _WIN32
|
|
#define PDO_LOG_STMT_ENTRY \
|
|
{ \
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); \
|
|
driver_stmt->set_func( __FUNCTION__ ); \
|
|
int length = strlen( __FUNCTION__ ) + strlen( ": entering" ); \
|
|
char func[length+1]; \
|
|
memset(func, '\0', length+1); \
|
|
strcpy_s( func, sizeof( __FUNCTION__ ), __FUNCTION__ ); \
|
|
strcat_s( func, length+1, ": entering" ); \
|
|
LOG( SEV_NOTICE, func ); \
|
|
}
|
|
#else
|
|
#define PDO_LOG_STMT_ENTRY \
|
|
{ \
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data ); \
|
|
driver_stmt->set_func( __FUNCTION__ ); \
|
|
LOG( SEV_NOTICE, __FUNCTION__ ## ": entering" ); \
|
|
}
|
|
#endif
|
|
|
|
// PDO SQLSRV statement destructor
|
|
pdo_sqlsrv_stmt::~pdo_sqlsrv_stmt( void )
|
|
{
|
|
std::for_each( current_meta_data.begin(), current_meta_data.end(), meta_data_free );
|
|
current_meta_data.clear();
|
|
|
|
if( bound_column_param_types ) {
|
|
sqlsrv_free( bound_column_param_types );
|
|
bound_column_param_types = NULL;
|
|
}
|
|
|
|
if( direct_query_subst_string ) {
|
|
// we use efree rather than sqlsrv_free since sqlsrv_free may wrap another allocation scheme
|
|
// and we use estrdup to allocate this string, which uses emalloc
|
|
efree( reinterpret_cast<void*>( const_cast<char*>( direct_query_subst_string )));
|
|
}
|
|
}
|
|
|
|
|
|
// pdo_sqlsrv_stmt_close_cursor
|
|
// Close any open cursors on the statement. Maps to PDO function PDOStatement::closeCursor.
|
|
// Parameters:
|
|
// *stmt - Pointer to current statement
|
|
// Return:
|
|
// Returns 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_close_cursor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
|
|
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_close_cursor: pdo_stmt object was null" );
|
|
|
|
sqlsrv_stmt* driver_stmt = reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data );
|
|
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_close_cursor: driver_data object was null" );
|
|
|
|
// to "close the cursor" means we make the statement ready for execution again. To do this, we
|
|
// skip all the result sets on the current statement.
|
|
// If the statement has not been executed there are no next results to iterate over.
|
|
if ( driver_stmt && driver_stmt->executed == true )
|
|
{
|
|
while( driver_stmt && driver_stmt->past_next_result_end == false ) {
|
|
core_sqlsrv_next_result( driver_stmt TSRMLS_CC );
|
|
}
|
|
}
|
|
}
|
|
catch( core::CoreException& ) {
|
|
|
|
return 0;
|
|
}
|
|
catch( ... ) {
|
|
|
|
DIE( "pdo_sqlsrv_stmt_close_cursor: Unknown exception occurred while advancing to the next result set." );
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_describe_col
|
|
// Gets the metadata for a column based on the column number.
|
|
// Calls the core_sqlsrv_field_metadata function present in the core layer.
|
|
// Parameters:
|
|
// *stmt - pointer to current statement
|
|
// colno - Index of the column which requires description.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_describe_col( _Inout_ pdo_stmt_t *stmt, _In_ int colno TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
SQLSRV_ASSERT(( colno >= 0 ), "pdo_sqlsrv_stmt_describe_col: Column number should be >= 0." );
|
|
SQLSRV_ASSERT( stmt->driver_data != NULL, "pdo_sqlsrv_stmt_describe_col: driver_data object was NULL." );
|
|
|
|
sqlsrv_malloc_auto_ptr<field_meta_data> core_meta_data;
|
|
|
|
try {
|
|
|
|
core_meta_data = core_sqlsrv_field_metadata( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), colno TSRMLS_CC );
|
|
}
|
|
|
|
catch( core::CoreException& ) {
|
|
return 0;
|
|
}
|
|
|
|
catch(...) {
|
|
DIE( "pdo_sqlsrv_stmt_describe_col: Unexpected exception occurred." );
|
|
}
|
|
|
|
pdo_column_data* column_data = &(stmt->columns[colno]);
|
|
|
|
SQLSRV_ASSERT( column_data != NULL, "pdo_sqsrv_stmt_describe_col: pdo_column_data was null" );
|
|
|
|
// Set the name
|
|
column_data->name = zend_string_init( (const char*)core_meta_data->field_name.get(), core_meta_data->field_name_len, 0 );
|
|
core_meta_data->field_name.reset();
|
|
|
|
// Set the maxlen
|
|
column_data->maxlen = ( core_meta_data->field_precision > 0 ) ? core_meta_data->field_precision : core_meta_data->field_size;
|
|
|
|
// Set the precision
|
|
column_data->precision = core_meta_data->field_scale;
|
|
|
|
// Set the param_type
|
|
column_data->param_type = PDO_PARAM_ZVAL;
|
|
|
|
// store the field data for use by pdo_sqlsrv_stmt_get_col_data
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "Invalid driver statement in pdo_sqlsrv_stmt_describe_col" );
|
|
driver_stmt->current_meta_data.push_back( core_meta_data.get() );
|
|
SQLSRV_ASSERT( driver_stmt->current_meta_data.size() == colno + 1, "Meta data vector out of sync with column numbers" );
|
|
core_meta_data.transferred();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
// pdo_sqlsrv_stmt_dtor
|
|
// Maps to PDOStatement::__destruct. Destructor for the PDO Statement.
|
|
// Parameters:
|
|
// *stmt - pointer to current statement
|
|
// Return:
|
|
// 1 for success.
|
|
int pdo_sqlsrv_stmt_dtor( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
|
|
{
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
|
|
LOG( SEV_NOTICE, "pdo_sqlsrv_stmt_dtor: entering" );
|
|
|
|
// if a PDO statement didn't complete preparation, its driver_data can be NULL
|
|
if (driver_stmt == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
// occasionally stmt->dbh->driver_data is already freed and reset but its driver_data is not
|
|
if (stmt->dbh != NULL && stmt->dbh->driver_data == NULL) {
|
|
stmt->driver_data = NULL;
|
|
return 1;
|
|
}
|
|
|
|
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 );
|
|
|
|
stmt->driver_data = NULL;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_execute
|
|
// Maps to PDOStatement::Execute. Executes the prepared statement.
|
|
// Parameters:
|
|
// *stmt - pointer to the current statement.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_execute( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_execute: driver_data object was null" );
|
|
|
|
// prepare for execution by flushing anything remaining in the result set if it wasn't already
|
|
// done before binding parameters
|
|
if( driver_stmt && driver_stmt->executed && !driver_stmt->past_next_result_end ) {
|
|
|
|
while( driver_stmt->past_next_result_end == false ) {
|
|
|
|
core_sqlsrv_next_result( driver_stmt TSRMLS_CC, false );
|
|
}
|
|
}
|
|
|
|
const char* query = NULL;
|
|
unsigned int query_len = 0;
|
|
|
|
// if the user is doing a direct query (PDO::SQLSRV_ATTR_DIRECT_QUERY), set the query here
|
|
if( driver_stmt->direct_query ) {
|
|
|
|
query = driver_stmt->direct_query_subst_string;
|
|
query_len = static_cast<unsigned int>( driver_stmt->direct_query_subst_string_len );
|
|
}
|
|
|
|
// 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
|
|
// Normally it's not a good idea to alter the internal pointer in a hashed array
|
|
// (see pull request 634 on GitHub) but in this case this is for internal use only
|
|
|
|
zend_hash_internal_pointer_reset(driver_stmt->placeholders);
|
|
|
|
query = stmt->active_query_string;
|
|
query_len = static_cast<unsigned int>(stmt->active_query_stringlen);
|
|
}
|
|
|
|
SQLRETURN execReturn = core_sqlsrv_execute( driver_stmt TSRMLS_CC, query, query_len );
|
|
|
|
if ( execReturn == SQL_NO_DATA ) {
|
|
stmt->column_count = 0;
|
|
stmt->row_count = 0;
|
|
driver_stmt->column_count = 0;
|
|
driver_stmt->row_count = 0;
|
|
}
|
|
else {
|
|
if (driver_stmt->column_count == ACTIVE_NUM_COLS_INVALID) {
|
|
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
|
|
driver_stmt->column_count = stmt->column_count;
|
|
}
|
|
else {
|
|
stmt->column_count = driver_stmt->column_count;
|
|
}
|
|
|
|
if (driver_stmt->row_count == ACTIVE_NUM_ROWS_INVALID) {
|
|
// return the row count regardless if there are any rows or not
|
|
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
|
|
driver_stmt->row_count = stmt->row_count;
|
|
}
|
|
else {
|
|
stmt->row_count = driver_stmt->row_count;
|
|
}
|
|
}
|
|
|
|
// workaround for a bug in the PDO driver manager. It is fairly simple to crash the PDO driver manager with
|
|
// the following sequence:
|
|
// 1) Prepare and execute a statement (that has some results with it)
|
|
// 2) call PDOStatement::nextRowset until there are no more results
|
|
// 3) execute the statement again
|
|
// 4) call PDOStatement::getColumnMeta
|
|
// It crashes from what I can tell because there is no metadata because there was no call to
|
|
// pdo_stmt_sqlsrv_describe_col and stmt->columns is NULL on the second call to
|
|
// PDO::execute. My guess is that because stmt->executed is true, it optimizes away a necessary call to
|
|
// pdo_sqlsrv_stmt_describe_col. By setting the stmt->executed flag to 0, this call is not optimized away
|
|
// and the crash disappears.
|
|
if( stmt->columns == NULL ) {
|
|
stmt->executed = 0;
|
|
}
|
|
}
|
|
catch( core::CoreException& /*e*/ ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
catch( ... ) {
|
|
|
|
DIE( "pdo_sqlsrv_stmt_execute: Unexpected exception occurred." );
|
|
|
|
}
|
|
|
|
// success
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
// pdo_sqlsrv_stmt_fetch
|
|
// Maps to PDOStatement::fetch
|
|
// Move the cursor to the record indicated. If the cursor is moved off the end,
|
|
// or before the beginning if a scrollable cursor is created, then FAILURE is returned.
|
|
// Parameters:
|
|
// *stmt - pointer to current statement for which the cursor should be moved.
|
|
// ori - cursor orientation. Maps to the list of PDO::FETCH_ORI_* constants
|
|
// offset - For orientations that use it, offset to move to.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_fetch( _Inout_ pdo_stmt_t *stmt, _In_ enum pdo_fetch_orientation ori,
|
|
_In_ zend_long offset TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
|
|
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_fetch: pdo_stmt object was null" );
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_fetch: driver_data object was null" );
|
|
|
|
// set the types for bound columns to zval so that PDO does no conversion when the value
|
|
// is returned by pdo_sqlsrv_get_col_data. Remember the types that were bound by the user
|
|
// and use it to manually convert data types
|
|
if( stmt->bound_columns ) {
|
|
|
|
pdo_bound_param_data* bind_data = NULL;
|
|
|
|
if( !driver_stmt->bound_column_param_types ) {
|
|
driver_stmt->bound_column_param_types =
|
|
reinterpret_cast<pdo_param_type*>( sqlsrv_malloc( stmt->column_count, sizeof( pdo_param_type ), 0 ));
|
|
std::fill( driver_stmt->bound_column_param_types, driver_stmt->bound_column_param_types + stmt->column_count,
|
|
PDO_PARAM_ZVAL );
|
|
}
|
|
|
|
for( long i = 0; i < stmt->column_count; ++i ) {
|
|
|
|
if (NULL== (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, i))) &&
|
|
(NULL == (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_columns, stmt->columns[i].name))))) {
|
|
|
|
driver_stmt->bound_column_param_types[i] = PDO_PARAM_ZVAL;
|
|
continue;
|
|
}
|
|
|
|
if( bind_data->param_type != PDO_PARAM_ZVAL ) {
|
|
|
|
driver_stmt->bound_column_param_types[i] = bind_data->param_type;
|
|
bind_data->param_type = PDO_PARAM_ZVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
SQLSMALLINT odbc_fetch_ori = pdo_fetch_ori_to_odbc_fetch_ori( ori );
|
|
bool data = core_sqlsrv_fetch( driver_stmt, odbc_fetch_ori, offset TSRMLS_CC );
|
|
|
|
// support for the PDO rowCount method. Since rowCount doesn't call a
|
|
// method, PDO relies on us to fill the pdo_stmt_t::row_count member
|
|
// The if condition was changed from
|
|
// `driver_stmt->past_fetch_end || driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY`
|
|
// because it caused SQLRowCount to be called at each fetch if using a non-forward cursor
|
|
// which is unnecessary and a performance hit
|
|
if( driver_stmt->past_fetch_end || driver_stmt->cursor_type == SQL_CURSOR_DYNAMIC) {
|
|
|
|
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
|
|
driver_stmt->row_count = stmt->row_count;
|
|
|
|
// a row_count of -1 means no rows, but we change it to 0
|
|
if( stmt->row_count == -1 ) {
|
|
|
|
stmt->row_count = 0;
|
|
}
|
|
}
|
|
|
|
// if no data was returned, then return false so data isn't retrieved
|
|
if( !data ) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
catch( core::CoreException& ) {
|
|
|
|
return 0;
|
|
}
|
|
catch( ... ) {
|
|
|
|
DIE ("pdo_sqlsrv_stmt_fetch: Unexpected exception occurred.");
|
|
}
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_get_col_data
|
|
// Called by the set of PDO Fetch functions.
|
|
// Retrieves a single column. PDO driver manager is responsible for freeing the
|
|
// returned buffer. Because PDO can request fields out of order and ODBC does not
|
|
// support out of order field requests, this function should also cache fields.
|
|
// Parameters:
|
|
// stmt - Statement to retrive the column for.
|
|
// colno - Index of the column that needs to be retrieved. Starts with 0.
|
|
// ptr - Returns the buffer containing the column data.
|
|
// len - Length of the buffer returned.
|
|
// caller_frees - Flag to let the PDO driver manager know that it is responsible for
|
|
// freeing the memory.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
|
|
_Out_writes_bytes_opt_(*len) char **ptr, _Inout_ size_t *len, _Out_opt_ int *caller_frees TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
|
|
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_get_col_data: pdo_stmt object was null" );
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_get_col_data: driver_data object was null" );
|
|
|
|
CHECK_CUSTOM_ERROR((colno < 0), driver_stmt, PDO_SQLSRV_ERROR_INVALID_COLUMN_INDEX ) {
|
|
return 0;
|
|
}
|
|
|
|
// Let PDO free the memory after use.
|
|
*caller_frees = 1;
|
|
|
|
// translate the pdo type to a type the core layer understands
|
|
sqlsrv_phptype sqlsrv_php_type;
|
|
SQLSRV_ASSERT( colno >= 0 && colno < static_cast<int>( driver_stmt->current_meta_data.size()),
|
|
"Invalid column number in pdo_sqlsrv_stmt_get_col_data" );
|
|
|
|
// set the encoding if the user specified one via bindColumn, otherwise use the statement's encoding
|
|
sqlsrv_php_type = driver_stmt->sql_type_to_php_type( static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[colno]->field_type ),
|
|
static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[colno]->field_size ), true );
|
|
|
|
// if a column is bound to a type different than the column type, figure out a way to convert it to the
|
|
// type they want
|
|
if( stmt->bound_columns && driver_stmt->bound_column_param_types[colno] != PDO_PARAM_ZVAL ) {
|
|
|
|
sqlsrv_php_type.typeinfo.type = pdo_type_to_sqlsrv_php_type( driver_stmt,
|
|
driver_stmt->bound_column_param_types[colno]
|
|
TSRMLS_CC );
|
|
|
|
pdo_bound_param_data* bind_data = NULL;
|
|
bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, colno));
|
|
if (bind_data == NULL) {
|
|
// can't find by index then try searching by name
|
|
bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_columns, stmt->columns[colno].name));
|
|
}
|
|
|
|
if( bind_data != NULL && !Z_ISUNDEF(bind_data->driver_params) ) {
|
|
|
|
CHECK_CUSTOM_ERROR( Z_TYPE( bind_data->driver_params ) != IS_LONG, driver_stmt,
|
|
PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA, colno + 1 ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
|
|
CHECK_CUSTOM_ERROR( driver_stmt->bound_column_param_types[colno] != PDO_PARAM_STR
|
|
&& driver_stmt->bound_column_param_types[colno] != PDO_PARAM_LOB, driver_stmt,
|
|
PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING, colno + 1 ) {
|
|
|
|
throw pdo::PDOException();
|
|
}
|
|
|
|
sqlsrv_php_type.typeinfo.encoding = Z_LVAL( bind_data->driver_params );
|
|
|
|
switch( sqlsrv_php_type.typeinfo.encoding ) {
|
|
case SQLSRV_ENCODING_SYSTEM:
|
|
case SQLSRV_ENCODING_BINARY:
|
|
case SQLSRV_ENCODING_UTF8:
|
|
break;
|
|
default:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING, colno );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SQLSRV_PHPTYPE sqlsrv_phptype_out = SQLSRV_PHPTYPE_INVALID;
|
|
core_sqlsrv_get_field( driver_stmt, colno, sqlsrv_php_type, false, *(reinterpret_cast<void**>(ptr)),
|
|
reinterpret_cast<SQLLEN*>( len ), true, &sqlsrv_phptype_out TSRMLS_CC );
|
|
|
|
if ( ptr ) {
|
|
zval* zval_ptr = reinterpret_cast<zval*>( sqlsrv_malloc( sizeof( zval )));
|
|
*zval_ptr = convert_to_zval( sqlsrv_phptype_out, reinterpret_cast<void**>( ptr ), *len );
|
|
*ptr = reinterpret_cast<char*>( zval_ptr );
|
|
*len = sizeof( zval );
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
catch ( core::CoreException& ) {
|
|
return 0;
|
|
}
|
|
catch ( ... ) {
|
|
DIE ("pdo_sqlsrv_stmt_get_col_data: Unexpected exception occurred.");
|
|
}
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_set_attr
|
|
// Maps to the PDOStatement::setAttribute. Sets the attribute on a statement.
|
|
// Parameters:
|
|
// stmt - Current statement on which the attribute should be set.
|
|
// attr - Represents any valid set of attribute constants supported by this driver.
|
|
// val - Attribute value.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *val TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_set_attr: driver_data object was null" );
|
|
|
|
try {
|
|
|
|
switch( attr ) {
|
|
|
|
case SQLSRV_ATTR_DIRECT_QUERY:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_DQ_ATTR_AT_PREPARE_ONLY );
|
|
break;
|
|
|
|
case SQLSRV_ATTR_ENCODING:
|
|
set_stmt_encoding( driver_stmt, val TSRMLS_CC );
|
|
break;
|
|
|
|
case PDO_ATTR_CURSOR:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY );
|
|
break;
|
|
|
|
case SQLSRV_ATTR_QUERY_TIMEOUT:
|
|
core_sqlsrv_set_query_timeout( driver_stmt, val TSRMLS_CC );
|
|
break;
|
|
|
|
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY );
|
|
break;
|
|
|
|
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
|
|
core_sqlsrv_set_buffered_query_limit( driver_stmt, val TSRMLS_CC );
|
|
break;
|
|
|
|
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
|
|
driver_stmt->fetch_numeric = ( zend_is_true( val )) ? true : false;
|
|
break;
|
|
|
|
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
|
|
driver_stmt->fetch_datetime = ( zend_is_true( val )) ? true : false;
|
|
break;
|
|
|
|
case SQLSRV_ATTR_FORMAT_DECIMALS:
|
|
driver_stmt->format_decimals = ( zend_is_true( val )) ? true : false;
|
|
break;
|
|
|
|
case SQLSRV_ATTR_DECIMAL_PLACES:
|
|
core_sqlsrv_set_decimal_places(driver_stmt, val TSRMLS_CC);
|
|
break;
|
|
|
|
default:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_STMT_ATTR );
|
|
break;
|
|
}
|
|
}
|
|
catch( core::CoreException& ) {
|
|
return 0;
|
|
}
|
|
|
|
catch ( ... ) {
|
|
DIE ("pdo_sqlsrv_stmt_set_attr: Unexpected exception occurred.");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
// pdo_sqlsrv_stmt_get_attr
|
|
// Maps to the PDOStatement::getAttribute. Gets the value of a given attribute on a statement.
|
|
// Parameters:
|
|
// stmt - Current statement for which the attribute value is requested.
|
|
// attr - Represents any valid set of attribute constants supported by this driver.
|
|
// return_value - Attribute value.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _Inout_ zval *return_value TSRMLS_DC )
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT(( driver_stmt != NULL ), "pdo_sqlsrv_stmt_get_attr: stmt->driver_data was null" );
|
|
|
|
try {
|
|
|
|
switch( attr ) {
|
|
|
|
case SQLSRV_ATTR_DIRECT_QUERY:
|
|
{
|
|
ZVAL_BOOL( return_value, driver_stmt->direct_query );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_ENCODING:
|
|
{
|
|
ZVAL_LONG( return_value, driver_stmt->encoding() );
|
|
break;
|
|
}
|
|
|
|
case PDO_ATTR_CURSOR:
|
|
{
|
|
ZVAL_LONG( return_value, ( driver_stmt->cursor_type != SQL_CURSOR_FORWARD_ONLY ?
|
|
PDO_CURSOR_SCROLL : PDO_CURSOR_FWDONLY ));
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
|
{
|
|
ZVAL_LONG( return_value, driver_stmt->cursor_type );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
|
|
{
|
|
ZVAL_LONG( return_value, driver_stmt->buffered_query_limit );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_QUERY_TIMEOUT:
|
|
{
|
|
ZVAL_LONG( return_value, ( driver_stmt->query_timeout == QUERY_TIMEOUT_INVALID ? 0 : driver_stmt->query_timeout ));
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
|
|
{
|
|
ZVAL_BOOL( return_value, driver_stmt->fetch_numeric );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
|
|
{
|
|
ZVAL_BOOL( return_value, driver_stmt->fetch_datetime );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_FORMAT_DECIMALS:
|
|
{
|
|
ZVAL_BOOL( return_value, driver_stmt->format_decimals );
|
|
break;
|
|
}
|
|
|
|
case SQLSRV_ATTR_DECIMAL_PLACES:
|
|
{
|
|
ZVAL_LONG( return_value, driver_stmt->decimal_places );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_STMT_ATTR );
|
|
break;
|
|
}
|
|
}
|
|
catch( core::CoreException& ) {
|
|
|
|
return 0;
|
|
}
|
|
catch ( ... ) {
|
|
|
|
DIE ("pdo_sqlsrv_stmt_get_attr: Unexpected exception occurred.");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_get_col_meta
|
|
// Maps to PDOStatement::getColumnMeta. Return extra metadata.
|
|
// Though we don't return any extra metadata, PDO relies on us to
|
|
// create the associative array that holds the standard information,
|
|
// so we create one and return it for PDO's use.
|
|
// Parameters:
|
|
// stmt - Current statement.
|
|
// colno - The index of the field for which to return the metadata.
|
|
// return_value - zval* consisting of the metadata.
|
|
// Return:
|
|
// FAILURE for failure, SUCCESS for success.
|
|
int pdo_sqlsrv_stmt_get_col_meta( _Inout_ pdo_stmt_t *stmt, _In_ zend_long colno, _Inout_ zval *return_value TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: pdo_stmt object was null" );
|
|
SQLSRV_ASSERT( Z_TYPE_P( return_value ) == IS_NULL, "Metadata already has value. Must be NULL." );
|
|
|
|
sqlsrv_stmt* driver_stmt = static_cast<sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_get_col_meta: stmt->driver_data was null");
|
|
|
|
// Based on PDOStatement::getColumnMeta API, this should return FALSE
|
|
// if the requested column does not exist in the result set, or if
|
|
// no result set exists. Thus, do not use SQLSRV_ASSERT, which causes
|
|
// the script to fail right away. Instead, log this warning if logging
|
|
// is enabled
|
|
if (colno < 0 || colno >= stmt->column_count || stmt->columns == NULL) {
|
|
LOG( SEV_WARNING, "Invalid column number %1!d!", colno );
|
|
return FAILURE;
|
|
}
|
|
|
|
// initialize the array to nothing, as PDO requires us to create it
|
|
core::sqlsrv_array_init( *driver_stmt, return_value TSRMLS_CC );
|
|
|
|
sqlsrv_malloc_auto_ptr<field_meta_data> core_meta_data;
|
|
|
|
core_meta_data = core_sqlsrv_field_metadata( driver_stmt, (SQLSMALLINT) colno TSRMLS_CC );
|
|
|
|
// add the following fields: flags, native_type, driver:decl_type, table
|
|
add_assoc_long( return_value, "flags", 0 );
|
|
|
|
// get the name of the data type
|
|
char field_type_name[SQL_SERVER_IDENT_SIZE_MAX] = {'\0'};
|
|
SQLSMALLINT out_buff_len;
|
|
SQLLEN not_used;
|
|
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TYPE_NAME, field_type_name,
|
|
sizeof( field_type_name ), &out_buff_len, ¬_used TSRMLS_CC );
|
|
add_assoc_string( return_value, "sqlsrv:decl_type", field_type_name );
|
|
|
|
// get the PHP type of the column. The types returned here mirror the types returned by debug_zval_dump when
|
|
// given a variable of the same type. However, debug_zval_dump also gives the length of a string, and we only
|
|
// say string, since the length is given in another field of the metadata array.
|
|
long pdo_type = sql_type_to_pdo_type( core_meta_data->field_type );
|
|
switch( pdo_type ) {
|
|
case PDO_PARAM_STR:
|
|
{
|
|
//Declarations eliminate compiler warnings about string constant to char* conversions
|
|
std::string key = "native_type";
|
|
std::string str = "string";
|
|
add_assoc_string( return_value, &key[0], &str[0] );
|
|
}
|
|
break;
|
|
default:
|
|
DIE( "pdo_sqlsrv_stmt_get_col_data: Unknown PDO type returned" );
|
|
break;
|
|
}
|
|
|
|
// add the table name of the field. All the tests so far show this to always be "", but we adhere to the PDO spec
|
|
char table_name[SQL_SERVER_IDENT_SIZE_MAX] = {'\0'};
|
|
SQLLEN field_type_num;
|
|
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TABLE_NAME, table_name, SQL_SERVER_IDENT_SIZE_MAX,
|
|
&out_buff_len, &field_type_num TSRMLS_CC );
|
|
add_assoc_string( return_value, "table", table_name );
|
|
|
|
if( stmt->columns && stmt->columns[colno].param_type == PDO_PARAM_ZVAL ) {
|
|
add_assoc_long( return_value, "pdo_type", pdo_type );
|
|
}
|
|
|
|
// this will ensure that the field_name field, which is an auto pointer gets freed.
|
|
(*core_meta_data).~field_meta_data();
|
|
}
|
|
catch( core::CoreException& ) {
|
|
|
|
return FAILURE;
|
|
}
|
|
catch(...) {
|
|
|
|
DIE( "pdo_sqlsrv_stmt_get_col_meta: Unknown exception occurred while retrieving metadata." );
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
// pdo_sqlsrv_stmt_next_rowset
|
|
// Maps to PDOStatement::nextRowset.
|
|
// Move the cursor to the beginning of the next rowset in a multi-rowset result.
|
|
// Clears the field cache from the last row retrieved using pdo_sqlsrv_stmt_get_col_data.
|
|
// Calls core_sqlsrv_next_result using the core_stmt found within stmt->driver_data.
|
|
// If another result set is available, this function returns 1. Otherwise it returns 0.
|
|
// Parameters:
|
|
// stmt - PDOStatement object containing the result set.
|
|
// Return:
|
|
// 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
try {
|
|
|
|
SQLSRV_ASSERT( stmt != NULL, "pdo_sqlsrv_stmt_next_rowset: pdo_stmt object was null" );
|
|
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
|
|
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_next_rowset: driver_data object was null" );
|
|
|
|
core_sqlsrv_next_result( static_cast<sqlsrv_stmt*>( stmt->driver_data ) TSRMLS_CC );
|
|
|
|
// clear the current meta data since the new result will generate new meta data
|
|
std::for_each( driver_stmt->current_meta_data.begin(), driver_stmt->current_meta_data.end(), meta_data_free );
|
|
driver_stmt->current_meta_data.clear();
|
|
|
|
// if there are no more result sets, return that it failed.
|
|
if( driver_stmt->past_next_result_end == true ) {
|
|
return 0;
|
|
}
|
|
|
|
stmt->column_count = core::SQLNumResultCols( driver_stmt TSRMLS_CC );
|
|
|
|
// return the row count regardless if there are any rows or not
|
|
stmt->row_count = core::SQLRowCount( driver_stmt TSRMLS_CC );
|
|
|
|
driver_stmt->column_count = stmt->column_count;
|
|
driver_stmt->row_count = stmt->row_count;
|
|
}
|
|
catch( core::CoreException& ) {
|
|
|
|
return 0;
|
|
}
|
|
catch( ... ) {
|
|
|
|
DIE( "pdo_sqlsrv_stmt_next_rowset: Unknown exception occurred while advancing to the next result set." );
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// pdo_sqlsrv_stmt_param_hook
|
|
// Maps to PDOStatement::bindColumn.
|
|
// Called by PDO driver manager to bind a parameter or column.
|
|
// This function pulls several duties for binding parameters and columns.
|
|
// It takes an event as a parameter that explains what the function should do on
|
|
// behalf of a parameter or column. We only use one of these events,
|
|
// PDO_PARAM_EVT_EXEC_PRE, the remainder simply return.
|
|
// Paramters:
|
|
// stmt - PDO Statement object to bind a parameter.
|
|
// param - paramter to bind.
|
|
// event_type - Event to bind a parameter
|
|
// Return:
|
|
// Returns 0 for failure, 1 for success.
|
|
int pdo_sqlsrv_stmt_param_hook( _Inout_ pdo_stmt_t *stmt,
|
|
_Inout_ struct pdo_bound_param_data *param, _In_ enum pdo_param_event event_type TSRMLS_DC)
|
|
{
|
|
PDO_RESET_STMT_ERROR;
|
|
|
|
try {
|
|
|
|
switch( event_type ) {
|
|
|
|
// since the param isn't reliable, we don't do anything here
|
|
case PDO_PARAM_EVT_ALLOC:
|
|
{
|
|
pdo_sqlsrv_stmt* driver_stmt = reinterpret_cast<pdo_sqlsrv_stmt*>(stmt->driver_data);
|
|
if (driver_stmt->conn->ce_option.enabled) {
|
|
if (driver_stmt->direct_query) {
|
|
THROW_PDO_ERROR(driver_stmt, PDO_SQLSRV_ERROR_CE_DIRECT_QUERY_UNSUPPORTED);
|
|
}
|
|
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
|
|
THROW_PDO_ERROR(driver_stmt, PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED);
|
|
}
|
|
}
|
|
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && (param->param_type & PDO_PARAM_INPUT_OUTPUT)) {
|
|
THROW_PDO_ERROR(driver_stmt, PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED);
|
|
}
|
|
}
|
|
break;
|
|
case PDO_PARAM_EVT_FREE:
|
|
break;
|
|
// bind the parameter in the core layer
|
|
case PDO_PARAM_EVT_EXEC_PRE:
|
|
{
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
// skip column bindings
|
|
if( !param->is_param ) {
|
|
break;
|
|
}
|
|
|
|
sqlsrv_stmt* driver_stmt = reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data );
|
|
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_param_hook: driver_data object was null" );
|
|
|
|
// prepare for binding parameters by flushing anything remaining in the result set
|
|
if( driver_stmt->executed && !driver_stmt->past_next_result_end ) {
|
|
|
|
while( driver_stmt->past_next_result_end == false ) {
|
|
|
|
core_sqlsrv_next_result( driver_stmt TSRMLS_CC, false );
|
|
}
|
|
}
|
|
|
|
int direction = SQL_PARAM_INPUT;
|
|
SQLSMALLINT sql_type = SQL_UNKNOWN_TYPE;
|
|
SQLULEN column_size = SQLSRV_UNKNOWN_SIZE;
|
|
SQLSMALLINT decimal_digits = 0;
|
|
// determine the direction of the parameter. By default it's input, but if the user specifies a size
|
|
// that means they want output, and if they include the flag, then it's input/output.
|
|
// It's invalid to specify the input/output flag but not specify a length
|
|
CHECK_CUSTOM_ERROR( (param->param_type & PDO_PARAM_INPUT_OUTPUT) && (param->max_value_len == 0),
|
|
driver_stmt, PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION, param->paramno + 1 ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
if( param->max_value_len > 0 || param->max_value_len == SQLSRV_DEFAULT_SIZE ) {
|
|
if( param->param_type & PDO_PARAM_INPUT_OUTPUT ) {
|
|
direction = SQL_PARAM_INPUT_OUTPUT;
|
|
}
|
|
else {
|
|
direction = SQL_PARAM_OUTPUT;
|
|
}
|
|
}
|
|
// if the parameter is output or input/output, translate the type between the PDO::PARAM_* constant
|
|
// and the SQLSRV_PHPTYPE_* constant
|
|
// vso 2829: derive the pdo_type for input/output parameter as well
|
|
int pdo_type = (direction == SQL_PARAM_OUTPUT) ? param->param_type : param->param_type & ~PDO_PARAM_INPUT_OUTPUT;
|
|
SQLSRV_PHPTYPE php_out_type = SQLSRV_PHPTYPE_INVALID;
|
|
switch (pdo_type) {
|
|
case PDO_PARAM_BOOL:
|
|
case PDO_PARAM_INT:
|
|
php_out_type = SQLSRV_PHPTYPE_INT;
|
|
break;
|
|
case PDO_PARAM_STR:
|
|
php_out_type = SQLSRV_PHPTYPE_STRING;
|
|
break;
|
|
// when the user states PDO::PARAM_NULL, they mean send a null no matter what the variable is
|
|
// since the core layer keys off the zval type, we substitute a null for what they gave us
|
|
case PDO_PARAM_NULL:
|
|
{
|
|
zval null_zval;
|
|
php_out_type = SQLSRV_PHPTYPE_NULL;
|
|
|
|
ZVAL_NULL( &null_zval );
|
|
zval_ptr_dtor( ¶m->parameter );
|
|
param->parameter = null_zval;
|
|
break;
|
|
}
|
|
case PDO_PARAM_LOB:
|
|
php_out_type = SQLSRV_PHPTYPE_STREAM;
|
|
break;
|
|
case PDO_PARAM_STMT:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_PDO_STMT_UNSUPPORTED );
|
|
break;
|
|
default:
|
|
SQLSRV_ASSERT( false, "Unknown PDO::PARAM_* constant given." );
|
|
break;
|
|
}
|
|
// set the column size parameter for bind_param if we are expecting something back
|
|
if( direction != SQL_PARAM_INPUT ) {
|
|
switch( php_out_type ) {
|
|
case SQLSRV_PHPTYPE_NULL:
|
|
case SQLSRV_PHPTYPE_STREAM:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE );
|
|
break;
|
|
case SQLSRV_PHPTYPE_INT:
|
|
column_size = SQLSRV_UNKNOWN_SIZE;
|
|
break;
|
|
case SQLSRV_PHPTYPE_STRING:
|
|
{
|
|
CHECK_CUSTOM_ERROR( param->max_value_len <= 0, driver_stmt,
|
|
PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE, param->paramno + 1 ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
column_size = param->max_value_len;
|
|
break;
|
|
}
|
|
default:
|
|
SQLSRV_ASSERT( false, "Invalid PHP type for output parameter. Should have been caught already." );
|
|
break;
|
|
}
|
|
}
|
|
// block all objects from being bound as input or input/output parameters since there is a
|
|
// weird case:
|
|
// $obj = date_create();
|
|
// $s->bindParam( n, $obj, PDO::PARAM_INT ); // anything different than PDO::PARAM_STR
|
|
// that succeeds since the core layer implements DateTime object handling for the sqlsrv
|
|
// 2.0 driver. To be consistent and avoid surprises of one object type working and others
|
|
// not, we block all objects here.
|
|
CHECK_CUSTOM_ERROR( direction != SQL_PARAM_OUTPUT && Z_TYPE( param->parameter ) == IS_OBJECT,
|
|
driver_stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param->paramno + 1 ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
// the encoding by default is that set on the statement
|
|
SQLSRV_ENCODING encoding = driver_stmt->encoding();
|
|
// if the statement's encoding is the default, then use the one on the connection
|
|
if( encoding == SQLSRV_ENCODING_DEFAULT ) {
|
|
encoding = driver_stmt->conn->encoding();
|
|
}
|
|
// if the user provided an encoding, use it instead
|
|
if( !Z_ISUNDEF(param->driver_params) ) {
|
|
CHECK_CUSTOM_ERROR( Z_TYPE( param->driver_params ) != IS_LONG, driver_stmt,
|
|
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
CHECK_CUSTOM_ERROR( pdo_type != PDO_PARAM_STR && pdo_type != PDO_PARAM_LOB, driver_stmt,
|
|
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE, param->paramno + 1 ) {
|
|
throw pdo::PDOException();
|
|
}
|
|
encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
|
|
|
|
switch( encoding ) {
|
|
case SQLSRV_ENCODING_SYSTEM:
|
|
case SQLSRV_ENCODING_BINARY:
|
|
case SQLSRV_ENCODING_UTF8:
|
|
break;
|
|
default:
|
|
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING,
|
|
param->paramno + 1 );
|
|
break;
|
|
}
|
|
}
|
|
// and bind the parameter
|
|
core_sqlsrv_bind_param( driver_stmt, static_cast<SQLUSMALLINT>( param->paramno ), direction, &(param->parameter) , php_out_type, encoding,
|
|
sql_type, column_size, decimal_digits TSRMLS_CC );
|
|
}
|
|
break;
|
|
// undo any work done by the core layer after the statement is executed
|
|
case PDO_PARAM_EVT_EXEC_POST:
|
|
{
|
|
PDO_VALIDATE_STMT;
|
|
PDO_LOG_STMT_ENTRY;
|
|
|
|
// skip column bindings
|
|
if( !param->is_param ) {
|
|
break;
|
|
}
|
|
|
|
core_sqlsrv_post_param( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), param->paramno,
|
|
&(param->parameter) TSRMLS_CC );
|
|
}
|
|
break;
|
|
case PDO_PARAM_EVT_FETCH_PRE:
|
|
break;
|
|
case PDO_PARAM_EVT_FETCH_POST:
|
|
break;
|
|
case PDO_PARAM_EVT_NORMALIZE:
|
|
break;
|
|
default:
|
|
DIE( "pdo_sqlsrv_stmt_param_hook: Unknown event type" );
|
|
break;
|
|
}
|
|
}
|
|
catch( core::CoreException& ) {
|
|
|
|
return 0;
|
|
}
|
|
catch( ... ) {
|
|
|
|
DIE( "pdo_sqlsrv_stmt_param_hook: Unknown exception" );
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
// Returns a sqlsrv_phptype for a given SQL Server data type.
|
|
sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type, _In_ SQLUINTEGER size, _In_ bool prefer_string_over_stream )
|
|
{
|
|
sqlsrv_phptype sqlsrv_phptype;
|
|
int local_encoding = this->encoding();
|
|
// if the encoding on the connection changed
|
|
if( this->encoding() == SQLSRV_ENCODING_DEFAULT ) {
|
|
local_encoding = conn->encoding();
|
|
SQLSRV_ASSERT( conn->encoding() != SQLSRV_ENCODING_DEFAULT || conn->encoding() == SQLSRV_ENCODING_INVALID,
|
|
"Invalid encoding on the connection. Must not be invalid or default." );
|
|
}
|
|
|
|
sqlsrv_phptype.typeinfo.encoding = local_encoding;
|
|
|
|
switch( sql_type ) {
|
|
case SQL_BIT:
|
|
case SQL_INTEGER:
|
|
case SQL_SMALLINT:
|
|
case SQL_TINYINT:
|
|
if ( this->fetch_numeric ) {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_INT;
|
|
}
|
|
else {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
|
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
|
|
}
|
|
break;
|
|
case SQL_FLOAT:
|
|
case SQL_REAL:
|
|
if ( this->fetch_numeric ) {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_FLOAT;
|
|
}
|
|
else {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
|
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
|
|
}
|
|
break;
|
|
case SQL_TYPE_DATE:
|
|
case SQL_SS_TIMESTAMPOFFSET:
|
|
case SQL_SS_TIME2:
|
|
case SQL_TYPE_TIMESTAMP:
|
|
if ( this->fetch_datetime ) {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_DATETIME;
|
|
}
|
|
else {
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
|
}
|
|
break;
|
|
case SQL_BIGINT:
|
|
case SQL_DECIMAL:
|
|
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:
|
|
case SQL_LONGVARCHAR:
|
|
case SQL_WLONGVARCHAR:
|
|
case SQL_SS_XML:
|
|
case SQL_SS_VARIANT:
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
|
break;
|
|
case SQL_BINARY:
|
|
case SQL_LONGVARBINARY:
|
|
case SQL_VARBINARY:
|
|
case SQL_SS_UDT:
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
|
|
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_BINARY;
|
|
break;
|
|
default:
|
|
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
|
|
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_INVALID;
|
|
break;
|
|
}
|
|
|
|
return sqlsrv_phptype;
|
|
}
|
|
|