使用准备好的语句在 PostgreSQL 上使用 SQL:2008 标准限制结果
Limiting results with the SQL:2008 standard on PostgreSQL using prepared statements
在编写示例来测试 PostgreSQL 对 SQL:2008 结果集限制功能的支持时,我意识到此语法不适用于准备好的语句:
SELECT pc.id AS pc_id, p.id AS p_id
FROM post_comment pc
INNER JOIN post p ON p.id = pc.post_id
ORDER BY pc.id
OFFSET ? ROWS
FETCH FIRST ? ROWS ONLY;
虽然对于使用文字的静态语句来说它很好,但使用准备好的语句会抛出:
org.postgresql.util.PSQLException: ERROR: syntax error at or near ""
Position: 140 at
org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182)
~[postgresql-9.4-1202-jdbc41.jar:9.4] at
org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911)
~[postgresql-9.4-1202-jdbc41.jar:9.4] at
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173)
~[postgresql-9.4-1202-jdbc41.jar:9.4] at
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:615)
~[postgresql-9.4-1202-jdbc41.jar:9.4] at
org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:465)
~[postgresql-9.4-1202-jdbc41.jar:9.4] at
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:458)
~[postgresql-9.4-1202-jdbc41.jar:9.4]
如果SQL查询改为:
SELECT pc.id AS pc_id, p.id AS p_id
FROM post_comment pc
INNER JOIN post p ON p.id = pc.post_id
ORDER BY pc.id
OFFSET ? ROWS
FETCH FIRST (?) ROWS ONLY;
括号似乎起到了作用,并且考虑了绑定参数。
这是一个错误还是只是一个实现细节?
测试时间为 GitHub
TL;DR:实现细节。
这不是 JDBC 驱动程序问题,而是数据库级别的问题。
test=> SELECT 1 OFFSET 1 ROWS FETCH FIRST 1 ROWS ONLY;
?column?
----------
(0 rows)
test=> PREPARE stmt(integer, integer) AS SELECT 1 OFFSET ROWS FETCH FIRST ROWS ONLY;
ERROR: syntax error at or near ""
LINE 1: ..., integer) AS SELECT 1 OFFSET ROWS FETCH FIRST ROWS ON...
问题是 FETCH FIRST n ROWS ONLY
的参数没有被解析为作为参数放置候选的文字。
在src/backend/parser/gram.y
中:
/* SQL:2008 syntax */
| FETCH first_or_next opt_select_fetch_first_value row_or_rows ONLY
{ $$ = ; }
和
/*
* Allowing full expressions without parentheses causes various parsing
* problems with the trailing ROW/ROWS key words. SQL only calls for
* constants, so we allow the rest only with parentheses. If omitted,
* default to 1.
*/
opt_select_fetch_first_value:
SignedIconst { $$ = makeIntConst(, @1); }
| '(' a_expr ')' { $$ = ; }
| /*EMPTY*/ { $$ = makeIntConst(1, -1); }
;
表明这实际上是有意的,并且需要括号来消除参数使用的歧义,但是 SQL:2008 并不要求无论如何都支持它作为查询参数。
如果要提供参数,请使用括号。
在编写示例来测试 PostgreSQL 对 SQL:2008 结果集限制功能的支持时,我意识到此语法不适用于准备好的语句:
SELECT pc.id AS pc_id, p.id AS p_id
FROM post_comment pc
INNER JOIN post p ON p.id = pc.post_id
ORDER BY pc.id
OFFSET ? ROWS
FETCH FIRST ? ROWS ONLY;
虽然对于使用文字的静态语句来说它很好,但使用准备好的语句会抛出:
org.postgresql.util.PSQLException: ERROR: syntax error at or near "" Position: 140 at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182) ~[postgresql-9.4-1202-jdbc41.jar:9.4] at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911) ~[postgresql-9.4-1202-jdbc41.jar:9.4] at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173) ~[postgresql-9.4-1202-jdbc41.jar:9.4] at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:615) ~[postgresql-9.4-1202-jdbc41.jar:9.4] at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:465) ~[postgresql-9.4-1202-jdbc41.jar:9.4] at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:458) ~[postgresql-9.4-1202-jdbc41.jar:9.4]
如果SQL查询改为:
SELECT pc.id AS pc_id, p.id AS p_id
FROM post_comment pc
INNER JOIN post p ON p.id = pc.post_id
ORDER BY pc.id
OFFSET ? ROWS
FETCH FIRST (?) ROWS ONLY;
括号似乎起到了作用,并且考虑了绑定参数。
这是一个错误还是只是一个实现细节?
测试时间为 GitHub
TL;DR:实现细节。
这不是 JDBC 驱动程序问题,而是数据库级别的问题。
test=> SELECT 1 OFFSET 1 ROWS FETCH FIRST 1 ROWS ONLY;
?column?
----------
(0 rows)
test=> PREPARE stmt(integer, integer) AS SELECT 1 OFFSET ROWS FETCH FIRST ROWS ONLY;
ERROR: syntax error at or near ""
LINE 1: ..., integer) AS SELECT 1 OFFSET ROWS FETCH FIRST ROWS ON...
问题是 FETCH FIRST n ROWS ONLY
的参数没有被解析为作为参数放置候选的文字。
在src/backend/parser/gram.y
中:
/* SQL:2008 syntax */
| FETCH first_or_next opt_select_fetch_first_value row_or_rows ONLY
{ $$ = ; }
和
/*
* Allowing full expressions without parentheses causes various parsing
* problems with the trailing ROW/ROWS key words. SQL only calls for
* constants, so we allow the rest only with parentheses. If omitted,
* default to 1.
*/
opt_select_fetch_first_value:
SignedIconst { $$ = makeIntConst(, @1); }
| '(' a_expr ')' { $$ = ; }
| /*EMPTY*/ { $$ = makeIntConst(1, -1); }
;
表明这实际上是有意的,并且需要括号来消除参数使用的歧义,但是 SQL:2008 并不要求无论如何都支持它作为查询参数。
如果要提供参数,请使用括号。