msphpsql
msphpsql copied to clipboard
Warning incorrectly captured as error message when throwing PDOException
PHP version
8.0.19 (also reproduced on 7.4.30)
PHP SQLSRV or PDO_SQLSRV version
pdo_sqlsrv 5.10.1
Microsoft ODBC Driver version
unixODBC 2.3.7
[ODBC Driver 17 for SQL Server]
SQL Server version Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)
Client operating system
Ubuntu Linux 18.04
Table schema
N/A
Problem description
When a PDOException is thrown from a query or stored procedure with multiple statements, if a Warning was issued before the error, this becomes the "message" of the exception object, even though it is not the cause of the exception.
This means the real error message is not visible from logs which rely on $exception->getMessage()
Note that the correct message is available in the errorInfo
property of the exception, but after the warning; it appears that the driver is incorrectly assuming that the first message is the cause of the error, rather than checking the severity of the messages returned.
Expected behavior and actual behavior
The below code should output the same error message for both queries:
Warning only (does not throw exception):
Error only (correct case):
SQLSTATE[23000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot insert the value NULL into column 'NotNullable', table 'tempdb.dbo.#Foo________________________________________________________________________________________________________________0000047B16E0'; column does not allow nulls. INSERT fails.
Warning and then error (incorrect case):
SQLSTATE[23000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot insert the value NULL into column 'NotNullable', table 'tempdb.dbo.#Foo________________________________________________________________________________________________________________0000047B16E0'; column does not allow nulls. INSERT fails.
Instead it outputs a different message for the second query:
Warning only (does not throw exception):
Error only (correct case):
SQLSTATE[23000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Cannot insert the value NULL into column 'NotNullable', table 'tempdb.dbo.#Foo________________________________________________________________________________________________________________0000047B16E0'; column does not allow nulls. INSERT fails.
Warning and then error (incorrect case):
SQLSTATE[01003]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Warning: Null value is eliminated by an aggregate or other SET operation.
Repro code or steps to reproduce
$hostname = '...';
$username = '...';
$password = '...';
$dsn = 'sqlsrv:Server=' . $hostname . ';Database=master;MultipleActiveResultSets=false';
$pdo = new \PDO($dsn, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->setAttribute(PDO::SQLSRV_ATTR_DIRECT_QUERY, true);
$pdo->query('Create Table #Foo (Nullable Int Null, NotNullable Int Not Null)');
$pdo->query('Insert Into #Foo (Nullable, NotNullable) Values (Null, 1)');
echo "Warning only (does not throw exception):\n";
try {
$stmt = $pdo->query('
Select Max(Nullable) as Example From #Foo
Insert Into #Foo (Nullable, NotNullable) Values (Null, 2)
');
$stmt->closeCursor();
}
catch ( PDOException $e ) {
echo $e->getMessage(), "\n";
}
echo "Error only (correct case):\n";
try {
$stmt = $pdo->query('
Select 1 as Example
Insert Into #Foo (Nullable, NotNullable) Values (Null, Null)
');
$stmt->closeCursor();
}
catch ( PDOException $e ) {
echo $e->getMessage(), "\n";
}
echo "Warning and then error (incorrect case):\n";
try {
$stmt = $pdo->query('
Select Max(Nullable) From #Foo
Insert Into #Foo (Nullable, NotNullable) Values (Null, Null)
');
$stmt->closeCursor();
}
catch ( PDOException $e ) {
echo $e->getMessage(), "\n";
}
$stmt = $pdo->query('Select * From #Foo');
$stmt->closeCursor();
$pdo->query('Drop Table #Foo');
Thanks for the details, we'll investigate it.
I reviewed this issue, if $pdo->query
contain multiple queries, all the warnings and errors will go to errorInfo
.
getMessage()
is a function from PHP, the driver won't be able to change it. Looks like it only print out the first message from errorInfo
. It's probably better to not rely on getMessage()
.