Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions ext/pdo_odbc/odbc_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *s

S->H = H;
S->assume_utf8 = H->assume_utf8;
S->stmt_timeout = H->stmt_timeout;

/* before we prepare, we need to peek at the query; if it uses named parameters,
* we want PDO to rewrite them for us */
Expand Down Expand Up @@ -194,6 +195,16 @@ static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *s
}
}

rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)(uintptr_t) S->stmt_timeout, SQL_IS_INTEGER);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_QUERY_TIMEOUT");
SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
if (nsql) {
zend_string_release(nsql);
}
return false;
}

rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);
if (nsql) {
zend_string_release(nsql);
Expand Down Expand Up @@ -341,6 +352,7 @@ static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
{
pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
bool bval;
zend_long lval;

switch (attr) {
case PDO_ODBC_ATTR_ASSUME_UTF8:
Expand Down Expand Up @@ -373,6 +385,29 @@ static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
}
}
return true;
case PDO_ATTR_TIMEOUT:
if (!pdo_get_long_param(&lval, val)) {
return false;
}
/* This will only apply to the current connection and
* any new statements; currently no way to set timeout
* for already existing statements.
*/
H->stmt_timeout = H->conn_timeout = lval;
/* set odbc conn attr in case driver cares */
RETCODE rc = SQLSetConnectAttr(
H->dbc,
SQL_ATTR_CONNECTION_TIMEOUT,
(SQLPOINTER) lval,
SQL_IS_INTEGER
);
if (rc != SQL_SUCCESS) {
pdo_odbc_drv_error(
dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" : "SQLSetConnectAttr AUTOCOMMIT = OFF"
);
return false;
}
return true;
default:
strcpy(H->einfo.last_err_msg, "Unknown Attribute");
H->einfo.what = "setAttribute";
Expand Down Expand Up @@ -408,16 +443,17 @@ static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);
case PDO_ATTR_SERVER_INFO:
return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);
case PDO_ATTR_PREFETCH:
case PDO_ATTR_TIMEOUT:
case PDO_ATTR_CONNECTION_STATUS:
break;
ZVAL_LONG(val, H->conn_timeout);
return 1;
case PDO_ODBC_ATTR_ASSUME_UTF8:
ZVAL_BOOL(val, H->assume_utf8);
return 1;
case PDO_ATTR_AUTOCOMMIT:
ZVAL_BOOL(val, dbh->auto_commit);
return 1;
default:
break;
}
return 0;
}
Expand Down Expand Up @@ -475,7 +511,7 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
pdo_odbc_db_handle *H;
RETCODE rc;
int use_direct = 0;
zend_ulong cursor_lib;
zend_ulong cursor_lib, connection_timeout;

H = pecalloc(1, sizeof(*H), dbh->is_persistent);

Expand Down Expand Up @@ -525,6 +561,15 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
goto fail;
}

/* setup connection timeout */
connection_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 0);
rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_TIMEOUT, (void*)connection_timeout, SQL_IS_INTEGER);
if (rc != SQL_SUCCESS) {
pdo_odbc_drv_error("SQLSetConnectAttr SQL_ATTR_CONNECTION_TIMEOUT");
goto fail;
}
H->stmt_timeout = H->conn_timeout = connection_timeout;

/* a connection string may have = but not ; - i.e. "DSN=PHP" */
if (strchr(dbh->data_source, '=')) {
SQLCHAR dsnbuf[1024];
Expand Down
3 changes: 3 additions & 0 deletions ext/pdo_odbc/php_pdo_odbc_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ typedef struct {
PDO_ODBC_HENV env;
PDO_ODBC_HDBC dbc;
pdo_odbc_errinfo einfo;
SQLUINTEGER conn_timeout;
SQLUINTEGER stmt_timeout;
unsigned assume_utf8:1;
unsigned _spare:31;
} pdo_odbc_db_handle;
Expand All @@ -144,6 +146,7 @@ typedef struct {
char *convbuf;
zend_ulong convbufsize;
HashTable *out_buffers;
SQLUINTEGER stmt_timeout;
unsigned going_long:1;
unsigned assume_utf8:1;
unsigned params_dirty:1;
Expand Down
49 changes: 49 additions & 0 deletions ext/pdo_odbc/tests/timeout.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
PDO_DBLIB: Set query timeouts
--EXTENSIONS--
pdo_dblib
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
require __DIR__ . '/config.inc';
$dsn = getenv('PDO_ODBC_TEST_DSN');
if (!$dsn || strpos($dsn, '=') === false) {
die('skip');
}
?>
--FILE--
<?php
require __DIR__ . '/config.inc';

$dsn = getenv('PDO_ODBC_TEST_DSN');
$user = getenv('PDO_ODBC_TEST_USER');
$password = getenv('PDO_ODBC_TEST_PASS');

$sql = 'WAITFOR DELAY \'00:00:02\'';

// regular timeout attribute, set after instance created, will affect query timeout, causing this query to fail
$db = new PDO($dsn, $user, $password);
$db->setAttribute(PDO::ATTR_TIMEOUT, 1);
$stmt = $db->prepare($sql);
try {
$stmt->execute();
echo "Shouldn't happen\n";
} catch (PDOException) {
echo "OK for timeout set after\n";
}

// regular timeout attribute will affect query timeout, causing this query to fail
$attributes = [PDO::ATTR_TIMEOUT => 1];
$db = new PDO($dsn, $user, $password, $attributes);
$stmt = $db->prepare($sql);
try {
$stmt->execute();
echo "Shouldn't happen\n";
} catch (PDOException) {
echo "OK for timeout set on ctor\n";
}

?>
--EXPECT--
OK for timeout set after
OK for timeout set on ctor
Loading