postgresql/src/backend/parser/parse_target.c

901 lines
21 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* parse_target.c
* handle target lists
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
1999-07-16 00:40:16 +02:00
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.43 1999/07/15 22:39:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
1997-11-26 02:14:33 +01:00
#include "postgres.h"
#include "nodes/makefuncs.h"
#include "parser/parse_expr.h"
1998-01-20 06:05:08 +01:00
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static List *ExpandAllTables(ParseState *pstate);
static char *FigureColname(Node *expr, Node *resval);
static Node *SizeTargetExpr(ParseState *pstate,
Node *expr,
Oid attrtype,
int32 attrtypmod);
/* MakeTargetEntryIdent()
* Transforms an Ident Node to a Target Entry
* Created this function to allow the ORDER/GROUP BY clause to be able
* to construct a TargetEntry from an Ident.
*
* resjunk = TRUE will hide the target entry in the final result tuple.
* daveh@insightdist.com 5/20/98
*
* Added more conversion logic to match up types from source to target.
* - thomas 1998-06-02
*/
TargetEntry *
MakeTargetEntryIdent(ParseState *pstate,
Node *node,
char **resname,
char *refname,
char *colname,
1999-05-17 19:03:51 +02:00
bool resjunk)
{
Node *expr = NULL;
Oid attrtype_target;
TargetEntry *tent = makeNode(TargetEntry);
if (pstate->p_is_insert && !resjunk)
{
1999-05-25 18:15:34 +02:00
/*
* Assign column name of destination column to the new TLE. XXX
* this is probably WRONG in INSERT ... SELECT case, since
* handling of GROUP BY and so forth probably should use the
* source table's names not the destination's names.
*/
if (pstate->p_insert_columns != NIL)
{
Ident *id = lfirst(pstate->p_insert_columns);
*resname = id->name;
pstate->p_insert_columns = lnext(pstate->p_insert_columns);
}
else
1998-12-04 16:34:49 +01:00
elog(ERROR, "INSERT has more expressions than target columns");
}
if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
{
Oid attrtype_id;
int resdomno_id,
resdomno_target;
RangeTblEntry *rte;
char *target_colname;
int32 attrtypmod,
attrtypmod_target;
target_colname = *resname;
/*
* this looks strange to me, returning an empty TargetEntry bjm
* 1998/08/24
*/
if (target_colname == NULL || colname == NULL)
1998-09-01 05:29:17 +02:00
return tent;
if (refname != NULL)
rte = refnameRangeTableEntry(pstate, refname);
else
{
rte = colnameRangeTableEntry(pstate, colname);
if (rte == (RangeTblEntry *) NULL)
1998-12-04 16:34:49 +01:00
elog(ERROR, "Attribute %s not found", colname);
refname = rte->refname;
}
resdomno_id = get_attnum(rte->relid, colname);
attrtype_id = get_atttype(rte->relid, resdomno_id);
attrtypmod = get_atttypmod(rte->relid, resdomno_id);
resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target);
if ((attrtype_id != attrtype_target)
|| ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod)))
{
if (can_coerce_type(1, &attrtype_id, &attrtype_target))
{
1999-05-22 06:12:29 +02:00
expr = coerce_type(pstate, node, attrtype_id,
1999-05-25 18:15:34 +02:00
attrtype_target,
get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST);
1999-05-17 19:03:51 +02:00
tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false);
expr = tent->expr;
}
else
{
elog(ERROR, "Unable to convert %s to %s for column %s",
typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
target_colname);
}
}
}
/*
* here we want to look for column names only, not relation names
* (even though they can be stored in Ident nodes, too)
*/
if (expr == NULL)
{
char *name;
int32 type_mod;
name = ((*resname != NULL) ? *resname : colname);
expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
attrtype_target = exprType(expr);
if (nodeTag(expr) == T_Var)
type_mod = ((Var *) expr)->vartypmod;
else
type_mod = -1;
tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
(Oid) attrtype_target,
type_mod,
name,
(Index) 0,
(Oid) 0,
resjunk);
tent->expr = expr;
}
1998-09-01 05:29:17 +02:00
return tent;
} /* MakeTargetEntryIdent() */
/* MakeTargetEntryExpr()
1998-08-23 16:43:46 +02:00
* Make a TargetEntry from an expression.
* arrayRef is a list of transformed A_Indices.
*
* For type mismatches between expressions and targets, use the same
* techniques as for function and operator type coersion.
1998-08-23 16:43:46 +02:00
* - thomas 1998-05-08
*
* Added resjunk flag and made extern so that it can be use by GROUP/
1998-12-04 16:34:49 +01:00
* ORDER BY a function or expression not in the target_list
* - daveh@insightdist.com 1998-07-31
1998-08-23 16:43:46 +02:00
*/
TargetEntry *
MakeTargetEntryExpr(ParseState *pstate,
char *colname,
Node *expr,
List *arrayRef,
1999-05-17 19:03:51 +02:00
bool resjunk)
1998-08-23 16:43:46 +02:00
{
Oid type_id,
attrtype;
int32 type_mod,
attrtypmod;
int resdomno;
Relation rd;
bool attrisset;
Resdom *resnode;
if (expr == NULL)
1998-12-04 16:34:49 +01:00
elog(ERROR, "Invalid use of NULL expression (internal error)");
1998-08-23 16:43:46 +02:00
type_id = exprType(expr);
if (nodeTag(expr) == T_Var)
type_mod = ((Var *) expr)->vartypmod;
else
type_mod = -1;
/* Process target columns that will be receiving results */
if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
1998-08-23 16:43:46 +02:00
{
1998-08-23 16:43:46 +02:00
/*
* insert or update query -- insert, update 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);
if (resdomno <= 0)
elog(ERROR, "Cannot assign to system attribute '%s'", colname);
1998-08-23 16:43:46 +02:00
attrisset = attnameIsSet(rd, colname);
attrtype = attnumTypeId(rd, resdomno);
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
attrtype = GetArrayElementType(attrtype);
attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
/*
* Check for InvalidOid since that seems to indicate a NULL
* constant...
*/
1998-08-23 16:43:46 +02:00
if (type_id != InvalidOid)
{
/* Mismatch on types? then try to coerce to target... */
if (attrtype != type_id)
{
Oid typelem;
1998-08-23 16:43:46 +02:00
if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
typelem = typeTypElem(typeidType(attrtype));
1998-08-23 16:43:46 +02:00
else
typelem = attrtype;
expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
if (!HeapTupleIsValid(expr))
1998-12-04 16:34:49 +01:00
elog(ERROR, "Attribute '%s' is of type '%s'"
1998-08-23 16:43:46 +02:00
" but expression is of type '%s'"
1999-05-25 18:15:34 +02:00
"\n\tYou will need to rewrite or cast the expression",
1998-08-23 16:43:46 +02:00
colname,
typeidTypeName(attrtype),
typeidTypeName(type_id));
}
/*
* Apparently going to a fixed-length string? Then explicitly
* size for storage...
1998-08-23 16:43:46 +02:00
*/
if (attrtypmod > 0)
expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod);
}
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 *) ParseNestedFuncOrColumn(pstate, att,
&pstate->p_last_resno,
EXPR_COLUMN_FIRST);
1998-08-23 16:43:46 +02:00
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);
attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno);
}
}
else
{
resdomno = pstate->p_last_resno++;
attrtype = type_id;
attrtypmod = type_mod;
}
resnode = makeResdom((AttrNumber) resdomno,
(Oid) attrtype,
attrtypmod,
colname,
(Index) 0,
(Oid) 0,
resjunk);
return makeTargetEntry(resnode, expr);
} /* MakeTargetEntryExpr() */
1998-08-23 16:43:46 +02:00
1998-12-04 16:34:49 +01:00
/*
* MakeTargetEntryCase()
* Make a TargetEntry from a case node.
*/
static TargetEntry *
MakeTargetEntryCase(ParseState *pstate,
ResTarget *res)
{
1999-05-25 18:15:34 +02:00
TargetEntry *tent;
CaseExpr *expr;
Resdom *resnode;
int resdomno;
Oid type_id;
int32 type_mod;
1998-12-04 16:34:49 +01:00
1999-05-25 18:15:34 +02:00
expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
1998-12-04 16:34:49 +01:00
type_id = expr->casetype;
type_mod = -1;
handleTargetColname(pstate, &res->name, NULL, NULL);
if (res->name == NULL)
1999-05-25 18:15:34 +02:00
res->name = FigureColname((Node *) expr, res->val);
1998-12-04 16:34:49 +01:00
resdomno = pstate->p_last_resno++;
resnode = makeResdom((AttrNumber) resdomno,
(Oid) type_id,
type_mod,
res->name,
(Index) 0,
(Oid) 0,
1999-05-17 19:03:51 +02:00
false);
1998-12-04 16:34:49 +01:00
tent = makeNode(TargetEntry);
tent->resdom = resnode;
1999-05-25 18:15:34 +02:00
tent->expr = (Node *) expr;
1998-12-04 16:34:49 +01:00
return tent;
} /* MakeTargetEntryCase() */
/*
* MakeTargetEntryComplex()
* Make a TargetEntry from a complex node.
*/
static TargetEntry *
MakeTargetEntryComplex(ParseState *pstate,
ResTarget *res)
{
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))
1998-12-04 16:34:49 +01:00
elog(ERROR, "String constant expected (internal error)");
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);
1998-08-23 16:43:46 +02:00
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->uidx, Const))
elog(ERROR, "Array Index for Append should be a constant");
1998-08-23 16:43:46 +02:00
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])
1998-12-04 16:34:49 +01:00
elog(ERROR, "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)
1998-12-04 16:34:49 +01:00
elog(ERROR, "Array dimensions do not match");
constval = makeNode(Value);
constval->type = T_String;
constval->val.str = save_str;
return MakeTargetEntryExpr(pstate, res->name,
(Node *) make_const(constval),
1999-05-17 19:03:51 +02:00
NULL, false);
pfree(save_str);
}
else
{
/* this is not an array assignment */
1999-05-25 18:15:34 +02:00
char *colname = res->name;
1998-12-04 16:34:49 +01:00
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;
return MakeTargetEntryExpr(pstate, res->name, expr,
1999-05-17 19:03:51 +02:00
res->indirection, false);
}
}
/*
* MakeTargetEntryAttr()
* Make a TargetEntry from a complex node.
*/
static TargetEntry *
MakeTargetEntryAttr(ParseState *pstate,
ResTarget *res)
{
Oid type_id;
int32 type_mod;
Attr *att = (Attr *) res->val;
Node *result;
char *attrname;
char *resname;
Resdom *resnode;
int resdomno;
List *attrs = att->attrs;
TargetEntry *tent;
attrname = strVal(lfirst(att->attrs));
/*
* Target item is fully specified: ie. relation.attribute
*/
result = ParseNestedFuncOrColumn(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);
if (nodeTag(result) == T_Var)
type_mod = ((Var *) result)->vartypmod;
else
type_mod = -1;
/* move to last entry */
while (lnext(attrs) != NIL)
attrs = lnext(attrs);
resname = (res->name) ? res->name : strVal(lfirst(attrs));
if (pstate->p_is_insert || pstate->p_is_update)
{
Relation rd;
/*
* insert or update query -- insert, update work only on one
* relation, so multiple occurence of same resdomno is bogus
*/
1998-08-25 05:22:49 +02:00
rd = pstate->p_target_relation;
Assert(rd != NULL);
resdomno = attnameAttNum(rd, res->name);
}
else
resdomno = pstate->p_last_resno++;
resnode = makeResdom((AttrNumber) resdomno,
(Oid) type_id,
type_mod,
resname,
(Index) 0,
(Oid) 0,
1999-05-17 19:03:51 +02:00
false);
tent = makeNode(TargetEntry);
tent->resdom = resnode;
tent->expr = result;
return tent;
}
/* 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 = NULL;
switch (nodeTag(res->val))
{
case T_Ident:
{
char *identname;
identname = ((Ident *) res->val)->name;
1999-05-17 19:03:51 +02:00
tent = MakeTargetEntryIdent(pstate,
1999-05-25 18:15:34 +02:00
(Node *) res->val, &res->name, NULL, identname, false);
break;
}
case T_ParamNo:
case T_FuncCall:
case T_A_Const:
case T_A_Expr:
{
tent = MakeTargetEntryComplex(pstate, res);
break;
}
1998-12-04 16:34:49 +01:00
case T_CaseExpr:
{
tent = MakeTargetEntryCase(pstate, res);
break;
}
case T_Attr:
{
bool expand_star = false;
char *attrname;
Attr *att = (Attr *) res->val;
/*
* 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);
expand_star = true;
}
1998-08-26 05:17:32 +02:00
else
{
/*
* Target item is relation.*, expand the table
* (eg. SELECT emp.*, dname FROM emp, dept)
*/
1998-08-26 05:17:32 +02:00
attrname = strVal(lfirst(att->attrs));
if (att->attrs != NIL && !strcmp(attrname, "*"))
{
1998-08-26 05:17:32 +02:00
/*
* tail_p_target is the target list we're
* building in the while loop. Make sure we
* fix it after appending more nodes.
1998-08-26 05:17:32 +02:00
*/
if (tail_p_target == NIL)
p_target = tail_p_target = expandAll(pstate, att->relname,
att->relname, &pstate->p_last_resno);
1998-08-26 05:17:32 +02:00
else
lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname,
1999-05-25 18:15:34 +02:00
&pstate->p_last_resno);
1998-08-26 05:17:32 +02:00
expand_star = true;
}
}
if (expand_star)
{
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;
}
else
{
tent = MakeTargetEntryAttr(pstate, res);
break;
}
}
default:
/* internal error */
1998-12-04 16:34:49 +01:00
elog(ERROR, "Unable to transform targetlist (internal error)");
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;
} /* transformTargetList() */
Node *
CoerceTargetExpr(ParseState *pstate,
Node *expr,
Oid type_id,
Oid attrtype)
{
if (can_coerce_type(1, &type_id, &attrtype))
1999-05-22 06:12:29 +02:00
expr = coerce_type(pstate, expr, type_id, attrtype, -1);
#ifndef DISABLE_STRING_HACKS
/*
* string hacks to get transparent conversions w/o explicit
* conversions
*/
else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
{
Oid text_id = TEXTOID;
if (type_id == TEXTOID)
{
}
else if (can_coerce_type(1, &type_id, &text_id))
1999-05-22 06:12:29 +02:00
expr = coerce_type(pstate, expr, type_id, text_id, -1);
else
expr = NULL;
}
#endif
else
expr = NULL;
return expr;
} /* CoerceTargetExpr() */
/* SizeTargetExpr()
* Apparently going to a fixed-length string?
* Then explicitly size for storage...
*/
static Node *
SizeTargetExpr(ParseState *pstate,
Node *expr,
Oid attrtype,
int32 attrtypmod)
{
int i;
HeapTuple ftup;
char *funcname;
Oid oid_array[MAXFARGS];
FuncCall *func;
A_Const *cons;
funcname = typeidTypeName(attrtype);
oid_array[0] = attrtype;
oid_array[1] = INT4OID;
for (i = 2; i < MAXFARGS; i++)
oid_array[i] = InvalidOid;
/* attempt to find with arguments exactly as specified... */
ftup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(funcname),
Int32GetDatum(2),
PointerGetDatum(oid_array),
0);
if (HeapTupleIsValid(ftup))
{
func = makeNode(FuncCall);
func->funcname = funcname;
cons = makeNode(A_Const);
cons->val.type = T_Integer;
cons->val.val.ival = attrtypmod;
func->args = lappend(lcons(expr, NIL), cons);
expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
}
return expr;
} /* SizeTargetExpr() */
/*
* 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;
1998-09-01 05:29:17 +02:00
Form_pg_attribute *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)
{
1999-05-25 18:15:34 +02:00
/*
* skip first two entries, "*new*" and "*current*"
*/
rtable = lnext(lnext(pstate->p_rtable));
}
1999-05-17 06:19:33 +02:00
/* SELECT *; */
if (rtable == NULL)
1999-05-17 06:19:33 +02:00
elog(ERROR, "Wildcard with no tables specified.");
/*
* 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))
{
1999-05-25 18:15:34 +02:00
case T_Aggref:
1999-01-24 01:28:37 +01:00
return (char *) ((Aggref *) expr)->aggname;
case T_Expr:
if (((Expr *) expr)->opType == FUNC_EXPR)
{
if (nodeTag(resval) == T_FuncCall)
return ((FuncCall *) resval)->funcname;
}
break;
case T_CaseExpr:
{
1999-05-25 18:15:34 +02:00
char *name;
name = FigureColname(((CaseExpr *) expr)->defresult, resval);
if (!strcmp(name, "?column?"))
name = "case";
return name;
}
break;
default:
break;
}
return "?column?";
}