aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/nodes/outfuncs.c4
-rw-r--r--src/backend/parser/gram.y8
-rw-r--r--src/backend/parser/parse_expr.c68
-rw-r--r--src/include/nodes/parsenodes.h1
4 files changed, 65 insertions, 16 deletions
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e1966227379..acaf4ea5ebc 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2903,6 +2903,10 @@ _outAExpr(StringInfo str, const A_Expr *node)
appendStringInfoString(str, " DISTINCT ");
WRITE_NODE_FIELD(name);
break;
+ case AEXPR_NOT_DISTINCT:
+ appendStringInfoString(str, " NOT_DISTINCT ");
+ WRITE_NODE_FIELD(name);
+ break;
case AEXPR_NULLIF:
appendStringInfoString(str, " NULLIF ");
WRITE_NODE_FIELD(name);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index edf4516dacd..0cae44641f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11839,9 +11839,7 @@ a_expr: c_expr { $$ = $1; }
}
| a_expr IS NOT DISTINCT FROM a_expr %prec IS
{
- $$ = makeNotExpr((Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
- "=", $1, $6, @2),
- @2);
+ $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
}
| a_expr IS OF '(' type_list ')' %prec IS
{
@@ -12025,9 +12023,7 @@ b_expr: c_expr
}
| b_expr IS NOT DISTINCT FROM b_expr %prec IS
{
- $$ = makeNotExpr((Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
- "=", $1, $6, @2),
- @2);
+ $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
}
| b_expr IS OF '(' type_list ')' %prec IS
{
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8b285165d5f..cead21283d0 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -124,6 +124,8 @@ static Node *make_row_distinct_op(ParseState *pstate, List *opname,
RowExpr *lrow, RowExpr *rrow, int location);
static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree, int location);
+static Node *make_nulltest_from_distinct(ParseState *pstate,
+ A_Expr *distincta, Node *arg);
static int operator_precedence_group(Node *node, const char **nodename);
static void emit_precedence_warnings(ParseState *pstate,
int opgroup, const char *opname,
@@ -224,6 +226,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
result = transformAExprOpAll(pstate, a);
break;
case AEXPR_DISTINCT:
+ case AEXPR_NOT_DISTINCT:
result = transformAExprDistinct(pstate, a);
break;
case AEXPR_NULLIF:
@@ -991,12 +994,23 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a)
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
+ Node *result;
if (operator_precedence_warning)
emit_precedence_warnings(pstate, PREC_GROUP_INFIX_IS, "IS",
lexpr, rexpr,
a->location);
+ /*
+ * If either input is an undecorated NULL literal, transform to a NullTest
+ * on the other input. That's simpler to process than a full DistinctExpr,
+ * and it avoids needing to require that the datatype have an = operator.
+ */
+ if (exprIsNullConstant(rexpr))
+ return make_nulltest_from_distinct(pstate, a, lexpr);
+ if (exprIsNullConstant(lexpr))
+ return make_nulltest_from_distinct(pstate, a, rexpr);
+
lexpr = transformExprRecurse(pstate, lexpr);
rexpr = transformExprRecurse(pstate, rexpr);
@@ -1004,20 +1018,31 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a)
rexpr && IsA(rexpr, RowExpr))
{
/* ROW() op ROW() is handled specially */
- return make_row_distinct_op(pstate, a->name,
- (RowExpr *) lexpr,
- (RowExpr *) rexpr,
- a->location);
+ result = make_row_distinct_op(pstate, a->name,
+ (RowExpr *) lexpr,
+ (RowExpr *) rexpr,
+ a->location);
}
else
{
/* Ordinary scalar operator */
- return (Node *) make_distinct_op(pstate,
- a->name,
- lexpr,
- rexpr,
- a->location);
+ result = (Node *) make_distinct_op(pstate,
+ a->name,
+ lexpr,
+ rexpr,
+ a->location);
}
+
+ /*
+ * If it's NOT DISTINCT, we first build a DistinctExpr and then stick a
+ * NOT on top.
+ */
+ if (a->kind == AEXPR_NOT_DISTINCT)
+ result = (Node *) makeBoolExpr(NOT_EXPR,
+ list_make1(result),
+ a->location);
+
+ return result;
}
static Node *
@@ -2870,6 +2895,28 @@ make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
}
/*
+ * Produce a NullTest node from an IS [NOT] DISTINCT FROM NULL construct
+ *
+ * "arg" is the untransformed other argument
+ */
+static Node *
+make_nulltest_from_distinct(ParseState *pstate, A_Expr *distincta, Node *arg)
+{
+ NullTest *nt = makeNode(NullTest);
+
+ nt->arg = (Expr *) transformExprRecurse(pstate, arg);
+ /* the argument can be any type, so don't coerce it */
+ if (distincta->kind == AEXPR_NOT_DISTINCT)
+ nt->nulltesttype = IS_NULL;
+ else
+ nt->nulltesttype = IS_NOT_NULL;
+ /* argisrow = false is correct whether or not arg is composite */
+ nt->argisrow = false;
+ nt->location = distincta->location;
+ return (Node *) nt;
+}
+
+/*
* Identify node's group for operator precedence warnings
*
* For items in nonzero groups, also return a suitable node name into *nodename
@@ -2971,7 +3018,8 @@ operator_precedence_group(Node *node, const char **nodename)
*nodename = strVal(llast(aexpr->name));
group = PREC_GROUP_POSTFIX_OP;
}
- else if (aexpr->kind == AEXPR_DISTINCT)
+ else if (aexpr->kind == AEXPR_DISTINCT ||
+ aexpr->kind == AEXPR_NOT_DISTINCT)
{
*nodename = "IS";
group = PREC_GROUP_INFIX_IS;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3773dd9c2ff..1481fff57de 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -237,6 +237,7 @@ typedef enum A_Expr_Kind
AEXPR_OP_ANY, /* scalar op ANY (array) */
AEXPR_OP_ALL, /* scalar op ALL (array) */
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
+ AEXPR_NOT_DISTINCT, /* IS NOT DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF, /* IS [NOT] OF - name must be "=" or "<>" */
AEXPR_IN, /* [NOT] IN - name must be "=" or "<>" */