web-dev-qa-db-ja.com

クエリにテーブル名を挿入すると、sqlite3.OperationalError:near "?":構文エラーが発生します

SQLクエリで使用するテーブルを動的に選択したいのですが、エラーが発生し続けますが、これをフォーマットしようとしています。試してみました%s の代わりに ?

助言がありますか?

group_food = (group, food)
group_food_new = (group, food, 1)

with con:

    cur = con.cursor() 
    tmp = cur.execute("SELECT COUNT(Name) FROM (?) WHERE Name=?", group_food)

    if tmp == 0:
        cur.execute("INSERT INTO ? VALUES(?, ?)", group_food_new)
    else: 
        times_before = cur.execute("SELECT Times FROM ? WHERE Name=?", group_food)
        group_food_update = (group, (times_before +1), food)

        cur.execute("UPDATE ? SET Times=? WHERE Name=?", group_food_update)
26
Crytrus

SQLパラメーターをSQLオブジェクトのプレースホルダーとして使用することはできません。 SQLパラメーターを使用するreasonsの1つは、データベースがデータベースオブジェクトの内容を間違えないように値をエスケープすることです。

データベースオブジェクトを個別に補間する必要があります。 "二重引用符パラメーターを2倍にして識別子をエスケープし、使用します

cur.execute('SELECT COUNT(Name) FROM "{}" WHERE Name=?'.format(group.replace('"', '""')), (food,))

そして

cur.execute('INSERT INTO "{}" VALUES(?, ?)'.format(group.replace('"', '""')), (food, 1))

そして

cur.execute('UPDATE "{}" SET Times=? WHERE Name=?'.format(group.replace('"', '""')),
            (times_before + 1, food))

".."二重引用符は、識別子が有効なキーワードであっても、識別子を適切にマーク解除するためにあります。名前にある既存の"文字はすべて二重にする必要があります。これは、SQLインジェクションの試行を解除するのにも役立ちます。

ただし、オブジェクト名がユーザーから提供されている場合は、ここでSQLインジェクション攻撃を防ぐために、オブジェクト名に対して独自の(厳密な)検証を行う必要があります。その場合、既存のオブジェクトに対して常に検証してください。

代わりに SQLAlchemy のようなプロジェクトを使用してSQLを生成することを検討する必要があります。オブジェクト名の検証を行い、SQLインジェクションのリスクから厳密に保護できます。 テーブル定義を前もってロードする できるため、どの名前が有効であるかがわかります。

from sqlalchemy import create_engine, func, select, MetaData

engine = create_engine('sqlite:////path/to/database')
meta = MetaData()
meta.reflect(bind=engine)
conn = engine.connect()

group_table = meta.tables[group]  # can only find existing tables
count_statement = select([func.count(group_table.c.Name)], group_table.c.Name == food)
count, = conn.execute(count_statement).fetchone()
if count:
    # etc.
44
Martijn Pieters