diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 322 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 698 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 7 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 30 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 322 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 384 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 6 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 62 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 104 |
9 files changed, 1083 insertions, 852 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 98f5030f78b..109062c529f 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 6951653ce8a..f94eaea5098 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.294 2002/03/20 19:44:21 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.295 2002/03/21 16:00:50 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -98,6 +98,7 @@ static void doNegateFloat(Value *v); %} + %union { int ival; @@ -108,19 +109,17 @@ static void doNegateFloat(Value *v); List *list; Node *node; Value *value; - - Attr *attr; - Ident *ident; + ColumnRef *columnref; TypeName *typnam; DefElem *defelt; SortGroupBy *sortgroupby; JoinExpr *jexpr; IndexElem *ielem; + Alias *alias; RangeVar *range; A_Indices *aind; ResTarget *target; - ParamNo *paramno; PrivTarget *privtarget; VersionStmt *vstmt; @@ -129,7 +128,7 @@ static void doNegateFloat(Value *v); InsertStmt *istmt; } -%type <node> stmt, +%type <node> stmt, schema_stmt, AlterDatabaseSetStmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, AlterUserSetStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, @@ -166,11 +165,12 @@ static void doNegateFloat(Value *v); %type <list> OptUserList %type <defelt> OptUserElem +%type <str> OptSchemaName +%type <list> OptSchemaEltList + %type <boolean> TriggerActionTime, TriggerForSpec, opt_trusted, opt_procedural %type <str> opt_lancompiler -%type <str> OptConstrFromTable - %type <str> TriggerEvents %type <value> TriggerFuncArg @@ -178,6 +178,8 @@ static void doNegateFloat(Value *v); database_name, access_method_clause, access_method, attr_name, class, index_name, name, func_name, file_name +%type <range> qualified_name, OptConstrFromTable + %type <str> opt_id, all_Op, MathOp, opt_name, OptUseOp, opt_class, SpecialRuleRelation @@ -193,17 +195,19 @@ static void doNegateFloat(Value *v); %type <chr> TriggerOneEvent %type <list> stmtblock, stmtmulti, - into_clause, OptTempTableName, relation_name_list, OptTableElementList, OptInherit, definition, opt_distinct, opt_with, func_args, func_args_list, func_as, oper_argtypes, RuleActionList, RuleActionMulti, opt_column_list, columnList, opt_name_list, sort_clause, sortby_list, index_params, index_list, name_list, - from_clause, from_list, opt_array_bounds, - expr_list, attrs, target_list, update_target_list, + from_clause, from_list, opt_array_bounds, qualified_name_list, + expr_list, attrs, opt_attrs, target_list, update_target_list, + insert_column_list, def_list, opt_indirection, group_clause, TriggerFuncArgs, select_limit, opt_select_limit +%type <range> into_clause, OptTempTableName + %type <typnam> func_arg, func_return, func_type, aggr_argtype %type <boolean> opt_arg, TriggerForOpt, TriggerForType, OptTemp, OptWithOids @@ -240,7 +244,7 @@ static void doNegateFloat(Value *v); %type <node> OptTableElement, ConstraintElem %type <node> columnDef %type <defelt> def_elem -%type <node> def_arg, columnElem, where_clause, +%type <node> def_arg, columnElem, where_clause, insert_column_item, a_expr, b_expr, c_expr, AexprConst, in_expr, having_clause %type <list> row_descriptor, row_list, in_expr_nodes @@ -252,14 +256,14 @@ static void doNegateFloat(Value *v); %type <list> OptCreateAs, CreateAsList %type <node> CreateAsElement %type <value> NumericOnly, FloatOnly, IntegerOnly -%type <attr> event_object, attr, alias_clause +%type <columnref> columnref +%type <alias> alias_clause %type <sortgroupby> sortby %type <ielem> index_elem, func_index %type <node> table_ref %type <jexpr> joined_table %type <range> relation_expr %type <target> target_el, update_target_el -%type <paramno> ParamNo %type <typnam> Typename, SimpleTypename, ConstTypename GenericType, Numeric, Character, ConstDatetime, ConstInterval, Bit @@ -290,6 +294,7 @@ static void doNegateFloat(Value *v); %type <boolean> opt_as + /* * If you make any token changes, remember to: * - use "yacc -d" and update parse.h @@ -753,35 +758,57 @@ DropGroupStmt: DROP GROUP UserId * *****************************************************************************/ -CreateSchemaStmt: CREATE SCHEMA UserId +CreateSchemaStmt: CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptSchemaEltList { - /* for now, just make this the same as CREATE DATABASE */ - CreatedbStmt *n = makeNode(CreatedbStmt); - n->dbname = $3; - n->dbowner = NULL; - n->dbpath = NULL; - n->dbtemplate = NULL; - n->encoding = -1; + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id... */ + if ($3 != NULL) + n->schemaname = $3; + else + n->schemaname = $5; + n->authid = $5; + n->schemaElts = $6; + $$ = (Node *)n; + } + | CREATE SCHEMA ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $3; + n->authid = NULL; + n->schemaElts = $4; $$ = (Node *)n; } ; -AlterSchemaStmt: ALTER SCHEMA UserId +AlterSchemaStmt: ALTER SCHEMA ColId { elog(ERROR, "ALTER SCHEMA not yet supported"); } ; -DropSchemaStmt: DROP SCHEMA UserId +DropSchemaStmt: DROP SCHEMA ColId { - DropdbStmt *n = makeNode(DropdbStmt); - n->dbname = $3; - $$ = (Node *)n; + elog(ERROR, "DROP SCHEMA not yet supported"); } ; +OptSchemaName: ColId { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +OptSchemaEltList: OptSchemaEltList schema_stmt { $$ = lappend($1, $2); } + | /* EMPTY */ { $$ = NIL; } + ; - +/* + * schema_stmt are the ones that can show up inside a CREATE SCHEMA + * statement (in addition to by themselves). + */ +schema_stmt: CreateStmt + | GrantStmt + | ViewStmt + ; /***************************************************************************** * @@ -978,6 +1005,7 @@ opt_encoding: Sconst { $$ = $1; } ColId_or_Sconst: ColId { $$ = $1; } | SCONST { $$ = $1; } + ; VariableShowStmt: SHOW ColId @@ -1073,8 +1101,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'A'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->def = $6; $$ = (Node *)n; } @@ -1083,8 +1110,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'T'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->name = $6; n->def = $7; $$ = (Node *)n; @@ -1094,8 +1120,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'S'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->name = $6; n->def = (Node *) makeInteger($9); $$ = (Node *)n; @@ -1105,8 +1130,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'M'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->name = $6; n->def = (Node *) makeString($9); $$ = (Node *)n; @@ -1116,8 +1140,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'D'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->name = $6; n->behavior = $7; $$ = (Node *)n; @@ -1127,8 +1150,7 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'C'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->def = $5; $$ = (Node *)n; } @@ -1137,28 +1159,27 @@ AlterTableStmt: { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'X'; - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->name = $6; n->behavior = $7; $$ = (Node *)n; } /* ALTER TABLE <name> CREATE TOAST TABLE */ - | ALTER TABLE relation_name CREATE TOAST TABLE + | ALTER TABLE qualified_name CREATE TOAST TABLE { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'E'; - n->relname = $3; - n->inhOpt = INH_NO; + $3->inhOpt = INH_NO; + n->relation = $3; $$ = (Node *)n; } /* ALTER TABLE <name> OWNER TO UserId */ - | ALTER TABLE relation_name OWNER TO UserId + | ALTER TABLE qualified_name OWNER TO UserId { AlterTableStmt *n = makeNode(AlterTableStmt); n->subtype = 'U'; - n->relname = $3; - n->inhOpt = INH_NO; + $3->inhOpt = INH_NO; + n->relation = $3; n->name = $6; $$ = (Node *)n; } @@ -1183,7 +1204,8 @@ drop_behavior: CASCADE { $$ = CASCADE; } opt_drop_behavior: CASCADE { $$ = CASCADE; } | RESTRICT { $$ = RESTRICT; } | /* EMPTY */ { $$ = RESTRICT; /* default */ } - ; + ; + /***************************************************************************** @@ -1214,11 +1236,11 @@ opt_id: ColId { $$ = $1; } * *****************************************************************************/ -CopyStmt: COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null +CopyStmt: COPY opt_binary qualified_name opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null { CopyStmt *n = makeNode(CopyStmt); n->binary = $2; - n->relname = $3; + n->relation = $3; n->oids = $4; n->direction = $5; n->filename = $6; @@ -1263,8 +1285,9 @@ opt_using: USING { $$ = TRUE; } | /*EMPTY*/ { $$ = TRUE; } ; -copy_null: WITH NULL_P AS Sconst { $$ = $4; } - | /*EMPTY*/ { $$ = "\\N"; } +copy_null: WITH NULL_P AS Sconst { $$ = $4; } + | /*EMPTY*/ { $$ = "\\N"; } + ; /***************************************************************************** * @@ -1273,14 +1296,14 @@ copy_null: WITH NULL_P AS Sconst { $$ = $4; } * *****************************************************************************/ -CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')' OptInherit OptWithOids +CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' OptInherit OptWithOids { CreateStmt *n = makeNode(CreateStmt); - n->relname = $4; + $4->istemp = $2; + n->relation = $4; n->tableElts = $6; - n->inhRelnames = $8; + n->inhRelations = $8; n->constraints = NIL; - n->istemp = $2; n->hasoids = $9; $$ = (Node *)n; } @@ -1448,10 +1471,7 @@ ColConstraintElem: n->name = NULL; if (exprIsNullConstant($2)) { - /* - * DEFAULT NULL should be reported as empty expr - * Required for NOT NULL Domain overrides - */ + /* DEFAULT NULL should be reported as empty expr */ n->raw_expr = NULL; } else @@ -1462,11 +1482,11 @@ ColConstraintElem: n->keys = NULL; $$ = (Node *)n; } - | REFERENCES ColId opt_column_list key_match key_actions + | REFERENCES qualified_name opt_column_list key_match key_actions { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; - n->pktable_name = $2; + n->pktable = $2; n->fk_attrs = NIL; n->pk_attrs = $3; n->match_type = $4; @@ -1573,12 +1593,12 @@ ConstraintElem: CHECK '(' a_expr ')' n->keys = $4; $$ = (Node *)n; } - | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list + | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name opt_column_list key_match key_actions ConstraintAttributeSpec { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; - n->pktable_name = $7; + n->pktable = $7; n->fk_attrs = $4; n->pk_attrs = $8; n->match_type = $9; @@ -1589,6 +1609,24 @@ ConstraintElem: CHECK '(' a_expr ')' } ; +opt_column_list: '(' columnList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +columnList: columnList ',' columnElem + { $$ = lappend($1, $3); } + | columnElem + { $$ = makeList1($1); } + ; + +columnElem: ColId + { + Ident *id = makeNode(Ident); + id->name = $1; + $$ = (Node *)id; + } + ; + key_match: MATCH FULL { $$ = "FULL"; @@ -1624,7 +1662,7 @@ key_reference: NO ACTION { $$ = FKCONSTR_ON_KEY_NOACTION; } | SET DEFAULT { $$ = FKCONSTR_ON_KEY_SETDEFAULT; } ; -OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; } +OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; @@ -1639,7 +1677,7 @@ OptWithOids: WITH OIDS { $$ = TRUE; } * SELECT ... INTO. */ -CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt +CreateAsStmt: CREATE OptTemp TABLE qualified_name OptCreateAs AS SelectStmt { /* * When the SelectStmt is a set-operation tree, we must @@ -1651,7 +1689,7 @@ CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt SelectStmt *n = findLeftmostSelect((SelectStmt *) $7); if (n->into != NULL) elog(ERROR,"CREATE TABLE AS may not specify INTO"); - n->istemp = $2; + $4->istemp = $2; n->into = $4; n->intoColNames = $5; $$ = $7; @@ -1687,11 +1725,11 @@ CreateAsElement: ColId * *****************************************************************************/ -CreateSeqStmt: CREATE OptTemp SEQUENCE relation_name OptSeqList +CreateSeqStmt: CREATE OptTemp SEQUENCE qualified_name OptSeqList { CreateSeqStmt *n = makeNode(CreateSeqStmt); - n->istemp = $2; - n->seqname = $4; + $4->istemp = $2; + n->sequence = $4; n->options = $5; $$ = (Node *)n; } @@ -1742,6 +1780,7 @@ OptSeqElem: CACHE NumericOnly NumericOnly: FloatOnly { $$ = $1; } | IntegerOnly { $$ = $1; } + ; FloatOnly: FCONST { @@ -1791,6 +1830,7 @@ opt_trusted: TRUSTED { $$ = TRUE; } opt_lancompiler: LANCOMPILER Sconst { $$ = $2; } | /*EMPTY*/ { $$ = ""; } + ; DropPLangStmt: DROP opt_procedural LANGUAGE ColId_or_Sconst { @@ -1813,12 +1853,12 @@ opt_procedural: PROCEDURAL { $$ = TRUE; } *****************************************************************************/ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON - relation_name TriggerForSpec EXECUTE PROCEDURE + qualified_name TriggerForSpec EXECUTE PROCEDURE name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $3; - n->relname = $7; + n->relation = $7; n->funcname = $11; n->args = $13; n->before = $4; @@ -1832,17 +1872,17 @@ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON n->isconstraint = FALSE; n->deferrable = FALSE; n->initdeferred = FALSE; - n->constrrelname = NULL; + n->constrrel = NULL; $$ = (Node *)n; } | CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON - relation_name OptConstrFromTable + qualified_name OptConstrFromTable ConstraintAttributeSpec FOR EACH ROW EXECUTE PROCEDURE name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $4; - n->relname = $8; + n->relation = $8; n->funcname = $16; n->args = $18; n->before = FALSE; @@ -1857,7 +1897,7 @@ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON n->deferrable = ($10 & 1) != 0; n->initdeferred = ($10 & 2) != 0; - n->constrrelname = $9; + n->constrrel = $9; $$ = (Node *)n; } ; @@ -1937,9 +1977,9 @@ TriggerFuncArg: ICONST OptConstrFromTable: /* Empty */ { - $$ = ""; + $$ = NULL; } - | FROM relation_name + | FROM qualified_name { $$ = $2; } @@ -1983,11 +2023,11 @@ ConstraintTimeSpec: INITIALLY IMMEDIATE ; -DropTrigStmt: DROP TRIGGER name ON relation_name +DropTrigStmt: DROP TRIGGER name ON qualified_name { DropTrigStmt *n = makeNode(DropTrigStmt); n->trigname = $3; - n->relname = $5; + n->relation = $5; $$ = (Node *) n; } ; @@ -2063,11 +2103,14 @@ def_arg: func_return { $$ = (Node *)$1; } * *****************************************************************************/ -DropStmt: DROP drop_type name_list opt_drop_behavior +/* DropStmt needs to use qualified_name_list as many of the objects + * are relations or other schema objects (names can be schema-qualified) */ + +DropStmt: DROP drop_type qualified_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = $2; - n->names = $3; + n->objects = $3; n->behavior = $4; $$ = (Node *)n; } @@ -2089,10 +2132,10 @@ drop_type: TABLE { $$ = DROP_TABLE; } * *****************************************************************************/ -TruncateStmt: TRUNCATE opt_table relation_name +TruncateStmt: TRUNCATE opt_table qualified_name { TruncateStmt *n = makeNode(TruncateStmt); - n->relName = $3; + n->relation = $3; $$ = (Node *)n; } ; @@ -2120,20 +2163,41 @@ CommentStmt: COMMENT ON comment_type name IS comment_text n->comment = $6; $$ = (Node *) n; } - | COMMENT ON COLUMN relation_name '.' attr_name IS comment_text + | COMMENT ON COLUMN ColId '.' attr_name IS comment_text { + /* + * We can't use qualified_name here as the '.' causes a shift/red; + * with ColId we do not test for NEW and OLD as table names, but it is OK + * as they would fail anyway as COMMENT cannot appear in a RULE. + * ColumnRef is also innapropriate as we don't take subscripts + * or '*' and have a very precise number of elements (2 or 3) + * so we do it from scratch. + */ CommentStmt *n = makeNode(CommentStmt); n->objtype = COLUMN; + n->objschema = NULL; n->objname = $4; n->objproperty = $6; n->objlist = NULL; n->comment = $8; $$ = (Node *) n; } + | COMMENT ON COLUMN ColId '.' ColId '.' attr_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = COLUMN; + n->objschema = $4; + n->objname = $6; + n->objproperty = $8; + n->objlist = NULL; + n->comment = $10; + $$ = (Node *) n; + } | COMMENT ON AGGREGATE name '(' aggr_argtype ')' IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = AGGREGATE; + n->objschema = NULL; n->objname = $4; n->objproperty = NULL; n->objlist = makeList1($6); @@ -2145,6 +2209,7 @@ CommentStmt: COMMENT ON comment_type name IS comment_text /* Obsolete syntax, but must support for awhile */ CommentStmt *n = makeNode(CommentStmt); n->objtype = AGGREGATE; + n->objschema = NULL; n->objname = $4; n->objproperty = NULL; n->objlist = makeList1($5); @@ -2155,6 +2220,7 @@ CommentStmt: COMMENT ON comment_type name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = FUNCTION; + n->objschema = NULL; n->objname = $4; n->objproperty = NULL; n->objlist = $5; @@ -2165,18 +2231,21 @@ CommentStmt: COMMENT ON comment_type name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OPERATOR; + n->objschema = NULL; n->objname = $4; n->objproperty = NULL; n->objlist = $6; n->comment = $9; $$ = (Node *) n; } - | COMMENT ON TRIGGER name ON relation_name IS comment_text + | COMMENT ON TRIGGER name ON qualified_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = TRIGGER; + /* NOTE: schemaname here refers to the table in objproperty */ + n->objschema = $6->schemaname; n->objname = $4; - n->objproperty = $6; + n->objproperty = $6->relname; n->objlist = NULL; n->comment = $8; $$ = (Node *) n; @@ -2414,14 +2483,14 @@ privilege: SELECT { $$ = SELECT; } /* Don't bother trying to fold the first two rules into one using opt_table. You're going to get conflicts. */ -privilege_target: relation_name_list +privilege_target: qualified_name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = TABLE; n->objs = $1; $$ = n; } - | TABLE relation_name_list + | TABLE qualified_name_list { PrivTarget *n = makeNode(PrivTarget); n->objtype = TABLE; @@ -2513,13 +2582,13 @@ function_with_argtypes: func_name func_args * *****************************************************************************/ -IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name +IndexStmt: CREATE index_opt_unique INDEX index_name ON qualified_name access_method_clause '(' index_params ')' where_clause { IndexStmt *n = makeNode(IndexStmt); n->unique = $2; n->idxname = $4; - n->relname = $6; + n->relation = $6; n->accessMethod = $7; n->indexParams = $9; n->whereClause = $11; @@ -2783,11 +2852,21 @@ oper_argtypes: Typename * *****************************************************************************/ -ReindexStmt: REINDEX reindex_type name opt_force +ReindexStmt: REINDEX reindex_type qualified_name opt_force { ReindexStmt *n = makeNode(ReindexStmt); n->reindexType = $2; + n->relation = $3; + n->name = NULL; + n->force = $4; + $$ = (Node *)n; + } + | REINDEX DATABASE name opt_force + { + ReindexStmt *n = makeNode(ReindexStmt); + n->reindexType = DATABASE; n->name = $3; + n->relation = NULL; n->force = $4; $$ = (Node *)n; } @@ -2795,8 +2874,8 @@ ReindexStmt: REINDEX reindex_type name opt_force reindex_type: INDEX { $$ = INDEX; } | TABLE { $$ = TABLE; } - | DATABASE { $$ = DATABASE; } ; + opt_force: FORCE { $$ = TRUE; } | /* EMPTY */ { $$ = FALSE; } ; @@ -2813,8 +2892,7 @@ opt_force: FORCE { $$ = TRUE; } RenameStmt: ALTER TABLE relation_expr RENAME opt_column opt_name TO name { RenameStmt *n = makeNode(RenameStmt); - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->column = $6; n->newname = $8; $$ = (Node *)n; @@ -2832,23 +2910,20 @@ opt_column: COLUMN { $$ = COLUMN; } /***************************************************************************** * - * QUERY: Define Rewrite Rule , Define Tuple Rule - * Define Rule <old rules > - * - * only rewrite rule is supported -- ay 9/94 + * QUERY: Define Rewrite Rule * *****************************************************************************/ RuleStmt: CREATE RULE name AS { QueryIsRule=TRUE; } - ON event TO event_object where_clause + ON event TO qualified_name where_clause DO opt_instead RuleActionList { RuleStmt *n = makeNode(RuleStmt); + n->relation = $9; n->rulename = $3; - n->event = $7; - n->object = $9; n->whereClause = $10; + n->event = $7; n->instead = $12; n->actions = $13; $$ = (Node *)n; @@ -2888,24 +2963,6 @@ RuleActionStmtOrEmpty: RuleActionStmt { $$ = (Node *)NULL; } ; -event_object: relation_name '.' attr_name - { - $$ = makeNode(Attr); - $$->relname = $1; - $$->paramNo = NULL; - $$->attrs = makeList1(makeString($3)); - $$->indirection = NIL; - } - | relation_name - { - $$ = makeNode(Attr); - $$->relname = $1; - $$->paramNo = NULL; - $$->attrs = NIL; - $$->indirection = NIL; - } - ; - /* change me to select, update, etc. some day */ event: SELECT { $$ = CMD_SELECT; } | UPDATE { $$ = CMD_UPDATE; } @@ -2921,40 +2978,42 @@ opt_instead: INSTEAD { $$ = TRUE; } /***************************************************************************** * * QUERY: - * NOTIFY <relation_name> can appear both in rule bodies and + * NOTIFY <qualified_name> can appear both in rule bodies and * as a query-level command * *****************************************************************************/ -NotifyStmt: NOTIFY relation_name +NotifyStmt: NOTIFY qualified_name { NotifyStmt *n = makeNode(NotifyStmt); - n->relname = $2; + n->relation = $2; $$ = (Node *)n; } ; -ListenStmt: LISTEN relation_name +ListenStmt: LISTEN qualified_name { ListenStmt *n = makeNode(ListenStmt); - n->relname = $2; + n->relation = $2; $$ = (Node *)n; } -; + ; -UnlistenStmt: UNLISTEN relation_name +UnlistenStmt: UNLISTEN qualified_name { UnlistenStmt *n = makeNode(UnlistenStmt); - n->relname = $2; + n->relation = $2; $$ = (Node *)n; } | UNLISTEN '*' { UnlistenStmt *n = makeNode(UnlistenStmt); - n->relname = "*"; + n->relation = makeNode(RangeVar); + n->relation->relname = "*"; + n->relation->schemaname = NULL; $$ = (Node *)n; } -; + ; /***************************************************************************** @@ -3036,10 +3095,10 @@ opt_chain: AND NO CHAIN * *****************************************************************************/ -ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt +ViewStmt: CREATE VIEW qualified_name opt_column_list AS SelectStmt { ViewStmt *n = makeNode(ViewStmt); - n->viewname = $3; + n->view = $3; n->aliases = $4; n->query = (Query *) $6; $$ = (Node *)n; @@ -3067,7 +3126,6 @@ LoadStmt: LOAD file_name * * CREATE DATABASE * - * *****************************************************************************/ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_list @@ -3192,21 +3250,6 @@ opt_equal: '=' { $$ = TRUE; } /***************************************************************************** * - * DROP DATABASE - * - *****************************************************************************/ - -DropdbStmt: DROP DATABASE database_name - { - DropdbStmt *n = makeNode(DropdbStmt); - n->dbname = $3; - $$ = (Node *)n; - } - ; - - -/***************************************************************************** - * * ALTER DATABASE * *****************************************************************************/ @@ -3229,10 +3272,25 @@ AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt } ; + /***************************************************************************** * - * Manipulate a domain + * DROP DATABASE + * + *****************************************************************************/ + +DropdbStmt: DROP DATABASE database_name + { + DropdbStmt *n = makeNode(DropdbStmt); + n->dbname = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** * + * Manipulate a domain * *****************************************************************************/ @@ -3254,17 +3312,18 @@ opt_as: AS {$$ = TRUE; } | /* EMPTY */ {$$ = FALSE; } ; + /***************************************************************************** * * QUERY: - * cluster <index_name> on <relation_name> + * cluster <index_name> on <qualified_name> * *****************************************************************************/ -ClusterStmt: CLUSTER index_name ON relation_name +ClusterStmt: CLUSTER index_name ON qualified_name { ClusterStmt *n = makeNode(ClusterStmt); - n->relname = $4; + n->relation = $4; n->indexname = $2; $$ = (Node*)n; } @@ -3286,11 +3345,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose n->full = $2; n->freeze = $3; n->verbose = $4; - n->vacrel = NULL; + n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; } - | VACUUM opt_full opt_freeze opt_verbose relation_name + | VACUUM opt_full opt_freeze opt_verbose qualified_name { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = true; @@ -3298,7 +3357,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose n->full = $2; n->freeze = $3; n->verbose = $4; - n->vacrel = $5; + n->relation = $5; n->va_cols = NIL; $$ = (Node *)n; } @@ -3321,11 +3380,11 @@ AnalyzeStmt: analyze_keyword opt_verbose n->full = false; n->freeze = false; n->verbose = $2; - n->vacrel = NULL; + n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; } - | analyze_keyword opt_verbose relation_name opt_name_list + | analyze_keyword opt_verbose qualified_name opt_name_list { VacuumStmt *n = makeNode(VacuumStmt); n->vacuum = false; @@ -3333,7 +3392,7 @@ AnalyzeStmt: analyze_keyword opt_verbose n->full = false; n->freeze = false; n->verbose = $2; - n->vacrel = $3; + n->relation = $3; n->va_cols = $4; $$ = (Node *)n; } @@ -3413,15 +3472,9 @@ OptimizableStmt: SelectStmt * *****************************************************************************/ -/* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest' - * originally. When the second rule of 'insert_rest' was changed to use the - * new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/reduce - * conflict. So I just changed the rules 'InsertStmt' and 'insert_rest' to - * accept the same statements without any shift/reduce conflicts - */ -InsertStmt: INSERT INTO relation_name insert_rest +InsertStmt: INSERT INTO qualified_name insert_rest { - $4->relname = $3; + $4->relation = $3; $$ = (Node *) $4; } ; @@ -3447,14 +3500,14 @@ insert_rest: VALUES '(' target_list ')' $$->targetList = NIL; $$->selectStmt = $1; } - | '(' columnList ')' VALUES '(' target_list ')' + | '(' insert_column_list ')' VALUES '(' target_list ')' { $$ = makeNode(InsertStmt); $$->cols = $2; $$->targetList = $6; $$->selectStmt = NULL; } - | '(' columnList ')' SelectStmt + | '(' insert_column_list ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; @@ -3463,22 +3516,19 @@ insert_rest: VALUES '(' target_list ')' } ; -opt_column_list: '(' columnList ')' { $$ = $2; } - | /*EMPTY*/ { $$ = NIL; } - ; - -columnList: columnList ',' columnElem +insert_column_list: insert_column_list ',' insert_column_item { $$ = lappend($1, $3); } - | columnElem + | insert_column_item { $$ = makeList1($1); } ; -columnElem: ColId opt_indirection +insert_column_item: ColId opt_indirection { - Ident *id = makeNode(Ident); - id->name = $1; - id->indirection = $2; - $$ = (Node *)id; + ResTarget *n = makeNode(ResTarget); + n->name = $1; + n->indirection = $2; + n->val = NULL; + $$ = (Node *)n; } ; @@ -3493,18 +3543,17 @@ columnElem: ColId opt_indirection DeleteStmt: DELETE FROM relation_expr where_clause { DeleteStmt *n = makeNode(DeleteStmt); - n->relname = $3->relname; - n->inhOpt = $3->inhOpt; + n->relation = $3; n->whereClause = $4; $$ = (Node *)n; } ; -LockStmt: LOCK_P opt_table relation_name_list opt_lock +LockStmt: LOCK_P opt_table qualified_name_list opt_lock { LockStmt *n = makeNode(LockStmt); - n->rellist = $3; + n->relations = $3; n->mode = $4; $$ = (Node *)n; } @@ -3538,8 +3587,7 @@ UpdateStmt: UPDATE relation_expr where_clause { UpdateStmt *n = makeNode(UpdateStmt); - n->relname = $2->relname; - n->inhOpt = $2->inhOpt; + n->relation = $2; n->targetList = $4; n->fromClause = $5; n->whereClause = $6; @@ -3687,8 +3735,7 @@ simple_select: SELECT opt_distinct target_list SelectStmt *n = makeNode(SelectStmt); n->distinctClause = $2; n->targetList = $3; - n->istemp = (bool) ((Value *) lfirst($4))->val.ival; - n->into = (char *) lnext($4); + n->into = $4; n->intoColNames = NIL; n->fromClause = $5; n->whereClause = $6; @@ -3710,40 +3757,56 @@ simple_select: SELECT opt_distinct target_list } ; - /* easy way to return two values. Can someone improve this? bjm */ into_clause: INTO OptTempTableName { $$ = $2; } - | /*EMPTY*/ { $$ = makeList1(makeInteger(FALSE)); } + | /*EMPTY*/ { $$ = NULL; } ; /* * Redundancy here is needed to avoid shift/reduce conflicts, * since TEMP is not a reserved word. See also OptTemp. - * - * The result is a cons cell (not a true list!) containing - * a boolean and a table name. */ -OptTempTableName: TEMPORARY opt_table relation_name - { $$ = lcons(makeInteger(TRUE), (List *) $3); } - | TEMP opt_table relation_name - { $$ = lcons(makeInteger(TRUE), (List *) $3); } - | LOCAL TEMPORARY opt_table relation_name - { $$ = lcons(makeInteger(TRUE), (List *) $4); } - | LOCAL TEMP opt_table relation_name - { $$ = lcons(makeInteger(TRUE), (List *) $4); } - | GLOBAL TEMPORARY opt_table relation_name +OptTempTableName: TEMPORARY opt_table qualified_name + { + $$ = $3; + $$->istemp = true; + } + | TEMP opt_table qualified_name + { + $$ = $3; + $$->istemp = true; + } + | LOCAL TEMPORARY opt_table qualified_name + { + $$ = $4; + $$->istemp = true; + } + | LOCAL TEMP opt_table qualified_name + { + $$ = $4; + $$->istemp = true; + } + | GLOBAL TEMPORARY opt_table qualified_name { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); - $$ = lcons(makeInteger(TRUE), (List *) $4); + $$ = $4; + $$->istemp = true; } - | GLOBAL TEMP opt_table relation_name + | GLOBAL TEMP opt_table qualified_name { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); - $$ = lcons(makeInteger(TRUE), (List *) $4); + $$ = $4; + $$->istemp = true; + } + | TABLE qualified_name + { + $$ = $2; + $$->istemp = false; + } + | qualified_name + { + $$ = $1; + $$->istemp = false; } - | TABLE relation_name - { $$ = lcons(makeInteger(FALSE), (List *) $2); } - | relation_name - { $$ = lcons(makeInteger(FALSE), (List *) $1); } ; opt_table: TABLE { $$ = TRUE; } @@ -3878,13 +3941,6 @@ select_offset_value: Iconst * ...however, recursive addattr and rename supported. make special * cases for these. */ -relation_name_list: name_list; - -name_list: name - { $$ = makeList1(makeString($1)); } - | name_list ',' name - { $$ = lappend($1, makeString($3)); } - ; group_clause: GROUP BY expr_list { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } @@ -3938,7 +3994,7 @@ table_ref: relation_expr } | relation_expr alias_clause { - $1->name = $2; + $1->alias = $2; $$ = (Node *) $1; } | select_with_parens @@ -3962,7 +4018,7 @@ table_ref: relation_expr { RangeSubselect *n = makeNode(RangeSubselect); n->subquery = $1; - n->name = $2; + n->alias = $2; $$ = (Node *) n; } | joined_table @@ -4077,25 +4133,25 @@ joined_table: '(' joined_table ')' alias_clause: AS ColId '(' name_list ')' { - $$ = makeNode(Attr); - $$->relname = $2; - $$->attrs = $4; + $$ = makeNode(Alias); + $$->aliasname = $2; + $$->colnames = $4; } | AS ColId { - $$ = makeNode(Attr); - $$->relname = $2; + $$ = makeNode(Alias); + $$->aliasname = $2; } | ColId '(' name_list ')' { - $$ = makeNode(Attr); - $$->relname = $1; - $$->attrs = $3; + $$ = makeNode(Alias); + $$->aliasname = $1; + $$->colnames = $3; } | ColId { - $$ = makeNode(Attr); - $$->relname = $1; + $$ = makeNode(Alias); + $$->aliasname = $1; } ; @@ -4124,29 +4180,26 @@ join_qual: USING '(' name_list ')' { $$ = (Node *) $3; } ; -relation_expr: relation_name +relation_expr: qualified_name { /* default inheritance */ - $$ = makeNode(RangeVar); - $$->relname = $1; + $$ = $1; $$->inhOpt = INH_DEFAULT; - $$->name = NULL; + $$->alias = NULL; } - | relation_name '*' + | qualified_name '*' { /* inheritance query */ - $$ = makeNode(RangeVar); - $$->relname = $1; + $$ = $1; $$->inhOpt = INH_YES; - $$->name = NULL; + $$->alias = NULL; } - | ONLY relation_name + | ONLY qualified_name { /* no inheritance */ - $$ = makeNode(RangeVar); - $$->relname = $2; + $$ = $2; $$->inhOpt = INH_NO; - $$->name = NULL; + $$->alias = NULL; } ; @@ -4357,6 +4410,7 @@ bit: BIT opt_varying else type = xlateSqlType("bit"); $$ = type; } + ; /* @@ -5010,20 +5064,32 @@ b_expr: c_expr * inside parentheses, such as function arguments; that cannot introduce * ambiguity to the b_expr syntax. */ -c_expr: attr +c_expr: columnref { $$ = (Node *) $1; } - | ColId opt_indirection + | AexprConst + { $$ = $1; } + | PARAM attrs opt_indirection { - /* could be a column name or a relation_name */ - Ident *n = makeNode(Ident); - n->name = $1; - n->indirection = $2; + /* + * PARAM without field names is considered a constant, + * but with 'em, it is not. Not very consistent ... + */ + ParamRef *n = makeNode(ParamRef); + n->number = $1; + n->fields = $2; + n->indirection = $3; $$ = (Node *)n; } - | AexprConst - { $$ = $1; } | '(' a_expr ')' { $$ = $2; } + | '(' a_expr ')' attrs opt_indirection + { + ExprFieldSelect *n = makeNode(ExprFieldSelect); + n->arg = $2; + n->fields = $4; + n->indirection = $5; + $$ = (Node *)n; + } | CAST '(' a_expr AS Typename ')' { $$ = makeTypeCast($3, $5); } | case_expr @@ -5569,34 +5635,40 @@ case_arg: a_expr { $$ = NULL; } ; -attr: relation_name '.' attrs opt_indirection +/* + * columnref starts with relation_name not ColId, so that OLD and NEW + * references can be accepted. Note that when there are more than two + * dotted names, the first name is not actually a relation name... + */ +columnref: relation_name opt_indirection { - $$ = makeNode(Attr); - $$->relname = $1; - $$->paramNo = NULL; - $$->attrs = $3; - $$->indirection = $4; + $$ = makeNode(ColumnRef); + $$->fields = makeList1(makeString($1)); + $$->indirection = $2; } - | ParamNo '.' attrs opt_indirection + | relation_name attrs opt_indirection { - $$ = makeNode(Attr); - $$->relname = NULL; - $$->paramNo = $1; - $$->attrs = $3; - $$->indirection = $4; + $$ = makeNode(ColumnRef); + $$->fields = lcons(makeString($1), $2); + $$->indirection = $3; } ; -attrs: attr_name - { $$ = makeList1(makeString($1)); } - | attrs '.' attr_name +attrs: opt_attrs '.' attr_name { $$ = lappend($1, makeString($3)); } - | attrs '.' '*' + | opt_attrs '.' '*' { $$ = lappend($1, makeString("*")); } ; +opt_attrs: /*EMPTY*/ + { $$ = NIL; } + | opt_attrs '.' attr_name + { $$ = lappend($1, makeString($3)); } + ; + opt_empty_parentheses: '(' ')' { $$ = TRUE; } | /*EMPTY*/ { $$ = TRUE; } + ; /***************************************************************************** * @@ -5617,39 +5689,25 @@ target_el: a_expr AS ColLabel { $$ = makeNode(ResTarget); $$->name = $3; - $$->indirection = NULL; + $$->indirection = NIL; $$->val = (Node *)$1; } | a_expr { $$ = makeNode(ResTarget); $$->name = NULL; - $$->indirection = NULL; + $$->indirection = NIL; $$->val = (Node *)$1; } - | relation_name '.' '*' - { - Attr *att = makeNode(Attr); - att->relname = $1; - att->paramNo = NULL; - att->attrs = makeList1(makeString("*")); - att->indirection = NIL; - $$ = makeNode(ResTarget); - $$->name = NULL; - $$->indirection = NULL; - $$->val = (Node *)att; - } | '*' { - Attr *att = makeNode(Attr); - att->relname = "*"; - att->paramNo = NULL; - att->attrs = NULL; - att->indirection = NIL; + ColumnRef *n = makeNode(ColumnRef); + n->fields = makeList1(makeString("*")); + n->indirection = NIL; $$ = makeNode(ResTarget); $$->name = NULL; - $$->indirection = NULL; - $$->val = (Node *)att; + $$->indirection = NIL; + $$->val = (Node *)n; } ; @@ -5685,6 +5743,42 @@ relation_name: SpecialRuleRelation $$ = $1; } ; + +qualified_name_list: qualified_name + { $$ = makeList1($1); } + | qualified_name_list ',' qualified_name + { $$ = lappend($1, $3); } + ; + +qualified_name: ColId + { + $$ = makeNode(RangeVar); + $$->catalogname = NULL; + $$->schemaname = NULL; + $$->relname = $1; + } + | ColId '.' ColId + { + $$ = makeNode(RangeVar); + $$->catalogname = NULL; + $$->schemaname = $1; + $$->relname = $3; + } + | ColId '.' ColId '.' ColId + { + $$ = makeNode(RangeVar); + $$->catalogname = $1; + $$->schemaname = $3; + $$->relname = $5; + } + ; + +name_list: name + { $$ = makeList1(makeString($1)); } + | name_list ',' name + { $$ = lappend($1, makeString($3)); } + ; + name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; @@ -5761,8 +5855,14 @@ AexprConst: Iconst $$ = (Node *)n; } - | ParamNo - { $$ = (Node *)$1; } + | PARAM opt_indirection + { + ParamRef *n = makeNode(ParamRef); + n->number = $1; + n->fields = NIL; + n->indirection = $2; + $$ = (Node *)n; + } | TRUE_P { A_Const *n = makeNode(A_Const); @@ -5791,14 +5891,6 @@ AexprConst: Iconst } ; -ParamNo: PARAM opt_indirection - { - $$ = makeNode(ParamNo); - $$->number = $1; - $$->indirection = $2; - } - ; - Iconst: ICONST { $$ = $1; }; Sconst: SCONST { $$ = $1; }; UserId: ColId { $$ = $1; }; @@ -5866,7 +5958,6 @@ unreserved_keyword: | AGGREGATE { $$ = "aggregate"; } | ALTER { $$ = "alter"; } | AT { $$ = "at"; } - | AUTHORIZATION { $$ = "authorization"; } | BACKWARD { $$ = "backward"; } | BEFORE { $$ = "before"; } | BEGIN_TRANS { $$ = "begin"; } @@ -5883,7 +5974,6 @@ unreserved_keyword: | COMMITTED { $$ = "committed"; } | CONSTRAINTS { $$ = "constraints"; } | COPY { $$ = "copy"; } - | CREATE { $$ = "create"; } | CREATEDB { $$ = "createdb"; } | CREATEUSER { $$ = "createuser"; } | CURSOR { $$ = "cursor"; } @@ -5909,7 +5999,6 @@ unreserved_keyword: | FORWARD { $$ = "forward"; } | FUNCTION { $$ = "function"; } | GLOBAL { $$ = "global"; } - | GRANT { $$ = "grant"; } | HANDLER { $$ = "handler"; } | HOUR_P { $$ = "hour"; } | IMMEDIATE { $$ = "immediate"; } @@ -6061,7 +6150,8 @@ col_name_keyword: * - thomas 2000-11-28 */ func_name_keyword: - BETWEEN { $$ = "between"; } + AUTHORIZATION { $$ = "authorization"; } + | BETWEEN { $$ = "between"; } | BINARY { $$ = "binary"; } | CROSS { $$ = "cross"; } | FREEZE { $$ = "freeze"; } @@ -6104,6 +6194,7 @@ reserved_keyword: | COLLATE { $$ = "collate"; } | COLUMN { $$ = "column"; } | CONSTRAINT { $$ = "constraint"; } + | CREATE { $$ = "create"; } | CURRENT_DATE { $$ = "current_date"; } | CURRENT_TIME { $$ = "current_time"; } | CURRENT_TIMESTAMP { $$ = "current_timestamp"; } @@ -6120,6 +6211,7 @@ reserved_keyword: | FOR { $$ = "for"; } | FOREIGN { $$ = "foreign"; } | FROM { $$ = "from"; } + | GRANT { $$ = "grant"; } | GROUP { $$ = "group"; } | HAVING { $$ = "having"; } | INITIALLY { $$ = "initially"; } @@ -6189,7 +6281,7 @@ static Node * makeTypeCast(Node *arg, TypeName *typename) { /* - * If arg is an A_Const or ParamNo, just stick the typename into the + * If arg is an A_Const, just stick the typename into the * field reserved for it --- unless there's something there already! * (We don't want to collapse x::type1::type2 into just x::type2.) * Otherwise, generate a TypeCast node. @@ -6200,12 +6292,6 @@ makeTypeCast(Node *arg, TypeName *typename) ((A_Const *) arg)->typename = typename; return arg; } - else if (IsA(arg, ParamNo) && - ((ParamNo *) arg)->typename == NULL) - { - ((ParamNo *) arg)->typename = typename; - return arg; - } else { TypeCast *n = makeNode(TypeCast); diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index d6ad0a07ea1..dc939a71801 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 2f1eda4a9bf..6e8868f8ea6 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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) { diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 6409ef3226a..73cd89fd3d8 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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; + + /*---------- + * 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.) + *---------- + */ + switch (numnames) + { + case 1: + { + char *name = strVal(lfirst(cref->fields)); - basenode = ParseNestedFuncOrColumn(pstate, att, precedence); - return transformIndirection(pstate, basenode, att->indirection); -} + /* 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; -static Node * -transformIdent(ParseState *pstate, Ident *ident, int precedence) -{ - Node *result = NULL; - int sublevels_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)); - /* - * try to find the ident as a relation ... but not if subscripts - * appear - */ - if (ident->indirection == NIL && - refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL) - { - ident->isRel = TRUE; - result = (Node *) ident; - } + /* Whole-row reference? */ + if (strcmp(name2, "*") == 0) + { + rv = makeNode(RangeVar); + rv->relname = name1; + rv->inhOpt = INH_DEFAULT; + node = (Node *) rv; + break; + } - if (result == NULL || precedence == EXPR_COLUMN_FIRST) - { - /* try to find the ident as a column */ - Node *var = colnameToVar(pstate, ident->name); + /* 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)))); - if (var != NULL) + /* 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: { - ident->isRel = FALSE; - result = transformIndirection(pstate, var, ident->indirection); + 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)); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 6dc7b440fa8..ee1bf6c0578 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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 - * - * 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. + * Parse a function call * - * 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. + * 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. * - * 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 */ - + fdresult = func_get_detail(funcname, fargs, nargs, oid_array, + &funcid, &rettype, &retset, + &true_oid_array); + if (fdresult == FUNCDETAIL_COERCION) + { /* - * 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. + * We can do it as a trivial coercion. coerce_type can handle + * these cases, so why duplicate code... */ - funcid = F_SETEVAL; - rettype = toid; - retset = true; - true_oid_array = oid_array; + return coerce_type(pstate, lfirst(fargs), + oid_array[0], rettype, -1); } - else + if (fdresult != FUNCDETAIL_NORMAL) { - 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. + * Oops. Time to die. + * + * If we are dealing with the attribute notation rel.function, + * give an error message that is appropriate for that case. */ - 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"); - } + 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 */ @@ -471,25 +349,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *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; } /* diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index be825c26f9e..8b259e97c1d 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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); diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 1609c89ce07..5cf868a4d12 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -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) { diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index f5791298f31..a4b78165178 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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; |