{"record":{"id":"8c260946e1aec9a0","repo":"NationalSecurityAgency/ghidra","slug":"unexpected-updated-row-count","errorCode":null,"errorMessage":"Unexpected updated row count: {}","messagePattern":"Unexpected updated row count: (.+?)","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"critical","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/H2VectorTable.java","lineNumber":220,"sourceCode":"\tpublic long updateVector(LSHVector vec, int countDiff) throws SQLException {\n\n\t\tif (countDiff <= 0) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid countDiff: \" + countDiff);\n\t\t}\n\n\t\t// TODO: it may be possible to optimize the technique employed here\n\n\t\tPreparedStatement s = update_by_hash_stmt.prepareIfNeeded(() -> db.prepareStatement(\n\t\t\t\"UPDATE \" + TABLE_NAME + \" SET count = count + ? WHERE vec_hash = ?\"));\n\t\tlong vecHash = vec.calcUniqueHash();\n\t\ts.setInt(1, countDiff);\n\t\ts.setLong(2, vecHash);\n\t\tint rc = s.executeUpdate();\n\t\tif (rc == 0) {\n\t\t\treturn insert(countDiff, vec);\n\t\t}\n\t\tif (rc > 1) {\n\t\t\tthrow new SQLException(\"Unexpected updated row count: \" + rc);\n\t\t}\n\n\t\ts = select_id_by_hash_stmt.prepareIfNeeded(() -> db\n\t\t\t\t.prepareStatement(\"SELECT id, count FROM \" + TABLE_NAME + \" WHERE vec_hash = ?\"));\n\t\ts.setLong(1, vecHash);\n\n\t\tlong id;\n\t\tint count;\n\t\ttry (ResultSet rs = s.executeQuery()) {\n\t\t\tif (!rs.next()) {\n\t\t\t\tthrow new SQLException(\"Unknown vector hash\");\n\t\t\t}\n\t\t\tid = rs.getLong(1);\n\t\t\tcount = rs.getInt(2);\n\t\t}\n\t\tvectorStore.update(\n\t\t\tnew VectorStoreEntry(id, vec, count, vectorFactory.getSelfSignificance(vec)));\n\t\treturn id;","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/H2VectorTable.java#L202-L238","documentation":"Thrown as an SQLException by H2VectorTable.updateVector() when an UPDATE on vec_hash affects more than one row. The h2_vectable has a unique index on vec_hash, so an UPDATE matching by vec_hash should affect at most one row. A row count greater than 1 indicates the unique constraint on vec_hash was violated (duplicate hash values exist), which is a data integrity violation.","triggerScenarios":"Occurs in updateVector() when 'UPDATE h2_vectable SET count = count + ? WHERE vec_hash = ?' returns rc > 1. This means multiple rows share the same vec_hash value despite the unique index h2_vectable_index. This can happen if: the unique index was dropped or corrupted, two different LSHVectors produce a hash collision in calcUniqueHash(), or a prior insert bypassed the unique constraint.","commonSituations":"Hash collision in calcUniqueHash() where two genuinely different vectors produce the same hash (extremely rare but possible). The unique index was manually dropped. Database corruption caused duplicate entries. A bug in a prior version allowed duplicate vec_hash values to be inserted.","solutions":["Query the table for duplicate vec_hash values: 'SELECT vec_hash, COUNT(*) FROM h2_vectable GROUP BY vec_hash HAVING COUNT(*) > 1' to identify and resolve duplicates.","If duplicates exist, remove or merge them so each vec_hash is unique.","Verify the unique index h2_vectable_index exists on the table: 'SELECT * FROM information_schema.indexes WHERE index_name = h2_vectable_index'.","If the index was dropped, recreate it.","If hash collisions are the root cause, investigate calcUniqueHash() for collisions and consider rebuilding the database."],"exampleFix":"// before\nint rc = s.executeUpdate();\nif (rc > 1) {\n    throw new SQLException(\"Unexpected updated row count: \" + rc);\n}\n\n// after (detect and report duplicates before failing)\nint rc = s.executeUpdate();\nif (rc > 1) {\n    throw new SQLException(String.format(\n        \"Data integrity violation: %d rows share vec_hash %d; \" +\n        \"run: SELECT id FROM h2_vectable WHERE vec_hash = %d\", rc, vecHash, vecHash));\n}","handlingStrategy":"validation","validationCode":"// Check for duplicate vec_hash values before update operations\ntry (Statement st = db.createStatement();\n        ResultSet rs = st.executeQuery(\n            \"SELECT vec_hash, COUNT(*) as cnt FROM \" + TABLE_NAME +\n            \" GROUP BY vec_hash HAVING COUNT(*) > 1\")) {\n    if (rs.next()) {\n        throw new SQLException(String.format(\n            \"Duplicate vec_hash detected: %d appears %d times\",\n            rs.getLong(1), rs.getInt(2)));\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return vectorTable.updateVector(vec, countDiff);\n} catch (SQLException e) {\n    if (e.getMessage().startsWith(\"Unexpected updated row count:\")) {\n        // Data integrity violation: resolve duplicates first\n        long hash = vec.calcUniqueHash();\n        resolveDuplicateVecHashes(db, hash);\n        return vectorTable.updateVector(vec, countDiff); // retry\n    }\n    throw e;\n}","preventionTips":["Periodically check for duplicate vec_hash values in production databases.","Verify the unique index h2_vectable_index exists after any schema operation.","Do not insert vectors bypassing the normal insert/updateVector path.","If hash collisions occur, investigate calcUniqueHash() and consider rebuilding the database.","Treat this as a critical data integrity issue requiring immediate investigation."],"tags":["bsim","h2","vector","data-integrity","unique-constraint","hash-collision"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}