0

How do I fix this error on passing a string parameter into pd.read_sql_query? Using Python 2.7.

table = 'mytable1'
query = (
        "SELECT * "
        "FROM ? "
    )
df = pd.read_sql_query(sql=query, con=conn, params=(table))

pandas.io.sql.DatabaseError: Execution failed on sql 'SELECT * FROM ? ': near "?": syntax error

I have tried replacing ? with % and %s but it returns the same error.

The following equality example works as expected:

query = (
        "SELECT * "
        "FROM mytable1 "
        "WHERE name = ? "
    )
df = pd.read_sql_query(sql=query, con=conn, params=('cat',))

Note that the comma in params appears to be required, otherwise this error is returned: Incorrect number of bindings supplied. The current statement uses 1, and there are 7 supplied.

I have also tried params=(table,) in my problem but no luck. I know that alternatively I can do this with "FROM '{t}' ").format(t=table), but I would like to understand how to use Pandas' built-in parameters option.

5
  • maybe replace ? by %? Commented Apr 2, 2021 at 0:35
  • and params = [table]? Commented Apr 2, 2021 at 0:46
  • @QuangHoang, yep same error, this one is persistent! Commented Apr 2, 2021 at 0:48
  • 1
    There is a difference between (table) and (table,) in Python. The latter is a tuple, while the formal is a string, which is table. Commented Apr 2, 2021 at 0:48
  • @QuangHoang yes, but the docs say it can take a list or tuple! Commented Apr 2, 2021 at 0:49

1 Answer 1

2

The problem is that params is intended to replace values, not SQL keywords (FROM, SELECT, etc...) or tables or columns.

You can't specify table that way, you have to use a string substitution.

query = (
        f"SELECT * "
        "FROM {table} "
    )

However, be very, very, very careful. Doing this, rather than using params opens you up to a very big family of vulnerabilities, SQL Injections.

Don't do it if you get the table names from external sources.

(Oh, and the likely reason for this limitation is that, besides their absolute necessity for security reasons, parametrized queries allow the database engine to examine the query, look at indices, statistics and all that and draw up an "execution plan". Successive calls then can reuse that execution plan and just substitute in the new variables. That could not be done if the query was changing in terms of what tables and columns were being accessed.)

Sign up to request clarification or add additional context in comments.

4 Comments

That makes sense, I was starting to suspect that. Does string format alleviate injection concerns, notwithstanding the * ? In other words, is query = ("SELECT * FROM '{table}' ").format(table=t) ok?
Not really, no. You could specify a regex test ahead, like re.match('[A-Za-z0-9_]+') on table. That should keep you safe-ish as it avoids little tricks like embedded quotes. But there are libraries to do this. Still, whenever you can, use parametrized queries rather than testing user input for safety. It so happens you can't here.
@QuangHoang Yes, as I have already noted twice in the answer. That doesn't change the fact that you can't specify either SQL keywords or schema objects using parametrized queries. Whether the OP's use case justifies the risk is for them to decide.
Rest assured, negligible risk :) trying my hand at a script that matches ingredients to recipes and a relational database seemed to be the way to go. I don't use databases professionally, but I like knowing the difference between the right and wrong way (e.g., SQL injection concerns) out of general curiosity.

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.