heap.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,069 行 · 第 1/4 页
C
2,069 行
(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(listptr2, rawConstraints) { Constraint *cdef2 = (Constraint *) lfirst(listptr2); if (cdef2 == cdef || cdef2->contype != CONSTR_CHECK || cdef2->raw_expr == NULL || cdef2->name == NULL) continue; if (strcmp(cdef2->name, ccname) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("check constraint \"%s\" already exists", ccname))); } } else { bool success; do { List *listptr2; /* * Generate a name that does not conflict with * pre-existing constraints, nor with any auto-generated * names so far. */ ccname = GenerateConstraintName(CONSTRAINT_RELATION, RelationGetRelid(rel), RelationGetNamespace(rel), &constr_name_ctr); /* * Check against other new constraints, in case the user * has specified a name that looks like an auto-generated * name. */ success = true; foreach(listptr2, rawConstraints) { Constraint *cdef2 = (Constraint *) lfirst(listptr2); if (cdef2 == cdef || cdef2->contype != CONSTR_CHECK || cdef2->raw_expr == NULL || cdef2->name == NULL) continue; if (strcmp(cdef2->name, ccname) == 0) { success = false; break; } } } while (!success); } /* * Transform raw parsetree to executable expression. */ expr = transformExpr(pstate, cdef->raw_expr); /* * Make sure it yields a boolean result. */ expr = coerce_to_boolean(pstate, expr, "CHECK"); /* * Make sure no outside relations are referred to. */ if (length(pstate->p_rtable) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("only table \"%s\" can be referenced in check constraint", relname))); /* * No subplans or aggregates, either... */ if (pstate->p_hasSubLinks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in check constraint"))); if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in check constraint"))); /* * Constraints are evaluated with execQual, which expects an * implicit-AND list, so convert expression to implicit-AND form. * (We could go so far as to convert to CNF, but that's probably * overkill...) */ expr = (Node *) make_ands_implicit((Expr *) expr); /* * OK, store it. */ StoreRelCheck(rel, ccname, nodeToString(expr)); numchecks++; } /* * 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);}/* * 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_openr(RelationRelationName, 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(RelationGetRelid(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_openr(ConstraintRelationName, RowExclusiveLock); /* Use the index to scan only constraints of the target relation */ ScanKeyEntryInitialize(&key[0], 0x0, Anum_pg_constraint_conrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); conscan = systable_beginscan(conrel, ConstraintRelidIndex, 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 = RelationGetRelid(conrel); 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. */static voidRemoveStatistics(Relation rel, AttrNumber attnum){ Relation pgstatistic; SysScanDesc scan; ScanKeyData key[2]; int nkeys; HeapTuple tuple; pgstatistic = heap_openr(StatisticRelationName, RowExclusiveLock); ScanKeyEntryInitialize(&key[0], 0x0, Anum_pg_statistic_starelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); if (attnum == 0) nkeys = 1; else { ScanKeyEntryInitialize(&key[1], 0x0, Anum_pg_statistic_staattnum, F_INT2EQ, Int16GetDatum(attnum)); nkeys = 2; } scan = systable_beginscan(pgstatistic, StatisticRelidAttnumIndex, 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; List *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 = lfirsto(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); /* * Drop any buffers associated with this index. If they're dirty, * they're just dropped without bothering to flush to disk. */ DropRelationBuffers(currentIndex); /* Now truncate the actual data and set blocks to zero */ smgrtruncate(DEFAULT_SMGR, currentIndex, 0); currentIndex->rd_nblocks = 0; currentIndex->rd_targblock = InvalidBlockNumber; /* 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 the specified relation. * * This is not transaction-safe! There is another, transaction-safe * implementation in commands/cluster.c. We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. */voidheap_truncate(Oid rid){ Relation rel; Oid toastrelid; /* Open relation for processing, and grab exclusive access on it. */ rel = heap_open(rid, AccessExclusiveLock); /* Don't allow truncate on tables that are referenced by foreign keys */ heap_truncate_check_FKs(rel); /* * Release any buffers associated with this relation. If they're * dirty, they're just dropped without bothering to flush to disk. */ DropRelationBuffers(rel); /* Now truncate the actual data and set blocks to zero */ smgrtruncate(DEFAULT_SMGR, rel, 0); rel->rd_nblocks = 0; rel->rd_targblock = InvalidBlockNumber; /* If this relation has indexes, truncate the indexes too */ RelationTruncateIndexes(rid); /* If it has a toast table, recursively truncate that too */ toastrelid = rel->rd_rel->reltoastrelid; if (OidIsValid(toastrelid)) heap_truncate(toastrelid); /* * 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 relation that's 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 relation. */voidheap_truncate_check_FKs(Relation rel){ Oid relid = RelationGetRelid(rel); ScanKeyData key; Relation fkeyRel; SysScanDesc fkeyScan; HeapTuple tuple; /* * Fast path: if the relation has no triggers, it surely has no FKs * either. */ if (rel->rd_rel->reltriggers == 0) return; /* * Otherwise, must scan pg_constraint. Right now, this is a seqscan * because there is no available index on confrelid. */ fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_constraint_confrelid, F_OIDEQ, ObjectIdGetDatum(relid)); fkeyScan = systable_beginscan(fkeyRel, NULL, false, SnapshotNow, 1, &key); while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); if (con->contype == CONSTRAINT_FOREIGN && con->conrelid != relid) 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), RelationGetRelationName(rel), NameStr(con->conname)))); } systable_endscan(fkeyScan); heap_close(fkeyRel, AccessShareLock);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?