动态SQL WHERE子句生成

时间:2013-03-19 09:47:23

标签: python sql sqlite language-agnostic sql-injection

为了记录,我正在使用Python和SQLlite。我有一个生成我需要的SQL的工作函数,但它似乎不正确。

def daily(self, host=None, day=None):
    sql = "SELECT * FROM daily WHERE 1"
    if host:
        sql += " AND host = '%s'" % (host,)
    if day:
        sql += " AND day = '%s'" % (day,)
    return sql

我可能需要稍后添加多个列和条件。

有更好的想法吗?

修改 看起来不正确的是我正在从Strings动态构建SQL。这通常不是最好的方法。 SQL注入attacs,需要正确转义字符串。我不能使用占位符,因为某些值为None,并且不需要处于WHERE子句条件中。

2 个答案:

答案 0 :(得分:10)

确实不想使用字符串格式来包含值。通过SQL参数将其保留到数据库API。

使用参数:

  • 为数据库提供准备语句的机会,并重用查询计划以获得更好的性能。
  • 让您自己避免正确逃避价值的麻烦(包括避免允许SQL转义和SQL注入攻击)。

从SQLLite supports named SQL parameters开始,我将返回一个带有参数的语句和字典:

def daily(self, host=None, day=None):
    sql = "SELECT * FROM daily"
    where = []
    params = {}
    if host is not None:
        where.append("host = :host")
        params['host'] = host
    if day is not None:
        where.append("day = :day")
        params['day'] = day
    if where:
        sql = '{} WHERE {}'.format(sql, ' AND '.join(where))
    return sql, params

然后将两个传递给cursor.execute()

cursor.execute(*daily(host, day))

SQL生成变得很复杂快速,您可能希望查看SQLAlchemy core来代替生成。

对于您的示例,您可以生成:

from sqlalchemy import Table, Column, Integer, String, Date, MetaData

metadata = MetaData()
daily = Table('daily', metadata, 
    Column('id', Integer, primary_key=True),
    Column('host', String),
    Column('day', Date),
)
from sqlalchemy.sql import select

def daily(self, host=None, day=None):
    query = select([daily])
    if host is not None:
        query = query.where(daily.c.host == host)
    if day is not None:
        query = query.where(daily.c.day == day)
    return query

query对象可以应用其他过滤器,排序,分组,用作其他查询的子选择,加入并最终发送执行,此时SQLAlchemy将此变为适合特定的SQL您要连接的数据库。

答案 1 :(得分:1)

仅出于完整性考虑。我发现pypika库非常方便(如果允许使用库):

https://pypika.readthedocs.io/en/latest/index.html

它允许构造如下的sql查询:

from pypika import Query

q = Query._from('daily').select('*')
if host:
     q = q.where('host' == host)
if day:
     q = q.where('day' == day)
sql = str(q)