Nom de la table Variable dans sqlite
Question: Est-il possible d'utiliser une variable comme nom de table sans avoir à utiliser des constructeurs de chaînes pour le faire?
Info:
Je travaille sur un projet en ce moment qui répertorie les données d'une de mes simulations d'étoiles. Pour ce faire, je charge toutes les données dans une base de données sqlite. Cela fonctionne plutôt bien, mais j'ai décidé d'ajouter beaucoup plus de flexibilité, d'efficacité et de convivialité à ma base de données. Je prévois d'ajouter plus tard des planétoïdes à la simulation, et je voulais avoir une table pour chaque étoile. De cette façon, je n'aurais pas à interroger une table de 20m de planétoïdes pour le 1-4k dans chaque système solaire.
On m'a dit que l'utilisation de constructeurs de chaînes est mauvaise car elle me laisse vulnérable à une attaque par injection SQL. Bien que ce ne soit pas un gros problème ici car je suis la seule personne ayant accès à ces DB, j'aimerais suivre les meilleures pratiques. Et aussi de cette façon, si je fais un projet avec une situation similaire où il est ouvert au public, je sais quoi faire.
Actuellement, je fais ce:
cursor.execute("CREATE TABLE StarFrame"+self.name+" (etc etc)")
Cela fonctionne, mais je voudrais faire quelque chose de plus comme:
cursor.execute("CREATE TABLE StarFrame(?) (etc etc)",self.name)
Bien que je comprenne que ce serait probablement impossible. bien que je me contenterais de quelque chose comme
cursor.execute("CREATE TABLE (?) (etc etc)",self.name)
Si ce n'est pas du tout possible, j'accepterai cette réponse, mais si quelqu'un connaît un moyen de le faire, dites-le. :)
JE CODE en python.
6 réponses
Malheureusement, les tables ne peuvent pas être la cible de la substitution de paramètres (Je n'ai trouvé aucune source définitive, mais je l'ai vu sur quelques forums web).
Si vous vous inquiétez de l'injection (vous devriez probablement l'être), vous pouvez écrire une fonction qui nettoie la chaîne avant de la passer. Puisque vous cherchez juste un nom de table, vous devriez être sûr d'accepter simplement les Alphanumériques, en supprimant toute la ponctuation, comme )(][;,
et les espaces. Fondamentalement, gardez simplement A-Z a-z 0-9
.
def scrub(table_name):
return ''.join( chr for chr in table_name if chr.isalnum() )
scrub('); drop tables --') # returns 'droptables'
Je ne séparerais pas les données en plus d'une table. Si vous créez un index sur la colonne en étoile, vous n'aurez aucun problème à accéder efficacement aux données.
Essayez avec la mise en forme de chaîne:
sql_cmd = '''CREATE TABLE {}(id, column1, column2, column2)'''.format(
'table_name')
db.execute(sql_cmd)
Remplacez 'table_name'
par votre désir.
Pour les personnes qui cherchent un moyen de faire de la table une variable, j'ai obtenu ceci d'une autre réponse à la même question ici:
Il a dit ce qui suit et cela fonctionne. Tout est cité de mhawke :
Vous ne pouvez pas utiliser de substitution de paramètre pour le nom de la table. Vous devez ajouter le nom de la table à la chaîne de requête vous-même. Quelque chose comme ceci:
query = 'SELECT * FROM {}'.format(table)
c.execute(query)
Une chose à prendre en compte est la source de la valeur pour le nom de la table. Si cela vient d'un non fiable source, par exemple un utilisateur, alors vous devez valider le nom de la table pour éviter les attaques D'injection SQL potentielles. Une façon pourrait être de construire une requête paramétrée qui recherche le nom de la table à partir du catalogue DB:
import sqlite3
def exists_table(db, name):
query = "SELECT 1 FROM sqlite_master WHERE type='table' and name = ?"
return db.execute(query, (name,)).fetchone() is not None
Comme cela a été dit dans les autres réponses, "les tables ne peuvent pas être la cible de la substitution de paramètres" mais si vous vous trouvez dans une liaison où vous n'avez pas d'option, voici une méthode de test si le nom de table fourni est valide.
Note: j'ai fait du nom de la table un vrai cochon pour tenter de couvrir toutes les bases.
import sys
import sqlite3
def delim(s):
delims="\"'`"
use_delim = []
for d in delims:
if d not in s:
use_delim.append(d)
return use_delim
db_name = "some.db"
db = sqlite3.connect(db_name)
mycursor = db.cursor()
table = 'so""m ][ `etable'
delimiters = delim(table)
if len(delimiters) < 1:
print "The name of the database will not allow this!"
sys.exit()
use_delimiter = delimiters[0]
print "Using delimiter ", use_delimiter
mycursor.execute('SELECT name FROM sqlite_master where (name = ?)', [table])
row = mycursor.fetchall()
valid_table = False
if row:
print (table,"table name verified")
valid_table = True
else:
print (table,"Table name not in database", db_name)
if valid_table:
try:
mycursor.execute('insert into ' +use_delimiter+ table +use_delimiter+ ' (my_data,my_column_name) values (?,?) ',(1,"Name"));
db.commit()
except Exception as e:
print "Error:", str(e)
try:
mycursor.execute('UPDATE ' +use_delimiter+ table +use_delimiter+ ' set my_column_name = ? where my_data = ?', ["ReNamed",1])
db.commit()
except Exception as e:
print "Error:", str(e)
db.close()
Vous pouvez passer une chaîne en tant que commande SQL:
import sqlite3
conn = sqlite3.connect('db.db')
c = conn.cursor()
tablename, field_data = 'some_table','some_data'
query = 'SELECT * FROM '+tablename+' WHERE column1=\"'+field_data+"\""
c.execute(query)