diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7f0995f..35e3eb0 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -40,8 +40,7 @@ bool Transform_null_equals = false; static Node *transformExprRecurse(ParseState *pstate, Node *expr); static Node *transformParamRef(ParseState *pstate, ParamRef *pref); static Node *transformAExprOp(ParseState *pstate, A_Expr *a); -static Node *transformAExprAnd(ParseState *pstate, A_Expr *a); -static Node *transformAExprOr(ParseState *pstate, A_Expr *a); +static Node *transformAExprAndOr(ParseState *pstate, A_Expr *a); static Node *transformAExprNot(ParseState *pstate, A_Expr *a); static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a); static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a); @@ -223,10 +222,10 @@ transformExprRecurse(ParseState *pstate, Node *expr) result = transformAExprOp(pstate, a); break; case AEXPR_AND: - result = transformAExprAnd(pstate, a); + result = transformAExprAndOr(pstate, a); break; case AEXPR_OR: - result = transformAExprOr(pstate, a); + result = transformAExprAndOr(pstate, a); break; case AEXPR_NOT: result = transformAExprNot(pstate, a); @@ -917,32 +916,76 @@ transformAExprOp(ParseState *pstate, A_Expr *a) return result; } +/* + * Transform the AND/OR trees non-recursively. + * + * The parser turns a list of consecutive AND/OR expressions into a left-deep tree. + * + * a AND b AND c + * + * AND + * / \ + * AND c + * / \ + * a b + * + * For very long lists, it gets deep enough that processing it recursively causes + * check_stack_depth() to raise error and abort the query. Hence, it is necessary + * that we process these trees iteratively. + */ static Node * -transformAExprAnd(ParseState *pstate, A_Expr *a) +transformAExprAndOr(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExprRecurse(pstate, a->lexpr); - Node *rexpr = transformExprRecurse(pstate, a->rexpr); + List *exprs = NIL; + List *pending = NIL; + Node *expr; + A_Expr *root = a; + A_Expr_Kind root_kind = a->kind; - lexpr = coerce_to_boolean(pstate, lexpr, "AND"); - rexpr = coerce_to_boolean(pstate, rexpr, "AND"); + pending = lappend(pending, a); - return (Node *) makeBoolExpr(AND_EXPR, - list_make2(lexpr, rexpr), - a->location); -} + while (list_length(pending) > 0) + { + Node *tmp; -static Node * -transformAExprOr(ParseState *pstate, A_Expr *a) -{ - Node *lexpr = transformExprRecurse(pstate, a->lexpr); - Node *rexpr = transformExprRecurse(pstate, a->rexpr); + a = (A_Expr*) list_nth(pending, 0); + pending = list_delete_first(pending); - lexpr = coerce_to_boolean(pstate, lexpr, "OR"); - rexpr = coerce_to_boolean(pstate, rexpr, "OR"); + /* + * Follow the left links to walk the left-deep tree, and process all the + * right branches. If a right branch is also the same kind of tree, then + * process that tree also right here instead of recursing. + */ + tmp = (Node*)a; + do { + a = (A_Expr*) tmp; - return (Node *) makeBoolExpr(OR_EXPR, - list_make2(lexpr, rexpr), - a->location); + /* + * If the right branch is also an AND condition, append it to the + * pending list, to be processed later. This allows us to walk even + * bushy trees, not just left-deep trees. + */ + if (IsA(a->rexpr, A_Expr) && ((A_Expr*)a->rexpr)->kind == root_kind) + { + pending = lappend(pending, a->rexpr); + } + else + { + expr = transformExprRecurse(pstate, a->rexpr); + expr = coerce_to_boolean(pstate, expr, root_kind == AEXPR_AND ? "AND" : "OR"); + exprs = lcons(expr, exprs); + } + + tmp = a->lexpr; + } while (IsA(tmp, A_Expr) && ((A_Expr*)tmp)->kind == root_kind); + + /* Now process the last left expression */ + expr = transformExprRecurse(pstate, a->lexpr); + expr = coerce_to_boolean(pstate, expr, root_kind == AEXPR_AND ? "AND" : "OR"); + exprs = lcons(expr, exprs); + } + + return (Node *) makeBoolExpr(root_kind == AEXPR_AND ? AND_EXPR : OR_EXPR, exprs, root->location); } static Node * diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 57ae842..42f0ece 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2092,7 +2092,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem | int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail + | FROM shoe rsh, + | shoelace rsl + - | WHERE (((rsl.sl_color = rsh.slcolor) AND (rsl.sl_len_cm >= rsh.slminlen_cm)) AND (rsl.sl_len_cm <= rsh.slmaxlen_cm)); + | WHERE ((rsl.sl_color = rsh.slcolor) AND (rsl.sl_len_cm >= rsh.slminlen_cm) AND (rsl.sl_len_cm <= rsh.slmaxlen_cm)); shoelace | SELECT s.sl_name, + | s.sl_avail, + | s.sl_color, +