1997-11-25 23:07:18 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* parse_relation.c
|
1997-11-25 23:07:18 +01:00
|
|
|
* parser support routines dealing with relations
|
|
|
|
*
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1997-11-25 23:07:18 +01:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2000-11-08 23:10:03 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.50 2000/11/08 22:09:58 tgl Exp $
|
1997-11-25 23:07:18 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "postgres.h"
|
2000-06-20 03:41:22 +02:00
|
|
|
|
1997-11-25 23:07:18 +01:00
|
|
|
#include "access/heapam.h"
|
1997-11-26 02:14:33 +01:00
|
|
|
#include "access/htup.h"
|
|
|
|
#include "catalog/pg_type.h"
|
1997-11-25 23:07:18 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
2000-09-12 23:07:18 +02:00
|
|
|
#include "parser/parsetree.h"
|
1998-07-08 16:04:11 +02:00
|
|
|
#include "parser/parse_coerce.h"
|
2000-09-12 23:07:18 +02:00
|
|
|
#include "parser/parse_expr.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "parser/parse_relation.h"
|
|
|
|
#include "parser/parse_type.h"
|
2000-09-12 23:07:18 +02:00
|
|
|
#include "rewrite/rewriteManip.h"
|
1997-11-25 23:07:18 +01:00
|
|
|
#include "utils/builtins.h"
|
1997-11-26 02:14:33 +01:00
|
|
|
#include "utils/lsyscache.h"
|
1997-11-25 23:07:18 +01:00
|
|
|
|
1997-11-26 04:43:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
|
|
|
|
char *colname);
|
|
|
|
static Node *scanJoinForColumn(JoinExpr *join, char *colname,
|
|
|
|
int sublevels_up);
|
2000-11-08 23:10:03 +01:00
|
|
|
static bool isForUpdate(ParseState *pstate, char *relname);
|
2000-09-12 23:07:18 +02:00
|
|
|
static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
|
|
|
|
static void warnAutoRange(ParseState *pstate, char *refname);
|
|
|
|
|
|
|
|
|
2000-06-20 03:41:22 +02:00
|
|
|
/*
|
|
|
|
* Information defining the "system" attributes of every relation.
|
|
|
|
*/
|
1999-07-19 02:26:20 +02:00
|
|
|
static struct
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
char *attrname; /* name of system attribute */
|
|
|
|
int attrnum; /* its attribute number (always < 0) */
|
|
|
|
Oid attrtype; /* its type id */
|
1997-11-25 23:07:18 +01:00
|
|
|
} special_attr[] =
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"ctid", SelfItemPointerAttributeNumber, TIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"oid", ObjectIdAttributeNumber, OIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"xmin", MinTransactionIdAttributeNumber, XIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"cmin", MinCommandIdAttributeNumber, CIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"xmax", MaxTransactionIdAttributeNumber, XIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
"cmax", MaxCommandIdAttributeNumber, CIDOID
|
1997-11-25 23:07:18 +01:00
|
|
|
},
|
2000-07-03 00:01:27 +02:00
|
|
|
{
|
|
|
|
"tableoid", TableOidAttributeNumber, OIDOID
|
|
|
|
}
|
1997-11-25 23:07:18 +01:00
|
|
|
};
|
|
|
|
|
2000-06-20 03:41:22 +02:00
|
|
|
#define SPECIALS ((int) (sizeof(special_attr)/sizeof(special_attr[0])))
|
1997-11-25 23:07:18 +01:00
|
|
|
|
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* refnameRangeOrJoinEntry
|
|
|
|
* Given a refname, look to see if it matches any RTE or join table.
|
|
|
|
* If so, return a pointer to the RangeTblEntry or JoinExpr.
|
|
|
|
* Optionally get its nesting depth (0 = current). If sublevels_up
|
|
|
|
* is NULL, only consider items at the current nesting level.
|
2000-02-15 04:38:29 +01:00
|
|
|
*/
|
2000-09-12 23:07:18 +02:00
|
|
|
Node *
|
|
|
|
refnameRangeOrJoinEntry(ParseState *pstate,
|
|
|
|
char *refname,
|
|
|
|
int *sublevels_up)
|
2000-02-15 04:38:29 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
if (sublevels_up)
|
|
|
|
*sublevels_up = 0;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
|
|
|
while (pstate != NULL)
|
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
List *temp;
|
|
|
|
JoinExpr *join;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the rangetable for RTEs; if no match, recursively scan
|
2000-09-29 20:21:41 +02:00
|
|
|
* the joinlist for join tables. We assume that no duplicate
|
2000-09-12 23:07:18 +02:00
|
|
|
* entries have been made in any one nesting level.
|
|
|
|
*/
|
2000-02-15 04:38:29 +01:00
|
|
|
foreach(temp, pstate->p_rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = lfirst(temp);
|
|
|
|
|
2000-03-15 00:06:59 +01:00
|
|
|
if (strcmp(rte->eref->relname, refname) == 0)
|
2000-09-12 23:07:18 +02:00
|
|
|
return (Node *) rte;
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
|
2000-09-12 23:07:18 +02:00
|
|
|
if (join)
|
|
|
|
return (Node *) join;
|
|
|
|
|
2000-03-23 08:38:30 +01:00
|
|
|
pstate = pstate->parentParseState;
|
2000-09-12 23:07:18 +02:00
|
|
|
if (sublevels_up)
|
|
|
|
(*sublevels_up)++;
|
|
|
|
else
|
|
|
|
break;
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
return NULL;
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
/*
|
|
|
|
* Recursively search a joinlist for a joinexpr with given refname
|
|
|
|
*
|
|
|
|
* Note that during parse analysis, we don't expect to find a FromExpr node
|
|
|
|
* in p_joinlist; its top level is just a bare List.
|
|
|
|
*/
|
2000-09-12 23:07:18 +02:00
|
|
|
JoinExpr *
|
2000-09-29 20:21:41 +02:00
|
|
|
scanJoinListForRefname(Node *jtnode, char *refname)
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
|
|
|
JoinExpr *result = NULL;
|
|
|
|
|
|
|
|
if (jtnode == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(jtnode, List))
|
|
|
|
{
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
foreach(l, (List *) jtnode)
|
|
|
|
{
|
2000-09-29 20:21:41 +02:00
|
|
|
result = scanJoinListForRefname(lfirst(l), refname);
|
2000-09-12 23:07:18 +02:00
|
|
|
if (result)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (IsA(jtnode, RangeTblRef))
|
|
|
|
{
|
|
|
|
/* ignore ... */
|
|
|
|
}
|
|
|
|
else if (IsA(jtnode, JoinExpr))
|
|
|
|
{
|
|
|
|
JoinExpr *j = (JoinExpr *) jtnode;
|
|
|
|
|
|
|
|
if (j->alias && strcmp(j->alias->relname, refname) == 0)
|
|
|
|
return j;
|
2000-09-29 20:21:41 +02:00
|
|
|
result = scanJoinListForRefname(j->larg, refname);
|
2000-09-12 23:07:18 +02:00
|
|
|
if (! result)
|
2000-09-29 20:21:41 +02:00
|
|
|
result = scanJoinListForRefname(j->rarg, refname);
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
else
|
2000-09-29 20:21:41 +02:00
|
|
|
elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
|
2000-09-12 23:07:18 +02:00
|
|
|
nodeTag(jtnode));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* given refname, return a pointer to the range table entry.
|
|
|
|
*
|
|
|
|
* NOTE that this routine will ONLY find RTEs, not join tables.
|
|
|
|
*/
|
1997-11-25 23:07:18 +01:00
|
|
|
RangeTblEntry *
|
1998-01-20 23:12:17 +01:00
|
|
|
refnameRangeTableEntry(ParseState *pstate, char *refname)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
|
|
|
List *temp;
|
|
|
|
|
1998-01-20 23:12:17 +01:00
|
|
|
while (pstate != NULL)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
1998-01-20 23:12:17 +01:00
|
|
|
foreach(temp, pstate->p_rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = lfirst(temp);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-03-15 00:06:59 +01:00
|
|
|
if (strcmp(rte->eref->relname, refname) == 0)
|
1998-01-20 23:12:17 +01:00
|
|
|
return rte;
|
|
|
|
}
|
2000-03-23 08:38:30 +01:00
|
|
|
pstate = pstate->parentParseState;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* given refname, return RT index (starting with 1) of the relation,
|
2000-04-12 19:17:23 +02:00
|
|
|
* and optionally get its nesting depth (0 = current). If sublevels_up
|
2000-03-23 08:38:30 +01:00
|
|
|
* is NULL, only consider rels at the current nesting level.
|
2000-09-12 23:07:18 +02:00
|
|
|
* A zero result means name not found.
|
|
|
|
*
|
|
|
|
* NOTE that this routine will ONLY find RTEs, not join tables.
|
2000-03-23 08:38:30 +01:00
|
|
|
*/
|
1997-11-25 23:07:18 +01:00
|
|
|
int
|
1998-01-20 23:12:17 +01:00
|
|
|
refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
|
|
|
int index;
|
|
|
|
List *temp;
|
|
|
|
|
1998-01-20 23:12:17 +01:00
|
|
|
if (sublevels_up)
|
|
|
|
*sublevels_up = 0;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
1998-01-20 23:12:17 +01:00
|
|
|
while (pstate != NULL)
|
|
|
|
{
|
|
|
|
index = 1;
|
|
|
|
foreach(temp, pstate->p_rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = lfirst(temp);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-03-15 00:06:59 +01:00
|
|
|
if (strcmp(rte->eref->relname, refname) == 0)
|
1998-01-20 23:12:17 +01:00
|
|
|
return index;
|
|
|
|
index++;
|
|
|
|
}
|
2000-03-23 08:38:30 +01:00
|
|
|
pstate = pstate->parentParseState;
|
|
|
|
if (sublevels_up)
|
|
|
|
(*sublevels_up)++;
|
1998-02-26 05:46:47 +01:00
|
|
|
else
|
|
|
|
break;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
1998-01-20 06:05:08 +01:00
|
|
|
return 0;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 23:07:18 +02:00
|
|
|
* given an RTE, return RT index (starting with 1) of the entry,
|
|
|
|
* and optionally get its nesting depth (0 = current). If sublevels_up
|
|
|
|
* is NULL, only consider rels at the current nesting level.
|
|
|
|
* Raises error if RTE not found.
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
2000-09-12 23:07:18 +02:00
|
|
|
int
|
|
|
|
RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
int index;
|
|
|
|
List *temp;
|
|
|
|
|
|
|
|
if (sublevels_up)
|
|
|
|
*sublevels_up = 0;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
1998-01-20 23:12:17 +01:00
|
|
|
while (pstate != NULL)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
index = 1;
|
|
|
|
foreach(temp, pstate->p_rtable)
|
|
|
|
{
|
|
|
|
if (rte == (RangeTblEntry *) lfirst(temp))
|
|
|
|
return index;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
pstate = pstate->parentParseState;
|
|
|
|
if (sublevels_up)
|
|
|
|
(*sublevels_up)++;
|
1998-01-20 23:12:17 +01:00
|
|
|
else
|
2000-09-12 23:07:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
|
|
|
|
return 0; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* scanRTEForColumn
|
|
|
|
* Search the column names of a single RTE for the given name.
|
|
|
|
* If found, return an appropriate Var node, else return NULL.
|
|
|
|
* If the name proves ambiguous within this RTE, raise error.
|
2000-09-29 20:21:41 +02:00
|
|
|
*
|
|
|
|
* Side effect: if we find a match, mark the RTE as requiring read access.
|
|
|
|
* See comments in setTargetTable().
|
2000-09-12 23:07:18 +02:00
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
|
|
|
{
|
|
|
|
Node *result = NULL;
|
|
|
|
int attnum = 0;
|
|
|
|
List *c;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* Scan the user column names (or aliases) for a match.
|
|
|
|
* Complain if multiple matches.
|
|
|
|
*/
|
|
|
|
foreach(c, rte->eref->attrs)
|
|
|
|
{
|
|
|
|
attnum++;
|
|
|
|
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
if (result)
|
|
|
|
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
|
|
|
|
result = (Node *) make_var(pstate, rte, attnum);
|
2000-09-29 20:21:41 +02:00
|
|
|
rte->checkForRead = true;
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* If we have a unique match, return it. Note that this allows a user
|
|
|
|
* alias to override a system column name (such as OID) without error.
|
|
|
|
*/
|
|
|
|
if (result)
|
|
|
|
return result;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* If the RTE represents a table (not a sub-select), consider system
|
|
|
|
* column names.
|
|
|
|
*/
|
|
|
|
if (rte->relid != InvalidOid)
|
|
|
|
{
|
|
|
|
attnum = specialAttNum(colname);
|
|
|
|
if (attnum != InvalidAttrNumber)
|
2000-09-29 20:21:41 +02:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
result = (Node *) make_var(pstate, rte, attnum);
|
2000-09-29 20:21:41 +02:00
|
|
|
rte->checkForRead = true;
|
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* scanJoinForColumn
|
|
|
|
* Search the column names of a single join table for the given name.
|
|
|
|
* If found, return an appropriate Var node or expression, else return NULL.
|
|
|
|
* If the name proves ambiguous within this jointable, raise error.
|
2000-09-29 20:21:41 +02:00
|
|
|
*
|
|
|
|
* NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
|
|
|
|
* checkForRead true for the referenced tables. This is so because a join
|
|
|
|
* expression can only appear in a FROM clause, and any table named in
|
|
|
|
* FROM will be marked checkForRead from the beginning.
|
2000-09-12 23:07:18 +02:00
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
|
|
|
|
{
|
|
|
|
Node *result = NULL;
|
|
|
|
int attnum = 0;
|
|
|
|
List *c;
|
|
|
|
|
|
|
|
foreach(c, join->colnames)
|
|
|
|
{
|
|
|
|
attnum++;
|
|
|
|
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
|
|
|
{
|
|
|
|
if (result)
|
|
|
|
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
|
|
|
|
result = copyObject(nth(attnum-1, join->colvars));
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2000-09-12 23:07:18 +02:00
|
|
|
* If referencing an uplevel join item, we must adjust
|
|
|
|
* sublevels settings in the copied expression.
|
2000-02-15 04:38:29 +01:00
|
|
|
*/
|
2000-09-12 23:07:18 +02:00
|
|
|
if (sublevels_up > 0)
|
|
|
|
IncrementVarSublevelsUp(result, sublevels_up, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* colnameToVar
|
|
|
|
* Search for an unqualified column name.
|
|
|
|
* If found, return the appropriate Var node (or expression).
|
|
|
|
* If not found, return NULL. If the name proves ambiguous, raise error.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
colnameToVar(ParseState *pstate, char *colname)
|
|
|
|
{
|
|
|
|
Node *result = NULL;
|
|
|
|
ParseState *orig_pstate = pstate;
|
|
|
|
int levels_up = 0;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
while (pstate != NULL)
|
|
|
|
{
|
|
|
|
List *jt;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* We want to look only at top-level jointree items, and even for
|
|
|
|
* those, ignore RTEs that are marked as not inFromCl and not
|
|
|
|
* the query's target relation.
|
|
|
|
*/
|
2000-09-29 20:21:41 +02:00
|
|
|
foreach(jt, pstate->p_joinlist)
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
|
|
|
Node *jtnode = (Node *) lfirst(jt);
|
|
|
|
Node *newresult = NULL;
|
|
|
|
|
|
|
|
if (IsA(jtnode, RangeTblRef))
|
2000-02-15 04:38:29 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
|
|
|
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
|
|
|
|
|
|
|
if (! rte->inFromCl &&
|
2000-02-15 04:38:29 +01:00
|
|
|
rte != pstate->p_target_rangetblentry)
|
2000-09-12 23:07:18 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* use orig_pstate here to get the right sublevels_up */
|
|
|
|
newresult = scanRTEForColumn(orig_pstate, rte, colname);
|
|
|
|
}
|
|
|
|
else if (IsA(jtnode, JoinExpr))
|
|
|
|
{
|
|
|
|
JoinExpr *j = (JoinExpr *) jtnode;
|
|
|
|
|
|
|
|
newresult = scanJoinForColumn(j, colname, levels_up);
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
|
|
|
else
|
2000-09-12 23:07:18 +02:00
|
|
|
elog(ERROR, "colnameToVar: unexpected node type %d",
|
|
|
|
nodeTag(jtnode));
|
|
|
|
|
|
|
|
if (newresult)
|
|
|
|
{
|
|
|
|
if (result)
|
|
|
|
elog(ERROR, "Column reference \"%s\" is ambiguous",
|
|
|
|
colname);
|
|
|
|
result = newresult;
|
|
|
|
}
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
if (result != NULL)
|
2000-03-23 08:38:30 +01:00
|
|
|
break; /* found */
|
|
|
|
|
|
|
|
pstate = pstate->parentParseState;
|
2000-09-12 23:07:18 +02:00
|
|
|
levels_up++;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
|
|
|
|
return result;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 23:07:18 +02:00
|
|
|
* qualifiedNameToVar
|
|
|
|
* Search for a qualified column name (refname + column name).
|
|
|
|
* If found, return the appropriate Var node (or expression).
|
|
|
|
* If not found, return NULL. If the name proves ambiguous, raise error.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
|
|
|
bool implicitRTEOK)
|
|
|
|
{
|
|
|
|
Node *result;
|
|
|
|
Node *rteorjoin;
|
|
|
|
int sublevels_up;
|
|
|
|
|
|
|
|
rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
|
|
|
|
|
|
|
|
if (rteorjoin == NULL)
|
|
|
|
{
|
|
|
|
if (! implicitRTEOK)
|
|
|
|
return NULL;
|
|
|
|
rteorjoin = (Node *) addImplicitRTE(pstate, refname);
|
|
|
|
sublevels_up = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsA(rteorjoin, RangeTblEntry))
|
|
|
|
result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
|
|
|
|
colname);
|
|
|
|
else if (IsA(rteorjoin, JoinExpr))
|
|
|
|
result = scanJoinForColumn((JoinExpr *) rteorjoin,
|
|
|
|
colname, sublevels_up);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
|
|
|
|
nodeTag(rteorjoin));
|
|
|
|
result = NULL; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-29 20:21:41 +02:00
|
|
|
* Add an entry for a relation to the pstate's range table (p_rtable).
|
|
|
|
*
|
|
|
|
* If the specified refname is already present, raise error.
|
2000-09-12 23:07:18 +02:00
|
|
|
*
|
|
|
|
* If pstate is NULL, we just build an RTE and return it without worrying
|
|
|
|
* about membership in an rtable list.
|
1999-07-19 02:26:20 +02:00
|
|
|
*/
|
1997-11-25 23:07:18 +01:00
|
|
|
RangeTblEntry *
|
|
|
|
addRangeTableEntry(ParseState *pstate,
|
|
|
|
char *relname,
|
2000-09-12 23:07:18 +02:00
|
|
|
Attr *alias,
|
1997-11-25 23:07:18 +01:00
|
|
|
bool inh,
|
2000-09-12 23:07:18 +02:00
|
|
|
bool inFromCl)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
char *refname = alias ? alias->relname : relname;
|
2000-11-08 23:10:03 +01:00
|
|
|
LOCKMODE lockmode;
|
2000-04-12 19:17:23 +02:00
|
|
|
Relation rel;
|
|
|
|
RangeTblEntry *rte;
|
|
|
|
Attr *eref;
|
|
|
|
int maxattrs;
|
2000-09-12 23:07:18 +02:00
|
|
|
int numaliases;
|
2000-04-12 19:17:23 +02:00
|
|
|
int varattno;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/* Check for conflicting RTE or jointable alias (at level 0 only) */
|
1998-01-20 23:12:17 +01:00
|
|
|
if (pstate != NULL)
|
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
|
1999-10-07 06:23:24 +02:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
if (rteorjoin)
|
|
|
|
elog(ERROR, "Table name \"%s\" specified more than once",
|
|
|
|
refname);
|
1998-01-20 23:12:17 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-10-07 06:23:24 +02:00
|
|
|
rte = makeNode(RangeTblEntry);
|
|
|
|
|
2000-02-15 04:38:29 +01:00
|
|
|
rte->relname = relname;
|
2000-09-12 23:07:18 +02:00
|
|
|
rte->alias = alias;
|
2000-09-29 20:21:41 +02:00
|
|
|
rte->subquery = NULL;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Get the rel's OID. This access also ensures that we have an
|
2000-11-08 23:10:03 +01:00
|
|
|
* up-to-date relcache entry for the rel. Since this is typically
|
|
|
|
* the first access to a rel in a statement, be careful to get the
|
|
|
|
* right access level depending on whether we're doing SELECT FOR UPDATE.
|
1999-10-07 06:23:24 +02:00
|
|
|
*/
|
2000-11-08 23:10:03 +01:00
|
|
|
lockmode = isForUpdate(pstate, relname) ? RowShareLock : AccessShareLock;
|
|
|
|
rel = heap_openr(relname, lockmode);
|
2000-02-15 04:38:29 +01:00
|
|
|
rte->relid = RelationGetRelid(rel);
|
2000-03-15 00:06:59 +01:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
|
2000-09-12 23:07:18 +02:00
|
|
|
numaliases = length(eref->attrs);
|
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
/*
|
|
|
|
* Since the rel is open anyway, let's check that the
|
|
|
|
* number of column aliases is reasonable. - Thomas 2000-02-04
|
|
|
|
*/
|
|
|
|
maxattrs = RelationGetNumberOfAttributes(rel);
|
2000-09-12 23:07:18 +02:00
|
|
|
if (maxattrs < numaliases)
|
|
|
|
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
|
|
|
|
refname, maxattrs, numaliases);
|
2000-02-15 04:38:29 +01:00
|
|
|
|
|
|
|
/* fill in any unspecified alias columns */
|
2000-09-12 23:07:18 +02:00
|
|
|
for (varattno = numaliases; varattno < maxattrs; varattno++)
|
2000-02-15 04:38:29 +01:00
|
|
|
{
|
|
|
|
char *attrname;
|
|
|
|
|
|
|
|
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
|
2000-03-15 00:06:59 +01:00
|
|
|
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
2000-03-15 00:06:59 +01:00
|
|
|
rte->eref = eref;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
/*
|
|
|
|
* Drop the rel refcount, but keep the access lock till end of transaction
|
|
|
|
* so that the table can't be deleted or have its schema modified
|
|
|
|
* underneath us.
|
|
|
|
*/
|
|
|
|
heap_close(rel, NoLock);
|
2000-09-12 23:07:18 +02:00
|
|
|
|
|
|
|
/*----------
|
|
|
|
* Flags:
|
|
|
|
* - this RTE should be expanded to include descendant tables,
|
|
|
|
* - this RTE is in the FROM clause,
|
2000-09-29 20:21:41 +02:00
|
|
|
* - this RTE should be checked for read/write access rights.
|
|
|
|
*
|
|
|
|
* The initial default on access checks is always check-for-READ-access,
|
|
|
|
* which is the right thing for all except target tables.
|
2000-09-12 23:07:18 +02:00
|
|
|
*----------
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
|
|
|
rte->inh = inh;
|
|
|
|
rte->inFromCl = inFromCl;
|
2000-09-29 20:21:41 +02:00
|
|
|
rte->checkForRead = true;
|
|
|
|
rte->checkForWrite = false;
|
|
|
|
|
|
|
|
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
|
1997-11-25 23:07:18 +01:00
|
|
|
|
|
|
|
/*
|
1999-10-07 06:23:24 +02:00
|
|
|
* Add completed RTE to range table list.
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
|
|
|
if (pstate != NULL)
|
|
|
|
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
|
|
|
|
|
|
|
return rte;
|
|
|
|
}
|
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
2000-09-29 20:21:41 +02:00
|
|
|
* Add an entry for a subquery to the pstate's range table (p_rtable).
|
|
|
|
*
|
|
|
|
* This is just like addRangeTableEntry() except that it makes a subquery RTE.
|
|
|
|
* Note that an alias clause *must* be supplied.
|
|
|
|
*/
|
|
|
|
RangeTblEntry *
|
|
|
|
addRangeTableEntryForSubquery(ParseState *pstate,
|
|
|
|
Query *subquery,
|
|
|
|
Attr *alias,
|
|
|
|
bool inFromCl)
|
|
|
|
{
|
|
|
|
char *refname = alias->relname;
|
|
|
|
RangeTblEntry *rte;
|
|
|
|
Attr *eref;
|
|
|
|
int numaliases;
|
|
|
|
int varattno;
|
|
|
|
List *tlistitem;
|
|
|
|
|
|
|
|
/* Check for conflicting RTE or jointable alias (at level 0 only) */
|
|
|
|
if (pstate != NULL)
|
|
|
|
{
|
|
|
|
Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
|
|
|
|
|
|
|
|
if (rteorjoin)
|
|
|
|
elog(ERROR, "Table name \"%s\" specified more than once",
|
|
|
|
refname);
|
|
|
|
}
|
|
|
|
|
|
|
|
rte = makeNode(RangeTblEntry);
|
|
|
|
|
|
|
|
rte->relname = NULL;
|
|
|
|
rte->relid = InvalidOid;
|
|
|
|
rte->subquery = subquery;
|
|
|
|
rte->alias = alias;
|
|
|
|
|
|
|
|
eref = copyObject(alias);
|
|
|
|
numaliases = length(eref->attrs);
|
|
|
|
|
|
|
|
/* fill in any unspecified alias columns */
|
|
|
|
varattno = 0;
|
|
|
|
foreach(tlistitem, subquery->targetList)
|
|
|
|
{
|
|
|
|
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
|
|
|
|
|
|
|
|
if (te->resdom->resjunk)
|
|
|
|
continue;
|
|
|
|
varattno++;
|
|
|
|
Assert(varattno == te->resdom->resno);
|
|
|
|
if (varattno > numaliases)
|
|
|
|
{
|
|
|
|
char *attrname;
|
|
|
|
|
|
|
|
attrname = pstrdup(te->resdom->resname);
|
|
|
|
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (varattno < numaliases)
|
|
|
|
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
|
|
|
|
refname, varattno, numaliases);
|
|
|
|
|
|
|
|
rte->eref = eref;
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
* Flags:
|
|
|
|
* - this RTE should be expanded to include descendant tables,
|
|
|
|
* - this RTE is in the FROM clause,
|
|
|
|
* - this RTE should be checked for read/write access rights.
|
|
|
|
*
|
|
|
|
* Subqueries are never checked for access rights.
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
rte->inh = false; /* never true for subqueries */
|
|
|
|
rte->inFromCl = inFromCl;
|
|
|
|
rte->checkForRead = false;
|
|
|
|
rte->checkForWrite = false;
|
|
|
|
|
|
|
|
rte->checkAsUser = InvalidOid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add completed RTE to range table list.
|
|
|
|
*/
|
|
|
|
if (pstate != NULL)
|
|
|
|
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
|
|
|
|
|
|
|
return rte;
|
|
|
|
}
|
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
/*
|
|
|
|
* Has the specified relname been selected FOR UPDATE?
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
isForUpdate(ParseState *pstate, char *relname)
|
|
|
|
{
|
|
|
|
/* Outer loop to check parent query levels as well as this one */
|
|
|
|
while (pstate != NULL)
|
|
|
|
{
|
|
|
|
if (pstate->p_forUpdate != NIL)
|
|
|
|
{
|
|
|
|
if (lfirst(pstate->p_forUpdate) == NULL)
|
|
|
|
{
|
|
|
|
/* all tables used in query */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* just the named tables */
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
foreach(l, pstate->p_forUpdate)
|
|
|
|
{
|
|
|
|
char *rname = lfirst(l);
|
|
|
|
|
|
|
|
if (strcmp(relname, rname) == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pstate = pstate->parentParseState;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
/*
|
|
|
|
* Add the given RTE as a top-level entry in the pstate's join list,
|
2000-09-12 23:07:18 +02:00
|
|
|
* unless there already is an entry for it.
|
2000-02-15 04:38:29 +01:00
|
|
|
*/
|
2000-09-12 23:07:18 +02:00
|
|
|
void
|
2000-09-29 20:21:41 +02:00
|
|
|
addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
|
|
|
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
|
|
|
|
List *jt;
|
|
|
|
RangeTblRef *rtr;
|
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
foreach(jt, pstate->p_joinlist)
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
|
|
|
Node *n = (Node *) lfirst(jt);
|
|
|
|
|
|
|
|
if (IsA(n, RangeTblRef))
|
|
|
|
{
|
|
|
|
if (rtindex == ((RangeTblRef *) n)->rtindex)
|
|
|
|
return; /* it's already being joined to */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not present, so add it */
|
|
|
|
rtr = makeNode(RangeTblRef);
|
|
|
|
rtr->rtindex = rtindex;
|
2000-09-29 20:21:41 +02:00
|
|
|
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a POSTQUEL-style implicit RTE.
|
|
|
|
*
|
|
|
|
* We assume caller has already checked that there is no such RTE now.
|
|
|
|
*/
|
|
|
|
RangeTblEntry *
|
|
|
|
addImplicitRTE(ParseState *pstate, char *relname)
|
2000-02-15 04:38:29 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
RangeTblEntry *rte;
|
2000-09-12 23:07:18 +02:00
|
|
|
|
|
|
|
rte = addRangeTableEntry(pstate, relname, NULL, false, false);
|
2000-09-29 20:21:41 +02:00
|
|
|
addRTEtoJoinList(pstate, rte);
|
2000-09-12 23:07:18 +02:00
|
|
|
warnAutoRange(pstate, relname);
|
|
|
|
|
|
|
|
return rte;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* expandRTE()
|
|
|
|
*
|
|
|
|
* Given a rangetable entry, create lists of its column names (aliases if
|
|
|
|
* provided, else real names) and Vars for each column. Only user columns
|
|
|
|
* are considered, since this is primarily used to expand '*' and determine
|
|
|
|
* the contents of JOIN tables.
|
|
|
|
*
|
|
|
|
* If only one of the two kinds of output list is needed, pass NULL for the
|
|
|
|
* output pointer for the unwanted one.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
|
|
|
List **colnames, List **colvars)
|
|
|
|
{
|
2000-09-29 20:21:41 +02:00
|
|
|
int rtindex,
|
|
|
|
sublevels_up,
|
|
|
|
varattno;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
if (colnames)
|
|
|
|
*colnames = NIL;
|
|
|
|
if (colvars)
|
|
|
|
*colvars = NIL;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/* Need the RT index of the entry for creating Vars */
|
|
|
|
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
if (rte->relname)
|
|
|
|
{
|
|
|
|
/* Ordinary relation RTE */
|
|
|
|
Relation rel;
|
|
|
|
int maxattrs;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
rel = heap_openr(rte->relname, AccessShareLock);
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
maxattrs = RelationGetNumberOfAttributes(rel);
|
|
|
|
|
|
|
|
for (varattno = 0; varattno < maxattrs; varattno++)
|
|
|
|
{
|
|
|
|
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-03-09 06:00:26 +01:00
|
|
|
#ifdef _DROP_COLUMN_HACK__
|
2000-09-29 20:21:41 +02:00
|
|
|
if (COLUMN_IS_DROPPED(attr))
|
|
|
|
continue;
|
2000-04-12 19:17:23 +02:00
|
|
|
#endif /* _DROP_COLUMN_HACK__ */
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
if (colnames)
|
|
|
|
{
|
|
|
|
char *label;
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
if (varattno < length(rte->eref->attrs))
|
|
|
|
label = strVal(nth(varattno, rte->eref->attrs));
|
|
|
|
else
|
|
|
|
label = NameStr(attr->attname);
|
|
|
|
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (colvars)
|
|
|
|
{
|
|
|
|
Var *varnode;
|
|
|
|
|
|
|
|
varnode = makeVar(rtindex, attr->attnum,
|
|
|
|
attr->atttypid, attr->atttypmod,
|
|
|
|
sublevels_up);
|
|
|
|
|
|
|
|
*colvars = lappend(*colvars, varnode);
|
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Subquery RTE */
|
|
|
|
List *aliasp = rte->eref->attrs;
|
|
|
|
List *tlistitem;
|
|
|
|
|
|
|
|
varattno = 0;
|
|
|
|
foreach(tlistitem, rte->subquery->targetList)
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
2000-09-29 20:21:41 +02:00
|
|
|
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
|
|
|
|
|
|
|
|
if (te->resdom->resjunk)
|
|
|
|
continue;
|
|
|
|
varattno++;
|
|
|
|
Assert(varattno == te->resdom->resno);
|
|
|
|
|
|
|
|
if (colnames)
|
|
|
|
{
|
|
|
|
/* Assume there is one alias per target item */
|
|
|
|
char *label = strVal(lfirst(aliasp));
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
|
|
|
aliasp = lnext(aliasp);
|
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2000-09-29 20:21:41 +02:00
|
|
|
if (colvars)
|
|
|
|
{
|
|
|
|
Var *varnode;
|
|
|
|
|
|
|
|
varnode = makeVar(rtindex, varattno,
|
|
|
|
te->resdom->restype,
|
|
|
|
te->resdom->restypmod,
|
|
|
|
sublevels_up);
|
|
|
|
|
|
|
|
*colvars = lappend(*colvars, varnode);
|
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
2000-02-15 04:38:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-11-25 23:07:18 +01:00
|
|
|
/*
|
2000-09-12 23:07:18 +02:00
|
|
|
* expandRelAttrs -
|
|
|
|
* makes a list of TargetEntry nodes for the attributes of the rel
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
2000-09-12 23:07:18 +02:00
|
|
|
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
|
1997-11-25 23:07:18 +01:00
|
|
|
{
|
2000-09-12 23:07:18 +02:00
|
|
|
List *name_list,
|
|
|
|
*var_list;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
expandRTE(pstate, rte, &name_list, &var_list);
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
return expandNamesVars(pstate, name_list, var_list);
|
|
|
|
}
|
1998-01-20 06:05:08 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* expandJoinAttrs -
|
|
|
|
* makes a list of TargetEntry nodes for the attributes of the join
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
|
|
|
|
{
|
|
|
|
List *vars;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
vars = copyObject(join->colvars);
|
|
|
|
/*
|
|
|
|
* If referencing an uplevel join item, we must adjust
|
|
|
|
* sublevels settings in the copied expression.
|
|
|
|
*/
|
|
|
|
if (sublevels_up > 0)
|
|
|
|
IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
return expandNamesVars(pstate,
|
|
|
|
copyObject(join->colnames),
|
|
|
|
vars);
|
|
|
|
}
|
2000-02-15 04:38:29 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* expandNamesVars -
|
|
|
|
* Workhorse for "*" expansion: produce a list of targetentries
|
|
|
|
* given lists of column names (as String nodes) and var references.
|
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
expandNamesVars(ParseState *pstate, List *names, List *vars)
|
|
|
|
{
|
|
|
|
List *te_list = NIL;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
while (names)
|
|
|
|
{
|
|
|
|
char *label = strVal(lfirst(names));
|
|
|
|
Node *varnode = (Node *) lfirst(vars);
|
|
|
|
TargetEntry *te = makeNode(TargetEntry);
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
|
|
|
|
exprType(varnode),
|
|
|
|
exprTypmod(varnode),
|
2000-02-15 04:38:29 +01:00
|
|
|
label,
|
1999-05-17 19:03:51 +02:00
|
|
|
false);
|
2000-09-12 23:07:18 +02:00
|
|
|
te->expr = varnode;
|
1999-07-19 02:26:20 +02:00
|
|
|
te_list = lappend(te_list, te);
|
2000-09-12 23:07:18 +02:00
|
|
|
|
|
|
|
names = lnext(names);
|
|
|
|
vars = lnext(vars);
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
Assert(vars == NIL); /* lists not same length? */
|
1998-01-20 06:05:08 +01:00
|
|
|
|
1999-07-19 02:26:20 +02:00
|
|
|
return te_list;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
|
2000-09-25 20:14:55 +02:00
|
|
|
/* ----------
|
|
|
|
* get_rte_attribute_name
|
|
|
|
* Get an attribute name from a RangeTblEntry
|
|
|
|
*
|
|
|
|
* This is unlike get_attname() because we use aliases if available.
|
|
|
|
* In particular, it will work on an RTE for a subselect, whereas
|
|
|
|
* get_attname() only works on real relations.
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
|
|
|
|
{
|
|
|
|
char *attname;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is an alias, use it
|
|
|
|
*/
|
|
|
|
if (attnum > 0 && attnum <= length(rte->eref->attrs))
|
|
|
|
return strVal(nth(attnum-1, rte->eref->attrs));
|
|
|
|
/*
|
|
|
|
* Can get here for a system attribute (which never has an alias),
|
|
|
|
* or if alias name list is too short (which probably can't happen
|
|
|
|
* anymore). Neither of these cases is valid for a subselect RTE.
|
|
|
|
*/
|
|
|
|
if (rte->relid == InvalidOid)
|
|
|
|
elog(ERROR, "Invalid attnum %d for rangetable entry %s",
|
|
|
|
attnum, rte->eref->relname);
|
|
|
|
/*
|
|
|
|
* Use the real name of the table's column
|
|
|
|
*/
|
|
|
|
attname = get_attname(rte->relid, attnum);
|
|
|
|
if (attname == NULL)
|
|
|
|
elog(ERROR, "cache lookup of attribute %d in relation %u failed",
|
|
|
|
attnum, rte->relid);
|
|
|
|
return attname;
|
|
|
|
}
|
|
|
|
|
1998-01-20 06:05:08 +01:00
|
|
|
/*
|
|
|
|
* given relation and att name, return id of variable
|
|
|
|
*
|
|
|
|
* This should only be used if the relation is already
|
|
|
|
* heap_open()'ed. Use the cache version get_attnum()
|
|
|
|
* for access to non-opened relations.
|
|
|
|
*/
|
1997-11-25 23:07:18 +01:00
|
|
|
int
|
|
|
|
attnameAttNum(Relation rd, char *a)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < rd->rd_rel->relnatts; i++)
|
|
|
|
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
|
1998-09-01 05:29:17 +02:00
|
|
|
return i + 1;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-02-15 04:38:29 +01:00
|
|
|
if ((i = specialAttNum(a)) != InvalidAttrNumber)
|
|
|
|
return i;
|
1997-11-25 23:07:18 +01:00
|
|
|
|
|
|
|
/* on failure */
|
1999-02-23 08:54:03 +01:00
|
|
|
elog(ERROR, "Relation '%s' does not have attribute '%s'",
|
1997-11-25 23:07:18 +01:00
|
|
|
RelationGetRelationName(rd), a);
|
2000-04-12 19:17:23 +02:00
|
|
|
return InvalidAttrNumber; /* lint */
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
|
|
|
|
2000-02-15 04:38:29 +01:00
|
|
|
/* specialAttNum()
|
|
|
|
* Check attribute name to see if it is "special", e.g. "oid".
|
|
|
|
* - thomas 2000-02-07
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
specialAttNum(char *a)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < SPECIALS; i++)
|
2000-06-20 03:41:22 +02:00
|
|
|
if (strcmp(special_attr[i].attrname, a) == 0)
|
|
|
|
return special_attr[i].attrnum;
|
2000-02-15 04:38:29 +01:00
|
|
|
|
|
|
|
return InvalidAttrNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-06-09 00:38:00 +02:00
|
|
|
#ifdef NOT_USED
|
1998-01-20 06:05:08 +01:00
|
|
|
/*
|
|
|
|
* Given range variable, return whether attribute of this name
|
1997-11-25 23:07:18 +01:00
|
|
|
* is a set.
|
|
|
|
* NOTE the ASSUMPTION here that no system attributes are, or ever
|
|
|
|
* will be, sets.
|
1998-01-20 06:05:08 +01:00
|
|
|
*
|
|
|
|
* This should only be used if the relation is already
|
|
|
|
* heap_open()'ed. Use the cache version get_attisset()
|
|
|
|
* for access to non-opened relations.
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
attnameIsSet(Relation rd, char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* First check if this is a system attribute */
|
|
|
|
for (i = 0; i < SPECIALS; i++)
|
|
|
|
{
|
2000-06-20 03:41:22 +02:00
|
|
|
if (strcmp(special_attr[i].attrname, name) == 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return false; /* no sys attr is a set */
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return get_attisset(RelationGetRelid(rd), name);
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
2000-06-09 00:38:00 +02:00
|
|
|
#endif
|
1997-11-25 23:07:18 +01:00
|
|
|
|
2000-06-09 00:38:00 +02:00
|
|
|
#ifdef NOT_USED
|
1998-01-20 06:05:08 +01:00
|
|
|
/*
|
|
|
|
* This should only be used if the relation is already
|
|
|
|
* heap_open()'ed. Use the cache version
|
|
|
|
* for access to non-opened relations.
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
attnumAttNelems(Relation rd, int attid)
|
|
|
|
{
|
1998-09-01 05:29:17 +02:00
|
|
|
return rd->rd_att->attrs[attid - 1]->attnelems;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
2000-06-09 00:38:00 +02:00
|
|
|
#endif
|
1997-11-25 23:07:18 +01:00
|
|
|
|
|
|
|
/* given attribute id, return type of that attribute */
|
1998-01-20 06:05:08 +01:00
|
|
|
/*
|
|
|
|
* This should only be used if the relation is already
|
|
|
|
* heap_open()'ed. Use the cache version get_atttype()
|
|
|
|
* for access to non-opened relations.
|
|
|
|
*/
|
1997-11-25 23:07:18 +01:00
|
|
|
Oid
|
|
|
|
attnumTypeId(Relation rd, int attid)
|
|
|
|
{
|
|
|
|
if (attid < 0)
|
2000-06-20 03:41:22 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < SPECIALS; i++)
|
|
|
|
{
|
|
|
|
if (special_attr[i].attrnum == attid)
|
|
|
|
return special_attr[i].attrtype;
|
|
|
|
}
|
|
|
|
/* negative but not a valid system attr? */
|
|
|
|
elog(ERROR, "attnumTypeId: bogus attribute number %d", attid);
|
|
|
|
}
|
1997-11-25 23:07:18 +01:00
|
|
|
|
|
|
|
/*
|
2000-06-20 03:41:22 +02:00
|
|
|
* -1 because attid is 1-based
|
1997-11-25 23:07:18 +01:00
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
return rd->rd_att->attrs[attid - 1]->atttypid;
|
1997-11-25 23:07:18 +01:00
|
|
|
}
|
2000-06-03 06:41:34 +02:00
|
|
|
|
2000-09-12 23:07:18 +02:00
|
|
|
/*
|
|
|
|
* Generate a warning about an implicit RTE, if appropriate.
|
|
|
|
*
|
|
|
|
* Our current theory on this is that we should allow "SELECT foo.*"
|
|
|
|
* but warn about a mixture of explicit and implicit RTEs.
|
|
|
|
*/
|
|
|
|
static void
|
2000-06-03 06:41:34 +02:00
|
|
|
warnAutoRange(ParseState *pstate, char *refname)
|
|
|
|
{
|
|
|
|
bool foundInFromCl = false;
|
2000-09-12 23:07:18 +02:00
|
|
|
List *temp;
|
2000-06-03 06:41:34 +02:00
|
|
|
|
|
|
|
foreach(temp, pstate->p_rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = lfirst(temp);
|
|
|
|
|
|
|
|
if (rte->inFromCl)
|
|
|
|
{
|
|
|
|
foundInFromCl = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (foundInFromCl)
|
2000-09-12 23:07:18 +02:00
|
|
|
elog(NOTICE, "Adding missing FROM-clause entry%s for table \"%s\"",
|
|
|
|
pstate->parentParseState != NULL ? " in subquery" : "",
|
|
|
|
refname);
|
2000-06-03 06:41:34 +02:00
|
|
|
}
|
|
|
|
|