web-dev-qa-db-fra.com

Passer le nom de la table comme paramètre dans psycopg2

J'ai le code suivant, en utilisant pscyopg2:

sql = 'select %s from %s where utctime > %s and utctime < %s order by utctime asc;'
data = (dataItems, voyage, dateRangeLower, dateRangeUpper)
rows = cur.mogrify(sql, data)

Cela produit:

select 'waterTemp, airTemp, utctime' from 'ss2012_t02' where utctime > '2012-05-03T17:01:35+00:00'::timestamptz and utctime < '2012-05-01T17:01:35+00:00'::timestamptz order by utctime asc;

Lorsque j'exécute cela, cela tombe - c'est compréhensible, car les guillemets autour du nom de la table sont illégaux.

Existe-t-il un moyen de transmettre légalement le nom de la table en tant que paramètre, ou dois-je faire une concaténation de chaîne (explicitement déconseillée), c'est-à-dire:

voyage = 'ss2012_t02'
sql = 'select %s from ' + voyage + ' where utctime > %s and utctime < %s order by utctime asc;'

Bravo pour toutes les idées.

44
Caligari

Le nom de la table ne peut pas être passé en tant que paramètre, mais tout le reste le peut. Ainsi, le nom de la table doit être codé en dur dans votre application (ne prenez pas d'entrées ou n'utilisez rien en dehors du programme comme nom). Le code que vous avez devrait fonctionner pour cela.

Au cas où vous auriez une raison légitime de prendre un nom de table externe, assurez-vous de ne pas autoriser l'utilisateur à le saisir directement. Peut-être qu'un index pourrait être passé pour sélectionner une table, ou le nom de la table pourrait être recherché d'une autre manière. Vous avez cependant raison de vous méfier de cela. Cela fonctionne, car il y a relativement peu de noms de table. Trouvez un moyen de valider le nom de la table, et ça devrait aller.

Il serait possible de faire quelque chose comme ça, pour voir si le nom de la table existe. Il s'agit d'une version paramétrée. Assurez-vous simplement de le faire et vérifiez la sortie avant d'exécuter le code SQL. Une partie de l'idée vient de cette réponse .

SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' and table_name=%s LIMIT 1
18
PearsonArtPhoto

Par cette réponse vous pouvez le faire comme suit:

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")})
30
jczaplew

Ceci est une solution de contournement que j'ai utilisée dans le passé

query = "INSERT INTO %s (col_1, col_2) VALUES (%%s, %%s)" % table_name
cur.execute(query, (col_1_var, col_2_var))

J'espère que cela vous aidera :)

3

J'ai créé un petit utilitaire pour le prétraitement des instructions SQL avec des noms de table variables (...):

from string import letters
NAMECHARS = frozenset(set(letters).union('.'))

def replace_names(sql, **kwargs):
    """
    Preprocess an SQL statement: securely replace table ... names
    before handing the result over to the database adapter,
    which will take care of the values.

    There will be no quoting of names, because this would make them
    case sensitive; instead it is ensured that no dangerous chars
    are contained.

    >>> replace_names('SELECT * FROM %(table)s WHERE val=%(val)s;',
    ...               table='fozzie')
    'SELECT * FROM fozzie WHERE val=%(val)s;'
    """
    for v in kwargs.values():
        check_name(v)
    dic = SmartDict(kwargs)
    return sql % dic

def check_name(tablename):
    """
    Check the given name for being syntactically valid,
    and usable without quoting
    """
    if not isinstance(tablename, basestring):
        raise TypeError('%r is not a string' % (tablename,))
    invalid = set(tablename).difference(NAMECHARS)
    if invalid:
        raise ValueError('Invalid chars: %s' % (Tuple(invalid),))
    for s in tablename.split('.'):
        if not s:
            raise ValueError('Empty segment in %r' % tablename)

class SmartDict(dict):
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            check_name(key)
            return key.join(('%(', ')s'))

L'objet SmartDict renvoie %(key)s pour chaque key inconnu, en les conservant pour la gestion des valeurs. La fonction pourrait vérifier l'absence de tout caractère de citation, car toutes les citations doivent maintenant être prises en charge ...

2
Tobias

Si vous souhaitez passer le nom de la table en tant que paramètre, vous pouvez utiliser ce wrapper:

class Literal(str):
    def __conform__(self, quote):
        return self

    @classmethod
    def mro(cls):
        return (object, )

    def getquoted(self):
        return str(self)

Utilisation: cursor.execute("CREATE TABLE %s ...", (Literal(name), ))

1
malthe

Vous pouvez simplement utiliser le format de module pour le nom de la table, puis utiliser la paramétrisation régulière pour l'exécution:

xlist = (column, table)
sql = 'select {0} from {1} where utctime > %s and utctime < %s order by utctime asc;'.format(xlist)

Gardez à l'esprit que si cela est exposé à l'utilisateur final, vous ne serez pas protégé contre l'injection SQL à moins que vous n'écriviez pour cela.

0
Derek Bartron