"Psycopg2.error column doesn't exist" 尝试在 Python 上编写注入安全的 PostgreSQL 查询时出错

"Psycopg2.error column doesn't exist" error when trying to write injection-safe PostgreSQL query on Python

我正在编写一个函数,使用 psycopg2 库为 PostgreSQL 数据库动态创建 insert/update/select 查询。

我一直在尝试根据 psycopg2 documentation 提供的建议编写函数注入安全 - 使用 sql.Sql 方法正确编写查询。所有参数(table 名称、要插入的列、值)都动态传递给函数:

def insert(table, columns: list, values: list):

    query = sql.SQL('INSERT INTO {} ({}) VALUES ({});').format(
                    sql.Identifier(table),
                    sql.SQL(', ').join(map(sql.Identifier, columns)),
                    sql.SQL(', ').join(map(sql.Identifier, values))
                   )
    cursor = foo_connection.cursor()
    cursor.execute(query)

当我尝试测试函数时:

insert('test_table', ['col1', 'col2', 'col3'], ['1', '2', '3'])

我收到以下错误:

psycopg2.errors.UndefinedColumn: column "1" does not exist
LINE 1: ...e" ("col1", "col2", "col3") VALUES ("1", "2", ...

我没有理解这个错误,因为从技术上讲它甚至不是一列,它是一个要插入的值。

我认为查询的组成不正确,但 print(query.as_string(foo_connection)) 的结果表明它似乎是正确的:

INSERT INTO "test_table" ("col1", "col2", "col3") VALUES ("1", "2", "3");

官方文档没有涵盖这种情况。谷歌搜索也没有给我答案。

所以,问题是:

  1. 我做错了什么?
  2. 如何使这段代码起作用?

我认为这是您对查询的引用。也许尝试在外面使用双打:

def insert(table, columns: list, values: list):

    query = sql.SQL("INSERT INTO {} ({}) VALUES ({});").format(
                    sql.Identifier(table),
                    sql.SQL(', ').join(map(sql.Identifier, columns)),
                    sql.SQL(', ').join(map(sql.Literlas, values))
                   )
    cursor = foo_connection.cursor()
    cursor.execute(query)

问题是您将值常量格式化为 sql.Identifier

这导致它们用双引号而不是单引号引起来,因此 PostgreSQL 将它们视为 table 列。因此出现错误消息。

最好的办法是对文字使用 %s 占位符,并将它们作为第二个参数传递给 execute()

正如 Laurenz 所说,在 psycopg2 文档中,它是这样写的:

Identifiers usually represent names of database objects, such as tables or fields.

我还看到了 Literals 对象:

representing an SQL value to include in a query.

也许您可以在格式化查询时尝试用文字替换标识符:

query = sql.SQL('INSERT INTO {} ({}) VALUES ({});').format(
                sql.Identifier(table),
                sql.SQL(', ').join(map(sql.Identifier, columns)),
                sql.SQL(', ').join(map(sql.Literals, values))
               )