{"record":{"id":"1b97baab7b3eb12c","repo":"hs-web/hsweb-framework","slug":"table-function-tablefunction-must-have-alias","errorCode":null,"errorMessage":"table function[\" + tableFunction + \"] must have alias","messagePattern":"table function\\[\" \\+ tableFunction \\+ \"\\] must have alias","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/query/QueryAnalyzerImpl.java","lineNumber":388,"sourceCode":"            for (Alias.AliasColumn alias : valuesList.getAlias().getAliasColumns()) {\n                RDBColumnMetadata ignore = view.getColumn(parsePlainName(alias.name)).orElse(null);\n            }\n        }\n\n        view.setName(name);\n        view.setRealName(name);\n        view.setSchema(database.getMetadata().getCurrentSchema());\n        view.setAlias(name);\n\n        Table table = new Table(name, view);\n\n        select = new QueryAnalyzer.Select(new ArrayList<>(), table);\n    }\n\n    @Override\n    public void visit(TableFunction tableFunction) {\n        if (tableFunction.getAlias() == null) {\n            throw new IllegalArgumentException(\"table function[\" + tableFunction + \"] must have alias\");\n        }\n        String name = parsePlainName(tableFunction.getAlias().getName());\n\n        FakeTable view = new FakeTable();\n\n        view.setName(name);\n        view.setSchema(database.getMetadata().getCurrentSchema());\n        view.setAlias(name);\n\n        Table table = new Table(name, view);\n\n        select = new QueryAnalyzer.Select(new ArrayList<>(), table);\n\n    }\n\n    @Override\n    public void visit(ParenthesisFromItem aThis) {\n        aThis.getFromItem().accept(this);","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/hs-web/hsweb-framework/blob/b2cfc85a57c70bf5b5cf6e7edae2d37102652ec8/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/query/QueryAnalyzerImpl.java#L370-L406","documentation":"QueryAnalyzerImpl visits a JSqlParser TableFunction in the FROM clause and requires it to carry an alias, because the analyzer resolves every result column to an owner alias. A table function has no real table name, so without an alias there is no stable identifier to bind columns against, and the analyzer fails fast with IllegalArgumentException.","triggerScenarios":"Executing a dynamic query (e.g. via QueryHelper/dql) whose FROM clause contains a table function such as `generate_series(...)`, `unnest(...)`, or a custom table-valued function without `AS alias`, causing visit(TableFunction) to throw.","commonSituations":"Hand-written dynamic SQL snippets that work in a raw psql/MySQL console (where aliasing is optional) are fed into the hsweb query analyzer; users porting SQL from other frameworks forget the mandatory alias rule.","solutions":["Add an alias to the table function in the SQL: `FROM generate_series(1,10) AS gs`.","If the SQL is generated by your code, append the alias programmatically before calling the analyzer.","If you do not actually need a table function, replace it with a plain table or a CTE (WITH ...) that is already aliased."],"exampleFix":"// before\nString dql = \"select * from generate_series(1,3)\";\n// after\nString dql = \"select * from generate_series(1,3) as gs\";","handlingStrategy":"validation","validationCode":"if (sql.toLowerCase().matches(\".*\\\\bfrom\\\\s+[a-z_0-9]+\\\\s*\\\\(.*\") && !sql.toLowerCase().matches(\".*\\\\bfrom\\\\s+[a-z_0-9]+\\\\s*\\\\([^)]*\\\\)\\\\s+(as\\\\s+)?[a-z_][a-z_0-9]*.*\")) { throw new IllegalArgumentException(\"table function in FROM must have an alias\"); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always alias table functions: `... AS t`","Add a unit test for each dynamic SQL template","Lint SQL snippets for unaliased FROM items"],"tags":["sql","query-analyzer","missing-alias","table-function"],"backgroundTag":"missing-required-argument","analyzedSha":"b2cfc85a57c70bf5b5cf6e7edae2d37102652ec8","analyzedAt":"2026-09-13T09:05:02.172Z","contentChangedAt":"2026-09-13T09:05:02.172Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}