Skip to content

Commit 2146f13

Browse files
committed
Avoid recursion when processing simple lists of AND'ed or OR'ed clauses.
Since most of the system thinks AND and OR are N-argument expressions anyway, let's have the grammar generate a representation of that form when dealing with input like "x AND y AND z AND ...", rather than generating a deeply-nested binary tree that just has to be flattened later by the planner. This avoids stack overflow in parse analysis when dealing with queries having more than a few thousand such clauses; and in any case it removes some rather unsightly inconsistencies, since some parts of parse analysis were generating N-argument ANDs/ORs already. It's still possible to get a stack overflow with weirdly parenthesized input, such as "x AND (y AND (z AND ( ... )))", but such cases are not mainstream usage. The maximum depth of parenthesization is already limited by Bison's stack in such cases, anyway, so that the limit is probably fairly platform-independent. Patch originally by Gurjeet Singh, heavily revised by me
1 parent ac608fe commit 2146f13

File tree

12 files changed

+155
-141
lines changed

12 files changed

+155
-141
lines changed

contrib/postgres_fdw/deparse.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,9 +1716,6 @@ deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
17161716

17171717
/*
17181718
* Deparse a BoolExpr node.
1719-
*
1720-
* Note: by the time we get here, AND and OR expressions have been flattened
1721-
* into N-argument form, so we'd better be prepared to deal with that.
17221719
*/
17231720
static void
17241721
deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context)

src/backend/nodes/nodeFuncs.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,6 +3047,14 @@ raw_expression_tree_walker(Node *node,
30473047
/* operator name is deemed uninteresting */
30483048
}
30493049
break;
3050+
case T_BoolExpr:
3051+
{
3052+
BoolExpr *expr = (BoolExpr *) node;
3053+
3054+
if (walker(expr->args, context))
3055+
return true;
3056+
}
3057+
break;
30503058
case T_ColumnRef:
30513059
/* we assume the fields contain nothing interesting */
30523060
break;

src/backend/nodes/outfuncs.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,15 +2437,6 @@ _outAExpr(StringInfo str, const A_Expr *node)
24372437
appendStringInfoChar(str, ' ');
24382438
WRITE_NODE_FIELD(name);
24392439
break;
2440-
case AEXPR_AND:
2441-
appendStringInfoString(str, " AND");
2442-
break;
2443-
case AEXPR_OR:
2444-
appendStringInfoString(str, " OR");
2445-
break;
2446-
case AEXPR_NOT:
2447-
appendStringInfoString(str, " NOT");
2448-
break;
24492440
case AEXPR_OP_ANY:
24502441
appendStringInfoChar(str, ' ');
24512442
WRITE_NODE_FIELD(name);

src/backend/optimizer/prep/prepjointree.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
133133
* transformations if any are found.
134134
*
135135
* This routine has to run before preprocess_expression(), so the quals
136-
* clauses are not yet reduced to implicit-AND format. That means we need
137-
* to recursively search through explicit AND clauses, which are
138-
* probably only binary ANDs. We stop as soon as we hit a non-AND item.
136+
* clauses are not yet reduced to implicit-AND format, and are not guaranteed
137+
* to be AND/OR-flat either. That means we need to recursively search through
138+
* explicit AND clauses. We stop as soon as we hit a non-AND item.
139139
*/
140140
void
141141
pull_up_sublinks(PlannerInfo *root)

src/backend/optimizer/prep/prepqual.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
* Routines for preprocessing qualification expressions
55
*
66
*
7-
* The parser regards AND and OR as purely binary operators, so a qual like
8-
* (A = 1) OR (A = 2) OR (A = 3) ...
9-
* will produce a nested parsetree
10-
* (OR (A = 1) (OR (A = 2) (OR (A = 3) ...)))
11-
* In reality, the optimizer and executor regard AND and OR as N-argument
12-
* operators, so this tree can be flattened to
13-
* (OR (A = 1) (A = 2) (A = 3) ...)
7+
* While the parser will produce flattened (N-argument) AND/OR trees from
8+
* simple sequences of AND'ed or OR'ed clauses, there might be an AND clause
9+
* directly underneath another AND, or OR underneath OR, if the input was
10+
* oddly parenthesized. Also, rule expansion and subquery flattening could
11+
* produce such parsetrees. The planner wants to flatten all such cases
12+
* to ensure consistent optimization behavior.
1413
*
1514
* Formerly, this module was responsible for doing the initial flattening,
1615
* but now we leave it to eval_const_expressions to do that since it has to

src/backend/optimizer/util/clauses.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3447,12 +3447,15 @@ simplify_or_arguments(List *args,
34473447
List *unprocessed_args;
34483448

34493449
/*
3450-
* Since the parser considers OR to be a binary operator, long OR lists
3451-
* become deeply nested expressions. We must flatten these into long
3452-
* argument lists of a single OR operator. To avoid blowing out the stack
3453-
* with recursion of eval_const_expressions, we resort to some tenseness
3454-
* here: we keep a list of not-yet-processed inputs, and handle flattening
3455-
* of nested ORs by prepending to the to-do list instead of recursing.
3450+
* We want to ensure that any OR immediately beneath another OR gets
3451+
* flattened into a single OR-list, so as to simplify later reasoning.
3452+
*
3453+
* To avoid stack overflow from recursion of eval_const_expressions, we
3454+
* resort to some tenseness here: we keep a list of not-yet-processed
3455+
* inputs, and handle flattening of nested ORs by prepending to the to-do
3456+
* list instead of recursing. Now that the parser generates N-argument
3457+
* ORs from simple lists, this complexity is probably less necessary than
3458+
* it once was, but we might as well keep the logic.
34563459
*/
34573460
unprocessed_args = list_copy(args);
34583461
while (unprocessed_args)

src/backend/parser/gram.y

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ static void insertSelectOptions(SelectStmt *stmt,
151151
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
152152
static Node *doNegate(Node *n, int location);
153153
static void doNegateFloat(Value *v);
154+
static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location);
155+
static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location);
156+
static Node *makeNotExpr(Node *expr, int location);
154157
static Node *makeAArrayExpr(List *elements, int location);
155158
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
156159
List *args, int location);
@@ -10849,11 +10852,11 @@ a_expr: c_expr { $$ = $1; }
1084910852
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
1085010853

1085110854
| a_expr AND a_expr
10852-
{ $$ = (Node *) makeA_Expr(AEXPR_AND, NIL, $1, $3, @2); }
10855+
{ $$ = makeAndExpr($1, $3, @2); }
1085310856
| a_expr OR a_expr
10854-
{ $$ = (Node *) makeA_Expr(AEXPR_OR, NIL, $1, $3, @2); }
10857+
{ $$ = makeOrExpr($1, $3, @2); }
1085510858
| NOT a_expr
10856-
{ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, $2, @1); }
10859+
{ $$ = makeNotExpr($2, @1); }
1085710860

1085810861
| a_expr LIKE a_expr
1085910862
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3, @2); }
@@ -11022,11 +11025,9 @@ a_expr: c_expr { $$ = $1; }
1102211025
}
1102311026
| a_expr IS NOT DISTINCT FROM a_expr %prec IS
1102411027
{
11025-
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
11026-
(Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
11027-
"=", $1, $6, @2),
11028-
@2);
11029-
11028+
$$ = makeNotExpr((Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
11029+
"=", $1, $6, @2),
11030+
@2);
1103011031
}
1103111032
| a_expr IS OF '(' type_list ')' %prec IS
1103211033
{
@@ -11044,43 +11045,43 @@ a_expr: c_expr { $$ = $1; }
1104411045
*/
1104511046
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
1104611047
{
11047-
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
11048+
$$ = makeAndExpr(
1104811049
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2),
1104911050
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2),
11050-
@2);
11051+
@2);
1105111052
}
1105211053
| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
1105311054
{
11054-
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
11055+
$$ = makeOrExpr(
1105511056
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2),
1105611057
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2),
11057-
@2);
11058+
@2);
1105811059
}
1105911060
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
1106011061
{
11061-
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
11062-
(Node *) makeA_Expr(AEXPR_AND, NIL,
11062+
$$ = makeOrExpr(
11063+
makeAndExpr(
1106311064
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2),
1106411065
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2),
11065-
@2),
11066-
(Node *) makeA_Expr(AEXPR_AND, NIL,
11066+
@2),
11067+
makeAndExpr(
1106711068
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6, @2),
1106811069
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4, @2),
11069-
@2),
11070-
@2);
11070+
@2),
11071+
@2);
1107111072
}
1107211073
| a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
1107311074
{
11074-
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
11075-
(Node *) makeA_Expr(AEXPR_OR, NIL,
11075+
$$ = makeAndExpr(
11076+
makeOrExpr(
1107611077
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2),
1107711078
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2),
11078-
@2),
11079-
(Node *) makeA_Expr(AEXPR_OR, NIL,
11079+
@2),
11080+
makeOrExpr(
1108011081
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7, @2),
1108111082
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5, @2),
11082-
@2),
11083-
@2);
11083+
@2),
11084+
@2);
1108411085
}
1108511086
| a_expr IN_P in_expr
1108611087
{
@@ -11114,7 +11115,7 @@ a_expr: c_expr { $$ = $1; }
1111411115
n->operName = list_make1(makeString("="));
1111511116
n->location = @3;
1111611117
/* Stick a NOT on top */
11117-
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n, @2);
11118+
$$ = makeNotExpr((Node *) n, @2);
1111811119
}
1111911120
else
1112011121
{
@@ -11162,10 +11163,9 @@ a_expr: c_expr { $$ = $1; }
1116211163
}
1116311164
| a_expr IS NOT DOCUMENT_P %prec IS
1116411165
{
11165-
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
11166-
makeXmlExpr(IS_DOCUMENT, NULL, NIL,
11167-
list_make1($1), @2),
11168-
@2);
11166+
$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
11167+
list_make1($1), @2),
11168+
@2);
1116911169
}
1117011170
;
1117111171

@@ -11216,8 +11216,9 @@ b_expr: c_expr
1121611216
}
1121711217
| b_expr IS NOT DISTINCT FROM b_expr %prec IS
1121811218
{
11219-
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL,
11220-
NULL, (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $6, @2), @2);
11219+
$$ = makeNotExpr((Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
11220+
"=", $1, $6, @2),
11221+
@2);
1122111222
}
1122211223
| b_expr IS OF '(' type_list ')' %prec IS
1122311224
{
@@ -11234,10 +11235,9 @@ b_expr: c_expr
1123411235
}
1123511236
| b_expr IS NOT DOCUMENT_P %prec IS
1123611237
{
11237-
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
11238-
makeXmlExpr(IS_DOCUMENT, NULL, NIL,
11239-
list_make1($1), @2),
11240-
@2);
11238+
$$ = makeNotExpr(makeXmlExpr(IS_DOCUMENT, NULL, NIL,
11239+
list_make1($1), @2),
11240+
@2);
1124111241
}
1124211242
;
1124311243

@@ -13692,6 +13692,46 @@ doNegateFloat(Value *v)
1369213692
v->val.str = psprintf("-%s", oldval);
1369313693
}
1369413694

13695+
static Node *
13696+
makeAndExpr(Node *lexpr, Node *rexpr, int location)
13697+
{
13698+
/* Flatten "a AND b AND c ..." to a single BoolExpr on sight */
13699+
if (IsA(lexpr, BoolExpr))
13700+
{
13701+
BoolExpr *blexpr = (BoolExpr *) lexpr;
13702+
13703+
if (blexpr->boolop == AND_EXPR)
13704+
{
13705+
blexpr->args = lappend(blexpr->args, rexpr);
13706+
return (Node *) blexpr;
13707+
}
13708+
}
13709+
return (Node *) makeBoolExpr(AND_EXPR, list_make2(lexpr, rexpr), location);
13710+
}
13711+
13712+
static Node *
13713+
makeOrExpr(Node *lexpr, Node *rexpr, int location)
13714+
{
13715+
/* Flatten "a OR b OR c ..." to a single BoolExpr on sight */
13716+
if (IsA(lexpr, BoolExpr))
13717+
{
13718+
BoolExpr *blexpr = (BoolExpr *) lexpr;
13719+
13720+
if (blexpr->boolop == OR_EXPR)
13721+
{
13722+
blexpr->args = lappend(blexpr->args, rexpr);
13723+
return (Node *) blexpr;
13724+
}
13725+
}
13726+
return (Node *) makeBoolExpr(OR_EXPR, list_make2(lexpr, rexpr), location);
13727+
}
13728+
13729+
static Node *
13730+
makeNotExpr(Node *expr, int location)
13731+
{
13732+
return (Node *) makeBoolExpr(NOT_EXPR, list_make1(expr), location);
13733+
}
13734+
1369513735
static Node *
1369613736
makeAArrayExpr(List *elements, int location)
1369713737
{

src/backend/parser/parse_clause.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,8 @@ transformJoinUsingClause(ParseState *pstate,
332332
RangeTblEntry *leftRTE, RangeTblEntry *rightRTE,
333333
List *leftVars, List *rightVars)
334334
{
335-
Node *result = NULL;
335+
Node *result;
336+
List *andargs = NIL;
336337
ListCell *lvars,
337338
*rvars;
338339

@@ -358,18 +359,16 @@ transformJoinUsingClause(ParseState *pstate,
358359
copyObject(lvar), copyObject(rvar),
359360
-1);
360361

361-
/* And combine into an AND clause, if multiple join columns */
362-
if (result == NULL)
363-
result = (Node *) e;
364-
else
365-
{
366-
A_Expr *a;
367-
368-
a = makeA_Expr(AEXPR_AND, NIL, result, (Node *) e, -1);
369-
result = (Node *) a;
370-
}
362+
/* Prepare to combine into an AND clause, if multiple join columns */
363+
andargs = lappend(andargs, e);
371364
}
372365

366+
/* Only need an AND if there's more than one join column */
367+
if (list_length(andargs) == 1)
368+
result = (Node *) linitial(andargs);
369+
else
370+
result = (Node *) makeBoolExpr(AND_EXPR, andargs, -1);
371+
373372
/*
374373
* Since the references are already Vars, and are certainly from the input
375374
* relations, we don't have to go through the same pushups that

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy