Changed how schema is provided for TVP input (#1264)

This commit is contained in:
Jenny Tam 2021-06-02 12:16:51 -07:00 committed by GitHub
parent b49cb5106f
commit a14cb70ad3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 57 deletions

View file

@ -1514,7 +1514,7 @@ struct sqlsrv_param_tvp : public sqlsrv_param
// The following methods are only applicable to a table-valued parameter or its individual columns
int parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z);
void get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ SQLCHAR* table_type_name);
void get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string* table_type_name, _In_ zend_string* schema_name);
void process_param_column_value(_Inout_ sqlsrv_stmt* stmt);
void process_null_param_value(_Inout_ sqlsrv_stmt* stmt);
void populate_cell_placeholder(_Inout_ sqlsrv_stmt* stmt, _In_ int ordinal);

View file

@ -3084,13 +3084,14 @@ void sqlsrv_param_inout::resize_output_string_buffer(_Inout_ zval* param_z, _In_
}
}
void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ SQLCHAR* table_type_name)
void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ zend_string* table_type_name, _In_ zend_string* schema_name)
{
SQLHANDLE chstmt = SQL_NULL_HANDLE;
SQLRETURN rc;
SQLSMALLINT data_type, dec_digits;
SQLINTEGER col_size;
SQLLEN cb_data_type, cb_col_size, cb_dec_digits;
char* table_type = ZSTR_VAL(table_type_name);
core::SQLAllocHandle(SQL_HANDLE_STMT, *(stmt->conn), &chstmt);
@ -3100,21 +3101,11 @@ void sqlsrv_param_tvp::get_tvp_metadata(_In_ sqlsrv_stmt* stmt, _In_ SQLCHAR* ta
}
// Check table type name and see if the schema is specified. Otherwise, assume DBO
std::string type_name(reinterpret_cast<char *>(table_type_name));
std::size_t pos = type_name.find_first_of(".");
if (pos != std::string::npos) {
std::string str1 = type_name.substr(0, pos);
std::string str2 = type_name.substr(pos + 1);
char schema[SS_MAXCOLNAMELEN] = { '\0' };
char type[SS_MAXCOLNAMELEN] = { '\0' };
strcpy_s(schema, SS_MAXCOLNAMELEN, str1.c_str());
strcpy_s(type, SS_MAXCOLNAMELEN, str2.c_str());
rc = SQLColumns(chstmt, NULL, 0, reinterpret_cast<SQLCHAR *>(schema), SQL_NTS, reinterpret_cast<SQLCHAR *>(type), SQL_NTS, NULL, 0);
if (schema_name != NULL) {
char* schema = ZSTR_VAL(schema_name);
rc = SQLColumns(chstmt, NULL, 0, reinterpret_cast<SQLCHAR*>(schema), SQL_NTS, reinterpret_cast<SQLCHAR*>(table_type), SQL_NTS, NULL, 0);
} else {
rc = SQLColumns(chstmt, NULL, 0, NULL, 0, table_type_name, SQL_NTS, NULL, 0);
rc = SQLColumns(chstmt, NULL, 0, NULL, SQL_NTS, reinterpret_cast<SQLCHAR*>(table_type), SQL_NTS, NULL, 0);
}
CHECK_CUSTOM_ERROR(!SQL_SUCCEEDED(rc), stmt, SQLSRV_ERROR_TVP_FETCH_METADATA, param_pos + 1) {
@ -3208,6 +3199,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
// The number of columns in the given table-valued parameter is returned, which may be zero.
HashTable* inputs_ht = Z_ARRVAL_P(param_z);
zend_string *tvp_name = NULL;
zend_string *schema_name = NULL;
zval *tvp_data_z = NULL;
HashPosition pos;
@ -3227,12 +3219,20 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
throw core::CoreException();
}
}
// TODO: Find the docs page somewhere that says a TVP can not be null but it may have null columns??
CHECK_CUSTOM_ERROR(tvp_data_z == NULL || Z_TYPE_P(tvp_data_z) == IS_NULL || Z_TYPE_P(tvp_data_z) != IS_ARRAY, stmt, SQLSRV_ERROR_TVP_INVALID_INPUTS, param_pos + 1) {
throw core::CoreException();
}
// Check if schema is provided by the user
if (zend_hash_move_forward_ex(inputs_ht, &pos) == SUCCESS) {
zval *schema_z = zend_hash_get_current_data_ex(inputs_ht, &pos);
if (schema_z != NULL && Z_TYPE_P(schema_z) == IS_STRING) {
schema_name = Z_STR_P(schema_z);
}
}
// Save the TVP multi-dim array data, which should be something like this
// [
// [r1c1, r1c2, r1c3],
@ -3249,7 +3249,7 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
// Given the table type name, get its column meta data next
size_t total_num_columns = 0;
get_tvp_metadata(stmt, reinterpret_cast<SQLCHAR*>(ZSTR_VAL(tvp_name)));
get_tvp_metadata(stmt, tvp_name, schema_name);
total_num_columns = tvp_columns.size();
// (1) Is the array empty?

View file

@ -68,11 +68,11 @@ try {
['klmop', 45678, '2007-04-08 06:15:15.333'],
];
$tvpType1 = "$schema.SupplierType";
$tvpType2 = "$schema.TestTVP3";
$tvpType1 = "SupplierType";
$tvpType2 = "TestTVP3";
$tvpInput1 = array($tvpType1 => $inputs1);
$tvpInput2 = array($tvpType2 => $inputs2);
$tvpInput1 = array($tvpType1 => $inputs1, $schema);
$tvpInput2 = array($tvpType2 => $inputs2, $schema);
$image = fopen($tvpIncPath. 'superlight_black_f_large.gif', 'rb');

View file

@ -63,10 +63,10 @@ try {
// Use a different schema instead of dbo
$schema = 'Sales DB';
$tvpType = 'TestTVP3';
$tvpTypeName = 'TestTVP3';
$procName = 'SelectTVP3';
cleanup($conn, $schema, $tvpType, $procName, $pre2016);
cleanup($conn, $schema, $tvpTypeName, $procName, $pre2016);
// Create the table type and stored procedure
$conn->exec($createSchema);
@ -89,9 +89,6 @@ try {
$tvpInput = array("" => array());
invokeProc($conn, $callSelectTVP3, $tvpInput, 2);
// The TVP name should include the schema
$tvpTypeName = "$schema.$tvpType";
// Case (3) - null inputs
$tvpInput = array($tvpTypeName => null);
invokeProc($conn, $callSelectTVP3, $tvpInput, 3);
@ -105,7 +102,7 @@ try {
invokeProc($conn, $callSelectTVP3, $tvpInput, 5);
// Case (6) - input rows are not the same size
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 6);
// Case (7) - input row wrong size
@ -113,7 +110,7 @@ try {
$inputs = [
['ABC', 12345, null, null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 7);
// Case (8) - use string keys
@ -121,13 +118,13 @@ try {
$inputs = [
['A' => null, null, null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 8);
// Case (9) - a row is not an array
unset($inputs);
$inputs = [null];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 9);
// Case (10) - a column value used a string key
@ -135,7 +132,7 @@ try {
$inputs = [
['ABC', 12345, "key"=>null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 10);
// Case (11) - invalid input object for a TVP column
@ -149,7 +146,7 @@ try {
['ABC', 1234, $bar],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 11);
// Case (12) - invalid input type for a TVP column
@ -158,7 +155,7 @@ try {
['ABC', &$str, null],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 12);
// Case (13) - bind a TVP as an OUTPUT param
@ -175,10 +172,10 @@ try {
[$utf8, 1234, null],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 14);
cleanup($conn, $schema, $tvpType, $procName, $pre2016);
cleanup($conn, $schema, $tvpTypeName, $procName, $pre2016);
unset($conn);
echo "Done" . PHP_EOL;

View file

@ -66,11 +66,11 @@ $inputs2 = [
['KLMOP', 45678, '2007-04-08 06:15:15.333'],
];
$tvpType1 = "$schema.SupplierType";
$tvpType2 = "$schema.TestTVP3";
$tvpType1 = "SupplierType";
$tvpType2 = "TestTVP3";
$tvpInput1 = array($tvpType1 => $inputs1);
$tvpInput2 = array($tvpType2 => $inputs2);
$tvpInput1 = array($tvpType1 => $inputs1, $schema);
$tvpInput2 = array($tvpType2 => $inputs2, $schema);
$image = fopen($tvpIncPath. 'awc_tee_male_large.gif', 'rb');

View file

@ -18,12 +18,10 @@ function invokeProc($conn, $proc, $tvpInput, $caseNo, $dir = SQLSRV_PARAM_IN)
$stmt = sqlsrv_query($conn, $proc, $params);
if (!$stmt) {
// $errors = sqlsrv_errors(SQLSRV_ERR_ALL);
$errors = sqlsrv_errors();
$errors = sqlsrv_errors(SQLSRV_ERR_ALL);
if (!empty($errors)) {
$count = count($errors);
}
$count = 1;
for ($i = 0; $i < $count; $i++) {
echo "Error $caseNo: ";
echo $errors[$i]['message'] . PHP_EOL;
@ -56,7 +54,7 @@ $conn = connect(array('CharacterSet'=>'UTF-8'));
// Use a different schema instead of dbo
$schema = 'Sales DB';
$tvpType = 'TestTVP3';
$tvpTypeName = 'TestTVP3';
$procName = 'SelectTVP3';
$stmt = sqlsrv_query($conn, "SELECT @@VERSION");
@ -66,7 +64,7 @@ if (sqlsrv_fetch($stmt)) {
$version = explode(' ', $result);
$pre2016 = ($version[3] < '2016');
cleanup($conn, $schema, $tvpType, $procName, $pre2016);
cleanup($conn, $schema, $tvpTypeName, $procName, $pre2016);
// Create table type and a stored procedure
sqlsrv_query($conn, $createSchema);
@ -89,9 +87,6 @@ invokeProc($conn, $callSelectTVP3, $tvpInput, 1);
$tvpInput = array("" => array());
invokeProc($conn, $callSelectTVP3, $tvpInput, 2);
// The TVP name should include the schema
$tvpTypeName = "$schema.$tvpType";
// Case (3) - null inputs
$tvpInput = array($tvpTypeName => null);
invokeProc($conn, $callSelectTVP3, $tvpInput, 3);
@ -105,7 +100,7 @@ $tvpInput = array($str => $inputs);
invokeProc($conn, $callSelectTVP3, $tvpInput, 5);
// Case (6) - input rows are not the same size
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 6);
// Case (7) - input row wrong size
@ -113,7 +108,7 @@ unset($inputs);
$inputs = [
['ABC', 12345, null, null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 7);
// Case (8) - use string keys
@ -121,13 +116,13 @@ unset($inputs);
$inputs = [
['A' => null, null, null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 8);
// Case (9) - a row is not an array
unset($inputs);
$inputs = [null];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 9);
// Case (10) - a column value used a string key
@ -135,7 +130,7 @@ unset($inputs);
$inputs = [
['ABC', 12345, "key"=>null]
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 10);
// Case (11) - invalid input object for a TVP column
@ -149,7 +144,7 @@ $inputs = [
['ABC', 1234, $bar],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 11);
// Case (12) - invalid input type for a TVP column
@ -158,7 +153,7 @@ $inputs = [
['ABC', &$str, null],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 12);
// Case (13) - bind a TVP as an OUTPUT param
@ -175,10 +170,10 @@ $inputs = [
[$utf8, 1234, null],
['DEF', 6789, null],
];
$tvpInput = array($tvpTypeName => $inputs);
$tvpInput = array($tvpTypeName => $inputs, $schema);
invokeProc($conn, $callSelectTVP3, $tvpInput, 14);
cleanup($conn, $schema, $tvpType, $procName, $pre2016);
cleanup($conn, $schema, $tvpTypeName, $procName, $pre2016);
sqlsrv_close($conn);