pentaho/pentaho-kettle · error · KettleDatabaseException
Couldn't prepare statement :
Error message
Couldn't prepare statement :
What it means
DimensionLookup builds an UPDATE statement for updating the current dimension row (e.g. setting date_to to infinity / updating non-key fields) and prepares it via the JDBC connection. A SQLException from prepareStatement is wrapped in this KettleDatabaseException including the failing SQL. It means the SQL text is not acceptable to the database at prepare time — usually a syntax or schema problem, not data.
Solutions
- Read the wrapped SQLException cause for the real database error and check the SQL printed in the message
- Verify the dimension table, schema and all configured key/lookup/update fields exist in the database
- Check the database connection type and identifier quoting (schema.table prefix) matches your actual database
- Grant the connection user UPDATE privileges on the dimension table
- Re-enter the field mappings in the step so stale columns are removed
Example fix
// before: step references removed column // UPDATE dim_customer SET email = ? WHERE dim_customer_tk = ? -- column 'email' dropped // after: re-add the column or remove it from the step's update fields ALTER TABLE dim_customer ADD COLUMN email VARCHAR(255);
Defensive patterns
Strategy: validation
Validate before calling
// Java: pre-flight the table and columns before preparing the statement
Database db = new Database(transMeta, databaseMeta);
db.connect();
if (!db.checkTableExists(schemaTable)) throw new IllegalStateException("Dimension table missing: " + schemaTable);
for (String f : updateFields) if (!db.getTableFields(schemaTable).indexOfValue(f) < 0) throw new IllegalStateException("Column missing: " + f); Try / catch
try {
dimUpdate(...);
} catch (KettleDatabaseException e) {
if (e.getMessage().startsWith("Couldn't prepare statement")) {
logError("Prepare failed, SQL=" + e.getMessage() + " cause=" + e.getCause());
// fix SQL/privileges/schema, then retry
} else throw e;
} Prevention
- Run the step's check() in Spoon after changing the table schema
- Keep the connection's database type metadata in sync with the actual DB
- Ensure the runtime DB user has SELECT/UPDATE privileges on the dimension table
- Avoid hand-editing SQL-relevant identifiers (case, special characters) in the step
When it happens
Trigger: conn.prepareStatement(sql) throws SQLException while preparing the dimension-update statement built from the step's table, key field, date-from/to fields and update fields.
Common situations: Wrong table name/schema or missing privileges; quoting/identifier case issues between database types; a field listed in the step no longer exists in the table; incompatible characters in identifiers; connecting to a different DB type than the metadata assumes.
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
- An error occurred executing SQL:
- Couldn't execute SQL:
- Couldn't find any rows because of an error :
- Couldn't get a result because of an error :
- Couldn't prepare statement:
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/19a25fe9c386655e.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/steps/dimensionlookup/DimensionLookup.java:1333
}
comma = true;
sql += meta.getDatabaseMeta().quoteField( valueMeta.getName() ) + " = ?" + Const.CR;
data.dimensionUpdateRowMeta.addValueMeta( valueMeta );
}
}
sql += "WHERE " + meta.getDatabaseMeta().quoteField( meta.getKeyField() ) + " = ?";
data.dimensionUpdateRowMeta
.addValueMeta( new ValueMetaInteger( meta.getKeyField() ) ); // The tk
try {
if ( isDebug() ) {
logDebug( "Preparing statement: [" + sql + "]" );
}
data.prepStatementDimensionUpdate =
data.db.getConnection().prepareStatement( meta.getDatabaseMeta().stripCR( sql ) );
} catch ( SQLException ex ) {
throw new KettleDatabaseException( "Couldn't prepare statement :" + Const.CR + sql, ex );
}
}
// Assemble information
// New
Object[] dimensionUpdateRow = new Object[ data.dimensionUpdateRowMeta.size() ];
int updateIndex = 0;
for ( int i = 0; i < data.fieldnrs.length; i++ ) {
// Ignore last_version, last_updated, etc. These are handled below...
//
if ( data.fieldnrs[ i ] >= 0 ) {
dimensionUpdateRow[ updateIndex++ ] = row[ data.fieldnrs[ i ] ];
}
}
for ( int i = 0; i < meta.getFieldUpdate().length; i++ ) {
switch ( meta.getFieldUpdate()[ i ] ) {
case DimensionLookupMeta.TYPE_UPDATE_DATE_INSUP:
case DimensionLookupMeta.TYPE_UPDATE_DATE_UPDATED:View on GitHub (pinned to f3058517a1)