pentaho/pentaho-kettle · error · KettleDatabaseException

Unable to prepare statement for SQL statement [

Error message

Unable to prepare statement for SQL statement [

What it means

In Delete.prepareDelete, the generated DELETE SQL is handed to the JDBC connection via prepareStatement. If the driver throws a SQLException, it is wrapped in a KettleDatabaseException with the offending SQL text. The SQL string is logged, so the actual bad SQL is visible in the message.

Solutions

  1. Read the SQL shown in the error message and validate it directly against the target database (e.g. run it with a WHERE clause manually).
  2. Check the Delete step's target table and connection settings; use 'Browse' to confirm the table exists on the selected connection.
  3. Test the database connection (Test button) and confirm the JDBC driver is on the classpath.
  4. Check DB user privileges to DELETE on the target table, and review the wrapped SQLException cause for the driver-level reason.

Example fix

// before
<connection>ProdDB_OLD</connection> <table>CUSTMR</table>
// after
<connection>ProdDB</connection> <table>CUSTOMERS</table> // names verified in DB
Defensive patterns

Strategy: try-catch

Validate before calling

// Before running: verify table and connection
assert connectionMeta.testConnectionSuccess();
assert database.tableExists(targetSchema, targetTable);

Try / catch

try { prepareDelete(sql); } catch (KettleDatabaseException e) {
  Throwable cause = e.getCause();
  log.error("SQL failed: {} cause: {}", sql, cause == null ? "none" : cause.getMessage());
}

Prevention

When it happens

Trigger: prepareDelete (called from processRow) executes data.db.getConnection().prepareStatement(stripCR(sql)) and the JDBC driver rejects the statement: bad table/field names, driver connectivity loss, or dialect-incompatible generated SQL.

Common situations: Wrong database connection selected in the step; table or schema name misspelled or missing; missing driver or wrong URL; database object renamed after the transformation was built; insufficient privileges on the table.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/16b51e997e4ad835. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/delete/Delete.java:210

        sql += " BETWEEN ? AND ? ";
        data.deleteParameterRowMeta.addValueMeta( rowMeta.searchValueMeta( meta.getKeyFields()[i].getKeyStream() ) );
        data.deleteParameterRowMeta.addValueMeta( rowMeta.searchValueMeta( meta.getKeyFields()[i].getKeyStream2() ) );
      } else if ( "IS NULL".equalsIgnoreCase( meta.getKeyFields()[i].getKeyCondition() )
              || "IS NOT NULL".equalsIgnoreCase( meta.getKeyFields()[i].getKeyCondition() ) ) {
        sql += " " + meta.getKeyFields()[i].getKeyCondition() + " ";
      } else {
        sql += " " + meta.getKeyFields()[i].getKeyCondition() + " ? ";
        data.deleteParameterRowMeta.addValueMeta( rowMeta.searchValueMeta( meta.getKeyFields()[i].getKeyStream() ) );
      }
    }

    try {
      if ( log.isDetailed() ) {
        logDetailed( "Setting delete preparedStatement to [" + sql + "]" );
      }
      data.prepStatementDelete = data.db.getConnection().prepareStatement( databaseMeta.stripCR( sql ) );
    } catch ( SQLException ex ) {
      throw new KettleDatabaseException( "Unable to prepare statement for SQL statement [" + sql + "]", ex );
    }
  }

  public boolean init( StepMetaInterface smi, StepDataInterface sdi ) {
    meta = (DeleteMeta) smi;
    data = (DeleteData) sdi;

    if ( super.init( smi, sdi ) ) {
        data.db.setCommitSize( meta.getCommitSize( this ) );
        return true;
    }
    return false;
  }

  @Override
  protected Class<?> getPKG() {
    return PKG;
  }

View on GitHub (pinned to f3058517a1)