71

私は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;'

洞察に乾杯。

4

9 に答える 9

100

公式文書によると:

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代わりに新しいモジュールを使用する必要があります:https ://stackoverflow.com/a/42980069/5285608 ]

また、psycopg2のドキュメントによると:

警告:SQLクエリ文字列に変数を渡すためにPython文字列連結()または文字列パラメータ補間( )を使用しないください。銃を突きつけても。+%

于 2017-03-22T09:37:00.463 に答える
32

この答えに従って、あなたはそうすることができます:

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")})
于 2015-02-18T20:26:45.763 に答える
25

テーブル名をパラメーターとして渡すことはできませんが、それ以外はすべて渡すことができます。したがって、テーブル名はアプリにハードコーディングする必要があります(入力を受け取ったり、プログラム外のものを名前として使用したりしないでください)。あなたが持っているコードはこれのために働くはずです。

外部のテーブル名を使用する正当な理由がある可能性がわずかにある場合は、ユーザーが直接入力できないようにしてください。おそらく、インデックスを渡してテーブルを選択したり、テーブル名を他の方法で検索したりできます。ただし、これを行う場合は注意が必要です。テーブル名が比較的少ないため、これは機能します。テーブル名を検証する方法を見つければ、問題ないはずです。

テーブル名が存在するかどうかを確認するために、このようなことを行うことができます。これはパラメータ化されたバージョンです。SQLコードを実行する前に、必ずこれを実行し、出力を確認してください。このためのアイデアの一部は、この回答から来ています。

SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' and table_name=%s LIMIT 1
于 2012-12-10T00:29:49.503 に答える
5

これは私が過去に使用した回避策です

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

お役に立てば幸いです:)

于 2017-05-27T21:52:08.050 に答える
2

変数テーブル(...)名を使用して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、値の処理のためにそれらを保持します。この関数は、引用符がないかどうかをチェックできます。これは、すべての引用符を処理する必要があるためです...

于 2013-12-13T19:06:17.613 に答える
1

テーブル名をパラメーターとして渡したい場合は、次のラッパーを使用できます。

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

于 2013-08-30T19:07:04.767 に答える
1

これは@AntoineDusséauxの答えへの小さな追加です。SQLクエリで2つの(引用符で囲まれていない)パラメータを渡したい場合は、次のように行うことができます。

query = sql.SQL("select {field} from {table} where {pkey} = %s").format(
    field=sql.Identifier('my_name'),
    table=sql.Identifier('some_table'),
    pkey=sql.Identifier('id'))

ドキュメントによると、

通常、クエリのテンプレートを{}スタイルのプレースホルダーを持つSQLインスタンスとして表現し、format()を使用して変数部分をそれらにマージする必要があります。これらはすべてComposableサブクラスである必要があります。クエリに%sスタイルのプレースホルダーを含めて、execute()に値を渡すことができます。このような値のプレースホルダーはformat()によって変更されません。

ソース:https ://www.psycopg.org/docs/sql.html#module-usage

また、クエリを作成するときは、このことに注意してください。

于 2021-04-14T03:11:39.107 に答える
0

テーブル名にモジュール形式を使用してから、実行に通常のパラメーター化を使用できます。

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

これがエンドユーザーに公開されている場合は、SQLインジェクションを記述しない限り、SQLインジェクションから保護されないことに注意してください。

于 2015-12-04T21:56:28.407 に答える
-6

誰もこれを行うことに言及していないことに驚いた:

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

formatは、引用符なしで文字列を挿入します。

于 2015-12-16T14:39:06.517 に答える