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 {