Hi,
I've found a problem that appears to be a driver or database bug.
While fetching a resultset in non-transactional context it is not allowed to start a transaction - this is well-known, you'll get a "New transaction is not allowed because there are other threads running in the session."
However, when using a specific type of COUNT()-query that should only have exactly one result record, the resultset appears to be left open even when sqlsrv_fetch_array() has been called thus leading to unexpected failures when calling sqlsrv_begin_transaction() afterwards.
I have written a reproduction script, please check:
###################################################################
<?php
try {
$db_conn = get_db_conn_sqlsrv();
echo '<br>php version: [' . phpversion() . ']';
echo '<br>sapi name: [' . php_sapi_name() . ']';
echo '<br>sqlsrv_client_info: [' . print_r(sqlsrv_client_info($db_conn), true) . ']';
/*
* symptoms:
* - when trying to open a transaction, this error occurs:
* "New transaction is not allowed because there are other threads running in the session."
* even though the resultset was fetched entirely!
*
* required column properties for physical tables:
* - contains at least one NULL value
* - column has no index
*/
$query = <<<SQL
SELECT COUNT(*) AS num_records FROM
(
SELECT MIN(foo.bar) AS baz
FROM
(
SELECT NULL AS bar
UNION
SELECT 'bar' AS bar
) foo
UNION
SELECT NULL AS bar
) foobar
SQL;
$ps = sqlsrv_prepare($db_conn, $query, array(), array('Scrollable' => SQLSRV_CURSOR_FORWARD));
if (!$ps) {
throw new Exception('no ps');
}
if (!sqlsrv_execute($ps)) {
throw new Exception('execute failed');
}
$result = sqlsrv_fetch_array($ps);
var_dump($result);
// uncomment to workaround the unexpected error below:
//$result = sqlsrv_fetch_array($ps);
//var_dump($result);
// this fails but should not fail
if (!sqlsrv_begin_transaction($db_conn)) {
throw new Exception(print_r(sqlsrv_errors(), true));
}
} catch (Exception $e) {
var_dump($e);
}
function get_db_conn_sqlsrv()
{
$host = "127.0.0.1\SQLExpress,1433";
$conn_info = array(
'Database' => '[to_be_replaced]',
'MultipleActiveResultSets' => true,
'CharacterSet' => 'UTF-8',
'UID' => '[to_be_replaced]',
'PWD' => '[to_be_replaced]'
);
sqlsrv_configure('WarningsReturnAsErrors', 0);
$conn = sqlsrv_connect($host, $conn_info);
if (!$conn) {
throw new Exception('no link');
}
return $conn;
}
###################################################################
Expected result: no error when calling sqlsrv_begin_transaction().
Actual result: "New transaction is not allowed because there are other threads running in the session."
php version: [7.0.15]
sqlsrv_client_info: [
(
[DriverDllName] => msodbcsql11.dll
[DriverODBCVer] => 03.80
[DriverVer] => 12.00.5579
[ExtensionVer] => 4.1.8930.1
)
Best regards
Lars
I've found a problem that appears to be a driver or database bug.
While fetching a resultset in non-transactional context it is not allowed to start a transaction - this is well-known, you'll get a "New transaction is not allowed because there are other threads running in the session."
However, when using a specific type of COUNT()-query that should only have exactly one result record, the resultset appears to be left open even when sqlsrv_fetch_array() has been called thus leading to unexpected failures when calling sqlsrv_begin_transaction() afterwards.
I have written a reproduction script, please check:
###################################################################
<?php
try {
$db_conn = get_db_conn_sqlsrv();
echo '<br>php version: [' . phpversion() . ']';
echo '<br>sapi name: [' . php_sapi_name() . ']';
echo '<br>sqlsrv_client_info: [' . print_r(sqlsrv_client_info($db_conn), true) . ']';
/*
* symptoms:
* - when trying to open a transaction, this error occurs:
* "New transaction is not allowed because there are other threads running in the session."
* even though the resultset was fetched entirely!
*
* required column properties for physical tables:
* - contains at least one NULL value
* - column has no index
*/
$query = <<<SQL
SELECT COUNT(*) AS num_records FROM
(
SELECT MIN(foo.bar) AS baz
FROM
(
SELECT NULL AS bar
UNION
SELECT 'bar' AS bar
) foo
UNION
SELECT NULL AS bar
) foobar
SQL;
$ps = sqlsrv_prepare($db_conn, $query, array(), array('Scrollable' => SQLSRV_CURSOR_FORWARD));
if (!$ps) {
throw new Exception('no ps');
}
if (!sqlsrv_execute($ps)) {
throw new Exception('execute failed');
}
$result = sqlsrv_fetch_array($ps);
var_dump($result);
// uncomment to workaround the unexpected error below:
//$result = sqlsrv_fetch_array($ps);
//var_dump($result);
// this fails but should not fail
if (!sqlsrv_begin_transaction($db_conn)) {
throw new Exception(print_r(sqlsrv_errors(), true));
}
} catch (Exception $e) {
var_dump($e);
}
function get_db_conn_sqlsrv()
{
$host = "127.0.0.1\SQLExpress,1433";
$conn_info = array(
'Database' => '[to_be_replaced]',
'MultipleActiveResultSets' => true,
'CharacterSet' => 'UTF-8',
'UID' => '[to_be_replaced]',
'PWD' => '[to_be_replaced]'
);
sqlsrv_configure('WarningsReturnAsErrors', 0);
$conn = sqlsrv_connect($host, $conn_info);
if (!$conn) {
throw new Exception('no link');
}
return $conn;
}
###################################################################
Expected result: no error when calling sqlsrv_begin_transaction().
Actual result: "New transaction is not allowed because there are other threads running in the session."
php version: [7.0.15]
sqlsrv_client_info: [
(
[DriverDllName] => msodbcsql11.dll
[DriverODBCVer] => 03.80
[DriverVer] => 12.00.5579
[ExtensionVer] => 4.1.8930.1
)
Best regards
Lars