diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index 55b4a116..83694f5d 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -545,6 +545,21 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c // prepare our wide char query string core::SQLPrepareW( stmt, reinterpret_cast( wsql_string.get() ), wsql_len TSRMLS_CC ); + + stmt->param_descriptions.clear(); + + // if AE is enabled, get meta data for all parameters before binding them + if( stmt->conn->ce_option.enabled ) { + SQLSMALLINT num_params; + core::SQLNumParams( stmt, &num_params); + for( int i = 0; i < num_params; i++ ) { + param_meta_data param; + + core::SQLDescribeParam( stmt, i + 1, &( param.sql_type ), &( param.column_size ), &( param.decimal_digits ), &( param.nullable ) ); + + stmt->param_descriptions.push_back( param ); + } + } } catch( core::CoreException& ) { diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 1bc680cd..9a44356b 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -156,6 +156,7 @@ OACR_WARNING_POP #include #include #include +#include // included for SQL Server specific constants #include "msodbcsql.h" @@ -1345,6 +1346,23 @@ struct sqlsrv_output_param { // forward decls struct sqlsrv_result_set; +// *** parameter metadata struct *** +struct param_meta_data +{ + SQLSMALLINT sql_type; + SQLSMALLINT decimal_digits; + SQLSMALLINT nullable; + SQLULEN column_size; + + param_meta_data() : sql_type(0), decimal_digits(0), column_size(0), nullable(0) + { + } + + ~param_meta_data() + { + } +}; + // *** Statement resource structure *** struct sqlsrv_stmt : public sqlsrv_context { @@ -1383,6 +1401,8 @@ struct sqlsrv_stmt : public sqlsrv_context { zval col_cache; // Used by get_field_as_string not to call SQLColAttribute() after every fetch. zval active_stream; // the currently active stream reading data from the database + std::vector param_descriptions; + sqlsrv_stmt( _In_ sqlsrv_conn* c, _In_ SQLHANDLE handle, _In_ error_callback e, _In_opt_ void* drv TSRMLS_DC ); virtual ~sqlsrv_stmt( void ); @@ -2007,6 +2027,16 @@ namespace core { } } + inline void SQLNumParams( _Inout_ sqlsrv_stmt* stmt, _Out_opt_ SQLSMALLINT* num_params) + { + SQLRETURN r; + r = ::SQLNumParams( stmt->handle(), num_params ); + + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw CoreException(); + } + } + inline void SQLEndTran( _In_ SQLSMALLINT handleType, _Inout_ sqlsrv_conn* conn, _In_ SQLSMALLINT completionType TSRMLS_DC ) { SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType ); diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index e47bca4d..587866b5 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -102,8 +102,6 @@ void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned in // given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate) void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, _Out_ SQLSMALLINT& sql_type TSRMLS_DC ); -void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ SQLSMALLINT direction, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, - _Out_ SQLSMALLINT& sql_type, _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ); void col_cache_dtor( _Inout_ zval* data_z ); void field_cache_dtor( _Inout_ zval* data_z ); void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ); @@ -466,7 +464,11 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ encoding == SQLSRV_ENCODING_BINARY ), "core_sqlsrv_bind_param: invalid encoding" ); if( stmt->conn->ce_option.enabled && ( sql_type == SQL_UNKNOWN_TYPE || column_size == SQLSRV_UNKNOWN_SIZE )){ - ae_get_sql_type_info( stmt, param_num, direction, param_z, encoding, sql_type, column_size, decimal_digits TSRMLS_CC ); + // use the meta data only if the user has not specified the sql type or column size + sql_type = stmt->param_descriptions[param_num].sql_type; + column_size = stmt->param_descriptions[param_num].column_size; + decimal_digits = stmt->param_descriptions[param_num].decimal_digits; + // change long to double if the sql type is decimal if(( sql_type == SQL_DECIMAL || sql_type == SQL_NUMERIC ) && Z_TYPE_P(param_z) == IS_LONG ) convert_to_double( param_z ); @@ -2085,13 +2087,6 @@ void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned in } } -void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ SQLSMALLINT direction, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, - _Out_ SQLSMALLINT& sql_type, _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ) -{ - SQLSMALLINT Nullable; - core::SQLDescribeParam( stmt, paramno + 1, &sql_type, &column_size, &decimal_digits, &Nullable ); -} - void col_cache_dtor( _Inout_ zval* data_z ) { col_cache* cache = static_cast( Z_PTR_P( data_z )); diff --git a/test/functional/pdo_sqlsrv/pdo_040_error_information.phpt b/test/functional/pdo_sqlsrv/pdo_040_error_information.phpt index 9de1bcd5..93036cbb 100644 --- a/test/functional/pdo_sqlsrv/pdo_040_error_information.phpt +++ b/test/functional/pdo_sqlsrv/pdo_040_error_information.phpt @@ -8,12 +8,12 @@ require_once("MsCommon_mid-refactor.inc"); try { // Connect - // set errmode to silent to compare sqlstates in the test - $conn = connect("", array(), PDO::ERRMODE_SILENT); + $conn = connect(); // Create table $tableName = 'pdo_040test'; - // common function insertRow() is not used here since the test deliberately executes an invalid insertion statement + // common function insertRow() is not used here since the test deliberately + // executes an invalid insertion statement // thus it's not necessary to create an encrypted column for testing column encryption $sql = "CREATE TABLE $tableName (code INT)"; $stmt = $conn->exec($sql); @@ -22,37 +22,36 @@ try { // Number of supplied values does not match table definition $sql = "INSERT INTO $tableName VALUES (?,?)"; $stmt = $conn->prepare($sql); - $params = array(2010,"London"); + $params = array(2010, "London"); // SQL statement has an error, which is then reported - $stmt->execute($params); - $error = $stmt->errorInfo(); - - $success = true; + if ($stmt) { + $stmt->execute($params); + } +} catch (PDOException $e) { + $error = $e->errorInfo; + $success = false; if (!isColEncrypted()) { // 21S01 is the expected ODBC Column name or number of supplied values does not match table definition error - if ($error[0] != "21S01") { - $success = false; + if ($error[0] === "21S01") { + $success = true; } } else { // 07009 is the expected ODBC Invalid Descriptor Index error - if ($error[0] != "07009") { - $success = false; + if ($error[0] === "07009") { + $success = true; } - } - - // Close connection - dropTable($conn, $tableName); - unset($stmt); - unset($conn); - + } if ($success) { print "Done"; } else { var_dump($error); } -} catch (PDOException $e) { - var_dump($e->errorInfo); +} finally { + // Clean up and close connection + dropTable($conn, $tableName); + unset($stmt); + unset($conn); } ?> diff --git a/test/functional/pdo_sqlsrv/pdo_065_construct_prefetch.phpt b/test/functional/pdo_sqlsrv/pdo_065_construct_prefetch.phpt index e0be3f60..463b7ac4 100644 --- a/test/functional/pdo_sqlsrv/pdo_065_construct_prefetch.phpt +++ b/test/functional/pdo_sqlsrv/pdo_065_construct_prefetch.phpt @@ -7,20 +7,14 @@ Exception is thrown for the unsupported connection attribute ATTR_PREFETCH only --FILE-- true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); $conn = new PDO($dsn, $uid, $pwd, $attr); - echo "Error from supported attribute (ATTR_PREFETCH) is silented\n\n"; + echo "Error from unsupported attribute (ATTR_PREFETCH) is silenced\n\n"; unset($conn); echo "Testing a connection with ATTR_PREFETCH after ERRMODE_EXCEPTION...\n"; @@ -35,7 +29,7 @@ try { --EXPECT-- Testing a connection with ATTR_PREFETCH before ERRMODE_EXCEPTION... -Error from supported attribute (ATTR_PREFETCH) is silented +Error from unsupported attribute (ATTR_PREFETCH) is silenced Testing a connection with ATTR_PREFETCH after ERRMODE_EXCEPTION... Exception from unsupported attribute (ATTR_PREFETCH) is caught diff --git a/test/functional/sqlsrv/sqlsrv_buffered.phpt b/test/functional/sqlsrv/sqlsrv_buffered.phpt index e05267aa..a733a10f 100644 --- a/test/functional/sqlsrv/sqlsrv_buffered.phpt +++ b/test/functional/sqlsrv/sqlsrv_buffered.phpt @@ -12,9 +12,9 @@ function insertOneRow($conn, $tableName) $result = null; if (AE\isColEncrypted()) { $data = array("Field2" => "This is field 2.", - "Field3" => array("010203", null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_IMAGE), + "Field3" => "010203", "Field4" => "This is field 4.", - "Field5" => array("040506", null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARBINARY('max')), + "Field5" => "040506", "Field6" => "This is field 6.", "Field7" => "This is field 7."); $query = AE\getInsertSqlPlaceholders($tableName, $data); @@ -42,8 +42,7 @@ function updateRow($conn, $tableName, $updateField, $params) if (AE\isColEncrypted()) { $query = "UPDATE $tableName SET $updateField=? WHERE $condField = ?"; - - array_push($params, array($condition, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_NVARCHAR('max'))); + array_push($params, $condition); } else { $query = "UPDATE $tableName SET $updateField=? WHERE $condField = '$condition'"; }