You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Xtext定制SQL DSL:表达式左右节点获取及生成器问题

解决Xtext自定义SQL DSL的表达式处理与生成器完善问题

我来帮你拆解并解决这个问题——核心是你需要递归遍历Xtext生成的表达式模型,同时修正语法中不符合SQL逻辑的部分,才能正确生成复杂SQL语句。

1. 先解决表达式无法获取left/right的问题

你当前的语法定义已经正确生成了带left/right属性的表达式模型,但生成器里只处理了单个Literal的情况(直接访问ID)。复杂表达式(比如A OR B)的类型是OrExpression/AndExpression等子类,需要通过类型判断来访问对应的属性。

另外,你的NotExpression语法定义有问题:

NotExpression returns Expression: ComparisonExpression({NotExpression.left=current} name='NOT' right=ComparisonExpression)*;

这会把NOT当成二元运算符(比如A NOT B),不符合SQL中NOT是一元运算符的逻辑(比如NOT (A = B))。需要修改为:

NotExpression returns Expression: 
    ('NOT' expr=ComparisonExpression) | 
    ComparisonExpression;

修改后生成的模型中,NotExpression会有一个expr属性,对应被否定的表达式。

2. 编写递归的表达式转SQL方法

在生成器中添加一个递归方法,根据表达式的实际类型将其转换为SQL字符串:

def String toSql(Expression expr) {
    switch expr {
        // 二元表达式处理
        case OrExpression:
            return toSql(expr.left) + " " + expr.name + " " + toSql(expr.right)
        case AndExpression:
            return toSql(expr.left) + " " + expr.name + " " + toSql(expr.right)
        case ComparisonExpression:
            return toSql(expr.left) + " " + expr.name + " " + toSql(expr.right)
        case BitwiseOR, BitwiseAND, BitwiseXOR:
            return toSql(expr.left) + " " + expr.name + " " + toSql(expr.right)
        case Addition, Substraction, Multiplication, Division, Modulo:
            return toSql(expr.left) + " " + expr.name + " " + toSql(expr.right)
        
        // 一元表达式与特殊结构
        case NotExpression:
            return "NOT " + toSql(expr.expr)
        case Bracket:
            return "(" + toSql(expr.inner) + ")"
        case Primary:
            var sql = ""
            if (expr.unop !== null) {
                sql += expr.unop + " "
            }
            if (expr.ID !== null) {
                sql += toSql(expr.ID)
                if (expr.isNotNull !== null) {
                    sql += " IS " + (expr.isNotNull ? "NOT " : "") + "NULL"
                }
            }
            return sql
        
        // 字面量与函数
        case Literal:
            val value = expr.value
            if (value instanceof StringValue) {
                return "'" + value.name.replace("'", "''") + "'" // 转义单引号避免语法错误
            } else if (value instanceof NumberValue) {
                return value.name
            } else if (value instanceof NullValue) {
                return "NULL"
            }
            return value.name
        case Funccall:
            return expr.func.name + "(" + expr.func.arguments.map[toSql(it)].join(", ") + ")"
        case Count:
            return "COUNT(" + (expr.count.column ? toSql(expr.count.column) : "*") + ")"
        
        default:
            return expr.toString()
    }
}

3. 完善SQL生成器的核心逻辑

替换生成器中处理SELECT、FROM、WHERE的代码,调用上面的toSql方法处理复杂表达式:

class TsqlGenerator extends AbstractGenerator {
    StringBuilder st = new StringBuilder();
    TimeConditionHandler tch = new TimeConditionHandler()
    int whereIndex = 0;

    def clear() {
        st.setLength(0);
        whereIndex = 0;
    }

    override beforeGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context) {
        clear()
    }

    override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
        for (e : resource.allContents.toIterable.filter(ComplexSelect)) {
            // 处理SELECT子句
            st.append("SELECT ");
            val selectArgs = e.left.selectArguments.arguments.map[toSql(expr)].join(", ")
            st.append(selectArgs);

            // 处理FROM子句
            st.append(" FROM ")
            val fromSources = e.left.from.sources.map[name].join(", ")
            st.append(fromSources);

            // 处理WHERE子句
            if (e.left.where !== null || e.left.timedef !== null) {
                st.append(" WHERE");
                if (e.left.where !== null) {
                    val wherePredicates = e.left.where.predicates.expr.map[toSql(it)].join(" AND ")
                    st.append(" " + wherePredicates);
                    whereIndex = 1;
                }
                // 处理时间条件
                if (e.left.timedef !== null) {
                    if (whereIndex > 0) {
                        st.append(" AND")
                    }
                    st.append(" " + tch.toIso8601(e.left.timedef))
                }
                fsa.generateFile("query.txt", st.toString());
            }
        }
    }

    // 上面的toSql方法放在这里
    def String toSql(Expression expr) {
        // ... 实现代码同上 ...
    }
}

关键说明

  • 递归遍历:表达式是嵌套结构(比如(A + B) * C),必须用递归才能正确展开为合法的SQL字符串。
  • 类型判断:通过switch expr识别表达式的实际类型,才能访问对应的left/right/expr属性。
  • 字面量转义:处理字符串字面量时转义单引号,避免SQL语法错误或注入风险。
  • 语法修正:修正NotExpression的定义,使其符合SQL中一元运算符的逻辑,才能正确生成NOT相关的表达式。

内容的提问来源于stack exchange,提问作者Saymos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:58:29