{"record":{"id":"bfe96b45cfc68dbb","repo":"OtterMind/Chat2DB","slug":"invalid-db2-index-column-ordering-ascordesc","errorCode":null,"errorMessage":"Invalid DB2 index column ordering: {ascOrDesc}","messagePattern":"Invalid DB2 index column ordering: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-plugins/chat2db-community-db2/src/main/java/ai/chat2db/plugin/db2/Db2SqlGuards.java","lineNumber":102,"sourceCode":"     * Validates a fallback column type expression (a type name that does not match\n     * an enum constant, e.g. {@code VARCHAR(10)}) before it is embedded into\n     * generated DDL. Returns the type unchanged when it matches the strict shape;\n     * throws otherwise.\n     */\n    public static String requireColumnTypeExpression(String columnType) {\n        if (columnType == null || !FALLBACK_COLUMN_TYPE_PATTERN.matcher(columnType).matches()) {\n            throw new IllegalArgumentException(\"Invalid DB2 column type: \" + columnType);\n        }\n        return columnType;\n    }\n\n    /**\n     * Validates an index column sort direction against the ASC/DESC whitelist.\n     * Returns the direction unchanged when whitelisted; throws otherwise.\n     */\n    public static String requireSortDirection(String ascOrDesc) {\n        if (!\"ASC\".equalsIgnoreCase(ascOrDesc) && !\"DESC\".equalsIgnoreCase(ascOrDesc)) {\n            throw new IllegalArgumentException(\"Invalid DB2 index column ordering: \" + ascOrDesc);\n        }\n        return ascOrDesc;\n    }\n}\n","sourceCodeStart":84,"sourceCodeEnd":107,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-plugins/chat2db-community-db2/src/main/java/ai/chat2db/plugin/db2/Db2SqlGuards.java#L84-L107","documentation":"Thrown by Db2SqlGuards.requireSortDirection when an index column's sort direction is not ASC or DESC (case-insensitive). The method whitelists only the two legal DB2 index ordering tokens and rejects anything else so a non-escapable value cannot be interpolated into generated CREATE INDEX DDL. This is a fail-closed SQL guard, not a runtime constraint from the DB2 server.","triggerScenarios":"Calling Db2SqlGuards.requireSortDirection(ascOrDesc) with a value that is neither \"ASC\" nor \"DESC\" (e.g. null, \"\", \"ASCENDING\", \"NULLS FIRST\", or a metadata-derived corrupted string). Indirectly triggered when DB2 index DDL is built for a TableIndex whose column's getAscOrDesc() returns an unexpected token.","commonSituations":"Importing DB2 index metadata where the SYSCAT.INDEXCOLUSE sort direction column is blank or null; copying an index model from another dialect that used a non-standard ordering; a tool populating TableIndexColumn.ascOrDesc with the raw DB2 'A'/'D' code instead of the spelled-out direction.","solutions":["Normalize ascOrDesc to \"ASC\"/\"DESC\" (or blank) before calling the builder so the guard only sees whitelisted tokens.","If you control the TableIndexColumn, leave ascOrDesc blank/null instead of passing an invalid sentinel like 'A'.","When reading raw DB2 catalog codes, map 'A'->\"ASC\", 'D'->\"DESC\" at the metadata layer rather than forwarding them.","Wrap the DDL build in try/catch on IllegalArgumentException and report the offending index/column to the user instead of failing the whole build."],"exampleFix":"// before\nString dir = indexColumn.getAscOrDesc(); // raw DB2 code 'A' / 'D'\nscript.append(\" \").append(Db2SqlGuards.requireSortDirection(dir));\n\n// after\nString dir = indexColumn.getAscOrDesc();\nif (\"A\".equalsIgnoreCase(dir)) dir = \"ASC\";\nelse if (\"D\".equalsIgnoreCase(dir)) dir = \"DESC\";\nif (StringUtils.isNotBlank(dir)) {\n    script.append(\" \").append(Db2SqlGuards.requireSortDirection(dir));\n}","handlingStrategy":"validation","validationCode":"// Run before building DB2 index DDL\nstatic String normalizeDir(String ascOrDesc) {\n    if (ascOrDesc == null) return null;\n    String t = ascOrDesc.trim();\n    if (\"A\".equalsIgnoreCase(t)) return \"ASC\";\n    if (\"D\".equalsIgnoreCase(t)) return \"DESC\";\n    if (\"ASC\".equalsIgnoreCase(t) || \"DESC\".equalsIgnoreCase(t)) return t.toUpperCase(Locale.ROOT);\n    return null; // blank -> skip\n}","typeGuard":"// Narrow to a known direction before calling the guard\nstatic boolean isDb2SortDir(String v) {\n    return v != null && (\"ASC\".equalsIgnoreCase(v) || \"DESC\".equalsIgnoreCase(v)\n        || \"A\".equalsIgnoreCase(v) || \"D\".equalsIgnoreCase(v));\n}","tryCatchPattern":null,"preventionTips":["Map raw DB2 catalog 'A'/'D' codes to ASC/DESC at the metadata import layer.","Leave ascOrDesc blank when no ordering is needed instead of passing a sentinel.","Constrain the index-column direction UI to an ASC/DESC/(none) selector."],"tags":["db2","sql-guard","ddl","index","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}