Closed MarcSkovMadsen closed 4 years ago
hey there -
if you could reduce the verbosity of stack traces that would be helpful.
There's much to scroll through here but the essential nature seems to be that this is the error:
(pyodbc.Error) ('FA003', "[FA003] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Password option must not be specified, if Authentication option is 'ActiveDirectoryInteractive'. (0) (SQLDriverConnect); [FA003] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Password option must not be specified, if Authentication option is 'ActiveDirectoryInteractive'. (0)")
E (Background on this error
the connection string being passed to pyodbc is:
cargs = ('DRIVER={ODBC Driver 17 for SQL Server};Server=<SERVERNAME>.database.windows.net,1433;Database=<DATABASE>;UID=<USERNAME>@<DOMAIN>;PWD=;Authentication=ActiveDirectoryInteractive',)
above we can see PWD is empty. This is the result of the following SQLAlchemy URL:
f"mssql+pyodbc://{username}@{server}:1433/{database}?driver={DRIVER}&autocommit=true&Authentication=ActiveDirectoryInteractive"
where we also see there is no password.
your example that works uses this string:
= pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+";Authentication=ActiveDirectoryInteractive")
and I guess the difference is that the "PWD" prefix is omitted altogether.
we could propose that PWD is omitted from the string if there is no password in the URL. however, for now, since you are working with ODBC strings, just use them directly with SQLAlchemy:
create_engine('mssql+pyodbc:///odbc_connect?='DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+";Authentication=ActiveDirectoryInteractive")
@gordthompson issue here would probably be resolved by:
diff --git a/lib/sqlalchemy/connectors/pyodbc.py b/lib/sqlalchemy/connectors/pyodbc.py
index 96ac0c1f1..b64d887c8 100644
--- a/lib/sqlalchemy/connectors/pyodbc.py
+++ b/lib/sqlalchemy/connectors/pyodbc.py
@@ -95,7 +95,9 @@ class PyODBCConnector(Connector):
user = keys.pop("user", None)
if user:
connectors.append("UID=%s" % user)
- connectors.append("PWD=%s" % keys.pop("password", ""))
+ pwd = keys.pop("password", "")
+ if pwd:
+ connectors.append("PWD=%s" % pwd)
else:
connectors.append("Trusted_Connection=Yes")
do you see any negative side effects from that? is "PWD=;" with no value ever something needed?
@zzzeek
is "PWD=;" with no value ever something needed?
Not in my experience. SQL Server lets us create a login with a blank password, but when connecting via ODBC we can either use UID=anon;PWD=
or just omit the PWD=
argument altogether.
@MarcSkovMadsen - If you want to test the above patch you can
python -m pip unininstall sqlalchemy
python -m pip install git+https://github.com/gordthompson/sqlalchemy@mssql_pwd_1_3
I believe there are two problems. One is that you cannot just specify a username without a password. Thats the problem you are adressing above.
But there is also the Cannot use Authentication option with Integrated Security option. (0)
problem. š
I don't understand the cause. For me it looks like a SQL Alchemy thing that the ";Authentication=ActiveDirectoryInteractive"
or
";Authentication=ActiveDirectoryIntegrated"
is not supported. Because that thing works with pyodbc on its own as you can see from the tests.
self = <sqlalchemy.dialects.mssql.pyodbc.MSDialect_pyodbc object at 0x0000019CF6042E48>
cargs = ('DRIVER={ODBC Driver 17 for SQL Server};Server=<SERVERNAME>.database.windows.net,1433;Database=<DATABASE>;Trusted_Connection=Yes;Authentication=ActiveDirectoryIntegrated',)
cparams = {'autocommit': True}
def connect(self, *cargs, **cparams):
# inherits the docstring from interfaces.Dialect.connect
> return self.dbapi.connect(*cargs, **cparams)
E pyodbc.Error: ('FA001', '[FA001] [Microsoft][ODBC Driver 17 for SQL Server]Cannot use Authentication option with Integrated Security option. (0) (SQLDriverConnect); [FA001] [Microsoft][ODBC Driver 17 for SQL Server]Cannot use Authentication option with Integrated Security option. (0)')
.venv\lib\site-packages\sqlalchemy\engine\default.py:493: Error
@MarcSkovMadsen - Thanks for the clarification. I have pushed another commit to my mssql_pwd_1_3 branch. Please uninstall and reinstall via pip and see if that works any better.
these can all be in a single gerrit w/ single changelog
@zzzeek - That's the ultimate plan, for sure. We'll also need to do some refactoring as this is mssql-specific so it really belongs in sqlalchemy/dialects/mssql/pyodbc.py, not sqlalchemy/connectors/pyodbc.py. Just trying to pin down the requirements first.
@MarcSkovMadsen - It occurred to me that the "authentication=" keyword should be lowercase to be consistent with "driver=" so I pushed another change to my branch. The connection URI I used for testing was
connection_uri = (
"mssql+pyodbc://@server_name/db_name?"
"driver=ODBC+Driver+17+for+SQL+Server;"
"authentication=ActiveDirectoryIntegrated"
)
Iām really impressed by the collaboration and Communication on this problem.
Will test tomorrow Danish time when iām Back on Work.
I lowercased authentication
in the connectionstring and then ran
python -m venv .venv2
source .venv2/Scripts/activate
pip install git+https://github.com/gordthompson/sqlalchemy.git@mssql_pwd_1_3
(Successfully installed SQLAlchemy-1.3.20.dev0)
pip install pyodbc pandas
(Successfully installed numpy-1.19.2 pandas-1.1.2 pyodbc-4.0.30 python-dateutil-2.8.1 pytz-2020.1 six-1.15.0)
pip install pytest
(Successfully installed atomicwrites-1.4.0 attrs-20.2.0 colorama-0.4.3 importlib-metadata-1.7.0 iniconfig-1.0.1 more-itertools-8.5.0 packaging-20.4 pluggy-0.13.1 py-1.9.0 pyparsing-2.4.7 pytest-6.0.2 toml-0.10.1 zipp-3.1.0)
The original error is gone. But I get a new warning.
(.venv2) C:\repos\trading_analytics\us-trading>pytest users\masma\test_ode_connections.py::test_can_connect_using_activedirectory_integrated_sqlalchemy
================================================= test session starts =================================================
platform win32 -- Python 3.7.6, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: C:\repos\trading_analytics\us-trading, configfile: pytest.ini
collected 1 item
users\masma\test_ode_connections.py . [100%]
================================================== warnings summary ===================================================
users/masma/test_ode_connections.py::test_can_connect_using_activedirectory_integrated_sqlalchemy
c:\repos\trading_analytics\us-trading\.venv2\lib\site-packages\sqlalchemy\dialects\mssql\base.py:2434: SAWarning: Could not fetch transaction isolation level, tried views: ('sys.dm_exec_sessions', 'sys.dm_pdw_nodes_exec_sessions'); final error was: ('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]User does not have permission to perform this action. (6004) (SQLExecDirectW)')
"tried views: %s; final error was: %s" % (views, last_error)
-- Docs: https://docs.pytest.org/en/stable/warnings.html
============================================ 1 passed, 1 warning in 23.23s ============================================
If I run it with pyodbc alone
(.venv2) C:\repos\trading_analytics\us-trading>pytest users\masma\test_ode_connections.py::test_can_connect_using_activedirectory_integrated_official
================================================================================================ test session starts =================================================================================================
platform win32 -- Python 3.7.6, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: C:\repos\trading_analytics\us-trading, configfile: pytest.ini
collected 1 item
users\masma\test_ode_connections.py . [100%]
================================================================================================= 1 passed in 23.77s =================================================================================================
it works.
My hypothesis is that sqlalchemy is doing some select that PYODBC is not. I'm just asking to execute "select top 2 * from eia.data_view"
Would it be possible to fix?
@MarcSkovMadsen - If it's just a warning then it doesn't really need to be "fixed". Azure DW is a bit different from SQL Server when it comes to things like transactions.
Gord Thompson has proposed a fix for this issue in the master branch:
Add support for Azure authentication options https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/2241
Gord Thompson has proposed a fix for this issue in the rel_1_3 branch:
Add support for Azure authentication options https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/2242
it would be good if the driver could get the transaction isolation level, so that warning does say something useful.
@zzzeek - We already try checking sys.dm_pdw_nodes_exec_sessions
which is the right place to look for Azure DW. In this particular case the problem is:
User does not have permission to perform this action. (6004) (SQLExecDirectW)')
Thanks for helping out š
Was just going to put in a PR to fix this only to find I was beaten to it - thanks! :tada:
Connecting to an Azure SQL database and having to work around this by passing through a pyodbc
DSN directly. Would love to see this fix in a release - is there one planned shortly?
Would love to see this fix in a release - is there one planned shortly?
Lately, releases have happened every 6 to 8 weeks and the last one was mid-August so I would expect there to be another one before too long.
Thanks for the info @gordthompson! Since it's not too far away I can just wait for the new release and pin to that version when it's out...
I am trying to use sqlalchemy to create an engine for Microsoft Azure Synapse SQL datawarehouse using pyodbc.
I have developed the code below.
It uses a
db_config.py
file that looks likeIf I run pytest on it via
pytest -s -vv test_connection.py
I get something likeAs you can see I can connect and get data via pyodbc on its own. But not via sqlalchemy.
Versions.