mirror of
https://github.com/postgres/postgres.git
synced 2025-08-25 20:23:07 +03:00
First phase of SCHEMA changes, concentrating on fixing the grammar and
the parsetree representation. As yet we don't *do* anything with schema names, just drop 'em on the floor; but you can enter schema-compatible command syntax, and there's even a primitive CREATE SCHEMA command. No doc updates yet, except to note that you can now extract a field from a function-returning-row's result with (foo(...)).fieldname.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.220 2002/03/12 00:51:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.221 2002/03/21 16:00:48 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -43,13 +43,30 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* State shared by transformCreateSchemaStmt and its subroutines */
|
||||
typedef struct
|
||||
{
|
||||
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
|
||||
char *schemaname; /* name of schema */
|
||||
char *authid; /* owner of schema */
|
||||
List *tables; /* CREATE TABLE items */
|
||||
List *views; /* CREATE VIEW items */
|
||||
List *grants; /* GRANT items */
|
||||
List *fwconstraints; /* Forward referencing FOREIGN KEY constraints */
|
||||
List *alters; /* Generated ALTER items (from the above) */
|
||||
List *ixconstraints; /* index-creating constraints */
|
||||
List *blist; /* "before list" of things to do before
|
||||
* creating the schema */
|
||||
List *alist; /* "after list" of things to do after
|
||||
* creating the schema */
|
||||
} CreateSchemaStmtContext;
|
||||
|
||||
/* State shared by transformCreateStmt and its subroutines */
|
||||
typedef struct
|
||||
{
|
||||
const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
|
||||
char *relname; /* name of relation */
|
||||
List *inhRelnames; /* names of relations to inherit from */
|
||||
bool istemp; /* is it to be a temp relation? */
|
||||
RangeVar *relation; /* relation to create */
|
||||
List *inhRelations; /* relations to inherit from */
|
||||
bool hasoids; /* does relation have an OID column? */
|
||||
Oid relOid; /* OID of table, if ALTER TABLE case */
|
||||
List *columns; /* ColumnDef items */
|
||||
@@ -330,8 +347,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
qry->commandType = CMD_DELETE;
|
||||
|
||||
/* set up range table with just the result rel */
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relname,
|
||||
interpretInhOption(stmt->inhOpt),
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation->relname,
|
||||
interpretInhOption(stmt->relation->inhOpt),
|
||||
true);
|
||||
|
||||
qry->distinctClause = NIL;
|
||||
@@ -398,7 +415,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
* table is also mentioned in the SELECT part. Note that the target
|
||||
* table is not added to the joinlist or namespace.
|
||||
*/
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relname,
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation->relname,
|
||||
false, false);
|
||||
|
||||
/*
|
||||
@@ -443,7 +460,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
*/
|
||||
rte = addRangeTableEntryForSubquery(pstate,
|
||||
selectQuery,
|
||||
makeAttr("*SELECT*", NULL),
|
||||
makeAlias("*SELECT*", NIL),
|
||||
true);
|
||||
rtr = makeNode(RangeTblRef);
|
||||
/* assume new rte is at end */
|
||||
@@ -515,14 +532,15 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||
foreach(tl, qry->targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
Ident *id;
|
||||
ResTarget *col;
|
||||
|
||||
Assert(!tle->resdom->resjunk);
|
||||
if (icolumns == NIL || attnos == NIL)
|
||||
elog(ERROR, "INSERT has more expressions than target columns");
|
||||
id = (Ident *) lfirst(icolumns);
|
||||
updateTargetListEntry(pstate, tle, id->name, lfirsti(attnos),
|
||||
id->indirection);
|
||||
col = (ResTarget *) lfirst(icolumns);
|
||||
Assert(IsA(col, ResTarget));
|
||||
updateTargetListEntry(pstate, tle, col->name, lfirsti(attnos),
|
||||
col->indirection);
|
||||
icolumns = lnext(icolumns);
|
||||
attnos = lnext(attnos);
|
||||
}
|
||||
@@ -691,9 +709,8 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
|
||||
List *elements;
|
||||
|
||||
cxt.stmtType = "CREATE TABLE";
|
||||
cxt.relname = stmt->relname;
|
||||
cxt.inhRelnames = stmt->inhRelnames;
|
||||
cxt.istemp = stmt->istemp;
|
||||
cxt.relation = stmt->relation;
|
||||
cxt.inhRelations = stmt->inhRelations;
|
||||
cxt.hasoids = stmt->hasoids;
|
||||
cxt.relOid = InvalidOid;
|
||||
cxt.columns = NIL;
|
||||
@@ -805,7 +822,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
* conflicting constraints the user wrote (like a different
|
||||
* DEFAULT).
|
||||
*/
|
||||
sname = makeObjectName(cxt->relname, column->colname, "seq");
|
||||
sname = makeObjectName((cxt->relation)->relname, column->colname, "seq");
|
||||
|
||||
/*
|
||||
* Create an expression tree representing the function call
|
||||
@@ -845,12 +862,12 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
* CREATE/ALTER TABLE.
|
||||
*/
|
||||
sequence = makeNode(CreateSeqStmt);
|
||||
sequence->seqname = pstrdup(sname);
|
||||
sequence->istemp = cxt->istemp;
|
||||
sequence->sequence = copyObject(cxt->relation);
|
||||
sequence->sequence->relname = pstrdup(sname);
|
||||
sequence->options = NIL;
|
||||
|
||||
elog(NOTICE, "%s will create implicit sequence '%s' for SERIAL column '%s.%s'",
|
||||
cxt->stmtType, sequence->seqname, cxt->relname, column->colname);
|
||||
cxt->stmtType, sequence->sequence->relname, (cxt->relation)->relname, column->colname);
|
||||
|
||||
cxt->blist = lappend(cxt->blist, sequence);
|
||||
}
|
||||
@@ -875,9 +892,6 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
Ident *id = makeNode(Ident);
|
||||
|
||||
id->name = column->colname;
|
||||
id->indirection = NIL;
|
||||
id->isRel = false;
|
||||
|
||||
fkconstraint->fk_attrs = makeList1(id);
|
||||
|
||||
cxt->fkconstraints = lappend(cxt->fkconstraints, fkconstraint);
|
||||
@@ -891,7 +905,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
case CONSTR_NULL:
|
||||
if (saw_nullable && column->is_not_null)
|
||||
elog(ERROR, "%s/(NOT) NULL conflicting declaration for '%s.%s'",
|
||||
cxt->stmtType, cxt->relname, column->colname);
|
||||
cxt->stmtType, (cxt->relation)->relname, column->colname);
|
||||
column->is_not_null = FALSE;
|
||||
saw_nullable = true;
|
||||
break;
|
||||
@@ -899,7 +913,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
case CONSTR_NOTNULL:
|
||||
if (saw_nullable && !column->is_not_null)
|
||||
elog(ERROR, "%s/(NOT) NULL conflicting declaration for '%s.%s'",
|
||||
cxt->stmtType, cxt->relname, column->colname);
|
||||
cxt->stmtType, (cxt->relation)->relname, column->colname);
|
||||
column->is_not_null = TRUE;
|
||||
saw_nullable = true;
|
||||
break;
|
||||
@@ -907,14 +921,14 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
case CONSTR_DEFAULT:
|
||||
if (column->raw_default != NULL)
|
||||
elog(ERROR, "%s/DEFAULT multiple values specified for '%s.%s'",
|
||||
cxt->stmtType, cxt->relname, column->colname);
|
||||
cxt->stmtType, (cxt->relation)->relname, column->colname);
|
||||
column->raw_default = constraint->raw_expr;
|
||||
Assert(constraint->cooked_expr == NULL);
|
||||
break;
|
||||
|
||||
case CONSTR_PRIMARY:
|
||||
if (constraint->name == NULL)
|
||||
constraint->name = makeObjectName(cxt->relname,
|
||||
constraint->name = makeObjectName((cxt->relation)->relname,
|
||||
NULL,
|
||||
"pkey");
|
||||
if (constraint->keys == NIL)
|
||||
@@ -928,7 +942,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
|
||||
case CONSTR_UNIQUE:
|
||||
if (constraint->name == NULL)
|
||||
constraint->name = makeObjectName(cxt->relname,
|
||||
constraint->name = makeObjectName((cxt->relation)->relname,
|
||||
column->colname,
|
||||
"key");
|
||||
if (constraint->keys == NIL)
|
||||
@@ -942,7 +956,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
|
||||
|
||||
case CONSTR_CHECK:
|
||||
if (constraint->name == NULL)
|
||||
constraint->name = makeObjectName(cxt->relname,
|
||||
constraint->name = makeObjectName((cxt->relation)->relname,
|
||||
column->colname,
|
||||
NULL);
|
||||
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
|
||||
@@ -970,7 +984,7 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
|
||||
{
|
||||
case CONSTR_PRIMARY:
|
||||
if (constraint->name == NULL)
|
||||
constraint->name = makeObjectName(cxt->relname,
|
||||
constraint->name = makeObjectName((cxt->relation)->relname,
|
||||
NULL,
|
||||
"pkey");
|
||||
cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
|
||||
@@ -1034,21 +1048,21 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
/* In ALTER TABLE case, a primary index might already exist */
|
||||
if (cxt->pkey != NULL ||
|
||||
(OidIsValid(cxt->relOid) &&
|
||||
relationHasPrimaryKey(cxt->relname)))
|
||||
relationHasPrimaryKey((cxt->relation)->relname)))
|
||||
elog(ERROR, "%s / PRIMARY KEY multiple primary keys"
|
||||
" for table '%s' are not allowed",
|
||||
cxt->stmtType, cxt->relname);
|
||||
cxt->stmtType, (cxt->relation)->relname);
|
||||
cxt->pkey = index;
|
||||
}
|
||||
|
||||
if (constraint->name != NULL)
|
||||
index->idxname = pstrdup(constraint->name);
|
||||
else if (constraint->contype == CONSTR_PRIMARY)
|
||||
index->idxname = makeObjectName(cxt->relname, NULL, "pkey");
|
||||
index->idxname = makeObjectName((cxt->relation)->relname, NULL, "pkey");
|
||||
else
|
||||
index->idxname = NULL; /* will set it later */
|
||||
|
||||
index->relname = cxt->relname;
|
||||
index->relation = cxt->relation;
|
||||
index->accessMethod = DEFAULT_INDEX_TYPE;
|
||||
index->indexParams = NIL;
|
||||
index->whereClause = NULL;
|
||||
@@ -1089,19 +1103,19 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
*/
|
||||
found = true;
|
||||
}
|
||||
else if (cxt->inhRelnames)
|
||||
else if (cxt->inhRelations)
|
||||
{
|
||||
/* try inherited tables */
|
||||
List *inher;
|
||||
|
||||
foreach(inher, cxt->inhRelnames)
|
||||
foreach(inher, cxt->inhRelations)
|
||||
{
|
||||
Value *inh = lfirst(inher);
|
||||
RangeVar *inh = lfirst(inher);
|
||||
Relation rel;
|
||||
int count;
|
||||
|
||||
Assert(IsA(inh, String));
|
||||
rel = heap_openr(strVal(inh), AccessShareLock);
|
||||
Assert(IsA(inh, RangeVar));
|
||||
rel = heap_openr(inh->relname, AccessShareLock);
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "inherited table \"%s\" is not a relation",
|
||||
strVal(inh));
|
||||
@@ -1257,7 +1271,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
if (index->idxname == NULL && index->indexParams != NIL)
|
||||
{
|
||||
iparam = lfirst(index->indexParams);
|
||||
index->idxname = CreateIndexName(cxt->relname, iparam->name,
|
||||
index->idxname = CreateIndexName((cxt->relation)->relname, iparam->name,
|
||||
"key", cxt->alist);
|
||||
}
|
||||
if (index->idxname == NULL) /* should not happen */
|
||||
@@ -1268,7 +1282,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
cxt->stmtType,
|
||||
(strcmp(cxt->stmtType, "ALTER TABLE") == 0) ? "ADD " : "",
|
||||
(index->primary ? "PRIMARY KEY" : "UNIQUE"),
|
||||
index->idxname, cxt->relname);
|
||||
index->idxname, (cxt->relation)->relname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,7 +1342,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
*/
|
||||
if (fkconstraint->pk_attrs == NIL)
|
||||
{
|
||||
if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
|
||||
if (strcmp(fkconstraint->pktable->relname, (cxt->relation)->relname) != 0)
|
||||
transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
|
||||
else if (cxt->pkey != NULL)
|
||||
{
|
||||
@@ -1342,8 +1356,6 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
Ident *pkattr = (Ident *) makeNode(Ident);
|
||||
|
||||
pkattr->name = pstrdup(ielem->name);
|
||||
pkattr->indirection = NIL;
|
||||
pkattr->isRel = false;
|
||||
fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs,
|
||||
pkattr);
|
||||
if (attnum >= INDEX_MAX_KEYS)
|
||||
@@ -1360,13 +1372,13 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
|
||||
else
|
||||
elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Validate the specified referenced key list */
|
||||
if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
|
||||
if (strcmp(fkconstraint->pktable->relname, (cxt->relation)->relname) != 0)
|
||||
transformFkeyCheckAttrs(fkconstraint, pktypoid);
|
||||
else
|
||||
{
|
||||
@@ -1422,7 +1434,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
transformFkeyCheckAttrs(fkconstraint, pktypoid);
|
||||
else
|
||||
elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1447,7 +1459,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
*/
|
||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relname = cxt->relname;
|
||||
fk_trigger->relation = cxt->relation;
|
||||
fk_trigger->funcname = "RI_FKey_check_ins";
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
@@ -1462,15 +1474,15 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrelname = fkconstraint->pktable_name;
|
||||
fk_trigger->constrrel = fkconstraint->pktable;
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(cxt->relname));
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable_name));
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
@@ -1478,7 +1490,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
if (length(fk_attr) != length(pk_attr))
|
||||
elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
|
||||
"\n\tIllegal FOREIGN KEY definition references \"%s\"",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
@@ -1502,7 +1514,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
*/
|
||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relname = fkconstraint->pktable_name;
|
||||
fk_trigger->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'd';
|
||||
@@ -1515,7 +1527,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrelname = cxt->relname;
|
||||
fk_trigger->constrrel = cxt->relation;
|
||||
switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
|
||||
>> FKCONSTR_ON_DELETE_SHIFT)
|
||||
{
|
||||
@@ -1545,9 +1557,9 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(cxt->relname));
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable_name));
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
@@ -1574,7 +1586,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
*/
|
||||
fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relname = fkconstraint->pktable_name;
|
||||
fk_trigger->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'u';
|
||||
@@ -1587,7 +1599,7 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrelname = cxt->relname;
|
||||
fk_trigger->constrrel = cxt->relation;
|
||||
switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
|
||||
>> FKCONSTR_ON_UPDATE_SHIFT)
|
||||
{
|
||||
@@ -1617,9 +1629,9 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(cxt->relname));
|
||||
makeString((cxt->relation)->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable_name));
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->match_type));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
@@ -1672,7 +1684,7 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
* easily support predicates on indexes created implicitly by
|
||||
* CREATE TABLE. Fortunately, that's not necessary.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, stmt->relname, NULL, false, true);
|
||||
rte = addRangeTableEntry(pstate, stmt->relation->relname, NULL, false, true);
|
||||
|
||||
/* no to join list, yes to namespace */
|
||||
addRTEtoQuery(pstate, rte, false, true);
|
||||
@@ -1712,7 +1724,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
* beforehand. We don't need to hold a refcount on the relcache
|
||||
* entry, however.
|
||||
*/
|
||||
heap_close(heap_openr(stmt->object->relname, AccessExclusiveLock),
|
||||
heap_close(heap_openr(stmt->relation->relname, AccessExclusiveLock),
|
||||
NoLock);
|
||||
|
||||
/*
|
||||
@@ -1721,11 +1733,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
* rule qualification.
|
||||
*/
|
||||
Assert(pstate->p_rtable == NIL);
|
||||
oldrte = addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
oldrte = addRangeTableEntry(pstate, stmt->relation->relname,
|
||||
makeAlias("*OLD*", NIL),
|
||||
false, true);
|
||||
newrte = addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
newrte = addRangeTableEntry(pstate, stmt->relation->relname,
|
||||
makeAlias("*NEW*", NIL),
|
||||
false, true);
|
||||
/* Must override addRangeTableEntry's default access-check flags */
|
||||
oldrte->checkForRead = false;
|
||||
@@ -1812,11 +1824,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||
* or they won't be accessible at all. We decide later
|
||||
* whether to put them in the joinlist.
|
||||
*/
|
||||
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
oldrte = addRangeTableEntry(sub_pstate, stmt->relation->relname,
|
||||
makeAlias("*OLD*", NIL),
|
||||
false, false);
|
||||
newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
newrte = addRangeTableEntry(sub_pstate, stmt->relation->relname,
|
||||
makeAlias("*NEW*", NIL),
|
||||
false, false);
|
||||
oldrte->checkForRead = false;
|
||||
newrte->checkForRead = false;
|
||||
@@ -1950,8 +1962,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
if (!IsTransactionBlock())
|
||||
elog(ERROR, "DECLARE CURSOR may only be used in begin/end transaction blocks");
|
||||
|
||||
qry->into = stmt->portalname;
|
||||
qry->isTemp = stmt->istemp;
|
||||
qry->into = makeNode(RangeVar);
|
||||
qry->into->relname = stmt->portalname;
|
||||
qry->isPortal = TRUE;
|
||||
qry->isBinary = stmt->binary; /* internal portal */
|
||||
}
|
||||
@@ -1959,7 +1971,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
{
|
||||
/* SELECT */
|
||||
qry->into = stmt->into;
|
||||
qry->isTemp = stmt->istemp;
|
||||
qry->isPortal = FALSE;
|
||||
qry->isBinary = FALSE;
|
||||
}
|
||||
@@ -2033,8 +2044,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
int leftmostRTI;
|
||||
Query *leftmostQuery;
|
||||
SetOperationStmt *sostmt;
|
||||
char *into;
|
||||
bool istemp;
|
||||
RangeVar *into;
|
||||
List *intoColNames;
|
||||
char *portalname;
|
||||
bool binary;
|
||||
@@ -2065,14 +2075,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
|
||||
leftmostSelect->larg == NULL);
|
||||
into = leftmostSelect->into;
|
||||
istemp = leftmostSelect->istemp;
|
||||
intoColNames = leftmostSelect->intoColNames;
|
||||
portalname = stmt->portalname;
|
||||
binary = stmt->binary;
|
||||
|
||||
/* clear them to prevent complaints in transformSetOperationTree() */
|
||||
leftmostSelect->into = NULL;
|
||||
leftmostSelect->istemp = false;
|
||||
leftmostSelect->intoColNames = NIL;
|
||||
stmt->portalname = NULL;
|
||||
stmt->binary = false;
|
||||
@@ -2174,8 +2182,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
if (!IsTransactionBlock())
|
||||
elog(ERROR, "DECLARE CURSOR may only be used in begin/end transaction blocks");
|
||||
|
||||
qry->into = portalname;
|
||||
qry->isTemp = istemp;
|
||||
qry->into = makeNode(RangeVar);
|
||||
qry->into->relname = portalname;
|
||||
qry->isPortal = TRUE;
|
||||
qry->isBinary = binary; /* internal portal */
|
||||
}
|
||||
@@ -2183,7 +2191,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
{
|
||||
/* SELECT */
|
||||
qry->into = into;
|
||||
qry->isTemp = istemp;
|
||||
qry->isPortal = FALSE;
|
||||
qry->isBinary = FALSE;
|
||||
}
|
||||
@@ -2325,8 +2332,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
|
||||
sprintf(selectName, "*SELECT* %d", length(pstate->p_rtable) + 1);
|
||||
rte = addRangeTableEntryForSubquery(pstate,
|
||||
selectQuery,
|
||||
makeAttr(pstrdup(selectName),
|
||||
NULL),
|
||||
makeAlias(selectName, NIL),
|
||||
false);
|
||||
|
||||
/*
|
||||
@@ -2468,8 +2474,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
qry->commandType = CMD_UPDATE;
|
||||
pstate->p_is_update = true;
|
||||
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relname,
|
||||
interpretInhOption(stmt->inhOpt),
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relation->relname,
|
||||
interpretInhOption(stmt->relation->inhOpt),
|
||||
true);
|
||||
|
||||
/*
|
||||
@@ -2553,11 +2559,11 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
{
|
||||
case 'A':
|
||||
cxt.stmtType = "ALTER TABLE";
|
||||
cxt.relname = stmt->relname;
|
||||
cxt.inhRelnames = NIL;
|
||||
cxt.istemp = is_temp_rel_name(stmt->relname);
|
||||
cxt.relation = stmt->relation;
|
||||
cxt.inhRelations = NIL;
|
||||
cxt.relation->istemp = is_temp_rel_name(stmt->relation->relname);
|
||||
cxt.relOid = GetSysCacheOid(RELNAME,
|
||||
PointerGetDatum(stmt->relname),
|
||||
PointerGetDatum((stmt->relation)->relname),
|
||||
0, 0, 0);
|
||||
cxt.hasoids = SearchSysCacheExists(ATTNUM,
|
||||
ObjectIdGetDatum(cxt.relOid),
|
||||
@@ -2585,11 +2591,11 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
|
||||
case 'C':
|
||||
cxt.stmtType = "ALTER TABLE";
|
||||
cxt.relname = stmt->relname;
|
||||
cxt.inhRelnames = NIL;
|
||||
cxt.istemp = is_temp_rel_name(stmt->relname);
|
||||
cxt.relation = stmt->relation;
|
||||
cxt.inhRelations = NIL;
|
||||
cxt.relation->istemp = is_temp_rel_name(stmt->relation->relname);
|
||||
cxt.relOid = GetSysCacheOid(RELNAME,
|
||||
PointerGetDatum(stmt->relname),
|
||||
PointerGetDatum((stmt->relation)->relname),
|
||||
0, 0, 0);
|
||||
cxt.hasoids = SearchSysCacheExists(ATTNUM,
|
||||
ObjectIdGetDatum(cxt.relOid),
|
||||
@@ -2713,15 +2719,18 @@ transformTypeRefsList(ParseState *pstate, List *l)
|
||||
static void
|
||||
transformTypeRef(ParseState *pstate, TypeName *tn)
|
||||
{
|
||||
Attr *att;
|
||||
ColumnRef *cref;
|
||||
Node *n;
|
||||
Var *v;
|
||||
char *tyn;
|
||||
|
||||
if (tn->attrname == NULL)
|
||||
return;
|
||||
att = makeAttr(tn->name, tn->attrname);
|
||||
n = transformExpr(pstate, (Node *) att, EXPR_COLUMN_FIRST);
|
||||
/* XXX this needs work; can't type name be qualified? */
|
||||
cref = makeNode(ColumnRef);
|
||||
cref->fields = makeList2(makeString(tn->name), makeString(tn->attrname));
|
||||
cref->indirection = NIL;
|
||||
n = transformExpr(pstate, (Node *) cref);
|
||||
if (!IsA(n, Var))
|
||||
elog(ERROR, "unsupported expression in %%TYPE");
|
||||
v = (Var *) n;
|
||||
@@ -2791,7 +2800,7 @@ transformForUpdate(Query *qry, List *forUpdate)
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
||||
|
||||
++i;
|
||||
if (strcmp(rte->eref->relname, relname) == 0)
|
||||
if (strcmp(rte->eref->aliasname, relname) == 0)
|
||||
{
|
||||
if (rte->subquery)
|
||||
{
|
||||
@@ -2835,11 +2844,11 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
|
||||
/*
|
||||
* Open the referenced table
|
||||
*/
|
||||
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
|
||||
pkrel = heap_openr(fkconstraint->pktable->relname, AccessShareLock);
|
||||
|
||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "Referenced relation \"%s\" is not a table",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
/*
|
||||
* Get the list of index OIDs for the table from the relcache, and
|
||||
@@ -2901,7 +2910,7 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
|
||||
}
|
||||
if (!found)
|
||||
elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
freeList(indexoidlist);
|
||||
heap_close(pkrel, AccessShareLock);
|
||||
@@ -2928,11 +2937,11 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
|
||||
/*
|
||||
* Open the referenced table
|
||||
*/
|
||||
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
|
||||
pkrel = heap_openr(fkconstraint->pktable->relname, AccessShareLock);
|
||||
|
||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "Referenced relation \"%s\" is not a table",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
/*
|
||||
* Get the list of index OIDs for the table from the relcache, and
|
||||
@@ -2965,7 +2974,7 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
|
||||
*/
|
||||
if (indexStruct == NULL)
|
||||
elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
/*
|
||||
* Now build the list of PK attributes from the indkey definition
|
||||
@@ -2977,8 +2986,6 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
|
||||
Ident *pkattr = makeNode(Ident);
|
||||
|
||||
pkattr->name = pstrdup(NameStr(*attnumAttName(pkrel, pkattno)));
|
||||
pkattr->indirection = NIL;
|
||||
pkattr->isRel = false;
|
||||
pktypoid[attnum++] = attnumTypeId(pkrel, pkattno);
|
||||
|
||||
fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr);
|
||||
@@ -3070,14 +3077,14 @@ transformFkeyGetColType(CreateStmtContext *cxt, char *colname)
|
||||
if (sysatt)
|
||||
return sysatt->atttypid;
|
||||
/* Look for column among inherited columns (if CREATE TABLE case) */
|
||||
foreach(inher, cxt->inhRelnames)
|
||||
foreach(inher, cxt->inhRelations)
|
||||
{
|
||||
Value *inh = lfirst(inher);
|
||||
RangeVar *inh = lfirst(inher);
|
||||
Relation rel;
|
||||
int count;
|
||||
|
||||
Assert(IsA(inh, String));
|
||||
rel = heap_openr(strVal(inh), AccessShareLock);
|
||||
Assert(IsA(inh, RangeVar));
|
||||
rel = heap_openr(inh->relname, AccessShareLock);
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "inherited table \"%s\" is not a relation",
|
||||
strVal(inh));
|
||||
@@ -3248,3 +3255,104 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
|
||||
|
||||
ReleaseSysCache(ctype);
|
||||
}
|
||||
|
||||
/*
|
||||
* analyzeCreateSchemaStmt -
|
||||
* analyzes the "create schema" statement
|
||||
*
|
||||
* Split the schema element list into individual commands and place
|
||||
* them in the result list in an order such that there are no
|
||||
* forward references (e.g. GRANT to a table created later in the list).
|
||||
*
|
||||
* SQL92 also allows constraints to make forward references, so thumb through
|
||||
* the table columns and move forward references to a posterior alter-table
|
||||
* command.
|
||||
*
|
||||
* The result is a list of parse nodes that still need to be analyzed ---
|
||||
* but we can't analyze the later commands until we've executed the earlier
|
||||
* ones, because of possible inter-object references.
|
||||
*
|
||||
* Note: Called from commands/command.c
|
||||
*/
|
||||
List *
|
||||
analyzeCreateSchemaStmt(CreateSchemaStmt *stmt)
|
||||
{
|
||||
CreateSchemaStmtContext cxt;
|
||||
List *result;
|
||||
List *elements;
|
||||
|
||||
cxt.stmtType = "CREATE SCHEMA";
|
||||
cxt.schemaname = stmt->schemaname;
|
||||
cxt.authid = stmt->authid;
|
||||
cxt.tables = NIL;
|
||||
cxt.views = NIL;
|
||||
cxt.grants = NIL;
|
||||
cxt.fwconstraints = NIL;
|
||||
cxt.alters = NIL;
|
||||
cxt.blist = NIL;
|
||||
cxt.alist = NIL;
|
||||
|
||||
/*
|
||||
* Run through each schema element in the schema element list.
|
||||
* Separate statements by type, and do preliminary analysis.
|
||||
*/
|
||||
foreach(elements, stmt->schemaElts)
|
||||
{
|
||||
Node *element = lfirst(elements);
|
||||
|
||||
switch (nodeTag(element))
|
||||
{
|
||||
case T_CreateStmt:
|
||||
{
|
||||
CreateStmt *elp = (CreateStmt *) element;
|
||||
|
||||
if (elp->relation->schemaname == NULL)
|
||||
elp->relation->schemaname = cxt.schemaname;
|
||||
else if (strcmp(cxt.schemaname, elp->relation->schemaname))
|
||||
elog(ERROR, "New table refers to a schema (%s)"
|
||||
" different from the one being created (%s)",
|
||||
elp->relation->schemaname, cxt.schemaname);
|
||||
|
||||
/*
|
||||
* XXX todo: deal with constraints
|
||||
*/
|
||||
|
||||
cxt.tables = lappend(cxt.tables, element);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_ViewStmt:
|
||||
{
|
||||
ViewStmt *elp = (ViewStmt *) element;
|
||||
|
||||
if (elp->view->schemaname == NULL)
|
||||
elp->view->schemaname = cxt.schemaname;
|
||||
else if (strcmp(cxt.schemaname, elp->view->schemaname))
|
||||
elog(ERROR, "New view refers to a schema (%s)"
|
||||
" different from the one being created (%s)",
|
||||
elp->view->schemaname, cxt.schemaname);
|
||||
|
||||
/*
|
||||
* XXX todo: deal with references between views
|
||||
*/
|
||||
|
||||
cxt.views = lappend(cxt.views, element);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_GrantStmt:
|
||||
cxt.grants = lappend(cxt.grants, element);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "parser: unsupported schema node (internal error)");
|
||||
}
|
||||
}
|
||||
|
||||
result = NIL;
|
||||
result = nconc(result, cxt.tables);
|
||||
result = nconc(result, cxt.views);
|
||||
result = nconc(result, cxt.grants);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.46 2001/10/25 05:49:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.47 2002/03/21 16:00:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -116,7 +116,7 @@ check_ungrouped_columns_walker(Node *node,
|
||||
rte = rt_fetch(var->varno, context->pstate->p_rtable);
|
||||
attname = get_rte_attribute_name(rte, var->varattno);
|
||||
elog(ERROR, "Attribute %s.%s must be GROUPed or used in an aggregate function",
|
||||
rte->eref->relname, attname);
|
||||
rte->eref->aliasname, attname);
|
||||
}
|
||||
/* Otherwise, recurse. */
|
||||
return expression_tree_walker(node, check_ungrouped_columns_walker,
|
||||
@@ -188,8 +188,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
|
||||
|
||||
Aggref *
|
||||
ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
List *args, bool agg_star, bool agg_distinct,
|
||||
int precedence)
|
||||
List *args, bool agg_star, bool agg_distinct)
|
||||
{
|
||||
HeapTuple aggtuple;
|
||||
Form_pg_aggregate aggform;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.84 2002/03/12 00:51:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.85 2002/03/21 16:00:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -285,7 +285,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
|
||||
* transformJoinOnClause() does. Just invoke transformExpr() to fix
|
||||
* up the operators, and we're done.
|
||||
*/
|
||||
result = transformExpr(pstate, result, EXPR_COLUMN_FIRST);
|
||||
result = transformExpr(pstate, result);
|
||||
|
||||
/*
|
||||
* We expect the result to yield bool directly, otherwise complain. We
|
||||
@@ -326,7 +326,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
pstate->p_namespace = makeList2(j->larg, j->rarg);
|
||||
|
||||
/* This part is just like transformWhereClause() */
|
||||
result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
|
||||
result = transformExpr(pstate, j->quals);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &result))
|
||||
elog(ERROR, "JOIN/ON clause must return type boolean, not type %s",
|
||||
@@ -350,7 +350,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
if (!intMember(varno, containedRels))
|
||||
{
|
||||
elog(ERROR, "JOIN/ON clause refers to \"%s\", which is not part of JOIN",
|
||||
rt_fetch(varno, pstate->p_rtable)->eref->relname);
|
||||
rt_fetch(varno, pstate->p_rtable)->eref->aliasname);
|
||||
}
|
||||
}
|
||||
freeList(clause_varnos);
|
||||
@@ -375,7 +375,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||
* automatically generate the range variable if not specified. However
|
||||
* there are times we need to know whether the entries are legitimate.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, relname, r->name,
|
||||
rte = addRangeTableEntry(pstate, relname, r->alias,
|
||||
interpretInhOption(r->inhOpt), true);
|
||||
|
||||
/*
|
||||
@@ -408,7 +408,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
* relax this, we'd have to be prepared to gin up a unique alias for
|
||||
* an unlabeled subselect.
|
||||
*/
|
||||
if (r->name == NULL)
|
||||
if (r->alias == NULL)
|
||||
elog(ERROR, "sub-select in FROM must have an alias");
|
||||
|
||||
/*
|
||||
@@ -444,7 +444,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
/*
|
||||
* OK, build an RTE for the subquery.
|
||||
*/
|
||||
rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
|
||||
rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true);
|
||||
|
||||
/*
|
||||
* We create a RangeTblRef, but we do not add it to the joinlist or
|
||||
@@ -748,11 +748,11 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
*/
|
||||
if (j->alias)
|
||||
{
|
||||
if (j->alias->attrs != NIL)
|
||||
if (j->alias->colnames != NIL)
|
||||
{
|
||||
if (length(j->alias->attrs) > length(res_colnames))
|
||||
if (length(j->alias->colnames) > length(res_colnames))
|
||||
elog(ERROR, "Column alias list for \"%s\" has too many entries",
|
||||
j->alias->relname);
|
||||
j->alias->aliasname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,7 +791,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
|
||||
if (clause == NULL)
|
||||
return NULL;
|
||||
|
||||
qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
|
||||
qual = transformExpr(pstate, clause);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &qual))
|
||||
elog(ERROR, "WHERE clause must return type boolean, not type %s",
|
||||
@@ -858,9 +858,11 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
|
||||
* an expression.
|
||||
*----------
|
||||
*/
|
||||
if (IsA(node, Ident) &&((Ident *) node)->indirection == NIL)
|
||||
if (IsA(node, ColumnRef) &&
|
||||
length(((ColumnRef *) node)->fields) == 1 &&
|
||||
((ColumnRef *) node)->indirection == NIL)
|
||||
{
|
||||
char *name = ((Ident *) node)->name;
|
||||
char *name = strVal(lfirst(((ColumnRef *) node)->fields));
|
||||
|
||||
if (clause == GROUP_CLAUSE)
|
||||
{
|
||||
@@ -934,7 +936,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
|
||||
* willing to match a resjunk target here, though the above cases must
|
||||
* ignore resjunk targets.
|
||||
*/
|
||||
expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
|
||||
expr = transformExpr(pstate, node);
|
||||
|
||||
foreach(tl, tlist)
|
||||
{
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.110 2002/03/20 19:44:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.111 2002/03/21 16:01:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/params.h"
|
||||
#include "parser/analyze.h"
|
||||
@@ -41,8 +42,7 @@ bool Transform_null_equals = false;
|
||||
static Node *parser_typecast_constant(Value *expr, TypeName *typename);
|
||||
static Node *parser_typecast_expression(ParseState *pstate,
|
||||
Node *expr, TypeName *typename);
|
||||
static Node *transformAttr(ParseState *pstate, Attr *att, int precedence);
|
||||
static Node *transformIdent(ParseState *pstate, Ident *ident, int precedence);
|
||||
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
|
||||
static Node *transformIndirection(ParseState *pstate, Node *basenode,
|
||||
List *indirection);
|
||||
|
||||
@@ -85,7 +85,7 @@ parse_expr_init(void)
|
||||
* input and output of transformExpr; see SubLink for example.
|
||||
*/
|
||||
Node *
|
||||
transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
transformExpr(ParseState *pstate, Node *expr)
|
||||
{
|
||||
Node *result = NULL;
|
||||
|
||||
@@ -105,9 +105,37 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
|
||||
switch (nodeTag(expr))
|
||||
{
|
||||
case T_Attr:
|
||||
case T_ColumnRef:
|
||||
{
|
||||
result = transformAttr(pstate, (Attr *) expr, precedence);
|
||||
result = transformColumnRef(pstate, (ColumnRef *) expr);
|
||||
break;
|
||||
}
|
||||
case T_ParamRef:
|
||||
{
|
||||
ParamRef *pref = (ParamRef *) expr;
|
||||
int paramno = pref->number;
|
||||
Oid paramtyp = param_type(paramno);
|
||||
Param *param;
|
||||
List *fields;
|
||||
|
||||
if (!OidIsValid(paramtyp))
|
||||
elog(ERROR, "Parameter '$%d' is out of range", paramno);
|
||||
param = makeNode(Param);
|
||||
param->paramkind = PARAM_NUM;
|
||||
param->paramid = (AttrNumber) paramno;
|
||||
param->paramname = "<unnamed>";
|
||||
param->paramtype = paramtyp;
|
||||
result = (Node *) param;
|
||||
/* handle qualification, if any */
|
||||
foreach(fields, pref->fields)
|
||||
{
|
||||
result = ParseFuncOrColumn(pstate, strVal(lfirst(fields)),
|
||||
makeList1(result),
|
||||
false, false, true);
|
||||
}
|
||||
/* handle subscripts, if any */
|
||||
result = transformIndirection(pstate, result,
|
||||
pref->indirection);
|
||||
break;
|
||||
}
|
||||
case T_A_Const:
|
||||
@@ -121,31 +149,28 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
result = (Node *) make_const(val);
|
||||
break;
|
||||
}
|
||||
case T_ParamNo:
|
||||
case T_ExprFieldSelect:
|
||||
{
|
||||
ParamNo *pno = (ParamNo *) expr;
|
||||
int paramno = pno->number;
|
||||
Oid toid = param_type(paramno);
|
||||
Param *param = makeNode(Param);
|
||||
ExprFieldSelect *efs = (ExprFieldSelect *) expr;
|
||||
List *fields;
|
||||
|
||||
if (!OidIsValid(toid))
|
||||
elog(ERROR, "Parameter '$%d' is out of range", paramno);
|
||||
param->paramkind = PARAM_NUM;
|
||||
param->paramid = (AttrNumber) paramno;
|
||||
param->paramname = "<unnamed>";
|
||||
param->paramtype = toid;
|
||||
result = transformIndirection(pstate, (Node *) param,
|
||||
pno->indirection);
|
||||
/* cope with typecast applied to param */
|
||||
if (pno->typename != NULL)
|
||||
result = parser_typecast_expression(pstate, result,
|
||||
pno->typename);
|
||||
result = transformExpr(pstate, efs->arg);
|
||||
/* handle qualification, if any */
|
||||
foreach(fields, efs->fields)
|
||||
{
|
||||
result = ParseFuncOrColumn(pstate, strVal(lfirst(fields)),
|
||||
makeList1(result),
|
||||
false, false, true);
|
||||
}
|
||||
/* handle subscripts, if any */
|
||||
result = transformIndirection(pstate, result,
|
||||
efs->indirection);
|
||||
break;
|
||||
}
|
||||
case T_TypeCast:
|
||||
{
|
||||
TypeCast *tc = (TypeCast *) expr;
|
||||
Node *arg = transformExpr(pstate, tc->arg, precedence);
|
||||
Node *arg = transformExpr(pstate, tc->arg);
|
||||
|
||||
result = parser_typecast_expression(pstate, arg, tc->typename);
|
||||
break;
|
||||
@@ -179,17 +204,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
n->arg = a->lexpr;
|
||||
|
||||
result = transformExpr(pstate,
|
||||
(Node *) n,
|
||||
precedence);
|
||||
(Node *) n);
|
||||
}
|
||||
else
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
a->lexpr);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
a->rexpr);
|
||||
|
||||
result = (Node *) make_op(a->opname,
|
||||
lexpr,
|
||||
@@ -200,11 +222,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
case AND:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
a->lexpr);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &lexpr))
|
||||
@@ -226,11 +246,9 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
case OR:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
a->lexpr);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &lexpr))
|
||||
@@ -252,8 +270,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
case NOT:
|
||||
{
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &rexpr))
|
||||
@@ -270,11 +287,6 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case T_Ident:
|
||||
{
|
||||
result = transformIdent(pstate, (Ident *) expr, precedence);
|
||||
break;
|
||||
}
|
||||
case T_FuncCall:
|
||||
{
|
||||
FuncCall *fn = (FuncCall *) expr;
|
||||
@@ -283,14 +295,13 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
/* transform the list of arguments */
|
||||
foreach(args, fn->args)
|
||||
lfirst(args) = transformExpr(pstate,
|
||||
(Node *) lfirst(args),
|
||||
precedence);
|
||||
(Node *) lfirst(args));
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
fn->funcname,
|
||||
fn->args,
|
||||
fn->agg_star,
|
||||
fn->agg_distinct,
|
||||
precedence);
|
||||
false);
|
||||
break;
|
||||
}
|
||||
case T_SubLink:
|
||||
@@ -357,8 +368,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
List *elist;
|
||||
|
||||
foreach(elist, left_list)
|
||||
lfirst(elist) = transformExpr(pstate, lfirst(elist),
|
||||
precedence);
|
||||
lfirst(elist) = transformExpr(pstate, lfirst(elist));
|
||||
|
||||
Assert(IsA(sublink->oper, A_Expr));
|
||||
op = ((A_Expr *) sublink->oper)->opname;
|
||||
@@ -455,7 +465,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
a->rexpr = warg;
|
||||
warg = (Node *) a;
|
||||
}
|
||||
neww->expr = transformExpr(pstate, warg, precedence);
|
||||
neww->expr = transformExpr(pstate, warg);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &neww->expr))
|
||||
elog(ERROR, "WHEN clause must have a boolean result");
|
||||
@@ -472,7 +482,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
n->val.type = T_Null;
|
||||
warg = (Node *) n;
|
||||
}
|
||||
neww->result = transformExpr(pstate, warg, precedence);
|
||||
neww->result = transformExpr(pstate, warg);
|
||||
|
||||
newargs = lappend(newargs, neww);
|
||||
typeids = lappendi(typeids, exprType(neww->result));
|
||||
@@ -496,7 +506,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
n->val.type = T_Null;
|
||||
defresult = (Node *) n;
|
||||
}
|
||||
newc->defresult = transformExpr(pstate, defresult, precedence);
|
||||
newc->defresult = transformExpr(pstate, defresult);
|
||||
|
||||
/*
|
||||
* Note: default result is considered the most significant
|
||||
@@ -534,7 +544,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
{
|
||||
NullTest *n = (NullTest *) expr;
|
||||
|
||||
n->arg = transformExpr(pstate, n->arg, precedence);
|
||||
n->arg = transformExpr(pstate, n->arg);
|
||||
/* the argument can be any type, so don't coerce it */
|
||||
result = expr;
|
||||
break;
|
||||
@@ -544,7 +554,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
{
|
||||
BooleanTest *b = (BooleanTest *) expr;
|
||||
|
||||
b->arg = transformExpr(pstate, b->arg, precedence);
|
||||
b->arg = transformExpr(pstate, b->arg);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &b->arg))
|
||||
{
|
||||
@@ -627,47 +637,183 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
||||
}
|
||||
|
||||
static Node *
|
||||
transformAttr(ParseState *pstate, Attr *att, int precedence)
|
||||
transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
{
|
||||
Node *basenode;
|
||||
int numnames = length(cref->fields);
|
||||
Node *node;
|
||||
RangeVar *rv;
|
||||
|
||||
basenode = ParseNestedFuncOrColumn(pstate, att, precedence);
|
||||
return transformIndirection(pstate, basenode, att->indirection);
|
||||
}
|
||||
|
||||
static Node *
|
||||
transformIdent(ParseState *pstate, Ident *ident, int precedence)
|
||||
{
|
||||
Node *result = NULL;
|
||||
int sublevels_up;
|
||||
|
||||
/*
|
||||
* try to find the ident as a relation ... but not if subscripts
|
||||
* appear
|
||||
/*----------
|
||||
* The allowed syntaxes are:
|
||||
*
|
||||
* A First try to resolve as unqualified column name;
|
||||
* if no luck, try to resolve as unqual. table name (A.*).
|
||||
* A.B A is an unqual. table name; B is either a
|
||||
* column or function name (trying column name first).
|
||||
* A.B.C schema A, table B, col or func name C.
|
||||
* A.B.C.D catalog A, schema B, table C, col or func D.
|
||||
* A.* A is an unqual. table name; means whole-row value.
|
||||
* A.B.* whole-row value of table B in schema A.
|
||||
* A.B.C.* whole-row value of table C in schema B in catalog A.
|
||||
*
|
||||
* We do not need to cope with bare "*"; that will only be accepted by
|
||||
* the grammar at the top level of a SELECT list, and transformTargetList
|
||||
* will take care of it before it ever gets here.
|
||||
*
|
||||
* Currently, if a catalog name is given then it must equal the current
|
||||
* database name; we check it here and then discard it.
|
||||
*
|
||||
* For whole-row references, the result is an untransformed RangeVar,
|
||||
* which will work as the argument to a function call, but not in any
|
||||
* other context at present. (We could instead coerce to a whole-row Var,
|
||||
* but that will fail for subselect and join RTEs, because there is no
|
||||
* pg_type entry for their rowtypes.)
|
||||
*----------
|
||||
*/
|
||||
if (ident->indirection == NIL &&
|
||||
refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL)
|
||||
switch (numnames)
|
||||
{
|
||||
ident->isRel = TRUE;
|
||||
result = (Node *) ident;
|
||||
}
|
||||
|
||||
if (result == NULL || precedence == EXPR_COLUMN_FIRST)
|
||||
{
|
||||
/* try to find the ident as a column */
|
||||
Node *var = colnameToVar(pstate, ident->name);
|
||||
|
||||
if (var != NULL)
|
||||
case 1:
|
||||
{
|
||||
ident->isRel = FALSE;
|
||||
result = transformIndirection(pstate, var, ident->indirection);
|
||||
char *name = strVal(lfirst(cref->fields));
|
||||
|
||||
/* Try to identify as an unqualified column */
|
||||
node = colnameToVar(pstate, name);
|
||||
if (node == NULL)
|
||||
{
|
||||
/*
|
||||
* Not known as a column of any range-table entry, so
|
||||
* try to find the name as a relation ... but not if
|
||||
* subscripts appear. Note also that only relations
|
||||
* already entered into the rangetable will be recognized.
|
||||
*/
|
||||
int levels_up;
|
||||
|
||||
if (cref->indirection == NIL &&
|
||||
refnameRangeTblEntry(pstate, name, &levels_up) != NULL)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Attribute \"%s\" not found", name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
char *name1 = strVal(lfirst(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name2, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name1;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to identify as a once-qualified column */
|
||||
node = qualifiedNameToVar(pstate, name1, name2, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/*
|
||||
* Not known as a column of any range-table entry, so
|
||||
* try it as a function call. Here, we will create an
|
||||
* implicit RTE for tables not already entered.
|
||||
*/
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name1;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = ParseFuncOrColumn(pstate, name2,
|
||||
makeList1(rv),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
char *name1 = strVal(lfirst(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
char *name3 = strVal(lfirst(lnext(lnext(cref->fields))));
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name3, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name1;
|
||||
rv->relname = name2;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
/* XXX do something with schema name here */
|
||||
node = qualifiedNameToVar(pstate, name2, name3, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name1;
|
||||
rv->relname = name2;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = ParseFuncOrColumn(pstate, name3,
|
||||
makeList1(rv),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
char *name1 = strVal(lfirst(cref->fields));
|
||||
char *name2 = strVal(lsecond(cref->fields));
|
||||
char *name3 = strVal(lfirst(lnext(lnext(cref->fields))));
|
||||
char *name4 = strVal(lfirst(lnext(lnext(lnext(cref->fields)))));
|
||||
|
||||
/*
|
||||
* We check the catalog name and then ignore it.
|
||||
*/
|
||||
if (strcmp(name1, DatabaseName) != 0)
|
||||
elog(ERROR, "Cross-database references are not implemented");
|
||||
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name4, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name2;
|
||||
rv->relname = name3;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
/* XXX do something with schema name here */
|
||||
node = qualifiedNameToVar(pstate, name3, name4, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name2;
|
||||
rv->relname = name3;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = ParseFuncOrColumn(pstate, name4,
|
||||
makeList1(rv),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "Invalid qualified name syntax (too many names)");
|
||||
node = NULL; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
elog(ERROR, "Attribute '%s' not found", ident->name);
|
||||
|
||||
return result;
|
||||
return transformIndirection(pstate, node, cref->indirection);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -748,10 +894,6 @@ exprType(Node *expr)
|
||||
case T_BooleanTest:
|
||||
type = BOOLOID;
|
||||
break;
|
||||
case T_Ident:
|
||||
/* XXX is this right? */
|
||||
type = UNKNOWNOID;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Do not know how to get type for %d node",
|
||||
nodeTag(expr));
|
||||
|
@@ -8,11 +8,10 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.118 2002/03/20 19:44:29 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.119 2002/03/21 16:01:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
@@ -36,8 +35,7 @@
|
||||
|
||||
static Node *ParseComplexProjection(ParseState *pstate,
|
||||
char *funcname,
|
||||
Node *first_arg,
|
||||
bool *attisset);
|
||||
Node *first_arg);
|
||||
static Oid **argtype_inherit(int nargs, Oid *argtypes);
|
||||
|
||||
static int find_inheritors(Oid relid, Oid **supervec);
|
||||
@@ -60,75 +58,31 @@ static Oid agg_select_candidate(Oid typeid, CandidateList candidates);
|
||||
|
||||
|
||||
/*
|
||||
** ParseNestedFuncOrColumn
|
||||
** Given a nested dot expression (i.e. (relation func ... attr), build up
|
||||
** a tree with of Iter and Func nodes.
|
||||
*/
|
||||
Node *
|
||||
ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
|
||||
{
|
||||
List *mutator_iter;
|
||||
Node *retval = NULL;
|
||||
|
||||
if (attr->paramNo != NULL)
|
||||
{
|
||||
Param *param = (Param *) transformExpr(pstate,
|
||||
(Node *) attr->paramNo,
|
||||
EXPR_RELATION_FIRST);
|
||||
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
makeList1(param),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ident *ident = makeNode(Ident);
|
||||
|
||||
ident->name = attr->relname;
|
||||
ident->isRel = TRUE;
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
makeList1(ident),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
|
||||
/* Do more attributes follow this one? */
|
||||
foreach(mutator_iter, lnext(attr->attrs))
|
||||
{
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
|
||||
makeList1(retval),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse function
|
||||
* Parse a function call
|
||||
*
|
||||
* This code is confusing because the database can accept
|
||||
* relation.column, column.function, or relation.column.function.
|
||||
* In these cases, funcname is the last parameter, and fargs are
|
||||
* the rest.
|
||||
* For historical reasons, Postgres tries to treat the notations tab.col
|
||||
* and col(tab) as equivalent: if a single-argument function call has an
|
||||
* argument of complex type and the function name matches any attribute
|
||||
* of the type, we take it as a column projection.
|
||||
*
|
||||
* It can also be called as func(col) or func(col,col).
|
||||
* In this case, Funcname is the part before parens, and fargs
|
||||
* are the part in parens.
|
||||
*
|
||||
* FYI, projection is choosing column from a table.
|
||||
* Hence, both cases come through here. The is_column parameter tells us
|
||||
* which syntactic construct is actually being dealt with, but this is
|
||||
* intended to be used only to deliver an appropriate error message,
|
||||
* not to affect the semantics. When is_column is true, we should have
|
||||
* a single argument (the putative table), function name equal to the
|
||||
* column name, and no aggregate decoration.
|
||||
*
|
||||
* In the function-call case, the argument expressions have been transformed
|
||||
* already. In the column case, we may get either a transformed expression
|
||||
* or a RangeVar node as argument.
|
||||
*/
|
||||
Node *
|
||||
ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
bool agg_star, bool agg_distinct,
|
||||
int precedence)
|
||||
bool agg_star, bool agg_distinct, bool is_column)
|
||||
{
|
||||
Oid rettype = InvalidOid;
|
||||
Oid argrelid = InvalidOid;
|
||||
Oid funcid = InvalidOid;
|
||||
List *i = NIL;
|
||||
Oid rettype;
|
||||
Oid funcid;
|
||||
List *i;
|
||||
Node *first_arg = NULL;
|
||||
char *refname;
|
||||
int nargs = length(fargs);
|
||||
@@ -140,9 +94,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
bool retset;
|
||||
bool must_be_agg = agg_star || agg_distinct;
|
||||
bool could_be_agg;
|
||||
bool attisset = false;
|
||||
Oid toid = InvalidOid;
|
||||
Expr *expr;
|
||||
FuncDetailCode fdresult;
|
||||
|
||||
/*
|
||||
* Most of the rest of the parser just assumes that functions do not
|
||||
@@ -157,33 +110,26 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
if (fargs)
|
||||
{
|
||||
first_arg = lfirst(fargs);
|
||||
if (first_arg == NULL)
|
||||
if (first_arg == NULL) /* should not happen */
|
||||
elog(ERROR, "Function '%s' does not allow NULL input", funcname);
|
||||
}
|
||||
|
||||
/*
|
||||
* test for relation.column
|
||||
*
|
||||
* check for projection methods: if function takes one argument, and that
|
||||
* argument is a relation, param, or PQ function returning a complex *
|
||||
* type, then the function could be a projection.
|
||||
* check for column projection: if function has one argument, and that
|
||||
* argument is of complex type, then the function could be a projection.
|
||||
*/
|
||||
/* We only have one parameter, and it's not got aggregate decoration */
|
||||
if (nargs == 1 && !must_be_agg)
|
||||
{
|
||||
/* Is it a plain Relation name from the parser? */
|
||||
if (IsA(first_arg, Ident) &&((Ident *) first_arg)->isRel)
|
||||
/* Is it a not-yet-transformed RangeVar node? */
|
||||
if (IsA(first_arg, RangeVar))
|
||||
{
|
||||
Ident *ident = (Ident *) first_arg;
|
||||
|
||||
/* First arg is a relation. This could be a projection. */
|
||||
refname = ident->name;
|
||||
refname = ((RangeVar *) first_arg)->relname;
|
||||
|
||||
retval = qualifiedNameToVar(pstate, refname, funcname, true);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* else drop through - attr is a set or function */
|
||||
}
|
||||
else if (ISCOMPLEX(exprType(first_arg)))
|
||||
{
|
||||
@@ -194,24 +140,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
*/
|
||||
retval = ParseComplexProjection(pstate,
|
||||
funcname,
|
||||
first_arg,
|
||||
&attisset);
|
||||
if (attisset)
|
||||
{
|
||||
toid = exprType(first_arg);
|
||||
argrelid = typeidTypeRelid(toid);
|
||||
if (argrelid == InvalidOid)
|
||||
elog(ERROR, "Type '%s' is not a relation type",
|
||||
typeidTypeName(toid));
|
||||
|
||||
/*
|
||||
* A projection must match an attribute name of the rel.
|
||||
*/
|
||||
if (get_attnum(argrelid, funcname) == InvalidAttrNumber)
|
||||
elog(ERROR, "No such attribute or function '%s'",
|
||||
funcname);
|
||||
}
|
||||
|
||||
first_arg);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
@@ -226,15 +155,14 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
if (nargs != 1)
|
||||
elog(ERROR, "Aggregate functions may only have one parameter");
|
||||
/* Agg's argument can't be a relation name, either */
|
||||
if (IsA(first_arg, Ident) &&((Ident *) first_arg)->isRel)
|
||||
if (IsA(first_arg, RangeVar))
|
||||
elog(ERROR, "Aggregate functions cannot be applied to relation names");
|
||||
could_be_agg = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try to parse as an aggregate if above-mentioned checks are OK */
|
||||
could_be_agg = (nargs == 1) &&
|
||||
!(IsA(first_arg, Ident) &&((Ident *) first_arg)->isRel);
|
||||
could_be_agg = (nargs == 1) && !(IsA(first_arg, RangeVar));
|
||||
}
|
||||
|
||||
if (could_be_agg)
|
||||
@@ -249,8 +177,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0))
|
||||
return (Node *) ParseAgg(pstate, funcname, basetype,
|
||||
fargs, agg_star, agg_distinct,
|
||||
precedence);
|
||||
fargs, agg_star, agg_distinct);
|
||||
|
||||
/* check for aggregate-that-accepts-any-type (eg, COUNT) */
|
||||
if (SearchSysCacheExists(AGGNAME,
|
||||
@@ -258,8 +185,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
ObjectIdGetDatum(0),
|
||||
0, 0))
|
||||
return (Node *) ParseAgg(pstate, funcname, 0,
|
||||
fargs, agg_star, agg_distinct,
|
||||
precedence);
|
||||
fargs, agg_star, agg_distinct);
|
||||
|
||||
/*
|
||||
* No exact match yet, so see if there is another entry in the
|
||||
@@ -277,8 +203,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
basetype, type, -1);
|
||||
basetype = type;
|
||||
return (Node *) ParseAgg(pstate, funcname, basetype,
|
||||
fargs, agg_star, agg_distinct,
|
||||
precedence);
|
||||
fargs, agg_star, agg_distinct);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -300,10 +225,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
}
|
||||
|
||||
/*
|
||||
* If we dropped through to here it's really a function (or a set,
|
||||
* which is implemented as a function). Extract arg type info and
|
||||
* transform relation name arguments into varnodes of the appropriate
|
||||
* form.
|
||||
* Okay, it's not a column projection, so it must really be a function.
|
||||
* Extract arg type info and transform RangeVar arguments into varnodes
|
||||
* of the appropriate form.
|
||||
*/
|
||||
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
|
||||
@@ -311,8 +235,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
foreach(i, fargs)
|
||||
{
|
||||
Node *arg = lfirst(i);
|
||||
Oid toid;
|
||||
|
||||
if (IsA(arg, Ident) &&((Ident *) arg)->isRel)
|
||||
if (IsA(arg, RangeVar))
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
int vnum;
|
||||
@@ -321,7 +246,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
/*
|
||||
* a relation
|
||||
*/
|
||||
refname = ((Ident *) arg)->name;
|
||||
refname = ((RangeVar *) arg)->relname;
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, refname,
|
||||
&sublevels_up);
|
||||
@@ -346,16 +271,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
* RTE is a join or subselect; must fail for lack of a
|
||||
* named tuple type
|
||||
*/
|
||||
if (nargs == 1)
|
||||
{
|
||||
/*
|
||||
* Here, we probably have an unrecognized attribute of
|
||||
* a sub-select; again can't tell if it was x.f or
|
||||
* f(x)
|
||||
*/
|
||||
elog(ERROR, "No such attribute or function %s.%s",
|
||||
if (is_column)
|
||||
elog(ERROR, "No such attribute %s.%s",
|
||||
refname, funcname);
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
|
||||
@@ -365,93 +283,53 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
|
||||
toid = typenameTypeId(rte->relname);
|
||||
|
||||
/* replace it in the arg list */
|
||||
/* replace RangeVar in the arg list */
|
||||
lfirst(i) = makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
sizeof(Pointer),
|
||||
sublevels_up);
|
||||
}
|
||||
else if (!attisset)
|
||||
toid = exprType(arg);
|
||||
else
|
||||
{
|
||||
/* if attisset is true, we already set toid for the single arg */
|
||||
}
|
||||
toid = exprType(arg);
|
||||
|
||||
oid_array[argn++] = toid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is it a set, or a function?
|
||||
* func_get_detail looks up the function in the catalogs, does
|
||||
* disambiguation for polymorphic functions, handles inheritance,
|
||||
* and returns the funcid and type and set or singleton status of
|
||||
* the function's return value. it also returns the true argument
|
||||
* types to the function.
|
||||
*/
|
||||
if (attisset)
|
||||
{ /* we know all of these fields already */
|
||||
|
||||
/*
|
||||
* We create a funcnode with a placeholder function seteval(). At
|
||||
* runtime, seteval() will execute the function identified by the
|
||||
* funcid it receives as parameter.
|
||||
*
|
||||
* Example: retrieve (emp.mgr.name). The plan for this will scan the
|
||||
* emp relation, projecting out the mgr attribute, which is a
|
||||
* funcid. This function is then called (via seteval()) and "name"
|
||||
* is projected from its result.
|
||||
*/
|
||||
funcid = F_SETEVAL;
|
||||
rettype = toid;
|
||||
retset = true;
|
||||
true_oid_array = oid_array;
|
||||
}
|
||||
else
|
||||
fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
|
||||
&funcid, &rettype, &retset,
|
||||
&true_oid_array);
|
||||
if (fdresult == FUNCDETAIL_COERCION)
|
||||
{
|
||||
FuncDetailCode fdresult;
|
||||
|
||||
/*
|
||||
* func_get_detail looks up the function in the catalogs, does
|
||||
* disambiguation for polymorphic functions, handles inheritance,
|
||||
* and returns the funcid and type and set or singleton status of
|
||||
* the function's return value. it also returns the true argument
|
||||
* types to the function.
|
||||
* We can do it as a trivial coercion. coerce_type can handle
|
||||
* these cases, so why duplicate code...
|
||||
*/
|
||||
fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
|
||||
&funcid, &rettype, &retset,
|
||||
&true_oid_array);
|
||||
if (fdresult == FUNCDETAIL_COERCION)
|
||||
{
|
||||
/*
|
||||
* We can do it as a trivial coercion. coerce_type can handle
|
||||
* these cases, so why duplicate code...
|
||||
*/
|
||||
return coerce_type(pstate, lfirst(fargs),
|
||||
oid_array[0], rettype, -1);
|
||||
}
|
||||
if (fdresult != FUNCDETAIL_NORMAL)
|
||||
{
|
||||
/*
|
||||
* Oops. Time to die.
|
||||
*
|
||||
* If there is a single argument of complex type, we might be
|
||||
* dealing with the PostQuel notation rel.function instead of
|
||||
* the more usual function(rel). Give a nonspecific error
|
||||
* message that will cover both cases.
|
||||
*/
|
||||
if (nargs == 1)
|
||||
{
|
||||
Type tp = typeidType(oid_array[0]);
|
||||
|
||||
if (typeTypeFlag(tp) == 'c')
|
||||
elog(ERROR, "No such attribute or function '%s'",
|
||||
funcname);
|
||||
ReleaseSysCache(tp);
|
||||
}
|
||||
|
||||
/* Else generate a detailed complaint */
|
||||
func_error(NULL, funcname, nargs, oid_array,
|
||||
"Unable to identify a function that satisfies the "
|
||||
"given argument types"
|
||||
"\n\tYou may need to add explicit typecasts");
|
||||
}
|
||||
return coerce_type(pstate, lfirst(fargs),
|
||||
oid_array[0], rettype, -1);
|
||||
}
|
||||
if (fdresult != FUNCDETAIL_NORMAL)
|
||||
{
|
||||
/*
|
||||
* Oops. Time to die.
|
||||
*
|
||||
* If we are dealing with the attribute notation rel.function,
|
||||
* give an error message that is appropriate for that case.
|
||||
*/
|
||||
if (is_column)
|
||||
elog(ERROR, "Attribute \"%s\" not found", funcname);
|
||||
/* Else generate a detailed complaint */
|
||||
func_error(NULL, funcname, nargs, oid_array,
|
||||
"Unable to identify a function that satisfies the "
|
||||
"given argument types"
|
||||
"\n\tYou may need to add explicit typecasts");
|
||||
}
|
||||
|
||||
/* got it */
|
||||
@@ -470,26 +348,12 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
expr->args = fargs;
|
||||
retval = (Node *) expr;
|
||||
|
||||
/*
|
||||
* For sets, we want to project out the desired attribute of the
|
||||
* tuples.
|
||||
*/
|
||||
if (attisset)
|
||||
{
|
||||
FieldSelect *fselect;
|
||||
|
||||
fselect = setup_field_select(retval, funcname, argrelid);
|
||||
rettype = fselect->resulttype;
|
||||
retval = (Node *) fselect;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the function returns a set of values, then we need to iterate
|
||||
* over all the returned values in the executor, so we stick an iter
|
||||
* node here. if it returns a singleton, then we don't need the iter
|
||||
* node.
|
||||
*/
|
||||
|
||||
if (retset)
|
||||
{
|
||||
Iter *iter = makeNode(Iter);
|
||||
@@ -1497,10 +1361,10 @@ make_arguments(ParseState *pstate,
|
||||
}
|
||||
|
||||
/*
|
||||
** setup_field_select
|
||||
** Build a FieldSelect node that says which attribute to project to.
|
||||
** This routine is called by ParseFuncOrColumn() when we have found
|
||||
** a projection on a function result or parameter.
|
||||
* setup_field_select
|
||||
* Build a FieldSelect node that says which attribute to project to.
|
||||
* This routine is called by ParseFuncOrColumn() when we have found
|
||||
* a projection on a function result or parameter.
|
||||
*/
|
||||
static FieldSelect *
|
||||
setup_field_select(Node *input, char *attname, Oid relid)
|
||||
@@ -1521,18 +1385,31 @@ setup_field_select(Node *input, char *attname, Oid relid)
|
||||
/*
|
||||
* ParseComplexProjection -
|
||||
* handles function calls with a single argument that is of complex type.
|
||||
* This routine returns NULL if it can't handle the projection (eg. sets).
|
||||
* If the function call is actually a column projection, return a suitably
|
||||
* transformed expression tree. If not, return NULL.
|
||||
*
|
||||
* NB: argument is expected to be transformed already, ie, not a RangeVar.
|
||||
*/
|
||||
static Node *
|
||||
ParseComplexProjection(ParseState *pstate,
|
||||
char *funcname,
|
||||
Node *first_arg,
|
||||
bool *attisset)
|
||||
Node *first_arg)
|
||||
{
|
||||
Oid argtype;
|
||||
Oid argtype = exprType(first_arg);
|
||||
Oid argrelid;
|
||||
AttrNumber attnum;
|
||||
FieldSelect *fselect;
|
||||
|
||||
argrelid = typeidTypeRelid(argtype);
|
||||
if (!argrelid)
|
||||
return NULL; /* probably should not happen */
|
||||
attnum = get_attnum(argrelid, funcname);
|
||||
if (attnum == InvalidAttrNumber)
|
||||
return NULL; /* funcname does not match any column */
|
||||
|
||||
/*
|
||||
* Check for special cases where we don't want to return a FieldSelect.
|
||||
*/
|
||||
switch (nodeTag(first_arg))
|
||||
{
|
||||
case T_Iter:
|
||||
@@ -1540,75 +1417,42 @@ ParseComplexProjection(ParseState *pstate,
|
||||
Iter *iter = (Iter *) first_arg;
|
||||
|
||||
/*
|
||||
* If the argument of the Iter returns a tuple, funcname
|
||||
* may be a projection. If so, we stick the FieldSelect
|
||||
* If it's an Iter, we stick the FieldSelect
|
||||
* *inside* the Iter --- this is klugy, but necessary
|
||||
* because ExecTargetList() currently does the right thing
|
||||
* only when the Iter node is at the top level of a
|
||||
* targetlist item.
|
||||
*
|
||||
* XXX Iter should go away altogether...
|
||||
*/
|
||||
argtype = iter->itertype;
|
||||
argrelid = typeidTypeRelid(argtype);
|
||||
if (argrelid &&
|
||||
get_attnum(argrelid, funcname) != InvalidAttrNumber)
|
||||
{
|
||||
fselect = setup_field_select(iter->iterexpr,
|
||||
funcname, argrelid);
|
||||
iter->iterexpr = (Node *) fselect;
|
||||
iter->itertype = fselect->resulttype;
|
||||
return (Node *) iter;
|
||||
}
|
||||
fselect = setup_field_select(iter->iterexpr,
|
||||
funcname, argrelid);
|
||||
iter->iterexpr = (Node *) fselect;
|
||||
iter->itertype = fselect->resulttype;
|
||||
return (Node *) iter;
|
||||
break;
|
||||
}
|
||||
case T_Var:
|
||||
{
|
||||
/*
|
||||
* The argument is a set, so this is either a projection
|
||||
* or a function call on this set.
|
||||
*/
|
||||
*attisset = true;
|
||||
break;
|
||||
}
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *) first_arg;
|
||||
Func *funcnode;
|
||||
|
||||
if (expr->opType != FUNC_EXPR)
|
||||
break;
|
||||
Var *var = (Var *) first_arg;
|
||||
|
||||
/*
|
||||
* If the argument is a function returning a tuple,
|
||||
* funcname may be a projection
|
||||
* If the Var is a whole-row tuple, we can just replace it
|
||||
* with a simple Var reference.
|
||||
*/
|
||||
funcnode = (Func *) expr->oper;
|
||||
argtype = funcnode->functype;
|
||||
argrelid = typeidTypeRelid(argtype);
|
||||
if (argrelid &&
|
||||
get_attnum(argrelid, funcname) != InvalidAttrNumber)
|
||||
if (var->varattno == InvalidAttrNumber)
|
||||
{
|
||||
fselect = setup_field_select((Node *) expr,
|
||||
funcname, argrelid);
|
||||
return (Node *) fselect;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case T_Param:
|
||||
{
|
||||
Param *param = (Param *) first_arg;
|
||||
Oid vartype;
|
||||
int32 vartypmod;
|
||||
|
||||
/*
|
||||
* If the Param is a complex type, this could be a
|
||||
* projection
|
||||
*/
|
||||
argtype = param->paramtype;
|
||||
argrelid = typeidTypeRelid(argtype);
|
||||
if (argrelid &&
|
||||
get_attnum(argrelid, funcname) != InvalidAttrNumber)
|
||||
{
|
||||
fselect = setup_field_select((Node *) param,
|
||||
funcname, argrelid);
|
||||
return (Node *) fselect;
|
||||
get_atttypetypmod(argrelid, attnum,
|
||||
&vartype, &vartypmod);
|
||||
|
||||
return (Node *) makeVar(var->varno,
|
||||
attnum,
|
||||
vartype,
|
||||
vartypmod,
|
||||
var->varlevelsup);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1616,7 +1460,9 @@ ParseComplexProjection(ParseState *pstate,
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
/* Else generate a FieldSelect expression */
|
||||
fselect = setup_field_select(first_arg, funcname, argrelid);
|
||||
return (Node *) fselect;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.60 2002/03/21 16:01:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -285,7 +285,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
{
|
||||
if (ai->lidx)
|
||||
{
|
||||
subexpr = transformExpr(pstate, ai->lidx, EXPR_COLUMN_FIRST);
|
||||
subexpr = transformExpr(pstate, ai->lidx);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
|
||||
INT4OID, -1);
|
||||
@@ -305,7 +305,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
}
|
||||
lowerIndexpr = lappend(lowerIndexpr, subexpr);
|
||||
}
|
||||
subexpr = transformExpr(pstate, ai->uidx, EXPR_COLUMN_FIRST);
|
||||
subexpr = transformExpr(pstate, ai->uidx);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr),
|
||||
INT4OID, -1);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.64 2002/03/21 16:01:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -104,7 +104,7 @@ scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
int varno = ((RangeTblRef *) nsnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
if (strcmp(rte->eref->relname, refname) == 0)
|
||||
if (strcmp(rte->eref->aliasname, refname) == 0)
|
||||
result = (Node *) rte;
|
||||
}
|
||||
else if (IsA(nsnode, JoinExpr))
|
||||
@@ -113,7 +113,7 @@ scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
|
||||
if (j->alias)
|
||||
{
|
||||
if (strcmp(j->alias->relname, refname) == 0)
|
||||
if (strcmp(j->alias->aliasname, refname) == 0)
|
||||
return (Node *) j; /* matched a join alias */
|
||||
|
||||
/*
|
||||
@@ -175,7 +175,7 @@ checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
int varno = ((RangeTblRef *) namespace1)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname);
|
||||
scanNameSpaceForConflict(pstate, namespace2, rte->eref->aliasname);
|
||||
}
|
||||
else if (IsA(namespace1, JoinExpr))
|
||||
{
|
||||
@@ -183,7 +183,7 @@ checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
|
||||
if (j->alias)
|
||||
{
|
||||
scanNameSpaceForConflict(pstate, namespace2, j->alias->relname);
|
||||
scanNameSpaceForConflict(pstate, namespace2, j->alias->aliasname);
|
||||
|
||||
/*
|
||||
* Tables within an aliased join are invisible from outside
|
||||
@@ -268,7 +268,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
* Scan the user column names (or aliases) for a match. Complain if
|
||||
* multiple matches.
|
||||
*/
|
||||
foreach(c, rte->eref->attrs)
|
||||
foreach(c, rte->eref->colnames)
|
||||
{
|
||||
attnum++;
|
||||
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
||||
@@ -420,15 +420,15 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
RangeTblEntry *
|
||||
addRangeTableEntry(ParseState *pstate,
|
||||
char *relname,
|
||||
Attr *alias,
|
||||
Alias *alias,
|
||||
bool inh,
|
||||
bool inFromCl)
|
||||
{
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
char *refname = alias ? alias->relname : relname;
|
||||
char *refname = alias ? alias->aliasname : relname;
|
||||
LOCKMODE lockmode;
|
||||
Relation rel;
|
||||
Attr *eref;
|
||||
Alias *eref;
|
||||
int maxattrs;
|
||||
int numaliases;
|
||||
int varattno;
|
||||
@@ -447,8 +447,8 @@ addRangeTableEntry(ParseState *pstate,
|
||||
rel = heap_openr(relname, lockmode);
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
|
||||
eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
|
||||
numaliases = length(eref->attrs);
|
||||
eref = alias ? (Alias *) copyObject(alias) : makeAlias(refname, NIL);
|
||||
numaliases = length(eref->colnames);
|
||||
|
||||
/*
|
||||
* Since the rel is open anyway, let's check that the number of column
|
||||
@@ -459,13 +459,13 @@ addRangeTableEntry(ParseState *pstate,
|
||||
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
|
||||
refname, maxattrs, numaliases);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
/* fill in any unspecified alias columns using actual column names */
|
||||
for (varattno = numaliases; varattno < maxattrs; varattno++)
|
||||
{
|
||||
char *attrname;
|
||||
|
||||
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
|
||||
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
||||
eref->colnames = lappend(eref->colnames, makeString(attrname));
|
||||
}
|
||||
rte->eref = eref;
|
||||
|
||||
@@ -512,12 +512,12 @@ addRangeTableEntry(ParseState *pstate,
|
||||
RangeTblEntry *
|
||||
addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
Query *subquery,
|
||||
Attr *alias,
|
||||
Alias *alias,
|
||||
bool inFromCl)
|
||||
{
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
char *refname = alias->relname;
|
||||
Attr *eref;
|
||||
char *refname = alias->aliasname;
|
||||
Alias *eref;
|
||||
int numaliases;
|
||||
int varattno;
|
||||
List *tlistitem;
|
||||
@@ -529,7 +529,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
rte->alias = alias;
|
||||
|
||||
eref = copyObject(alias);
|
||||
numaliases = length(eref->attrs);
|
||||
numaliases = length(eref->colnames);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
varattno = 0;
|
||||
@@ -546,7 +546,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
char *attrname;
|
||||
|
||||
attrname = pstrdup(te->resdom->resname);
|
||||
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
||||
eref->colnames = lappend(eref->colnames, makeString(attrname));
|
||||
}
|
||||
}
|
||||
if (varattno < numaliases)
|
||||
@@ -594,11 +594,11 @@ addRangeTableEntryForJoin(ParseState *pstate,
|
||||
List *coltypmods,
|
||||
List *leftcols,
|
||||
List *rightcols,
|
||||
Attr *alias,
|
||||
Alias *alias,
|
||||
bool inFromCl)
|
||||
{
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
Attr *eref;
|
||||
Alias *eref;
|
||||
int numaliases;
|
||||
|
||||
rte->rtekind = RTE_JOIN;
|
||||
@@ -612,15 +612,15 @@ addRangeTableEntryForJoin(ParseState *pstate,
|
||||
rte->joinrightcols = rightcols;
|
||||
rte->alias = alias;
|
||||
|
||||
eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL);
|
||||
numaliases = length(eref->attrs);
|
||||
eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
|
||||
numaliases = length(eref->colnames);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
if (numaliases < length(colnames))
|
||||
{
|
||||
while (numaliases-- > 0)
|
||||
colnames = lnext(colnames);
|
||||
eref->attrs = nconc(eref->attrs, colnames);
|
||||
eref->colnames = nconc(eref->colnames, colnames);
|
||||
}
|
||||
|
||||
rte->eref = eref;
|
||||
@@ -759,7 +759,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
|
||||
rel = heap_openr(rte->relname, AccessShareLock);
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
numaliases = length(rte->eref->attrs);
|
||||
numaliases = length(rte->eref->colnames);
|
||||
|
||||
for (varattno = 0; varattno < maxattrs; varattno++)
|
||||
{
|
||||
@@ -775,7 +775,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
char *label;
|
||||
|
||||
if (varattno < numaliases)
|
||||
label = strVal(nth(varattno, rte->eref->attrs));
|
||||
label = strVal(nth(varattno, rte->eref->colnames));
|
||||
else
|
||||
label = NameStr(attr->attname);
|
||||
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
||||
@@ -798,7 +798,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
else if (rte->rtekind == RTE_SUBQUERY)
|
||||
{
|
||||
/* Subquery RTE */
|
||||
List *aliasp = rte->eref->attrs;
|
||||
List *aliasp = rte->eref->colnames;
|
||||
List *tlistitem;
|
||||
|
||||
varattno = 0;
|
||||
@@ -836,7 +836,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
else if (rte->rtekind == RTE_JOIN)
|
||||
{
|
||||
/* Join RTE */
|
||||
List *aliasp = rte->eref->attrs;
|
||||
List *aliasp = rte->eref->colnames;
|
||||
List *coltypes = rte->joincoltypes;
|
||||
List *coltypmods = rte->joincoltypmods;
|
||||
|
||||
@@ -936,8 +936,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
|
||||
* If there is an alias, use it. (This path should always be taken
|
||||
* for non-relation RTEs.)
|
||||
*/
|
||||
if (attnum > 0 && attnum <= length(rte->eref->attrs))
|
||||
return strVal(nth(attnum - 1, rte->eref->attrs));
|
||||
if (attnum > 0 && attnum <= length(rte->eref->colnames))
|
||||
return strVal(nth(attnum - 1, rte->eref->colnames));
|
||||
|
||||
/*
|
||||
* Can get here for a system attribute (which never has an alias), or
|
||||
@@ -946,7 +946,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
|
||||
*/
|
||||
if (rte->rtekind != RTE_RELATION)
|
||||
elog(ERROR, "Invalid attnum %d for rangetable entry %s",
|
||||
attnum, rte->eref->relname);
|
||||
attnum, rte->eref->aliasname);
|
||||
|
||||
/*
|
||||
* Use the real name of the table's column
|
||||
@@ -1002,7 +1002,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
||||
}
|
||||
/* falling off end of list shouldn't happen... */
|
||||
elog(ERROR, "Subquery %s does not have attribute %d",
|
||||
rte->eref->relname, attnum);
|
||||
rte->eref->aliasname, attnum);
|
||||
}
|
||||
else if (rte->rtekind == RTE_JOIN)
|
||||
{
|
||||
|
@@ -8,13 +8,13 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.77 2002/03/12 00:51:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.78 2002/03/21 16:01:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
@@ -56,9 +56,9 @@ transformTargetEntry(ParseState *pstate,
|
||||
|
||||
/* Transform the node if caller didn't do it already */
|
||||
if (expr == NULL)
|
||||
expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
|
||||
expr = transformExpr(pstate, node);
|
||||
|
||||
if (IsA(expr, Ident) &&((Ident *) expr)->isRel)
|
||||
if (IsA(expr, RangeVar))
|
||||
elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
|
||||
|
||||
type_id = exprType(expr);
|
||||
@@ -99,11 +99,13 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
{
|
||||
ResTarget *res = (ResTarget *) lfirst(targetlist);
|
||||
|
||||
if (IsA(res->val, Attr))
|
||||
if (IsA(res->val, ColumnRef))
|
||||
{
|
||||
Attr *att = (Attr *) res->val;
|
||||
ColumnRef *cref = (ColumnRef *) res->val;
|
||||
List *fields = cref->fields;
|
||||
int numnames = length(fields);
|
||||
|
||||
if (att->relname != NULL && strcmp(att->relname, "*") == 0)
|
||||
if (numnames == 1 && strcmp(strVal(lfirst(fields)), "*") == 0)
|
||||
{
|
||||
/*
|
||||
* Target item is a single '*', expand all tables (eg.
|
||||
@@ -112,27 +114,59 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
p_target = nconc(p_target,
|
||||
ExpandAllTables(pstate));
|
||||
}
|
||||
else if (att->attrs != NIL &&
|
||||
strcmp(strVal(lfirst(att->attrs)), "*") == 0)
|
||||
else if (strcmp(strVal(nth(numnames-1, fields)), "*") == 0)
|
||||
{
|
||||
/*
|
||||
* Target item is relation.*, expand that table (eg.
|
||||
* SELECT emp.*, dname FROM emp, dept)
|
||||
*/
|
||||
char *schemaname;
|
||||
char *relname;
|
||||
RangeTblEntry *rte;
|
||||
int sublevels_up;
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, att->relname,
|
||||
switch (numnames)
|
||||
{
|
||||
case 2:
|
||||
schemaname = NULL;
|
||||
relname = strVal(lfirst(fields));
|
||||
break;
|
||||
case 3:
|
||||
schemaname = strVal(lfirst(fields));
|
||||
relname = strVal(lsecond(fields));
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
char *name1 = strVal(lfirst(fields));
|
||||
|
||||
/*
|
||||
* We check the catalog name and then ignore it.
|
||||
*/
|
||||
if (strcmp(name1, DatabaseName) != 0)
|
||||
elog(ERROR, "Cross-database references are not implemented");
|
||||
schemaname = strVal(lsecond(fields));
|
||||
relname = strVal(lfirst(lnext(lnext(fields))));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "Invalid qualified name syntax (too many names)");
|
||||
schemaname = NULL; /* keep compiler quiet */
|
||||
relname = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX do something with schema name */
|
||||
rte = refnameRangeTblEntry(pstate, relname,
|
||||
&sublevels_up);
|
||||
if (rte == NULL)
|
||||
rte = addImplicitRTE(pstate, att->relname);
|
||||
rte = addImplicitRTE(pstate, relname);
|
||||
|
||||
p_target = nconc(p_target,
|
||||
expandRelAttrs(pstate, rte));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Plain Attr node, treat it as an expression */
|
||||
/* Plain ColumnRef node, treat it as an expression */
|
||||
p_target = lappend(p_target,
|
||||
transformTargetEntry(pstate,
|
||||
res->val,
|
||||
@@ -143,7 +177,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Everything else but Attr */
|
||||
/* Everything else but ColumnRef */
|
||||
p_target = lappend(p_target,
|
||||
transformTargetEntry(pstate,
|
||||
res->val,
|
||||
@@ -317,10 +351,9 @@ CoerceTargetExpr(ParseState *pstate,
|
||||
|
||||
/*
|
||||
* checkInsertTargets -
|
||||
* generate a list of column names if not supplied or
|
||||
* generate a list of INSERT column targets if not supplied, or
|
||||
* test supplied column names to make sure they are in target table.
|
||||
* Also return an integer list of the columns' attribute numbers.
|
||||
* (used exclusively for inserts)
|
||||
*/
|
||||
List *
|
||||
checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
@@ -338,17 +371,16 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
|
||||
for (i = 0; i < numcol; i++)
|
||||
{
|
||||
Ident *id = makeNode(Ident);
|
||||
ResTarget *col = makeNode(ResTarget);
|
||||
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(attr[i]))
|
||||
continue;
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
id->name = palloc(NAMEDATALEN);
|
||||
StrNCpy(id->name, NameStr(attr[i]->attname), NAMEDATALEN);
|
||||
id->indirection = NIL;
|
||||
id->isRel = false;
|
||||
cols = lappend(cols, id);
|
||||
col->name = pstrdup(NameStr(attr[i]->attname));
|
||||
col->indirection = NIL;
|
||||
col->val = NULL;
|
||||
cols = lappend(cols, col);
|
||||
*attrnos = lappendi(*attrnos, i + 1);
|
||||
}
|
||||
}
|
||||
@@ -361,7 +393,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
|
||||
foreach(tl, cols)
|
||||
{
|
||||
char *name = ((Ident *) lfirst(tl))->name;
|
||||
char *name = ((ResTarget *) lfirst(tl))->name;
|
||||
int attrno;
|
||||
|
||||
/* Lookup column name, elog on failure */
|
||||
@@ -458,19 +490,35 @@ FigureColnameInternal(Node *node, char **name)
|
||||
case T_Ident:
|
||||
*name = ((Ident *) node)->name;
|
||||
return 2;
|
||||
case T_Attr:
|
||||
case T_ColumnRef:
|
||||
{
|
||||
List *attrs = ((Attr *) node)->attrs;
|
||||
List *fields = ((ColumnRef *) node)->fields;
|
||||
|
||||
if (attrs)
|
||||
while (lnext(fields) != NIL)
|
||||
fields = lnext(fields);
|
||||
if (strcmp(strVal(lfirst(fields)), "*") != 0)
|
||||
{
|
||||
while (lnext(attrs) != NIL)
|
||||
attrs = lnext(attrs);
|
||||
*name = strVal(lfirst(attrs));
|
||||
*name = strVal(lfirst(fields));
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_ExprFieldSelect:
|
||||
{
|
||||
List *fields = ((ExprFieldSelect *) node)->fields;
|
||||
|
||||
if (fields)
|
||||
{
|
||||
while (lnext(fields) != NIL)
|
||||
fields = lnext(fields);
|
||||
if (strcmp(strVal(lfirst(fields)), "*") != 0)
|
||||
{
|
||||
*name = strVal(lfirst(fields));
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_FuncCall:
|
||||
*name = ((FuncCall *) node)->funcname;
|
||||
return 2;
|
||||
|
Reference in New Issue
Block a user