in source/shared/core_stmt.cpp [364:447]
void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_num, _In_ SQLSMALLINT direction, _Inout_ zval* param_z,
_In_ SQLSRV_PHPTYPE php_out_type, _Inout_ SQLSRV_ENCODING encoding, _Inout_ SQLSMALLINT sql_type, _Inout_ SQLULEN column_size,
_Inout_ SQLSMALLINT decimal_digits)
{
// check is only < because params are 0 based
CHECK_CUSTOM_ERROR(param_num >= SQL_SERVER_MAX_PARAMS, stmt, SQLSRV_ERROR_MAX_PARAMS_EXCEEDED, param_num + 1) {
throw core::CoreException();
}
// Dereference the parameter if necessary
zval* param_ref = param_z;
if (Z_ISREF_P(param_z)) {
ZVAL_DEREF(param_z);
}
sqlsrv_param* param_ptr = stmt->params_container.find_param(param_num, (direction == SQL_PARAM_INPUT));
try {
if (param_ptr == NULL) {
sqlsrv_malloc_auto_ptr<sqlsrv_param> new_param;
if (direction == SQL_PARAM_INPUT) {
// Check if it's a Table-Valued Parameter first
if (Z_TYPE_P(param_z) == IS_ARRAY) {
new_param = new (sqlsrv_malloc(sizeof(sqlsrv_param_tvp))) sqlsrv_param_tvp(param_num, encoding, SQL_SS_TABLE, 0, 0, NULL);
} else {
new_param = new (sqlsrv_malloc(sizeof(sqlsrv_param))) sqlsrv_param(param_num, direction, encoding, sql_type, column_size, decimal_digits);
}
} else if (direction == SQL_PARAM_OUTPUT || direction == SQL_PARAM_INPUT_OUTPUT) {
new_param = new (sqlsrv_malloc(sizeof(sqlsrv_param_inout))) sqlsrv_param_inout(param_num, direction, encoding, sql_type, column_size, decimal_digits, php_out_type);
} else {
SQLSRV_ASSERT(false, "sqlsrv_params_container::insert_param - Invalid parameter direction.");
}
stmt->params_container.insert_param(param_num, new_param);
param_ptr = new_param;
new_param.transferred();
} else if (direction == SQL_PARAM_INPUT
&& param_ptr->sql_data_type != SQL_SS_TABLE
&& param_ptr->strlen_or_indptr == SQL_NULL_DATA) {
// reset the followings for regular input parameters if it was bound as a null param before
param_ptr->sql_data_type = sql_type;
param_ptr->column_size = column_size;
param_ptr->strlen_or_indptr = 0;
}
SQLSRV_ASSERT(param_ptr != NULL, "core_sqlsrv_bind_param: param_ptr is null. Something went wrong.");
bool result = param_ptr->prepare_param(param_ref, param_z);
if (!result && direction == SQL_PARAM_INPUT_OUTPUT) {
CHECK_CUSTOM_ERROR(!result, stmt, SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH, param_num + 1) {
throw core::CoreException();
}
}
// If Always Encrypted is enabled, transfer the known param meta data if applicable, which might alter param_z for decimal types
if (stmt->conn->ce_option.enabled) {
if (param_ptr->sql_data_type == SQL_UNKNOWN_TYPE || param_ptr->column_size == SQLSRV_UNKNOWN_SIZE) {
// meta data parameters are always sorted based on parameter number
param_ptr->copy_param_meta_ae(param_z, stmt->params_container.params_meta_ae[param_num]);
}
}
// Get all necessary values to prepare for SQLBindParameter
param_ptr->process_param(stmt, param_z);
param_ptr->bind_param(stmt);
// When calling SQLDescribeParam() on a parameter targeting a Datetime column, the return values for ParameterType, ColumnSize and DecimalDigits are SQL_TYPE_TIMESTAMP, 23, and 3 respectively.
// For a parameter targeting a SmallDatetime column, the return values are SQL_TYPE_TIMESTAMP, 16, and 0. Inputting these values into SQLBindParameter() results in Operand type clash error.
// This is because SQL_TYPE_TIMESTAMP corresponds to Datetime2 by default, and conversion of Datetime2 to Datetime and conversion of Datetime2 to SmallDatatime is not allowed with encrypted columns.
// To fix the conversion problem, set the SQL_CA_SS_SERVER_TYPE field of the parameter to SQL_SS_TYPE_DATETIME and SQL_SS_TYPE_SMALLDATETIME respectively for a Datetime and Smalldatetime column.
// Note this must be called after SQLBindParameter() or SQLSetDescField() may fail.
// VSO BUG 2693: how to correctly distinguish datetime from datetime2(3)? Both have the same decimal_digits and column_size
if (stmt->conn->ce_option.enabled && param_ptr->sql_data_type == SQL_TYPE_TIMESTAMP) {
if (param_ptr->decimal_digits == 3) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, SQL_IS_INTEGER);
} else if (param_ptr->decimal_digits == 0 && param_ptr->column_size == 16) {
core::SQLSetDescField(stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, SQL_IS_INTEGER);
}
}
}
catch( core::CoreException& e ){
stmt->free_param_data();
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
throw e;
}
}