Подтвердить что ты не робот

Передача имени таблицы в качестве параметра в psycopg2

У меня есть следующий код, используя 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)

Выводится:

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;

Когда я выполняю это, он падает - это понятно, поскольку цитаты вокруг имени таблицы являются незаконными.

Есть ли способ законно передать имя таблицы в качестве параметра или мне нужно выполнить конкатенацию строки (явно предупрежденную против), то есть:

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

Приветствия за любые идеи.

4b9b3361

Ответ 1

Имя таблицы нельзя передать как параметр, но все остальное может. Таким образом, имя таблицы должно быть жестко закодировано в вашем приложении (не принимать входные данные или использовать что-либо за пределами программы в качестве имени). Код, который у вас есть, должен работать для этого.

При небольшой вероятности того, что у вас есть законная причина принять имя внешней таблицы, убедитесь, что вы не позволяете пользователю напрямую вводить его. Возможно, индекс можно было бы выбрать для выбора таблицы, иначе имя таблицы можно было бы искать другим способом. Однако вы правы, чтобы быть осторожным в этом. Это работает, потому что относительно немного имен таблиц. Найдите способ проверки имени таблицы, и все должно быть в порядке.

Можно было бы сделать что-то подобное, чтобы увидеть, существует ли имя таблицы. Это параметризованная версия. Просто убедитесь, что вы это сделаете и проверьте вывод перед запуском SQL-кода. Часть идеи для этого исходит от этого ответа.

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

Ответ 2

Согласно официальной документации:

Если вам нужно динамически генерировать SQL-запрос (, например выбирая динамически имя таблицы), вы можете использовать средства предоставляемый модулем psycopg2.sql.

Модуль sql является новым в psycopg2 версии 2.7. Он имеет следующий синтаксис:

from psycopg2 import sql

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

Подробнее о: http://initd.org/psycopg/docs/sql.html#module-psycopg2.sql

[Обновить 2017-03-24: AsIs НЕ следует использовать для представления имен таблиц или полей, вместо этого следует использовать новый sql модуль: fooobar.com/questions/155625/...]

Кроме того, согласно документации psycopg2:

Предупреждение: никогда, никогда, НИКОГДА использовать конкатенацию строк Python (+) или интерполяцию строк (%) на передать переменные в строку запроса SQL. Даже под прицелом.

Ответ 3

Per этот ответ вы можете сделать это так:

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

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

Ответ 4

Это обходной путь, который я использовал в прошлом

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

Надеюсь, что это поможет:)

Ответ 5

Я создал небольшую утилиту для предварительной обработки операторов SQL с именами переменных таблицы (...):

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'))

Объект SmartDict возвращает %(key)s для каждого неизвестного key, сохраняя их для обработки значений. Функция может проверять отсутствие каких-либо символов кавычек, так как теперь все цитаты должны быть учтены...

Ответ 6

Если вы хотите передать имя таблицы в качестве параметра, вы можете использовать эту оболочку:

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

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

    def getquoted(self):
        return str(self)

Использование: cursor.execute("CREATE TABLE %s ...", (Literal(name), ))

Ответ 7

Вы можете просто использовать формат модуля для имени таблицы, а затем использовать регулярную параметризацию для выполнения:

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

Имейте в виду, если это будет доступно для конечного пользователя, вы не будете защищены от SQL-инъекции, если не будете писать для него.

Ответ 8

Удивленный никто не упомянул об этом:

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

помещает строку без кавычек.