Commit 943b3962 authored by Tom Lane's avatar Tom Lane

Add Oracle-compatible GREATEST and LEAST functions. Pavel Stehule

parent d395aecf
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -6901,6 +6901,39 @@ SELECT NULLIF(value, '(none)') ... ...@@ -6901,6 +6901,39 @@ SELECT NULLIF(value, '(none)') ...
</sect2> </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> </sect1>
......
<!-- <!--
$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"> <chapter Id="typeconv">
...@@ -120,7 +120,7 @@ with, and perhaps converted to, the types of the target columns. ...@@ -120,7 +120,7 @@ with, and perhaps converted to, the types of the target columns.
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<literal>UNION</literal>, <literal>CASE</literal>, and <literal>ARRAY</literal> constructs <literal>UNION</literal>, <literal>CASE</literal>, and related constructs
</term> </term>
<listitem> <listitem>
<para> <para>
...@@ -129,7 +129,8 @@ must appear in a single set of columns, the types of the results of each ...@@ -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. <command>SELECT</> clause must be matched up and converted to a uniform set.
Similarly, the result expressions of a <literal>CASE</> construct must be 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 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -782,7 +783,7 @@ padding spaces. ...@@ -782,7 +783,7 @@ padding spaces.
</sect1> </sect1>
<sect1 id="typeconv-union-case"> <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"> <indexterm zone="typeconv-union-case">
<primary>UNION</primary> <primary>UNION</primary>
...@@ -799,20 +800,31 @@ padding spaces. ...@@ -799,20 +800,31 @@ padding spaces.
<secondary>determination of result type</secondary> <secondary>determination of result type</secondary>
</indexterm> </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> <para>
SQL <literal>UNION</> constructs must match up possibly dissimilar SQL <literal>UNION</> constructs must match up possibly dissimilar
types to become a single result set. The resolution algorithm is types to become a single result set. The resolution algorithm is
applied separately to each output column of a union query. The applied separately to each output column of a union query. The
<literal>INTERSECT</> and <literal>EXCEPT</> constructs resolve <literal>INTERSECT</> and <literal>EXCEPT</> constructs resolve
dissimilar types in the same way as <literal>UNION</>. The 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 algorithm to match up their component expressions and select a result
data type. data type.
</para> </para>
<procedure> <procedure>
<title><literal>UNION</literal>, <literal>CASE</literal>, and <title>Type Resolution for <literal>UNION</literal>, <literal>CASE</literal>,
<literal>ARRAY</literal> Type Resolution</title> and Related Constructs</title>
<step performance="required"> <step performance="required">
<para> <para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -105,6 +105,9 @@ static Datum ExecEvalRow(RowExprState *rstate,
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
...@@ -2247,6 +2250,63 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, ...@@ -2247,6 +2250,63 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
return (Datum) 0; 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 * ExecEvalNullIf
* *
...@@ -3206,6 +3266,36 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -3206,6 +3266,36 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) cstate; state = (ExprState *) cstate;
} }
break; 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: case T_NullIfExpr:
{ {
NullIfExpr *nullifexpr = (NullIfExpr *) node; NullIfExpr *nullifexpr = (NullIfExpr *) node;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1047,6 +1047,21 @@ _copyCoalesceExpr(CoalesceExpr *from)
return newnode; 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) * _copyNullIfExpr (same as OpExpr)
*/ */
...@@ -2805,6 +2820,9 @@ copyObject(void *from) ...@@ -2805,6 +2820,9 @@ copyObject(void *from)
case T_CoalesceExpr: case T_CoalesceExpr:
retval = _copyCoalesceExpr(from); retval = _copyCoalesceExpr(from);
break; break;
case T_MinMaxExpr:
retval = _copyMinMaxExpr(from);
break;
case T_NullIfExpr: case T_NullIfExpr:
retval = _copyNullIfExpr(from); retval = _copyNullIfExpr(from);
break; break;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -450,6 +450,16 @@ _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
return true; 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 static bool
_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b) _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
{ {
...@@ -1868,6 +1878,9 @@ equal(void *a, void *b) ...@@ -1868,6 +1878,9 @@ equal(void *a, void *b)
case T_CoalesceExpr: case T_CoalesceExpr:
retval = _equalCoalesceExpr(a, b); retval = _equalCoalesceExpr(a, b);
break; break;
case T_MinMaxExpr:
retval = _equalMinMaxExpr(a, b);
break;
case T_NullIfExpr: case T_NullIfExpr:
retval = _equalNullIfExpr(a, b); retval = _equalNullIfExpr(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -864,6 +864,16 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node) ...@@ -864,6 +864,16 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
WRITE_NODE_FIELD(args); 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 static void
_outNullIfExpr(StringInfo str, NullIfExpr *node) _outNullIfExpr(StringInfo str, NullIfExpr *node)
{ {
...@@ -1896,6 +1906,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1896,6 +1906,9 @@ _outNode(StringInfo str, void *obj)
case T_CoalesceExpr: case T_CoalesceExpr:
_outCoalesceExpr(str, obj); _outCoalesceExpr(str, obj);
break; break;
case T_MinMaxExpr:
_outMinMaxExpr(str, obj);
break;
case T_NullIfExpr: case T_NullIfExpr:
_outNullIfExpr(str, obj); _outNullIfExpr(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -658,6 +658,21 @@ _readCoalesceExpr(void) ...@@ -658,6 +658,21 @@ _readCoalesceExpr(void)
READ_DONE(); 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 * _readNullIfExpr
*/ */
...@@ -982,6 +997,8 @@ parseNodeString(void) ...@@ -982,6 +997,8 @@ parseNodeString(void)
return_value = _readRowExpr(); return_value = _readRowExpr();
else if (MATCH("COALESCE", 8)) else if (MATCH("COALESCE", 8))
return_value = _readCoalesceExpr(); return_value = _readCoalesceExpr();
else if (MATCH("MINMAX", 6))
return_value = _readMinMaxExpr();
else if (MATCH("NULLIFEXPR", 10)) else if (MATCH("NULLIFEXPR", 10))
return_value = _readNullIfExpr(); return_value = _readNullIfExpr();
else if (MATCH("NULLTEST", 8)) else if (MATCH("NULLTEST", 8))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -542,6 +542,8 @@ expression_returns_set_walker(Node *node, void *context) ...@@ -542,6 +542,8 @@ expression_returns_set_walker(Node *node, void *context)
return false; return false;
if (IsA(node, CoalesceExpr)) if (IsA(node, CoalesceExpr))
return false; return false;
if (IsA(node, MinMaxExpr))
return false;
if (IsA(node, NullIfExpr)) if (IsA(node, NullIfExpr))
return false; return false;
...@@ -847,6 +849,8 @@ contain_nonstrict_functions_walker(Node *node, void *context) ...@@ -847,6 +849,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
return true; return true;
if (IsA(node, CoalesceExpr)) if (IsA(node, CoalesceExpr))
return true; return true;
if (IsA(node, MinMaxExpr))
return true;
if (IsA(node, NullIfExpr)) if (IsA(node, NullIfExpr))
return true; return true;
if (IsA(node, NullTest)) if (IsA(node, NullTest))
...@@ -1685,7 +1689,7 @@ eval_const_expressions_mutator(Node *node, ...@@ -1685,7 +1689,7 @@ eval_const_expressions_mutator(Node *node,
newargs = lappend(newargs, newcasewhen); newargs = lappend(newargs, newcasewhen);
continue; continue;
} }
/* /*
* Found a TRUE condition, so none of the remaining alternatives * Found a TRUE condition, so none of the remaining alternatives
* can be reached. We treat the result as the default result. * can be reached. We treat the result as the default result.
...@@ -2932,6 +2936,8 @@ expression_tree_walker(Node *node, ...@@ -2932,6 +2936,8 @@ expression_tree_walker(Node *node,
return walker(((RowExpr *) node)->args, context); return walker(((RowExpr *) node)->args, context);
case T_CoalesceExpr: case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context); return walker(((CoalesceExpr *) node)->args, context);
case T_MinMaxExpr:
return walker(((MinMaxExpr *) node)->args, context);
case T_NullIfExpr: case T_NullIfExpr:
return walker(((NullIfExpr *) node)->args, context); return walker(((NullIfExpr *) node)->args, context);
case T_NullTest: case T_NullTest:
...@@ -3392,6 +3398,16 @@ expression_tree_mutator(Node *node, ...@@ -3392,6 +3398,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; 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: case T_NullIfExpr:
{ {
NullIfExpr *expr = (NullIfExpr *) node; NullIfExpr *expr = (NullIfExpr *) node;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -360,7 +360,7 @@ static void doNegateFloat(Value *v); ...@@ -360,7 +360,7 @@ static void doNegateFloat(Value *v);
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
FREEZE FROM FULL FUNCTION FREEZE FROM FULL FUNCTION
GLOBAL GRANT GROUP_P GLOBAL GRANT GREATEST GROUP_P
HANDLER HAVING HEADER HOLD HOUR_P HANDLER HAVING HEADER HOLD HOUR_P
...@@ -373,8 +373,8 @@ static void doNegateFloat(Value *v); ...@@ -373,8 +373,8 @@ static void doNegateFloat(Value *v);
KEY KEY
LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL
LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
LOCK_P LOCK_P
MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
...@@ -1154,7 +1154,7 @@ AlterTableStmt: ...@@ -1154,7 +1154,7 @@ AlterTableStmt:
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableStmt *n = makeNode(AlterTableStmt);
n->relation = $3; n->relation = $3;
n->cmds = $4; n->cmds = $4;
n->relkind = OBJECT_TABLE; n->relkind = OBJECT_TABLE;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER INDEX relation_expr alter_rel_cmds | ALTER INDEX relation_expr alter_rel_cmds
...@@ -3821,7 +3821,7 @@ opt_column: COLUMN { $$ = COLUMN; } ...@@ -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; } ...@@ -6373,7 +6373,7 @@ a_expr: c_expr { $$ = $1; }
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6)), (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6)),
(Node *) makeA_Expr(AEXPR_AND, NIL, (Node *) makeA_Expr(AEXPR_AND, NIL,
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6), (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 | a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
{ {
...@@ -6383,7 +6383,7 @@ a_expr: c_expr { $$ = $1; } ...@@ -6383,7 +6383,7 @@ a_expr: c_expr { $$ = $1; }
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7)), (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7)),
(Node *) makeA_Expr(AEXPR_OR, NIL, (Node *) makeA_Expr(AEXPR_OR, NIL,
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7), (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 | a_expr IN_P in_expr
{ {
...@@ -7065,6 +7065,20 @@ func_expr: func_name '(' ')' ...@@ -7065,6 +7065,20 @@ func_expr: func_name '(' ')'
c->args = $3; c->args = $3;
$$ = (Node *)c; $$ = (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: ...@@ -7944,10 +7958,12 @@ col_name_keyword:
| EXISTS | EXISTS
| EXTRACT | EXTRACT
| FLOAT_P | FLOAT_P
| GREATEST
| INOUT | INOUT
| INT_P | INT_P
| INTEGER | INTEGER
| INTERVAL | INTERVAL
| LEAST
| NATIONAL | NATIONAL
| NCHAR | NCHAR
| NONE | NONE
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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[] = { ...@@ -146,6 +146,7 @@ static const ScanKeyword ScanKeywords[] = {
{"function", FUNCTION}, {"function", FUNCTION},
{"global", GLOBAL}, {"global", GLOBAL},
{"grant", GRANT}, {"grant", GRANT},
{"greatest", GREATEST},
{"group", GROUP_P}, {"group", GROUP_P},
{"handler", HANDLER}, {"handler", HANDLER},
{"having", HAVING}, {"having", HAVING},
...@@ -184,6 +185,7 @@ static const ScanKeyword ScanKeywords[] = { ...@@ -184,6 +185,7 @@ static const ScanKeyword ScanKeywords[] = {
{"large", LARGE_P}, {"large", LARGE_P},
{"last", LAST_P}, {"last", LAST_P},
{"leading", LEADING}, {"leading", LEADING},
{"least", LEAST},
{"left", LEFT}, {"left", LEFT},
{"level", LEVEL}, {"level", LEVEL},
{"like", LIKE}, {"like", LIKE},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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); ...@@ -53,6 +53,7 @@ static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a); static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r); static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
...@@ -209,6 +210,10 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -209,6 +210,10 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr); result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr);
break; break;
case T_MinMaxExpr:
result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr);
break;
case T_NullTest: case T_NullTest:
{ {
NullTest *n = (NullTest *) expr; NullTest *n = (NullTest *) expr;
...@@ -1229,6 +1234,44 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) ...@@ -1229,6 +1234,44 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
return (Node *) newc; 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 * static Node *
transformBooleanTest(ParseState *pstate, BooleanTest *b) transformBooleanTest(ParseState *pstate, BooleanTest *b)
{ {
...@@ -1503,6 +1546,9 @@ exprType(Node *expr) ...@@ -1503,6 +1546,9 @@ exprType(Node *expr)
case T_CoalesceExpr: case T_CoalesceExpr:
type = ((CoalesceExpr *) expr)->coalescetype; type = ((CoalesceExpr *) expr)->coalescetype;
break; break;
case T_MinMaxExpr:
type = ((MinMaxExpr *) expr)->minmaxtype;
break;
case T_NullIfExpr: case T_NullIfExpr:
type = exprType((Node *) linitial(((NullIfExpr *) expr)->args)); type = exprType((Node *) linitial(((NullIfExpr *) expr)->args));
break; break;
...@@ -1637,6 +1683,30 @@ exprTypmod(Node *expr) ...@@ -1637,6 +1683,30 @@ exprTypmod(Node *expr)
return typmod; return typmod;
} }
break; 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: case T_NullIfExpr:
{ {
NullIfExpr *nexpr = (NullIfExpr *) expr; NullIfExpr *nexpr = (NullIfExpr *) expr;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1123,6 +1123,18 @@ FigureColnameInternal(Node *node, char **name)
/* make coalesce() act like a regular function */ /* make coalesce() act like a regular function */
*name = "coalesce"; *name = "coalesce";
return 2; 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: default:
break; break;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -2781,6 +2781,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2781,6 +2781,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_ArrayExpr: case T_ArrayExpr:
case T_RowExpr: case T_RowExpr:
case T_CoalesceExpr: case T_CoalesceExpr:
case T_MinMaxExpr:
case T_NullIfExpr: case T_NullIfExpr:
case T_Aggref: case T_Aggref:
case T_FuncExpr: case T_FuncExpr:
...@@ -2886,10 +2887,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2886,10 +2887,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_BoolExpr: /* lower precedence */ case T_BoolExpr: /* lower precedence */
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
return true; return true;
default: default:
...@@ -2933,10 +2935,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2933,10 +2935,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
} }
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */ case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_MinMaxExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
case T_CaseExpr: /* other separators */ case T_CaseExpr: /* other separators */
return true; return true;
default: default:
...@@ -3491,6 +3494,24 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3491,6 +3494,24 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; 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: case T_NullIfExpr:
{ {
NullIfExpr *nullifexpr = (NullIfExpr *) node; NullIfExpr *nullifexpr = (NullIfExpr *) node;
...@@ -4109,7 +4130,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -4109,7 +4130,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
bool need_paren_on_right; bool need_paren_on_right;
need_paren_on_right = PRETTY_PAREN(context) && need_paren_on_right = PRETTY_PAREN(context) &&
!IsA(j->rarg, RangeTblRef) && !IsA(j->rarg, RangeTblRef) &&
!(IsA(j->rarg, JoinExpr) && ((JoinExpr*) j->rarg)->alias != NULL); !(IsA(j->rarg, JoinExpr) && ((JoinExpr*) j->rarg)->alias != NULL);
if (!PRETTY_PAREN(context) || j->alias != NULL) if (!PRETTY_PAREN(context) || j->alias != NULL)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -673,6 +673,17 @@ typedef struct CoalesceExprState
List *args; /* the arguments */ List *args; /* the arguments */
} CoalesceExprState; } CoalesceExprState;
/* ----------------
* MinMaxExprState node
* ----------------
*/
typedef struct MinMaxExprState
{
ExprState xprstate;
List *args; /* the arguments */
FmgrInfo cfunc; /* lookup info for comparison func */
} MinMaxExprState;
/* ---------------- /* ----------------
* CoerceToDomainState node * CoerceToDomainState node
* ---------------- * ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -126,6 +126,7 @@ typedef enum NodeTag
T_ArrayExpr, T_ArrayExpr,
T_RowExpr, T_RowExpr,
T_CoalesceExpr, T_CoalesceExpr,
T_MinMaxExpr,
T_NullIfExpr, T_NullIfExpr,
T_NullTest, T_NullTest,
T_BooleanTest, T_BooleanTest,
...@@ -159,6 +160,7 @@ typedef enum NodeTag ...@@ -159,6 +160,7 @@ typedef enum NodeTag
T_ArrayExprState, T_ArrayExprState,
T_RowExprState, T_RowExprState,
T_CoalesceExprState, T_CoalesceExprState,
T_MinMaxExprState,
T_CoerceToDomainState, T_CoerceToDomainState,
T_DomainConstraintState, T_DomainConstraintState,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -657,6 +657,23 @@ typedef struct CoalesceExpr
List *args; /* the arguments */ List *args; /* the arguments */
} 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 * NullIfExpr - a NULLIF expression
* *
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -4296,6 +4296,16 @@ exec_simple_check_node(Node *node) ...@@ -4296,6 +4296,16 @@ exec_simple_check_node(Node *node)
return TRUE; return TRUE;
} }
case T_MinMaxExpr:
{
MinMaxExpr *expr = (MinMaxExpr *) node;
if (!exec_simple_check_node((Node *) expr->args))
return FALSE;
return TRUE;
}
case T_NullIfExpr: case T_NullIfExpr:
{ {
NullIfExpr *expr = (NullIfExpr *) node; NullIfExpr *expr = (NullIfExpr *) node;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment