2015-12-09 12 views
5

Sto riscontrando un problema interessante con PostgreSQL tramite JDBC (non è stato possibile riprodurlo al di fuori di JDBC) dove sto ottenendo un"ERRORE: il piano memorizzato nella cache non deve modificare il tipo di risultato" durante il mix DDL con SELECT via JDBC

“ERROR: cached plan must not change result type”

il modo più semplice per riprodurre il problema è quello di utilizzare il seguente codice:

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(); 
    } 

il fatto che il seguente codice funziona porta bene a me assumendo questo è un molto sottile bug nel driver JDBC (nota, ho semplicemente rimosso la sesta istruzione DDL nel batch):

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(); 
    } 

sembrerebbe che scartando tutti i piani memorizzati nella cache tramite DISCARD ALL dovrebbe funzionare, ma rende le cose peggiori:

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

“ERROR: prepared statement "S_1" doesn't exist”

Qualcuno sa una soluzione? O un puntatore che documenta questo bug? Bit interessante, sembra essere correlato allo default prepare threshold of 5

+0

Hai provato con altri nomi di variabili? Forse 'f' è una sorta di parola chiave riservata? Inoltre, sarebbero utili le tracce di stack complete. –

+0

@a_horse_with_no_name: È, ma questa è la versione semplificata del problema reale in cui rimuovere la dichiarazione preparata dal gioco è meno semplice –

+0

@aguibert;) Buon punto. Ci ho provato, ma no. La versione reale usa le virgolette sui nomi comunque ... –

risposta

5

Questo sembra essere correlato a PostgreSQL's PREPARE_THRESHOLD, which defaults to 5 for the JDBC driver.

impostandolo a zero risolverà/lavoro intorno a questo particolare problema:

((PGConnection) connection).setPrepareThreshold(0); 

More info is also available in this stack overflow question

+1

Puoi anche modificarlo tramite un parametro URL: https://jdbc.postgresql.org/documentation/94/connect.html#connection-parameters –

+2

Ecco la correzione: https://github.com/pgjdbc/pgjdbc/pull/451 –

1

Disabilitare prepared statement è troppo drastico un'azione per risolvere questo problema. È ora possibile risolvere il problema specifico impostando autosave=conservative sulle impostazioni di connessione pgjdbc, vedere: https://stackoverflow.com/a/48536394/924597

Problemi correlati