将 DDL 与 SELECT 通过 JDBC 混合时出现“错误:缓存计划不能更改结果类型”
Posted
技术标签:
【中文标题】将 DDL 与 SELECT 通过 JDBC 混合时出现“错误:缓存计划不能更改结果类型”【英文标题】:"ERROR: cached plan must not change result type" when mixing DDL with SELECT via JDBC 【发布时间】:2015-12-09 14:12:37 【问题描述】:我在通过 JDBC 使用 PostgreSQL 时遇到了一个有趣的问题(无法在 JDBC 之外重现它),我得到了一个
“错误:缓存的计划不能改变结果类型”
重现此问题的最简单方法是使用以下代码:
Connection c = getConnection();
c.setAutoCommit(true);
List<String> statements = Arrays.asList(
"create table t(a int)",
"select * from t",
"alter table t add b int",
"select * from t",
"alter table t add c int",
"select * from t",
"alter table t add d int",
"select * from t",
"alter table t add e int",
"select * from t",
"alter table t add f int",
"select * from t"
);
for (String statement : statements)
try (PreparedStatement s = c.prepareStatement(statement))
System.out.println(s);
s.execute();
以下代码运行良好的事实让我认为这是 JDBC 驱动程序中的一个非常微妙的错误(注意,我只是删除了批处理中的第六个 DDL 语句):
Connection c = getConnection();
c.setAutoCommit(true);
List<String> statements = Arrays.asList(
"create table t(a int)",
"select * from t",
"alter table t add b int",
"select * from t",
"alter table t add c int",
"select * from t",
"alter table t add d int",
"select * from t",
"alter table t add e int",
"select * from t"
);
for (String statement : statements)
try (PreparedStatement s = c.prepareStatement(statement))
System.out.println(s);
s.execute();
似乎通过DISCARD ALL
丢弃所有缓存的计划应该可行,但它会使事情变得更糟:
Connection c = getConnection();
c.setAutoCommit(true);
List<String> statements = Arrays.asList(
"create table t(a int)",
"select * from t",
"alter table t add b int",
"select * from t",
"alter table t add c int",
"select * from t",
"alter table t add d int",
"select * from t",
"alter table t add e int",
"select * from t",
"alter table t add f int",
"discard all",
"select * from t"
);
for (String statement : statements)
try (PreparedStatement s = c.prepareStatement(statement))
System.out.println(s);
s.execute();
I'm running into another error message
“错误:准备好的语句“S_1”不存在”
有人知道解决方法吗?还是记录此错误的指针?有点意思,好像和default prepare threshold of 5有关
【问题讨论】:
您是否尝试过使用其他变量名?也许f
是某种保留关键字?此外,完整的堆栈跟踪也会有所帮助。
@a_horse_with_no_name:是的,但这是真正问题的简化版本,从游戏中删除准备好的语句并不容易
@aguibert ;) 好点。我试过了,但没有。无论如何,真实版本在名称周围使用引号......
你检查过这个问题吗?看起来可能是重复:***.com/questions/2783813/…
@aguibert:我确实有,而且它不是重复的,至少不是来自公认的答案(由 OP)声称事情是并行发生的。就我而言,所有语句都在一个会话中执行。
【参考方案1】:
这似乎与PostgreSQL's PREPARE_THRESHOLD
, which defaults to 5 for the JDBC driver有关。
将其设置为零将解决/解决此特定问题:
((PGConnection) connection).setPrepareThreshold(0);
More info is also available in this stack overflow question
【讨论】:
您也可以通过 URL 参数更改此设置:jdbc.postgresql.org/documentation/94/…【参考方案2】:禁用准备好的语句对于解决此问题而言过于激烈。您现在可以通过在 pgjdbc 连接设置上设置autosave=conservative
来解决具体问题,请参阅:https://***.com/a/48536394/924597
【讨论】:
以上是关于将 DDL 与 SELECT 通过 JDBC 混合时出现“错误:缓存计划不能更改结果类型”的主要内容,如果未能解决你的问题,请参考以下文章
GenerationTarget 遇到异常接受命令:通过 JDBC 语句执行 DDL 时出错
Spring Boot 应用程序 Heroku PostgreSQL 错误:GenerationTarget 遇到异常接受命令:执行 DDL 时出错 ...通过 JDBC 语句
通过JDBC语句执行DDL“drop sequence player_seq”时出错
org.hibernate.tool.schema.spi.CommandAcceptanceException:通过JDBC语句执行DDL时出错