Skip to content

Commit 68d9fbe

Browse files
author
Thomas G. Lockhart
committed
Implement the IS DISTINCT FROM operator per SQL99.
Reused the Expr node to hold DISTINCT which strongly resembles the existing OP info. Define DISTINCT_EXPR which strongly resembles the existing OPER_EXPR opType, but with handling for NULLs required by SQL99. We have explicit support for single-element DISTINCT comparisons all the way through to the executor. But, multi-element DISTINCTs are handled by expanding into a comparison tree in gram.y as is done for other row comparisons. Per discussions, it might be desirable to move this into one or more purpose-built nodes to be handled in the backend. Define the optional ROW keyword and token per SQL99. This allows single-element row constructs, which were formerly disallowed due to shift/reduce conflicts with parenthesized a_expr clauses. Define the SQL99 TREAT() function. Currently, use as a synonym for CAST().
1 parent c7eea66 commit 68d9fbe

File tree

10 files changed

+438
-100
lines changed

10 files changed

+438
-100
lines changed

src/backend/executor/execQual.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.94 2002/06/20 20:29:27 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.95 2002/07/04 15:23:29 thomas Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -51,6 +51,8 @@ static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
5151
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
5252
static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
5353
bool *isNull, ExprDoneCond *isDone);
54+
static Datum ExecEvalDistinct(Expr *opClause, ExprContext *econtext,
55+
bool *isNull, ExprDoneCond *isDone);
5456
static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
5557
bool *isNull, ExprDoneCond *isDone);
5658
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
@@ -832,6 +834,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
832834

833835
/* ----------------------------------------------------------------
834836
* ExecEvalOper
837+
* ExecEvalDistinct
835838
* ExecEvalFunc
836839
*
837840
* Evaluate the functional result of a list of arguments by calling the
@@ -878,6 +881,80 @@ ExecEvalOper(Expr *opClause,
878881
isNull, isDone);
879882
}
880883

884+
/* ----------------------------------------------------------------
885+
* ExecEvalDistinct
886+
*
887+
* IS DISTINCT FROM must evaluate arguments to determine whether
888+
* they are NULL; if either is NULL then the result is already
889+
* known. If neither is NULL, then proceed to evaluate the
890+
* function. Note that this is *always* derived from the equals
891+
* operator, but since we've already evaluated the arguments
892+
* we can not simply reuse ExecEvalOper() or ExecEvalFunc().
893+
* ----------------------------------------------------------------
894+
*/
895+
static Datum
896+
ExecEvalDistinct(Expr *opClause,
897+
ExprContext *econtext,
898+
bool *isNull,
899+
ExprDoneCond *isDone)
900+
{
901+
bool result;
902+
FunctionCachePtr fcache;
903+
FunctionCallInfoData fcinfo;
904+
ExprDoneCond argDone;
905+
Oper *op;
906+
List *argList;
907+
908+
/*
909+
* we extract the oid of the function associated with the op and then
910+
* pass the work onto ExecMakeFunctionResult which evaluates the
911+
* arguments and returns the result of calling the function on the
912+
* evaluated arguments.
913+
*/
914+
op = (Oper *) opClause->oper;
915+
argList = opClause->args;
916+
917+
/*
918+
* get the fcache from the Oper node. If it is NULL, then initialize
919+
* it
920+
*/
921+
fcache = op->op_fcache;
922+
if (fcache == NULL)
923+
{
924+
fcache = init_fcache(op->opid, length(argList),
925+
econtext->ecxt_per_query_memory);
926+
op->op_fcache = fcache;
927+
}
928+
Assert(fcache->func.fn_retset == FALSE);
929+
930+
/* Need to prep callinfo structure */
931+
MemSet(&fcinfo, 0, sizeof(fcinfo));
932+
fcinfo.flinfo = &(fcache->func);
933+
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
934+
Assert(fcinfo->nargs == 2);
935+
936+
if (fcinfo.argnull[0] && fcinfo.argnull[1])
937+
{
938+
/* Both NULL? Then is not distinct... */
939+
result = FALSE;
940+
}
941+
else if (fcinfo.argnull[0] || fcinfo.argnull[1])
942+
{
943+
/* One is NULL? Then is distinct... */
944+
result = TRUE;
945+
}
946+
else
947+
{
948+
fcinfo.isnull = false;
949+
result = FunctionCallInvoke(&fcinfo);
950+
*isNull = fcinfo.isnull;
951+
952+
result = (!DatumGetBool(result));
953+
}
954+
955+
return BoolGetDatum(result);
956+
}
957+
881958
/* ----------------------------------------------------------------
882959
* ExecEvalFunc
883960
* ----------------------------------------------------------------
@@ -1367,6 +1444,10 @@ ExecEvalExpr(Node *expression,
13671444
case NOT_EXPR:
13681445
retDatum = ExecEvalNot(expr, econtext, isNull);
13691446
break;
1447+
case DISTINCT_EXPR:
1448+
retDatum = ExecEvalDistinct(expr, econtext,
1449+
isNull, isDone);
1450+
break;
13701451
case SUBPLAN_EXPR:
13711452
retDatum = ExecSubPlan((SubPlan *) expr->oper,
13721453
expr->args, econtext,

src/backend/nodes/outfuncs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
66
* Portions Copyright (c) 1994, Regents of the University of California
77
*
8-
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.160 2002/06/20 20:29:29 momjian Exp $
8+
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.161 2002/07/04 15:23:53 thomas Exp $
99
*
1010
* NOTES
1111
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -719,6 +719,9 @@ _outExpr(StringInfo str, Expr *node)
719719
case OP_EXPR:
720720
opstr = "op";
721721
break;
722+
case DISTINCT_EXPR:
723+
opstr = "distinct";
724+
break;
722725
case FUNC_EXPR:
723726
opstr = "func";
724727
break;

src/backend/nodes/readfuncs.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.123 2002/06/20 20:29:29 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.124 2002/07/04 15:23:54 thomas Exp $
1212
*
1313
* NOTES
1414
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -804,6 +804,8 @@ _readExpr(void)
804804
token = pg_strtok(&length); /* get opType */
805805
if (strncmp(token, "op", 2) == 0)
806806
local_node->opType = OP_EXPR;
807+
else if (strncmp(token, "distinct", 8) == 0)
808+
local_node->opType = DISTINCT_EXPR;
807809
else if (strncmp(token, "func", 4) == 0)
808810
local_node->opType = FUNC_EXPR;
809811
else if (strncmp(token, "or", 2) == 0)

src/backend/optimizer/path/costsize.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Portions Copyright (c) 1994, Regents of the University of California
4343
*
4444
* IDENTIFICATION
45-
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.88 2002/06/26 21:58:56 momjian Exp $
45+
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.89 2002/07/04 15:23:56 thomas Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -1092,6 +1092,7 @@ cost_qual_eval_walker(Node *node, Cost *total)
10921092
switch (expr->opType)
10931093
{
10941094
case OP_EXPR:
1095+
case DISTINCT_EXPR:
10951096
case FUNC_EXPR:
10961097
*total += cpu_operator_cost;
10971098
break;

src/backend/optimizer/util/clauses.c

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.101 2002/06/20 20:29:31 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.102 2002/07/04 15:23:58 thomas Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -71,6 +71,7 @@ make_clause(int type, Node *oper, List *args)
7171
expr->typeOid = BOOLOID;
7272
break;
7373
case OP_EXPR:
74+
case DISTINCT_EXPR:
7475
expr->typeOid = ((Oper *) oper)->opresulttype;
7576
break;
7677
case FUNC_EXPR:
@@ -107,7 +108,8 @@ is_opclause(Node *clause)
107108
{
108109
return (clause != NULL &&
109110
IsA(clause, Expr) &&
110-
((Expr *) clause)->opType == OP_EXPR);
111+
((((Expr *) clause)->opType == OP_EXPR) ||
112+
((Expr *) clause)->opType == DISTINCT_EXPR));
111113
}
112114

113115
/*
@@ -458,7 +460,7 @@ pull_agg_clause_walker(Node *node, List **listptr)
458460

459461
/*
460462
* expression_returns_set
461-
* Test whethe an expression returns a set result.
463+
* Test whether an expression returns a set result.
462464
*
463465
* Because we use expression_tree_walker(), this can also be applied to
464466
* whole targetlists; it'll produce TRUE if any one of the tlist items
@@ -482,6 +484,7 @@ expression_returns_set_walker(Node *node, void *context)
482484
switch (expr->opType)
483485
{
484486
case OP_EXPR:
487+
case DISTINCT_EXPR:
485488
if (((Oper *) expr->oper)->opretset)
486489
return true;
487490
/* else fall through to check args */
@@ -757,6 +760,7 @@ contain_mutable_functions_walker(Node *node, void *context)
757760
switch (expr->opType)
758761
{
759762
case OP_EXPR:
763+
case DISTINCT_EXPR:
760764
if (op_volatile(((Oper *) expr->oper)->opno) != PROVOLATILE_IMMUTABLE)
761765
return true;
762766
break;
@@ -806,6 +810,7 @@ contain_volatile_functions_walker(Node *node, void *context)
806810
switch (expr->opType)
807811
{
808812
case OP_EXPR:
813+
case DISTINCT_EXPR:
809814
if (op_volatile(((Oper *) expr->oper)->opno) == PROVOLATILE_VOLATILE)
810815
return true;
811816
break;
@@ -1138,7 +1143,7 @@ eval_const_expressions_mutator(Node *node, void *context)
11381143
* expression_tree_mutator directly rather than recursing to self.
11391144
*/
11401145
args = (List *) expression_tree_mutator((Node *) expr->args,
1141-
eval_const_expressions_mutator,
1146+
eval_const_expressions_mutator,
11421147
(void *) context);
11431148

11441149
switch (expr->opType)
@@ -1159,6 +1164,97 @@ eval_const_expressions_mutator(Node *node, void *context)
11591164
* args
11601165
*/
11611166
break;
1167+
case DISTINCT_EXPR:
1168+
{
1169+
List *arg;
1170+
bool has_null_input = false;
1171+
bool all_null_input = true;
1172+
bool has_nonconst_input = false;
1173+
1174+
/*
1175+
* Check for constant inputs and especially constant-NULL inputs.
1176+
*/
1177+
Assert(length(args) == 2);
1178+
foreach(arg, args)
1179+
{
1180+
if (IsA(lfirst(arg), Const))
1181+
{
1182+
has_null_input |= ((Const *) lfirst(arg))->constisnull;
1183+
all_null_input &= ((Const *) lfirst(arg))->constisnull;
1184+
}
1185+
else
1186+
{
1187+
has_nonconst_input = true;
1188+
}
1189+
}
1190+
/* all nulls? then not distinct */
1191+
if (all_null_input)
1192+
return MAKEBOOLCONST(false, false);
1193+
1194+
/* one null? then distinct */
1195+
if (has_null_input)
1196+
return MAKEBOOLCONST(true, false);
1197+
1198+
/* all constants? then optimize this out */
1199+
if (!has_nonconst_input)
1200+
{
1201+
Oid result_typeid;
1202+
int16 resultTypLen;
1203+
bool resultTypByVal;
1204+
ExprContext *econtext;
1205+
Datum const_val;
1206+
bool const_is_null;
1207+
1208+
Oper *oper = (Oper *) expr->oper;
1209+
replace_opid(oper); /* OK to scribble on input to this extent */
1210+
result_typeid = oper->opresulttype;
1211+
1212+
/*
1213+
* OK, looks like we can simplify this operator/function.
1214+
*
1215+
* We use the executor's routine ExecEvalExpr() to avoid duplication of
1216+
* code and ensure we get the same result as the executor would get.
1217+
*
1218+
* Build a new Expr node containing the already-simplified arguments. The
1219+
* only other setup needed here is the replace_opid() that we already
1220+
* did for the OP_EXPR case.
1221+
*/
1222+
newexpr = makeNode(Expr);
1223+
newexpr->typeOid = expr->typeOid;
1224+
newexpr->opType = expr->opType;
1225+
newexpr->oper = expr->oper;
1226+
newexpr->args = args;
1227+
1228+
/* Get info needed about result datatype */
1229+
get_typlenbyval(result_typeid, &resultTypLen, &resultTypByVal);
1230+
1231+
/*
1232+
* It is OK to pass a dummy econtext because none of the
1233+
* ExecEvalExpr() code used in this situation will use econtext. That
1234+
* might seem fortuitous, but it's not so unreasonable --- a constant
1235+
* expression does not depend on context, by definition, n'est ce pas?
1236+
*/
1237+
econtext = MakeExprContext(NULL, CurrentMemoryContext);
1238+
1239+
const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
1240+
&const_is_null, NULL);
1241+
1242+
/* Must copy result out of sub-context used by expression eval */
1243+
if (!const_is_null)
1244+
const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
1245+
1246+
FreeExprContext(econtext);
1247+
pfree(newexpr);
1248+
1249+
/*
1250+
* Make the constant result node.
1251+
*/
1252+
return (Node *) makeConst(result_typeid, resultTypLen,
1253+
const_val, const_is_null,
1254+
resultTypByVal, false, false);
1255+
}
1256+
break;
1257+
}
11621258
case OR_EXPR:
11631259
{
11641260

0 commit comments

Comments
 (0)