mirror of
https://github.com/postgres/postgres.git
synced 2026-02-26 11:21:03 -05:00
varchar length. Cleans up code so attlen is always length. Removed varchar() hack added earlier. Will fix bug in selecting varchar() fields, and varchar() can be variable length.
659 lines
16 KiB
C
659 lines
16 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* parse_target.c
|
|
* handle target lists
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.6 1998/01/16 23:20:22 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "postgres.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/primnodes.h"
|
|
#include "parser/parse_expr.h"
|
|
#include "parser/parse_node.h"
|
|
#include "parser/parse_relation.h"
|
|
#include "parser/parse_target.h"
|
|
#include "utils/builtins.h"
|
|
|
|
static List *expandAllTables(ParseState *pstate);
|
|
static char *figureColname(Node *expr, Node *resval);
|
|
static TargetEntry *make_targetlist_expr(ParseState *pstate,
|
|
char *colname,
|
|
Node *expr,
|
|
List *arrayRef);
|
|
|
|
/*
|
|
* transformTargetList -
|
|
* turns a list of ResTarget's into a list of TargetEntry's
|
|
*/
|
|
List *
|
|
transformTargetList(ParseState *pstate, List *targetlist)
|
|
{
|
|
List *p_target = NIL;
|
|
List *tail_p_target = NIL;
|
|
|
|
while (targetlist != NIL)
|
|
{
|
|
ResTarget *res = (ResTarget *) lfirst(targetlist);
|
|
TargetEntry *tent = makeNode(TargetEntry);
|
|
|
|
switch (nodeTag(res->val))
|
|
{
|
|
case T_Ident:
|
|
{
|
|
Node *expr;
|
|
Oid type_id;
|
|
int type_len;
|
|
char *identname;
|
|
char *resname;
|
|
|
|
identname = ((Ident *) res->val)->name;
|
|
handleTargetColname(pstate, &res->name, NULL, identname);
|
|
|
|
/*
|
|
* here we want to look for column names only, not relation
|
|
* names (even though they can be stored in Ident nodes, too)
|
|
*/
|
|
expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
|
|
type_id = exprType(expr);
|
|
type_len = typeLen(typeidType(type_id));
|
|
resname = (res->name) ? res->name : identname;
|
|
tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
|
|
(Oid) type_id,
|
|
(Size) type_len,
|
|
resname,
|
|
(Index) 0,
|
|
(Oid) 0,
|
|
0);
|
|
|
|
tent->expr = expr;
|
|
break;
|
|
}
|
|
case T_ParamNo:
|
|
case T_FuncCall:
|
|
case T_A_Const:
|
|
case T_A_Expr:
|
|
{
|
|
Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
|
|
|
|
handleTargetColname(pstate, &res->name, NULL, NULL);
|
|
/* note indirection has not been transformed */
|
|
if (pstate->p_is_insert && res->indirection != NIL)
|
|
{
|
|
/* this is an array assignment */
|
|
char *val;
|
|
char *str,
|
|
*save_str;
|
|
List *elt;
|
|
int i = 0,
|
|
ndims;
|
|
int lindx[MAXDIM],
|
|
uindx[MAXDIM];
|
|
int resdomno;
|
|
Relation rd;
|
|
Value *constval;
|
|
|
|
if (exprType(expr) != UNKNOWNOID ||
|
|
!IsA(expr, Const))
|
|
elog(ERROR, "yyparse: string constant expected");
|
|
|
|
val = (char *) textout((struct varlena *)
|
|
((Const *) expr)->constvalue);
|
|
str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
|
|
foreach(elt, res->indirection)
|
|
{
|
|
A_Indices *aind = (A_Indices *) lfirst(elt);
|
|
|
|
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
|
|
if (!IsA(aind->uidx, Const))
|
|
elog(ERROR,
|
|
"Array Index for Append should be a constant");
|
|
uindx[i] = ((Const *) aind->uidx)->constvalue;
|
|
if (aind->lidx != NULL)
|
|
{
|
|
aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
|
|
if (!IsA(aind->lidx, Const))
|
|
elog(ERROR,
|
|
"Array Index for Append should be a constant");
|
|
lindx[i] = ((Const *) aind->lidx)->constvalue;
|
|
}
|
|
else
|
|
{
|
|
lindx[i] = 1;
|
|
}
|
|
if (lindx[i] > uindx[i])
|
|
elog(ERROR, "yyparse: lower index cannot be greater than upper index");
|
|
sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
|
|
str += strlen(str);
|
|
i++;
|
|
}
|
|
sprintf(str, "=%s", val);
|
|
rd = pstate->p_target_relation;
|
|
Assert(rd != NULL);
|
|
resdomno = attnameAttNum(rd, res->name);
|
|
ndims = attnumAttNelems(rd, resdomno);
|
|
if (i != ndims)
|
|
elog(ERROR, "yyparse: array dimensions do not match");
|
|
constval = makeNode(Value);
|
|
constval->type = T_String;
|
|
constval->val.str = save_str;
|
|
tent = make_targetlist_expr(pstate, res->name,
|
|
(Node *) make_const(constval),
|
|
NULL);
|
|
pfree(save_str);
|
|
}
|
|
else
|
|
{
|
|
char *colname = res->name;
|
|
|
|
/* this is not an array assignment */
|
|
if (colname == NULL)
|
|
{
|
|
|
|
/*
|
|
* if you're wondering why this is here, look
|
|
* at the yacc grammar for why a name can be
|
|
* missing. -ay
|
|
*/
|
|
colname = figureColname(expr, res->val);
|
|
}
|
|
if (res->indirection)
|
|
{
|
|
List *ilist = res->indirection;
|
|
|
|
while (ilist != NIL)
|
|
{
|
|
A_Indices *ind = lfirst(ilist);
|
|
|
|
ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
|
|
ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
|
|
ilist = lnext(ilist);
|
|
}
|
|
}
|
|
res->name = colname;
|
|
tent = make_targetlist_expr(pstate, res->name, expr,
|
|
res->indirection);
|
|
}
|
|
break;
|
|
}
|
|
case T_Attr:
|
|
{
|
|
Oid type_id;
|
|
int type_len;
|
|
Attr *att = (Attr *) res->val;
|
|
Node *result;
|
|
char *attrname;
|
|
char *resname;
|
|
Resdom *resnode;
|
|
List *attrs = att->attrs;
|
|
|
|
/*
|
|
* Target item is a single '*', expand all tables (eg.
|
|
* SELECT * FROM emp)
|
|
*/
|
|
if (att->relname != NULL && !strcmp(att->relname, "*"))
|
|
{
|
|
if (tail_p_target == NIL)
|
|
p_target = tail_p_target = expandAllTables(pstate);
|
|
else
|
|
lnext(tail_p_target) = expandAllTables(pstate);
|
|
|
|
while (lnext(tail_p_target) != NIL)
|
|
/* make sure we point to the last target entry */
|
|
tail_p_target = lnext(tail_p_target);
|
|
|
|
/*
|
|
* skip rest of while loop
|
|
*/
|
|
targetlist = lnext(targetlist);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Target item is relation.*, expand the table (eg.
|
|
* SELECT emp.*, dname FROM emp, dept)
|
|
*/
|
|
attrname = strVal(lfirst(att->attrs));
|
|
if (att->attrs != NIL && !strcmp(attrname, "*"))
|
|
{
|
|
|
|
/*
|
|
* tail_p_target is the target list we're building
|
|
* in the while loop. Make sure we fix it after
|
|
* appending more nodes.
|
|
*/
|
|
if (tail_p_target == NIL)
|
|
p_target = tail_p_target = expandAll(pstate, att->relname,
|
|
att->relname, &pstate->p_last_resno);
|
|
else
|
|
lnext(tail_p_target) =
|
|
expandAll(pstate, att->relname, att->relname,
|
|
&pstate->p_last_resno);
|
|
while (lnext(tail_p_target) != NIL)
|
|
/* make sure we point to the last target entry */
|
|
tail_p_target = lnext(tail_p_target);
|
|
|
|
/*
|
|
* skip the rest of the while loop
|
|
*/
|
|
targetlist = lnext(targetlist);
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* Target item is fully specified: ie.
|
|
* relation.attribute
|
|
*/
|
|
result = handleNestedDots(pstate, att, &pstate->p_last_resno,EXPR_COLUMN_FIRST);
|
|
handleTargetColname(pstate, &res->name, att->relname, attrname);
|
|
if (att->indirection != NIL)
|
|
{
|
|
List *ilist = att->indirection;
|
|
|
|
while (ilist != NIL)
|
|
{
|
|
A_Indices *ind = lfirst(ilist);
|
|
|
|
ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
|
|
ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
|
|
ilist = lnext(ilist);
|
|
}
|
|
result = (Node *) make_array_ref(result, att->indirection);
|
|
}
|
|
type_id = exprType(result);
|
|
type_len = typeLen(typeidType(type_id));
|
|
/* move to last entry */
|
|
while (lnext(attrs) != NIL)
|
|
attrs = lnext(attrs);
|
|
resname = (res->name) ? res->name : strVal(lfirst(attrs));
|
|
resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
|
|
(Oid) type_id,
|
|
(Size) type_len,
|
|
resname,
|
|
(Index) 0,
|
|
(Oid) 0,
|
|
0);
|
|
tent->resdom = resnode;
|
|
tent->expr = result;
|
|
break;
|
|
}
|
|
default:
|
|
/* internal error */
|
|
elog(ERROR,
|
|
"internal error: do not know how to transform targetlist");
|
|
break;
|
|
}
|
|
|
|
if (p_target == NIL)
|
|
{
|
|
p_target = tail_p_target = lcons(tent, NIL);
|
|
}
|
|
else
|
|
{
|
|
lnext(tail_p_target) = lcons(tent, NIL);
|
|
tail_p_target = lnext(tail_p_target);
|
|
}
|
|
targetlist = lnext(targetlist);
|
|
}
|
|
|
|
return p_target;
|
|
}
|
|
|
|
|
|
/*
|
|
* make_targetlist_expr -
|
|
* make a TargetEntry from an expression
|
|
*
|
|
* arrayRef is a list of transformed A_Indices
|
|
*/
|
|
static TargetEntry *
|
|
make_targetlist_expr(ParseState *pstate,
|
|
char *colname,
|
|
Node *expr,
|
|
List *arrayRef)
|
|
{
|
|
Oid type_id,
|
|
attrtype;
|
|
int type_len,
|
|
attrlen,
|
|
attrtypmod;
|
|
int resdomno;
|
|
Relation rd;
|
|
bool attrisset;
|
|
TargetEntry *tent;
|
|
Resdom *resnode;
|
|
|
|
if (expr == NULL)
|
|
elog(ERROR, "make_targetlist_expr: invalid use of NULL expression");
|
|
|
|
type_id = exprType(expr);
|
|
if (type_id == InvalidOid)
|
|
{
|
|
type_len = 0;
|
|
}
|
|
else
|
|
type_len = typeLen(typeidType(type_id));
|
|
|
|
/* I have no idea what the following does! */
|
|
/* It appears to process target columns that will be receiving results */
|
|
if (pstate->p_is_insert || pstate->p_is_update)
|
|
{
|
|
|
|
/*
|
|
* append or replace query -- append, replace work only on one
|
|
* relation, so multiple occurence of same resdomno is bogus
|
|
*/
|
|
rd = pstate->p_target_relation;
|
|
Assert(rd != NULL);
|
|
resdomno = attnameAttNum(rd, colname);
|
|
attrisset = attnameIsSet(rd, colname);
|
|
attrtype = attnumTypeId(rd, resdomno);
|
|
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
|
|
attrtype = GetArrayElementType(attrtype);
|
|
attrlen = typeLen(typeidType(attrtype));
|
|
attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
|
|
#if 0
|
|
if (Input_is_string && Typecast_ok)
|
|
{
|
|
Datum val;
|
|
|
|
if (type_id == typeTypeId(type("unknown")))
|
|
{
|
|
val = (Datum) textout((struct varlena *)
|
|
((Const) lnext(expr))->constvalue);
|
|
}
|
|
else
|
|
{
|
|
val = ((Const) lnext(expr))->constvalue;
|
|
}
|
|
if (attrisset)
|
|
{
|
|
lnext(expr) = makeConst(attrtype,
|
|
attrlen,
|
|
val,
|
|
false,
|
|
true,
|
|
true, /* is set */
|
|
false);
|
|
}
|
|
else
|
|
{
|
|
lnext(expr) =
|
|
makeConst(attrtype,
|
|
attrlen,
|
|
(Datum) fmgr(typeidRetinfunc(attrtype),
|
|
val, typeidTypElem(attrtype), -1),
|
|
false,
|
|
true /* Maybe correct-- 80% chance */ ,
|
|
false, /* is not a set */
|
|
false);
|
|
}
|
|
}
|
|
else if ((Typecast_ok) && (attrtype != type_id))
|
|
{
|
|
lnext(expr) =
|
|
parser_typecast2(expr, typeidType(attrtype));
|
|
}
|
|
else if (attrtype != type_id)
|
|
{
|
|
if ((attrtype == INT2OID) && (type_id == INT4OID))
|
|
lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */
|
|
else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
|
|
lfirst(expr) = lispInteger(FLOAT4OID);
|
|
else
|
|
elog(ERROR, "unequal type in tlist : %s \n", colname);
|
|
}
|
|
|
|
Input_is_string = false;
|
|
Input_is_integer = false;
|
|
Typecast_ok = true;
|
|
#endif
|
|
|
|
if (attrtype != type_id)
|
|
{
|
|
if (IsA(expr, Const))
|
|
{
|
|
/* try to cast the constant */
|
|
if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
|
|
{
|
|
/* updating a single item */
|
|
Oid typelem = typeidTypElem(attrtype);
|
|
|
|
expr = (Node *) parser_typecast2(expr,
|
|
type_id,
|
|
typeidType(typelem),
|
|
attrtypmod);
|
|
}
|
|
else
|
|
expr = (Node *) parser_typecast2(expr,
|
|
type_id,
|
|
typeidType(attrtype),
|
|
attrtypmod);
|
|
}
|
|
else
|
|
{
|
|
/* currently, we can't handle casting of expressions */
|
|
elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
|
|
colname,
|
|
typeidTypeName(attrtype),
|
|
typeidTypeName(type_id));
|
|
}
|
|
}
|
|
|
|
if (arrayRef != NIL)
|
|
{
|
|
Expr *target_expr;
|
|
Attr *att = makeNode(Attr);
|
|
List *ar = arrayRef;
|
|
List *upperIndexpr = NIL;
|
|
List *lowerIndexpr = NIL;
|
|
|
|
att->relname = pstrdup(RelationGetRelationName(rd)->data);
|
|
att->attrs = lcons(makeString(colname), NIL);
|
|
target_expr = (Expr *) handleNestedDots(pstate, att,
|
|
&pstate->p_last_resno,
|
|
EXPR_COLUMN_FIRST);
|
|
while (ar != NIL)
|
|
{
|
|
A_Indices *ind = lfirst(ar);
|
|
|
|
if (lowerIndexpr || (!upperIndexpr && ind->lidx))
|
|
{
|
|
|
|
/*
|
|
* XXX assume all lowerIndexpr is non-null in this
|
|
* case
|
|
*/
|
|
lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
|
|
}
|
|
upperIndexpr = lappend(upperIndexpr, ind->uidx);
|
|
ar = lnext(ar);
|
|
}
|
|
|
|
expr = (Node *) make_array_set(target_expr,
|
|
upperIndexpr,
|
|
lowerIndexpr,
|
|
(Expr *) expr);
|
|
attrtype = attnumTypeId(rd, resdomno);
|
|
attrlen = typeLen(typeidType(attrtype));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resdomno = pstate->p_last_resno++;
|
|
attrtype = type_id;
|
|
attrlen = type_len;
|
|
}
|
|
tent = makeNode(TargetEntry);
|
|
|
|
resnode = makeResdom((AttrNumber) resdomno,
|
|
(Oid) attrtype,
|
|
(Size) attrlen,
|
|
colname,
|
|
(Index) 0,
|
|
(Oid) 0,
|
|
0);
|
|
|
|
tent->resdom = resnode;
|
|
tent->expr = expr;
|
|
|
|
return tent;
|
|
}
|
|
|
|
/*
|
|
* makeTargetNames -
|
|
* generate a list of column names if not supplied or
|
|
* test supplied column names to make sure they are in target table
|
|
* (used exclusively for inserts)
|
|
*/
|
|
List *
|
|
makeTargetNames(ParseState *pstate, List *cols)
|
|
{
|
|
List *tl = NULL;
|
|
|
|
/* Generate ResTarget if not supplied */
|
|
|
|
if (cols == NIL)
|
|
{
|
|
int numcol;
|
|
int i;
|
|
AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs;
|
|
|
|
numcol = pstate->p_target_relation->rd_rel->relnatts;
|
|
for (i = 0; i < numcol; i++)
|
|
{
|
|
Ident *id = makeNode(Ident);
|
|
|
|
id->name = palloc(NAMEDATALEN);
|
|
StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN);
|
|
id->indirection = NIL;
|
|
id->isRel = false;
|
|
if (tl == NIL)
|
|
cols = tl = lcons(id, NIL);
|
|
else
|
|
{
|
|
lnext(tl) = lcons(id, NIL);
|
|
tl = lnext(tl);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach(tl, cols)
|
|
{
|
|
List *nxt;
|
|
char *name = ((Ident *) lfirst(tl))->name;
|
|
|
|
/* elog on failure */
|
|
attnameAttNum(pstate->p_target_relation, name);
|
|
foreach(nxt, lnext(tl))
|
|
if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
|
|
elog(ERROR, "Attribute '%s' should be specified only once", name);
|
|
}
|
|
}
|
|
|
|
return cols;
|
|
}
|
|
|
|
/*
|
|
* expandAllTables -
|
|
* turns '*' (in the target list) into a list of attributes
|
|
* (of all relations in the range table)
|
|
*/
|
|
static List *
|
|
expandAllTables(ParseState *pstate)
|
|
{
|
|
List *target = NIL;
|
|
List *legit_rtable = NIL;
|
|
List *rt,
|
|
*rtable;
|
|
|
|
rtable = pstate->p_rtable;
|
|
if (pstate->p_is_rule)
|
|
{
|
|
|
|
/*
|
|
* skip first two entries, "*new*" and "*current*"
|
|
*/
|
|
rtable = lnext(lnext(pstate->p_rtable));
|
|
}
|
|
|
|
/* this should not happen */
|
|
if (rtable == NULL)
|
|
elog(ERROR, "cannot expand: null p_rtable");
|
|
|
|
/*
|
|
* go through the range table and make a list of range table entries
|
|
* which we will expand.
|
|
*/
|
|
foreach(rt, rtable)
|
|
{
|
|
RangeTblEntry *rte = lfirst(rt);
|
|
|
|
/*
|
|
* we only expand those specify in the from clause. (This will
|
|
* also prevent us from using the wrong table in inserts: eg.
|
|
* tenk2 in "insert into tenk2 select * from tenk1;")
|
|
*/
|
|
if (!rte->inFromCl)
|
|
continue;
|
|
legit_rtable = lappend(legit_rtable, rte);
|
|
}
|
|
|
|
foreach(rt, legit_rtable)
|
|
{
|
|
RangeTblEntry *rte = lfirst(rt);
|
|
List *temp = target;
|
|
|
|
if (temp == NIL)
|
|
target = expandAll(pstate, rte->relname, rte->refname,
|
|
&pstate->p_last_resno);
|
|
else
|
|
{
|
|
while (temp != NIL && lnext(temp) != NIL)
|
|
temp = lnext(temp);
|
|
lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
|
|
&pstate->p_last_resno);
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
|
|
/*
|
|
* figureColname -
|
|
* if the name of the resulting column is not specified in the target
|
|
* list, we have to guess.
|
|
*
|
|
*/
|
|
static char *
|
|
figureColname(Node *expr, Node *resval)
|
|
{
|
|
switch (nodeTag(expr))
|
|
{
|
|
case T_Aggreg:
|
|
return (char *) /* XXX */
|
|
((Aggreg *) expr)->aggname;
|
|
case T_Expr:
|
|
if (((Expr *) expr)->opType == FUNC_EXPR)
|
|
{
|
|
if (nodeTag(resval) == T_FuncCall)
|
|
return ((FuncCall *) resval)->funcname;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "?column?";
|
|
}
|