summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/func.sgml35
-rw-r--r--doc/src/sgml/typeconv.sgml26
-rw-r--r--src/backend/executor/execQual.c92
-rw-r--r--src/backend/nodes/copyfuncs.c20
-rw-r--r--src/backend/nodes/equalfuncs.c15
-rw-r--r--src/backend/nodes/outfuncs.c15
-rw-r--r--src/backend/nodes/readfuncs.c19
-rw-r--r--src/backend/optimizer/util/clauses.c20
-rw-r--r--src/backend/parser/gram.y32
-rw-r--r--src/backend/parser/keywords.c4
-rw-r--r--src/backend/parser/parse_expr.c72
-rw-r--r--src/backend/parser/parse_target.c14
-rw-r--r--src/backend/utils/adt/ruleutils.c33
-rw-r--r--src/include/nodes/execnodes.h13
-rw-r--r--src/include/nodes/nodes.h4
-rw-r--r--src/include/nodes/primnodes.h19
-rw-r--r--src/pl/plpgsql/src/pl_exec.c12
17 files changed, 409 insertions, 36 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8c7a48308ec..d7430f1ccf8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.259 2005/06/24 20:53:29 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.260 2005/06/26 22:05:35 tgl Exp $
PostgreSQL documentation
-->
@@ -6901,6 +6901,39 @@ SELECT NULLIF(value, '(none)') ...
</sect2>
+ <sect2>
+ <title><literal>GREATEST</literal> and <literal>LEAST</literal></title>
+
+ <indexterm>
+ <primary>GREATEST</primary>
+ </indexterm>
+ <indexterm>
+ <primary>LEAST</primary>
+ </indexterm>
+
+<synopsis>
+<function>GREATEST</function>(<replaceable>value</replaceable> <optional>, ...</optional>)
+</synopsis>
+<synopsis>
+<function>LEAST</function>(<replaceable>value</replaceable> <optional>, ...</optional>)
+</synopsis>
+
+ <para>
+ The <function>GREATEST</> and <function>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 <xref linkend="typeconv-union-case"> for details). NULL values
+ in the list are ignored. The result will be NULL only if all the
+ expressions evaluate to NULL.
+ </para>
+
+ <para>
+ Note that <function>GREATEST</> and <function>LEAST</> are not in
+ the SQL standard, but are a common extension.
+ </para>
+ </sect2>
+
</sect1>
diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml
index ae0e3aea4d7..86e0561acc0 100644
--- a/doc/src/sgml/typeconv.sgml
+++ b/doc/src/sgml/typeconv.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.43 2004/12/23 23:07:38 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.44 2005/06/26 22:05:36 tgl Exp $
-->
<chapter Id="typeconv">
@@ -120,7 +120,7 @@ with, and perhaps converted to, the types of the target columns.
</varlistentry>
<varlistentry>
<term>
-<literal>UNION</literal>, <literal>CASE</literal>, and <literal>ARRAY</literal> constructs
+<literal>UNION</literal>, <literal>CASE</literal>, and related constructs
</term>
<listitem>
<para>
@@ -129,7 +129,8 @@ must appear in a single set of columns, the types of the results of each
<command>SELECT</> clause must be matched up and converted to a uniform set.
Similarly, the result expressions of a <literal>CASE</> construct must be
converted to a common type so that the <literal>CASE</> expression as a whole
-has a known output type. The same holds for <literal>ARRAY</> constructs.
+has a known output type. The same holds for <literal>ARRAY</> constructs,
+and for the <function>GREATEST</> and <function>LEAST</> functions.
</para>
</listitem>
</varlistentry>
@@ -782,7 +783,7 @@ padding spaces.
</sect1>
<sect1 id="typeconv-union-case">
-<title><literal>UNION</literal>, <literal>CASE</literal>, and <literal>ARRAY</literal> Constructs</title>
+<title><literal>UNION</literal>, <literal>CASE</literal>, and Related Constructs</title>
<indexterm zone="typeconv-union-case">
<primary>UNION</primary>
@@ -799,20 +800,31 @@ padding spaces.
<secondary>determination of result type</secondary>
</indexterm>
+<indexterm zone="typeconv-union-case">
+ <primary>GREATEST</primary>
+ <secondary>determination of result type</secondary>
+</indexterm>
+
+<indexterm zone="typeconv-union-case">
+ <primary>LEAST</primary>
+ <secondary>determination of result type</secondary>
+</indexterm>
+
<para>
SQL <literal>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
<literal>INTERSECT</> and <literal>EXCEPT</> constructs resolve
dissimilar types in the same way as <literal>UNION</>. The
-<literal>CASE</> and <literal>ARRAY</> constructs use the identical
+<literal>CASE</>, <literal>ARRAY</>, <function>GREATEST</> and
+<function>LEAST</> constructs use the identical
algorithm to match up their component expressions and select a result
data type.
</para>
<procedure>
-<title><literal>UNION</literal>, <literal>CASE</literal>, and
-<literal>ARRAY</literal> Type Resolution</title>
+<title>Type Resolution for <literal>UNION</literal>, <literal>CASE</literal>,
+and Related Constructs</title>
<step performance="required">
<para>
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 1cbe70571a9..87fcf53bf05 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);
@@ -2248,6 +2251,63 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
}
/* ----------------------------------------------------------------
+ * 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
*
* Note that this is *always* derived from the equals operator,
@@ -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 2ce9edac707..b6885f0d149 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 $
*
*-------------------------------------------------------------------------
*/
@@ -1048,6 +1048,21 @@ _copyCoalesceExpr(CoalesceExpr *from)
}
/*
+ * _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)
*/
static NullIfExpr *
@@ -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 e66ac81b755..31a2c302244 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 $
*
*-------------------------------------------------------------------------
*/
@@ -451,6 +451,16 @@ _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
}
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)
{
COMPARE_SCALAR_FIELD(opno);
@@ -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 2be5e1d98f6..91705123bd7 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*
@@ -865,6 +865,16 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
}
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)
{
WRITE_NODE_TYPE("NULLIFEXPR");
@@ -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 e01646a71d1..9b27dc478ef 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
@@ -659,6 +659,21 @@ _readCoalesceExpr(void)
}
/*
+ * _readMinMaxExpr
+ */
+static MinMaxExpr *
+_readMinMaxExpr(void)
+{
+ READ_LOCALS(MinMaxExpr);
+
+ READ_OID_FIELD(minmaxtype);
+ READ_ENUM_FIELD(op, MinMaxOp);
+ READ_NODE_FIELD(args);
+
+ READ_DONE();
+}
+
+/*
* _readNullIfExpr
*/
static NullIfExpr *
@@ -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 5663cce6e8d..38291be27af 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 4ad61ab409a..28e1ac0264d 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 009cc2e7ba6..41e45bc7475 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 6d5f21b52ef..fdb4c4dcf25 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;
@@ -1230,6 +1235,44 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
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)
{
const char *clausename;
@@ -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 dd2c0b4e31c..00185a05e12 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 bc3e774d640..0bd1d73eae1 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 df41c856108..46d27a56f53 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 $
*
*-------------------------------------------------------------------------
*/
@@ -674,6 +674,17 @@ typedef struct CoalesceExprState
} 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 d9c98321ea3..fd923f72af4 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 a1d1ef3ebf0..279b79738ef 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 $
*
*-------------------------------------------------------------------------
*/
@@ -658,6 +658,23 @@ typedef struct CoalesceExpr
} 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
*
* Like DistinctExpr, this is represented the same as an OpExpr referencing
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 946a5b1c82a..356210ccb36 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;