{"record":{"id":"fe36790c03014c45","repo":"Tencent/APIJSON","slug":"expression-key-value-value-sql-fe3679","errorCode":null,"errorMessage":"字符 {expression} 不合法！{key}:value 中 value 里的 SQL 函数必须为 function(arg0,arg1,...) 这种格式！","messagePattern":"字符 (.+?) 不合法！(.+?):value 中 value 里的 SQL 函数必须为 function\\(arg0,arg1,\\.\\.\\.\\) 这种格式！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":2613,"sourceCode":"\t\t\t\t\tthrow new UnsupportedOperationException(\"字符串 \" + suffix + \" 不合法！预编译模式下 \" + key\n\t\t\t\t\t\t\t+ \":\\\"column?value;function(arg0,arg1,...)?value...\\\"\"\n\t\t\t\t\t\t\t+ \" 中 ?value 必须符合正则表达式 \" + PATTERN_RANGE + \" 且不包含连续减号 -- 或注释符 /* ！不允许多余的空格！\");\n\t\t\t\t}\n\n\t\t\t\tString origin = fun + \"(\" + (distinct ? PREFIX_DISTINCT : \"\") + StringUtil.get(ckeys) + \")\" + suffix;\n\t\t\t\texpression = origin + (StringUtil.isEmpty(alias, true) ? \"\" : gainAs() + quote + alias + quote);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t//是窗口函数   fun(arg0,agr1) OVER (agr0 agr1 ...)\n\t\t\t\tint keyIndex = containOver ? overIndex : againstIndex;\n\t\t\t\tString s1 = expression.substring(0, keyIndex + 1); // OVER 前半部分\n\t\t\t\tString s2 = expression.substring(keyIndex + 1); // OVER 后半部分\n\n\t\t\t\tint index1 = s1.indexOf(\"(\"); //  函数 \"(\" 的起始位置\n\t\t\t\tint end = s2.lastIndexOf(\")\"); // 后半部分 “)” 的位置\n\n\t\t\t\tif (index1 >= end + s1.length()) {\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\n\t\t\t\tString fun = s1.substring(0, index1); // 函数名称\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}\n\t\t\t\t\telse if (SQL_FUNCTION_MAP.containsKey(fun) == false) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + fun + \" 不合法！预编译模式下 \" + example\n\t\t\t\t\t\t\t\t+ \" 中 function 必须符合小写英文单词的 SQL 函数名格式！且必须是后端允许调用的 SQL 函数!\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// 获取前半部分函数的参数解析   fun(arg0,agr1)","sourceCodeStart":2595,"sourceCodeEnd":2631,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2595-L2631","documentation":"In the window/full-text branch of parseSQLExpression, the expression is split at ')OVER(' or ')AGAINST(' into s1 and s2; if the first '(' in s1 is positioned at or after the last ')' of s2 (adjusted by s1.length()), the expression is not in the supported fun(args) OVER (args) shape and this IllegalArgumentException is thrown.","triggerScenarios":"Malformed window expressions such as \"@column\":\"rank() OVER (PARTITION BY id\" (missing final ')'), \"@column\":\"OVER (x)rank()\", or extra characters that shift the parenthesis indices so index1 >= end + s1.length().","commonSituations":"Hand-writing window functions without the exact no-space ')OVER(' marker; truncation of long expressions; refactoring expressions and unbalancing parens.","solutions":["Use the exact format fun(args)OVER(args2) with no spaces around OVER and balanced parentheses, e.g. \"row_number()OVER(PARTITION BY id ORDER BY id ASC)\".","Validate parenthesis balance client-side before sending.","Register complex window expressions as @raw on the server."],"exampleFix":"// before\n{\"Comment\":{\"@column\":\"rank()OVER(PARTITION BY userId\"}}\n// after\n{\"Comment\":{\"@column\":\"rank()OVER(PARTITION BY userId ORDER BY id ASC)\"}}","handlingStrategy":"validation","validationCode":"function balanced(s){let d=0;for(const c of s){if(c==='(')d++;if(c===')')d--;if(d<0)return false}return d===0}\nconst m=expr.match(/^([A-Za-z][A-Za-z0-9_]*\\([^()]*\\))(OVER|AGAINST)\\((.*)\\)$/);\nif(!m||!balanced(expr))throw new Error('expect fn(args)OVER(args)');","typeGuard":"null","tryCatchPattern":"catch IllegalArgumentException; fix parens/spaces and re-send","preventionTips":["Use the exact ')OVER(' marker without spaces","Template window functions, never hand-edit","Validate balance before send"],"tags":["apijson","sql","input-validation","window-function","syntax"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}