*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
* 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"
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
*
}
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
InvalidBuffer, /* tuple has no buffer */
true);
}
+
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
case T_SubLink:
retval = _copySubLink(from);
break;
+ case T_CaseExpr:
+ retval = _copyCaseExpr(from);
+ break;
+ case T_CaseWhen:
+ retval = _copyCaseWhen(from);
+ break;
/*
* RELATION NODES
*
*
* 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
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,"<unrecognized constraint>");
+ 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'
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));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
*/
else
return clause;
}
+ else if (IsA(clause, CaseExpr))
+ {
+ elog(NOTICE,"optimizer: fix_indxqual_references sees CaseExpr");
+ return clause;
+ }
else
{
List *oldclauses = (List *) clause;
*
*
* 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
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);
+}
+
/*****************************************************************************
* *
* *
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);
+ }
+ }
}
* 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,
lfirst(((Expr *) clause)->args) = lsecond(((Expr *) clause)->args);
lsecond(((Expr *) clause)->args) = temp;
}
+
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
flatten_tlistentry(lfirst(elt), flat_tlist));
return ((Node *) make_funcclause((Func *) expr->oper, temp_result));
-
}
else if (IsA(tlistentry, Aggreg))
{
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;
}
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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;
}
break;
default:
- elog(ERROR, "parser: internal error; unrecognized constraint", NULL);
+ elog(ERROR, "parser: unrecognized constraint (internal error)", NULL);
break;
}
clist = lnext(clist);
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);
{
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)
{
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);
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;
}
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",
/*
* 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);
*/
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;
}
*
*
* 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
%type <list> row_descriptor, row_list, c_list, c_expr
%type <node> row_expr
%type <str> row_op
+%type <node> case_expr, case_arg, when_clause, case_default
+%type <list> when_clause_list
%type <ival> sub_type
%type <list> OptCreateAs, CreateAsList
%type <node> CreateAsElement
/* 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
{ $$ = lcons(makeInteger(-1), $3); }
| '[' Iconst ']' nest_array_bounds
{ $$ = lcons(makeInteger($2), $4); }
- | /* EMPTY */
+ | /*EMPTY*/
{ $$ = NIL; }
;
| 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;
{ $$ = 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;
ai->uidx = $4;
$$ = lcons(ai, $6);
}
- | /* EMPTY */
+ | /*EMPTY*/
{ $$ = NIL; }
;
n->val.val.str = $1;
$$ = lappend(lcons((Node *)n,NIL), $3);
}
- | /* EMPTY */
+ | /*EMPTY*/
{ $$ = NIL; }
;
position_list: position_expr IN position_expr
{ $$ = makeList($3, $1, -1); }
- | /* EMPTY */
+ | /*EMPTY*/
{ $$ = NIL; }
;
{
$$ = 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;
substr_for: FOR expr_list
{ $$ = $2; }
- | /* EMPTY */
+ | /*EMPTY*/
{ $$ = NIL; }
;
}
;
+/* 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);
;
opt_id: ColId { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
+ | /*EMPTY*/ { $$ = NULL; }
;
relation_name: SpecialRuleRelation
| 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"; }
| MOVE { $$ = "move"; }
| NEW { $$ = "new"; }
| NONE { $$ = "none"; }
+ | NULLIF { $$ = "nullif"; }
| ORDER { $$ = "order"; }
| POSITION { $$ = "position"; }
| PRECISION { $$ = "precision"; }
| SETOF { $$ = "setof"; }
| SHOW { $$ = "show"; }
| TABLE { $$ = "table"; }
+ | THEN { $$ = "then"; }
| TRANSACTION { $$ = "transaction"; }
| TRUE_P { $$ = "true"; }
| VACUUM { $$ = "vacuum"; }
| VERBOSE { $$ = "verbose"; }
+ | WHEN { $$ = "when"; }
;
SpecialRuleRelation: CURRENT
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"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},
{"double", DOUBLE},
{"drop", DROP},
{"each", EACH},
+ {"else", ELSE},
{"encoding", ENCODING},
{"end", END_TRANS},
{"execute", EXECUTE},
{"notify", NOTIFY},
{"notnull", NOTNULL},
{"null", NULL_P},
+ {"nullif", NULLIF},
{"numeric", NUMERIC},
{"of", OF},
{"oids", OIDS},
{"stdout", STDOUT},
{"substring", SUBSTRING},
{"table", TABLE},
+ {"then", THEN},
{"time", TIME},
{"timestamp", TIMESTAMP},
{"timezone_hour", TIMEZONE_HOUR},
{"verbose", VERBOSE},
{"version", VERSION},
{"view", VIEW},
+ {"when", WHEN},
{"where", WHERE},
{"with", WITH},
{"work", WORK},
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* IsPreferredType()
- * Assign a category to the specified OID.
+ * Check if this type is a preferred type.
*/
bool
IsPreferredType(CATEGORY category, Oid type)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
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;
}
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
{
Oid type = (Oid) 0;
+ if (!expr)
+ return type;
+
switch (nodeTag(expr))
{
case T_Func:
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
Oid attrtype,
int32 attrtypmod);
+static TargetEntry *
+MakeTargetEntryCase(ParseState *pstate,
+ ResTarget *res);
/* MakeTargetEntryIdent()
* Transforms an Ident Node to a Target Entry
*/
TargetEntry *
MakeTargetEntryIdent(ParseState *pstate,
-#if FALSE
- Ident *ident,
-#else
Node *node,
-#endif
char **resname,
char *refname,
char *colname,
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)
{
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;
}
}
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
}
}
}
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)
* - 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 *
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)
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));
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.
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);
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);
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;
}
else
{
- char *colname = res->name;
-
/* this is not an array assignment */
+ char *colname = res->name;
+
if (colname == NULL)
{
tent = MakeTargetEntryComplex(pstate, res);
break;
}
+ case T_CaseExpr:
+ {
+ tent = MakeTargetEntryCase(pstate, res);
+ break;
+ }
case T_Attr:
{
bool expand_star = false;
}
default:
/* internal error */
- elog(ERROR, "internal error: do not know how to transform targetlist");
+ elog(ERROR, "Unable to transform targetlist (internal error)");
break;
}
/* 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
{
switch (nodeTag(expr))
{
- case T_Aggreg:
+ case T_Aggreg:
return (char *) ((Aggreg *) expr)->aggname;
case T_Expr:
if (((Expr *) expr)->opType == FUNC_EXPR)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_type.h"
-
static RewriteInfo *gatherRewriteMeta(Query *parsetree,
Query *rule_action,
Node *rule_qual,
static void modifyAggregQual(Node **nodePtr, Query *parsetree);
-
-
-
-
-
-
-
-
-
-
-
static Query *fireRIRrules(Query *parsetree);
}
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;
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);
}
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;
}
break;
+ case T_CaseExpr:
+ case T_CaseWhen:
+ break;
+
case T_Query:
{
Query *qry = (Query *)node;
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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.
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
*/
} ResTarget;
/*
- * ParamString - used in with clauses
+ * ParamString - used in WITH clauses
*/
typedef struct ParamString
{
} RelExpr;
/*
- * SortGroupBy - for order by clause
+ * SortGroupBy - for ORDER BY clause
*/
typedef struct SortGroupBy
{
} SortGroupBy;
/*
- * JoinUsing - for join using clause
+ * JoinUsing - for JOIN USING clause
*/
typedef struct JoinUsing
{
} JoinUsing;
/*
- * RangeVar - range variable, used in from clauses
+ * RangeVar - range variable, used in FROM clauses
*/
typedef struct RangeVar
{
} RangeVar;
/*
- * IndexElem - index parameters (used in create index)
+ * IndexElem - index parameters (used in CREATE INDEX)
*/
typedef struct IndexElem
{
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);