From 56be925e4b8f5b1c0e6716ca5cbe0360d1229f50 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 23 Feb 2015 12:46:46 -0500 Subject: [PATCH] Further tweaking of raw grammar output to distinguish different inputs. Use a different A_Expr_Kind for LIKE/ILIKE/SIMILAR TO constructs, so that they can be distinguished from direct invocation of the underlying operators. Also, postpone selection of the operator name when transforming "x IN (select)" to "x = ANY (select)", so that those syntaxes can be told apart at parse analysis time. I had originally thought I'd also have to do something special for the syntaxes IS NOT DISTINCT FROM, IS NOT DOCUMENT, and x NOT IN (SELECT...), which the grammar translates as though they were NOT (construct). On reflection though, we can distinguish those cases reliably by noting whether the parse location shown for the NOT is the same as for its child node. This only requires tweaking the parse locations for NOT IN, which I've done here. These changes should have no effect outside the parser; they're just in support of being able to give accurate warnings for planned operator precedence changes. --- src/backend/nodes/outfuncs.c | 12 ++++++++ src/backend/parser/gram.y | 52 +++++++++++++++++++++++---------- src/backend/parser/parse_expr.c | 12 ++++++++ src/include/nodes/parsenodes.h | 3 ++ src/include/nodes/primnodes.h | 1 - 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 98aa5f0310..2f417fee20 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2516,6 +2516,18 @@ _outAExpr(StringInfo str, const A_Expr *node) appendStringInfoString(str, " IN "); WRITE_NODE_FIELD(name); break; + case AEXPR_LIKE: + appendStringInfoString(str, " LIKE "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_ILIKE: + appendStringInfoString(str, " ILIKE "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_SIMILAR: + appendStringInfoString(str, " SIMILAR "); + WRITE_NODE_FIELD(name); + break; case AEXPR_BETWEEN: appendStringInfoString(str, " BETWEEN "); WRITE_NODE_FIELD(name); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 76b0affff0..6c21002875 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11220,40 +11220,56 @@ a_expr: c_expr { $$ = $1; } { $$ = makeNotExpr($2, @1); } | a_expr LIKE a_expr - { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3, @2); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~", + $1, $3, @2); + } | a_expr LIKE a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), list_make2($3, $5), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~", + $1, (Node *) n, @2); } | a_expr NOT LIKE a_expr - { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, $4, @2); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~", + $1, $4, @2); + } | a_expr NOT LIKE a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), list_make2($4, $6), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~", + $1, (Node *) n, @2); } | a_expr ILIKE a_expr - { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, $3, @2); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*", + $1, $3, @2); + } | a_expr ILIKE a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), list_make2($3, $5), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*", + $1, (Node *) n, @2); } | a_expr NOT ILIKE a_expr - { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, $4, @2); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*", + $1, $4, @2); + } | a_expr NOT ILIKE a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), list_make2($4, $6), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*", + $1, (Node *) n, @2); } | a_expr SIMILAR TO a_expr %prec SIMILAR @@ -11261,28 +11277,32 @@ a_expr: c_expr { $$ = $1; } FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), list_make2($4, makeNullAConst(-1)), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~", + $1, (Node *) n, @2); } | a_expr SIMILAR TO a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), list_make2($4, $6), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~", + $1, (Node *) n, @2); } | a_expr NOT SIMILAR TO a_expr %prec SIMILAR { FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), list_make2($5, makeNullAConst(-1)), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~", + $1, (Node *) n, @2); } | a_expr NOT SIMILAR TO a_expr ESCAPE a_expr { FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), list_make2($5, $7), @2); - $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); + $$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~", + $1, (Node *) n, @2); } /* NullTest clause @@ -11450,7 +11470,7 @@ a_expr: c_expr { $$ = $1; } n->subLinkType = ANY_SUBLINK; n->subLinkId = 0; n->testexpr = $1; - n->operName = list_make1(makeString("=")); + n->operName = NIL; /* show it's IN not = ANY */ n->location = @2; $$ = (Node *)n; } @@ -11471,9 +11491,9 @@ a_expr: c_expr { $$ = $1; } n->subLinkType = ANY_SUBLINK; n->subLinkId = 0; n->testexpr = $1; - n->operName = list_make1(makeString("=")); - n->location = @3; - /* Stick a NOT on top */ + n->operName = NIL; /* show it's IN not = ANY */ + n->location = @2; + /* Stick a NOT on top; must have same parse location */ $$ = makeNotExpr((Node *) n, @2); } else diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index f314745818..7829bcbac1 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -182,6 +182,12 @@ transformExprRecurse(ParseState *pstate, Node *expr) case AEXPR_IN: result = transformAExprIn(pstate, a); break; + case AEXPR_LIKE: + case AEXPR_ILIKE: + case AEXPR_SIMILAR: + /* we can transform these just like AEXPR_OP */ + result = transformAExprOp(pstate, a); + break; case AEXPR_BETWEEN: case AEXPR_NOT_BETWEEN: case AEXPR_BETWEEN_SYM: @@ -1648,6 +1654,12 @@ transformSubLink(ParseState *pstate, SubLink *sublink) List *right_list; ListCell *l; + /* + * If the source was "x IN (select)", convert to "x = ANY (select)". + */ + if (sublink->operName == NIL) + sublink->operName = list_make1(makeString("=")); + /* * Transform lefthand expression, and convert to a list */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d7b6148cd5..ac133023b4 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -233,6 +233,9 @@ typedef enum A_Expr_Kind AEXPR_NULLIF, /* NULLIF - name must be "=" */ AEXPR_OF, /* IS [NOT] OF - name must be "=" or "<>" */ AEXPR_IN, /* [NOT] IN - name must be "=" or "<>" */ + AEXPR_LIKE, /* [NOT] LIKE - name must be "~~" or "!~~" */ + AEXPR_ILIKE, /* [NOT] ILIKE - name must be "~~*" or "!~~*" */ + AEXPR_SIMILAR, /* [NOT] SIMILAR - name must be "~" or "!~" */ AEXPR_BETWEEN, /* name must be "BETWEEN" */ AEXPR_NOT_BETWEEN, /* name must be "NOT BETWEEN" */ AEXPR_BETWEEN_SYM, /* name must be "BETWEEN SYMMETRIC" */ diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index b004da6dc4..dbc5a35cc8 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -994,7 +994,6 @@ typedef struct MinMaxExpr * Note: result type/typmod/collation are not stored, but can be deduced * from the XmlExprOp. The type/typmod fields are just used for display * purposes, and are NOT necessarily the true result type of the node. - * (We also use type == InvalidOid to mark a not-yet-parse-analyzed XmlExpr.) */ typedef enum XmlExprOp {