{"record":{"id":"8ba3c63f78db5fc5","repo":"pentaho/pentaho-kettle","slug":"couldn-t-close-query-resultset-or-prepared-statements","errorCode":null,"errorMessage":"Couldn't close query: resultset or prepared statements","messagePattern":"Couldn't close query: resultset or prepared statements","errorType":"exception","errorClass":"KettleDatabaseException","httpStatus":null,"severity":"warning","filePath":"core/src/main/java/org/pentaho/di/core/database/Database.java","lineNumber":2862,"sourceCode":"    return fields;\n  }\n\n  public void closeQuery( ResultSet res ) throws KettleDatabaseException {\n    // close everything involved in the query!\n    try {\n      if ( res != null ) {\n        res.close();\n      }\n      if ( selStmt != null ) {\n        selStmt.close();\n        selStmt = null;\n      }\n      if ( pstmt != null ) {\n        pstmt.close();\n        pstmt = null;\n      }\n    } catch ( SQLException ex ) {\n      throw new KettleDatabaseException( \"Couldn't close query: resultset or prepared statements\", ex );\n    }\n  }\n\n  /**\n   * Build the row using ResultSetMetaData rsmd\n   *\n   * @param rm             The resultset metadata to inquire\n   * @param ignoreLength   true if you want to ignore the length (workaround for MySQL bug/problem)\n   * @param lazyConversion true if lazy conversion needs to be enabled where possible\n   */\n  private RowMetaInterface getRowInfo( ResultSetMetaData rm, boolean ignoreLength, boolean lazyConversion )\n    throws KettleDatabaseException {\n    try {\n      log.snap( Metrics.METRIC_DATABASE_GET_ROW_META_START, databaseMeta.getName() );\n\n      if ( rm == null ) {\n        throw new KettleDatabaseException( \"No result set metadata available to retrieve row metadata!\" );\n      }","sourceCodeStart":2844,"sourceCodeEnd":2880,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/database/Database.java#L2844-L2880","documentation":"closeQuery() closes the ResultSet and PreparedStatement of an open query; if closing either throws SQLException, Kettle wraps it in this KettleDatabaseException. The original query result is lost and the statement resources may be left to GC.","triggerScenarios":"SQLException from rs.close() or pstmt.close() — typically because the connection was already closed/aborted before closeQuery was called.","commonSituations":"Closing the Database/connection before calling closeQuery(), network drop mid-session, or double-close scenarios in pooling drivers.","solutions":["Call closeQuery() before closing the Database connection","Inspect the wrapped cause for the underlying SQLException","Treat as cleanup-stage: log and continue if the query results were already consumed","Avoid double-closing by nulling references after close (as the source does)"],"exampleFix":"// before\ndatabase.disconnect();\ndatabase.closeQuery(res); // connection already closed\n// after\ndatabase.closeQuery(res);\ndatabase.disconnect();","handlingStrategy":"try-catch","validationCode":"// ensure resources still open before closing\nif (res == null || res.isClosed()) return;","typeGuard":null,"tryCatchPattern":"try {\n  db.closeQuery(res);\n} catch (KettleDatabaseException e) {\n  log.warn(\"query cleanup failed (results already consumed): \" + e.getCause());\n}","preventionTips":["Always closeQuery() before disconnect()","Null out ResultSet/statement references after closing","Order cleanup: ResultSet, then PreparedStatement, then connection"],"tags":["resource-cleanup","resultset","jdbc"],"backgroundTag":"database-query-failed","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}