{"record":{"id":"e0dd5f61776e63f3","repo":"Tencent/APIJSON","slug":"fill-value-item-value","errorCode":null,"errorMessage":"预编译模式下 @fill:value 中 {item} 不合法! value 里面用 , 分割的每一项必须是 column 且其中 column 必须是 数字或英语字母组合！并且不要有多余的空格！","messagePattern":"预编译模式下 @fill:value 中 (.+?) 不合法! value 里面用 , 分割的每一项必须是 column 且其中 column 必须是 数字或英语字母组合！并且不要有多余的空格！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":2063,"sourceCode":"\t\t\treturn StringUtil.isEmpty(joinFill, true) ? \"\" : (hasPrefix ? \" FILL(\" : \"\") + joinFill + \")\";\n\t\t}\n\n\t\tfor (int i = 0; i < keys.length; i++) {\n\t\t\tString item = keys[i];\n\t\t\tif (\"NULL\".equals(item) || \"LINEAR\".equals(item) || \"PREV\".equals(item) || \"PREVIOUS\".equals(item)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tString origin = item;\n\n\t\t\tif (isPrepared()) { //不能通过 ? 来代替，SELECT 'id','name' 返回的就是 id:\"id\", name:\"name\"，而不是数据库里的值！\n\t\t\t\t//这里既不对origin trim，也不对 ASC/DESC ignoreCase，希望前端严格传没有任何空格的字符串过来，减少传输数据量，节约服务器性能\n\t\t\t\tif (StringUtil.isName(origin)) {}\n\t\t\t\telse if (StringUtil.isCombineOfNumOrAlpha(origin)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalArgumentException(\"预编译模式下 @fill:value 中 \" + item + \" 不合法! value 里面用 , 分割的\"\n\t\t\t\t\t\t\t+ \"每一项必须是 column 且其中 column 必须是 数字或英语字母组合！并且不要有多余的空格！\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tkeys[i] = gainKey(origin);\n\t\t}\n\n\t\treturn (hasPrefix ? \" FILL(\" : \"\") + StringUtil.concat(StringUtil.get(keys), joinFill, \", \") + \")\";\n\t}\n\n\t@Override\n\tpublic String getOrder() {\n\t\treturn order;\n\t}\n\tpublic AbstractSQLConfig<T, M, L> setOrder(String... conditions) {\n\t\treturn setOrder(StringUtil.get(conditions));\n\t}\n\t@Override","sourceCodeStart":2045,"sourceCodeEnd":2081,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2045-L2081","documentation":"Prepared-mode validation of @fill:value: every comma-separated item must be StringUtil.isName or StringUtil.isCombineOfNumOrAlpha (letters and digits only). FILL clause items are inlined, so symbols/spaces are rejected.","triggerScenarios":"\"@fill\": \"id, name\" (space after comma), \"@fill\": \"prev()\" — an item that is neither a name nor a digit/letter combination.","commonSituations":"Readable lists built with ', ' separators; ClickHouse FILL modifiers expressed as expressions rather than bare tokens.","solutions":["Join items with ',' and no spaces: \"@fill\": \"id,name\"","Keep each item a bare column or alphanumeric token","Move any expression into RAW_MAP + @raw"],"exampleFix":"// before\n{\"@fill\": \"id, name\"}\n// after\n{\"@fill\": \"id,name\"}","handlingStrategy":"validation","validationCode":"const ok = s => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s) || /^[A-Za-z0-9]+$/.test(s);\nfor (const item of String(obj['@fill'] ?? '').split(',')) {\n  if (item && !ok(item)) throw new Error(`@fill item '${item}' must be a name or digit/letter combo`);\n}","typeGuard":"const isFillItemValid = s => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s) || /^[A-Za-z0-9]+$/.test(s);","tryCatchPattern":"try { await api.get(req); } catch (e) { if (e.message.includes('@fill')) req['@fill'] = req['@fill'].split(',').map(x => x.trim()).join(','); else throw e; }","preventionTips":["Join FILL lists without spaces","Use RAW_MAP tokens for FILL expressions"],"tags":["apijson","fill","prepared-statement","clickhouse"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}