0

Trying to connect to MSSQL DB with pyodbc and SQLAlchemy using code (from another SO post) like

import json
import urllib

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker



CONF = json.load(open(f"{PROJECT_HOME}/configs/configs.json"))
assert isinstance(CONF, ty.Dict)

params = urllib.parse.quote_plus(f"DRIVER={{ODBC Driver 17 for SQL Server}};"
                                 f"SERVER={CONF['db']['db_ip']};"
                                 f"DATABASE={CONF['db']['dest_db']};"
                                 f"UID={CONF['db']['username']};"
                                 f"PWD={CONF['db']['password']}")

engine = create_engine(f"mssql+pyodbc:///?odbc_connect={params}")
print(params)
sql_conn = engine.raw_connection()
cursor = sql_conn.cursor()

and getting error

sqlalchemy.exc.InterfaceError: (pyodbc.InterfaceError) ('28000', "[28000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Login failed for user 'myuser'. (18456) (SQLDriverConnect)")

Looking at the URL that gets generated, it looks like

mssql+pyodbc:///?odbc_connect=DRIVER%3D%7BODBC+Driver+17+for+SQL+Server%7D%3BSERVER%3D172.12.3.45%3BDATABASE%3Dmy_db%3BUID%3Dmyuser%3BPWD%3Dpass%2Bword10

even though 1) I can connect to the same DB via mssql-tools (eg. via bcp) using the same credentials I'm using here and 2) this same code snippet does work for another script that accesses a different DB (though was a slightly older version of SQLAlchemy (v1.3.15), but same ODBC DSN).

Any ideas what could be going wrong in this case?

The configs I'm using in the code above look like

{
"db":
    {
        "db_dsn": "MyMSSQLServer",
        "db_ip": "172.12.3.45",
        "dest_db": "my_db",
        "dest_table": "my_table",
        "username": "myuser",
        "password": "pass+word10"
    }
}

Note that my password here is "pass+word10" w/ a plus sign. When I look at the docs for urllib.parse.quote_plus, it seems like the plus sign should not be an issue, but I may be misinterpreting that and I can't think of what else it could be (since, again this general snippet has already worked in the past).

My various package versions are...

(venv) $ pip freeze | grep odbc
pyodbc==4.0.30
(venv) $ pip freeze | grep SQL
Flask-SQLAlchemy==2.5.1
SQLAlchemy==1.4.7
SQLAlchemy-JSONField==1.0.0
SQLAlchemy-Utils==0.37.0

Have also tried

engine = create_engine("mssql+pyodbc:///?odbc_connect="
                           "DRIVER={ODBC Driver 17 for SQL Server};"
                           f"SERVER={CONF['db']['db_ip']};"
                           f"DATABASE={CONF['db']['dest_db']};"
                           f"UID={CONF['db']['username']};"
                           f"PWD={CONF['db']['password']}")

(w/ and w/out "+"s between the driver words) showing the engine string as

Engine(mssql+pyodbc:///?DATABASE=mydb&PWD=mypassword&SERVER=172.12.3.45&UID=myuser&odbc_connect=DRIVER%3D%7BODBC+Driver+17+for+SQL+Server%7D)

and gotten the error

pyodbc.OperationalError: ('08001', '[08001] [Microsoft][ODBC Driver 17 for SQL Server]Neither DSN nor SERVER keyword supplied (0) (SQLDriverConnect)')

Even though I can see a DSN in my odbc.ini like...

$cat /etc/odbc.ini 

[MyMSSQLServer]
Driver=ODBC Driver 17 for SQL Server
Description=My MS SQL Server
Trace=No
Server=172.12.3.45

Anyone with more experience with this know what could be going on here? Any debugging stuff I could try or info that could help this post?

* I know this post title is not super informative for what the problem seems to be, but I don't really have a great idea of what the problem is even related to atm, so would need to get more info on the problem before being able to edit to make a more relevant title.

lampShadesDrifter
  • 3,925
  • 8
  • 40
  • 102
  • 1
    Please check your password. When I look at the generated URL the password entered is `pass+eword10` (note the extra `e`. – rfkortekaas Apr 23 '21 at 10:49
  • @rfkortekaas That was just a typo from me changing my actual URL to one with a mock/simplified password (ie. the original had an "e" in it) for the purpose of this SO post, corrected that. (The plus sign is the only non-standard character there though). – lampShadesDrifter May 18 '21 at 07:56
  • What happens if you do not use quoteplus and use the curly braces around the values? – rfkortekaas May 18 '21 at 08:17
  • @rfkortekaas I see. To be clear, you mean rather than generating a URL query string with `quote_plus`, just doing something like f"mssql+pyodbc:///?odbc_connect=Driver={{SQL Server}};Server={myServerAddress};Database={myDataBase};Uid={myUsername};Pwd={myPassword};"? (Noting the double brackets around the driver param) – lampShadesDrifter May 18 '21 at 09:39
  • Yes, and maybe without the double braces around the driver. – rfkortekaas May 18 '21 at 09:43
  • @rfkortekaas Tried that w/ not success, see updated post. The only thing that seems to work is using less exact version of connection strings, eg. `engine = create_engine(f"mssql+pyodbc://{CONF['db']['username']}:{CONF['data_warehouse']['password']}@{CONF['db']['db_ip']}:1433/{CONF['db']['dest_db']}?driver=ODBC+Driver+17+for+SQL+Server")` based on what I found here (https://www.kite.com/python/docs/sqlalchemy.dialects.mssql.pyodbc). This is functional, but does not answer the question (and makes the error seem even more mysterious to me). – lampShadesDrifter May 19 '21 at 06:13

0 Answers0