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

Changed how schema is provided for TVP input #1264

Merged
merged 1 commit into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/shared/core_sqlsrv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
34 changes: 17 additions & 17 deletions source/shared/core_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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) {
Expand Down Expand Up @@ -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;

Expand All @@ -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],
Expand All @@ -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?
Expand Down
8 changes: 4 additions & 4 deletions test/functional/pdo_sqlsrv/pdo_test_TVP_double_tvps.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
25 changes: 11 additions & 14 deletions test/functional/pdo_sqlsrv/pdo_test_TVP_error_cases.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -105,37 +102,37 @@ 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
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
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
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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions test/functional/sqlsrv/sqlsrv_test_TVP_double_tvps.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
29 changes: 12 additions & 17 deletions test/functional/sqlsrv/sqlsrv_test_TVP_error_cases.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -105,37 +100,37 @@ $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
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
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
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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);

Expand Down