Skip to content
Open
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
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def get_compiler_settings():

settings['libraries'].append('odbc32')
settings['libraries'].append('advapi32')
settings['libraries'].append('user32')

elif os.environ.get("OS", '').lower().startswith('windows'):
# Windows Cygwin (posix on windows)
Expand Down
27 changes: 23 additions & 4 deletions src/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static char* StrDup(const char* text) {
}


static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject* encoding)
static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject* encoding, SQLUSMALLINT driver_completion)
{
assert(PyUnicode_Check(pConnectString));

Expand Down Expand Up @@ -88,12 +88,31 @@ static bool Connect(PyObject* pConnectString, HDBC hdbc, long timeout, PyObject*
if (!cstring.isValid())
return false;

SQLHWND hwnd = 0;
#ifdef _WIN32
if (driver_completion != SQL_DRIVER_NOPROMPT)
{
hwnd = GetDesktopWindow();
if (!hwnd)
{
PyErr_SetString(OperationalError, "Failed to get desktop window handle");
return false;
}
}
#endif

Py_BEGIN_ALLOW_THREADS
ret = SQLDriverConnectW(hdbc, 0, cstring, SQL_NTS, 0, 0, 0, SQL_DRIVER_NOPROMPT);
ret = SQLDriverConnectW(hdbc, hwnd, cstring, SQL_NTS, 0, 0, 0, driver_completion);
Py_END_ALLOW_THREADS
if (SQL_SUCCEEDED(ret))
return true;

if (ret == SQL_NO_DATA)
{
PyErr_SetString(OperationalError, "User cancelled connection request");
return false;
}

RaiseErrorFromHandle(0, "SQLDriverConnect", hdbc, SQL_NULL_HANDLE);

return false;
Expand Down Expand Up @@ -170,7 +189,7 @@ static bool ApplyPreconnAttrs(HDBC hdbc, SQLINTEGER ikey, PyObject *value, char
}

PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeout, bool fReadOnly,
PyObject* attrs_before, PyObject* encoding)
PyObject* attrs_before, PyObject* encoding, SQLUSMALLINT driver_completion)
{
//
// Allocate HDBC and connect
Expand Down Expand Up @@ -213,7 +232,7 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeou
}
}

if (!Connect(pConnectString, hdbc, timeout, encoding))
if (!Connect(pConnectString, hdbc, timeout, encoding, driver_completion))
{
// Connect has already set an exception.
Py_BEGIN_ALLOW_THREADS
Expand Down
2 changes: 1 addition & 1 deletion src/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ struct Connection
* exception is set and zero is returned.
*/
PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, long timeout, bool fReadOnly,
PyObject* attrs_before, PyObject* encoding);
PyObject* attrs_before, PyObject* encoding, SQLUSMALLINT driver_completion);

/*
* Used by the Cursor to implement commit and rollback.
Expand Down
8 changes: 8 additions & 0 deletions src/pyodbc.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,11 @@ SQL_UNION: int
SQL_USER_NAME: int
SQL_XOPEN_CLI_YEAR: int

# driver connect completion modes
SQL_DRIVER_COMPLETE: int
SQL_DRIVER_COMPLETE_REQUIRED: int
SQL_DRIVER_NOPROMPT: int
SQL_DRIVER_PROMPT: int

# pyodbc-specific constants
BinaryNull: Any # to distinguish binary NULL values from char NULL values
Expand Down Expand Up @@ -999,6 +1004,9 @@ def connect(connstring: str | None = None,
readonly: To set the connection read-only. Not all drivers and/or databases support this.
timeout: Set the connection timeout, in seconds. This is managed by the driver, not
pyodbc, and not all drivers support this.
driver_completion: One of SQL_DRIVER_PROMPT (only available on Windows),
SQL_DRIVER_NO_PROMPT (the default), SQL_DRIVER_COMPLETE, or
SQL_DRIVER_COMPLETE_REQUIRED.
attrs_before: Set low-level connection attributes before a connection is attempted.
**kwargs: These key/value pairs are used to construct the connection string, or add
to it (as "key=value;" combinations).
Expand Down
41 changes: 39 additions & 2 deletions src/pyodbcmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
int fReadOnly = 0;
long timeout = 0;
PyObject* encoding = 0;
SQLUSMALLINT driver_completion = SQL_DRIVER_NOPROMPT;

Object attrs_before; // Optional connect attrs set before connecting

Expand Down Expand Up @@ -475,6 +476,31 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
encoding = value;
continue;
}
if (PyUnicode_CompareWithASCIIString(key, "driver_completion") == 0)
{
driver_completion = (SQLUSMALLINT)PyLong_AsLong(value);
if (PyErr_Occurred())
return 0;
switch (driver_completion)
{
case SQL_DRIVER_PROMPT:
case SQL_DRIVER_COMPLETE:
case SQL_DRIVER_COMPLETE_REQUIRED:
case SQL_DRIVER_NOPROMPT:
break;
default:
PyErr_SetString(ProgrammingError, "Invalid value for driver_completion");
return 0;
}
#ifndef _WIN32
if (driver_completion == SQL_DRIVER_PROMPT)
{
PyErr_SetString(NotSupportedError, "SQL_DRIVER_PROMPT not supported on this platform");
return 0;
}
#endif
continue;
}

// Map DB API recommended names to ODBC names (e.g. user --> uid).

Expand Down Expand Up @@ -521,7 +547,8 @@ static PyObject* mod_connect(PyObject* self, PyObject* args, PyObject* kwargs)
}

return (PyObject*)Connection_New(pConnectString.Get(), fAutoCommit != 0, timeout,
fReadOnly != 0, attrs_before.Detach(), encoding);
fReadOnly != 0, attrs_before.Detach(), encoding,
driver_completion);
}


Expand Down Expand Up @@ -739,7 +766,11 @@ static char connect_doc[] =
" 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";
" default timeout, if any, is used.\n"
"\n"
" driver_completion\n"
" One of SQL_DRIVER_PROMPT (only available on Windows), SQL_DRIVER_NO_PROMPT\n"
" (the default), SQL_DRIVER_COMPLETE, or SQL_DRIVER_COMPLETE_REQUIRED.\n";

static char timefromticks_doc[] =
"TimeFromTicks(ticks) --> datetime.time\n"
Expand Down Expand Up @@ -1065,6 +1096,12 @@ static const ConstantDef aConstants[] = {
MAKECONST(SQL_PACKET_SIZE),
MAKECONST(SQL_ATTR_ANSI_APP),

// Driver connection completion modes
MAKECONST(SQL_DRIVER_COMPLETE),
MAKECONST(SQL_DRIVER_COMPLETE_REQUIRED),
MAKECONST(SQL_DRIVER_NOPROMPT),
MAKECONST(SQL_DRIVER_PROMPT),

// SQL_CONVERT_X
MAKECONST(SQL_CONVERT_FUNCTIONS),
MAKECONST(SQL_CONVERT_BIGINT),
Expand Down
Loading