将DDL与SELECT混合使用时,"错误:缓存计划不得更改结果类型"

Luk*_*der 6 java sql postgresql jdbc

我正在通过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();
    }
Run Code Online (Sandbox Code Playgroud)

以下代码工作正常的事实导致我假设这是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();
    }
Run Code Online (Sandbox Code Playgroud)

似乎丢弃所有缓存的计划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();
    }
Run Code Online (Sandbox Code Playgroud)

我正在遇到另一条错误消息

"错误:准备好的声明"S_1"不存在"

有没有人知道解决方法?还是记录这个bug的指针?有趣的是,它似乎与默认的准备阈值5相关

Luk*_*der 5

这似乎与PostgreSQL相关PREPARE_THRESHOLD,对于JDBC驱动程序,默认为5.

将其设置为零将解决/解决此特定问题:

 ((PGConnection) connection).setPrepareThreshold(0);
Run Code Online (Sandbox Code Playgroud)

此堆栈溢出问题中也提供了更多信息