4

I have a list of values that I'd like to use in an IN clause for an SQL (SQL Server) statement to be executed with pyodbc. Example:

files = ['file1', 'file2', ...]  # this list can have a variable number of elements
con = pyodbc.connect(...)

# What I'd like to do
result = con.cursor().execute('SELECT * FROM sometable WHERE file_name IN (?)', files)

However when I execute the statement above I get an error such as this:

ProgrammingError: ('The SQL contains 1 parameter markers, but 18 parameters were supplied', 'HY000')

I can generate a variable parameter string using something like:

params = ','.join(['?']*len(files))
query = 'SELECT * FROM sometable WHERE file_name IN ({})'.format(params)
result = con.cursor().execute(query, files)

But doing so would put me at risk for SQL injection, if I understand correctly. Is there a way to accomplish this safely?

1
  • 1
    There is no SQL injection risk with your attempt. You are only building a prepared statement with dynamic parameter placeholders. But you are binding values in execute. Commented Aug 23, 2019 at 2:02

2 Answers 2

4

You can use JSON to pass the list to SQL Server. EG

import numpy as np
import pandas as pd
import pyodbc
import json 

files = ['file1', 'file2', 'file3']  # this list can have a variable number of elements
json_files = json.dumps(files)
print(json_files)
conn = pyodbc.connect('Driver={Sql Server};'
                      'Server=localhost;'
                      'Database=tempdb;'
                      'Trusted_Connection=yes;')

cursor = conn.cursor()

cursor.execute("create table #sometable(id int, file_name varchar(255)); insert into #sometable(id,file_name) values (1,'file2')")
# What I'd like to do
result = cursor.execute('SELECT * FROM #sometable WHERE file_name IN (select value from openjson(?))', json_files)
rows = cursor.fetchall()
print(rows)
Sign up to request clarification or add additional context in comments.

2 Comments

Neat trick if you've got a database with compatibility level 130 or higher.
Yep. The Python data access libraries are not the greatest, SQL Server JSON features fill a lot of gaps like Bulk Insert, Table Valued Parameters, shaped data retrieval, and object mapping.
2

doing so would put me at risk for SQL injection

No, it wouldn't, because you are in complete control over what you are injecting into the SQL command text (i.e., a comma-separated string of question marks). Your approach is fine, provided that your list doesn't contain more than approximately 2100 items.

1 Comment

Ah, I see. these links appear to suggest not using Python string formatting in general, but perhaps it isn't a hard-and-fast rule.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.