php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80586 PDO constructor overrides dblib timeout
Submitted: 2021-01-04 15:21 UTC Modified: 2021-01-11 14:57 UTC
From: jeremys at ha dot com Assigned:
Status: Open Package: PDO DBlib
PHP Version: 7.3.25 OS: RHEL 7.8
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: jeremys at ha dot com
New email:
PHP Version: OS:

 

 [2021-01-04 15:21 UTC] jeremys at ha dot com
Description:
------------
When instantiating a new PDO instance with dblib as the driver, passing a non-null value to $options causes PDO_DBLIB to override the configured timeout and default it to 30 seconds for both connects and queries.

For example, when using FreeTDS with a defined query timeout of 300 seconds in freetds.conf ("timeout = 300"), passing an empty array or an array with options that have nothing to do with timeouts sets the query timeout to 30 seconds.

The gist is, passing $options to PDO::__construct() should only set the driver options specifically defined in $options, not every driver option.

https://github.com/php/php-src/blob/caa710037e663fd78f67533b29611183090068b2/ext/pdo_dblib/dblib_driver.c#L481

Test script:
---------------
test-1.php:
<?php

$pdo = new PDO( 'dblib:dbname=northwinds;host=contoso', 'username', 'password' );
try
{
		$pdo->query( "WAITFOR DELAY '00:00:35'" );
		var_dump( $pdo->query( "SELECT 'output' AS Output" )->fetch()['Output'] );
}
catch( Throwable $e )
{
	var_dump( stripos( $pdo->errorInfo()[2], 'DBPROCESS is dead or not enabled' ) === 0 );
}

?>

test-2.php:
<?php

$pdo = new PDO( 'dblib:dbname=northwinds;host=contoso', 'username', 'password', [] );
try
{
		$pdo->query( "WAITFOR DELAY '00:00:35'" );
		var_dump( $pdo->query( "SELECT 'output' AS Output" )->fetch()['Output'] );
}
catch( Throwable $e )
{
	var_dump( stripos( $pdo->errorInfo()[2], 'DBPROCESS is dead or not enabled' ) === 0 );
}

Expected result:
----------------
test-1.php:
string(6) "output"

test-2.php:
string(6) "output"

Actual result:
--------------
test-1.php:
string(6) "output"

test-2.php:
bool(true)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-01-10 21:47 UTC] adambaratz@php.net
This behavior is certainly awkward, but it was intended as a compromise between PDO's generic timeout attribute and the two timeouts that dblib can set. We could certainly come up with new logic for getting to the values to send to dbsetlogintime and dbsettime, but it would require some thought (and likely break a fair bit of existing code in the wild). Is it an option for you to set a timeout value in your PHP userland code instead of relying on freetds.conf?
 [2021-01-10 21:47 UTC] adambaratz@php.net
-Status: Open +Status: Feedback
 [2021-01-11 14:57 UTC] jeremys at ha dot com
-Status: Feedback +Status: Open
 [2021-01-11 14:57 UTC] jeremys at ha dot com
It's possible, but due to the bug in #80587 it doesn't work when using multiple PDO objects in the same script.

But I suppose the point of this bug report is, if I pass PDO::ATTR_ERRMODE to the constructor, only the error mode should be changed. Not timeouts, not datetime conversion, etc.

https://github.com/php/php-src/blob/caa710037e663fd78f67533b29611183090068b2/ext/pdo_dblib/dblib_driver.c#L481

I'm not too familiar with C but I presume the "if (driver_options)" block needs to be changed to only set the appropriate driver option if that particular driver option is passed and is not null.

Example:

if(driver_options) {
    zval *connect_timeout_value;
    zval *query_timeout_value;
    zval *datetime_convert_value;

    /* ignoring PDO_ATTR_TIMEOUT for this example */

    if(connect_timeout_value = zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_DBLIB_ATTR_CONNECTION_TIMEOUT)) {
        dbsetlogintime(zval_get_long(connect_timeout_value));
    }

    if(query_timeout_value = zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_DBLIB_ATTR_QUERY_TIMEOUT)) {
        dbsettime(zval_get_long(query_timeout_value));
    }

    /* more driver option checks here */

    if(datetime_convert_value = zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_DBLIB_ATTR_DATETIME_CONVERT)) {
        H->datetime_convert = zval_get_long(datetime_convert_value);
    }
}


It doesn't make sense for the driver options to be set when $options in the PDO constructor is an empty array, but not when $options is null.
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sat Mar 06 15:01:23 2021 UTC