{"record":{"id":"8105a02e779d091c","repo":"Tencent/APIJSON","slug":"method-column-column0-column1-a","errorCode":null,"errorMessage":"字符 ${method} 不合法！预编译模式下 @column:\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\" 中 function 必须符合小写英文单词的 SQL 函数名格式！且必须是后端允许调用的 SQL 函数!","messagePattern":"字符 (.+?) 不合法！预编译模式下 @column:\"column0,column1:alias;function0\\(arg0,arg1,\\.\\.\\.\\);function1\\(\\.\\.\\.\\):alias\\.\\.\\.\" 中 function 必须符合小写英文单词的 SQL 函数名格式！且必须是后端允许调用的 SQL 函数!","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":1792,"sourceCode":"\t\t}\n\n\t\tint end = expression.lastIndexOf(\")\");\n\t\tif (start >= end) {\n\t\t\tthrow new IllegalArgumentException(\"字符 \" + expression + \" 不合法！\"\n\t\t\t\t\t+ \"@having:value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式！\");\n\t\t}\n\n\t\tString method = expression.substring(0, start);\n\t\tif (method.isEmpty() == false) {\n\t\t\tif (SQL_FUNCTION_MAP == null || SQL_FUNCTION_MAP.isEmpty()) {\n\t\t\t\tif (StringUtil.isName(method) == false) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + method + \" 不合法！\"\n\t\t\t\t\t\t\t+ \"预编译模式下 @having:\\\"column?value;function(arg0,arg1,...)?value...\\\"\"\n\t\t\t\t\t\t\t+ \" 中 function 必须符合小写英文单词的 SQL 函数名格式！\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (SQL_FUNCTION_MAP.containsKey(method) == false) {\n\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + method + \" 不合法！\"\n\t\t\t\t\t\t+ \"预编译模式下 @column:\\\"column0,column1:alias;function0(arg0,arg1,...);function1(...):alias...\\\"\"\n\t\t\t\t\t\t+ \" 中 function 必须符合小写英文单词的 SQL 函数名格式！且必须是后端允许调用的 SQL 函数!\");\n\t\t\t}\n\t\t}\n\n\t\treturn method + parseSQLExpression(KEY_HAVING, expression.substring(start), containRaw, false, null);\n\t}\n\n\t@Override\n\tpublic String getSample() {\n\t\treturn sample;\n\t}\n\tpublic AbstractSQLConfig<T, M, L> setSample(String... conditions) {\n\t\treturn setSample(StringUtil.get(conditions));\n\t}\n\t@Override\n\tpublic AbstractSQLConfig<T, M, L> setSample(String sample) {\n\t\tthis.sample = sample;","sourceCodeStart":1774,"sourceCodeEnd":1810,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1774-L1810","documentation":"When SQL_FUNCTION_MAP IS configured, a @having function must not only look like a name — it must be a key in that map. The map is the backend's whitelist of allowed SQL functions; anything else (even valid SQL like ifnull when not whitelisted) is rejected. Note the message text mistakenly references @column but the check is in gainHavingItem.","triggerScenarios":"\"@having\": \"stddev(amount)>1\" where stddev is not a key in the deployed SQL_FUNCTION_MAP; using a database-specific function the admin never enabled.","commonSituations":"Code works on a dev server with a broad SQL_FUNCTION_MAP and fails on production with a narrow one; upgrading APIJSON changes the default map; DB dialect differences (function exists in MySQL but not whitelisted).","solutions":["Switch to a function that is whitelisted in your SQL_FUNCTION_MAP","Backend: add the needed function as a key in SQL_FUNCTION_MAP (and restart/reload)","If the expression cannot be reduced to a whitelisted function, configure it in RAW_MAP and reference via @raw"],"exampleFix":"// backend: allow the function\n// SQL_FUNCTION_MAP.put(\"stddev\", \"stddev\");\n// request stays {\"@having\": \"stddev(amount)>1\"}","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['count','sum','max','min','avg']); // mirror of backend SQL_FUNCTION_MAP\nconst fn = hv.slice(0, hv.indexOf('('));\nif (fn && !ALLOWED.has(fn)) throw new Error(`function ${fn} is not in the backend SQL_FUNCTION_MAP whitelist`);","typeGuard":"const isWhitelistedFunction = (fn, allowed) => allowed.has(fn);","tryCatchPattern":"try { await api.get(req); } catch (e) { if (e.message.includes('必须是后端允许调用的 SQL 函数')) fallbackToWhitelistedAggregation(req); else throw e; }","preventionTips":["Publish the SQL_FUNCTION_MAP contents as part of the API contract","Fail CI when a PR introduces a @having function not in the shared allowlist constant"],"tags":["apijson","having","whitelist","config"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}