Pasando el nombre de la tabla como parámetro en psycopg2

Tengo el siguiente código, usando 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) 

Esto produce:

 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; 

Cuando ejecuto esto, se cae, esto es comprensible, ya que las comillas alrededor del nombre de la tabla son ilegales.

¿Hay alguna manera de pasar legalmente el nombre de la tabla como un parámetro, o tengo que hacer una concatenación de cadenas (explícitamente advertida contra), es decir:

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

Saludos por cualquier información.

El nombre de la tabla no se puede pasar como parámetro, pero todo lo demás puede pasarse. Por lo tanto, el nombre de la tabla debe estar codificado en su aplicación (No tome entradas ni use nada fuera del progtwig como nombre). El código que tienes debería funcionar para esto.

En la pequeña posibilidad de que tenga una razón legítima para tomar un nombre de tabla externa, asegúrese de no permitir que el usuario lo ingrese directamente. Tal vez se podría pasar un índice para seleccionar una tabla, o se podría buscar el nombre de la tabla de alguna otra manera. Sin embargo, tienes razón en tener cuidado de hacer esto. Esto funciona, porque hay relativamente pocos nombres de tablas alrededor. Encuentre una manera de validar el nombre de la tabla, y debería estar bien.

Sería posible hacer algo como esto, para ver si el nombre de la tabla existe. Esta es una versión parametrizada. Solo asegúrese de hacer esto y verifique la salida antes de ejecutar el código SQL. Parte de la idea para esto viene de esta respuesta .

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

Según la documentación oficial:

Si necesita generar dinámicamente una consulta SQL ( por ejemplo, elegir dinámicamente un nombre de tabla ) puede usar las facilidades proporcionadas por el módulo psycopg2.sql.

El módulo sql es nuevo en psycopg2 versión 2.7. Tiene la siguiente syntax:

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

Más en: http://initd.org/psycopg/docs/sql.html#module-psycopg2.sql

[Actualización 2017-03-24: AsIs no se debe usar para representar nombres de tablas o campos, se debe usar el nuevo módulo sql su lugar: https://stackoverflow.com/a/42980069/5285608 ]

Además, según la documentación de psycopg2:

Advertencia : Nunca, nunca , NUNCA utilice la concatenación de cadenas de Python ( + ) o la interpolación de parámetros de cadenas ( % ) para pasar variables a una cadena de consulta SQL. Ni siquiera a punta de pistola.

Por esta respuesta puedes hacerlo así:

 import psycopg2 from psycopg2.extensions import AsIs #Create your connection and cursor... cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")}) 

He creado una pequeña utilidad para el preprocesamiento de sentencias SQL con nombres de tabla de 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')) 

El objeto SmartDict devuelve %(key)s por cada key desconocida, conservándolas para el manejo del valor. La función podría verificar la ausencia de caracteres de comillas, ya que todas las citas ahora deben ser atendidas …

Si desea pasar el nombre de la tabla como un parámetro, puede usar este contenedor:

 class Literal(str): def __conform__(self, quote): return self @classmethod def mro(cls): return (object, ) def getquoted(self): return str(self) 

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

Esta es una solución que he usado en el pasado

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

Espero que te ayude 🙂

Solo puede usar el formato del módulo para el nombre de la tabla y luego usar la patwigterización regular para la ejecución:

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

Tenga en cuenta que si está expuesto al usuario final, no estará protegido contra la inyección de SQL a menos que escriba para ello.

Sorprendido nadie ha mencionado hacer esto:

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

Formato pone en la cadena sin comillas.