analyze.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,258 行 · 第 1/5 页
C
2,258 行
{ found = true; break; } } if (found) { /* found column in the new table; force it to be NOT NULL */ if (constraint->contype == CONSTR_PRIMARY) column->is_not_null = TRUE; } else if (SystemAttributeByName(key, cxt->hasoids) != NULL) { /* * column will be a system column in the new table, so * accept it. System columns can't ever be null, so no * need to worry about PRIMARY/NOT NULL constraint. */ found = true; } else if (cxt->inhRelations) { /* try inherited tables */ List *inher; foreach(inher, cxt->inhRelations) { RangeVar *inh = lfirst(inher); Relation rel; int count; Assert(IsA(inh, RangeVar)); rel = heap_openrv(inh, AccessShareLock); if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("inherited relation \"%s\" is not a table", inh->relname))); for (count = 0; count < rel->rd_att->natts; count++) { Form_pg_attribute inhattr = rel->rd_att->attrs[count]; char *inhname = NameStr(inhattr->attname); if (inhattr->attisdropped) continue; if (strcmp(key, inhname) == 0) { found = true; /* * We currently have no easy way to force an * inherited column to be NOT NULL at * creation, if its parent wasn't so already. * We leave it to DefineIndex to fix things up * in this case. */ break; } } heap_close(rel, NoLock); if (found) break; } } else if (OidIsValid(cxt->relOid)) { /* ALTER TABLE case: does column already exist? */ HeapTuple atttuple; atttuple = SearchSysCacheAttName(cxt->relOid, key); if (HeapTupleIsValid(atttuple)) { found = true; /* * If it's not already NOT NULL, leave it to * DefineIndex to fix later. */ ReleaseSysCache(atttuple); } } if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", key))); /* Check for PRIMARY KEY(foo, foo) */ foreach(columns, index->indexParams) { iparam = (IndexElem *) lfirst(columns); if (iparam->name && strcmp(key, iparam->name) == 0) { if (index->primary) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" appears twice in primary key constraint", key))); else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" appears twice in unique constraint", key))); } } /* OK, add it to the index definition */ iparam = makeNode(IndexElem); iparam->name = pstrdup(key); iparam->expr = NULL; iparam->opclass = NIL; index->indexParams = lappend(index->indexParams, iparam); } indexlist = lappend(indexlist, index); } /* * Scan the index list and remove any redundant index specifications. * This can happen if, for instance, the user writes UNIQUE PRIMARY * KEY. A strict reading of SQL92 would suggest raising an error * instead, but that strikes me as too anal-retentive. - tgl * 2001-02-14 * * XXX in ALTER TABLE case, it'd be nice to look for duplicate * pre-existing indexes, too. */ cxt->alist = NIL; if (cxt->pkey != NULL) { /* Make sure we keep the PKEY index in preference to others... */ cxt->alist = makeList1(cxt->pkey); } while (indexlist != NIL) { index = lfirst(indexlist); /* if it's pkey, it's already in cxt->alist */ if (index != cxt->pkey) { bool keep = true; List *priorlist; foreach(priorlist, cxt->alist) { IndexStmt *priorindex = lfirst(priorlist); if (equal(index->indexParams, priorindex->indexParams)) { /* * If the prior index is as yet unnamed, and this one * is named, then transfer the name to the prior * index. This ensures that if we have named and * unnamed constraints, we'll use (at least one of) * the names for the index. */ if (priorindex->idxname == NULL) priorindex->idxname = index->idxname; keep = false; break; } } if (keep) cxt->alist = lappend(cxt->alist, index); } indexlist = lnext(indexlist); } /* * Finally, select unique names for all not-previously-named indices, * and display NOTICE messages. * * XXX in ALTER TABLE case, we fail to consider name collisions against * pre-existing indexes. */ foreach(indexlist, cxt->alist) { index = lfirst(indexlist); if (index->idxname == NULL && index->indexParams != NIL) { iparam = (IndexElem *) lfirst(index->indexParams); /* we should never see an expression item here */ Assert(iparam->expr == NULL); index->idxname = CreateIndexName(cxt->relation->relname, iparam->name, "key", cxt->alist); } if (index->idxname == NULL) /* should not happen */ elog(ERROR, "failed to make implicit index name"); ereport(NOTICE, (errmsg("%s / %s%s will create implicit index \"%s\" for table \"%s\"", cxt->stmtType, (strcmp(cxt->stmtType, "ALTER TABLE") == 0) ? "ADD " : "", (index->primary ? "PRIMARY KEY" : "UNIQUE"), index->idxname, cxt->relation->relname))); }}static voidtransformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, bool isAddConstraint){ if (cxt->fkconstraints == NIL) return; /* * For ALTER TABLE ADD CONSTRAINT, nothing to do. For CREATE TABLE or * ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD CONSTRAINT * command to execute after the basic command is complete. * * Note: the ADD CONSTRAINT command must also execute after any index * creation commands. Thus, this should run after * transformIndexConstraints, so that the CREATE INDEX commands are * already in cxt->alist. */ if (!isAddConstraint) { AlterTableStmt *alterstmt = makeNode(AlterTableStmt); List *fkclist; alterstmt->subtype = 'c'; /* preprocessed add constraint */ alterstmt->relation = cxt->relation; alterstmt->name = NULL; alterstmt->def = (Node *) cxt->fkconstraints; /* Don't need to scan the table contents in this case */ foreach(fkclist, cxt->fkconstraints) { FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist); fkconstraint->skip_validation = true; } cxt->alist = lappend(cxt->alist, (Node *) alterstmt); }}/* * transformIndexStmt - * transforms the qualification of the index statement */static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt){ Query *qry; RangeTblEntry *rte = NULL; List *l; qry = makeNode(Query); qry->commandType = CMD_UTILITY; /* take care of the where clause */ if (stmt->whereClause) { /* * Put the parent table into the rtable so that the WHERE clause * can refer to its fields without qualification. Note that this * only works if the parent table already exists --- so we can't * easily support predicates on indexes created implicitly by * CREATE TABLE. Fortunately, that's not necessary. */ rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); /* no to join list, yes to namespace */ addRTEtoQuery(pstate, rte, false, true); stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, "WHERE"); } /* take care of any index expressions */ foreach(l, stmt->indexParams) { IndexElem *ielem = (IndexElem *) lfirst(l); if (ielem->expr) { /* Set up rtable as for predicate, see notes above */ if (rte == NULL) { rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); /* no to join list, yes to namespace */ addRTEtoQuery(pstate, rte, false, true); } ielem->expr = transformExpr(pstate, ielem->expr); /* * We check only that the result type is legitimate; this is * for consistency with what transformWhereClause() checks for * the predicate. DefineIndex() will make more checks. */ if (expression_returns_set(ielem->expr)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("index expression may not return a set"))); } } qry->hasSubLinks = pstate->p_hasSubLinks; stmt->rangetable = pstate->p_rtable; qry->utilityStmt = (Node *) stmt; return qry;}/* * transformRuleStmt - * transform a Create Rule Statement. The actions is a list of parse * trees which is transformed into a list of query trees. */static Query *transformRuleStmt(ParseState *pstate, RuleStmt *stmt, List **extras_before, List **extras_after){ Query *qry; RangeTblEntry *oldrte; RangeTblEntry *newrte; qry = makeNode(Query); qry->commandType = CMD_UTILITY; qry->utilityStmt = (Node *) stmt; /* * To avoid deadlock, make sure the first thing we do is grab * AccessExclusiveLock on the target relation. This will be needed by * DefineQueryRewrite(), and we don't want to grab a lesser lock * beforehand. We don't need to hold a refcount on the relcache * entry, however. */ heap_close(heap_openrv(stmt->relation, AccessExclusiveLock), NoLock); /* * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to * 2. Set up their RTEs in the main pstate for use in parsing the * rule qualification. */ Assert(pstate->p_rtable == NIL); oldrte = addRangeTableEntry(pstate, stmt->relation, makeAlias("*OLD*", NIL), false, true); newrte = addRangeTableEntry(pstate, stmt->relation, makeAlias("*NEW*", NIL), false, true); /* Must override addRangeTableEntry's default access-check flags */ oldrte->checkForRead = false; newrte->checkForRead = false; /* * They must be in the namespace too for lookup purposes, but only add * the one(s) that are relevant for the current kind of rule. In an * UPDATE rule, quals must refer to OLD.field or NEW.field to be * unambiguous, but there's no need to be so picky for INSERT & * DELETE. (Note we marked the RTEs "inFromCl = true" above to allow * unqualified references to their fields.) We do not add them to the * joinlist. */ switch (stmt->event) { case CMD_SELECT: addRTEtoQuery(pstate, oldrte, false, true); break; case CMD_UPDATE: addRTEtoQuery(pstate, oldrte, false, true); addRTEtoQuery(pstate, newrte, false, true); break; case CMD_INSERT: addRTEtoQuery(pstate, newrte, false, true); break; case CMD_DELETE: addRTEtoQuery(pstate, oldrte, false, true); break; default: elog(ERROR, "unrecognized event type: %d", (int) stmt->event); break; } /* take care of the where clause */ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, "WHERE"); if (length(pstate->p_rtable) != 2) /* naughty, naughty... */ ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("rule WHERE condition may not contain references to other relations"))); /* aggregates not allowed (but subselects are okay) */ if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("rule WHERE condition may not contain aggregate functions"))); /* save info about sublinks in where clause */ qry->hasSubLinks = pstate->p_hasSubLinks; /* * 'instead nothing' rules with a qualification need a query * rangetable so the rewrite handler can add the negated rule * qualification to the original query. We create a query with the new * command type CMD_NOTHING here that is treated specially by the * rewrite system. */ if (stmt->actions == NIL) { Query *nothing_qry = makeNode(Query); nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ stmt->actions = makeList1(nothing_qry); } else { List *oldactions; List *newactions = NIL; /* * transform each statement, like parse_sub_analyze() */ foreach(oldactions, stmt->actions) { Node *action = (Node *) lfirst(oldactions); ParseState *sub_pstate = make_parsestate(pstate->parentParseState); Query *sub_qry, *top_subqry; bool has_old, has_new; /* * Set up OLD/NEW in the rtable for this statement. The * entries are marked not inFromCl because we don't want them * to be referred to by unqualified field names nor "*" in the * rule actions. We must add them to the namespace, however, * or they won't be accessible at all. We decide later * whether to put them in the joinlist. */ oldrte = addRangeTableEntry(sub_pstate, stmt->relation, makeAlias("*OLD*", NIL), false, false); newrte = addRangeTableEntry(sub_pstate, stmt->relation, makeAlias("*NEW*", NIL), false, false);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?