{"record":{"id":"1ae8053f93d842b5","repo":"NationalSecurityAgency/ghidra","slug":"unable-to-obtain-vector-id-for-insert","errorCode":null,"errorMessage":"Unable to obtain vector id for insert","messagePattern":"Unable to obtain vector id for insert","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/H2VectorTable.java","lineNumber":106,"sourceCode":"\t\tLSHVector vec = (LSHVector) arguments[1];\n\n\t\tPreparedStatement s = insert_stmt.prepareIfNeeded(() -> db.prepareStatement(\n\t\t\t\"INSERT INTO \" + TABLE_NAME + \" (count,vec_hash,vec) VALUES(?,?,?)\",\n\t\t\tStatement.RETURN_GENERATED_KEYS));\n\n\t\tStringBuilder vecBuf = new StringBuilder();\n\t\tvec.saveBase64(vecBuf, Base64Lite.encode);\n\n\t\ts.setInt(1, count);\n\t\ts.setLong(2, vec.calcUniqueHash());\n\t\ts.setString(3, vecBuf.toString());\n\t\tif (s.executeUpdate() != 1) {\n\t\t\tthrow new SQLException(\"Insert failed for vector table\");\n\t\t}\n\t\tlong id;\n\t\ttry (ResultSet rs = s.getGeneratedKeys()) {\n\t\t\tif (!rs.next()) {\n\t\t\t\tthrow new SQLException(\"Unable to obtain vector id for insert\");\n\t\t\t}\n\t\t\tid = rs.getLong(1);\n\t\t}\n\t\tvectorStore.update(\n\t\t\tnew VectorStoreEntry(id, vec, count, vectorFactory.getSelfSignificance(vec)));\n\t\treturn id;\n\t}\n\n\t/**\n\t * Read all vectors from table and generate an ID-based vector map\n\t * @return vector map (ID->VectorStoreEntry)\n\t * @throws SQLException if error occurs\n\t */\n\tpublic Map<Long, VectorStoreEntry> readVectors() throws SQLException {\n\t\tchar[] vectorDecodeBuffer = Base64VectorFactory.allocateBuffer();\n\t\tHashMap<Long, VectorStoreEntry> map = new HashMap<>();\n\t\ttry (Statement st = db.createStatement();\n\t\t\t\tResultSet rs = st.executeQuery(\"SELECT id,count,vec FROM \" + TABLE_NAME)) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/H2VectorTable.java#L88-L124","documentation":"Thrown as an SQLException by H2VectorTable.insert() when getGeneratedKeys() returns an empty ResultSet after a successful INSERT. The insert reported 1 affected row, but the H2 driver did not provide a generated key, leaving the new vector's auto-generated ID unavailable.","triggerScenarios":"Occurs when the INSERT succeeded (returned 1) but Statement.getGeneratedKeys() yields no rows. This happens when: the PreparedStatement was not created with RETURN_GENERATED_KEYS properly, the H2 driver version doesn't support generated keys for the SERIAL/CLOB combination, or there is a driver-level bug in key retrieval.","commonSituations":"H2 JDBC driver version incompatibility with Ghidra's BSim module. The SERIAL PRIMARY KEY column doesn't properly report generated keys in certain H2 modes. H2 running in a compatibility mode (e.g., PostgreSQL emulation) that alters generated key behavior.","solutions":["Verify the H2 JDBC driver version matches what Ghidra bundles/expects.","Check that the PreparedStatement was created with Statement.RETURN_GENERATED_KEYS (it is in the current code).","If the issue persists, query the last inserted ID via an alternative method (e.g., IDENTITY() or SELECT MAX(id)).","Rebuild the database if the schema or driver was changed mid-stream."],"exampleFix":"// before\ntry (ResultSet rs = s.getGeneratedKeys()) {\n    if (!rs.next()) {\n        throw new SQLException(\"Unable to obtain vector id for insert\");\n    }\n    id = rs.getLong(1);\n}\n\n// after (fallback: query by vec_hash to get the assigned id)\ntry (ResultSet rs = s.getGeneratedKeys()) {\n    if (rs.next()) {\n        id = rs.getLong(1);\n    } else {\n        try (PreparedStatement q = db.prepareStatement(\n                \"SELECT id FROM \" + TABLE_NAME + \" WHERE vec_hash = ?\")) {\n            q.setLong(1, vec.calcUniqueHash());\n            try (ResultSet qr = q.executeQuery()) {\n                qr.next();\n                id = qr.getLong(1);\n            }\n        }\n    }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"long id;\ntry (ResultSet rs = s.getGeneratedKeys()) {\n    if (rs.next()) {\n        id = rs.getLong(1);\n    } else {\n        // Fallback: retrieve ID via the unique vec_hash\n        try (PreparedStatement q = db.prepareStatement(\n                \"SELECT id FROM \" + TABLE_NAME + \" WHERE vec_hash = ?\")) {\n            q.setLong(1, vec.calcUniqueHash());\n            try (ResultSet qr = q.executeQuery()) {\n                if (!qr.next()) throw new SQLException(\"Vector insert succeeded but ID unrecoverable\");\n                id = qr.getLong(1);\n            }\n        }\n    }\n}","preventionTips":["Ensure the H2 JDBC driver version supports RETURN_GENERATED_KEYS for SERIAL columns.","Do not downgrade the H2 driver after a database is in use.","Monitor for this error after Ghidra or driver updates.","Consider a vec_hash-based ID lookup fallback if generated keys are unreliable."],"tags":["bsim","h2","vector","insert","generated-keys","driver"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}