Added TVP support to non-procedure statements (#1309)
This commit is contained in:
parent
c87af63d57
commit
36d2704c0a
|
@ -3107,8 +3107,6 @@ void sqlsrv_param_tvp::process_param(_Inout_ sqlsrv_stmt* stmt, _Inout_ zval* pa
|
|||
int num_columns = parse_tv_param_arrays(stmt, param_z);
|
||||
column_size = num_rows;
|
||||
|
||||
buffer = NULL;
|
||||
buffer_length = 0;
|
||||
strlen_or_indptr = (num_columns == 0)? SQL_DEFAULT_PARAM : SQL_DATA_AT_EXEC;
|
||||
} else {
|
||||
// This is one of the constituent columns of the table-valued parameter
|
||||
|
@ -3154,11 +3152,16 @@ int sqlsrv_param_tvp::parse_tv_param_arrays(_Inout_ sqlsrv_stmt* stmt, _Inout_ z
|
|||
throw core::CoreException();
|
||||
}
|
||||
|
||||
// Save the TVP type name for SQLSetDescField later
|
||||
buffer = ZSTR_VAL(tvp_name);
|
||||
buffer_length = SQL_NTS;
|
||||
|
||||
// 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);
|
||||
ZVAL_NEW_STR(&placeholder_z, schema_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3308,6 +3311,34 @@ void sqlsrv_param_tvp::bind_param(_Inout_ sqlsrv_stmt* stmt)
|
|||
return;
|
||||
}
|
||||
|
||||
// Set Table-Valued parameter type name (and the schema where it is defined)
|
||||
SQLHDESC hIpd = NULL;
|
||||
core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0);
|
||||
|
||||
if (buffer != NULL) {
|
||||
// SQL_CA_SS_TYPE_NAME is optional for stored procedure calls, but it must be
|
||||
// specified for SQL statements that are not procedure calls to enable the
|
||||
// server to determine the type of the table-valued parameter.
|
||||
char *tvp_name = reinterpret_cast<char *>(buffer);
|
||||
SQLRETURN r = ::SQLSetDescField(hIpd, param_pos + 1, SQL_CA_SS_TYPE_NAME, reinterpret_cast<SQLCHAR*>(tvp_name), SQL_NTS);
|
||||
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
}
|
||||
if (Z_TYPE(placeholder_z) == IS_STRING) {
|
||||
// If the table type for the table-valued parameter is defined in a different
|
||||
// schema than the default, SQL_CA_SS_SCHEMA_NAME must be specified. If not,
|
||||
// the server will not be able to determine the type of the table-valued parameter.
|
||||
char * schema_name = Z_STRVAL(placeholder_z);
|
||||
SQLRETURN r = ::SQLSetDescField(hIpd, param_pos + 1, SQL_CA_SS_SCHEMA_NAME, reinterpret_cast<SQLCHAR*>(schema_name), SQL_NTS);
|
||||
CHECK_SQL_ERROR_OR_WARNING(r, stmt) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
// Free and reset the placeholder_z
|
||||
zend_string_release(Z_STR(placeholder_z));
|
||||
ZVAL_UNDEF(&placeholder_z);
|
||||
}
|
||||
|
||||
// Bind the TVP columns one by one
|
||||
// Register this object first using SQLSetDescField() for sending TVP data post execution
|
||||
SQLHDESC desc;
|
||||
|
@ -3606,12 +3637,17 @@ bool sqlsrv_params_container::get_next_parameter(_Inout_ sqlsrv_stmt* stmt)
|
|||
// Done now, reset current_param
|
||||
current_param = NULL;
|
||||
return false;
|
||||
} else if (r == SQL_NEED_DATA) {
|
||||
if (param != NULL) {
|
||||
current_param = reinterpret_cast<sqlsrv_param*>(param);
|
||||
SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!");
|
||||
current_param->init_data_from_zval(stmt);
|
||||
} else {
|
||||
// Do not reset current_param when param is NULL, because
|
||||
// it means that data is expected from the existing current_param
|
||||
}
|
||||
}
|
||||
|
||||
current_param = reinterpret_cast<sqlsrv_param*>(param);
|
||||
SQLSRV_ASSERT(current_param != NULL, "sqlsrv_params_container::get_next_parameter - The parameter requested is missing!");
|
||||
current_param->init_data_from_zval(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
97
test/functional/pdo_sqlsrv/pdo_1307_tvp_non_procedure.phpt
Normal file
97
test/functional/pdo_sqlsrv/pdo_1307_tvp_non_procedure.phpt
Normal file
|
@ -0,0 +1,97 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed.
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable)
|
||||
{
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname);
|
||||
$conn->exec($dropTableType);
|
||||
$conn->exec("DROP TABLE IF EXISTS [$testTable]");
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
function readData($conn, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM $testTable ORDER BY id";
|
||||
$stmt = $conn->query($tsql);
|
||||
$stmt->bindColumn('id', $ID);
|
||||
while ($row = $stmt->fetch( PDO::FETCH_BOUND ) ){
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$tvpname = 'pdo_id_table';
|
||||
$testTable = 'pdo_test_table';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE $tvpname AS TABLE(id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
$tsql = "CREATE TABLE $testTable (id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO $testTable SELECT * FROM ?";
|
||||
$tvpinput = array($tvpname => [[1], [2], [3]]);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO $testTable t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
unset($tvpinput);
|
||||
$tvpinput = array($tvpname => [[5], [4], [3], [2]]);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
Done
|
|
@ -0,0 +1,105 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed but TVP and table are defined in a different schema
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable, $schema)
|
||||
{
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname, $schema);
|
||||
$conn->exec($dropTableType);
|
||||
$conn->exec("DROP TABLE IF EXISTS [$schema].[$testTable]");
|
||||
$conn->exec("DROP SCHEMA IF EXISTS [$schema]");
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
function readData($conn, $schema, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM [$schema].[$testTable] ORDER BY id";
|
||||
$stmt = $conn->query($tsql);
|
||||
$stmt->bindColumn('id', $ID);
|
||||
while ($row = $stmt->fetch( PDO::FETCH_BOUND ) ){
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = new PDO("sqlsrv:Server=$server;Database=$databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$tvpname = 'pdo_id_table2';
|
||||
$testTable = 'pdo_test_table2';
|
||||
$schema = 'pdo schema';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
// Create the schema
|
||||
$tsql = "CREATE SCHEMA [$schema]";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE [$schema].[$tvpname] AS TABLE(id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
$tsql = "CREATE TABLE [$schema].[$testTable] (id INT PRIMARY KEY)";
|
||||
$conn->exec($tsql);
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO [$schema].[$testTable] SELECT * FROM ?";
|
||||
$tvpinput = array($tvpname => [[5], [3], [1]], $schema);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO [$schema].[$testTable] t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
unset($tvpinput);
|
||||
$tvpinput = array($tvpname => [[2], [4], [6], [7]], $schema);
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->bindParam(1, $tvpinput, PDO::PARAM_LOB);
|
||||
$result = $stmt->execute();
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
1
|
||||
3
|
||||
5
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
Done
|
111
test/functional/sqlsrv/srv_1307_tvp_non_procedure.phpt
Normal file
111
test/functional/sqlsrv/srv_1307_tvp_non_procedure.phpt
Normal file
|
@ -0,0 +1,111 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed.
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable)
|
||||
{
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname);
|
||||
sqlsrv_query($conn, $dropTableType);
|
||||
sqlsrv_query($conn, "DROP TABLE IF EXISTS [$testTable]");
|
||||
}
|
||||
|
||||
function readData($conn, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM $testTable ORDER BY id";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
while ($result = sqlsrv_fetch($stmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
$ID = sqlsrv_get_field($stmt, 0);
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
$conn = connect();
|
||||
|
||||
$tvpname = 'srv_id_table';
|
||||
$testTable = 'srv_test_table';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE $tvpname AS TABLE(id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
$tsql = "CREATE TABLE $testTable (id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO $testTable SELECT * FROM ?";
|
||||
$params = [
|
||||
[[$tvpname => [[5], [7], [9]]]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO $testTable t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
$params = [
|
||||
[[$tvpname => [[2], [6], [4], [8], [3]]]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_prepare($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
5
|
||||
7
|
||||
9
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
Done
|
119
test/functional/sqlsrv/srv_1307_tvp_non_procedure_schema.phpt
Normal file
119
test/functional/sqlsrv/srv_1307_tvp_non_procedure_schema.phpt
Normal file
|
@ -0,0 +1,119 @@
|
|||
--TEST--
|
||||
Verify Github Issue 1307 is fixed but TVP and table are defined in a different schema
|
||||
--DESCRIPTION--
|
||||
To show that table-valued parameters work with non-procedure statements
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
function cleanup($conn, $tvpname, $testTable, $schema)
|
||||
{
|
||||
$dropTableType = dropTableTypeSQL($conn, $tvpname, $schema);
|
||||
sqlsrv_query($conn, $dropTableType);
|
||||
sqlsrv_query($conn, "DROP TABLE IF EXISTS [$schema].[$testTable]");
|
||||
sqlsrv_query($conn, "DROP SCHEMA IF EXISTS [$schema]");
|
||||
}
|
||||
|
||||
function readData($conn, $schema, $testTable)
|
||||
{
|
||||
$tsql = "SELECT id FROM [$schema].[$testTable] ORDER BY id";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
while ($result = sqlsrv_fetch($stmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
$ID = sqlsrv_get_field($stmt, 0);
|
||||
echo $ID . PHP_EOL;
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
$conn = connect();
|
||||
|
||||
$tvpname = 'srv_id_table2';
|
||||
$testTable = 'srv_test_table2';
|
||||
$schema = 'srv schema';
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
// Create the schema
|
||||
$tsql = "CREATE SCHEMA [$schema]";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Create the table type and test table
|
||||
$tsql = "CREATE TYPE [$schema].[$tvpname] AS TABLE(id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
$tsql = "CREATE TABLE [$schema].[$testTable] (id INT PRIMARY KEY)";
|
||||
$stmt = sqlsrv_query($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Populate the table using the table type
|
||||
$tsql = "INSERT INTO [$schema].[$testTable] SELECT * FROM ?";
|
||||
$params = [
|
||||
[[$tvpname => [[15], [13], [11]], $schema]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_query($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
sqlsrv_free_stmt($stmt);
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
// Use Merge statement next
|
||||
$tsql = <<<QRY
|
||||
MERGE INTO [$schema].[$testTable] t
|
||||
USING ? s ON s.id = t.id
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (id) VALUES(s.id);
|
||||
QRY;
|
||||
|
||||
$params = [
|
||||
[[$tvpname => [[10], [16], [14], [12]], $schema]],
|
||||
];
|
||||
|
||||
$stmt = sqlsrv_prepare($conn, $tsql, $params);
|
||||
if (!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Verify the results
|
||||
readData($conn, $schema, $testTable);
|
||||
|
||||
cleanup($conn, $tvpname, $testTable, $schema);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
11
|
||||
13
|
||||
15
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
Done
|
Loading…
Reference in a new issue