Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Issue 101: Added connect timeout keyword.

  • Loading branch information...
commit c6608858a6ac22f0de7afac1c6ebf06f548838c9 1 parent d19c67b
Michael Kleehammer authored committed
View
15 src/connection.cpp
@@ -45,7 +45,7 @@ Connection_Validate(PyObject* self)
return cnxn;
}
-static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi)
+static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi, long timeout)
{
// This should have been checked by the global connect function.
I(PyString_Check(pConnectString) || PyUnicode_Check(pConnectString));
@@ -70,6 +70,15 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi)
SQLRETURN ret;
+ if (timeout > 0)
+ {
+ Py_BEGIN_ALLOW_THREADS
+ ret = SQLSetConnectAttr(hdbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)timeout, SQL_IS_UINTEGER);
+ Py_END_ALLOW_THREADS
+ if (!SQL_SUCCEEDED(ret))
+ RaiseErrorFromHandle("SQLSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT)", hdbc, SQL_NULL_HANDLE);
+ }
+
if (!fAnsi)
{
SQLWChar connectString(pConnectString);
@@ -124,7 +133,7 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, bool fAnsi)
}
-PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi, bool fUnicodeResults)
+PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi, bool fUnicodeResults, long timeout)
{
// pConnectString
// A string or unicode object. (This must be checked by the caller.)
@@ -147,7 +156,7 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi,
if (!SQL_SUCCEEDED(ret))
return RaiseErrorFromHandle("SQLAllocHandle", SQL_NULL_HANDLE, SQL_NULL_HANDLE);
- if (!Connect(pConnectString, hdbc, fAnsi))
+ if (!Connect(pConnectString, hdbc, fAnsi, timeout))
{
// Connect has already set an exception.
Py_BEGIN_ALLOW_THREADS
View
2  src/connection.h
@@ -71,6 +71,6 @@ struct Connection
* Used by the module's connect function to create new connection objects. If unable to connect to the database, an
* exception is set and zero is returned.
*/
-PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi, bool fUnicodeResults);
+PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi, bool fUnicodeResults, long timeout);
#endif
View
59 src/pyodbcmodule.cpp
@@ -280,6 +280,7 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
int fAutoCommit = 0;
int fAnsi = 0; // force ansi
int fUnicodeResults = 0;
+ long timeout = 0;
Py_ssize_t size = args ? PyTuple_Size(args) : 0;
@@ -334,6 +335,13 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
fUnicodeResults = PyObject_IsTrue(value);
continue;
}
+ if (_strcmpi(szKey, "timeout") == 0)
+ {
+ timeout = PyInt_AsLong(value);
+ if (PyErr_Occurred())
+ return 0;
+ continue;
+ }
// Anything else must be a string that is appended, along with the keyword to the connection string.
@@ -384,7 +392,7 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
return 0;
}
- return (PyObject*)Connection_New(pConnectString.Get(), fAutoCommit != 0, fAnsi != 0, fUnicodeResults != 0);
+ return (PyObject*)Connection_New(pConnectString.Get(), fAutoCommit != 0, fAnsi != 0, fUnicodeResults != 0, timeout);
}
@@ -481,14 +489,14 @@ mod_timestampfromticks(PyObject* self, PyObject* args)
}
static char connect_doc[] =
- "connect(str, autocommit=False, ansi=False, **kwargs) --> Connection\n"
+ "connect(str, autocommit=False, ansi=False, timeout=0, **kwargs) --> Connection\n"
"\n"
"Accepts an ODBC connection string and returns a new Connection object.\n"
"\n"
"The connection string will be passed to SQLDriverConnect, so a DSN connection\n"
"can be created using:\n"
"\n"
- " DSN=DataSourceName;UID=user;PWD=password\n"
+ " cnxn = pyodbc.connect('DSN=DataSourceName;UID=user;PWD=password')\n"
"\n"
"To connect without requiring a DSN, specify the driver and connection\n"
"information:\n"
@@ -499,33 +507,42 @@ static char connect_doc[] =
"documentation or the documentation of your ODBC driver for details.\n"
"\n"
"The connection string can be passed as the string `str`, as a list of keywords,\n"
- "or a combination of the two. Any keywords except autocommit and ansi (see\n"
- "below) are simply added to the connection string.\n"
+ "or a combination of the two. Any keywords except autocommit, ansi, and timeout\n"
+ "(see below) are simply added to the connection string.\n"
"\n"
" connect('server=localhost;user=me')\n"
" connect(server='localhost', user='me')\n"
" connect('server=localhost', user='me')\n"
"\n"
- "The DB API recommends the keywords 'user', 'password', and 'host', but these are\n"
- "not valid ODBC keywords, so these will be converted to 'uid', 'pwd', and\n"
+ "The DB API recommends the keywords 'user', 'password', and 'host', but these\n"
+ "are not valid ODBC keywords, so these will be converted to 'uid', 'pwd', and\n"
"'server'.\n"
"\n"
- "autocommit\n"
+ "Special Keywords\n"
"\n"
- " If False or zero, the default, transactions are created automatically as\n"
- " defined in the DB API 2. If True or non-zero, the connection is put into ODBC\n"
- " autocommit mode and statements are committed automatically.\n"
+ "The following specal keywords are processed by pyodbc and are not added to the\n"
+ "connection string. (If you must use these in your connection string, pass them\n"
+ "as a string, not as keywords.)\n"
"\n"
- "ansi\n"
- "\n"
- " By default, pyodbc first attempts to connect using the Unicode version of\n"
- " SQLDriverConnectW. If the driver returns IM001 indicating it does not support\n"
- " the Unicode version, the ANSI version is tried. Any other SQLSTATE is turned\n"
- " into an exception. Setting ansi to true skips the Unicode attempt and only\n"
- " connects using the ANSI version. This is useful for drivers that return the\n"
- " wrong SQLSTATE (or if pyodbc is out of date and should support other\n"
- " SQLSTATEs).";
-
+ " autocommit\n"
+ " If False or zero, the default, transactions are created automatically as\n"
+ " defined in the DB API 2. If True or non-zero, the connection is put into\n"
+ " ODBC autocommit mode and statements are committed automatically.\n"
+ " \n"
+ " ansi\n"
+ " By default, pyodbc first attempts to connect using the Unicode version of\n"
+ " SQLDriverConnectW. If the driver returns IM001 indicating it does not\n"
+ " support the Unicode version, the ANSI version is tried. Any other SQLSTATE\n"
+ " is turned into an exception. Setting ansi to true skips the Unicode\n"
+ " attempt and only connects using the ANSI version. This is useful for\n"
+ " drivers that return the wrong SQLSTATE (or if pyodbc is out of date and\n"
+ " should support other SQLSTATEs).\n"
+ " \n"
+ " timeout\n"
+ " An integer login timeout in seconds, used to set the SQL_ATTR_LOGIN_TIMEOUT\n"
+ " attribute of the connection. The default is 0 which means the database's\n"
+ " default timeout, if any, is used.\n";
+
static char timefromticks_doc[] =
"TimeFromTicks(ticks) --> datetime.time\n"
"\n"
View
5 tests/sqlservertests.py
@@ -1135,6 +1135,11 @@ def convert(value):
self.cnxn.clear_output_converters()
+ def test_login_timeout(self):
+ # This can only test setting since there isn't a way to cause it to block on the server side.
+ cnxns = pyodbc.connect(self.connection_string, timeout=2)
+
+
def main():
from optparse import OptionParser
parser = OptionParser(usage=usage)
Please sign in to comment.
Something went wrong with that request. Please try again.