From: Tom Lane Date: Sun, 26 Jun 2005 22:05:42 +0000 (+0000) Subject: Add Oracle-compatible GREATEST and LEAST functions. Pavel Stehule X-Git-Tag: REL8_1_0BETA1~461 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=943b396245bd699a66c894c5e11303b3ef93ac7b;p=postgresql Add Oracle-compatible GREATEST and LEAST functions. Pavel Stehule --- diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 8c7a48308e..d7430f1ccf 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ @@ -6901,6 +6901,39 @@ SELECT NULLIF(value, '(none)') ... + + <literal>GREATEST</literal> and <literal>LEAST</literal> + + + GREATEST + + + LEAST + + + +GREATEST(value , ...) + + +LEAST(value , ...) + + + + The GREATEST and LEAST functions select the + largest or smallest value from a list of any number of expressions. + The expressions must all be convertible to a common data type, which + will be the type of the result + (see for details). NULL values + in the list are ignored. The result will be NULL only if all the + expressions evaluate to NULL. + + + + Note that GREATEST and LEAST are not in + the SQL standard, but are a common extension. + + + diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index ae0e3aea4d..86e0561acc 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -1,5 +1,5 @@ @@ -120,7 +120,7 @@ with, and perhaps converted to, the types of the target columns. -UNION, CASE, and ARRAY constructs +UNION, CASE, and related constructs @@ -129,7 +129,8 @@ must appear in a single set of columns, the types of the results of each SELECT clause must be matched up and converted to a uniform set. Similarly, the result expressions of a CASE construct must be converted to a common type so that the CASE expression as a whole -has a known output type. The same holds for ARRAY constructs. +has a known output type. The same holds for ARRAY constructs, +and for the GREATEST and LEAST functions. @@ -782,7 +783,7 @@ padding spaces. -<literal>UNION</literal>, <literal>CASE</literal>, and <literal>ARRAY</literal> Constructs +<literal>UNION</literal>, <literal>CASE</literal>, and Related Constructs UNION @@ -799,20 +800,31 @@ padding spaces. determination of result type + + GREATEST + determination of result type + + + + LEAST + determination of result type + + SQL UNION constructs must match up possibly dissimilar types to become a single result set. The resolution algorithm is applied separately to each output column of a union query. The INTERSECT and EXCEPT constructs resolve dissimilar types in the same way as UNION. The -CASE and ARRAY constructs use the identical +CASE, ARRAY, GREATEST and +LEAST constructs use the identical algorithm to match up their component expressions and select a result data type. -<literal>UNION</literal>, <literal>CASE</literal>, and -<literal>ARRAY</literal> Type Resolution +Type Resolution for <literal>UNION</literal>, <literal>CASE</literal>, +and Related Constructs diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 1cbe70571a..87fcf53bf0 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.179 2005/05/12 20:41:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.180 2005/06/26 22:05:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -105,6 +105,9 @@ static Datum ExecEvalRow(RowExprState *rstate, static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -2247,6 +2250,63 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, return (Datum) 0; } +/* ---------------------------------------------------------------- + * ExecEvalMinMax + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + Datum result = (Datum) 0; + MinMaxOp op = ((MinMaxExpr *) minmaxExpr->xprstate.expr)->op; + FunctionCallInfoData locfcinfo; + ListCell *arg; + + if (isDone) + *isDone = ExprSingleResult; + *isNull = true; /* until we get a result */ + + InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, NULL, NULL); + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + + foreach(arg, minmaxExpr->args) + { + ExprState *e = (ExprState *) lfirst(arg); + Datum value; + bool valueIsNull; + int32 cmpresult; + + value = ExecEvalExpr(e, econtext, &valueIsNull, NULL); + if (valueIsNull) + continue; /* ignore NULL inputs */ + + if (*isNull) + { + /* first nonnull input, adopt value */ + result = value; + *isNull = false; + } + else + { + /* apply comparison function */ + locfcinfo.arg[0] = result; + locfcinfo.arg[1] = value; + locfcinfo.isnull = false; + cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); + if (locfcinfo.isnull) /* probably should not happen */ + continue; + if (cmpresult > 0 && op == IS_LEAST) + result = value; + else if (cmpresult < 0 && op == IS_GREATEST) + result = value; + } + } + + return result; +} + /* ---------------------------------------------------------------- * ExecEvalNullIf * @@ -3206,6 +3266,36 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) cstate; } break; + case T_MinMaxExpr: + { + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + MinMaxExprState *mstate = makeNode(MinMaxExprState); + List *outlist = NIL; + ListCell *l; + TypeCacheEntry *typentry; + + mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax; + foreach(l, minmaxexpr->args) + { + Expr *e = (Expr *) lfirst(l); + ExprState *estate; + + estate = ExecInitExpr(e, parent); + outlist = lappend(outlist, estate); + } + mstate->args = outlist; + /* Look up the btree comparison function for the datatype */ + typentry = lookup_type_cache(minmaxexpr->minmaxtype, + TYPECACHE_CMP_PROC); + if (!OidIsValid(typentry->cmp_proc)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify a comparison function for type %s", + format_type_be(minmaxexpr->minmaxtype)))); + fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); + state = (ExprState *) mstate; + } + break; case T_NullIfExpr: { NullIfExpr *nullifexpr = (NullIfExpr *) node; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 2ce9edac70..b6885f0d14 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.308 2005/06/22 21:14:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.309 2005/06/26 22:05:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1047,6 +1047,21 @@ _copyCoalesceExpr(CoalesceExpr *from) return newnode; } +/* + * _copyMinMaxExpr + */ +static MinMaxExpr * +_copyMinMaxExpr(MinMaxExpr *from) +{ + MinMaxExpr *newnode = makeNode(MinMaxExpr); + + COPY_SCALAR_FIELD(minmaxtype); + COPY_SCALAR_FIELD(op); + COPY_NODE_FIELD(args); + + return newnode; +} + /* * _copyNullIfExpr (same as OpExpr) */ @@ -2805,6 +2820,9 @@ copyObject(void *from) case T_CoalesceExpr: retval = _copyCoalesceExpr(from); break; + case T_MinMaxExpr: + retval = _copyMinMaxExpr(from); + break; case T_NullIfExpr: retval = _copyNullIfExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index e66ac81b75..31a2c30224 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.245 2005/06/22 21:14:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.246 2005/06/26 22:05:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -450,6 +450,16 @@ _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) return true; } +static bool +_equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b) +{ + COMPARE_SCALAR_FIELD(minmaxtype); + COMPARE_SCALAR_FIELD(op); + COMPARE_NODE_FIELD(args); + + return true; +} + static bool _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) { @@ -1868,6 +1878,9 @@ equal(void *a, void *b) case T_CoalesceExpr: retval = _equalCoalesceExpr(a, b); break; + case T_MinMaxExpr: + retval = _equalMinMaxExpr(a, b); + break; case T_NullIfExpr: retval = _equalNullIfExpr(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2be5e1d98f..91705123bd 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.255 2005/06/09 04:18:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.256 2005/06/26 22:05:37 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -864,6 +864,16 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node) WRITE_NODE_FIELD(args); } +static void +_outMinMaxExpr(StringInfo str, MinMaxExpr *node) +{ + WRITE_NODE_TYPE("MINMAX"); + + WRITE_OID_FIELD(minmaxtype); + WRITE_ENUM_FIELD(op, MinMaxOp); + WRITE_NODE_FIELD(args); +} + static void _outNullIfExpr(StringInfo str, NullIfExpr *node) { @@ -1896,6 +1906,9 @@ _outNode(StringInfo str, void *obj) case T_CoalesceExpr: _outCoalesceExpr(str, obj); break; + case T_MinMaxExpr: + _outMinMaxExpr(str, obj); + break; case T_NullIfExpr: _outNullIfExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e01646a71d..9b27dc478e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.178 2005/06/05 22:32:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.179 2005/06/26 22:05:37 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -658,6 +658,21 @@ _readCoalesceExpr(void) READ_DONE(); } +/* + * _readMinMaxExpr + */ +static MinMaxExpr * +_readMinMaxExpr(void) +{ + READ_LOCALS(MinMaxExpr); + + READ_OID_FIELD(minmaxtype); + READ_ENUM_FIELD(op, MinMaxOp); + READ_NODE_FIELD(args); + + READ_DONE(); +} + /* * _readNullIfExpr */ @@ -982,6 +997,8 @@ parseNodeString(void) return_value = _readRowExpr(); else if (MATCH("COALESCE", 8)) return_value = _readCoalesceExpr(); + else if (MATCH("MINMAX", 6)) + return_value = _readMinMaxExpr(); else if (MATCH("NULLIFEXPR", 10)) return_value = _readNullIfExpr(); else if (MATCH("NULLTEST", 8)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 5663cce6e8..38291be27a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.198 2005/06/05 22:32:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.199 2005/06/26 22:05:38 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -542,6 +542,8 @@ expression_returns_set_walker(Node *node, void *context) return false; if (IsA(node, CoalesceExpr)) return false; + if (IsA(node, MinMaxExpr)) + return false; if (IsA(node, NullIfExpr)) return false; @@ -847,6 +849,8 @@ contain_nonstrict_functions_walker(Node *node, void *context) return true; if (IsA(node, CoalesceExpr)) return true; + if (IsA(node, MinMaxExpr)) + return true; if (IsA(node, NullIfExpr)) return true; if (IsA(node, NullTest)) @@ -1685,7 +1689,7 @@ eval_const_expressions_mutator(Node *node, newargs = lappend(newargs, newcasewhen); continue; } - + /* * Found a TRUE condition, so none of the remaining alternatives * can be reached. We treat the result as the default result. @@ -2932,6 +2936,8 @@ expression_tree_walker(Node *node, return walker(((RowExpr *) node)->args, context); case T_CoalesceExpr: return walker(((CoalesceExpr *) node)->args, context); + case T_MinMaxExpr: + return walker(((MinMaxExpr *) node)->args, context); case T_NullIfExpr: return walker(((NullIfExpr *) node)->args, context); case T_NullTest: @@ -3392,6 +3398,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_MinMaxExpr: + { + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + MinMaxExpr *newnode; + + FLATCOPY(newnode, minmaxexpr, MinMaxExpr); + MUTATE(newnode->args, minmaxexpr->args, List *); + return (Node *) newnode; + } + break; case T_NullIfExpr: { NullIfExpr *expr = (NullIfExpr *) node; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4ad61ab409..28e1ac0264 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.497 2005/06/24 14:28:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.498 2005/06/26 22:05:38 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -360,7 +360,7 @@ static void doNegateFloat(Value *v); FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION - GLOBAL GRANT GROUP_P + GLOBAL GRANT GREATEST GROUP_P HANDLER HAVING HEADER HOLD HOUR_P @@ -373,8 +373,8 @@ static void doNegateFloat(Value *v); KEY - LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT - LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION + LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL + LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE @@ -1154,7 +1154,7 @@ AlterTableStmt: AlterTableStmt *n = makeNode(AlterTableStmt); n->relation = $3; n->cmds = $4; - n->relkind = OBJECT_TABLE; + n->relkind = OBJECT_TABLE; $$ = (Node *)n; } | ALTER INDEX relation_expr alter_rel_cmds @@ -3821,7 +3821,7 @@ opt_column: COLUMN { $$ = COLUMN; } /***************************************************************************** * - * ALTER THING name OWNER TO newname. + * ALTER THING name OWNER TO newname. * *****************************************************************************/ @@ -6373,7 +6373,7 @@ a_expr: c_expr { $$ = $1; } (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6)), (Node *) makeA_Expr(AEXPR_AND, NIL, (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6), - (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4))); + (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4))); } | a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN { @@ -6383,7 +6383,7 @@ a_expr: c_expr { $$ = $1; } (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7)), (Node *) makeA_Expr(AEXPR_OR, NIL, (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7), - (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5))); + (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5))); } | a_expr IN_P in_expr { @@ -7065,6 +7065,20 @@ func_expr: func_name '(' ')' c->args = $3; $$ = (Node *)c; } + | GREATEST '(' expr_list ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->args = $3; + v->op = IS_GREATEST; + $$ = (Node *)v; + } + | LEAST '(' expr_list ')' + { + MinMaxExpr *v = makeNode(MinMaxExpr); + v->args = $3; + v->op = IS_LEAST; + $$ = (Node *)v; + } ; /* @@ -7944,10 +7958,12 @@ col_name_keyword: | EXISTS | EXTRACT | FLOAT_P + | GREATEST | INOUT | INT_P | INTEGER | INTERVAL + | LEAST | NATIONAL | NCHAR | NONE diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 009cc2e7ba..41e45bc747 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.158 2005/06/22 21:14:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.159 2005/06/26 22:05:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -146,6 +146,7 @@ static const ScanKeyword ScanKeywords[] = { {"function", FUNCTION}, {"global", GLOBAL}, {"grant", GRANT}, + {"greatest", GREATEST}, {"group", GROUP_P}, {"handler", HANDLER}, {"having", HAVING}, @@ -184,6 +185,7 @@ static const ScanKeyword ScanKeywords[] = { {"large", LARGE_P}, {"last", LAST_P}, {"leading", LEADING}, + {"least", LEAST}, {"left", LEFT}, {"level", LEVEL}, {"like", LIKE}, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 6d5f21b52e..fdb4c4dcf2 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.183 2005/06/04 20:56:13 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.184 2005/06/26 22:05:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ static Node *transformSubLink(ParseState *pstate, SubLink *sublink); static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a); static Node *transformRowExpr(ParseState *pstate, RowExpr *r); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); +static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, @@ -209,6 +210,10 @@ transformExpr(ParseState *pstate, Node *expr) result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr); break; + case T_MinMaxExpr: + result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr); + break; + case T_NullTest: { NullTest *n = (NullTest *) expr; @@ -1229,6 +1234,44 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) return (Node *) newc; } +static Node * +transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) +{ + MinMaxExpr *newm = makeNode(MinMaxExpr); + List *newargs = NIL; + List *newcoercedargs = NIL; + List *typeids = NIL; + ListCell *args; + + newm->op = m->op; + foreach(args, m->args) + { + Node *e = (Node *) lfirst(args); + Node *newe; + + newe = transformExpr(pstate, e); + newargs = lappend(newargs, newe); + typeids = lappend_oid(typeids, exprType(newe)); + } + + newm->minmaxtype = select_common_type(typeids, "GREATEST/LEAST"); + + /* Convert arguments if necessary */ + foreach(args, newargs) + { + Node *e = (Node *) lfirst(args); + Node *newe; + + newe = coerce_to_common_type(pstate, e, + newm->minmaxtype, + "GREATEST/LEAST"); + newcoercedargs = lappend(newcoercedargs, newe); + } + + newm->args = newcoercedargs; + return (Node *) newm; +} + static Node * transformBooleanTest(ParseState *pstate, BooleanTest *b) { @@ -1503,6 +1546,9 @@ exprType(Node *expr) case T_CoalesceExpr: type = ((CoalesceExpr *) expr)->coalescetype; break; + case T_MinMaxExpr: + type = ((MinMaxExpr *) expr)->minmaxtype; + break; case T_NullIfExpr: type = exprType((Node *) linitial(((NullIfExpr *) expr)->args)); break; @@ -1637,6 +1683,30 @@ exprTypmod(Node *expr) return typmod; } break; + case T_MinMaxExpr: + { + /* + * If all the alternatives agree on type/typmod, return + * that typmod, else use -1 + */ + MinMaxExpr *mexpr = (MinMaxExpr *) expr; + Oid minmaxtype = mexpr->minmaxtype; + int32 typmod; + ListCell *arg; + + typmod = exprTypmod((Node *) linitial(mexpr->args)); + foreach(arg, mexpr->args) + { + Node *e = (Node *) lfirst(arg); + + if (exprType(e) != minmaxtype) + return -1; + if (exprTypmod(e) != typmod) + return -1; + } + return typmod; + } + break; case T_NullIfExpr: { NullIfExpr *nexpr = (NullIfExpr *) expr; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index dd2c0b4e31..00185a05e1 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.136 2005/06/05 00:38:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.137 2005/06/26 22:05:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1123,6 +1123,18 @@ FigureColnameInternal(Node *node, char **name) /* make coalesce() act like a regular function */ *name = "coalesce"; return 2; + case T_MinMaxExpr: + /* make greatest/least act like a regular function */ + switch (((MinMaxExpr*) node)->op) + { + case IS_GREATEST: + *name = "greatest"; + return 2; + case IS_LEAST: + *name = "least"; + return 2; + } + break; default: break; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index bc3e774d64..0bd1d73eae 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.200 2005/06/05 00:38:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.201 2005/06/26 22:05:40 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -2781,6 +2781,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_ArrayExpr: case T_RowExpr: case T_CoalesceExpr: + case T_MinMaxExpr: case T_NullIfExpr: case T_Aggref: case T_FuncExpr: @@ -2886,10 +2887,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_BoolExpr: /* lower precedence */ case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ + case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ + case T_MinMaxExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ + case T_Aggref: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: @@ -2933,10 +2935,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) } case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ + case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ + case T_MinMaxExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ + case T_Aggref: /* own parentheses */ case T_CaseExpr: /* other separators */ return true; default: @@ -3491,6 +3494,24 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_MinMaxExpr: + { + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + + switch (minmaxexpr->op) + { + case IS_GREATEST: + appendStringInfo(buf, "GREATEST("); + break; + case IS_LEAST: + appendStringInfo(buf, "LEAST("); + break; + } + get_rule_expr((Node *) minmaxexpr->args, context, true); + appendStringInfoChar(buf, ')'); + } + break; + case T_NullIfExpr: { NullIfExpr *nullifexpr = (NullIfExpr *) node; @@ -4109,7 +4130,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) bool need_paren_on_right; need_paren_on_right = PRETTY_PAREN(context) && - !IsA(j->rarg, RangeTblRef) && + !IsA(j->rarg, RangeTblRef) && !(IsA(j->rarg, JoinExpr) && ((JoinExpr*) j->rarg)->alias != NULL); if (!PRETTY_PAREN(context) || j->alias != NULL) diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index df41c85610..46d27a56f5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.135 2005/06/20 18:37:02 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.136 2005/06/26 22:05:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -673,6 +673,17 @@ typedef struct CoalesceExprState List *args; /* the arguments */ } CoalesceExprState; +/* ---------------- + * MinMaxExprState node + * ---------------- + */ +typedef struct MinMaxExprState +{ + ExprState xprstate; + List *args; /* the arguments */ + FmgrInfo cfunc; /* lookup info for comparison func */ +} MinMaxExprState; + /* ---------------- * CoerceToDomainState node * ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index d9c98321ea..fd923f72af 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.170 2005/06/09 04:19:00 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.171 2005/06/26 22:05:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -126,6 +126,7 @@ typedef enum NodeTag T_ArrayExpr, T_RowExpr, T_CoalesceExpr, + T_MinMaxExpr, T_NullIfExpr, T_NullTest, T_BooleanTest, @@ -159,6 +160,7 @@ typedef enum NodeTag T_ArrayExprState, T_RowExprState, T_CoalesceExprState, + T_MinMaxExprState, T_CoerceToDomainState, T_DomainConstraintState, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index a1d1ef3ebf..279b79738e 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.107 2005/04/06 16:34:07 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.108 2005/06/26 22:05:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -657,6 +657,23 @@ typedef struct CoalesceExpr List *args; /* the arguments */ } CoalesceExpr; +/* + * MinMaxExpr - a GREATEST or LEAST function + */ +typedef enum MinMaxOp +{ + IS_GREATEST, + IS_LEAST +} MinMaxOp; + +typedef struct MinMaxExpr +{ + Expr xpr; + Oid minmaxtype; /* common type of arguments and result */ + MinMaxOp op; /* function to execute */ + List *args; /* the arguments */ +} MinMaxExpr; + /* * NullIfExpr - a NULLIF expression * diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 946a5b1c82..356210ccb3 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.148 2005/06/22 07:28:47 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.149 2005/06/26 22:05:42 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -4296,6 +4296,16 @@ exec_simple_check_node(Node *node) return TRUE; } + case T_MinMaxExpr: + { + MinMaxExpr *expr = (MinMaxExpr *) node; + + if (!exec_simple_check_node((Node *) expr->args)) + return FALSE; + + return TRUE; + } + case T_NullIfExpr: { NullIfExpr *expr = (NullIfExpr *) node;