Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

Started by Pavel Stehuleover 20 years ago21 messages
#1Pavel Stehule
stehule@kix.fsv.cvut.cz
1 attachment(s)

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

Attachments:

greatest.difftext/plain; charset=US-ASCII; name=greatest.diffDownload
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');
#2Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Pavel Stehule (#1)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic

This patch looks good. I know greater/least are seen as valuable, but
do we want decode()? It seems OK to me but I thought there was concern
about it in the past because it duplicated some existing functionality.

---------------------------------------------------------------------------

Pavel Stehule wrote:

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

Content-Description:

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?

http://archives.postgresql.org

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
#3Pavel Stehule
stehule@kix.fsv.cvut.cz
In reply to: Bruce Momjian (#2)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Hello,

I sended version with only LEAST and GREATEST
http://archives.postgresql.org/pgsql-patches/2005-06/msg00185.php

this patch contains diff files for NEXT_DAY and LAST_DAY function too,
which I think are generally usefull.

There is duplicity with CASE.

Regards
Pavel

On Tue, 21 Jun 2005, Bruce Momjian wrote:

Show quoted text

This patch looks good. I know greater/least are seen as valuable, but
do we want decode()? It seems OK to me but I thought there was concern
about it in the past because it duplicated some existing functionality.

---------------------------------------------------------------------------

Pavel Stehule wrote:

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

Content-Description:

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?

http://archives.postgresql.org

#4Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Pavel Stehule (#3)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Pavel Stehule wrote:

Hello,

I sended version with only LEAST and GREATEST
http://archives.postgresql.org/pgsql-patches/2005-06/msg00185.php

OK, did you remove DECODE for a reason?

this patch contains diff files for NEXT_DAY and LAST_DAY function too,
which I think are generally usefull.

There is duplicity with CASE.

I didn't think we needed NEXT_DAY and LAST_DAY because we have +
interval like + '1 day'. We decided that, right?

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
#5Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Pavel Stehule (#3)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Pavel Stehule wrote:

Hello,

I sended version with only LEAST and GREATEST
http://archives.postgresql.org/pgsql-patches/2005-06/msg00185.php

this patch contains diff files for NEXT_DAY and LAST_DAY function too,
which I think are generally usefull.

There is duplicity with CASE.

Oh, and I personally think DECODE is fine, even if it partly duplicates
something we already have. I was just asking to make sure everyone else
was OK before I applied it.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#5)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Bruce Momjian <pgman@candle.pha.pa.us> writes:

Oh, and I personally think DECODE is fine, even if it partly duplicates
something we already have. I was just asking to make sure everyone else
was OK before I applied it.

I would rather not have a useless variant spelling of CASE ...
LEAST/GREATEST at least do something that's a bit hard to do otherwise.

regards, tom lane

#7Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Tom Lane (#6)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Tom Lane wrote:

Bruce Momjian <pgman@candle.pha.pa.us> writes:

Oh, and I personally think DECODE is fine, even if it partly duplicates
something we already have. I was just asking to make sure everyone else
was OK before I applied it.

I would rather not have a useless variant spelling of CASE ...
LEAST/GREATEST at least do something that's a bit hard to do otherwise.

OK, I will apply the LEAST/GREATEST parts and see if others can argue
for decode().

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#7)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Bruce Momjian <pgman@candle.pha.pa.us> writes:

OK, I will apply the LEAST/GREATEST parts and see if others can argue
for decode().

Actually, I'd like to review the patch before it goes in --- if anyone's
looked at the code, I saw no comments about it ...

regards, tom lane

#9Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Tom Lane (#8)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

Tom Lane wrote:

Bruce Momjian <pgman@candle.pha.pa.us> writes:

OK, I will apply the LEAST/GREATEST parts and see if others can argue
for decode().

Actually, I'd like to review the patch before it goes in --- if anyone's
looked at the code, I saw no comments about it ...

No, no one commented. I wanted to improve the SGML documentation a
little, but feel free to adjust and apply. It is in the patch queue.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
#10Pavel Stehule
stehule@kix.fsv.cvut.cz
In reply to: Bruce Momjian (#4)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

On Tue, 21 Jun 2005, Bruce Momjian wrote:

Pavel Stehule wrote:

Hello,

I sended version with only LEAST and GREATEST
http://archives.postgresql.org/pgsql-patches/2005-06/msg00185.php

OK, did you remove DECODE for a reason?

o simplify patch
o decode can be added in future if will be requirement

this patch contains diff files for NEXT_DAY and LAST_DAY function too,
which I think are generally usefull.

There is duplicity with CASE.

I didn't think we needed NEXT_DAY and LAST_DAY because we have +
interval like + '1 day'. We decided that, right?

next_day not equal date + 1day, but like next monday, ... look to doc.
next_day (date, varchar) -- varchar = sunday|monday|...

last_day is very often used function. Really. I can substitute

create or replace function last_day(date) returns date $$
select date_trunc('month', $1 + interval '1month') - 1;
$$ language sql;

but I think can be usefull (for first day we have date_drunc, for
last_date nothing)

regards
Pavel

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#1)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:
+ /* If any argument is null, then result is null (for GREATEST and LEAST)*/

Are you sure about that? The only reference I could find says that
these functions are not strict in Oracle:

http://download-east.oracle.com/otn_hosted_doc/rdb/pdf/sql_ref_v71_vol1.pdf
on page 2-185:

The NULL keyword can appear in the list but is ignored. However, not all
value expressions can be specified as NULL. That is, a non-NULL value
expression must be in the list so that the data type for the expression
can be determined.
The GREATEST and LEAST functions can result in NULL only if at run time
all value expressions result in NULL.

The strict interpretation is mathematically cleaner, no doubt, but
offhand it seems less useful.

regards, tom lane

#12Pavel Stehule
stehule@kix.fsv.cvut.cz
In reply to: Tom Lane (#11)
Re: Function's LEAST, GREATEST and DECODE (Oracle vararg

On Thu, 23 Jun 2005, Tom Lane wrote:

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:
+ /* If any argument is null, then result is null (for GREATEST and LEAST)*/

Are you sure about that? The only reference I could find says that
these functions are not strict in Oracle:

http://download-east.oracle.com/otn_hosted_doc/rdb/pdf/sql_ref_v71_vol1.pdf
on page 2-185:

The NULL keyword can appear in the list but is ignored. However, not all
value expressions can be specified as NULL. That is, a non-NULL value
expression must be in the list so that the data type for the expression
can be determined.
The GREATEST and LEAST functions can result in NULL only if at run time
all value expressions result in NULL.

The strict interpretation is mathematically cleaner, no doubt, but
offhand it seems less useful.

I know it, But when moustly PostgreSQL function is strict I desided so
greatest and least will be strict. There is two analogy:

one, normal comparing which implicate strinct
aggregate function which ignore NULL.

what I have to chose? For compatibility there isn't biggeer changes. Only

//if (*isNull)
// return value;
if (result && *isNull == false)
{
locfcinfo.arg[0] = result;
...
}

-----
foreach(arg, ..)
{
if (IsA(e, Const))
if (!((Const *) e)->constisnull)
newargs = lappend(newargs, e);
}
if (newargs == NULL)
return (Node *) makeNullConst(varargexpr->..);

-----

Tom I don't know, what is better. Maybe Oracle,

because

least(nullif(col2, +max), nullif(col2, +max)) isn't really readable, but
it's "precedens" for PostgreSQL. I selected more conservative solution,
but my patches are only start points for discussion (really) :).

Please, if You think, so Oracle way is good, correct it.

Best regards
Pavel

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#12)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict (return
null if any input is null) or not (return null only if all inputs are
null, else return the largest/smallest of the non-null inputs)?

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:

On Thu, 23 Jun 2005, Tom Lane wrote:

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:
+ /* If any argument is null, then result is null (for GREATEST and LEAST)*/

Are you sure about that? The only reference I could find says that
these functions are not strict in Oracle:

http://download-east.oracle.com/otn_hosted_doc/rdb/pdf/sql_ref_v71_vol1.pdf
on page 2-185:

The NULL keyword can appear in the list but is ignored. However, not all
value expressions can be specified as NULL. That is, a non-NULL value
expression must be in the list so that the data type for the expression
can be determined.
The GREATEST and LEAST functions can result in NULL only if at run time
all value expressions result in NULL.

The strict interpretation is mathematically cleaner, no doubt, but
offhand it seems less useful.

I know it, But when moustly PostgreSQL function is strict I desided so
greatest and least will be strict. There is two analogy:

one, normal comparing which implicate strinct
aggregate function which ignore NULL.

Tom I don't know, what is better. Maybe Oracle,

because

least(nullif(col2, +max), nullif(col2, +max)) isn't really readable, but
it's "precedens" for PostgreSQL. I selected more conservative solution,
but my patches are only start points for discussion (really) :).

Please, if You think, so Oracle way is good, correct it.

I'm still favoring non-strict but it deserves more than two votes.
Anybody else have an opinion?

regards, tom lane

#14Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#13)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle

Tom Lane wrote:

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict (return
null if any input is null) or not (return null only if all inputs are
null, else return the largest/smallest of the non-null inputs)?

My initial reaction was to say "not strict", and since that's apparently
what Oracle does that reinforces it for me.

cheers

andrew

#15Mike Rylander
mrylander@gmail.com
In reply to: Tom Lane (#13)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

On 6/24/05, Tom Lane <tgl@sss.pgh.pa.us> wrote:

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict (return
null if any input is null) or not (return null only if all inputs are
null, else return the largest/smallest of the non-null inputs)?

[snip]

Please, if You think, so Oracle way is good, correct it.

I'm still favoring non-strict but it deserves more than two votes.
Anybody else have an opinion?

regards, tom lane

My $0.02: I'd prefer the non-strict version.

--
Mike Rylander
mrylander@gmail.com
GPLS -- PINES Development
Database Developer
http://open-ils.org

#16John Hansen
john@geeknet.com.au
In reply to: Mike Rylander (#15)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

I'd vote that these functions should follow the semantics of the <, and

operators.

(NULL < x) is NULL;

... John

Show quoted text

-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Tom Lane
Sent: Friday, June 24, 2005 11:21 PM
To: Pavel Stehule
Cc: pgsql-hackers@postgresql.org
Subject: Re: [HACKERS] [PATCHES] Function's LEAST, GREATEST
and DECODE (Oracle vararg polymorphic functions)

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict
(return null if any input is null) or not (return null only
if all inputs are null, else return the largest/smallest of
the non-null inputs)?

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:

On Thu, 23 Jun 2005, Tom Lane wrote:

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:
+ /* If any argument is null, then result

is null (for GREATEST

+ and LEAST)*/

Are you sure about that? The only reference I could find

says that

these functions are not strict in Oracle:

http://download-east.oracle.com/otn_hosted_doc/rdb/pdf/sql_ref_v71_vo

l1.pdf
on page 2-185:

The NULL keyword can appear in the list but is ignored.

However, not

all value expressions can be specified as NULL. That is,

a non-NULL

value expression must be in the list so that the data

type for the

expression can be determined.
The GREATEST and LEAST functions can result in NULL only

if at run

time all value expressions result in NULL.

The strict interpretation is mathematically cleaner, no doubt, but
offhand it seems less useful.

I know it, But when moustly PostgreSQL function is strict I

desided so

greatest and least will be strict. There is two analogy:

one, normal comparing which implicate strinct aggregate

function which

ignore NULL.

Tom I don't know, what is better. Maybe Oracle,

because

least(nullif(col2, +max), nullif(col2, +max)) isn't really

readable,

but it's "precedens" for PostgreSQL. I selected more conservative
solution, but my patches are only start points for

discussion (really) :).

Please, if You think, so Oracle way is good, correct it.

I'm still favoring non-strict but it deserves more than two votes.
Anybody else have an opinion?

regards, tom lane

---------------------------(end of
broadcast)---------------------------
TIP 8: explain analyze is your friend

#17Robert Treat
xzilla@users.sourceforge.net
In reply to: Tom Lane (#13)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE

On Fri, 2005-06-24 at 09:21, Tom Lane wrote:

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict (return
null if any input is null) or not (return null only if all inputs are
null, else return the largest/smallest of the non-null inputs)?

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:

On Thu, 23 Jun 2005, Tom Lane wrote:

Pavel Stehule <stehule@kix.fsv.cvut.cz> writes:
+ /* If any argument is null, then result is null (for GREATEST and LEAST)*/

Are you sure about that? The only reference I could find says that
these functions are not strict in Oracle:

http://download-east.oracle.com/otn_hosted_doc/rdb/pdf/sql_ref_v71_vol1.pdf
on page 2-185:

The NULL keyword can appear in the list but is ignored. However, not all
value expressions can be specified as NULL. That is, a non-NULL value
expression must be in the list so that the data type for the expression
can be determined.
The GREATEST and LEAST functions can result in NULL only if at run time
all value expressions result in NULL.

The strict interpretation is mathematically cleaner, no doubt, but
offhand it seems less useful.

I know it, But when moustly PostgreSQL function is strict I desided so
greatest and least will be strict. There is two analogy:

one, normal comparing which implicate strinct
aggregate function which ignore NULL.

Tom I don't know, what is better. Maybe Oracle,

because

least(nullif(col2, +max), nullif(col2, +max)) isn't really readable, but
it's "precedens" for PostgreSQL. I selected more conservative solution,
but my patches are only start points for discussion (really) :).

Please, if You think, so Oracle way is good, correct it.

I'm still favoring non-strict but it deserves more than two votes.
Anybody else have an opinion?

If the sql spec has nothing to say on it, then we should probably
support Oracles take, since this seems like an Oracleism anyway.

Robert Treat
--
Build A Brighter Lamp :: Linux Apache {middleware} PostgreSQL

#18Tom Lane
tgl@sss.pgh.pa.us
In reply to: John Hansen (#16)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

"John Hansen" <john@geeknet.com.au> writes:

I'd vote that these functions should follow the semantics of the <, and

operators.

(NULL < x) is NULL;

Well, that's a fair analogy, but then so is the analogy to MAX/MIN ...
so it seems about a wash to me.

regards, tom lane

#19David Fetter
david@fetter.org
In reply to: Tom Lane (#13)
Re: [PATCHES] Function's LEAST, GREATEST and DECODE (Oracle vararg polymorphic functions)

On Fri, Jun 24, 2005 at 09:21:25AM -0400, Tom Lane wrote:

[ moving to -hackers for a wider audience ]

Today's issue: should the GREATEST/LEAST functions be strict (return
null if any input is null) or not (return null only if all inputs
are null, else return the largest/smallest of the non-null inputs)?

I'd say non-strict unless SQL:2003 says different.

Cheers,
D
--
David Fetter david@fetter.org http://fetter.org/
phone: +1 510 893 6100 mobile: +1 415 235 3778

Remember to vote!

#20Pavel Stehule
stehule@kix.fsv.cvut.cz
In reply to: Bruce Momjian (#4)
ToDo: Allow PL/pgSQL EXECUTE query_var INTO record_var;

Done

Regards
Pavel Stehule

#21Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Pavel Stehule (#20)
Re: ToDo: Allow PL/pgSQL EXECUTE query_var INTO record_var;

TODO updated.

---------------------------------------------------------------------------

Pavel Stehule wrote:

Done

Regards
Pavel Stehule

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073