{"record":{"id":"d84e0c3c6e9be79f","repo":"Tencent/APIJSON","slug":"expression-example-function","errorCode":null,"errorMessage":"字符 {expression} 不合法！预编译模式下 {example} 中 function 必须符合小写英文单词的 SQL 函数名格式！不能同时存在窗口函数关键词 OVER 和全文索引关键词 AGAINST！","messagePattern":"字符 (.+?) 不合法！预编译模式下 (.+?) 中 function 必须符合小写英文单词的 SQL 函数名格式！不能同时存在窗口函数关键词 OVER 和全文索引关键词 AGAINST！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":2548,"sourceCode":"\t\t\t\tif (KEY_COLUMN.equals(key)) {\n\t\t\t\t\texample = key + \":\\\"column0,column1:alias1;function0(arg0,arg1,...);function1(...):alias2...\\\"\";\n\t\t\t\t}\n\t\t\t\t//\t和 key{}:\"\" 一样\t\telse if (KEY_HAVING.equals(key) || KEY_HAVING_AND.equals(key)) {\n\t\t\t\t//\t\t\t\t\texeptionExample = key + \":\\\"function0(arg0,arg1,...)>1;function1(...)%5<=3...\\\"\";\n\t\t\t\t//\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\texample = key + \":\\\"column0!=0;column1+3*2<=10;function0(arg0,arg1,...)>1;function1(...)%5<=3...\\\"\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//有函数,但不是窗口函数\n\t\t\tint overIndex = expression.indexOf(\")OVER(\");  // 传参不传空格，拼接带空格  \") OVER (\");\n\t\t\tint againstIndex = expression.indexOf(\")AGAINST(\");  // 传参不传空格，拼接带空格  \") AGAINST (\");\n\t\t\tboolean containOver = overIndex > 0 && overIndex < expression.length() - \")OVER(\".length();\n\t\t\tboolean containAgainst = againstIndex > 0 && againstIndex < expression.length() - \")AGAINST(\".length();\n\n\t\t\tif (containOver && containAgainst) {\n\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + expression + \" 不合法！预编译模式下 \" + example\n\t\t\t\t\t\t+ \" 中 function 必须符合小写英文单词的 SQL 函数名格式！不能同时存在窗口函数关键词 OVER 和全文索引关键词 AGAINST！\");\n\t\t\t}\n\n\t\t\tif (containOver == false && containAgainst == false) {\n\t\t\t\tint end = expression.lastIndexOf(')');\n\t\t\t\tif (start >= end) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + expression + \" 不合法！\"\n\t\t\t\t\t\t\t+ key + \":value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式！\");\n\t\t\t\t}\n\t\t\t\tString fun = expression.substring(0, start);\n\t\t\t\tif (fun.isEmpty() == false) {\n\t\t\t\t\tif (SQL_FUNCTION_MAP == null || SQL_FUNCTION_MAP.isEmpty()) {\n\t\t\t\t\t\tif (StringUtil.isName(fun) == false) {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + fun + \" 不合法！预编译模式下 \" + example\n\t\t\t\t\t\t\t\t\t+ \" 中 function 必须符合小写英文单词的 SQL 函数名格式！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (SQL_FUNCTION_MAP.containsKey(fun) == false) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + fun + \" 不合法！预编译模式下 \" + example","sourceCodeStart":2530,"sourceCodeEnd":2566,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2530-L2566","documentation":"parseSQLExpression detects both the window-function marker \")OVER(\" and the full-text marker \")AGAINST(\" in one @column expression, which is not a supported shape — the parser can handle one of the two, never both, and refuses rather than mis-parse. It is an anti-injection/robustness guard on function expressions.","triggerScenarios":"An @column item literally containing both ')OVER(' and ')AGAINST(', e.g. an attempt to nest a full-text match inside a window function: \"match(name) AGAINST ('a') OVER (PARTITION BY id)\" written without spaces, or fuzzed/adversarial input probing the parser.","commonSituations":"Attempting to combine MATCH...AGAINST with window framing in one expression (not valid in this DSL); copy-pasting SQL containing both keywords without spaces; penetration-test payloads.","solutions":["Split into two ';'-separated expressions so each contains only one of OVER or AGAINST.","Write the complex expression as server-side @raw and reference its alias.","Verify the target SQL dialect even supports the combination before trying to express it here."],"exampleFix":"// before\n{\"Comment\":{\"@column\":\"match(content)AGAINST('api')OVER(PARTITION BY id)\"}}\n// after (two items)\n{\"Comment\":{\"@column\":\"match(content)AGAINST('api');row_number()OVER(PARTITION BY id)\"}}","handlingStrategy":"validation","validationCode":"if (expr.includes(')OVER(') && expr.includes(')AGAINST(')) throw new Error('OVER and AGAINST cannot coexist in one expression');","typeGuard":"null","tryCatchPattern":"catch IllegalArgumentException; split the expression and retry as two items","preventionTips":["One OVER or AGAINST per ';'-item","Keep full-text and window logic separate"],"tags":["apijson","sql","input-validation","window-function","fulltext"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}