aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_expr.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>1998-12-04 15:34:49 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>1998-12-04 15:34:49 +0000
commitbedd04a551c30350ebb8da2aa2596d16053b5d15 (patch)
treee682a8c3fa0a7d849793a7cc0abb101256e6cec5 /src/backend/parser/parse_expr.c
parent19740e2fff22e07244183ac0e888794e63016c89 (diff)
downloadpostgresql-bedd04a551c30350ebb8da2aa2596d16053b5d15.tar.gz
postgresql-bedd04a551c30350ebb8da2aa2596d16053b5d15.zip
Implement CASE expression.
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r--src/backend/parser/parse_expr.c156
1 files changed, 154 insertions, 2 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 0c2a40a190a..8dcabc48dbb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.36 1998/10/02 16:23:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.37 1998/12/04 15:34:30 thomas Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,6 +29,7 @@
#include "parser/parse_node.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
+#include "parser/parse_coerce.h"
#include "utils/builtins.h"
static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod);
@@ -265,7 +266,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
foreach(args, fn->args)
lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
result = ParseFuncOrColumn(pstate,
- fn->funcname, fn->args, &pstate->p_last_resno,
+ fn->funcname,
+ fn->args,
+ &pstate->p_last_resno,
precedence);
break;
}
@@ -332,6 +335,146 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
}
+ case T_CaseExpr:
+ {
+ CaseExpr *c = (CaseExpr *) expr;
+ CaseWhen *w;
+ List *args;
+ Oid ptype;
+ CATEGORY pcategory;
+
+ /* transform the list of arguments */
+ foreach(args, c->args)
+ {
+ w = lfirst(args);
+ /* shorthand form was specified, so expand... */
+ if (c->arg != NULL)
+ {
+ A_Expr *a = makeNode(A_Expr);
+ a->oper = OP;
+ a->opname = "=";
+ a->lexpr = c->arg;
+ a->rexpr = w->expr;
+ w->expr = (Node *)a;
+ }
+ lfirst(args) = transformExpr(pstate, (Node *) w, precedence);
+
+ if (w->result == NULL)
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_Null;
+ w->result = (Node *)n;
+ }
+ }
+
+ if (c->defresult == NULL)
+ {
+ A_Const *n = makeNode(A_Const);
+ n->val.type = T_Null;
+ c->defresult = (Node *)n;
+ }
+ c->defresult = transformExpr(pstate, (Node *) c->defresult, precedence);
+ c->casetype = exprType(c->defresult);
+
+ /* now check types across result clauses... */
+ ptype = c->casetype;
+ pcategory = TypeCategory(ptype);
+ foreach(args, c->args)
+ {
+ Oid wtype;
+
+ w = lfirst(args);
+ wtype = exprType(w->result);
+ /* move on to next one if no new information... */
+ if (wtype && (wtype != UNKNOWNOID)
+ && (wtype != ptype))
+ {
+ /* so far, only nulls so take anything... */
+ if (!ptype)
+ {
+ ptype = wtype;
+ pcategory = TypeCategory(ptype);
+ }
+ /* both types in different categories? then not much hope... */
+ else if ((TypeCategory(wtype) != pcategory)
+ || ((TypeCategory(wtype) == USER_TYPE)
+ && (TypeCategory(c->casetype) == USER_TYPE)))
+ {
+ elog(ERROR,"CASE/WHEN types '%s' and '%s' not matched",
+ typeidTypeName(c->casetype), typeidTypeName(wtype));
+ }
+ /* new one is preferred and can convert? then take it... */
+ else if (IsPreferredType(pcategory, wtype)
+ && can_coerce_type(1, &ptype, &wtype))
+ {
+ ptype = wtype;
+ pcategory = TypeCategory(ptype);
+ }
+ }
+ }
+
+ /* Convert default clause, if necessary */
+ if (c->casetype != ptype)
+ {
+ if (! c->casetype)
+ {
+ /* default clause is NULL,
+ * so assign preferred type from WHEN clauses... */
+ c->casetype = ptype;
+ }
+ else if (can_coerce_type(1, &c->casetype, &ptype))
+ {
+ c->defresult = coerce_type(pstate, c->defresult, c->casetype, ptype);
+ c->casetype = ptype;
+ }
+ else
+ {
+ elog(ERROR,"CASE/ELSE unable to convert to type %s",
+ typeidTypeName(ptype));
+ }
+ }
+
+ /* Convert when clauses, if not null and if necessary */
+ foreach(args, c->args)
+ {
+ Oid wtype;
+
+ w = lfirst(args);
+ wtype = exprType(w->result);
+ /* only bother with conversion if not NULL and different type... */
+ if (wtype && (wtype != ptype))
+ {
+ if (can_coerce_type(1, &wtype, &ptype))
+ {
+ w->result = coerce_type(pstate, w->result, wtype, ptype);
+ }
+ else
+ {
+ elog(ERROR,"CASE/WHEN unable to convert to type %s",
+ typeidTypeName(ptype));
+ }
+ }
+ }
+
+ result = expr;
+ break;
+ }
+
+ case T_CaseWhen:
+ {
+ CaseWhen *w = (CaseWhen *) expr;
+
+ w->expr = transformExpr(pstate, (Node *) w->expr, precedence);
+ if (exprType(w->expr) != BOOLOID)
+ elog(ERROR,"WHEN clause must have a boolean result");
+
+ /* result is NULL for NULLIF() construct - thomas 1998-11-11 */
+ if (w->result != NULL)
+ w->result = transformExpr(pstate, (Node *) w->result, precedence);
+ result = expr;
+ break;
+ }
+
/* Some nodes do _not_ come from the original parse tree,
* but result from parser transformation in this phase.
* At least one construct (BETWEEN/AND) puts the same nodes
@@ -423,6 +566,9 @@ exprType(Node *expr)
{
Oid type = (Oid) 0;
+ if (!expr)
+ return type;
+
switch (nodeTag(expr))
{
case T_Func:
@@ -452,6 +598,12 @@ exprType(Node *expr)
case T_SubLink:
type = BOOLOID;
break;
+ case T_CaseExpr:
+ type = ((CaseExpr *) expr)->casetype;
+ break;
+ case T_CaseWhen:
+ type = exprType(((CaseWhen *) expr)->result);
+ break;
case T_Ident:
/* is this right? */
type = UNKNOWNOID;