From bedd04a551c30350ebb8da2aa2596d16053b5d15 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Fri, 4 Dec 1998 15:34:49 +0000 Subject: Implement CASE expression. --- src/backend/parser/analyze.c | 25 +++--- src/backend/parser/gram.y | 137 ++++++++++++++++++++++++++++----- src/backend/parser/keywords.c | 8 +- src/backend/parser/parse_coerce.c | 4 +- src/backend/parser/parse_expr.c | 156 +++++++++++++++++++++++++++++++++++++- src/backend/parser/parse_target.c | 90 +++++++++++++++------- 6 files changed, 355 insertions(+), 65 deletions(-) (limited to 'src/backend/parser') 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) -- cgit v1.2.1