From: Thomas G. Lockhart Date: Fri, 4 Dec 1998 15:34:49 +0000 (+0000) Subject: Implement CASE expression. X-Git-Tag: REL6_5~940 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bedd04a551c30350ebb8da2aa2596d16053b5d15;p=postgresql Implement CASE expression. --- diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 6f9148b56d..d751b2cb6e 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.38 1998/11/27 19:52:00 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.39 1998/12/04 15:33:19 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,7 @@ * NOTES * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster * will speed up the entire system. Unfortunately they are currently - * implemented recursively.. Eliminating the recursion is bound to + * implemented recursively. Eliminating the recursion is bound to * improve the speed of the executor. * * ExecTargetList() is used to make tuple projections. Rather then @@ -205,7 +205,7 @@ ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull) * variable with respect to given expression context. * * - * As an entry condition, we expect that the the datatype the + * As an entry condition, we expect that the datatype the * plan expects to get (as told by our "variable" argument) is in * fact the datatype of the attribute the plan says to fetch (as * seen in the current context, identified by our "econtext" @@ -1124,6 +1124,79 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) return const_value; } +/* ---------------------------------------------------------------- + * ExecEvalCase + * + * Evaluate a CASE clause. Will have boolean expressions + * inside the WHEN clauses, and will have constants + * for results. + * - thomas 1998-11-09 + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) +{ + List *clauses; + List *clause; + CaseWhen *wclause; + Datum const_value = 0; + bool isDone; + + clauses = caseExpr->args; + + /****************** + * we evaluate each of the WHEN clauses in turn, + * as soon as one is true we return the corresponding + * result. If none are true then we return the value + * of the default clause, or NULL. + ****************** + */ + foreach(clause, clauses) + { + + /****************** + * We don't iterate over sets in the quals, so pass in an isDone + * flag, but ignore it. + ****************** + */ + + wclause = lfirst(clause); + const_value = ExecEvalExpr((Node *) wclause->expr, + econtext, + isNull, + &isDone); + + /****************** + * if we have a true test, then we return the result, + * since the case statement is satisfied. + ****************** + */ + if (DatumGetInt32(const_value) != 0) + { + const_value = ExecEvalExpr((Node *) wclause->result, + econtext, + isNull, + &isDone); + return (Datum) const_value; + + } + } + + if (caseExpr->defresult) + { + const_value = ExecEvalExpr((Node *) caseExpr->defresult, + econtext, + isNull, + &isDone); + } + else + { + *isNull = true; + } + + return const_value; +} + /* ---------------------------------------------------------------- * ExecEvalExpr * @@ -1236,13 +1309,18 @@ ExecEvalExpr(Node *expression, } break; } + case T_CaseExpr: + retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull); + break; + default: elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression)); break; } return retDatum; -} +} /* ExecEvalExpr() */ + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList @@ -1642,3 +1720,4 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone) InvalidBuffer, /* tuple has no buffer */ true); } + diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5809a4044c..33f7b14894 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.50 1998/11/22 10:48:38 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.51 1998/12/04 15:33:33 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -943,6 +943,47 @@ _copySubLink(SubLink *from) return newnode; } +/* ---------------- + * _copyCaseExpr + * ---------------- + */ +static CaseExpr * +_copyCaseExpr(CaseExpr *from) +{ + CaseExpr *newnode = makeNode(CaseExpr); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->casetype = from->casetype; + + Node_Copy(from, newnode, arg); + Node_Copy(from, newnode, args); + Node_Copy(from, newnode, defresult); + + return newnode; +} + +/* ---------------- + * _copyCaseWhen + * ---------------- + */ +static CaseWhen * +_copyCaseWhen(CaseWhen *from) +{ + CaseWhen *newnode = makeNode(CaseWhen); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, expr); + Node_Copy(from, newnode, result); + + return newnode; +} + static Array * _copyArray(Array *from) { @@ -1734,6 +1775,12 @@ copyObject(void *from) case T_SubLink: retval = _copySubLink(from); break; + case T_CaseExpr: + retval = _copyCaseExpr(from); + break; + case T_CaseWhen: + retval = _copyCaseWhen(from); + break; /* * RELATION NODES diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 33bba59d65..2cbec8e615 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.48 1998/11/22 10:48:39 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.49 1998/12/04 15:33:33 thomas Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -1635,6 +1635,82 @@ _outAConst(StringInfo str, A_Const *node) return; } +static void +_outConstraint(StringInfo str, Constraint *node) +{ + char buf[500]; + + sprintf(buf," %s :type", + ((node->name != NULL)? node->name: "<>")); + appendStringInfo(str, buf); + + switch (node->contype) + { + case CONSTR_PRIMARY: + sprintf(buf," PRIMARY KEY "); + appendStringInfo(str, buf); + _outNode(str, node->keys); + break; + + case CONSTR_CHECK: + sprintf(buf," CHECK "); + appendStringInfo(str, buf); + appendStringInfo(str, node->def); + break; + + case CONSTR_DEFAULT: + sprintf(buf," DEFAULT "); + appendStringInfo(str, buf); + appendStringInfo(str, node->def); + break; + + case CONSTR_NOTNULL: + sprintf(buf," NOT NULL "); + appendStringInfo(str, buf); + break; + + case CONSTR_UNIQUE: + sprintf(buf," UNIQUE "); + appendStringInfo(str, buf); + _outNode(str, node->keys); + break; + + default: + sprintf(buf,""); + appendStringInfo(str, buf); + break; + } + return; +} + +static void +_outCaseExpr(StringInfo str, CaseExpr *node) +{ + char buf[500]; + + sprintf(buf, "CASE "); + appendStringInfo(str, buf); + _outNode(str, node->args); + sprintf(buf, " :default "); + appendStringInfo(str, buf); + _outNode(str, node->defresult); + return; +} + +static void +_outCaseWhen(StringInfo str, CaseWhen *node) +{ + char buf[500]; + + sprintf(buf, " :when "); + appendStringInfo(str, buf); + _outNode(str, node->expr); + sprintf(buf, " :then "); + appendStringInfo(str, buf); + _outNode(str, node->result); + return; +} + /* * _outNode - * converts a Node into ascii string and append it to 'str' @@ -1861,6 +1937,15 @@ _outNode(StringInfo str, void *obj) case T_A_Const: _outAConst(str, obj); break; + case T_Constraint: + _outConstraint(str, obj); + break; + case T_CaseExpr: + _outCaseExpr(str, obj); + break; + case T_CaseWhen: + _outCaseWhen(str, obj); + break; default: elog(NOTICE, "_outNode: don't know how to print type %d ", nodeTag(obj)); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ccde0f81ea..af1e1275a0 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.33 1998/11/22 10:48:43 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.34 1998/12/04 15:34:05 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -437,7 +437,7 @@ create_nestloop_node(JoinPath *best_path, * used in the inner scan path, so we need only consider the first * set of qualifications in indxqual. * - * But there may be more than one clauses in this "first set" in the + * But there may be more than one clause in this "first set" in the * case of multi-column indices. - vadim 03/18/97 */ @@ -735,6 +735,11 @@ fix_indxqual_references(Node *clause, Path *index_path) else return clause; } + else if (IsA(clause, CaseExpr)) + { + elog(NOTICE,"optimizer: fix_indxqual_references sees CaseExpr"); + return clause; + } else { List *oldclauses = (List *) clause; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 892a2d45c3..dfacc55518 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.25 1998/11/27 19:52:07 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.26 1998/12/04 15:34:14 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -306,6 +306,26 @@ make_andclause(List *andclauses) return expr; } + +/***************************************************************************** + * CASE clause functions + *****************************************************************************/ + + +/* + * case_clause-- + * + * Returns t iff its argument is a 'case' clause: (CASE { expr }). + * + */ +bool +case_clause(Node *clause) +{ + return + (clause != NULL && + nodeTag(clause) == T_CaseExpr); +} + /***************************************************************************** * * * * @@ -540,6 +560,19 @@ fix_opid(Node *clause) fix_opid((Node *) get_leftop((Expr *) lfirst(lst))); } } + else if (case_clause(clause)) + { + List *lst; + + fix_opid(((CaseExpr *) clause)->defresult); + + /* Run through the WHEN clauses... */ + foreach(lst, ((CaseExpr *) clause)->args) + { + fix_opid(((CaseWhen *) lfirst(lst))->expr); + fix_opid(((CaseWhen *) lfirst(lst))->result); + } + } } @@ -577,13 +610,12 @@ fix_opids(List *clauses) * returned for the value if it is unknown or null. * END OF OLD OBSOLETE COMMENT. * NEW COMMENT: - * when defining rules one of the attibutes of the operator can + * when defining rules one of the attributes of the operator can * be a Param node (which is supposed to be treated as a constant). * However as there is no value specified for a parameter until run time - * this routine used to return "" as value, which made 'compute_selec' + * this routine used to return "" as value, which caused 'compute_selec' * to bomb (because it was expecting a lisp integer and got back a lisp * string). Now the code returns a plain old good "lispInteger(0)". - * */ void get_relattval(Node *clause, @@ -787,3 +819,4 @@ CommuteClause(Node *clause) lfirst(((Expr *) clause)->args) = lsecond(((Expr *) clause)->args); lsecond(((Expr *) clause)->args) = temp; } + diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index dba994423f..66c52404ec 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.20 1998/09/22 21:48:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.21 1998/12/04 15:34:15 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -482,7 +482,6 @@ flatten_tlistentry(Node *tlistentry, List *flat_tlist) flatten_tlistentry(lfirst(elt), flat_tlist)); return ((Node *) make_funcclause((Func *) expr->oper, temp_result)); - } else if (IsA(tlistentry, Aggreg)) { @@ -509,19 +508,36 @@ flatten_tlistentry(Node *tlistentry, List *flat_tlist) return tlistentry; } + else if (case_clause(tlistentry)) + { + CaseExpr *cexpr = (CaseExpr *) tlistentry; + CaseWhen *cwhen; + List *elt = NIL; + + foreach(elt, cexpr->args) + cwhen = (CaseWhen *)lfirst(elt); + cwhen->result = flatten_tlistentry(cwhen->result, flat_tlist); + cexpr->defresult = flatten_tlistentry(cexpr->defresult, flat_tlist); + + return ((Node *) cexpr); + } else { - Expr *expr = (Expr *) tlistentry; + Expr *expr, *final; + Var *left, *right; + + Assert(IsA(tlistentry, Expr)); - Var *left = (Var *) flatten_tlistentry((Node *) get_leftop(expr), + expr = (Expr *) tlistentry; + left = (Var *) flatten_tlistentry((Node *) get_leftop(expr), flat_tlist); - Var *right = (Var *) flatten_tlistentry((Node *) get_rightop(expr), + right = (Var *) flatten_tlistentry((Node *) get_rightop(expr), flat_tlist); - Expr *final = make_opclause((Oper *) expr->oper, left, right); - Assert(IsA(tlistentry, Expr)); + final = make_opclause((Oper *) expr->oper, left, right); final->opType = expr->opType; final->typeOid = expr->typeOid; + return (Node *)final; } } diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 4f7b0b41b4..3dade59a5c 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.13 1998/09/01 03:24:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.14 1998/12/04 15:34:15 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -137,6 +137,21 @@ contain_var_clause(Node *clause) else if (is_opclause(clause)) return (contain_var_clause((Node *) get_leftop((Expr *) clause)) || contain_var_clause((Node *) get_rightop((Expr *) clause))); + else if (case_clause(clause)) + { + List *args; + CaseWhen *when; + + foreach(args, ((CaseExpr *) clause)->args) + { + when = lfirst(args); + if (contain_var_clause(when->expr)) + return TRUE; + if (contain_var_clause(when->result)) + return TRUE; + } + return (contain_var_clause(((CaseExpr *) clause)->defresult)); + } return FALSE; } @@ -199,6 +214,18 @@ pull_var_clause(Node *clause) else if (is_opclause(clause)) retval = nconc(pull_var_clause((Node *) get_leftop((Expr *) clause)), pull_var_clause((Node *) get_rightop((Expr *) clause))); + else if (case_clause(clause)) + { + List *temp; + + foreach(temp, ((CaseExpr *) clause)->args) + { + retval = nconc(retval, pull_var_clause(((CaseWhen *) lfirst(temp))->expr)); + retval = nconc(retval, pull_var_clause(((CaseWhen *) lfirst(temp))->result)); + } + + retval = nconc(retval, pull_var_clause(((CaseExpr *) clause)->defresult)); + } else retval = NIL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index f3e3f9e078..0a692bd506 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.89 1998/10/28 16:06:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.90 1998/12/04 15:34:28 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -396,7 +396,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ if ((qry->hasAggs == false) && (qry->havingQual != NULL)) { - elog(ERROR, "This is not a valid having query!"); + elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); return (Query *) NIL; } @@ -621,7 +621,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) break; default: - elog(ERROR, "parser: internal error; unrecognized constraint", NULL); + elog(ERROR, "parser: unrecognized constraint (internal error)", NULL); break; } clist = lnext(clist); @@ -653,16 +653,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) case CONSTR_NOTNULL: case CONSTR_DEFAULT: - elog(ERROR, "parser: internal error; illegal context for constraint", NULL); + elog(ERROR, "parser: illegal context for constraint (internal error)", NULL); break; default: - elog(ERROR, "parser: internal error; unrecognized constraint", NULL); + elog(ERROR, "parser: unrecognized constraint (internal error)", NULL); break; } break; default: - elog(ERROR, "parser: internal error; unrecognized node", NULL); + elog(ERROR, "parser: unrecognized node (internal error)", NULL); } elements = lnext(elements); @@ -684,7 +684,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) { constraint = lfirst(dlist); if (nodeTag(constraint) != T_Constraint) - elog(ERROR, "parser: internal error; unrecognized deferred node", NULL); + elog(ERROR, "parser: unrecognized deferred node (internal error)", NULL); if (constraint->contype == CONSTR_PRIMARY) { @@ -695,7 +695,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) have_pkey = TRUE; } else if (constraint->contype != CONSTR_UNIQUE) - elog(ERROR, "parser: internal error; unrecognized deferred constraint", NULL); + elog(ERROR, "parser: unrecognized deferred constraint (internal error)", NULL); index = makeNode(IndexStmt); @@ -735,7 +735,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) columns = lnext(columns); } if (column == NULL) - elog(ERROR, "parser: column '%s' in key does not exist", key->name); + elog(ERROR, "CREATE TABLE column '%s' in key does not exist", key->name); if (constraint->contype == CONSTR_PRIMARY) column->is_not_null = TRUE; @@ -753,7 +753,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) } if (index->idxname == NULL) - elog(ERROR, "parser: unable to construct implicit index for table %s" + elog(ERROR, "CREATE TABLE unable to construct implicit index for table %s" "; name too long", stmt->relname); else elog(NOTICE, "CREATE TABLE/%s will create implicit index %s for table %s", @@ -918,8 +918,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) /* * The havingQual has a similar meaning as "qual" in the where * statement. So we can easily use the code from the "where clause" - * with some additional traversals done in - * .../optimizer/plan/planner.c + * with some additional traversals done in optimizer/plan/planner.c */ qry->havingQual = transformWhereClause(pstate, stmt->havingClause); @@ -955,7 +954,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) */ if ((qry->hasAggs == false) && (qry->havingQual != NULL)) { - elog(ERROR, "This is not a valid having query!"); + elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); return (Query *) NIL; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d2e34484da..9382d4a7dc 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.37 1998/10/14 15:56:43 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.38 1998/12/04 15:34:29 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -208,6 +208,8 @@ Oid param_type(int t); /* used in parse_expr.c */ %type row_descriptor, row_list, c_list, c_expr %type row_expr %type row_op +%type case_expr, case_arg, when_clause, case_default +%type when_clause_list %type sub_type %type OptCreateAs, CreateAsList %type CreateAsElement @@ -259,26 +261,27 @@ Oid param_type(int t); /* used in parse_expr.c */ /* Keywords (in SQL92 reserved words) */ %token ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC, BEGIN_TRANS, BETWEEN, BOTH, BY, - CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT, + CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE, + COALESCE, COLLATE, COLUMN, COMMIT, CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, - END_TRANS, EXECUTE, EXISTS, EXTRACT, + ELSE, END_TRANS, EXECUTE, EXISTS, EXTRACT, FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, GRANT, GROUP, HAVING, HOUR_P, IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS, JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL, MATCH, MINUTE_P, MONTH_P, NAMES, - NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC, + NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC, OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P, PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC, READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK, SCROLL, SECOND_P, SELECT, SET, SUBSTRING, - TABLE, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, + TABLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P, UNION, UNIQUE, UPDATE, USER, USING, VALUES, VARCHAR, VARYING, VIEW, - WHERE, WITH, WORK, YEAR_P, ZONE + WHEN, WHERE, WITH, WORK, YEAR_P, ZONE /* Keywords (in SQL3 reserved words) */ %token TRIGGER @@ -2861,7 +2864,7 @@ opt_array_bounds: '[' ']' nest_array_bounds { $$ = lcons(makeInteger(-1), $3); } | '[' Iconst ']' nest_array_bounds { $$ = lcons(makeInteger($2), $4); } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; @@ -3276,14 +3279,14 @@ sub_type: ANY { $$ = ANY_SUBLINK; } | ALL { $$ = ALL_SUBLINK; } ; -/* +/* General expressions * This is the heart of the expression syntax. * Note that the BETWEEN clause looks similar to a boolean expression * and so we must define b_expr which is almost the same as a_expr * but without the boolean expressions. - * All operations are allowed in a BETWEEN clause if surrounded by parens. + * All operations/expressions are allowed in a BETWEEN clause + * if surrounded by parens. */ - a_expr: attr opt_indirection { $1->indirection = $2; @@ -3895,14 +3898,15 @@ a_expr: attr opt_indirection { $$ = makeA_Expr(OR, NULL, $1, $3); } | NOT a_expr { $$ = makeA_Expr(NOT, NULL, NULL, $2); } + | case_expr + { $$ = $1; } ; -/* +/* Restricted expressions * b_expr is a subset of the complete expression syntax * defined by a_expr. b_expr is used in BETWEEN clauses * to eliminate parser ambiguities stemming from the AND keyword. */ - b_expr: attr opt_indirection { $1->indirection = $2; @@ -4150,7 +4154,7 @@ opt_indirection: '[' a_expr ']' opt_indirection ai->uidx = $4; $$ = lcons(ai, $6); } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; @@ -4169,7 +4173,7 @@ extract_list: extract_arg FROM a_expr n->val.val.str = $1; $$ = lappend(lcons((Node *)n,NIL), $3); } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; @@ -4180,7 +4184,7 @@ extract_arg: datetime { $$ = $1; } position_list: position_expr IN position_expr { $$ = makeList($3, $1, -1); } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; @@ -4314,13 +4318,13 @@ substr_list: expr_list substr_from substr_for { $$ = nconc(nconc($1,$2),$3); } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; substr_from: FROM expr_list { $$ = $2; } - | /* EMPTY */ + | /*EMPTY*/ { A_Const *n = makeNode(A_Const); n->val.type = T_Integer; @@ -4331,7 +4335,7 @@ substr_from: FROM expr_list substr_for: FOR expr_list { $$ = $2; } - | /* EMPTY */ + | /*EMPTY*/ { $$ = NIL; } ; @@ -4379,6 +4383,94 @@ not_in_expr_nodes: AexprConst } ; +/* Case clause + * Define SQL92-style case clause. + * Allow all four forms described in the standard: + * - Full specification + * CASE WHEN a = b THEN c ... ELSE d END + * - Implicit argument + * CASE a WHEN b THEN c ... ELSE d END + * - Conditional NULL + * NULLIF(x,y) + * same as CASE WHEN x = y THEN NULL ELSE x END + * - Conditional substitution from list, use first non-null argument + * COALESCE(a,b,...) + * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END + * - thomas 1998-11-09 + */ +case_expr: CASE case_arg when_clause_list case_default END_TRANS + { + CaseExpr *c = makeNode(CaseExpr); + c->arg = $2; + c->args = $3; + c->defresult = $4; + $$ = (Node *)c; + } + | NULLIF '(' a_expr ',' a_expr ')' + { + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w = makeNode(CaseWhen); + c->args = lcons(w, NIL); + c->defresult = $3; + w->expr = makeA_Expr(OP, "=", $3, $5); + $$ = (Node *)c; + + elog(NOTICE,"NULLIF() not yet fully implemented"); + } + | COALESCE '(' expr_list ')' + { + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w; + List *l; + foreach (l,$3) + { + w = makeNode(CaseWhen); + w->expr = makeA_Expr(NOTNULL, NULL, lfirst(l), NULL); + w->result = lfirst(l); + c->args = lappend(c->args, w); + } + $$ = (Node *)c; + + elog(NOTICE,"COALESCE() not yet fully implemented"); + } + ; + +when_clause_list: when_clause_list when_clause + { $$ = lappend($1, $2); } + | when_clause + { $$ = lcons($1, NIL); } + ; + +when_clause: WHEN a_expr THEN a_expr_or_null + { + CaseWhen *w = makeNode(CaseWhen); + w->expr = $2; + w->result = $4; + $$ = (Node *)w; + } + ; + +case_default: ELSE a_expr_or_null { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +case_arg: attr opt_indirection + { + $1->indirection = $2; + $$ = (Node *)$1; + } + | ColId + { + /* could be a column name or a relation_name */ + Ident *n = makeNode(Ident); + n->name = $1; + n->indirection = NULL; + $$ = (Node *)n; + } + | /*EMPTY*/ + { $$ = NULL; } + ; + attr: relation_name '.' attrs { $$ = makeNode(Attr); @@ -4512,7 +4604,7 @@ res_target_el2: a_expr_or_null AS ColLabel ; opt_id: ColId { $$ = $1; } - | /* EMPTY */ { $$ = NULL; } + | /*EMPTY*/ { $$ = NULL; } ; relation_name: SpecialRuleRelation @@ -4723,12 +4815,16 @@ ColLabel: ColId { $$ = $1; } | ABORT_TRANS { $$ = "abort"; } | ANALYZE { $$ = "analyze"; } | BINARY { $$ = "binary"; } + | CASE { $$ = "case"; } | CLUSTER { $$ = "cluster"; } + | COALESCE { $$ = "coalesce"; } | CONSTRAINT { $$ = "constraint"; } | COPY { $$ = "copy"; } | CROSS { $$ = "cross"; } | CURRENT { $$ = "current"; } | DO { $$ = "do"; } + | ELSE { $$ = "else"; } + | END_TRANS { $$ = "end"; } | EXPLAIN { $$ = "explain"; } | EXTEND { $$ = "extend"; } | FALSE_P { $$ = "false"; } @@ -4740,6 +4836,7 @@ ColLabel: ColId { $$ = $1; } | MOVE { $$ = "move"; } | NEW { $$ = "new"; } | NONE { $$ = "none"; } + | NULLIF { $$ = "nullif"; } | ORDER { $$ = "order"; } | POSITION { $$ = "position"; } | PRECISION { $$ = "precision"; } @@ -4747,10 +4844,12 @@ ColLabel: ColId { $$ = $1; } | SETOF { $$ = "setof"; } | SHOW { $$ = "show"; } | TABLE { $$ = "table"; } + | THEN { $$ = "then"; } | TRANSACTION { $$ = "transaction"; } | TRUE_P { $$ = "true"; } | VACUUM { $$ = "vacuum"; } | VERBOSE { $$ = "verbose"; } + | WHEN { $$ = "when"; } ; SpecialRuleRelation: CURRENT diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index eddfd0b315..aa9a9c329c 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.48 1998/10/18 23:30:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.49 1998/12/04 15:34:29 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -51,12 +51,14 @@ static ScanKeyword ScanKeywords[] = { {"by", BY}, {"cache", CACHE}, {"cascade", CASCADE}, + {"case", CASE}, {"cast", CAST}, {"char", CHAR}, {"character", CHARACTER}, {"check", CHECK}, {"close", CLOSE}, {"cluster", CLUSTER}, + {"coalesce", COALESCE}, {"collate", COLLATE}, {"column", COLUMN}, {"commit", COMMIT}, @@ -88,6 +90,7 @@ static ScanKeyword ScanKeywords[] = { {"double", DOUBLE}, {"drop", DROP}, {"each", EACH}, + {"else", ELSE}, {"encoding", ENCODING}, {"end", END_TRANS}, {"execute", EXECUTE}, @@ -154,6 +157,7 @@ static ScanKeyword ScanKeywords[] = { {"notify", NOTIFY}, {"notnull", NOTNULL}, {"null", NULL_P}, + {"nullif", NULLIF}, {"numeric", NUMERIC}, {"of", OF}, {"oids", OIDS}, @@ -201,6 +205,7 @@ static ScanKeyword ScanKeywords[] = { {"stdout", STDOUT}, {"substring", SUBSTRING}, {"table", TABLE}, + {"then", THEN}, {"time", TIME}, {"timestamp", TIMESTAMP}, {"timezone_hour", TIMEZONE_HOUR}, @@ -228,6 +233,7 @@ static ScanKeyword ScanKeywords[] = { {"verbose", VERBOSE}, {"version", VERSION}, {"view", VIEW}, + {"when", WHEN}, {"where", WHERE}, {"with", WITH}, {"work", WORK}, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 4546922633..4af1f7eafa 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.9 1998/10/22 13:50:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.10 1998/12/04 15:34:30 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -342,7 +342,7 @@ TypeCategory(Oid inType) /* IsPreferredType() - * Assign a category to the specified OID. + * Check if this type is a preferred type. */ bool IsPreferredType(CATEGORY category, Oid type) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 0c2a40a190..8dcabc48db 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; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index f05764e765..e77183387d 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.30 1998/10/08 18:29:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.31 1998/12/04 15:34:30 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,9 @@ static Node *SizeTargetExpr(ParseState *pstate, Oid attrtype, int32 attrtypmod); +static TargetEntry * +MakeTargetEntryCase(ParseState *pstate, + ResTarget *res); /* MakeTargetEntryIdent() * Transforms an Ident Node to a Target Entry @@ -53,11 +56,7 @@ static Node *SizeTargetExpr(ParseState *pstate, */ TargetEntry * MakeTargetEntryIdent(ParseState *pstate, -#if FALSE - Ident *ident, -#else Node *node, -#endif char **resname, char *refname, char *colname, @@ -77,7 +76,7 @@ MakeTargetEntryIdent(ParseState *pstate, pstate->p_insert_columns = lnext(pstate->p_insert_columns); } else - elog(ERROR, "insert: more expressions than target columns"); + elog(ERROR, "INSERT has more expressions than target columns"); } if (pstate->p_is_insert || pstate->p_is_update) @@ -105,7 +104,7 @@ MakeTargetEntryIdent(ParseState *pstate, { rte = colnameRangeTableEntry(pstate, colname); if (rte == (RangeTblEntry *) NULL) - elog(ERROR, "attribute %s not found", colname); + elog(ERROR, "Attribute %s not found", colname); refname = rte->refname; } @@ -129,14 +128,9 @@ MakeTargetEntryIdent(ParseState *pstate, } else { -#if TRUE elog(ERROR, "Unable to convert %s to %s for column %s", typeidTypeName(attrtype_id), typeidTypeName(attrtype_target), target_colname); -#else - elog(ERROR, "Type or size of %s(%d) does not match target column %s(%d)", - colname, attrtypmod, target_colname, attrtypmod_target); -#endif } } } @@ -152,11 +146,7 @@ MakeTargetEntryIdent(ParseState *pstate, name = ((*resname != NULL) ? *resname : colname); -#if FALSE - expr = transformIdent(pstate, (Node *) ident, EXPR_COLUMN_FIRST); -#else expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST); -#endif attrtype_target = exprType(expr); if (nodeTag(expr) == T_Var) @@ -187,7 +177,7 @@ MakeTargetEntryIdent(ParseState *pstate, * - thomas 1998-05-08 * * Added resjunk flag and made extern so that it can be use by GROUP/ - * ORDER BY a function or expersion not in the target_list + * ORDER BY a function or expression not in the target_list * - daveh@insightdist.com 1998-07-31 */ TargetEntry * @@ -207,7 +197,7 @@ MakeTargetEntryExpr(ParseState *pstate, Resdom *resnode; if (expr == NULL) - elog(ERROR, "MakeTargetEntryExpr: invalid use of NULL expression"); + elog(ERROR, "Invalid use of NULL expression (internal error)"); type_id = exprType(expr); if (nodeTag(expr) == T_Var) @@ -251,9 +241,9 @@ MakeTargetEntryExpr(ParseState *pstate, expr = CoerceTargetExpr(pstate, expr, type_id, typelem); if (!HeapTupleIsValid(expr)) - elog(ERROR, "parser: attribute '%s' is of type '%s'" + elog(ERROR, "Attribute '%s' is of type '%s'" " but expression is of type '%s'" - "\n\tYou will need to rewrite or cast the expression", + "\n\tYou will need to rewrite or cast the expression", colname, typeidTypeName(attrtype), typeidTypeName(type_id)); @@ -323,6 +313,45 @@ MakeTargetEntryExpr(ParseState *pstate, return makeTargetEntry(resnode, expr); } /* MakeTargetEntryExpr() */ +/* + * MakeTargetEntryCase() + * Make a TargetEntry from a case node. + */ +static TargetEntry * +MakeTargetEntryCase(ParseState *pstate, + ResTarget *res) +{ + TargetEntry *tent; + CaseExpr *expr; + Resdom *resnode; + int resdomno; + Oid type_id; + int32 type_mod; + + expr = (CaseExpr *)transformExpr(pstate, (Node *)res->val, EXPR_COLUMN_FIRST); + + type_id = expr->casetype; + type_mod = -1; + handleTargetColname(pstate, &res->name, NULL, NULL); + if (res->name == NULL) + res->name = FigureColname((Node *)expr, res->val); + + resdomno = pstate->p_last_resno++; + resnode = makeResdom((AttrNumber) resdomno, + (Oid) type_id, + type_mod, + res->name, + (Index) 0, + (Oid) 0, + 0); + + tent = makeNode(TargetEntry); + tent->resdom = resnode; + tent->expr = (Node *)expr; + + return tent; +} /* MakeTargetEntryCase() */ + /* * MakeTargetEntryComplex() * Make a TargetEntry from a complex node. @@ -351,7 +380,7 @@ MakeTargetEntryComplex(ParseState *pstate, Value *constval; if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const)) - elog(ERROR, "yyparse: string constant expected"); + elog(ERROR, "String constant expected (internal error)"); val = (char *) textout((struct varlena *) ((Const *) expr)->constvalue); @@ -376,7 +405,7 @@ MakeTargetEntryComplex(ParseState *pstate, else lindx[i] = 1; if (lindx[i] > uindx[i]) - elog(ERROR, "yyparse: lower index cannot be greater than upper index"); + elog(ERROR, "Lower index cannot be greater than upper index"); sprintf(str, "[%d:%d]", lindx[i], uindx[i]); str += strlen(str); @@ -388,7 +417,7 @@ MakeTargetEntryComplex(ParseState *pstate, resdomno = attnameAttNum(rd, res->name); ndims = attnumAttNelems(rd, resdomno); if (i != ndims) - elog(ERROR, "yyparse: array dimensions do not match"); + elog(ERROR, "Array dimensions do not match"); constval = makeNode(Value); constval->type = T_String; @@ -400,9 +429,9 @@ MakeTargetEntryComplex(ParseState *pstate, } else { - char *colname = res->name; - /* this is not an array assignment */ + char *colname = res->name; + if (colname == NULL) { @@ -540,6 +569,11 @@ transformTargetList(ParseState *pstate, List *targetlist) tent = MakeTargetEntryComplex(pstate, res); break; } + case T_CaseExpr: + { + tent = MakeTargetEntryCase(pstate, res); + break; + } case T_Attr: { bool expand_star = false; @@ -604,7 +638,7 @@ transformTargetList(ParseState *pstate, List *targetlist) } default: /* internal error */ - elog(ERROR, "internal error: do not know how to transform targetlist"); + elog(ERROR, "Unable to transform targetlist (internal error)"); break; } @@ -788,7 +822,7 @@ ExpandAllTables(ParseState *pstate) /* this should not happen */ if (rtable == NULL) - elog(ERROR, "cannot expand: null p_rtable"); + elog(ERROR, "Cannot expand tables; null p_rtable (internal error)"); /* * go through the range table and make a list of range table entries @@ -838,7 +872,7 @@ FigureColname(Node *expr, Node *resval) { switch (nodeTag(expr)) { - case T_Aggreg: + case T_Aggreg: return (char *) ((Aggreg *) expr)->aggname; case T_Expr: if (((Expr *) expr)->opType == FUNC_EXPR) diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 8eaa8dbb94..89665d8858 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.25 1998/10/21 16:21:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.26 1998/12/04 15:34:36 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -39,7 +39,6 @@ #include "catalog/pg_type.h" - static RewriteInfo *gatherRewriteMeta(Query *parsetree, Query *rule_action, Node *rule_qual, @@ -55,17 +54,6 @@ static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree); static void modifyAggregQual(Node **nodePtr, Query *parsetree); - - - - - - - - - - - static Query *fireRIRrules(Query *parsetree); @@ -293,6 +281,46 @@ rangeTableEntry_used(Node *node, int rt_index, int sublevels_up) } break; + case T_CaseExpr: + { + CaseExpr *exp = (CaseExpr *)node; + + if (rangeTableEntry_used( + (Node *)(exp->args), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(exp->defresult), + rt_index, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + + case T_CaseWhen: + { + CaseWhen *when = (CaseWhen *)node; + + if (rangeTableEntry_used( + (Node *)(when->expr), + rt_index, + sublevels_up)) + return TRUE; + + if (rangeTableEntry_used( + (Node *)(when->result), + rt_index, + sublevels_up)) + return TRUE; + + return FALSE; + } + break; + case T_Query: { Query *qry = (Query *)node; @@ -1055,10 +1083,12 @@ modifyAggregMakeSublink(Expr *origexp, Query *parsetree) Expr *exp = copyObject(origexp); if (nodeTag(nth(0, exp->args)) == T_Aggreg) + { if (nodeTag(nth(1, exp->args)) == T_Aggreg) elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported"); else elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual"); + } aggreg = (Aggreg *)nth(1, exp->args); target = (Var *)(aggreg->target); @@ -1189,6 +1219,33 @@ modifyAggregQual(Node **nodePtr, Query *parsetree) } break; + case T_CaseExpr: + { + /* We're calling recursively, + * and this routine knows how to handle lists + * so let it do the work to handle the WHEN clauses... */ + modifyAggregQual( + (Node **)(&(((CaseExpr *)node)->args)), + parsetree); + + modifyAggregQual( + (Node **)(&(((CaseExpr *)node)->defresult)), + parsetree); + } + break; + + case T_CaseWhen: + { + modifyAggregQual( + (Node **)(&(((CaseWhen *)node)->expr)), + parsetree); + + modifyAggregQual( + (Node **)(&(((CaseWhen *)node)->result)), + parsetree); + } + break; + case T_Iter: { Iter *iter = (Iter *)node; @@ -1827,6 +1884,10 @@ fireRIRonSubselect(Node *node) } break; + case T_CaseExpr: + case T_CaseWhen: + break; + case T_Query: { Query *qry = (Query *)node; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 5ddbfd142e..526e847de9 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.31 1998/10/01 02:04:01 tgl Exp $ + * $Id: nodes.h,v 1.32 1998/12/04 15:34:44 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -213,11 +213,13 @@ typedef enum NodeTag T_SortClause, T_GroupClause, T_SubSelect, - T_JoinUsing + T_JoinUsing, + T_CaseExpr, + T_CaseWhen } NodeTag; /* - * The first field of a node of any type is gauranteed to be the NodeTag. + * The first field of a node of any type is guaranteed to be the NodeTag. * Hence the type of any node can be gotten by casting it to Node. Declaring * a variable to be of Node * (instead of void *) can also facilitate * debugging. diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index afe6fe43f7..20fc0788bd 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.61 1998/10/22 13:52:24 momjian Exp $ + * $Id: parsenodes.h,v 1.62 1998/12/04 15:34:44 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -713,6 +713,28 @@ typedef struct A_Const TypeName *typename; /* typecast */ } A_Const; +/* + * CaseExpr - a CASE expression + */ +typedef struct CaseExpr +{ + NodeTag type; + Oid casetype; + Node *arg; /* implicit equality comparison argument */ + List *args; /* the arguments (list of WHEN clauses) */ + Node *defresult; /* the default result (ELSE clause) */ +} CaseExpr; + +/* + * CaseWhen - an argument to a CASE expression + */ +typedef struct CaseWhen +{ + NodeTag type; + Node *expr; /* comparison expression */ + Node *result; /* substitution result */ +} CaseWhen; + /* * ColumnDef - column definition (used in various creates) */ @@ -777,7 +799,7 @@ typedef struct ResTarget } ResTarget; /* - * ParamString - used in with clauses + * ParamString - used in WITH clauses */ typedef struct ParamString { @@ -797,7 +819,7 @@ typedef struct RelExpr } RelExpr; /* - * SortGroupBy - for order by clause + * SortGroupBy - for ORDER BY clause */ typedef struct SortGroupBy { @@ -807,7 +829,7 @@ typedef struct SortGroupBy } SortGroupBy; /* - * JoinUsing - for join using clause + * JoinUsing - for JOIN USING clause */ typedef struct JoinUsing { @@ -818,7 +840,7 @@ typedef struct JoinUsing } JoinUsing; /* - * RangeVar - range variable, used in from clauses + * RangeVar - range variable, used in FROM clauses */ typedef struct RangeVar { @@ -828,7 +850,7 @@ typedef struct RangeVar } RangeVar; /* - * IndexElem - index parameters (used in create index) + * IndexElem - index parameters (used in CREATE INDEX) */ typedef struct IndexElem { diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 08f4a84f10..3f93920af8 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.13 1998/09/01 04:36:53 momjian Exp $ + * $Id: clauses.h,v 1.14 1998/12/04 15:34:49 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,8 @@ extern Expr *get_notclausearg(Expr *notclause); extern bool and_clause(Node *clause); extern Expr *make_andclause(List *andclauses); +extern bool case_clause(Node *clause); + extern List *pull_constant_clauses(List *quals, List **constantQual); extern void clause_get_relids_vars(Node *clause, List **relids, List **vars); extern int NumRelids(Node *clause);