📄 heap.c
字号:
ListCell *cell2; ccname = cdef->name; /* Check against pre-existing constraints */ if (ConstraintNameIsUsed(CONSTRAINT_RELATION, RelationGetRelid(rel), RelationGetNamespace(rel), ccname)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("constraint \"%s\" for relation \"%s\" already exists", ccname, RelationGetRelationName(rel)))); /* Check against other new constraints */ /* Needed because we don't do CommandCounterIncrement in loop */ foreach(cell2, checknames) { if (strcmp((char *) lfirst(cell2), ccname) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("check constraint \"%s\" already exists", ccname))); } } else { /* * When generating a name, we want to create "tab_col_check" for a * column constraint and "tab_check" for a table constraint. We * no longer have any info about the syntactic positioning of the * constraint phrase, so we approximate this by seeing whether the * expression references more than one column. (If the user * played by the rules, the result is the same...) * * Note: pull_var_clause() doesn't descend into sublinks, but we * eliminated those above; and anyway this only needs to be an * approximate answer. */ List *vars; char *colname; vars = pull_var_clause(expr, false); /* eliminate duplicates */ vars = list_union(NIL, vars); if (list_length(vars) == 1) colname = get_attname(RelationGetRelid(rel), ((Var *) linitial(vars))->varattno); else colname = NULL; ccname = ChooseConstraintName(RelationGetRelationName(rel), colname, "check", RelationGetNamespace(rel), checknames); } /* save name for future checks */ checknames = lappend(checknames, ccname); /* * OK, store it. */ StoreRelCheck(rel, ccname, nodeToString(expr)); numchecks++; cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint)); cooked->contype = CONSTR_CHECK; cooked->name = ccname; cooked->attnum = 0; cooked->expr = expr; cookedConstraints = lappend(cookedConstraints, cooked); } /* * Update the count of constraints in the relation's pg_class tuple. We do * this even if there was no change, in order to ensure that an SI update * message is sent out for the pg_class tuple, which will force other * backends to rebuild their relcache entries for the rel. (This is * critical if we added defaults but not constraints.) */ SetRelationNumChecks(rel, numchecks); return cookedConstraints;}/* * Update the count of constraints in the relation's pg_class tuple. * * Caller had better hold exclusive lock on the relation. * * An important side effect is that a SI update message will be sent out for * the pg_class tuple, which will force other backends to rebuild their * relcache entries for the rel. Also, this backend will rebuild its * own relcache entry at the next CommandCounterIncrement. */static voidSetRelationNumChecks(Relation rel, int numchecks){ Relation relrel; HeapTuple reltup; Form_pg_class relStruct; relrel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(RelationGetRelid(rel)), 0, 0, 0); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(rel)); relStruct = (Form_pg_class) GETSTRUCT(reltup); if (relStruct->relchecks != numchecks) { relStruct->relchecks = numchecks; simple_heap_update(relrel, &reltup->t_self, reltup); /* keep catalog indexes current */ CatalogUpdateIndexes(relrel, reltup); } else { /* Skip the disk update, but force relcache inval anyway */ CacheInvalidateRelcache(rel); } heap_freetuple(reltup); heap_close(relrel, RowExclusiveLock);}/* * Take a raw default and convert it to a cooked format ready for * storage. * * Parse state should be set up to recognize any vars that might appear * in the expression. (Even though we plan to reject vars, it's more * user-friendly to give the correct error message than "unknown var".) * * If atttypid is not InvalidOid, coerce the expression to the specified * type (and typmod atttypmod). attname is only needed in this case: * it is used in the error message, if any. */Node *cookDefault(ParseState *pstate, Node *raw_default, Oid atttypid, int32 atttypmod, char *attname){ Node *expr; Assert(raw_default != NULL); /* * Transform raw parsetree to executable expression. */ expr = transformExpr(pstate, raw_default); /* * Make sure default expr does not refer to any vars. */ if (contain_var_clause(expr)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot use column references in default expression"))); /* * It can't return a set either. */ if (expression_returns_set(expr)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("default expression must not return a set"))); /* * No subplans or aggregates, either... */ if (pstate->p_hasSubLinks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in default expression"))); if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in default expression"))); /* * Coerce the expression to the correct type and typmod, if given. This * should match the parser's processing of non-defaulted expressions --- * see updateTargetListEntry(). */ if (OidIsValid(atttypid)) { Oid type_id = exprType(expr); expr = coerce_to_target_type(pstate, expr, type_id, atttypid, atttypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (expr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of type %s" " but default expression is of type %s", attname, format_type_be(atttypid), format_type_be(type_id)), errhint("You will need to rewrite or cast the expression."))); } return expr;}/* * Removes all constraints on a relation that match the given name. * * It is the responsibility of the calling function to acquire a suitable * lock on the relation. * * Returns: The number of constraints removed. */intRemoveRelConstraints(Relation rel, const char *constrName, DropBehavior behavior){ int ndeleted = 0; Relation conrel; SysScanDesc conscan; ScanKeyData key[1]; HeapTuple contup; /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); /* Use the index to scan only constraints of the target relation */ ScanKeyInit(&key[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true, SnapshotNow, 1, key); /* * Scan over the result set, removing any matching entries. */ while ((contup = systable_getnext(conscan)) != NULL) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup); if (strcmp(NameStr(con->conname), constrName) == 0) { ObjectAddress conobj; conobj.classId = ConstraintRelationId; conobj.objectId = HeapTupleGetOid(contup); conobj.objectSubId = 0; performDeletion(&conobj, behavior); ndeleted++; } } /* Clean up after the scan */ systable_endscan(conscan); heap_close(conrel, RowExclusiveLock); return ndeleted;}/* * RemoveStatistics --- remove entries in pg_statistic for a rel or column * * If attnum is zero, remove all entries for rel; else remove only the one * for that column. */voidRemoveStatistics(Oid relid, AttrNumber attnum){ Relation pgstatistic; SysScanDesc scan; ScanKeyData key[2]; int nkeys; HeapTuple tuple; pgstatistic = heap_open(StatisticRelationId, RowExclusiveLock); ScanKeyInit(&key[0], Anum_pg_statistic_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); if (attnum == 0) nkeys = 1; else { ScanKeyInit(&key[1], Anum_pg_statistic_staattnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(attnum)); nkeys = 2; } scan = systable_beginscan(pgstatistic, StatisticRelidAttnumIndexId, true, SnapshotNow, nkeys, key); while (HeapTupleIsValid(tuple = systable_getnext(scan))) simple_heap_delete(pgstatistic, &tuple->t_self); systable_endscan(scan); heap_close(pgstatistic, RowExclusiveLock);}/* * RelationTruncateIndexes - truncate all * indexes associated with the heap relation to zero tuples. * * The routine will truncate and then reconstruct the indexes on * the relation specified by the heapId parameter. */static voidRelationTruncateIndexes(Oid heapId){ Relation heapRelation; ListCell *indlist; /* * Open the heap rel. We need grab no lock because we assume * heap_truncate is holding an exclusive lock on the heap rel. */ heapRelation = heap_open(heapId, NoLock); /* Ask the relcache to produce a list of the indexes of the rel */ foreach(indlist, RelationGetIndexList(heapRelation)) { Oid indexId = lfirst_oid(indlist); Relation currentIndex; IndexInfo *indexInfo; /* Open the index relation */ currentIndex = index_open(indexId); /* Obtain exclusive lock on it, just to be sure */ LockRelation(currentIndex, AccessExclusiveLock); /* Fetch info needed for index_build */ indexInfo = BuildIndexInfo(currentIndex); /* Now truncate the actual file (and discard buffers) */ RelationTruncate(currentIndex, 0); /* Initialize the index and rebuild */ index_build(heapRelation, currentIndex, indexInfo); /* * index_build will close both the heap and index relations (but not * give up the locks we hold on them). We're done with this index, * but we must re-open the heap rel. */ heapRelation = heap_open(heapId, NoLock); } /* Finish by closing the heap rel again */ heap_close(heapRelation, NoLock);}/* * heap_truncate * * This routine deletes all data within all the specified relations. * * This is not transaction-safe! There is another, transaction-safe * implementation in commands/tablecmds.c. We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. */voidheap_truncate(List *relids){ List *relations = NIL; ListCell *cell; /* Open relations for processing, and grab exclusive access on each */ foreach(cell, relids) { Oid rid = lfirst_oid(cell); Relation rel; Oid toastrelid; rel = heap_open(rid, AccessExclusiveLock); relations = lappend(relations, rel); /* If there is a toast table, add it to the list too */ toastrelid = rel->rd_rel->reltoastrelid; if (OidIsValid(toastrelid)) { rel = heap_open(toastrelid, AccessExclusiveLock); relations = lappend(relations, rel); } } /* Don't allow truncate on tables that are referenced by foreign keys */ heap_truncate_check_FKs(relations, true); /* OK to do it */ foreach(cell, relations) { Relation rel = lfirst(cell); /* Truncate the actual file (and discard buffers) */ RelationTruncate(rel, 0); /* If this relation has indexes, truncate the indexes too */ RelationTruncateIndexes(RelationGetRelid(rel)); /* * Close the relation, but keep exclusive lock on it until commit. */ heap_close(rel, NoLock); }}/* * heap_truncate_check_FKs * Check for foreign keys referencing a list of relations that * are to be truncated * * We disallow such FKs (except self-referential ones) since the whole point * of TRUNCATE is to not scan the individual rows to be thrown away. * * This is split out so it can be shared by both implementations of truncate. * Caller should already hold a suitable lock on the relations. * * tempTables is only used to select an appropriate error message. */voidheap_truncate_check_FKs(List *relations, bool tempTables){ List *oids = NIL; ListCell *cell; Relation fkeyRel; SysScanDesc fkeyScan; HeapTuple tuple; /* * Build a list of OIDs of the interesting relations. * * If a relation has no triggers, then it can neither have FKs nor be * referenced by a FK from another table, so we can ignore it. */ foreach(cell, relations) { Relation rel = lfirst(cell); if (rel->rd_rel->reltriggers != 0) oids = lappend_oid(oids, RelationGetRelid(rel)); } /* * Fast path: if no relation has triggers, none has FKs either. */ if (oids == NIL) return; /* * Otherwise, must scan pg_constraint. Right now, it is a seqscan because * there is no available index on confrelid. */ fkeyRel = heap_open(ConstraintRelationId, AccessShareLock); fkeyScan = systable_beginscan(fkeyRel, InvalidOid, false, SnapshotNow, 0, NULL); while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); /* Not a foreign key */ if (con->contype != CONSTRAINT_FOREIGN) continue; /* Not for one of our list of tables */ if (!list_member_oid(oids, con->confrelid)) continue; /* The referencer should be in our list too */ if (!list_member_oid(oids, con->conrelid)) { if (tempTables) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported ON COMMIT and foreign key combination"), errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\", but they do not have the same ON COMMIT setting.", get_rel_name(con->conrelid), get_rel_name(con->confrelid), NameStr(con->conname)))); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot truncate a table referenced in a foreign key constraint"), errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".", get_rel_name(con->conrelid), get_rel_name(con->confrelid), NameStr(con->conname)), errhint("Truncate table \"%s\" at the same time.", get_rel_name(con->conrelid)))); } } systable_endscan(fkeyScan); heap_close(fkeyRel, AccessShareLock);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -