Simple "CREATE" or "DROP TABLE" fails with PyMySQL and parameter replacement - python

I'm basically using the following code to drop an existing table using Python 3.6 and PyMySQL, with a MySQL 5.7 database:
connection = pymysql.connect(
host=host,
user=username,
password=password,
db=database,
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
table_name = "video_playback_statistics"
sql = "DROP TABLE IF EXISTS %s"
params = (table_name,)
with connection.cursor() as cursor:
cursor.execute(sql, params)
I get the following error:
pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''video_playback_statistics'' at line 1"
I've successfully executed other, more complicated SQL statements with more parameters, but this simple one does not work.
When I run the SQL command without parametrization, it works fine:
cursor.execute('DROP TABLE IF EXISTS video_playback_statistics')
I've looked at the following questions:
1064, "You have an error in your SQL syntax;..." Python MySQL
pymysql return error SQL syntax
But these kinds of queries seem to work fine.
Also I've seen this:
Python MYSQL update statement
But here the table name is directly written into the string.

Apparently the parametrization of table names or column names is not possible, according to this comment. The adapter, for some reason, inserts two double quotes for the value, which causes a syntax error.
Two possible solutions would be:
Use backslash escaping for the parameter:
sql = "DROP TABLE IF EXISTS `%s`"
Use Python string formatting:
sql = "DROP TABLE IF EXISTS {}".format(table_name)
For values (e.g. in INSERT or WHERE statements), you should still use parametrization.

Related

How can I create a database with MySQL using query parameters? [duplicate]

I'm using Python + MySQL and want to use parameterized query. I'm stuck. I've encountered an error and can't figure out how to solve it. I've spent a day, checked dozens of articles, used various options (sinle quotes, double quotes, prepared statements) and still no luck.
Requirements: use Parameterized Query
Here is basic demo of the issue:
#!/usr/bin/python3
import mysql.connector as mysql
conn = mysql.connect(host=server, user=username, passwd=password, autocommit=True)
try:
create_database_query = "CREATE DATABASE %s;"
db_name = "BOOKS"
cursor = conn.cursor()
print(f"Creating {db_name} database... ", end='')
cursor.execute(create_database_query, (db_name,))
print("Success")
except mysql.Error as error:
print("Parameterized query failed {}".format(error))
Output:
Creating BOOKS database... Parameterized query failed 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''BOOKS'' at line 1
So it looks like it uses too many quotes (2 single quotes on each side). The code above works fine if I change the following line:
create_database_query = "CREATE DATABASE %s;"
and put backtick around %s
The problem that now it creates a database but with invalid chars - 'BOOKS' (quotes are now part of db name). Duh...
If I use prepared statements then the same issue occurs but slightly different error message:
1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1
Environment:
MacOS Catalina
Python 3.8
PyCharm 2019.3 IDE
MySQL 8.0.19
mysql-connector-python module 8.0.19
What is going on? Any ideas?
Thanks
You can't use query parameters for identifiers (like a database name or table name or column name).
Query parameters can be used only in place of a constant value — a quoted string, quoted date/time, or a numeric value. Not identifiers, expressions, SQL keywords, etc.
To combine a database name with your CREATE DATABASE statement, you have to format it into the string in a way that forms the full statement before it is sent to MySQL.
db_name = "BOOKS"
create_database_query = "CREATE DATABASE %s;" % db_name
cursor.execute(create_database_query)
Because this creates a risk of SQL injection when you format variables into your string, it's up to you to make sure the db_name is safe.
Update: Thanks to #Parfait for the reminder about current best practices of string-formatting.
Prefer:
db_name = "BOOKS"
create_database_query = "CREATE DATABASE {};".format(db_name)
Or F-strings:
db_name = "BOOKS"
create_database_query = f"CREATE DATABASE {db_name};"
(In other words, Python has become Ruby ;-)

PyMySQL table name placeholder

I know this has been asked a million times, but noone seems to provide an alternative...
So this doesn't work:
DW = pymysql.connect(...)
table = "myTable"
with DW.cursor() as cur:
cur.execute("SELECT * FROM %s", (table,)) # error here
data = cur.fetchall()
Error:
ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''myTable'' at line 1")
psycopg2 for PostgreSQL has psycopg2.sql.Identifier or AsIs which basically removes the single quotes and acts as an identifier in the query...is there an equivalent for pymysql?
Also, other answers use string formatting and other methods that are vulnerable to SQL injection - this is something I want to avoid.

SQL statement fails through mysqldb.query in Python

I am trying to pass a query through my script, but i get a SQL error.
Running the same sql statement in Heidisql works fine.
My question is:
- What am I doing wrong?
error message
_mysql.connection.query(self, query)
_mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server
version for the right syntax to use near 'Gabrielsen)' at line 1")
Python script where Database is the correct connection to database
F="Gunnar Gabrielsen"
Database.query('INSERT INTO documents (name) values (' + F + ');')
i=Database.query('SELECT * from documents;')
print(i)
Python version:Python 3.4
Module:Mysqldb
DB:MariaDB
You haven't put quotes around your value.
But you should never do it this way anyway. Quite apart from the quoting problem, you are opening yourself to sql injection attacks.
Use a parametrised query instead:
cursor.execute('INSERT INTO documents (name) values (%s)', (F,))
You have generated this:
INSERT INTO documents (name) values (Gunnar Gabrielsen);
What you need is
INSERT INTO documents (name) values ("Gunnar Gabrielsen");
But, without escaping or parameterizing, you are opening your code (and system) up to "sql injection" and other hacking.

How can I pass parameters to execute() when creating new database

I'm now learning to use MySQL with python.
When I'm trying to create a new database like this:
sql = 'CREATE DATABASE IF NOT EXISTS %s'
cursor.execute(sql, (self.DB_NAME,))
DB_NAME is a string, in this case
self.DB_NAME = 'bmagym'
I got this error:
MySQL Error [1064]: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''bmagym'' at line 1
But if I replace the code with:
sql = 'CREATE DATABASE IF NOT EXISTS %s' %self.DB_NAME
cursor.execute(sql)
It works as expected.
My question is how I can pass parameters to execute() instead of using %?
Database names in the SQL syntax are written like
`dbname`
SQL accepts plain dbname as well.
CREATE DATABASE IF NOT EXISTS dbname
# works like
CREATE DATABASE IF NOT EXISTS `dbname`
# BUT: only if dbname is not a SQL keyword.
The cursor.execute() function will automatically format and escape strings (prevent SQL injection). So this query is executed:
CREATE DATABASE IF NOT EXISTS 'dbname'
which is a syntax error. Here is a similar topic on this question. Your second method is fine, just substitute the database name with python % operator.
sql = 'CREATE DATABASE IF NOT EXISTS `%s`' %self.DB_NAME

How to prevent injection attack in CREATE query in python mysql

I know the proper way to avoid injection attack in SELECT or INSERT queries using python module MySQLdb is:
cursor.execute("SELECT spam, eggs, sausage FROM breakfast WHERE price < %s", (max_price,))
However, when I tried to apply the same rule in CREATE query it returns error. My code:
cursor.execute("CREATE DATABASE %s;", ("test",))
It reports syntax error:
_mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''test'' at line 1")
It seems it is because the query adds single quote to the db name and Mysql does not allow that in CREATE query?
So what is the proper way to CREATE database or table using python MySQLdb module then, given the db name or table name is not fixed?
Try square brackets;
cursor.execute("CREATE DATABASE %s;", ["test"])

Categories