{"record":{"id":"16ac94a0e9c6c7fa","repo":"NationalSecurityAgency/ghidra","slug":"optional-table-column-type-mismatch","errorCode":null,"errorMessage":"Optional table: column type mismatch","messagePattern":"Optional table: column type mismatch","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/AbstractSQLFunctionDatabase.java","lineNumber":772,"sourceCode":"\t * Establish an optional key/value table with this connection.\n\t * The OptionalTable object is created and added to the list for this connection.\n\t * If the caller desires, the table is tested for existence. If it doesn't\n\t * exist, the object is not added to the list and null is returned.\n\t * @param tableName is the name of the SQL table\n\t * @param keyType is the type-code of the key column\n\t * @param valueType is the type-code of the value column\n\t * @param testExistence if true, we test the existence of the table\n\t * @return the OptionalTable or null\n\t * @throws SQLException for problems with the connection, or if\n\t *     the table exists with different column types\n\t */\n\tprivate OptionalTable getOptionalTable(String tableName, int keyType, int valueType,\n\t\t\tboolean testExistence) throws SQLException {\n\t\tif (optionaltables != null) {\t\t// Search for existing table\n\t\t\tfor (OptionalTable table : optionaltables) {\n\t\t\t\tif (table.getName().equals(tableName)) {\n\t\t\t\t\tif (keyType != table.getKeyType() || valueType != table.getValueType()) {\n\t\t\t\t\t\tthrow new SQLException(\"Optional table: column type mismatch\");\n\t\t\t\t\t}\n\t\t\t\t\treturn table;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// If we reach here, table object doesn't exist, so we create it\n\t\tOptionalTable table = new OptionalTable(tableName, keyType, valueType, db);\n\t\tif (testExistence) {\t\t\t// If the user requested\n\t\t\tif (!table.exists()) {\t\t//    test for the existence of the table\n\t\t\t\ttable.close();\n\t\t\t\treturn null;\t\t\t// If it doesn't exist, don't save new table object, return null\n\t\t\t}\n\t\t}\n\t\t// Insert the new table object at the end of the list\n\t\tOptionalTable[] newArray;\n\t\tif (optionaltables != null) {\n\t\t\tnewArray = Arrays.copyOf(optionaltables, optionaltables.length + 1);\n\t\t\tnewArray[optionaltables.length] = table;","sourceCodeStart":754,"sourceCodeEnd":790,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/AbstractSQLFunctionDatabase.java#L754-L790","documentation":"BSim supports optional metadata tables (extra key/value tables). `getOptionalTable` searches existing optional tables by name; if one is found whose key or value column types differ from the requested `keyType`/`valueType`, it throws SQLException. This refuses to silently read data under a mismatched schema.","triggerScenarios":"A database created under one BSim version defined an optional table with certain column types; a later access requests the same table name with different types -- e.g. after an incomplete schema migration or with mixed client/server versions.","commonSituations":"Version skew between BSim client and server; a manual ALTER TABLE on an optional table; partial/aborted migration; two deployments sharing one DB with divergent schemas.","solutions":["Drop the offending optional table and let BSim recreate it with the correct types.","Align client and server BSim versions so both expect the same schema.","Re-run the official BSim migration tooling to normalize the schema.","If data must be preserved, export, drop, recreate, and re-import."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before requesting an optional table, verify its column types match expectations.\nString sql = \"SELECT column_name, data_type FROM information_schema.columns \" +\n    \"WHERE table_name = ? ORDER BY ordinal_position\";\ntry (Connection c = ds.getConnection();\n     PreparedStatement ps = c.prepareStatement(sql)) {\n    ps.setString(1, tableName);\n    ResultSet rs = ps.executeQuery();\n    // compare returned types against the keyType/valueType you intend to request\n}","typeGuard":null,"tryCatchPattern":"try {\n    OptionalTable t = db.getOptionalTable(name, keyType, valueType, true);\n} catch (SQLException e) {\n    if (e.getMessage().equals(\"Optional table: column type mismatch\")) {\n        // schema incompatibility -- align versions or recreate the table\n        throw new SchemaIncompatibleException(name, e);\n    }\n    throw e;\n}","preventionTips":["Keep BSim client and server versions in lockstep.","Run official migrations before accessing optional tables.","Never ALTER optional tables manually; let BSim own their schema.","Treat a type mismatch as a version-skew smell and audit the deployment."],"tags":["database","schema","migration","bsim","version-skew"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}