forked from huawei/openGauss-server
1328 lines
40 KiB
C++
1328 lines
40 KiB
C++
/*
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
|
|
*
|
|
* openGauss is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
*
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* ---------------------------------------------------------------------------------------
|
|
*
|
|
* tcap_drop.cpp
|
|
* Routines to support Timecapsule `Recyclebin-based query, restore`.
|
|
* We use Tr prefix to indicate it in following coding.
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/storage/tcap/tcap_drop.cpp
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "pgstat.h"
|
|
#include "access/reloptions.h"
|
|
#include "access/sysattr.h"
|
|
#include "access/xlog.h"
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/heap.h"
|
|
#include "catalog/index.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/objectaccess.h"
|
|
#include "catalog/pg_collation_fn.h"
|
|
#include "catalog/pg_collation.h"
|
|
#include "catalog/pg_constraint.h"
|
|
#include "catalog/pg_conversion_fn.h"
|
|
#include "catalog/pg_conversion.h"
|
|
#include "catalog/pg_depend.h"
|
|
#include "catalog/pg_extension_data_source.h"
|
|
#include "catalog/pg_extension.h"
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
#include "catalog/pg_foreign_server.h"
|
|
#include "catalog/pg_job.h"
|
|
#include "catalog/pg_language.h"
|
|
#include "catalog/pg_largeobject.h"
|
|
#include "catalog/pg_object.h"
|
|
#include "catalog/pg_opclass.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_opfamily.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_recyclebin.h"
|
|
#include "catalog/pg_rewrite.h"
|
|
#include "catalog/pg_rlspolicy.h"
|
|
#include "catalog/pg_synonym.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "catalog/pg_trigger.h"
|
|
#include "catalog/pg_ts_config.h"
|
|
#include "catalog/pg_ts_dict.h"
|
|
#include "catalog/pg_ts_parser.h"
|
|
#include "catalog/pg_ts_template.h"
|
|
#include "catalog/pgxc_class.h"
|
|
#include "catalog/storage.h"
|
|
#include "commands/comment.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "commands/directory.h"
|
|
#include "commands/extension.h"
|
|
#include "commands/proclang.h"
|
|
#include "commands/schemacmds.h"
|
|
#include "commands/seclabel.h"
|
|
#include "commands/sec_rls_cmds.h"
|
|
#include "commands/tablecmds.h"
|
|
#include "commands/tablespace.h"
|
|
#include "commands/trigger.h"
|
|
#include "commands/typecmds.h"
|
|
#include "executor/node/nodeModifyTable.h"
|
|
#include "rewrite/rewriteRemove.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/predicate.h"
|
|
#include "storage/smgr/relfilenode.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/relcache.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "utils/syscache.h"
|
|
|
|
#include "storage/tcap.h"
|
|
#include "storage/tcap_impl.h"
|
|
|
|
static void TrRenameClass(TrObjDesc *baseDesc, ObjectAddress *object, const char *newName)
|
|
{
|
|
Relation rel;
|
|
HeapTuple tup;
|
|
HeapTuple newtup;
|
|
char rbname[NAMEDATALEN];
|
|
Datum values[Natts_pg_class] = { 0 };
|
|
bool nulls[Natts_pg_class] = { false };
|
|
bool replaces[Natts_pg_class] = { false };
|
|
Oid relid = object->objectId;
|
|
errno_t rc = EOK;
|
|
|
|
if (newName) {
|
|
rc = strncpy_s(rbname, NAMEDATALEN, newName, strlen(newName));
|
|
securec_check(rc, "\0", "\0");
|
|
} else {
|
|
TrGenObjName(rbname, object->classId, relid);
|
|
}
|
|
|
|
replaces[Anum_pg_class_relname - 1] = true;
|
|
values[Anum_pg_class_relname - 1] = CStringGetDatum(rbname);
|
|
|
|
rel = heap_open(RelationRelationId, RowExclusiveLock);
|
|
|
|
tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tup)) {
|
|
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relid)));
|
|
}
|
|
|
|
newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces);
|
|
|
|
simple_heap_update(rel, &newtup->t_self, newtup);
|
|
|
|
CatalogUpdateIndexes(rel, newtup);
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
heap_freetuple_ext(newtup);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
}
|
|
|
|
static void TrRenameCommon(TrObjDesc *baseDesc, ObjectAddress *object, Oid relid, int natts, int oidAttrNum,
|
|
Oid oidIndexId, char *objTag)
|
|
{
|
|
Relation rel;
|
|
HeapTuple tup;
|
|
HeapTuple newtup;
|
|
char rbname[NAMEDATALEN];
|
|
Datum *values = (Datum *)palloc0(sizeof(Datum) * natts);
|
|
bool *nulls = (bool *)palloc0(sizeof(bool) * natts);
|
|
bool *replaces = (bool *)palloc0(sizeof(bool) * natts);
|
|
ScanKeyData skey[1];
|
|
SysScanDesc sd;
|
|
|
|
TrGenObjName(rbname, object->classId, object->objectId);
|
|
|
|
replaces[oidAttrNum - 1] = true;
|
|
values[oidAttrNum - 1] = CStringGetDatum(rbname);
|
|
|
|
rel = heap_open(relid, RowExclusiveLock);
|
|
|
|
ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->objectId));
|
|
sd = systable_beginscan(rel, oidIndexId, true, NULL, 1, skey);
|
|
|
|
tup = systable_getnext(sd);
|
|
if (!HeapTupleIsValid(tup)) {
|
|
pfree(values);
|
|
pfree(nulls);
|
|
pfree(replaces);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NO_DATA_FOUND), errmsg("could not find tuple for %s %u", objTag, object->objectId)));
|
|
}
|
|
|
|
newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces);
|
|
|
|
simple_heap_update(rel, &newtup->t_self, newtup);
|
|
|
|
CatalogUpdateIndexes(rel, newtup);
|
|
|
|
heap_freetuple_ext(newtup);
|
|
|
|
systable_endscan(sd);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
pfree(values);
|
|
pfree(nulls);
|
|
pfree(replaces);
|
|
}
|
|
|
|
static void TrDeleteBaseid(Oid baseid)
|
|
{
|
|
Relation rbRel;
|
|
SysScanDesc sd;
|
|
ScanKeyData skey[1];
|
|
HeapTuple tup;
|
|
|
|
rbRel = heap_open(RecyclebinRelationId, RowExclusiveLock);
|
|
|
|
ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcybaseid, BTEqualStrategyNumber, F_INT8EQ, ObjectIdGetDatum(baseid));
|
|
|
|
sd = systable_beginscan(rbRel, RecyclebinBaseidIndexId, true, NULL, 1, skey);
|
|
while (HeapTupleIsValid(tup = systable_getnext(sd))) {
|
|
simple_heap_delete(rbRel, &tup->t_self);
|
|
}
|
|
|
|
systable_endscan(sd);
|
|
heap_close(rbRel, RowExclusiveLock);
|
|
|
|
/*
|
|
* CommandCounterIncrement here to ensure that preceding changes are all
|
|
* visible to the next deletion step.
|
|
*/
|
|
CommandCounterIncrement();
|
|
}
|
|
|
|
static void TrDeleteId(Oid id)
|
|
{
|
|
Relation rbRel;
|
|
SysScanDesc sd;
|
|
ScanKeyData skey[1];
|
|
HeapTuple tup;
|
|
|
|
rbRel = heap_open(RecyclebinRelationId, RowExclusiveLock);
|
|
|
|
ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(id));
|
|
|
|
sd = systable_beginscan(rbRel, RecyclebinIdIndexId, true, NULL, 1, skey);
|
|
if (HeapTupleIsValid(tup = systable_getnext(sd))) {
|
|
simple_heap_delete(rbRel, &tup->t_self);
|
|
}
|
|
|
|
systable_endscan(sd);
|
|
heap_close(rbRel, RowExclusiveLock);
|
|
|
|
/*
|
|
* CommandCounterIncrement here to ensure that preceding changes are all
|
|
* visible to the next deletion step.
|
|
*/
|
|
CommandCounterIncrement();
|
|
}
|
|
|
|
static inline bool TrNeedLogicDrop(const ObjectAddress *object)
|
|
{
|
|
return object->rbDropMode == RB_DROP_MODE_LOGIC;
|
|
}
|
|
|
|
static bool TrCanPurge(const TrObjDesc *baseDesc, const ObjectAddress *object, char relKind)
|
|
{
|
|
Relation depRel;
|
|
SysScanDesc sd;
|
|
HeapTuple tuple;
|
|
ScanKeyData key[3];
|
|
int nkeys;
|
|
bool found = false;
|
|
|
|
if (relKind != RELKIND_INDEX && relKind != RELKIND_GLOBAL_INDEX && relKind != RELKIND_RELATION) {
|
|
return false;
|
|
}
|
|
|
|
depRel = heap_open(DependRelationId, AccessShareLock);
|
|
|
|
ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->classId));
|
|
ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->objectId));
|
|
nkeys = 2;
|
|
if (object->objectSubId != 0) {
|
|
ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ,
|
|
Int32GetDatum(object->objectSubId));
|
|
nkeys = 3;
|
|
}
|
|
|
|
sd = systable_beginscan(depRel, DependDependerIndexId, true, NULL, nkeys, key);
|
|
while (HeapTupleIsValid(tuple = systable_getnext(sd))) {
|
|
Form_pg_depend depForm = (Form_pg_depend)GETSTRUCT(tuple);
|
|
if (depForm->refclassid == RelationRelationId && depForm->refobjid == baseDesc->relid) {
|
|
if (depForm->deptype != DEPENDENCY_AUTO) {
|
|
found = false;
|
|
break;
|
|
}
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
systable_endscan(sd);
|
|
heap_close(depRel, AccessShareLock);
|
|
return found;
|
|
}
|
|
|
|
static void TrDoDropIndex(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
Assert(object->objectSubId == 0);
|
|
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrObjDesc desc = *baseDesc;
|
|
|
|
if (!TR_IS_BASE_OBJ(baseDesc, object)) {
|
|
/* Deletion lock already accquired before single object drop. */
|
|
Relation rel = relation_open(object->objectId, NoLock);
|
|
|
|
TrDescInit(rel, &desc, RB_OPER_DROP, TrGetObjType(RelationGetNamespace(rel), RELKIND_INDEX),
|
|
TrCanPurge(baseDesc, object, RelationGetRelkind(rel)));
|
|
relation_close(rel, NoLock);
|
|
|
|
TrDescWrite(&desc);
|
|
}
|
|
|
|
TrRenameClass(baseDesc, object, desc.name);
|
|
} else {
|
|
index_drop(object->objectId, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTable(TrObjDesc *baseDesc, ObjectAddress *object, char relKind)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrObjDesc desc;
|
|
|
|
if (object->objectSubId != 0 || relKind == RELKIND_VIEW || relKind == RELKIND_COMPOSITE_TYPE ||
|
|
relKind == RELKIND_FOREIGN_TABLE) {
|
|
TrRenameClass(baseDesc, object, NULL);
|
|
return;
|
|
}
|
|
|
|
desc = *baseDesc;
|
|
if (!TR_IS_BASE_OBJ(baseDesc, object)) {
|
|
/* Deletion lock already accquired before single object drop. */
|
|
Relation rel = relation_open(object->objectId, NoLock);
|
|
|
|
TrDescInit(rel, &desc, RB_OPER_DROP, TrGetObjType(InvalidOid, relKind),
|
|
TrCanPurge(baseDesc, object, relKind));
|
|
relation_close(rel, NoLock);
|
|
|
|
TrDescWrite(&desc);
|
|
}
|
|
|
|
TrRenameClass(baseDesc, object, desc.name);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* relation_open() must be before the heap_drop_with_catalog(). If you reload
|
|
* relation after drop, it may cause other exceptions during the drop process.
|
|
*/
|
|
if (object->objectSubId != 0)
|
|
RemoveAttributeById(object->objectId, object->objectSubId);
|
|
else
|
|
heap_drop_with_catalog(object->objectId);
|
|
|
|
/*
|
|
* IMPORANT: The relation must not be reloaded after heap_drop_with_catalog()
|
|
* is executed to drop this relation.If you reload relation after drop, it may
|
|
* cause other exceptions during the drop process
|
|
*/
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropType(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TypeRelationId, Natts_pg_type, Anum_pg_type_typname, TypeOidIndexId, "type");
|
|
} else {
|
|
RemoveTypeById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropConstraint(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ConstraintRelationId, Natts_pg_constraint, Anum_pg_constraint_conname,
|
|
ConstraintOidIndexId, "constraint");
|
|
} else {
|
|
RemoveConstraintById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTrigger(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TriggerRelationId, Natts_pg_trigger, Anum_pg_trigger_tgname, TriggerOidIndexId,
|
|
"trigger");
|
|
} else {
|
|
RemoveTriggerById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropRewrite(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as rule-based view requires that origin rule name preserved. */
|
|
} else {
|
|
RemoveRewriteRuleById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropAttrdef(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveAttrDefaultById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropProc(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ProcedureRelationId, Natts_pg_proc, Anum_pg_proc_proname, ProcedureOidIndexId,
|
|
"procedure");
|
|
} else {
|
|
RemoveFunctionById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropCast(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
DropCastById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropCollation(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, CollationRelationId, Natts_pg_collation, Anum_pg_collation_collname,
|
|
CollationOidIndexId, "collation");
|
|
} else {
|
|
RemoveCollationById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropConversion(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ConversionRelationId, Natts_pg_conversion, Anum_pg_conversion_conname,
|
|
ConversionOidIndexId, "conversion");
|
|
} else {
|
|
RemoveConversionById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static void TrDoDropProceduralLanguage(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, LanguageRelationId, Natts_pg_language, Anum_pg_language_lanname,
|
|
LanguageOidIndexId, "language");
|
|
} else {
|
|
DropProceduralLanguageById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropLargeObject(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
LargeObjectDrop(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropOperator(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, OperatorRelationId, Natts_pg_operator, Anum_pg_operator_oprname,
|
|
OperatorOidIndexId, "operator");
|
|
} else {
|
|
RemoveOperatorById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropOpClass(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, OperatorClassRelationId, Natts_pg_opclass, Anum_pg_opclass_opcname,
|
|
OpclassOidIndexId, "opclass");
|
|
} else {
|
|
RemoveOpClassById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropOpFamily(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, OperatorFamilyRelationId, Natts_pg_opfamily, Anum_pg_opfamily_opfname,
|
|
OpfamilyOidIndexId, "opfamily");
|
|
} else {
|
|
RemoveOpFamilyById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static void TrDoDropAmOp(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveAmOpEntryById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropAmProc(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveAmProcEntryById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropSchema(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, NamespaceRelationId, Natts_pg_namespace, Anum_pg_namespace_nspname,
|
|
NamespaceOidIndexId, "namespace");
|
|
} else {
|
|
RemoveSchemaById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTSParser(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TSParserRelationId, Natts_pg_ts_parser, Anum_pg_ts_parser_prsname,
|
|
TSParserOidIndexId, "ts parser");
|
|
} else {
|
|
RemoveTSParserById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTSDictionary(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TSDictionaryRelationId, Natts_pg_ts_dict, Anum_pg_ts_dict_dictname,
|
|
TSDictionaryOidIndexId, "ts dictionary");
|
|
} else {
|
|
RemoveTSDictionaryById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTSTemplate(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TSTemplateRelationId, Natts_pg_ts_template, Anum_pg_ts_template_tmplname,
|
|
TSTemplateOidIndexId, "ts template");
|
|
} else {
|
|
RemoveTSTemplateById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropTSConfiguration(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, TSConfigRelationId, Natts_pg_ts_config, Anum_pg_ts_config_cfgname,
|
|
TSConfigOidIndexId, "ts configuration");
|
|
} else {
|
|
RemoveTSConfigurationById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropForeignDataWrapper(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ForeignDataWrapperRelationId, Natts_pg_foreign_data_wrapper,
|
|
Anum_pg_foreign_data_wrapper_fdwname, ForeignDataWrapperOidIndexId, "foreign data wrapper");
|
|
} else {
|
|
RemoveForeignDataWrapperById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropForeignServer(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ForeignServerRelationId, Natts_pg_foreign_server,
|
|
Anum_pg_foreign_server_srvname, ForeignServerOidIndexId, "foreign server");
|
|
} else {
|
|
RemoveForeignServerById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropUserMapping(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveUserMappingById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static void TrDoDropDefaultACL(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveDefaultACLById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropPgxcClass(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemovePgxcClass(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropExtension(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, ExtensionRelationId, Natts_pg_extension, Anum_pg_extension_extname,
|
|
ExtensionOidIndexId, "extension");
|
|
} else {
|
|
RemoveExtensionById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropDataSource(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, DataSourceRelationId, Natts_pg_extension_data_source,
|
|
Anum_pg_extension_data_source_srcname, DataSourceOidIndexId, "extension data source");
|
|
} else {
|
|
RemoveDataSourceById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropDirectory(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, PgDirectoryRelationId, Natts_pg_directory, Anum_pg_directory_directory_name,
|
|
PgDirectoryOidIndexId, "directory");
|
|
} else {
|
|
RemoveDirectoryById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropRlsPolicy(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, RlsPolicyRelationId, Natts_pg_rlspolicy, Anum_pg_rlspolicy_polname,
|
|
PgRlspolicyOidIndex, "rlspolicy");
|
|
} else {
|
|
RemoveRlsPolicyById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropJob(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
/* nothing to do as no name attribute in system catalog */
|
|
} else {
|
|
RemoveJobById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void TrDoDropSynonym(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
if (TrNeedLogicDrop(object)) {
|
|
TrRenameCommon(baseDesc, object, PgSynonymRelationId, Natts_pg_synonym, Anum_pg_synonym_synname,
|
|
SynonymOidIndexId, "synonym");
|
|
} else {
|
|
RemoveSynonymById(object->objectId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* doDeletion: delete a single object
|
|
* return false if logic deleted,
|
|
* return true if physical deleted,
|
|
*/
|
|
static void TrDoDrop(TrObjDesc *baseDesc, ObjectAddress *object)
|
|
{
|
|
switch (getObjectClass(object)) {
|
|
case OCLASS_CLASS: {
|
|
char relKind = get_rel_relkind(object->objectId);
|
|
if (relKind == RELKIND_INDEX) {
|
|
TrDoDropIndex(baseDesc, object);
|
|
} else {
|
|
/*
|
|
* We use a unified entry for others:
|
|
* RELKIND_RELATION, RELKIND_SEQUENCE,
|
|
* RELKIND_TOASTVALUE, RELKIND_VIEW,
|
|
* RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE
|
|
*/
|
|
TrDoDropTable(baseDesc, object, relKind);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OCLASS_TYPE:
|
|
TrDoDropType(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_CONSTRAINT:
|
|
TrDoDropConstraint(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_TRIGGER:
|
|
TrDoDropTrigger(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_REWRITE:
|
|
TrDoDropRewrite(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_DEFAULT:
|
|
TrDoDropAttrdef(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_PROC:
|
|
TrDoDropProc(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_CAST:
|
|
TrDoDropCast(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_COLLATION:
|
|
TrDoDropCollation(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_CONVERSION:
|
|
TrDoDropConversion(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_LANGUAGE:
|
|
TrDoDropProceduralLanguage(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_LARGEOBJECT:
|
|
TrDoDropLargeObject(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_OPERATOR:
|
|
TrDoDropOperator(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_OPCLASS:
|
|
TrDoDropOpClass(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_OPFAMILY:
|
|
TrDoDropOpFamily(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_AMOP:
|
|
TrDoDropAmOp(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_AMPROC:
|
|
TrDoDropAmProc(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_SCHEMA:
|
|
TrDoDropSchema(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_TSPARSER:
|
|
TrDoDropTSParser(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_TSDICT:
|
|
TrDoDropTSDictionary(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_TSTEMPLATE:
|
|
TrDoDropTSTemplate(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_TSCONFIG:
|
|
TrDoDropTSConfiguration(baseDesc, object);
|
|
break;
|
|
|
|
/*
|
|
* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE intentionally not
|
|
* handled here
|
|
*/
|
|
|
|
case OCLASS_FDW:
|
|
TrDoDropForeignDataWrapper(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_FOREIGN_SERVER:
|
|
TrDoDropForeignServer(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_USER_MAPPING:
|
|
TrDoDropUserMapping(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_DEFACL:
|
|
TrDoDropDefaultACL(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_PGXC_CLASS:
|
|
TrDoDropPgxcClass(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_EXTENSION:
|
|
TrDoDropExtension(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_DATA_SOURCE:
|
|
TrDoDropDataSource(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_DIRECTORY:
|
|
TrDoDropDirectory(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_RLSPOLICY:
|
|
TrDoDropRlsPolicy(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_PG_JOB:
|
|
if ((IS_PGXC_COORDINATOR && !IsConnFromCoord()) || (g_instance.role == VSINGLENODE))
|
|
TrDoDropJob(baseDesc, object);
|
|
break;
|
|
|
|
case OCLASS_SYNONYM:
|
|
TrDoDropSynonym(baseDesc, object);
|
|
break;
|
|
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized object class: %u", object->classId)));
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* deleteOneObject: delete a single object for TrDrop.
|
|
*
|
|
* *depRel is the already-open pg_depend relation.
|
|
*/
|
|
static void TrDropOneObject(TrObjDesc *baseDesc, ObjectAddress *object, Relation *depRel)
|
|
{
|
|
ScanKeyData key[3];
|
|
int nkeys;
|
|
SysScanDesc scan;
|
|
HeapTuple tup;
|
|
|
|
/* DROP hook of the objects being removed */
|
|
if (object_access_hook) {
|
|
ObjectAccessDrop dropArg;
|
|
|
|
dropArg.dropflags = PERFORM_DELETION_INVALID;
|
|
InvokeObjectAccessHook(OAT_DROP, object->classId, object->objectId, object->objectSubId, &dropArg);
|
|
}
|
|
|
|
/*
|
|
* Delete the object itself, in an object-type-dependent way.
|
|
*
|
|
* We used to do this after removing the outgoing dependency links, but it
|
|
* seems just as reasonable to do it beforehand. In the concurrent case
|
|
* we *must *do it in this order, because we can't make any transactional
|
|
* updates before calling doDeletion() --- they'd get committed right
|
|
* away, which is not cool if the deletion then fails.
|
|
*/
|
|
TrDoDrop(baseDesc, object);
|
|
|
|
/*
|
|
* In logical drop mode, we will keep all related system entries, including
|
|
* linked entries such as pg_depend records. It is done!
|
|
*/
|
|
if (TrNeedLogicDrop(object)) {
|
|
/*
|
|
* CommandCounterIncrement here to ensure that preceding changes are all
|
|
* visible to the next deletion step.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
/*
|
|
* Logic Drop done!
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* In physical drop mode, we continue to remove all related system entries.
|
|
*/
|
|
|
|
/*
|
|
* Now remove any pg_depend records that link from this object to others.
|
|
* (Any records linking to this object should be gone already.)
|
|
*
|
|
* When dropping a whole object (subId = 0), remove all pg_depend records
|
|
* for its sub-objects too.
|
|
*/
|
|
ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->classId));
|
|
ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(object->objectId));
|
|
if (object->objectSubId != 0) {
|
|
ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ,
|
|
Int32GetDatum(object->objectSubId));
|
|
nkeys = 3;
|
|
} else
|
|
nkeys = 2;
|
|
|
|
scan = systable_beginscan(*depRel, DependDependerIndexId, true, NULL, nkeys, key);
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan))) {
|
|
simple_heap_delete(*depRel, &tup->t_self);
|
|
}
|
|
|
|
systable_endscan(scan);
|
|
|
|
/*
|
|
* Delete shared dependency references related to this object. Again, if
|
|
* subId = 0, remove records for sub-objects too.
|
|
*/
|
|
deleteSharedDependencyRecordsFor(object->classId, object->objectId, object->objectSubId);
|
|
|
|
/*
|
|
* Delete any comments or security labels associated with this object.
|
|
* (This is a convenient place to do these things, rather than having
|
|
* every object type know to do it.)
|
|
*/
|
|
DeleteComments(object->objectId, object->classId, object->objectSubId);
|
|
DeleteSecurityLabel(object);
|
|
|
|
/*
|
|
* CommandCounterIncrement here to ensure that preceding changes are all
|
|
* visible to the next deletion step.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
/*
|
|
* Physical Drop done!
|
|
*/
|
|
}
|
|
|
|
static bool TrObjIsInList(const ObjectAddresses *targetObjects, const ObjectAddress *thisobj)
|
|
{
|
|
ObjectAddress *item = NULL;
|
|
|
|
for (int i = 0; i < targetObjects->numrefs; i++) {
|
|
item = targetObjects->refs + i;
|
|
if (TrObjIsEqual(thisobj, item)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static ObjectAddress *TrFindIdxInTarget(ObjectAddresses *targetObjects, ObjectAddress *item)
|
|
{
|
|
ObjectAddress *thisobj = NULL;
|
|
|
|
for (int i = 0; i < targetObjects->numrefs; i++) {
|
|
thisobj = targetObjects->refs + i;
|
|
if (TrObjIsEqual(item, thisobj)) {
|
|
return thisobj;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* output: refthisobjs
|
|
*/
|
|
static void TrFindAllSubObjs(Relation depRel, const ObjectAddress *refobj, ObjectAddresses *refthisobjs)
|
|
{
|
|
SysScanDesc sd;
|
|
HeapTuple tuple;
|
|
ScanKeyData key[3];
|
|
int nkeys;
|
|
|
|
ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(refobj->classId));
|
|
ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(refobj->objectId));
|
|
nkeys = 2;
|
|
if (refobj->objectSubId != 0) {
|
|
ScanKeyInit(&key[2], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ,
|
|
Int32GetDatum(refobj->objectSubId));
|
|
nkeys = 3;
|
|
}
|
|
|
|
sd = systable_beginscan(depRel, DependReferenceIndexId, true, NULL, nkeys, key);
|
|
while (HeapTupleIsValid(tuple = systable_getnext(sd))) {
|
|
Form_pg_depend depForm = (Form_pg_depend)GETSTRUCT(tuple);
|
|
|
|
/* add the refs to list */
|
|
add_object_address_ext(depForm->classid, depForm->objid, depForm->objsubid, depForm->deptype, refthisobjs);
|
|
}
|
|
|
|
systable_endscan(sd);
|
|
return;
|
|
}
|
|
|
|
static void TrTagPhyDeleteSubObjs(Relation depRel, ObjectAddresses *targetObjects, ObjectAddress *thisobj)
|
|
{
|
|
ObjectAddress *item = NULL;
|
|
|
|
ObjectAddresses *refthisobjs = new_object_addresses();
|
|
|
|
/* Tag this obj RB_DROP_MODE_PHYSICAL */
|
|
thisobj->rbDropMode = RB_DROP_MODE_PHYSICAL;
|
|
|
|
/* Find all sub objs refered to this obj */
|
|
TrFindAllSubObjs(depRel, thisobj, refthisobjs);
|
|
|
|
for (int i = 0; i < refthisobjs->numrefs; i++) {
|
|
item = refthisobjs->refs + i;
|
|
|
|
/* the item must exists in targetObjects. */
|
|
item = TrFindIdxInTarget(targetObjects, item);
|
|
if (item == NULL || item->rbDropMode == RB_DROP_MODE_PHYSICAL) {
|
|
continue;
|
|
}
|
|
TrTagPhyDeleteSubObjs(depRel, targetObjects, item);
|
|
}
|
|
|
|
free_object_addresses(refthisobjs);
|
|
return;
|
|
}
|
|
|
|
static bool TrNeedPhyDelete(Relation depRel, ObjectAddresses *targetObjects, ObjectAddress *thisobj)
|
|
{
|
|
ObjectAddress *item = NULL;
|
|
ObjectAddresses *refobjs = new_object_addresses();
|
|
bool result = false;
|
|
|
|
/* Find all objs this obj refered */
|
|
TrFindAllRefObjs(depRel, thisobj, refobjs);
|
|
|
|
/* Step 1: tag refobjs of thisobj, return directly if ALL refobjs not need physical drop. */
|
|
for (int i = 0; i < refobjs->numrefs; i++) {
|
|
item = refobjs->refs + i;
|
|
if (!TrObjIsInList(targetObjects, item)) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!result) {
|
|
free_object_addresses(refobjs);
|
|
return result;
|
|
}
|
|
|
|
/* Step 2: tag refobjs with 'i' deptype to physical drop. */
|
|
for (int i = 0; i < refobjs->numrefs; i++) {
|
|
item = refobjs->refs + i;
|
|
if (item->deptype == 'i') {
|
|
item = TrFindIdxInTarget(targetObjects, item);
|
|
Assert(item != NULL);
|
|
if (item->rbDropMode == RB_DROP_MODE_PHYSICAL) {
|
|
continue;
|
|
}
|
|
TrTagPhyDeleteSubObjs(depRel, targetObjects, item);
|
|
}
|
|
}
|
|
|
|
free_object_addresses(refobjs);
|
|
return result;
|
|
}
|
|
|
|
static void TrResetDropMode(const ObjectAddresses *targetObjects, const ObjectAddress *baseObj)
|
|
{
|
|
ObjectAddress *thisobj = NULL;
|
|
|
|
for (int i = 0; i < targetObjects->numrefs; i++) {
|
|
thisobj = targetObjects->refs + i;
|
|
if (TrObjIsEqual(thisobj, baseObj)) {
|
|
thisobj->rbDropMode = RB_DROP_MODE_LOGIC;
|
|
continue;
|
|
}
|
|
thisobj->rbDropMode = RB_DROP_MODE_INVALID;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void TrTagDependentObjects(Relation depRel, ObjectAddresses *targetObjects, const ObjectAddress *baseObj)
|
|
{
|
|
ObjectAddress *thisobj = NULL;
|
|
|
|
TrResetDropMode(targetObjects, baseObj);
|
|
for (int i = 0; i < targetObjects->numrefs; i++) {
|
|
thisobj = targetObjects->refs + i;
|
|
if (TrDropModeIsAlreadySet(thisobj)) {
|
|
continue;
|
|
}
|
|
|
|
if (TrNeedPhyDelete(depRel, targetObjects, thisobj)) {
|
|
TrTagPhyDeleteSubObjs(depRel, targetObjects, thisobj);
|
|
} else {
|
|
thisobj->rbDropMode = RB_DROP_MODE_LOGIC;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool TrCheckRecyclebinDrop(const DropStmt *stmt, ObjectAddresses *objects)
|
|
{
|
|
Relation depRel;
|
|
bool rbDrop = false;
|
|
|
|
/* No work if no objects... */
|
|
if (objects->numrefs <= 0)
|
|
return false;
|
|
|
|
if (/*
|
|
* Disable Recyclebin-based-Drop when target object is not OBJECT_TABLE, or
|
|
*/
|
|
stmt->removeType != OBJECT_TABLE ||
|
|
/* in concurrent drop mode, or */
|
|
stmt->concurrent ||
|
|
/* with purge option, or */
|
|
stmt->purge ||
|
|
/* multi objects drop. */
|
|
list_length(stmt->objects) != 1) {
|
|
return false;
|
|
}
|
|
|
|
if (!NeedTrComm(objects->refs->objectId)) {
|
|
return false;
|
|
}
|
|
|
|
depRel = heap_open(DependRelationId, AccessShareLock);
|
|
rbDrop = !TrNeedPhyDelete(depRel, objects, &objects->refs[0]);
|
|
heap_close(depRel, AccessShareLock);
|
|
|
|
return rbDrop;
|
|
}
|
|
|
|
void TrDrop(const DropStmt* drop, const ObjectAddresses *objects, DropBehavior behavior)
|
|
{
|
|
Relation depRel;
|
|
Relation baseRel;
|
|
TrObjDesc baseDesc;
|
|
ObjectAddresses *targetObjects = NULL;
|
|
ObjectAddress *baseObj = objects->refs;
|
|
|
|
/*
|
|
* We save some cycles by opening pg_depend just once and passing the
|
|
* Relation pointer down to all the recursive deletion steps.
|
|
*/
|
|
depRel = heap_open(DependRelationId, RowExclusiveLock);
|
|
|
|
/*
|
|
* Construct a list of objects to delete (ie, the given objects plus
|
|
* everything directly or indirectly dependent on them). Note that
|
|
* because we pass the whole objects list as pendingObjects context, we
|
|
* won't get a failure from trying to delete an object that is internally
|
|
* dependent on another one in the list; we'll just skip that object and
|
|
* delete it when we reach its owner.
|
|
*/
|
|
targetObjects = new_object_addresses();
|
|
|
|
/*
|
|
* Acquire deletion lock on each target object. (Ideally the caller
|
|
* has done this already, but many places are sloppy about it.)
|
|
*/
|
|
AcquireDeletionLock(baseObj, PERFORM_DELETION_INVALID);
|
|
|
|
/*
|
|
* Finds all subobjects that reference the base table recursively.
|
|
*/
|
|
findDependentObjects(baseObj, DEPFLAG_ORIGINAL, NULL, /* empty stack */
|
|
targetObjects, objects, &depRel);
|
|
ereport(LOG, (errmsg("Delete object %u/%u/%d", baseObj->classId, baseObj->objectId, baseObj->objectSubId)));
|
|
|
|
/*
|
|
* Check if deletion is allowed, and report about cascaded deletes.
|
|
*
|
|
* If there's exactly one object being deleted, report it the same way as
|
|
* in performDeletion(), else we have to be vaguer.
|
|
*/
|
|
reportDependentObjects(targetObjects, behavior, NOTICE, baseObj);
|
|
|
|
/*
|
|
* Tag all subobjects' drop mode: LOGIC_DROP, PYHSICAL_DROP.
|
|
*/
|
|
TrTagDependentObjects(depRel, targetObjects, baseObj);
|
|
|
|
/*
|
|
* Initialize the baseDesc structure so that the logic dropped subobjects
|
|
* can be correctly processed when renamed or placed in recycle bin. Notice
|
|
* that base object already locked.
|
|
*/
|
|
baseRel = relation_open(baseObj->objectId, NoLock);
|
|
TrDescInit(baseRel, &baseDesc, RB_OPER_DROP, RB_OBJ_TABLE, true, true);
|
|
baseDesc.id = baseDesc.baseid = TrDescWrite(&baseDesc);
|
|
TrUpdateBaseid(&baseDesc);
|
|
relation_close(baseRel, NoLock);
|
|
|
|
Oid relid = RelationGetRelid(baseRel);
|
|
UpdatePgObjectChangecsn(relid, baseRel->rd_rel->relkind);
|
|
|
|
/*
|
|
* Drop all the objects in the proper order.
|
|
*/
|
|
for (int i = 0; i < targetObjects->numrefs; i++) {
|
|
ObjectAddress *thisobj = targetObjects->refs + i;
|
|
TrDropOneObject(&baseDesc, thisobj, &depRel);
|
|
}
|
|
|
|
/* And clean up */
|
|
free_object_addresses(targetObjects);
|
|
heap_close(depRel, RowExclusiveLock);
|
|
}
|
|
|
|
void TrDoPurgeObjectDrop(TrObjDesc *desc)
|
|
{
|
|
ObjectAddresses *objects;
|
|
ObjectAddress obj;
|
|
|
|
objects = new_object_addresses();
|
|
|
|
obj.classId = RelationRelationId;
|
|
obj.objectId = desc->relid;
|
|
obj.objectSubId = 0;
|
|
add_exact_object_address(&obj, objects);
|
|
|
|
performMultipleDeletions(objects, DROP_CASCADE, PERFORM_DELETION_INVALID);
|
|
|
|
if (desc->type == RB_OBJ_TABLE) {
|
|
TrDeleteBaseid(desc->baseid);
|
|
} else { /* RB_OBJ_INDEX */
|
|
TrDeleteId(desc->id);
|
|
}
|
|
|
|
free_object_addresses(objects);
|
|
return;
|
|
}
|
|
|
|
/* TIMECAPSULE TABLE { table_name } TO BEFORE DROP [RENAME TO new_tablename] */
|
|
void TrRestoreDrop(const TimeCapsuleStmt *stmt)
|
|
{
|
|
TrObjDesc desc;
|
|
ObjectAddress obj;
|
|
Relation rel;
|
|
|
|
desc.relid = 0;
|
|
TrOperFetch(stmt->relation, RB_OBJ_TABLE, &desc, RB_OPER_RESTORE_DROP);
|
|
if (desc.relid != 0 && (desc.type == RB_OBJ_TABLE)) {
|
|
stmt->relation->relname = desc.name;
|
|
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
|
if (rel->rd_tam_type == TAM_HEAP) {
|
|
heap_close(rel, NoLock);
|
|
elog(ERROR, "timecapsule does not support astore yet");
|
|
return;
|
|
}
|
|
heap_close(rel, NoLock);
|
|
}
|
|
|
|
desc.authid = GetUserId();
|
|
TrOperPrep(&desc, RB_OPER_RESTORE_DROP);
|
|
|
|
obj.classId = RelationRelationId;
|
|
obj.objectId = desc.relid;
|
|
obj.objectSubId = 0;
|
|
|
|
TrRenameClass(&desc, &obj, stmt->new_relname ? stmt->new_relname : desc.originname);
|
|
|
|
TrDeleteBaseid(desc.baseid);
|
|
|
|
return;
|
|
}
|