3

Hi I'm doing something like:

# pyodbc extension
cursor.execute("select a from tbl where b=? and c=?", x, y)

-- some values in the query in provided by variables. But sometimes the variable is interpreted as @P1 in the query.

For example:

import pyodbc
ch = pyodbc.connect('DRIVER={SQL Server};SERVER=xxxx;DATABASE=xxx;Trusted_Connection=True')
cur = ch.cursor()
x = 123
cur.execute('''
CREATE TABLE table_? (
 id int IDENTITY(1,1) PRIMARY KEY,
 obj varchar(max) NOT NULL
)
''', x).commit()

This results in a new table named table_@P1 (I want table_123)

Another example:

x = 123
cur.execute('''
CREATE TABLE table_2 (
 id int IDENTITY(1,1) PRIMARY KEY,
 obj varchar(?) NOT NULL
)
''', x).commit()

it reports error:

ProgrammingError: ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near '@P1'. (102) (SQLExecDirectW)")

Again, the variable is interpreted as @P1.

Anyone know how to fix this? Any help's appreciated. Thanks-

Gord Thompson
125k39 gold badges252 silver badges458 bronze badges
asked Jun 6, 2018 at 20:00

2 Answers 2

4

In your first case, parameter substitution does not work for table/column names. This is common to the vast majority of (if not all) database platforms.

In your second case, SQL Server does not appear to support parameter substitution for DDL statements. The SQL Server ODBC driver converts the pyodbc parameter placeholders (?) to T-SQL parameter placeholders (@P1, @P2, ...) so the statement passed to SQL Server is

CREATE TABLE table_2 (id int IDENTITY(1,1) PRIMARY KEY, obj varchar(@P1) NOT NULL

specifically

exec sp_prepexec @p1 output,N'@P1 int',N'CREATE TABLE table_2 (id int IDENTITY(1,1) PRIMARY KEY, obj varchar(@P1) NOT NULL',123

and when SQL Server tries to prepare that statement it expects a literal value, not a parameter placeholder.

So, in both cases you will need to use dynamic SQL (string formatting) to insert the appropriate values.

answered Jun 6, 2018 at 21:09
Sign up to request clarification or add additional context in comments.

Comments

-1

There is a way to do this sort of thing. What you need to do is dynamically build the command (ideally as a nvarchar( MAX), not varchar( MAX)) string variable and pass that variable to the cur.execute() - or any other - command. Modifying your first example accordingly:

ch = pyodbc.connect( 'DRIVER={SQL Server};SERVER=xxxx;DATABASE=xxx;Trusted_Connection=True' )
cur = ch.cursor()
x = 123
SQL_Commands = 'CREATE TABLE table_' + str( x ) + ''' 
(
 id int IDENTITY(1,1) PRIMARY KEY,
 obj varchar(max) NOT NULL
) '
'''
cur.execute( SQL_Commands ).commit()

BTW, you shouldn't try to do everything in one line, if only to avoid problems like this one. I'd also suggest looking into adding "autocommit=True" to your connect string, that way you wouldn't have to append .commit() to cur.execute().

answered Jun 7, 2018 at 22:17

1 Comment

This is not recommended. It is possible to do SQL Injection if you just concatenate strings.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.