Hello

        This patch contains three oracle users missing functions. But I 
hope can be usefull for all PostgreSQL users (users vote me ;-) Certainly 
LEAST and GREATEST, which has not analogy. Using of DECODE is similar 
CASE, but with some differences. There exist some workarounds in plpgsql,  
but are ugly and neefective, or impossible (function DECODE rotate type of 
args). All functions share code.

David, please, can you enhance documentation?

pokus=# select least(1,2,3,4);
 least
-------
     1
(1 row)

pokus=# select greatest(1,2,3,4);
 greatest
----------
        4
(1 row)

pokus=# select decode('c','a',2,1);
 decode
--------
      1

Best regards
Pavel Stehule



diff -c -r --new-file pgsql.02/doc/src/sgml/func.sgml 
pgsql/doc/src/sgml/func.sgml
*** pgsql.02/doc/src/sgml/func.sgml     2005-06-06 15:28:59.000000000 +0200
--- pgsql/doc/src/sgml/func.sgml        2005-06-07 00:49:35.000000000 +0200
***************
*** 6805,6810 ****
--- 6805,6855 ----
    </sect2>
  
    <sect2>
+     <title><literal>DECODE</literal></title>
+     
+   <indexterm>
+    <primary>DECODE</primary>
+   </indexterm>
+ 
+ <synopsis>
+ <function>DECODE</function>(<replaceable>expr</replaceable> 
<replaceable>search, result</replaceable><optional>,<replaceable>search, 
result</replaceable></optional><optional>, default</optional>)
+ </synopsis>
+ 
+   <para>
+   The first argument to the DECODE function is the expression that you want 
to decode. 
+   First, compare the value expr to the value of search, and if the values are 
equal, 
+   DECODE returns the value result. If they're not equal, DECODE try next pair 
search, 
+   result. If there are not other pair returns NULL else last unmatched 
argumet - default
+   value. If is possible, use ANSI SQL CASE</para>
+   </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 GREATEST and LEAST functions determine the largest and smallest values 
from multiple 
+    columns or expressions.
+    </para>
+   </sect2>
+   
+       
+ 
+   <sect2>
     <title><literal>NULLIF</></title>
  
    <indexterm>
diff -c -r --new-file pgsql.02/src/backend/executor/execQual.c 
pgsql/src/backend/executor/execQual.c
*** pgsql.02/src/backend/executor/execQual.c    2005-06-06 15:29:05.000000000 
+0200
--- pgsql/src/backend/executor/execQual.c       2005-06-06 23:37:18.000000000 
+0200
***************
*** 105,110 ****
--- 105,115 ----
  static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
                                 ExprContext *econtext,
                                 bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalVarargGreatest(VarargExprState *varargExpr,
+                                ExprContext *econtext,
+                                bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalVarargDecode(VarargExprState *varargExpr, ExprContext 
*econtext,
+                                bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
                           ExprContext *econtext,
                           bool *isNull, ExprDoneCond *isDone);
***************
*** 2248,2253 ****
--- 2253,2408 ----
  }
  
  /* ----------------------------------------------------------------
+  *            ExecEvalVarargDecode
+  * ----------------------------------------------------------------
+  */
+ 
+ #define FROM_EXPR_TO_SEARCH   argtype = IS_SEARCH;
+ #define FROM_SEARCH_TO_RESULT         argtype = IS_RESULT; 
+ #define FROM_RESULT_TO_SEARCH argtype = IS_SEARCH;                        
+ 
+ typedef enum DecodeArgsType
+ {
+     IS_EXPR,
+     IS_SEARCH,
+     IS_RESULT
+ } DecodeArgsType;
+ 
+ 
+ static Datum
+ ExecEvalVarargDecode(VarargExprState *varargExpr, ExprContext *econtext,
+                                bool *isNull, ExprDoneCond *isDone)
+ {
+     ListCell *arg;
+     Datum expr;
+     FunctionCallInfoData locfcinfo;
+     TypeCacheEntry *typentry;
+     bool isNullExpr = false;
+     bool found = false;
+     DecodeArgsType argtype = IS_EXPR; 
+ 
+     if (isDone)
+       *isDone = ExprSingleResult;
+ 
+     foreach(arg, varargExpr->args)
+     {
+       Datum search;  
+         int32         cmpresult;
+         ExprState  *e = (ExprState *) lfirst(arg);
+           
+         switch (argtype)
+       {
+           case IS_EXPR:
+               expr = ExecEvalExpr(e, econtext, isNull, NULL);
+ 
+               if (*isNull)
+                   isNullExpr = true;
+               else
+               {
+                   typentry = lookup_type_cache(varargExpr->paramtype, 
TYPECACHE_CMP_PROC_FINFO);
+                   if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
+                       ereport(ERROR,
+                           (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                               errmsg("could not identify a comparison 
function for type %s",
+                                   format_type_be(varargExpr->paramtype))));
+             
+                   InitFunctionCallInfoData(locfcinfo, 
&typentry->cmp_proc_finfo, 2,
+                                              NULL, NULL);   
+                   locfcinfo.argnull[0] = false;
+                   locfcinfo.argnull[1] = false;
+                   locfcinfo.isnull = false;
+             
+                   locfcinfo.arg[0] = expr;
+               }
+               FROM_EXPR_TO_SEARCH;
+               break;
+           
+           case IS_SEARCH:
+           {
+               search = ExecEvalExpr(e, econtext, isNull, NULL);
+               if (lnext(arg) == NULL) /* Is default? */
+                   return search;
+               if (*isNull && isNullExpr) 
+                   found = true;
+               if (isNullExpr == false && *isNull == false)
+               {
+                   locfcinfo.arg[1] = search;
+                   cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+                   if (cmpresult == 0)
+                       found = true;
+               }
+               FROM_SEARCH_TO_RESULT;
+               break;
+           }
+           case IS_RESULT: /* only if is result and found */
+               if (found)
+                   return ExecEvalExpr(e, econtext, isNull, NULL);
+               FROM_RESULT_TO_SEARCH;
+               break;
+       }
+     }      
+     *isNull = true;
+     return (Datum) 0;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *            ExecEvalVarargGreatest
+  * ----------------------------------------------------------------
+  */
+ 
+ static Datum
+ ExecEvalVarargGreatest(VarargExprState *varargExpr, ExprContext *econtext,
+                                bool *isNull, ExprDoneCond *isDone)
+ {
+     ListCell *arg;
+     Datum result = (Datum) 0;
+     TypeCacheEntry *typentry;
+     FunctionCallInfoData locfcinfo;    
+ 
+     if (isDone)
+       *isDone = ExprSingleResult;
+ 
+     typentry = lookup_type_cache(varargExpr->varargtype, 
TYPECACHE_CMP_PROC_FINFO);
+     
+     if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
+         ereport(ERROR,
+             (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                   errmsg("could not identify a comparison function for type 
%s",
+                       format_type_be(varargExpr->varargtype))));
+     
+     InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+                            NULL, NULL);   
+     locfcinfo.argnull[0] = false;
+     locfcinfo.argnull[1] = false;
+     locfcinfo.isnull = false;
+ 
+     foreach(arg, varargExpr->args)
+     {
+       int32           cmpresult;
+         ExprState  *e = (ExprState *) lfirst(arg);
+         Datum value = ExecEvalExpr(e, econtext, isNull, NULL);
+         if (*isNull)
+           return value;
+         if (result) 
+       {
+           locfcinfo.arg[0] = result;
+           locfcinfo.arg[1] = value;
+           cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+ 
+           if (cmpresult > 0 && varargExpr->type == IS_LEAST)
+               result = value;
+           else if (cmpresult < 0 && varargExpr->type == IS_GREATEST)
+               result = value;
+       }
+       else
+           result = value;
+     }
+     *isNull = result == 0;
+     return result;
+ }
+ 
+ 
+ /* ----------------------------------------------------------------
   *            ExecEvalNullIf
   *
   * Note that this is *always* derived from the equals operator,
***************
*** 3206,3211 ****
--- 3361,3400 ----
                                state = (ExprState *) cstate;
                        }
                        break;
+               case T_VarargExpr:
+                       {
+                               VarargExpr *varargexpr = (VarargExpr *) node;
+                               VarargExprState *vstate = 
makeNode(VarargExprState);
+                               List       *outlist = NIL;
+                               ListCell   *l;
+                               
+                               switch(varargexpr->type)
+                               {
+                                   case IS_GREATEST:
+                                   case IS_LEAST:
+                                       vstate->xprstate.evalfunc = 
(ExprStateEvalFunc) ExecEvalVarargGreatest;
+                                       
+                                       break;
+                                   case IS_DECODE:
+                                       vstate->xprstate.evalfunc = 
(ExprStateEvalFunc) ExecEvalVarargDecode;
+                                       vstate->paramtype = 
varargexpr->paramtype;
+                                       break;
+                               }
+                               
+                               foreach(l, varargexpr->args)
+                               {
+                                       Expr       *e = (Expr *) lfirst(l);
+                                       ExprState  *estate;
+ 
+                                       estate = ExecInitExpr(e, parent);
+                                       outlist = lappend(outlist, estate);
+                               }
+                               vstate->args = outlist;
+                               vstate->varargtype = varargexpr->varargtype;
+                               vstate->type = varargexpr->type;
+                               state = (ExprState *) vstate;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
                                NullIfExpr *nullifexpr = (NullIfExpr *) node;
diff -c -r --new-file pgsql.02/src/backend/nodes/copyfuncs.c 
pgsql/src/backend/nodes/copyfuncs.c
*** pgsql.02/src/backend/nodes/copyfuncs.c      2005-06-06 15:29:07.000000000 
+0200
--- pgsql/src/backend/nodes/copyfuncs.c 2005-06-06 23:33:19.000000000 +0200
***************
*** 1048,1053 ****
--- 1048,1071 ----
  }
  
  /*
+  * _copyVarargExpr
+  */
+   
+ static VarargExpr *
+ _copyVarargExpr(VarargExpr *from)
+ {
+         VarargExpr *newnode = makeNode(VarargExpr);
+         
+       COPY_SCALAR_FIELD(varargtype);
+       COPY_SCALAR_FIELD(paramtype);
+       COPY_SCALAR_FIELD(type);
+       COPY_NODE_FIELD(args);
+                                         
+         return newnode;
+ }
+                                                 
+ 
+ /*
   * _copyNullIfExpr (same as OpExpr)
   */
  static NullIfExpr *
***************
*** 2817,2822 ****
--- 2835,2843 ----
                case T_CoalesceExpr:
                        retval = _copyCoalesceExpr(from);
                        break;
+               case T_VarargExpr:
+                       retval = _copyVarargExpr(from);
+                       break;                                                  
        
                case T_NullIfExpr:
                        retval = _copyNullIfExpr(from);
                        break;
diff -c -r --new-file pgsql.02/src/backend/nodes/equalfuncs.c 
pgsql/src/backend/nodes/equalfuncs.c
*** pgsql.02/src/backend/nodes/equalfuncs.c     2005-06-06 15:29:07.000000000 
+0200
--- pgsql/src/backend/nodes/equalfuncs.c        2005-06-06 23:31:14.000000000 
+0200
***************
*** 451,456 ****
--- 451,467 ----
  }
  
  static bool
+ _equalVarargExpr(VarargExpr *a, VarargExpr *b)
+ {
+       COMPARE_SCALAR_FIELD(varargtype);
+       COMPARE_SCALAR_FIELD(paramtype);
+       COMPARE_SCALAR_FIELD(type);
+       COMPARE_NODE_FIELD(args);
+ 
+       return true;
+ }
+                   
+ static bool
  _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
  {
        COMPARE_SCALAR_FIELD(opno);
***************
*** 1875,1880 ****
--- 1886,1894 ----
                case T_CoalesceExpr:
                        retval = _equalCoalesceExpr(a, b);
                        break;
+               case T_VarargExpr:
+                       retval = _equalVarargExpr(a, b);
+                       break;                                                  
                case T_NullIfExpr:
                        retval = _equalNullIfExpr(a, b);
                        break;
diff -c -r --new-file pgsql.02/src/backend/nodes/outfuncs.c 
pgsql/src/backend/nodes/outfuncs.c
*** pgsql.02/src/backend/nodes/outfuncs.c       2005-06-06 15:29:07.000000000 
+0200
--- pgsql/src/backend/nodes/outfuncs.c  2005-06-06 23:28:31.000000000 +0200
***************
*** 865,870 ****
--- 865,881 ----
  }
  
  static void
+ _outVarargExpr(StringInfo str, VarargExpr *node)
+ {
+         WRITE_NODE_TYPE("VARARG");
+       
+       WRITE_OID_FIELD(varargtype);
+       WRITE_OID_FIELD(paramtype);
+       WRITE_ENUM_FIELD(type, VarargExprType);
+       WRITE_NODE_FIELD(args);
+ }
+                                       
+ static void
  _outNullIfExpr(StringInfo str, NullIfExpr *node)
  {
        WRITE_NODE_TYPE("NULLIFEXPR");
***************
*** 1904,1909 ****
--- 1915,1923 ----
                        case T_CoalesceExpr:
                                _outCoalesceExpr(str, obj);
                                break;
+                       case T_VarargExpr:
+                               _outVarargExpr(str, obj);
+                               break;                                          
        
                        case T_NullIfExpr:
                                _outNullIfExpr(str, obj);
                                break;
diff -c -r --new-file pgsql.02/src/backend/nodes/readfuncs.c 
pgsql/src/backend/nodes/readfuncs.c
*** pgsql.02/src/backend/nodes/readfuncs.c      2005-06-06 15:29:07.000000000 
+0200
--- pgsql/src/backend/nodes/readfuncs.c 2005-06-06 23:29:17.000000000 +0200
***************
*** 659,664 ****
--- 659,680 ----
  }
  
  /*
+  * _readVarargExpr
+  */
+ static VarargExpr *
+ _readVarargExpr(void)
+ {
+         READ_LOCALS(VarargExpr);
+       
+         READ_OID_FIELD(varargtype);
+       READ_OID_FIELD(paramtype);
+       READ_ENUM_FIELD(type,VarargExprType);
+       READ_NODE_FIELD(args);
+                     
+       READ_DONE();
+ }
+                         
+ /*
   * _readNullIfExpr
   */
  static NullIfExpr *
***************
*** 982,987 ****
--- 998,1005 ----
                return_value = _readRowExpr();
        else if (MATCH("COALESCE", 8))
                return_value = _readCoalesceExpr();
+         else if (MATCH("VARARG",6))
+               return_value = _readVarargExpr();                              
        else if (MATCH("NULLIFEXPR", 10))
                return_value = _readNullIfExpr();
        else if (MATCH("NULLTEST", 8))
diff -c -r --new-file pgsql.02/src/backend/optimizer/util/clauses.c 
pgsql/src/backend/optimizer/util/clauses.c
*** pgsql.02/src/backend/optimizer/util/clauses.c       2005-06-06 
15:29:09.000000000 +0200
--- pgsql/src/backend/optimizer/util/clauses.c  2005-06-06 23:25:00.000000000 
+0200
***************
*** 542,547 ****
--- 542,549 ----
                return false;
        if (IsA(node, CoalesceExpr))
                return false;
+         if (IsA(node, VarargExpr))
+         return false;
        if (IsA(node, NullIfExpr))
                return false;
  
***************
*** 847,852 ****
--- 849,856 ----
                return true;
        if (IsA(node, CoalesceExpr))
                return true;
+         if (IsA(node, VarargExpr))
+         return true;
        if (IsA(node, NullIfExpr))
                return true;
        if (IsA(node, NullTest))
***************
*** 1796,1801 ****
--- 1800,1836 ----
                newcoalesce->args = newargs;
                return (Node *) newcoalesce;
        }
+       if (IsA(node, VarargExpr))
+       {
+               VarargExpr *varargexpr = (VarargExpr *) node;
+               VarargExpr *newvararg;
+               List       *newargs;
+               ListCell   *arg;
+ 
+               newargs = NIL;
+               
+               foreach(arg, varargexpr->args)
+               {
+                       Node       *e;
+                       e = eval_const_expressions_mutator((Node *) lfirst(arg),
+                                                                               
   context);
+                       /* If any argument is null, then result is null (for 
GREATEST and LEAST)*/
+                       if (IsA(e, Const))
+                       {
+                               if (((Const *) e)->constisnull && 
+                                       (varargexpr->type == IS_GREATEST || 
varargexpr->type == IS_LEAST))
+                                           return (Node *) 
makeNullConst(varargexpr->varargtype);
+                       }
+                       newargs = lappend(newargs, e);
+               }
+ 
+               newvararg = makeNode(VarargExpr);
+               newvararg->varargtype = varargexpr->varargtype;
+               newvararg->type = varargexpr->type;
+               newvararg->paramtype = varargexpr->paramtype;
+               newvararg->args = newargs;
+               return (Node *) newvararg;
+       }
        if (IsA(node, FieldSelect))
        {
                /*
***************
*** 2932,2937 ****
--- 2967,2974 ----
                        return walker(((RowExpr *) node)->args, context);
                case T_CoalesceExpr:
                        return walker(((CoalesceExpr *) node)->args, context);
+               case T_VarargExpr:
+                       return walker(((VarargExpr *) node)->args, context);
                case T_NullIfExpr:
                        return walker(((NullIfExpr *) node)->args, context);
                case T_NullTest:
***************
*** 3392,3397 ****
--- 3429,3444 ----
                                return (Node *) newnode;
                        }
                        break;
+               case T_VarargExpr:
+                       {
+                               VarargExpr *varargexpr = (VarargExpr *) node;
+                               VarargExpr *newnode;
+ 
+                               FLATCOPY(newnode, varargexpr, VarargExpr);
+                               MUTATE(newnode->args, varargexpr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
                                NullIfExpr *expr = (NullIfExpr *) node;
diff -c -r --new-file pgsql.02/src/backend/parser/gram.y 
pgsql/src/backend/parser/gram.y
*** pgsql.02/src/backend/parser/gram.y  2005-06-06 15:29:09.000000000 +0200
--- pgsql/src/backend/parser/gram.y     2005-06-06 23:19:32.000000000 +0200
***************
*** 350,356 ****
        CREATEUSER CROSS CSV CURRENT_DATE CURRENT_TIME
        CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
!       DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
        DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
        DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
  
--- 350,356 ----
        CREATEUSER CROSS CSV CURRENT_DATE CURRENT_TIME
        CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
!       DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DECODE DEFAULT DEFAULTS
        DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
        DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
  
***************
*** 360,366 ****
        FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
        FREEZE FROM FULL FUNCTION
  
!       GLOBAL GRANT GROUP_P
  
        HANDLER HAVING HEADER HOLD HOUR_P
  
--- 360,366 ----
        FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
        FREEZE FROM FULL FUNCTION
  
!       GLOBAL GRANT GREATEST GROUP_P
  
        HANDLER HAVING HEADER HOLD HOUR_P
  
***************
*** 373,379 ****
  
        KEY
  
!       LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEFT LEVEL LIKE LIMIT
        LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
        LOCK_P
  
--- 373,379 ----
  
        KEY
  
!       LANCOMPILER LANGUAGE LARGE_P  LAST_P LEADING LEAST LEFT LEVEL LIKE LIMIT
        LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
        LOCK_P
  
***************
*** 7067,7072 ****
--- 7067,7098 ----
                                        c->args = $3;
                                        $$ = (Node *)c;
                                }
+                         | GREATEST '(' expr_list ')'
+                               {
+                                       VarargExpr *v = makeNode(VarargExpr);
+                                       v->args = $3;
+                                       v->type = IS_GREATEST;
+                                       $$ = (Node *)v;
+                               }
+                       | LEAST  '(' expr_list ')'
+                               {
+                                       VarargExpr *v = makeNode(VarargExpr);
+                                       v->args = $3;
+                                       v->type = IS_LEAST;
+                                       $$ = (Node *)v;
+                               }                                               
                                                                                
                                                                                
                         
+                       | DECODE '(' expr_list ')'
+                               {
+                                         if (list_length($3) < 3)
+                                           ereport(ERROR,
+                                                     
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                       errmsg("Function Decode 
needs minimal three arguments")));
+ 
+                                       VarargExpr *v = makeNode(VarargExpr);
+                                       v->args = $3;
+                                       v->type = IS_DECODE;
+                                       $$ = (Node *)v;
+                               }                                               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                ;
  
  /*
***************
*** 7937,7949 ****
--- 7963,7978 ----
                        | CONVERT
                        | DEC
                        | DECIMAL_P
+                       | DECODE
                        | EXISTS
                        | EXTRACT
                        | FLOAT_P
+                       | GREATEST
                        | INOUT
                        | INT_P
                        | INTEGER
                        | INTERVAL
+                       | LEAST
                        | NATIONAL
                        | NCHAR
                        | NONE
diff -c -r --new-file pgsql.02/src/backend/parser/keywords.c 
pgsql/src/backend/parser/keywords.c
*** pgsql.02/src/backend/parser/keywords.c      2005-06-06 15:29:09.000000000 
+0200
--- pgsql/src/backend/parser/keywords.c 2005-06-06 23:45:19.000000000 +0200
***************
*** 103,108 ****
--- 103,109 ----
        {"dec", DEC},
        {"decimal", DECIMAL_P},
        {"declare", DECLARE},
+       {"decode", DECODE},
        {"default", DEFAULT},
        {"defaults", DEFAULTS},
        {"deferrable", DEFERRABLE},
***************
*** 145,150 ****
--- 146,152 ----
        {"function", FUNCTION},
        {"global", GLOBAL},
        {"grant", GRANT},
+       {"greatest", GREATEST},
        {"group", GROUP_P},
        {"handler", HANDLER},
        {"having", HAVING},
***************
*** 183,188 ****
--- 185,191 ----
        {"large", LARGE_P},
        {"last", LAST_P},
        {"leading", LEADING},
+       {"least", LEAST},
        {"left", LEFT},
        {"level", LEVEL},
        {"like", LIKE},
diff -c -r --new-file pgsql.02/src/backend/parser/parse_expr.c 
pgsql/src/backend/parser/parse_expr.c
*** pgsql.02/src/backend/parser/parse_expr.c    2005-06-06 15:29:10.000000000 
+0200
--- pgsql/src/backend/parser/parse_expr.c       2005-06-06 23:39:15.000000000 
+0200
***************
*** 53,58 ****
--- 53,59 ----
  static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
  static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
  static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
+ static Node *transformVarargExpr(ParseState *pstate, VarargExpr *v);
  static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
  static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
  static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
***************
*** 209,214 ****
--- 210,219 ----
                        result = transformCoalesceExpr(pstate, (CoalesceExpr *) 
expr);
                        break;
  
+               case T_VarargExpr:
+                         result = transformVarargExpr(pstate, (VarargExpr *) 
expr);
+                       break;
+                                               
                case T_NullTest:
                        {
                                NullTest   *n = (NullTest *) expr;
***************
*** 1229,1234 ****
--- 1234,1374 ----
        return (Node *) newc;
  }
  
+ #define FROM_EXPR_TO_SEARCH     argtype = IS_SEARCH;
+ #define FROM_SEARCH_TO_RESULT   argtype = IS_RESULT;
+ #define FROM_RESULT_TO_SEARCH   argtype = IS_SEARCH;
+ 
+ typedef enum DecodeArgsType
+ {
+       IS_EXPR,
+         IS_SEARCH,
+       IS_RESULT
+ } DecodeArgsType;
+ 
+           
+ static Node *
+ transformVarargExpr(ParseState *pstate, VarargExpr *v)
+ {
+       VarargExpr *newva = makeNode(VarargExpr);
+       List       *newargs = NIL;
+       List       *newcoercedargs = NIL;
+       List       *typeids = NIL;
+       ListCell   *args;
+       List *searchtypeids = NIL;
+ 
+       newva->type = v->type;
+ 
+       switch (v->type)
+       {
+               case IS_DECODE:
+               {
+                       DecodeArgsType argtype = IS_EXPR;
+                       foreach(args, v->args)
+                       {
+                           Node           *e = (Node *) lfirst(args);
+                           Node           *newe;
+ 
+                           newe = transformExpr(pstate, e);
+                           newargs = lappend(newargs, newe);
+ 
+                           if (lnext(args) == NULL && argtype == IS_SEARCH)
+                               argtype = IS_RESULT;
+                           
+                           switch (argtype)
+                           {
+                               case IS_EXPR:
+                                   searchtypeids = lappend_oid(searchtypeids, 
exprType(newe));
+                                   FROM_EXPR_TO_SEARCH;
+                                   break;
+                               case IS_RESULT:
+                                   typeids = lappend_oid(typeids, 
exprType(newe));
+                                   FROM_RESULT_TO_SEARCH;
+                                   break;
+                               case IS_SEARCH:
+                                   searchtypeids = lappend_oid(searchtypeids, 
exprType(newe));
+                                   FROM_SEARCH_TO_RESULT;
+                                   break;
+                           }
+                                   
+                       }
+                       newva->varargtype = select_common_type(typeids, 
"VARARG");
+                       newva->paramtype = select_common_type(searchtypeids, 
"VARARG");
+ 
+                       /* Convert arguments if necessary */
+                       argtype = IS_EXPR;
+                       foreach(args, newargs)
+                       {
+                           Node           *e = (Node *) lfirst(args);
+                           Node           *newe;
+                           
+                           if (lnext(args) == NULL && argtype == IS_SEARCH)
+                               argtype = IS_RESULT;
+                           switch (argtype)
+                           {    
+                               case IS_EXPR:
+                                   newe = coerce_to_common_type(pstate, e,
+                                                                        
newva->paramtype,
+                                                                        
"VARARG");
+                                   FROM_EXPR_TO_SEARCH;
+                                   break;
+                               case IS_RESULT:
+                                   newe = coerce_to_common_type(pstate, e,
+                                                                        
newva->varargtype,
+                                                                        
"VARARG");
+                                   FROM_RESULT_TO_SEARCH;
+                                   break;
+                               case IS_SEARCH:
+                                   newe = coerce_to_common_type(pstate, e,
+                                                                        
newva->paramtype,
+                                                                        
"VARARG");
+                                   FROM_SEARCH_TO_RESULT;
+                                   break;
+                           }
+                           
+                           newcoercedargs = lappend(newcoercedargs, newe);
+                       }
+                       newva->args = newcoercedargs;
+                       
+                       break;
+               }
+               case IS_GREATEST:
+               case IS_LEAST:
+               {
+                       foreach(args, v->args)
+                       {
+                           Node           *e = (Node *) lfirst(args);
+                           Node           *newe;
+ 
+                           newe = transformExpr(pstate, e);
+                           newargs = lappend(newargs, newe);
+                           typeids = lappend_oid(typeids, exprType(newe));
+                       }
+ 
+                       newva->varargtype = select_common_type(typeids, 
"VARARG");
+ 
+                       /* Convert arguments if necessary */
+                       foreach(args, newargs)
+                       {
+                           Node           *e = (Node *) lfirst(args);
+                           Node           *newe;
+ 
+                           newe = coerce_to_common_type(pstate, e,
+                                                                        
newva->varargtype,
+                                                                        
"VARARG");
+                           newcoercedargs = lappend(newcoercedargs, newe);
+                       }
+ 
+                       newva->args = newcoercedargs;
+                       
+                       break;
+               }
+                       
+       }
+       return (Node *) newva;
+ }
+           
+ 
+ 
  static Node *
  transformBooleanTest(ParseState *pstate, BooleanTest *b)
  {
***************
*** 1503,1508 ****
--- 1643,1651 ----
                case T_CoalesceExpr:
                        type = ((CoalesceExpr *) expr)->coalescetype;
                        break;
+               case T_VarargExpr:
+                       type = ((VarargExpr *) expr)->varargtype;
+                       break;
                case T_NullIfExpr:
                        type = exprType((Node *) linitial(((NullIfExpr *) 
expr)->args));
                        break;
***************
*** 1637,1642 ****
--- 1780,1845 ----
                                return typmod;
                        }
                        break;
+               case T_VarargExpr:
+                       {
+                               /*
+                                * If all the alternatives agree on 
type/typmod, return
+                                * that typmod, else use -1
+                                */
+                               VarargExpr *vexpr = (VarargExpr *) expr;
+                               Oid                     varargtype = 
vexpr->varargtype;
+                               int32           typmod;
+                               ListCell   *arg;
+                               DecodeArgsType argtype = IS_EXPR;
+                               bool firstResult = true; 
+                               bool isResult = true;
+                               
+                               /* for decode function is usefull only results 
*/
+                               
+                               if (vexpr->type != IS_DECODE)
+                                   typmod = exprTypmod((Node *) 
linitial(vexpr->args));
+                               
+                               foreach(arg, vexpr->args)
+                               {
+                                       Node       *e = (Node *) lfirst(arg);
+ 
+                                       if (vexpr->type == IS_DECODE)
+                                       {
+                                           switch (argtype)
+                                           {
+                                           
+                                               case IS_EXPR:
+                                                   FROM_EXPR_TO_SEARCH
+                                                   isResult = false;
+                                                   break;
+                                               case IS_SEARCH:
+                                                   if (lnext(arg) != NULL)
+                                                   {
+                                                       isResult = false;
+                                                       FROM_SEARCH_TO_RESULT
+                                                       break;
+                                                   }
+                                               case IS_RESULT:
+                                                   if (firstResult) 
+                                                   {
+                                                       firstResult = false;
+                                                       typmod = 
exprTypmod((Node *) e);
+                                                   }
+                                                   isResult = true;
+                                                   FROM_RESULT_TO_SEARCH
+                                           }
+                                       }
+                                       if (isResult)
+                                       {
+                                           if (exprType(e) != varargtype)
+                                                   return -1;
+                                           if (exprTypmod(e) != typmod)
+                                                   return -1;
+                                       }
+                               }
+                               return typmod;
+                       }
+                       break;
                case T_NullIfExpr:
                        {
                                NullIfExpr *nexpr = (NullIfExpr *) expr;
diff -c -r --new-file pgsql.02/src/backend/parser/parse_target.c 
pgsql/src/backend/parser/parse_target.c
*** pgsql.02/src/backend/parser/parse_target.c  2005-06-06 15:29:10.000000000 
+0200
--- pgsql/src/backend/parser/parse_target.c     2005-06-06 23:00:35.000000000 
+0200
***************
*** 1123,1128 ****
--- 1123,1142 ----
                        /* make coalesce() act like a regular function */
                        *name = "coalesce";
                        return 2;
+            case T_VarargExpr:
+                         switch (((VarargExpr*) node)->type)
+                       {
+                              case IS_GREATEST:
+                                  *name = "greatest";
+                                  return 2;
+                              case IS_LEAST:
+                                  *name = "least";
+                                  return 2;
+                              case IS_DECODE:
+                                  *name = "decode";
+                                  return 2;
+                       }
+                                                                               
                                                                                
                        
                default:
                        break;
        }
diff -c -r --new-file pgsql.02/src/backend/utils/adt/ruleutils.c 
pgsql/src/backend/utils/adt/ruleutils.c
*** pgsql.02/src/backend/utils/adt/ruleutils.c  2005-06-06 15:29:19.000000000 
+0200
--- pgsql/src/backend/utils/adt/ruleutils.c     2005-06-06 22:57:30.000000000 
+0200
***************
*** 2781,2786 ****
--- 2781,2787 ----
                case T_ArrayExpr:
                case T_RowExpr:
                case T_CoalesceExpr:
+               case T_VarargExpr:
                case T_NullIfExpr:
                case T_Aggref:
                case T_FuncExpr:
***************
*** 2888,2893 ****
--- 2889,2895 ----
                                case T_ArrayExpr:               /* other 
separators */
                                case T_RowExpr: /* other separators */
                                case T_CoalesceExpr:    /* own parentheses */
+                               case T_VarargExpr:      /* own parentheses */
                                case T_NullIfExpr:              /* other 
separators */
                                case T_Aggref:  /* own parentheses */
                                case T_CaseExpr:                /* other 
separators */
***************
*** 2935,2940 ****
--- 2937,2943 ----
                                case T_ArrayExpr:               /* other 
separators */
                                case T_RowExpr: /* other separators */
                                case T_CoalesceExpr:    /* own parentheses */
+                               case T_VarargExpr:      /* own parentheses */
                                case T_NullIfExpr:              /* other 
separators */
                                case T_Aggref:  /* own parentheses */
                                case T_CaseExpr:                /* other 
separators */
***************
*** 3491,3496 ****
--- 3494,3520 ----
                        }
                        break;
  
+               case T_VarargExpr:
+                        {
+                               VarargExpr *varargexpr = (VarargExpr *) node;
+    
+                               switch (varargexpr->type)
+                                 {
+                                   case IS_GREATEST:
+                                           appendStringInfo(buf, "GREATEST(");
+                                           break;
+                                   case IS_LEAST:
+                                           appendStringInfo(buf, "LEAST(");
+                                           break;
+                                     case IS_DECODE:
+                                             appendStringInfo(buf, "DECODE(");
+                                             break;
+                                 }
+                                 get_rule_expr((Node *) varargexpr->args, 
context, true);
+                                 appendStringInfoChar(buf, ')');
+                       }
+                       break;
+                                                                               
                                                                                
                                                                                
                                                                                
                                                                                
                case T_NullIfExpr:
                        {
                                NullIfExpr *nullifexpr = (NullIfExpr *) node;
diff -c -r --new-file pgsql.02/src/include/nodes/execnodes.h 
pgsql/src/include/nodes/execnodes.h
*** pgsql.02/src/include/nodes/execnodes.h      2005-06-06 15:29:42.000000000 
+0200
--- pgsql/src/include/nodes/execnodes.h 2005-06-06 22:48:01.000000000 +0200
***************
*** 672,677 ****
--- 672,692 ----
  } CoalesceExprState;
  
  /* ----------------
+  *              VarargExprState node
+  * ----------------
+  */
+ typedef struct VarargExprState
+ {
+        ExprState       xprstate;
+        VarargExprType type;
+        Oid     varargtype;                     /* type of arguments and 
result */
+        Oid     paramtype;                      /* type of params */
+        List       *args;                       /* the arguments */
+ } VarargExprState;
+                                          
+                                          
+ 
+ /* ----------------
   *            CoerceToDomainState node
   * ----------------
   */
diff -c -r --new-file pgsql.02/src/include/nodes/nodes.h 
pgsql/src/include/nodes/nodes.h
*** pgsql.02/src/include/nodes/nodes.h  2005-06-06 15:29:42.000000000 +0200
--- pgsql/src/include/nodes/nodes.h     2005-06-06 22:49:09.000000000 +0200
***************
*** 136,141 ****
--- 136,142 ----
        T_RangeTblRef,
        T_JoinExpr,
        T_FromExpr,
+       T_VarargExpr,
  
        /*
         * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
***************
*** 161,166 ****
--- 162,168 ----
        T_CoalesceExprState,
        T_CoerceToDomainState,
        T_DomainConstraintState,
+       T_VarargExprState,
  
        /*
         * TAGS FOR PLANNER NODES (relation.h)
diff -c -r --new-file pgsql.02/src/include/nodes/primnodes.h 
pgsql/src/include/nodes/primnodes.h
*** pgsql.02/src/include/nodes/primnodes.h      2005-06-06 15:29:42.000000000 
+0200
--- pgsql/src/include/nodes/primnodes.h 2005-06-06 22:50:27.000000000 +0200
***************
*** 657,662 ****
--- 657,683 ----
        List       *args;                       /* the arguments */
  } CoalesceExpr;
  
+ 
+ /*
+  * VarargExpr - a GREATEST, LEAST expression
+  */
+   
+ typedef enum VarargExprType
+ {
+       IS_GREATEST,
+       IS_LEAST,
+       IS_DECODE
+ } VarargExprType;
+             
+ typedef struct VarargExpr
+ {
+       Expr            xpr;
+       Oid             varargtype;
+       Oid             paramtype;
+       VarargExprType  type;
+       List    *args;
+ } VarargExpr;
+                                                     
  /*
   * NullIfExpr - a NULLIF expression
   *
diff -c -r --new-file pgsql.02/src/test/regress/expected/oracle.out 
pgsql/src/test/regress/expected/oracle.out
*** pgsql.02/src/test/regress/expected/oracle.out       1970-01-01 
01:00:00.000000000 +0100
--- pgsql/src/test/regress/expected/oracle.out  2005-06-07 00:55:32.000000000 
+0200
***************
*** 0 ****
--- 1,74 ----
+ SELECT least(1,10,20,30);
+  least 
+ -------
+      1
+ (1 row)
+ 
+ SELECT least('a','b','c','d');
+  least 
+ -------
+  a
+ (1 row)
+ 
+ SELECT least('2004-05-22'::date, '2004-05-10'::date);
+    least    
+ ------------
+  2004-05-10
+ (1 row)
+ 
+ SELECT greatest(1,10,20,30);
+  greatest 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT greatest('a','b','c','d');
+  greatest 
+ ----------
+  d
+ (1 row)
+ 
+ SELECT greatest('2004-05-22'::date, '2004-05-10'::date);
+   greatest  
+ ------------
+  2004-05-22
+ (1 row)
+ 
+ SELECT decode('a','n',10,'m',20,'a',30);
+  decode 
+ --------
+      30
+ (1 row)
+ 
+ SELECT decode('a','n');
+ ERROR:  Function Decode needs minimal three arguments
+ SELECT decode('a','n',10,'m',20,'o',30,40);
+  decode 
+ --------
+      40
+ (1 row)
+ 
+ SELECT 
decode(2,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+    decode   
+ ------------
+  2004-04-01
+ (1 row)
+ 
+ SELECT 
decode(null,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+  decode 
+ --------
+  
+ (1 row)
+ 
+ SELECT 
decode(4,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+  decode 
+ --------
+  
+ (1 row)
+ 
+ SELECT decode(null,'a','a',null,'b');
+  decode 
+ --------
+  b
+ (1 row)
+ 
diff -c -r --new-file pgsql.02/src/test/regress/sql/oracle.sql 
pgsql/src/test/regress/sql/oracle.sql
*** pgsql.02/src/test/regress/sql/oracle.sql    1970-01-01 01:00:00.000000000 
+0100
--- pgsql/src/test/regress/sql/oracle.sql       2005-06-07 00:52:13.000000000 
+0200
***************
*** 0 ****
--- 1,15 ----
+ SELECT least(1,10,20,30);
+ SELECT least('a','b','c','d');
+ SELECT least('2004-05-22'::date, '2004-05-10'::date);
+ 
+ SELECT greatest(1,10,20,30);
+ SELECT greatest('a','b','c','d');
+ SELECT greatest('2004-05-22'::date, '2004-05-10'::date);
+ 
+ SELECT decode('a','n',10,'m',20,'a',30);
+ SELECT decode('a','n');
+ SELECT decode('a','n',10,'m',20,'o',30,40);
+ SELECT 
decode(2,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+ SELECT 
decode(null,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+ SELECT 
decode(4,1,'2004-01-01'::date,2,'2004-04-01'::date,3,'2004-07-01'::date);
+ SELECT decode(null,'a','a',null,'b');
---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to