1
votes

Python cursor.execute () avec MySQL UPDATE provoque une erreur de syntaxe

En suivant cet exemple, je tente de réécrire du code qui fonctionne avec du code qui protège contre l'injection SQL:

Code qui fonctionne:

mysql.connector.errors.ProgrammingError: 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 ''led_status' SET 'test_led' = 'FALSE'' at line 1

code qui ne fonctionne pas:

table = "led_status"
field = "test_led"
value = "FALSE"

sql_update_command = "UPDATE %s SET %s = %s"
cursor.execute(sql_update_command, (table, field, value))

Ce code ne fonctionne pas non plus:

table = "led_status"
field = "test_led"
value = "FALSE"

cursor.execute(("UPDATE %s SET %s = %s", table, field, value))

Le premier exemple fonctionne, les autres non et chacun d'eux renvoie cette erreur de syntaxe:

table = "led_status"
field = "test_led"
value = "FALSE"

cursor.execute(("UPDATE %s SET %s = %s") % (table, field, value))

Je ne suis pas sûr de ce que je fais de mal, donc tout pointeur serait grandement apprécié.


0 commentaires

5 Réponses :


0
votes

Votre SQL est incorrect dans les 3 exemples. Le code suivant devrait fonctionner:

table = "led_status"
field = "test_led"
value = False

cursor.execute("UPDATE `%s` SET `%s` = %s", (table, field, value))

Notez les accents (`) autour des noms de table et de colonne, cependant, les valeurs doivent être représentées comme le type d'objet respectif des colonnes. Des guillemets simples et doubles doivent être utilisés pour représenter les valeurs de chaîne.

Dans votre cas, FALSE ne devrait probablement pas être stocké sous forme de chaîne, mais sous forme de booléen ou de tinyint dans votre schéma de base de données.


2 commentaires

En fait, dans mon cas, je veux que FALSE soit stocké sous forme de chaîne littérale. J'essaierai les autres changements!


hummm ... ne fonctionnait toujours pas, maintenant j'obtiens: AttributeError: 'tuple' object has no attribute 'encode'



0
votes

J'aime utiliser psycopg2 pour des instances comme celle-ci où vous essayez d'alimenter un nom de colonne en tant qu'entrée et que vous ne voulez pas qu'il s'échappe

from psycopg2.extensions import AsIs

cmd = """
      UPDATE %(table)s SET %(column)s = %(val)s
      """

kwargs = {
    'table': AsIs('led_status'),
    'column': AsIs('test_led'),
    'val': False
}

cursor.execute(cmd, kwargs)


0 commentaires

0
votes

Merci pour les conseils. Finalement, j'ai compris mon erreur après avoir lu cet article . Apparemment, vous ne pouvez pas paramétrer un nom de table!

Voici comment j'ai corrigé le code:

table = "led_status"
field = "test_led"
value = "FALSE"

sql_update = "UPDATE " + table + " SET " + column + " = %s"
cursor.execute(sql_update, (value,))


2 commentaires

Vous n'avez pas de déclaration préparée. Le dernier exemple a été mis à jour car les tuples entouraient la déclaration entière, et je ne l'ai pas vu au premier coup d'œil.


Pour passer le nom de la table, vous devez utiliser sql.Identifier et format()



2
votes

La manière optimale de le faire selon la documentation est avec:

from psycopg2 import sql

cur.execute(
sql.SQL("insert into {} values (%s, %s)")
    .format(sql.Identifier('my_table')),
[10, 20])

La source


0 commentaires

0
votes

Puisque votre question concerne l'injection SQL, je vais aller un peu plus loin que de vous donner une seule ligne. Votre problème est que vous ne pouvez pas paramétrer un nom de table. Lorsque vous traitez avec l'injection SQL, vous devez distinguer la différence entre le SQL dynamique et les valeurs d'entrée utilisateur. Les valeurs d'échappement pour se protéger contre l'injection SQL doivent être gérées par le mécanisme de guillemet / échappement du pilote. Vous validez vous-même les fragments SQL dynamiques. Les fragments SQL dynamiques incluent des éléments tels que des expressions de condition de variable WHERE, des noms de table de variables ou des listes de champs SELECT variables.

La syntaxe correcte pour votre exemple est:

valid_fields = {
   'table_1': ['field_1', 'field_2'],
   'table_2': ['field_a', 'field_b'],
   'table_3': ['field_x', 'field_y']
}

ou

cursor.execute("UPDATE %s SET %s = %%s" % (table, field), value)

Cependant, si la table provient de l'utilisateur, vous devez la valider par rapport à une liste prédéfinie avant d'arriver à cette instruction. Vous ne pouvez pas faire confiance à la base de données pour faire ce qu'il faut avec les noms de table ou de champ. La structure de données suivante fournirait quelque chose à valider par exemple.

cursor.execute("UPDATE {} SET {} = %s".format(table, field), value)

Vous pouvez également utiliser des tables de catalogue spéciales fournies par votre base de données (comme pg_catalog) pour les obtenir de manière dynamique, mais vous devez toujours vérifier les noms de champs / tables spéciaux (OID par exemple).


1 commentaires

Merci pour l'information supplémentaire. D'un point de vue de la programmation, je veux me protéger contre l'injection, mais les valeurs de la table, de la colonne, etc. sont toutes codées en dur à partir de diverses autres fonctions et passées en tant que variable à une seule fonction de base de données pour écrire une mise à jour qui définit l'ensemble du programme, il n'y a pas d'interaction de l'utilisateur ou entrée du tout. Cependant, j'ai essayé vos deux exemples et les deux ont échoué avec: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version