1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Implement CASE expression.

This commit is contained in:
Thomas G. Lockhart
1998-12-04 15:34:49 +00:00
parent 19740e2fff
commit bedd04a551
17 changed files with 779 additions and 110 deletions

View File

@@ -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;
}

View File

@@ -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 <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
@@ -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

View File

@@ -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},

View File

@@ -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)

View File

@@ -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;

View File

@@ -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)