Changed how schema is provided for TVP input (#1264)
This commit is contained in:
parent
b49cb5106f
commit
a14cb70ad3
|
@ -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);
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue