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 ---- + <literal>DECODE</literal> + + + DECODE + + + + DECODE(expr search, result,search, result, default) + + + + 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 + + + + <literal>GREATEST</literal> and <literal>LEAST</literal> + + + GREATEST + + + + LEAST + + + + GREATEST(value , ...) + + + LEAST(value , ...) + + + + The GREATEST and LEAST functions determine the largest and smallest values from multiple + columns or expressions. + + + + + + <literal>NULLIF</> 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');