暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL查询引擎——General Expressions Grammar之CaseExpr

肥叔菌 2023-02-09
384

General expressions语法规则定义在src/backend/parser/gram.y文件中,其是表达式语法的核心。有两种表达式类型:a_expr
是不受限制的类型,b_expr
是必须在某些地方使用的子集,以避免移位/减少冲突。例如,我们不能将BETWEEN
作为BETWEEN a_expr AND a_exp
,因为AND
的使用与AND
作为布尔运算符冲突。因此,b_expr
BETWEEN
中使用,我们从b_expr
中删除布尔关键字。请注意,( a_expr )
b_expr
,因此始终可以使用无限制表达式,方法是用括号将其括起来。c_expr
a_expr
b_expr
共同的所有乘积;它被分解出来只是为了消除冗余编码。注意涉及多个terminal token的产出productions。默认情况下,bison将为此类productions分配其最后一个terminal的优先级,但在几乎所有情况下,您都希望它是第一个terminal的优先权;否则你不会得到你期望的行为!因此,我们可以自由使用%prec
注释来设置优先级。

productions expression

c_expr
a_expr
b_expr
所共有的规则,递归引用a_expr
b_expr
的productions通常不能出现在c_expr
规则中。但是,可以引用出现在括号内的a_exprs
,例如函数参数;这不会给b_expr
语法带来歧义。productions that refer recursively to a_expr or b_expr mostly cannot appear here. However, it’s OK to refer to a_exprs that occur inside parentheses, such as function arguments; that cannot introduce ambiguity to the b_expr syntax.

规则五:SQL-style CASE子句,两种形式:Full specification(CASE WHEN a = b THEN c ... ELSE d END
)和 Implicit argument(CASE a WHEN b THEN c ... ELSE d END
)。

在第二种形式种,WHEN子句的条件表达式是comparison values比较值。解析分析器将其转换为CaseTestExpr '=' comexpr形式的合法布尔表达式。CaseTestExpr节点是placeholder,在运行时返回正确的值。该结构为例能使testexpr执行一次而引入。在解析分期之后,条件表达式永远返回布尔值。

transformCaseExpr

定义在src/backend/parser/parser_expr.c文件中的Node *transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)函数用于分析和转换表达式,包含类型检查和类型转换的工作。该函数将原始语法输出转换为具有完全确定语义的表达式树(Type checking and type casting is done here. This processing converts the raw grammar output into expression trees with fully determined semantics)。

static Node *transformExprRecurse(ParseState *pstate, Node *expr)函数用于递归处理表达式,其整体代码框架如下所示。

    static Node *transformExprRecurse(ParseState *pstate, Node *expr){
    Node *result;
    if (expr == NULL) return NULL;
    check_stack_depth(); /* Guard against stack overflow due to overly complex expressions */


    switch (nodeTag(expr)){
    case T_ColumnRef:
    case T_ParamRef:
    case T_A_Const:
    case T_A_Indirection:
    case T_A_ArrayExpr:
    case T_TypeCast:
    case T_CollateClause:
    case T_A_Expr:
    case T_BoolExpr:
    case T_FuncCall:
    case T_MultiAssignRef:
    case T_GroupingFunc:
    case T_NamedArgExpr:
    case T_SubLink:
    case T_CaseExpr:
    result = transformCaseExpr(pstate, (CaseExpr *) expr);
    break;
    case T_RowExpr:
    case T_CoalesceExpr:
    case T_MinMaxExpr:
    case T_SQLValueFunction:
    case T_XmlExpr:
    case T_XmlSerialize:
    case T_NullTest:
    case T_BooleanTest:
    case T_CurrentOfExpr:
    case T_SetToDefault:
    case T_CaseTestExpr:
    case T_Var:
    default:
    /* should not reach here */
    elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
    result = NULL; /* keep compiler quiet */ break;
    }
    return result;
    }


    复制

    T_CaseExpr

    static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c)函数定义在src/backend/parser/parse_expr.c文件中,用于处理T_CaseExpr节点,其主要执行流程如下:(从中可以看出transformExprRecurse函数遇到General Expressions就会被调用,也即是递归处理的含义)

    • 首先处理testexpr(test expression)子句,也就是CaseExpr.arg成员,递归调用transformExprRecurse处理a_expr表达式,并将其设置到transform流程中新的CaseExpr.arg成员中。如果式第二种CASE语法,则需要创建CaseTestExpr节点类型的placeholder,用于实现后续WHEN子句的CaseTestExpr = complexpr A_Expr节点。

    • 处理WHEN子句的CaseWhen节点,首先处理CaseExpr中的args元素CaseWhen节点的expr和result成员,其中要注意上一步中生成的placeholder,需要将CaseWhen.expr转为A_Expr节点,实现CaseTestExpr = complexpr 转变,然后调用transformExprRecurse处理。

    • 最后处理CaseExpr中的defresult表达式,也就是default分支子句,如果为空,需要添加A_Const值为Null的几点,然后调用transformExprRecurse处理。

    T_CaseTestExpr

    CaseTestExpr不需要任何处理,仅仅用于插入parse trees。CaseTestExpr dosen't require any processing; it is only injected into parse trees in a fully-formed state.

    欢迎关注微信公众号肥叔菌PostgreSQL数据库专栏:



    文章转载自肥叔菌,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论