Django + ODBC Driver 17 for SQL Server: Raw query can't process list or tuple arguments
Code
I wrote a function that wraps Django's standard processing for raw SQL queries.
def run_mssql_query(query, connection="prod", args=None):
"""Method will run query on MSSQL database
Args:
query (str): MSSQL Query
connection (str): Which connection to use. Defaults to "prod".
Returns:
dict: Results of query
"""
if connection == "omron":
results = {}
conn = get_oracle_connection()
with conn.cursor() as cursor:
try:
if args == None:
args = dict()
cursor.execute(query, args)
cursor.rowfactory = lambda *args: dict(zip([d[0] for d in cursor.description], args))
results = cursor.fetchall()
except:
results = {}
return results
with connections[connection].cursor() as cursor:
if args == None:
args = list()
cursor.execute(query, args) # Replace with your actual query
try:
columns = [col[0] for col in cursor.description]
results = [dict(zip(columns, row)) for row in cursor.fetchall()]
except:
results = {}
return results
Execution looks something like this:
data = run_mssql_query(f"""SELECT *
FROM EVENTS.dbo.MES_BARCODES
WHERE BARCODE = %s""", args=["123"])
Problem
When i'm using string, int, etc..., it's working like a charm, but when i'm tring to pass list to the params of my params, like this (for example):
barcodes = ["123", "321"]
data = run_mssql_query(f"""SELECT *
FROM EVENTS.dbo.MES_BARCODES
WHERE BARCODE IN %s""", args=[list(barcodes)])
It's giving me this error:
('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column, parameter, or variable #1: Cannot find data type 321.123. (2715) (SQLExecDirectW)')
Here's a traceback:
File \"C:\\python-projects\\fas-mes\\fasmes\\APP__LogsDashboard\\decorators.py\", line 32, in wrapper\n response = func(*args, **kwargs)\n File \"C:\\python-projects\\fas-mes\\fasmes\\APP__Constructors\\views.py\", line 74, in get_duplicated_barcodes\n data = run_mssql_query(f\"\"\"SELECT *\n File \"C:\\python-projects\\fas-mes\\fasmes\\API__Database\\views_functions.py\", line 90, in run_mssql_query\n cursor.execute(query, args) # Replace with your actual query\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 102, in execute\n return super().execute(sql, params)\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 67, in execute\n return self._execute_with_wrappers(\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 80, in _execute_with_wrappers\n return executor(sql, params, many, context)\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 89, in _execute\n return self.cursor.execute(sql, params)\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\utils.py\", line 91, in __exit__\n raise dj_exc_value.with_traceback(traceback) from exc_value\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 89, in _execute\n return self.cursor.execute(sql, params)\n File \"C:\\python-projects\\fas-mes\\venv\\lib\\site-packages\\mssql\\base.py\", line 677, in execute\n return self.cursor.execute(sql, params)\n
pip freeze of my project
asgiref==3.8.1
backports.zoneinfo==0.2.1
certifi==2024.7.4
cffi==1.17.1
charset-normalizer==3.3.2
cryptography==43.0.1
cx-Oracle==8.3.0
Django==4.2.15
django-appconf==1.0.6
django-compressor==4.5.1
django-cors-headers==4.4.0
django-extensions==3.2.3
django-mssql==1.8
django-mssql-backend==2.8.1
django-sass-processor==1.4.1
django-xff==1.4.0
djangorestframework==3.15.2
idna==3.7
libsass==0.23.0
MarkupSafe==2.1.5
mssql-django==1.5
oracledb==2.4.1
packaging==24.1
pillow==10.4.0
pycparser==2.22
pyodbc==5.1.0
pyOpenSSL==24.2.1
python-ipware==3.0.0
pytz==2024.1
rcssmin==1.1.2
requests==2.32.3
rjsmin==1.2.2
sqlparse==0.5.1
typing-extensions==4.12.2
tzdata==2024.1
urllib3==2.2.2
werkzeug==3.0.4
What i tried to do
- Trying to add ANY() to the query, like this:
data = run_mssql_query(f"""SELECT *
FROM EVENTS.dbo.MES_BARCODES
WHERE BARCODE IN ANY(%s)""", args=[list(barcodes)])
But that's not helped me at all.
- I was trying to see queries, that django tries to execute, then execute it manually.
Here's a function to get queries:
print("Executed queries:" )
for i, query in enumerate(connections[connection].queries):
print(f'----------------- {i} -------------------')
print(query["sql"])
print(f'-----------------------------------------')
Here's an output:
Executed queries:
----------------- 0 -------------------
SELECT CAST(SERVERPROPERTY('ProductVersion') AS varchar)
-----------------------------------------
----------------- 1 -------------------
SELECT SYSDATETIME()
-----------------------------------------
----------------- 2 -------------------
SELECT *
FROM EVENTS.dbo.MES_BARCODES
WHERE BARCODE IN ['123', '321']
-----------------------------------------
As a result, the received queries look absolutely correct, and I think that additional query transformations are taking place under the hood
Results
After viewing a bunch of articles on StackOverflow and official documentation, it did not work to make a query containing list() or tuple() in arguments work.
P.S. I can't use f-string or string.format in a risk of SQL-injections