对于 postgresql,java sql 准备好的语句不能正确转义

Posted

技术标签:

【中文标题】对于 postgresql,java sql 准备好的语句不能正确转义【英文标题】:java sql prepared statement doesn't escape correctly for postgresql 【发布时间】:2021-06-23 01:41:09 【问题描述】:

我在 Java 中使用 PreparedStatement 为 postgresql 创建 sql 命令,但是所有占位符都用单引号转义,这与使用双引号的 postgresql 要求不同。

下面是日志和错误消息

2021-06-22 18:33:08,157 INFO  [ai.tus.map.fea.per.uti.MapperHelper] (executor-thread-199) Insert prepared statement: wrapped[ insert into "known_fruits" (?) VALUES (?) ]
2021-06-22 18:33:08,158 INFO  [ai.tus.map.fea.per.uti.MapperHelper] (executor-thread-199) Insert statement: wrapped[ insert into "known_fruits" ('name') VALUES ('ddf') ]
2021-06-22 18:33:08,159 ERROR [ai.tus.map.fea.FruitResource] (executor-thread-199) Failed to handle request: org.postgresql.util.PSQLException: ERROR: syntax error at or near "$1"
  Position: 29
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
        at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
        at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
        at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
        at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:130)
        at io.agroal.pool.wrapper.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:88)

对应的代码是:

        LOGGER.info("Insert prepared statement: " + stmt.toString());
        for (int i = 1; i <= fieldNum; ++i) 
            stmt.setString(i, existFields.get(i - 1).name);
            stmt.setObject(i + fieldNum, existFiledVals.get(i - 1));
        
        LOGGER.info("Insert statement: " + stmt.toString());

编辑:

我要构建的 sql 字符串:

insert into "known_fruits" ("name") VALUES ("ddf")

我用占位符放入 PreparedStatement 的 sql 字符串是:

insert into "known_fruits" (?) VALUES (?)

我得到的最后一个 sql 字符串是:

insert into "known_fruits" ('name') VALUES ('ddf')

正如@Andreas 指出的那样,我不应该将? 用于表名或列名,它们仅用于值注入。所以我的问题是有什么方法可以安全地使 sql 处理转义表名或列名?由于这些值是运行时确定的,所以不知道是否会有一些与sql关键字相同的表名需要转义。

【问题讨论】:

您不能使用? 参数标记来注入标识符,例如表名和列名。您只能注入 。您必须使用字符串连接来插入列名,然后使用? 来处理值。 请edit您的问题并向我们展示您正在构建的完整 SQL 字符串。 @Andreas 有没有让 sql 安全地注入表名或列名?因为这些仍然是运行时值,所以我不知道是否需要转义它们 values ("ddf") 开头是错误的,因为"ddf" 指的是列名。它不是 SQL 字符串常量 表名和列名是否来自可信来源?如果是,例如代码中其他地方的硬编码列表,那么您不需要转义。如果没有,例如它们是 CSV 文件中的标题名称,那么您绝对需要验证或转义这些名称。 --- 如果您希望名称不被引用(推荐),那么您应该简单地验证名称仅包含有效字符,这基本上意味着匹配正则表达式\w+ 【参考方案1】:

你不能为列名使用参数,比如在

insert into "known_fruits" (?) VALUES (?)

您必须构造一个查询字符串,其中包含作为文字的列名。

【讨论】:

OP 从未编写过$1JDBC 驱动程序? 替换为$1 @Andreas 啊,我明白了,是的。我已经确定了答案。

以上是关于对于 postgresql,java sql 准备好的语句不能正确转义的主要内容,如果未能解决你的问题,请参考以下文章

使用 JDBC 插入 PostgreSQL 时间类型时出错

PostgreSQL之慢SQL语句

PostgreSQL之慢SQL语句

PostgreSQL“列不存在”但它确实存在

PostgreSQL之SQL函数介绍及实践

如何使用 Java 在 PostgreSQL 中安全地转义 SQL 的任意字符串