diff --git a/src/bin/gs_cgroup/cgexcp.cpp b/src/bin/gs_cgroup/cgexcp.cpp index 585d67302..b75d8f6b9 100644 --- a/src/bin/gs_cgroup/cgexcp.cpp +++ b/src/bin/gs_cgroup/cgexcp.cpp @@ -34,7 +34,20 @@ #include #include "cgutil.h" - +/* + * macro name : EXCP_PARSE_KEY + * description : parse strings in an exception format and assign the parsed value to the variable val + * Note : The macro takes two arguments: p, which is the string to be parsed, and val, the variable to store + * the parsed result.Inside the macro, two pointer variables tmp and bad are declared and initialized + * to NULL.The strchr function is used to find the position of the character '=' within the string p. + * The resulting pointer is assigned to tmp. At the same time, the character '=' is replaced with '\0' + * to treat tmp as the key string.The strtoul function is used to convert the string pointed by tmp to + * an unsigned long integer, and the converted result is assigned to val.The code checks if the conversion + * result is valid. If the converted string is empty or contains invalid characters, an error message + * is printed to the standard error stream, indicating that the value is invalid. Then, it returns -1 to + * indicate an error.If no errors occur during the parsing process, the code continues with the + * subsequent instructions + */ #define EXCP_PARSE_KEY(p, val) \ { \ char *tmp = NULL, *bad = NULL; \ diff --git a/src/bin/gs_cgroup/cgexec.cpp b/src/bin/gs_cgroup/cgexec.cpp index 5554ac83f..23ac917aa 100644 --- a/src/bin/gs_cgroup/cgexec.cpp +++ b/src/bin/gs_cgroup/cgexec.cpp @@ -14,10 +14,10 @@ *------------------------------------------------------------------------- * * cgexec.cpp - * Cgroup configration file process functions + * Cgroup execute file place the processes in specific control groups (cgroups) and executed * * IDENTIFICATION - * src/bin/gs_cgroup/cgconf.cpp + * src/bin/gs_cgroup/cgexec.cpp * *------------------------------------------------------------------------- */ @@ -70,7 +70,13 @@ static int cgexec_update_class_cpuset(int cls, char* cpuset); static int cgexec_update_top_group_cpuset(int top, char* cpuset); /* update one group cpu cores */ static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset); - +/* + * function name : CheckBackendEnv + * description : check if the input environment variable input_env_value contains any dangerous characters + * return value : + * 0: normal + * 1: abnormal + */ int CheckBackendEnv(const char* input_env_value) { const int max_env_len = 1024; @@ -91,7 +97,13 @@ int CheckBackendEnv(const char* input_env_value) } return 0; } - +/* + * function name : CheckSystemSucess + * description : check the execution result of a system call + * return value : + * 0: bormal + * 1: abnormal + */ inline int CheckSystemSucess(pid_t status) { if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { @@ -182,6 +194,10 @@ int cgexec_check_cpuset_value(const char* clsset, const char* grpset) int clsstart, clsend; int grpstart, grpend; + /* sscanf_s is used to parse two strings, clsset and grpset, + * and store the parsed integer values in the variables clsstart, + * clsend, grpstart, and grpend. + */ errno_t ret = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); if (ret != 2) { fprintf(stderr, diff --git a/src/bin/gs_guc/cluster_config.cpp b/src/bin/gs_guc/cluster_config.cpp index 77b83c08e..416531466 100644 --- a/src/bin/gs_guc/cluster_config.cpp +++ b/src/bin/gs_guc/cluster_config.cpp @@ -323,7 +323,10 @@ int get_all_cndn_num() return count; } - +/* + *function name: get_all_gtm_num + *description : calculate the number of GTM (Global Transaction Manager) nodes that meet certain conditions + */ int get_all_gtm_num() { uint32 nodeidx = 0; diff --git a/src/gausskernel/storage/buffer/buf_init.cpp b/src/gausskernel/storage/buffer/buf_init.cpp index 6956b35d7..19af300ee 100644 --- a/src/gausskernel/storage/buffer/buf_init.cpp +++ b/src/gausskernel/storage/buffer/buf_init.cpp @@ -179,28 +179,22 @@ void InitBufferPool(void) Size BufferShmemSize(void) { Size size = 0; - - /* size of buffer descriptors */ + /* 缓冲区描述符的大小*/ size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(BufferDescPadded))); size = add_size(size, PG_CACHE_LINE_SIZE); - - /* size of data pages */ + /* 数据页面的大小 */ size = add_size(size, mul_size(TOTAL_BUFFER_NUM, BLCKSZ)); #ifdef __aarch64__ size = add_size(size, PG_CACHE_LINE_SIZE); #endif - /* size of stuff controlled by freelist.c */ + /* 由freelist.c所控制的东西的大小 */ size = add_size(size, StrategyShmemSize()); - - /* size of checkpoint sort array in bufmgr.c */ + /* 在bufmgr.c中的检查点排序数组的大小*/ size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(CkptSortItem))); - - /* size of candidate buffers */ + /* 候选缓冲区的大小 */ size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(Buffer))); - - /* size of candidate free map */ + /* 候选自由映射的大小 */ size = add_size(size, mul_size(TOTAL_BUFFER_NUM, sizeof(bool))); - return size; } diff --git a/src/gausskernel/storage/buffer/buf_table.cpp b/src/gausskernel/storage/buffer/buf_table.cpp index 2d69f0fd0..829b5f00a 100644 --- a/src/gausskernel/storage/buffer/buf_table.cpp +++ b/src/gausskernel/storage/buffer/buf_table.cpp @@ -52,16 +52,11 @@ Size BufTableShmemSize(int size) void InitBufTable(int size) { HASHCTL info; - - /* assume no locking is needed yet - * - * BufferTag maps to Buffer - */ + /*缓冲区标签映射到缓冲区*/ info.keysize = sizeof(BufferTag); info.entrysize = sizeof(BufferLookupEnt); info.hash = tag_hash; info.num_partitions = NUM_BUFFER_PARTITIONS; - t_thrd.storage_cxt.SharedBufHash = ShmemInitHash("Shared Buffer Lookup Table", size, size, &info, HASH_ELEM | HASH_FUNCTION | HASH_PARTITION); } diff --git a/src/gausskernel/storage/buffer/bufmgr.cpp b/src/gausskernel/storage/buffer/bufmgr.cpp index 1f384f92b..9281d4ba0 100644 --- a/src/gausskernel/storage/buffer/bufmgr.cpp +++ b/src/gausskernel/storage/buffer/bufmgr.cpp @@ -1679,41 +1679,28 @@ Buffer ReadBufferExtended(Relation reln, ForkNumber fork_num, BlockNumber block_ { bool hit = false; Buffer buf; - if (block_num == P_NEW) { STORAGE_SPACE_OPERATION(reln, BLCKSZ); } - - /* Open it at the smgr level if not already done */ + /* 以smgr(存储管理器)级别打开一个缓冲区 */ RelationOpenSmgr(reln); - - /* - * Reject attempts to read non-local temporary relations; we would be - * likely to get wrong data since we have no visibility into the owning - * session's local buffers. - */ - if (RELATION_IS_OTHER_TEMP(reln) && fork_num <= INIT_FORKNUM) + /*拒绝读取非局部临时关系的请求,因为可能会获得监控不到的错误数据 */ + if (RELATION_IS_OTHER_TEMP(reln) && fork_num <= INIT_FORKNUM) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); - - /* - * Read the buffer, and update pgstat counters to reflect a cache hit or - * miss. - */ + /*读取缓冲区,更新pgstat 数量反馈cache 命中与否情况*/ pgstat_count_buffer_read(reln); pgstatCountBlocksFetched4SessionLevel(); - if (RelationisEncryptEnable(reln)) { - reln->rd_smgr->encrypt = true; + reln->rd_smgr->encrypt = true; } - buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence, fork_num, + buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence, fork_num, block_num, mode, strategy, &hit, NULL); if (hit) { pgstat_count_buffer_hit(reln); } return buf; } - /* * ReadBufferWithoutRelcache -- like ReadBufferExtended, but doesn't require * a relcache entry for the relation. diff --git a/src/gausskernel/storage/freespace/freespace.cpp b/src/gausskernel/storage/freespace/freespace.cpp index 42b087e73..a771d7104 100644 --- a/src/gausskernel/storage/freespace/freespace.cpp +++ b/src/gausskernel/storage/freespace/freespace.cpp @@ -78,9 +78,8 @@ static void fsm_update_recursive(Relation rel, const FSMAddress& addr, uint8 new */ BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded) { - uint8 min_cat = fsm_space_needed_to_cat(spaceNeeded); - - return fsm_search(rel, min_cat); + uint8 min_cat = fsm_space_needed_to_cat(spaceNeeded);/*将所需的空间大小转换为一个最小类别(min_cat)的整数*/ + return fsm_search(rel, min_cat);/*在关系中查找具有至少min_cat类别的可用空间的页面,并返回该页面的块号*/ } /* @@ -94,19 +93,20 @@ BlockNumber GetPageWithFreeSpace(Relation rel, Size spaceNeeded) */ BlockNumber RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage, Size oldSpaceAvail, Size spaceNeeded) { + // 将旧可用空间大小转换为相应的类别 int old_cat = fsm_space_avail_to_cat(oldSpaceAvail); + // 将所需空间大小转换为相应的类别 int search_cat = fsm_space_needed_to_cat(spaceNeeded); FSMAddress addr; uint16 slot; int search_slot; - - /* Get the location of the FSM byte representing the heap block */ + // 获取表示堆块的FSM字节的位置 addr = fsm_get_location(oldPage, &slot); - + // 调用fsm_set_and_search函数,尝试在FSM中设置一个新的适合条件的页面,并返回其插槽号 search_slot = fsm_set_and_search(rel, addr, slot, (uint8)old_cat, (uint8)search_cat); /* - * If fsm_set_and_search found a suitable new block, return that. - * Otherwise, search as usual. + * 如果fsm_set_and_search找到了适合的新块,返回该新块。 + * 否则,按照通常的方式进行搜索。 */ if (search_slot != -1) return fsm_get_heap_blk(addr, (uint16)search_slot); @@ -123,13 +123,12 @@ BlockNumber RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage, Siz */ void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail) { - int new_cat = fsm_space_avail_to_cat(spaceAvail); - FSMAddress addr; - uint16 slot; - - /* Get the location of the FSM byte representing the heap block */ - addr = fsm_get_location(heapBlk, &slot); - + int new_cat = fsm_space_avail_to_cat(spaceAvail); // 将空闲空间大小转换为 FSM 分类 + FSMAddress addr; // FSM 地址 + uint16 slot; // 插槽号 + /* 获取表示堆块的 FSM 字节的位置 */ + addr = fsm_get_location(heapBlk, &slot); // 获取 FSM 地址和插槽号 + // 调用 fsm_set_and_search 函数,更新 FSM 信息 fsm_set_and_search(rel, addr, slot, (uint8)new_cat, 0); } @@ -142,26 +141,22 @@ void RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail) */ void UpdateFreeSpaceMap(Relation rel, BlockNumber startBlkNum, BlockNumber endBlkNum, Size freespace, bool search) { + // 将可用空间大小(freespace)转换为相应的类别(new_cat) int new_cat = fsm_space_avail_to_cat(freespace); FSMAddress addr; uint16 slot; BlockNumber blockNum; BlockNumber lastBlkOnPage; - blockNum = startBlkNum; - while (blockNum <= endBlkNum) { /* - * Find FSM address for this block; update tree all the way to the - * root. + * 找到这个块的FSM地址;逐级更新树直到根节点。 */ addr = fsm_get_location(blockNum, &slot); fsm_update_recursive(rel, addr, (uint8)new_cat, search); - /* - * Get the last block number on this FSM page. If that's greater - * than or equal to our endBlkNum, we're done. Otherwise, advance - * to the first block on the next page. + * 获取此FSM页面上的最后一个块号。如果该块号大于或等于endBlkNum,我们完成了更新。 + * 否则,前进到下一页的第一个块。 */ lastBlkOnPage = fsm_get_lastblckno(rel, addr); if (lastBlkOnPage >= endBlkNum) @@ -177,60 +172,52 @@ void UpdateFreeSpaceMap(Relation rel, BlockNumber startBlkNum, BlockNumber endBl void XLogRecordPageWithFreeSpace(const RelFileNode& rnode, BlockNumber heapBlk, Size spaceAvail) { /* - * FSM can not be read by physical location in recovery. It is possible to write on wrong places - * if the FSM fork is dropped and then allocated when replaying old xlog. - * Since FSM does not have to be totally accurate anyway, just skip it. + * 在恢复过程中无法根据物理位置读取FSM。如果在回放旧的XLOG时删除了FSM分支,然后重新分配了它,可能会在错误的位置写入。 + * 由于FSM不必完全准确,因此可以跳过它。 */ if (IsSegmentFileNode(rnode)) { return; } - + // 将可用空间大小转换为新的类别 int new_cat = fsm_space_avail_to_cat(spaceAvail); FSMAddress addr; uint16 slot; BlockNumber blkno; Buffer buf; Page page; - - /* Get the location of the FSM byte representing the heap block */ + // 获取表示堆块的FSM字节的位置 addr = fsm_get_location(heapBlk, &slot); blkno = fsm_logical_to_physical(addr); - - /* If the page doesn't exist already, extend */ + // 如果页面尚不存在,则扩展 buf = XLogReadBufferExtended(rnode, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR, NULL); - LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); - page = BufferGetPage(buf); if (PageIsNew(page)) PageInit(page, BLCKSZ, 0); - + // 设置新的可用类别 if (fsm_set_avail(page, (int)slot, (uint8)new_cat)) MarkBufferDirtyHint(buf, false); UnlockReleaseBuffer(buf); } /* - * GetRecordedFreePage - return the amount of free space on a particular page, - * according to the FSM. + * GetRecordedFreePage - 返回特定页面上的自由空间大小, + * 根据FSM(Free Space Map)确定。 */ Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk) { - FSMAddress addr; - uint16 slot; - Buffer buf; - uint8 cat; - - /* Get the location of the FSM byte representing the heap block */ - addr = fsm_get_location(heapBlk, &slot); - - buf = fsm_readbuf(rel, addr, false); - if (!BufferIsValid(buf)) - return 0; - cat = fsm_get_avail(BufferGetPage(buf), slot); - ReleaseBuffer(buf); - - return fsm_space_cat_to_avail(cat); + FSMAddress addr; // 声明FSM地址变量 + uint16 slot; // 声明槽位变量 + Buffer buf; // 声明缓冲区变量 + uint8 cat; // 声明类别变量 + /* 获取表示堆块的FSM字节位置 */ + addr = fsm_get_location(heapBlk, &slot); // 调用函数获取FSM位置信息 + buf = fsm_readbuf(rel, addr, false); // 读取FSM中的数据块到缓冲区 + if (!BufferIsValid(buf)) // 检查缓冲区是否有效 + return 0; // 如果无效,返回0表示无法获取空闲空间信息 + cat = fsm_get_avail(BufferGetPage(buf), slot); // 获取槽位相关的空闲空间信息 + ReleaseBuffer(buf); // 释放缓冲区 + return fsm_space_cat_to_avail(cat); // 将类别转换为实际可用空间大小并返回 } void XLogBlockTruncateRelFSM(Relation rel, BlockNumber nblocks) @@ -275,68 +262,49 @@ void FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks) FSMAddress first_removed_address; uint16 first_removed_slot; Buffer buf; - + // 打开关系的存储管理器 RelationOpenSmgr(rel); - /* - * If no FSM has been created yet for this relation, there's nothing to - * truncate. + * 如果该关系尚未创建Free Space Map(FSM),则无需截断。 */ if (!smgrexists(rel->rd_smgr, FSM_FORKNUM)) return; - - /* Get the location in the FSM of the first removed heap block */ + /* 获取第一个被移除堆块在FSM中的位置 */ first_removed_address = fsm_get_location(nblocks, &first_removed_slot); - /* - * Zero out the tail of the last remaining FSM page. If the slot - * representing the first removed heap block is at a page boundary, as the - * first slot on the FSM page that first_removed_address points to, we can - * just truncate that page altogether. + * 将最后一个剩余FSM页面的尾部清零。如果第一个被移除的堆块在FSM页面的边界上, + * 作为指向的第一个槽(slot)在FSM页面上的地址,我们可以将整个页面截断。 */ if (first_removed_slot > 0) { buf = fsm_readbuf(rel, first_removed_address, false); if (!BufferIsValid(buf)) - return; /* nothing to do; the FSM was already smaller */ + return; /* 无需处理,FSM已经更小了 */ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); - - /* NO EREPORT(ERROR) from here till changes are logged */ + /* 从此处到更改被记录的地方不会发生错误 */ START_CRIT_SECTION(); - fsm_truncate_avail(BufferGetPage(buf), (int)first_removed_slot); - /* - * Truncation of a relation is WAL-logged at a higher-level, and we - * will be called at WAL replay. But if checksums are enabled, we need - * to still write a WAL record to protect against a torn page, if the - * page is flushed to disk before the truncation WAL record. We cannot - * use MarkBufferDirtyHint here, because that will not dirty the page - * during recovery. + * 关系的截断在更高级别上记录到WAL中,在WAL回放时会调用我们。但是,如果启用了校验和, + * 我们仍然需要写入WAL记录,以保护免受破碎页面的影响,如果在截断WAL记录之前将页面刷新到磁盘上的话。 + * 在此处我们不能使用MarkBufferDirtyHint,因为在恢复时它不会使页面变脏。 */ MarkBufferDirty(buf); if (!t_thrd.xlog_cxt.InRecovery && RelationNeedsWAL(rel) && XLogHintBitIsNeeded()) { log_newpage_buffer(buf, false); } END_CRIT_SECTION(); - UnlockReleaseBuffer(buf); - new_nfsmblocks = fsm_logical_to_physical(first_removed_address) + 1; } else { new_nfsmblocks = fsm_logical_to_physical(first_removed_address); if (smgrnblocks(rel->rd_smgr, FSM_FORKNUM) <= new_nfsmblocks) - return; /* nothing to do; the FSM was already smaller */ + return; /* 无需处理,FSM已经更小了 */ } - - /* Truncate the unused FSM pages, and send smgr inval message */ + /* 截断未使用的FSM页面,并发送存储管理器的失效消息 */ smgrtruncate(rel->rd_smgr, FSM_FORKNUM, new_nfsmblocks); - /* - * We might as well update the local smgr_fsm_nblocks setting. - * smgrtruncate sent an smgr cache inval message, which will cause other - * backends to invalidate their copy of smgr_fsm_nblocks, and this one too - * at the next command boundary. But this ensures it isn't outright wrong - * until then. + * 我们也可以更新本地的smgr_fsm_nblocks设置。smgrtruncate发送了一个存储管理器缓存失效消息, + * 这将导致其他后端在下一个命令边界时使其拥有的smgr_fsm_nblocks无效。但是,这确保了在那之前它不会完全错误。 */ rel->rd_smgr->smgr_fsm_nblocks = new_nfsmblocks; } diff --git a/src/gausskernel/storage/tcap/tcap_drop.cpp b/src/gausskernel/storage/tcap/tcap_drop.cpp index a6cb8515c..f8cedd43b 100644 --- a/src/gausskernel/storage/tcap/tcap_drop.cpp +++ b/src/gausskernel/storage/tcap/tcap_drop.cpp @@ -1,1327 +1,1334 @@ -/* - * 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; -} +/* + * 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" + +//This function renames a class (relation) in the database +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); +} + +//This function is a common utility for renaming objects in the database. +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); +} + +//This function is used to delete entries from the recycle bin based on the given base ID. +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(); +} + +//This function is used to delete an entry from the recycle bin based on the given object ID. +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(); +} + +//This function is a part of the recycling feature and is used to determine if an object needs a logical drop based on its drop mode stored in the recycle bin. +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; +} + +//This function is used to perform the drop operation on an index object in the recycle bin. +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; +} + +//This function is used to perform the drop operation on a table object in the recycle bin. +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; +} diff --git a/src/gausskernel/storage/tcap/tcap_manager.cpp b/src/gausskernel/storage/tcap/tcap_manager.cpp index da0bee6fb..1c5f4fbdc 100644 --- a/src/gausskernel/storage/tcap/tcap_manager.cpp +++ b/src/gausskernel/storage/tcap/tcap_manager.cpp @@ -1,1896 +1,1901 @@ -/* - * 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_manager.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_manager.cpp - * - * --------------------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "pgstat.h" -#include "access/reloptions.h" -#include "access/sysattr.h" -#include "access/xlog.h" -#include "catalog/pg_database.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_partition_fn.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/pg_partition.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 bool TrIsRefRbObject(const ObjectAddress *obj, Relation depRel = NULL); -void TrDoPurgeObjectDrop(TrObjDesc *desc); - -char *TrGenObjName(char *rbname, Oid classId, Oid objid) -{ - int rc = EOK; - - rc = snprintf_s(rbname, NAMEDATALEN, NAMEDATALEN - 1, "BIN$%X%X%X$%llX==$0", - u_sess->proc_cxt.MyDatabaseId, classId, objid, (uint64)GetXLogInsertRecPtr()); - securec_check_ss_c(rc, "\0", "\0"); - - return rbname; -} - -static TransactionId TrRbGetRcyfrozenxid64(HeapTuple rbtup, Relation rbRel = NULL) -{ - Datum datum; - bool isNull = false; - TransactionId rcyfrozenxid64; - bool relArgNull = rbRel == NULL; - - if (relArgNull) { - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - } - - datum = heap_getattr(rbtup, Anum_pg_recyclebin_rcyfrozenxid64, RelationGetDescr(rbRel), &isNull); - Assert(!isNull); - - rcyfrozenxid64 = DatumGetTransactionId(datum); - - if (relArgNull) { - heap_close(rbRel, AccessShareLock); - } - - return rcyfrozenxid64; -} - -void TrDescInit(Relation rel, TrObjDesc *desc, TrObjOperType operType, - TrObjType objType, bool canpurge, bool isBaseObj) -{ - errno_t rc = EOK; - - /* Notice: desc->id, desc->baseid will be assigned by invoker later. */ - desc->dbid = u_sess->proc_cxt.MyDatabaseId; - desc->relid = RelationGetRelid(rel); - - (void)TrGenObjName(desc->name, RelationRelationId, desc->relid); - - rc = strncpy_s(desc->originname, NAMEDATALEN, RelationGetRelationName(rel), - strlen(RelationGetRelationName(rel))); - securec_check(rc, "\0", "\0"); - - desc->operation = operType; - desc->type = objType; - desc->recyclecsn = t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo; - desc->recycletime = GetCurrentTimestamp(); - desc->createcsn = RelationGetCreatecsn(rel); - desc->changecsn = RelationGetChangecsn(rel); - desc->nspace = RelationGetNamespace(rel); - desc->owner = RelationGetOwner(rel); - desc->tablespace = RelationGetTablespace(rel); - desc->relfilenode = RelationGetRelFileNode(rel); - desc->frozenxid = RelationGetRelFrozenxid(rel); - desc->frozenxid64 = RelationGetRelFrozenxid64(rel); - desc->canrestore = objType == RB_OBJ_TABLE; - desc->canpurge = canpurge; -} - -void TrPartDescInit(Relation rel, Partition part, TrObjDesc *desc, TrObjOperType operType, - TrObjType objType, bool canpurge, bool isBaseObj) -{ - errno_t rc = EOK; - - /* Notice: desc->id, desc->baseid will be assigned by invoker later. */ - desc->dbid = u_sess->proc_cxt.MyDatabaseId; - desc->relid = part->pd_id; - - (void)TrGenObjName(desc->name, PartitionRelationId, desc->relid); - - rc = strncpy_s(desc->originname, NAMEDATALEN, RelationGetRelationName(rel), - strlen(RelationGetRelationName(rel))); - securec_check(rc, "\0", "\0"); - - int len = strlen(PartitionGetPartitionName(part)) + strlen(RelationGetRelationName(rel)) + 1; - rc = strcat_s(desc->originname, len, PartitionGetPartitionName(part)); - securec_check(rc, "\0", "\0"); - - desc->operation = operType; - desc->type = objType; - desc->recyclecsn = t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo; - desc->recycletime = GetCurrentTimestamp(); - desc->createcsn = RelationGetCreatecsn(rel); - desc->changecsn = RelationGetChangecsn(rel); - desc->nspace = RelationGetNamespace(rel); - desc->owner = RelationGetOwner(rel); - desc->tablespace = part->pd_part->reltablespace; - desc->relfilenode = part->pd_part->relfilenode; - desc->frozenxid = part->pd_part->relfrozenxid; - desc->frozenxid64 = PartGetRelFrozenxid64(part); - desc->canrestore = false; - desc->canpurge = canpurge; -} - -static void TrDescRead(TrObjDesc *desc, HeapTuple rbtup) -{ - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbtup); - - desc->id = HeapTupleGetOid(rbtup); - desc->baseid = rbForm->rcybaseid; - - desc->dbid = rbForm->rcydbid; - desc->relid = rbForm->rcyrelid; - (void)namestrcpy((Name)desc->name, NameStr(rbForm->rcyname)); - (void)namestrcpy((Name)desc->originname, NameStr(rbForm->rcyoriginname)); - desc->operation = (rbForm->rcyoperation == 'd') ? RB_OPER_DROP : RB_OPER_TRUNCATE; - desc->type = (TrObjType)rbForm->rcytype; - desc->recyclecsn = rbForm->rcyrecyclecsn; - desc->recycletime = rbForm->rcyrecycletime; - desc->createcsn = rbForm->rcycreatecsn; - desc->changecsn = rbForm->rcychangecsn; - desc->nspace = rbForm->rcynamespace; - desc->owner = rbForm->rcyowner; - desc->tablespace = rbForm->rcytablespace; - desc->relfilenode = rbForm->rcyrelfilenode; - desc->canrestore = rbForm->rcycanrestore; - desc->canpurge = rbForm->rcycanpurge; - desc->frozenxid = rbForm->rcyfrozenxid; - desc->frozenxid64 = TrRbGetRcyfrozenxid64(rbtup); -} - -Oid TrDescWrite(TrObjDesc *desc) -{ - Relation rel; - HeapTuple tup; - bool nulls[Natts_pg_recyclebin] = {0}; - Datum values[Natts_pg_recyclebin]; - NameData name; - NameData originname; - Oid rbid; - - values[Anum_pg_recyclebin_rcydbid - 1] = ObjectIdGetDatum(desc->dbid); - values[Anum_pg_recyclebin_rcybaseid - 1] = ObjectIdGetDatum(desc->baseid); - values[Anum_pg_recyclebin_rcyrelid - 1] = ObjectIdGetDatum(desc->relid); - (void)namestrcpy(&name, desc->name); - values[Anum_pg_recyclebin_rcyname - 1] = NameGetDatum(&name); - (void)namestrcpy(&originname, desc->originname); - values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&originname); - values[Anum_pg_recyclebin_rcyoperation - 1] = (desc->operation == RB_OPER_DROP) ? 'd' : 't'; - values[Anum_pg_recyclebin_rcytype - 1] = Int32GetDatum(desc->type); - values[Anum_pg_recyclebin_rcyrecyclecsn - 1] = Int64GetDatum(desc->recyclecsn); - values[Anum_pg_recyclebin_rcyrecycletime - 1] = TimestampTzGetDatum(desc->recycletime); - values[Anum_pg_recyclebin_rcycreatecsn - 1] = Int64GetDatum(desc->createcsn); - values[Anum_pg_recyclebin_rcychangecsn - 1] = Int64GetDatum(desc->changecsn); - values[Anum_pg_recyclebin_rcynamespace - 1] = ObjectIdGetDatum(desc->nspace); - values[Anum_pg_recyclebin_rcyowner - 1] = ObjectIdGetDatum(desc->owner); - values[Anum_pg_recyclebin_rcytablespace - 1] = ObjectIdGetDatum(desc->tablespace); - values[Anum_pg_recyclebin_rcyrelfilenode - 1] = ObjectIdGetDatum(desc->relfilenode); - values[Anum_pg_recyclebin_rcycanrestore - 1] = BoolGetDatum(desc->canrestore); - values[Anum_pg_recyclebin_rcycanpurge - 1] = BoolGetDatum(desc->canpurge); - values[Anum_pg_recyclebin_rcyfrozenxid - 1] = ShortTransactionIdGetDatum(desc->frozenxid); - values[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = TransactionIdGetDatum(desc->frozenxid64); - - rel = heap_open(RecyclebinRelationId, RowExclusiveLock); - - tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); - - rbid = simple_heap_insert(rel, tup); - - CatalogUpdateIndexes(rel, tup); - - heap_freetuple_ext(tup); - - heap_close(rel, RowExclusiveLock); - - CommandCounterIncrement(); - - return rbid; -} - -static bool TrFetchOrinameImpl(Oid nspId, const char *oriname, TrObjType type, - TrObjDesc *desc, TrOperMode operMode) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[3]; - HeapTuple tup; - bool found = false; - - if (!OidIsValid(nspId)) { - return false; - } - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(nspId)); - ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - ScanKeyInit(&skey[2], Anum_pg_recyclebin_rcyoriginname, BTEqualStrategyNumber, - F_NAMEEQ, CStringGetDatum(oriname)); - - sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 3, skey); - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_TABLE) || - (rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_INDEX) || - (operMode == RB_OPER_RESTORE_DROP && rbForm->rcyoperation != 'd') || - (operMode == RB_OPER_RESTORE_TRUNCATE && rbForm->rcyoperation != 't')) { - continue; - } - - found = true; - TrDescRead(desc, tup); - break; - } - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return found; -} - -bool TrFetchName(const char *rcyname, TrObjType type, TrObjDesc *desc, TrOperMode operMode) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - bool found = false; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcyname, BTEqualStrategyNumber, - F_NAMEEQ, CStringGetDatum(rcyname)); - - sd = systable_beginscan(rbRel, RecyclebinNameIndexId, true, NULL, 1, skey); - if ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_TABLE) || - (rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_INDEX)) { - ereport(ERROR, - (errmsg("The recycle object \"%s\" type mismatched.", rcyname))); - } - if ((operMode == RB_OPER_RESTORE_DROP && rbForm->rcyoperation != 'd') || - (operMode == RB_OPER_RESTORE_TRUNCATE && rbForm->rcyoperation != 't')) { - ereport(ERROR, - (errmsg("recycle object \"%s\" desired does not exist", rcyname))); - } - - found = true; - TrDescRead(desc, tup); - } - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return found; -} - -static bool TrFetchOriname(const char *schemaname, const char *relname, TrObjType type, - TrObjDesc *desc, TrOperMode operMode) -{ - bool found = false; - Oid nspId; - - if (schemaname) { - nspId = get_namespace_oid(schemaname, true); - found = TrFetchOrinameImpl(nspId, relname, type, desc, operMode); - } else { - List *activeSearchPath = NIL; - ListCell *l = NULL; - - recomputeNamespacePath(); - activeSearchPath = list_copy(u_sess->catalog_cxt.activeSearchPath); - foreach (l, activeSearchPath) { - nspId = lfirst_oid(l); - if (TrFetchOrinameImpl(nspId, relname, type, desc, operMode)) { - found = true; - break; - } - } - list_free_ext(activeSearchPath); - if (!found) { - nspId = PG_TOAST_NAMESPACE; - found = TrFetchOrinameImpl(nspId, relname, type, desc, operMode); - } - } - - return found; -} - -void TrUpdateBaseid(const TrObjDesc *desc) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - HeapTuple newtup; - Datum values[Natts_pg_recyclebin] = { 0 }; - bool nulls[Natts_pg_recyclebin] = { false }; - bool replaces[Natts_pg_recyclebin] = { false }; - - rbRel = heap_open(RecyclebinRelationId, RowExclusiveLock); - - ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(desc->id)); - - sd = systable_beginscan(rbRel, RecyclebinIdIndexId, true, NULL, 1, skey); - if ((tup = systable_getnext(sd)) == NULL) { - ereport(ERROR, (errmsg("recycle object %u does not exist", desc->id))); - } - - replaces[Anum_pg_recyclebin_rcybaseid - 1] = true; - values[Anum_pg_recyclebin_rcybaseid - 1] = ObjectIdGetDatum(desc->baseid); - - newtup = heap_modify_tuple(tup, RelationGetDescr(rbRel), values, nulls, replaces); - - simple_heap_update(rbRel, &newtup->t_self, newtup); - - CatalogUpdateIndexes(rbRel, newtup); - - heap_freetuple_ext(newtup); - - systable_endscan(sd); - heap_close(rbRel, RowExclusiveLock); - - return; -} - -static void TrLockRelationImpl(Oid relid, TrObjType type) -{ - /* - * Lock failed may due to concurrently purge/timecapsule/DQL - * on recycle object, or access on normal relation. - */ - if (!ConditionalLockRelationOid(relid, AccessExclusiveLock)) { - ereport(ERROR, - (errcode(ERRCODE_RBIN_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on relation \"%u\"", relid))); - } - - /* - * Now that we have the lock, probe to see if the relation - * really exists or not. - */ - AcceptInvalidationMessages(); - if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)) && type != RB_OBJ_PARTITION) { - /* Clean already held locks if error return. */ - UnlockRelationOid(relid, AccessExclusiveLock); - ereport(ERROR, - (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), - errmsg("relation \"%u\" does not exist", relid))); - } else if (!SearchSysCacheExists1(PARTRELID, ObjectIdGetDatum(relid)) && type == RB_OBJ_PARTITION) { - /* Clean already held locks if error return. */ - UnlockRelationOid(relid, AccessExclusiveLock); - ereport(ERROR, - (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), - errmsg("partition \"%u\" does not exist", relid))); - } -} - -static void TrLockRelation(TrObjDesc *desc) -{ - Oid heapOid = InvalidOid; - - /* Lock heap relation for index first */ - if (desc->type == RB_OBJ_INDEX) { - heapOid = IndexGetRelation(desc->relid, true); - if (!OidIsValid(heapOid)) { - ereport(ERROR, - (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), - errmsg("relation \"%u\" does not exist", desc->relid))); - } - TrLockRelationImpl(heapOid, desc->type); - } - - /* Use TRY-CATCH block to clean locks already held if error. */ - PG_TRY(); - { - /* Lock relation self */ - TrLockRelationImpl(desc->relid, desc->type); - } - PG_CATCH(); - { - if (desc->type == RB_OBJ_INDEX) { - UnlockRelationOid(heapOid, AccessExclusiveLock); - } - PG_RE_THROW(); - } - PG_END_TRY(); -} - -static void TrUnlockTrItem(TrObjDesc *desc) -{ - UnlockDatabaseObject(RecyclebinRelationId, desc->id, 0, - AccessExclusiveLock); -} - -static void TrLockTrItem(TrObjDesc *desc) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - - /* 1. Try to lock rb item in AccessExclusiveLock */ - if (!ConditionalLockDatabaseObject(RecyclebinRelationId, desc->id, 0, AccessExclusiveLock)) { - ereport(ERROR, - (errcode(ERRCODE_RBIN_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on recycle object '%s'", desc->name))); - } - - /* - * 2. Now that we have the lock, probe to see if the rb item really - * exists or not. - */ - AcceptInvalidationMessages(); - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(desc->id)); - - sd = systable_beginscan(rbRel, RecyclebinIdIndexId, true, NULL, 1, skey); - if ((tup = systable_getnext(sd)) == NULL) { - UnlockDatabaseObject(RecyclebinRelationId, desc->id, 0, AccessExclusiveLock); - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - ereport(ERROR, - (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), - errmsg("recycle object \"%s\" does not exist", desc->name))); - } - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return; -} - -static void TrOperMatch(const TrObjDesc *desc, TrOperMode operMode) -{ - switch (operMode) { - case RB_OPER_PURGE: - if (!desc->canpurge && desc->type != RB_OBJ_PARTITION) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_OPERATION), - errmsg("recycle object \"%s\" cannot be purged", desc->name))); - } - break; - - case RB_OPER_RESTORE_DROP: - if ((!desc->canrestore && desc->type != RB_OBJ_PARTITION) || desc->operation != RB_OPER_DROP) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_OPERATION), - errmsg("recycle object \"%s\" cannot be restored", desc->name))); - } - break; - - case RB_OPER_RESTORE_TRUNCATE: - if ((!desc->canrestore && desc->type != RB_OBJ_PARTITION) || desc->operation != RB_OPER_TRUNCATE) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_OPERATION), - errmsg("recycle object \"%s\" cannot be restored", desc->name))); - } - break; - - default: - ereport(ERROR, - (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), - errmsg("unrecognized recyclebin operation: %u", operMode))); - break; - } -} - -/* - * Fetch object from recycle bin for rb operations - purge, restore : - * Prefer to fetch as original name, then recycle name. - */ -void TrOperFetch(const RangeVar *purobj, TrObjType objtype, TrObjDesc *desc, TrOperMode operMode) -{ - bool found = false; - - AcceptInvalidationMessages(); - - /* Prefer to fetch as original name */ - found = TrFetchOriname(purobj->schemaname, purobj->relname, objtype, desc, operMode); - /* if not found, then fetch as recycle name */ - if (!found) { - found = TrFetchName(purobj->relname, objtype, desc, operMode); - } - - /* not found, throw error */ - if (!found) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("recycle object \"%s\" desired does not exist", purobj->relname))); - } - - TrOperMatch(desc, operMode); - - return; -} - -static void TrPermRestore(TrObjDesc *desc, TrOperMode operMode) -{ - AclResult aclCreateResult; - - /* Check namespace permissions. */ - aclCreateResult = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_CREATE); - if (aclCreateResult != ACLCHECK_OK) { - aclcheck_error(aclCreateResult, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); - } - - AclResult aclUsageResult = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_USAGE); - if (aclUsageResult != ACLCHECK_OK) { - aclcheck_error(aclUsageResult, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); - } - - /* Allow restore to either table owner or schema owner */ - if (!pg_class_ownercheck(desc->relid, desc->authid) && !pg_namespace_ownercheck(desc->nspace, desc->authid)) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, desc->name); - return; - } - - if (operMode == RB_OPER_RESTORE_TRUNCATE) { - AclResult aclTruncateResult = pg_class_aclcheck(desc->relid, desc->authid, ACL_TRUNCATE); - if (aclTruncateResult != ACLCHECK_OK) { - aclcheck_error(aclTruncateResult, ACL_KIND_CLASS, desc->name); - } - } -} - -static void TrPermPurge(TrObjDesc *desc, TrOperMode operMode) -{ - AclResult result; - - result = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_USAGE); - if (result != ACLCHECK_OK) { - aclcheck_error(result, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); - } - if (!pg_class_ownercheck(desc->relid, desc->authid) && !pg_namespace_ownercheck(desc->nspace, desc->authid)) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, desc->name); - } -} - -/* - * Check permission for rb operations - purge, restore - */ -static void TrPerm(TrObjDesc *desc, TrOperMode operMode) -{ - switch (operMode) { - case RB_OPER_RESTORE_DROP: - case RB_OPER_RESTORE_TRUNCATE: - TrPermRestore(desc, operMode); - break; - case RB_OPER_PURGE: - TrPermPurge(desc, operMode); - break; - default: - /* Never reached here. */ - Assert(0); - break; - } -} - -/* - * Prepare for rb operations - purge, restore : - * check permission, lock objects - */ -void TrOperPrep(TrObjDesc *desc, TrOperMode operMode) -{ - bool needLockRelation = false; - - /* - * 1. Check permission. - */ - TrPerm(desc, operMode); - - /* - * 2. Acquire lock on rb item, avoid concurrently purge, restore. - */ - TrLockTrItem(desc); - - /* - * 3. Acquire lock on relation, avoid concurrently DQL. - * Notice: ignore this step when we purge truncated relation - * as base relation may not exists. - */ - needLockRelation = !(operMode == RB_OPER_PURGE && desc->operation == RB_OPER_TRUNCATE); - if (needLockRelation) { - /* Use TRY-CATCH block to clean locks already held if error. */ - PG_TRY(); - { - TrLockRelation(desc); - } - PG_CATCH(); - { - TrUnlockTrItem(desc); - PG_RE_THROW(); - } - PG_END_TRY(); - } -} - -bool NeedTrComm(Oid relid) -{ - Relation rel; - Form_pg_class classForm; - - if (/* - *Disable Recyclebin-based-Drop/Truncate when - */ - /* recyclebin disabled, or */ - !u_sess->attr.attr_storage.enable_recyclebin || - /* target db is template1, or */ - u_sess->proc_cxt.MyDatabaseId == TemplateDbOid || - /* in maintenance mode, or */ - u_sess->attr.attr_common.xc_maintenance_mode || - /* in in-place upgrade mode, or */ - t_thrd.proc->workingVersionNum < 92350 || - /* in non-singlenode mode, or */ - (g_instance.role != VSINGLENODE) || - /* in bootstrap mode. */ - IsInitdb) { - return false; - } - - rel = relation_open(relid, NoLock); - classForm = rel->rd_rel; - if (/* - * Disable Recyclebin-based-Drop/Truncate if - */ - /* table is non ordinary table, or */ - classForm->relkind != RELKIND_RELATION || - /* is non heap table, or */ - rel->rd_tam_type == TAM_HEAP || - /* is non regular table, or */ - classForm->relpersistence != RELPERSISTENCE_PERMANENT || - /* is shared table across databases, or */ - classForm->relisshared || - /* has derived classes, or */ - classForm->relhassubclass || - /* has any PARTIAL CLUSTER KEY, or */ - classForm->relhasclusterkey || - /* is cstore table, or */ - (rel->rd_options && StdRelOptIsColStore(rel->rd_options)) || RelationIsColStore(rel) || - /* is hbkt table, or */ - (RELATION_HAS_BUCKET(rel) || RELATION_OWN_BUCKET(rel)) || - /* is dfs table, or */ - RelationIsPAXFormat(rel) || - /* is resizing, or */ - RelationInClusterResizing(rel) || - /* is in system namespace. */ - (IsSystemNamespace(classForm->relnamespace) || IsToastNamespace(classForm->relnamespace) || - IsCStoreNamespace(classForm->relnamespace))) { - relation_close(rel, NoLock); - return false; - } - - relation_close(rel, NoLock); - - return true; -} - -TrObjType TrGetObjType(Oid nspId, char relKind) -{ - TrObjType type = RB_OBJ_TABLE; - - switch (relKind) { - case RELKIND_INDEX: - type = IsToastNamespace(nspId) ? RB_OBJ_TOAST_INDEX : RB_OBJ_INDEX; - break; - case RELKIND_RELATION: - type = RB_OBJ_TABLE; - break; - case RELKIND_SEQUENCE: - case RELKIND_LARGE_SEQUENCE: - type = RB_OBJ_SEQUENCE; - break; - case RELKIND_TOASTVALUE: - type = RB_OBJ_TOAST; - break; - case PARTTYPE_PARTITIONED_RELATION: - type = RB_OBJ_PARTITION; - break; - case RELKIND_GLOBAL_INDEX: - type = RB_OBJ_GLOBAL_INDEX; - break; - case RELKIND_MATVIEW: - type = RB_OBJ_MATVIEW; - break; - default: - /* Never reached here. */ - Assert(0); - break; - } - - return type; -} - -static bool TrObjAddrExists(Oid classid, Oid objid, ObjectAddresses *objSet) -{ - int i; - - for (i = 0; i < objSet->numrefs; i++) { - if (TrObjIsEqualEx(classid, objid, &objSet->refs[i])) { - return true; - } - } - - return false; -} - -/* - * output: refobjs - */ -void TrFindAllRefObjs(Relation depRel, const ObjectAddress *subobj, - ObjectAddresses *refobjs, bool ignoreObjSubId) -{ - SysScanDesc sd; - HeapTuple tuple; - ScanKeyData key[3]; - int nkeys; - - ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(subobj->classId)); - ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(subobj->objectId)); - nkeys = 2; - if (!ignoreObjSubId && subobj->objectSubId != 0) { - ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(subobj->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); - /* Cascaded clean rb object in `DROP SCHEMA` command. */ - if (depForm->refclassid == NamespaceRelationId) { - continue; - } - - /* We keep `objSet` unique when `ignoreObjSubId = true` to avoid circle recursive. */ - if (!ignoreObjSubId || !TrObjAddrExists(depForm->refclassid, depForm->refobjid, refobjs)) { - add_object_address_ext(depForm->refclassid, depForm->refobjid, - depForm->refobjsubid, depForm->deptype, refobjs); - } - } - - systable_endscan(sd); - return; -} - -static void TrFindAllInternalObjs(Relation depRel, const ObjectAddress *refobj, - ObjectAddresses *objSet, bool ignoreObjSubId = false) -{ - 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 (!ignoreObjSubId && 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); - if (depForm->deptype != 'i') { - continue; - } - - /* We keep `objSet` unique when `ignoreObjSubId = true` to avoid circle recursive. */ - if (!ignoreObjSubId || !TrObjAddrExists(depForm->classid, depForm->objid, objSet)) { - add_object_address_ext(depForm->classid, depForm->objid, - depForm->objsubid, depForm->deptype, objSet); - } - } - - systable_endscan(sd); - return; -} - -static void TrDoPurgeObject(TrObjDesc *desc) -{ - if (desc->operation == RB_OPER_DROP) { - TrDoPurgeObjectDrop(desc); - } else { - TrDoPurgeObjectTruncate(desc); - } -} - -void TrPurgeObject(RangeVar *purobj, TrObjType type) -{ - TrObjDesc desc; - - TrOperFetch(purobj, type, &desc, RB_OPER_PURGE); - - desc.authid = GetUserId(); - TrOperPrep(&desc, RB_OPER_PURGE); - - TrDoPurgeObject(&desc); - - return; -} - -const int PURGE_BATCH = 64; -const int PURGE_SINGL = 64; -typedef void (*TrFetchBeginHook)(SysScanDesc *sd, Oid objId); -typedef bool (*TrFetchMatchHook)(Relation rbRel, HeapTuple rbTup, Oid objId); - -static void TrFetchBegin(TrFetchBeginHook fetchHook, SysScanDesc *sd, Oid objId) -{ - fetchHook(sd, objId); -} - -// @return: true for eof -static bool TrFetchExec(TrFetchMatchHook matchHook, Oid objId, SysScanDesc sd, TrObjDesc *desc) -{ - HeapTuple tup; - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((rbForm->rcytype == RB_OBJ_TABLE) && matchHook(sd->heap_rel, tup, objId)) { - Assert (rbForm->rcycanpurge); - TrDescRead(desc, tup); - return false; - } else if ((rbForm->rcytype == RB_OBJ_PARTITION) && matchHook(sd->heap_rel, tup, objId)) { - Assert (!rbForm->rcycanpurge); - TrDescRead(desc, tup); - return false; - } - } - return true; -} - -static void TrFetchEnd(SysScanDesc sd) -{ - Relation rbRel = sd->heap_rel; - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); -} - -static bool TrPurgeBatch(TrFetchBeginHook beginHook, TrFetchMatchHook matchHook, - Oid objId, Oid roleid, uint32 maxBatch, PurgeMsgRes *localRes) -{ - SysScanDesc sd = NULL; - TrObjDesc desc; - uint32 count = 0; - bool eof = false; - - RbMsgResetRes(localRes); - - StartTransactionCommand(); - - TrFetchBegin(beginHook, &sd, objId); - while (!(eof = TrFetchExec(matchHook, objId, sd, &desc))) { - CHECK_FOR_INTERRUPTS(); - - PG_TRY(); - { - desc.authid = roleid; - TrOperPrep(&desc, RB_OPER_PURGE); - - TrDoPurgeObject(&desc); - localRes->purgedNum++; - } - PG_CATCH(); - { - int errcode = geterrcode(); - if (errcode == ERRCODE_RBIN_LOCK_NOT_AVAILABLE) { - errno_t rc; - rc = strncpy_s(localRes->errMsg, RB_MAX_ERRMSG_SIZE, Geterrmsg(), RB_MAX_ERRMSG_SIZE - 1); - securec_check(rc, "\0", "\0"); - localRes->skippedNum++; - } else if (errcode == ERRCODE_RBIN_UNDEFINED_OBJECT) { - localRes->undefinedNum++; - } else { - PG_RE_THROW(); - } - } - PG_END_TRY(); - - if (++count >= maxBatch) { - break; - } - } - - TrFetchEnd(sd); - - CommitTransactionCommand(); - - return eof; -} - -static void TrFetchBeginSpace(SysScanDesc *sd, Oid spcId) -{ - ScanKeyData skey[2]; - Relation rbRel; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(spcId)); - ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - *sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 2, skey); -} - -static bool TrFetchMatchSpace(Relation rbRel, HeapTuple rbTup, Oid objId) -{ - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); - return rbForm->rcytablespace == objId; -} - -void TrPurgeTablespace(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - - do { - eof = TrPurgeBatch(TrFetchBeginSpace, TrFetchMatchSpace, req->objId, req->authId, PURGE_BATCH, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof && localRes.skippedNum == 0); -} - -void TrPurgeTablespaceDML(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - - do { - eof = TrPurgeBatch(TrFetchBeginSpace, TrFetchMatchSpace, req->objId, req->authId, PURGE_SINGL, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof && localRes.purgedNum == 0); -} - -static void TrFetchBeginRecyclebin(SysScanDesc *sd, Oid objId) -{ - ScanKeyData skey[2]; - Relation rbRel; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); -} - -static bool TrFetchMatchRecyclebin(Relation rbRel, HeapTuple rbTup, Oid objId) -{ - return true; -} - -void TrPurgeRecyclebin(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - - do { - eof = TrPurgeBatch(TrFetchBeginRecyclebin, TrFetchMatchRecyclebin, - InvalidOid, req->authId, PURGE_BATCH, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof && localRes.skippedNum == 0); -} - -static void TrFetchBeginSchema(SysScanDesc *sd, Oid objId) -{ - ScanKeyData skey[2]; - Relation rbRel; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(objId)); - ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - *sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 2, skey); -} - -static bool TrFetchMatchSchema(Relation rbRel, HeapTuple rbTup, Oid objId) -{ - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); - return rbForm->rcynamespace == objId; -} - -void TrPurgeSchema(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - - do { - eof = TrPurgeBatch(TrFetchBeginSchema, TrFetchMatchSchema, req->objId, req->authId, PURGE_BATCH, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof && localRes.skippedNum == 0); -} - -static void TrFetchBeginUser(SysScanDesc *sd, Oid objId) -{ - ScanKeyData skey[2]; - Relation rbRel; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); -} - -static bool TrFetchMatchUser(Relation rbRel, HeapTuple rbTup, Oid objId) -{ - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); - return rbForm->rcyowner == objId; -} - -void TrPurgeUser(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - - do { - eof = TrPurgeBatch(TrFetchBeginUser, TrFetchMatchUser, req->objId, req->authId, PURGE_BATCH, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof && localRes.skippedNum == 0); -} - -static void TrFetchBeginAuto(SysScanDesc *sd, Oid objId) -{ - ScanKeyData skey[2]; - Relation rbRel; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); -} - -static bool TrFetchMatchAuto(Relation rbRel, HeapTuple rbTup, Oid objId) -{ - bool isNull = false; - Datum datumRcyTime = heap_getattr(rbTup, Anum_pg_recyclebin_rcyrecycletime, - RelationGetDescr(rbRel), &isNull); - - long secs; - int msecs; - TimestampDifference(isNull ? 0 : DatumGetTimestampTz(datumRcyTime), - GetCurrentTimestamp(), &secs, &msecs); - - return secs > u_sess->attr.attr_storage.recyclebin_retention_time || secs < 0; -} - -void TrPurgeAuto(int64 id) -{ - PurgeMsgReq *req = &RbMsg(id)->req; - PurgeMsgRes localRes; - bool eof = false; - do { - eof = TrPurgeBatch(TrFetchBeginAuto, TrFetchMatchAuto, InvalidOid, req->authId, PURGE_BATCH, &localRes); - RbMsgSetStatistics(id, &localRes); - } while (!eof); -} - -void TrSwapRelfilenode(Relation rbRel, HeapTuple rbTup, bool isPart) -{ - Relation relRel; - HeapTuple relTup; - HeapTuple newTup; - TrObjDesc desc; - int maxNattr = 0; - Datum *values = NULL; - bool *nulls = NULL; - bool *replaces = NULL; - NameData name; - errno_t rc = EOK; - bool isNull = false; - int relfilenoIndex = 0; - int frozenxidIndex = 0; - int frozenxid64Index = 0; - bool isPartition = false; - - TrDescRead(&desc, rbTup); - - if (desc.type == RB_OBJ_PARTITION || (desc.type == RB_OBJ_INDEX && isPart)) { - isPartition = true; - } - if (isPartition) { - maxNattr = Max(Natts_pg_partition, Natts_pg_recyclebin); - relRel = heap_open(PartitionRelationId, RowExclusiveLock); - relTup = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(desc.relid)); - relfilenoIndex = Anum_pg_partition_relfilenode; - frozenxidIndex = Anum_pg_partition_relfrozenxid; - frozenxid64Index = Anum_pg_partition_relfrozenxid64; - } else { - maxNattr = Max(Natts_pg_class, Natts_pg_recyclebin); - relRel = heap_open(RelationRelationId, RowExclusiveLock); - relTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(desc.relid)); - relfilenoIndex = Anum_pg_class_relfilenode; - frozenxidIndex = Anum_pg_class_relfrozenxid; - frozenxid64Index = Anum_pg_class_relfrozenxid64; - } - - /* 1. Update pg_class or pg_partition */ - if (!HeapTupleIsValid(relTup)) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("cache lookup failed for relation %u", desc.relid))); - } - - values = (Datum *)palloc0(sizeof(Datum) * maxNattr); - nulls = (bool *)palloc0(sizeof(bool) * maxNattr); - replaces = (bool *)palloc0(sizeof(bool) * maxNattr); - - replaces[relfilenoIndex - 1] = true; - values[relfilenoIndex - 1] = ObjectIdGetDatum(desc.relfilenode); - - replaces[frozenxidIndex - 1] = true; - values[frozenxidIndex - 1] = ShortTransactionIdGetDatum(desc.frozenxid); - - replaces[frozenxid64Index - 1] = true; - values[frozenxid64Index - 1] = TransactionIdGetDatum(desc.frozenxid64); - - newTup = heap_modify_tuple(relTup, RelationGetDescr(relRel), values, nulls, replaces); - - simple_heap_update(relRel, &newTup->t_self, newTup); - - CatalogUpdateIndexes(relRel, newTup); - - heap_freetuple_ext(newTup); - - /* 2. Update pg_recyclebin */ - rc = memset_s(values, sizeof(Datum) * maxNattr, 0, sizeof(Datum) * maxNattr); - securec_check(rc, "\0", "\0"); - rc = memset_s(nulls, sizeof(bool) * maxNattr, false, sizeof(bool) * maxNattr); - securec_check(rc, "\0", "\0"); - rc = memset_s(replaces, sizeof(bool) * maxNattr, false, sizeof(bool) * maxNattr); - securec_check(rc, "\0", "\0"); - - (void)TrGenObjName(NameStr(name), RelationRelationId, desc.relid); - replaces[Anum_pg_recyclebin_rcyname - 1] = true; - values[Anum_pg_recyclebin_rcyname - 1] = NameGetDatum(&name); - - replaces[Anum_pg_recyclebin_rcyoriginname - 1] = true; - if (isPartition) { - values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&desc.originname); - } else { - values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&((Form_pg_class)GETSTRUCT(relTup))->relname); - } - - replaces[Anum_pg_recyclebin_rcyrecyclecsn - 1] = true; - values[Anum_pg_recyclebin_rcyrecyclecsn - 1] = Int64GetDatum(t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo); - - replaces[Anum_pg_recyclebin_rcyrecycletime - 1] = true; - values[Anum_pg_recyclebin_rcyrecycletime - 1] = TimestampTzGetDatum(GetCurrentTimestamp()); - - replaces[Anum_pg_recyclebin_rcyrelfilenode - 1] = true; - if (isPartition) { - values[Anum_pg_recyclebin_rcyrelfilenode - 1] = - ObjectIdGetDatum(((Form_pg_partition)GETSTRUCT(relTup))->relfilenode); - } else { - values[Anum_pg_recyclebin_rcyrelfilenode - 1] = - ObjectIdGetDatum(((Form_pg_class)GETSTRUCT(relTup))->relfilenode); - } - - replaces[Anum_pg_recyclebin_rcyfrozenxid - 1] = true; - if (isPartition) { - values[Anum_pg_recyclebin_rcyfrozenxid - 1] = - ShortTransactionIdGetDatum(((Form_pg_partition)GETSTRUCT(relTup))->relfrozenxid); - } else { - values[Anum_pg_recyclebin_rcyfrozenxid - 1] = - ShortTransactionIdGetDatum(((Form_pg_class)GETSTRUCT(relTup))->relfrozenxid); - } - - replaces[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = true; - Datum xid64datum = heap_getattr(relTup, frozenxid64Index, RelationGetDescr(relRel), &isNull); - values[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = DatumGetTransactionId(xid64datum); - - newTup = heap_modify_tuple(rbTup, RelationGetDescr(rbRel), values, nulls, replaces); - - simple_heap_update(rbRel, &newTup->t_self, newTup); - - CatalogUpdateIndexes(rbRel, newTup); - - heap_freetuple_ext(newTup); - - pfree(values); - pfree(nulls); - pfree(replaces); - - heap_freetuple_ext(relTup); - heap_close(relRel, RowExclusiveLock); - return; -} - -void TrBaseRelMatched(TrObjDesc *baseDesc) -{ - ObjectAddress obj = {RelationRelationId, baseDesc->relid}; - if (TrIsRefRbObject(&obj)) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("relation \"%s\" does not exist", baseDesc->originname))); - } - - Relation rel = RelationIdGetRelation(baseDesc->relid); - Assert(RelationIsValid(rel)); - if (RelationGetCreatecsn(rel) != (CommitSeqNo)baseDesc->createcsn) { - ereport(ERROR, - (errmsg("The recycle object \"%s\" and relation \"%s\" mismatched.", - baseDesc->name, RelationGetRelationName(rel)))); - } - - if (RelationGetChangecsn(rel) > (CommitSeqNo)baseDesc->changecsn) { - ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("The table definition of \"%s\" has been changed.", - RelationGetRelationName(rel)))); - } - - RelationClose(rel); -} - -void TrAdjustFrozenXid64(Oid dbid, TransactionId *frozenXID) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple rbtup; - - if (!TcapFeatureAvail()) { - return; - } - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); - while ((rbtup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbtup); - TransactionId rcyfrozenxid64; - - if (rbForm->rcydbid != dbid || (rbForm->rcytype != RB_OBJ_TABLE && rbForm->rcytype != RB_OBJ_TOAST)) { - continue; - } - - rcyfrozenxid64 = TrRbGetRcyfrozenxid64(rbtup, rbRel); - Assert(TransactionIdIsNormal(rcyfrozenxid64)); - - if (TransactionIdPrecedes(rcyfrozenxid64, *frozenXID)) { - *frozenXID = rcyfrozenxid64; - } - } - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return; -} - -bool TrRbIsEmptyDb(Oid dbid) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - ScanKeyData skey[1]; - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(dbid)); - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); - tup = systable_getnext(sd); - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return tup == NULL; -} - -bool TrRbIsEmptySpc(Oid spcId) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(spcId)); - - sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 1, skey); - tup = systable_getnext(sd); - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return tup == NULL; -} - -bool TrRbIsEmptySchema(Oid nspId) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[2]; - HeapTuple tup; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(nspId)); - ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - - sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 2, skey); - tup = systable_getnext(sd); - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return tup == NULL; -} - -bool TrRbIsEmptyUser(Oid roleId) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - bool found = false; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((TrObjType)rbForm->rcytype != RB_OBJ_TABLE || rbForm->rcyowner != roleId) { - continue; - } - - found = true; - break; - } - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return !found; -} - -static bool TrOidExists(const List *lOid, Oid oid) -{ - ListCell *cell = NULL; - if (lOid == NULL) { - return false; - } - - foreach (cell, lOid) { - if (oid == (*(Oid *)lfirst(cell))) { - return true; - } - } - return false; -} - -List *TrGetDbListRcy(void) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - - List *lName = NIL; - List *lOid = NIL; - char *dbname = NULL; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if (TrOidExists(lOid, rbForm->rcydbid)) { - continue; - } - Oid *oid = (Oid *)palloc0(sizeof(Oid)); - *oid = rbForm->rcydbid; - lOid = lappend(lOid, oid); - - dbname = get_database_name(rbForm->rcydbid); - if (dbname == NULL) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%u\" does not exist", rbForm->rcydbid))); - } - lName = lappend(lName, dbname); - } - - list_free_deep(lOid); - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return lName; -} - -List *TrGetDbListSpc(Oid spcId) -{ - List *lName = NIL; - List *lOid = NIL; - char *dbname = NULL; - - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(spcId)); - - sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 1, skey); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if (TrOidExists(lOid, rbForm->rcydbid)) { - continue; - } - Oid *oid = (Oid *)palloc0(sizeof(Oid)); - *oid = rbForm->rcydbid; - lOid = lappend(lOid, oid); - - dbname = get_database_name(rbForm->rcydbid); - if (dbname == NULL) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%u\" does not exist", rbForm->rcydbid))); - } - lName = lappend(lName, dbname); - } - - list_free_deep(lOid); - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return lName; -} - -List *TrGetDbListSchema(Oid nspId) -{ - Relation rbRel; - SysScanDesc sd; - ScanKeyData skey[1]; - HeapTuple tup; - - List *lName = NIL; - List *lOid = NIL; - char *dbname = NULL; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(nspId)); - - sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 1, skey); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if (TrOidExists(lOid, rbForm->rcydbid)) { - continue; - } - Oid *oid = (Oid *)palloc0(sizeof(Oid)); - *oid = rbForm->rcydbid; - lOid = lappend(lOid, oid); - - dbname = get_database_name(rbForm->rcydbid); - if (dbname == NULL) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%u\" does not exist", rbForm->rcydbid))); - } - lName = lappend(lName, dbname); - } - - list_free_deep(lOid); - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return lName; -} - -List *TrGetDbListUser(Oid roleId) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - - List *lName = NIL; - List *lOid = NIL; - char *dbname = NULL; - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - - sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((TrObjType)rbForm->rcytype != RB_OBJ_TABLE || rbForm->rcyowner != roleId) { - continue; - } - if (TrOidExists(lOid, rbForm->rcydbid)) { - continue; - } - Oid *oid = (Oid *)palloc0(sizeof(Oid)); - *oid = rbForm->rcydbid; - lOid = lappend(lOid, oid); - - dbname = get_database_name(rbForm->rcydbid); - if (dbname == NULL) { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%u\" does not exist", rbForm->rcydbid))); - } - - lName = lappend(lName, dbname); - } - - list_free_deep(lOid); - - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return lName; -} - -/* - * TrGetDatabaseList - * Return a list of all databases found in pg_database. - */ -List *TrGetDbListAuto(void) -{ - List* dblist = NIL; - Relation rel; - SysScanDesc sd; - HeapTuple tup; - - rel = heap_open(DatabaseRelationId, AccessShareLock); - sd = systable_beginscan(rel, InvalidOid, false, NULL, 0, NULL); - - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_database pgdatabase = (Form_pg_database)GETSTRUCT(tup); - if (strcmp(NameStr(pgdatabase->datname), "template0") == 0 || - strcmp(NameStr(pgdatabase->datname), "template1") == 0) { - continue; - } - dblist = lappend(dblist, pstrdup(NameStr(pgdatabase->datname))); - } - - systable_endscan(sd); - heap_close(rel, AccessShareLock); - - return dblist; -} - -static bool TrObjInRecyclebin(const ObjectAddress *obj) -{ - Relation rbRel; - SysScanDesc sd; - HeapTuple tup; - ScanKeyData skey[2]; - bool found = false; - - if (getObjectClass(obj) != OCLASS_CLASS) { - return false; - } - - ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); - ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcyrelid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(obj->objectId)); - - rbRel = heap_open(RecyclebinRelationId, AccessShareLock); - sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 2, skey); - while ((tup = systable_getnext(sd)) != NULL) { - Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); - if ((TrObjType)rbForm->rcyoperation == 'd') { - found = true; - break; - } - } - systable_endscan(sd); - heap_close(rbRel, AccessShareLock); - - return found; -} - -/* - * May this object be a recyclebin object? - * true: with "BIN$" prefix, or not a Relation\Type\Trigger\Constraint\Rule - * false: without "BIN$" prefix, or not exists - */ -static bool TrMaybeRbObject(Oid classid, Oid objid, const char *objname = NULL) -{ - HeapTuple tup; - - /* Note: we preserve rule origin name when RbDrop. */ - if (classid != RewriteRelationId && objname) { - return strncmp(objname, "BIN$", 4) == 0; - } - - switch (classid) { - case RelationRelationId: - tup = SearchSysCache1(RELOID, ObjectIdGetDatum(objid)); - if (tup != NULL) { - objname = NameStr(((Form_pg_class)GETSTRUCT(tup))->relname); - ReleaseSysCache(tup); - } - break; - case TypeRelationId: - tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objid)); - if (tup != NULL) { - objname = NameStr(((Form_pg_type)GETSTRUCT(tup))->typname); - ReleaseSysCache(tup); - } - break; - case TriggerRelationId: { - Relation relTrig; - ScanKeyData skey[1]; - SysScanDesc sd; - - relTrig = heap_open(TriggerRelationId, AccessShareLock); - ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, - F_OIDEQ, ObjectIdGetDatum(objid)); - sd = systable_beginscan(relTrig, TriggerOidIndexId, true, NULL, 1, skey); - if ((tup = systable_getnext(sd)) != NULL) { - objname = NameStr(((Form_pg_trigger)GETSTRUCT(tup))->tgname); - } - systable_endscan(sd); - heap_close(relTrig, AccessShareLock); - break; - } - case ConstraintRelationId: - tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(objid)); - if (tup != NULL) { - objname = NameStr(((Form_pg_constraint)GETSTRUCT(tup))->conname); - ReleaseSysCache(tup); - } - break; - case NamespaceRelationId: - /* Treate Namespace as non-recyclebin object. */ - return false; - default: - /* May be a recyclebin object. */ - return true; - } - - if (objname) { - return strncmp(objname, "BIN$", 4) == 0; - } - - return false; -} - -static bool TrIsRefRbObjectImpl(Relation depRel, const ObjectAddress *obj, ObjectAddresses *objSet) -{ - int startIdx; - - if (TrObjInRecyclebin(obj)) { - return true; - } - - startIdx = objSet->numrefs; - - if (!TrMaybeRbObject(obj->classId, obj->objectId)) { - return false; - } - - TrFindAllRefObjs(depRel, obj, objSet, true); - TrFindAllInternalObjs(depRel, obj, objSet, true); - - for (int i = startIdx; i < objSet->numrefs; i++) { - if (TrIsRefRbObjectImpl(depRel, &objSet->refs[i], objSet)) { - return true; - } - } - - return false; -} - -/* object is a rb object, or reference to a rb object. */ -static bool TrIsRefRbObject(const ObjectAddress *obj, Relation depRel) -{ - ObjectAddresses *objSet = new_object_addresses(); - bool relArgNull = depRel == NULL; - bool result = false; - - if (relArgNull) { - depRel = heap_open(DependRelationId, AccessShareLock); - } - - /* Note: we not care obj->deptype here. */ - add_object_address_ext1(obj, objSet); - - result = TrIsRefRbObjectImpl(depRel, obj, objSet); - - free_object_addresses(objSet); - - if (relArgNull) { - heap_close(depRel, AccessShareLock); - } - - return result; -} - -bool TrIsRefRbObjectEx(Oid classid, Oid objid, const char *objname) -{ - if (!TcapFeatureAvail()) { - return false; - } - - /* Note: we preserve rule origin name when RbDrop. */ - if (TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId)) { - return false; - } - - if (classid != RewriteRelationId && objname && strncmp(objname, "BIN$", 4) != 0) { - return false; - } - - ObjectAddress obj = {classid, objid}; - - return TrIsRefRbObject(&obj); -} - -void TrForbidAccessRbDependencies(Relation depRel, const ObjectAddress *depender, - const ObjectAddress *referenced, int nreferenced) -{ - if (!TcapFeatureAvail()) { - return; - } - - if (IsInitdb || TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId)) { - return; - } - - if (TrIsRefRbObject(depender, depRel)) { - elog (ERROR, "can not access recycle object."); - } - - for (int i = 0; i < nreferenced; i++, referenced++) { - if (TrIsRefRbObject(referenced, depRel)) { - elog (ERROR, "can not access recycle object."); - } - } - - return; -} - -void TrForbidAccessRbObject(Oid classid, Oid objid, const char *objname) -{ - if (!TcapFeatureAvail()) { - return; - } - - if (TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId) || !TrMaybeRbObject(classid, objid, objname)) { - return; - } - - ObjectAddress obj = {classid, objid}; - if (TrIsRefRbObject(&obj)) { - elog (ERROR, "can not access recycle object."); - } - - return; -} - -Datum gs_is_recycle_object(PG_FUNCTION_ARGS) -{ - int classid = PG_GETARG_INT32(0); - int objid = PG_GETARG_INT32(1); - Name objname = PG_GETARG_NAME(2); - bool result = false; - result = TrIsRefRbObjectEx(classid, objid, NameStr(*objname)); - PG_RETURN_BOOL(result); -} +/* + * 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_manager.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_manager.cpp + * + * --------------------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "pgstat.h" +#include "access/reloptions.h" +#include "access/sysattr.h" +#include "access/xlog.h" +#include "catalog/pg_database.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_partition_fn.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/pg_partition.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 bool TrIsRefRbObject(const ObjectAddress *obj, Relation depRel = NULL); +void TrDoPurgeObjectDrop(TrObjDesc *desc); + +//This function is used to generate the name for an object in the recycle bin. +char *TrGenObjName(char *rbname, Oid classId, Oid objid) +{ + int rc = EOK; + + rc = snprintf_s(rbname, NAMEDATALEN, NAMEDATALEN - 1, "BIN$%X%X%X$%llX==$0", + u_sess->proc_cxt.MyDatabaseId, classId, objid, (uint64)GetXLogInsertRecPtr()); + securec_check_ss_c(rc, "\0", "\0"); + + return rbname; +} + +//This function is used to retrieve the rcyfrozenxid64 (recycle frozen transaction ID) from a given heap tuple in the recycle bin. +static TransactionId TrRbGetRcyfrozenxid64(HeapTuple rbtup, Relation rbRel = NULL) +{ + Datum datum; + bool isNull = false; + TransactionId rcyfrozenxid64; + bool relArgNull = rbRel == NULL; + + if (relArgNull) { + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + } + + datum = heap_getattr(rbtup, Anum_pg_recyclebin_rcyfrozenxid64, RelationGetDescr(rbRel), &isNull); + Assert(!isNull); + + rcyfrozenxid64 = DatumGetTransactionId(datum); + + if (relArgNull) { + heap_close(rbRel, AccessShareLock); + } + + return rcyfrozenxid64; +} + +//This function is used to initialize a recycle bin object descriptor with information from a given relation. +void TrDescInit(Relation rel, TrObjDesc *desc, TrObjOperType operType, + TrObjType objType, bool canpurge, bool isBaseObj) +{ + errno_t rc = EOK; + + /* Notice: desc->id, desc->baseid will be assigned by invoker later. */ + desc->dbid = u_sess->proc_cxt.MyDatabaseId; + desc->relid = RelationGetRelid(rel); + + (void)TrGenObjName(desc->name, RelationRelationId, desc->relid); + + rc = strncpy_s(desc->originname, NAMEDATALEN, RelationGetRelationName(rel), + strlen(RelationGetRelationName(rel))); + securec_check(rc, "\0", "\0"); + + desc->operation = operType; + desc->type = objType; + desc->recyclecsn = t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo; + desc->recycletime = GetCurrentTimestamp(); + desc->createcsn = RelationGetCreatecsn(rel); + desc->changecsn = RelationGetChangecsn(rel); + desc->nspace = RelationGetNamespace(rel); + desc->owner = RelationGetOwner(rel); + desc->tablespace = RelationGetTablespace(rel); + desc->relfilenode = RelationGetRelFileNode(rel); + desc->frozenxid = RelationGetRelFrozenxid(rel); + desc->frozenxid64 = RelationGetRelFrozenxid64(rel); + desc->canrestore = objType == RB_OBJ_TABLE; + desc->canpurge = canpurge; +} + +//This function is used to initialize a recycle bin object descriptor for a partitioned relation. +void TrPartDescInit(Relation rel, Partition part, TrObjDesc *desc, TrObjOperType operType, + TrObjType objType, bool canpurge, bool isBaseObj) +{ + errno_t rc = EOK; + + /* Notice: desc->id, desc->baseid will be assigned by invoker later. */ + desc->dbid = u_sess->proc_cxt.MyDatabaseId; + desc->relid = part->pd_id; + + (void)TrGenObjName(desc->name, PartitionRelationId, desc->relid); + + rc = strncpy_s(desc->originname, NAMEDATALEN, RelationGetRelationName(rel), + strlen(RelationGetRelationName(rel))); + securec_check(rc, "\0", "\0"); + + int len = strlen(PartitionGetPartitionName(part)) + strlen(RelationGetRelationName(rel)) + 1; + rc = strcat_s(desc->originname, len, PartitionGetPartitionName(part)); + securec_check(rc, "\0", "\0"); + + desc->operation = operType; + desc->type = objType; + desc->recyclecsn = t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo; + desc->recycletime = GetCurrentTimestamp(); + desc->createcsn = RelationGetCreatecsn(rel); + desc->changecsn = RelationGetChangecsn(rel); + desc->nspace = RelationGetNamespace(rel); + desc->owner = RelationGetOwner(rel); + desc->tablespace = part->pd_part->reltablespace; + desc->relfilenode = part->pd_part->relfilenode; + desc->frozenxid = part->pd_part->relfrozenxid; + desc->frozenxid64 = PartGetRelFrozenxid64(part); + desc->canrestore = false; + desc->canpurge = canpurge; +} + +//This function is used to read the values from a heap tuple representing a recycle bin object descriptor and populate the corresponding TrObjDesc structure. +static void TrDescRead(TrObjDesc *desc, HeapTuple rbtup) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbtup); + + desc->id = HeapTupleGetOid(rbtup); + desc->baseid = rbForm->rcybaseid; + + desc->dbid = rbForm->rcydbid; + desc->relid = rbForm->rcyrelid; + (void)namestrcpy((Name)desc->name, NameStr(rbForm->rcyname)); + (void)namestrcpy((Name)desc->originname, NameStr(rbForm->rcyoriginname)); + desc->operation = (rbForm->rcyoperation == 'd') ? RB_OPER_DROP : RB_OPER_TRUNCATE; + desc->type = (TrObjType)rbForm->rcytype; + desc->recyclecsn = rbForm->rcyrecyclecsn; + desc->recycletime = rbForm->rcyrecycletime; + desc->createcsn = rbForm->rcycreatecsn; + desc->changecsn = rbForm->rcychangecsn; + desc->nspace = rbForm->rcynamespace; + desc->owner = rbForm->rcyowner; + desc->tablespace = rbForm->rcytablespace; + desc->relfilenode = rbForm->rcyrelfilenode; + desc->canrestore = rbForm->rcycanrestore; + desc->canpurge = rbForm->rcycanpurge; + desc->frozenxid = rbForm->rcyfrozenxid; + desc->frozenxid64 = TrRbGetRcyfrozenxid64(rbtup); +} + +Oid TrDescWrite(TrObjDesc *desc) +{ + Relation rel; + HeapTuple tup; + bool nulls[Natts_pg_recyclebin] = {0}; + Datum values[Natts_pg_recyclebin]; + NameData name; + NameData originname; + Oid rbid; + + values[Anum_pg_recyclebin_rcydbid - 1] = ObjectIdGetDatum(desc->dbid); + values[Anum_pg_recyclebin_rcybaseid - 1] = ObjectIdGetDatum(desc->baseid); + values[Anum_pg_recyclebin_rcyrelid - 1] = ObjectIdGetDatum(desc->relid); + (void)namestrcpy(&name, desc->name); + values[Anum_pg_recyclebin_rcyname - 1] = NameGetDatum(&name); + (void)namestrcpy(&originname, desc->originname); + values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&originname); + values[Anum_pg_recyclebin_rcyoperation - 1] = (desc->operation == RB_OPER_DROP) ? 'd' : 't'; + values[Anum_pg_recyclebin_rcytype - 1] = Int32GetDatum(desc->type); + values[Anum_pg_recyclebin_rcyrecyclecsn - 1] = Int64GetDatum(desc->recyclecsn); + values[Anum_pg_recyclebin_rcyrecycletime - 1] = TimestampTzGetDatum(desc->recycletime); + values[Anum_pg_recyclebin_rcycreatecsn - 1] = Int64GetDatum(desc->createcsn); + values[Anum_pg_recyclebin_rcychangecsn - 1] = Int64GetDatum(desc->changecsn); + values[Anum_pg_recyclebin_rcynamespace - 1] = ObjectIdGetDatum(desc->nspace); + values[Anum_pg_recyclebin_rcyowner - 1] = ObjectIdGetDatum(desc->owner); + values[Anum_pg_recyclebin_rcytablespace - 1] = ObjectIdGetDatum(desc->tablespace); + values[Anum_pg_recyclebin_rcyrelfilenode - 1] = ObjectIdGetDatum(desc->relfilenode); + values[Anum_pg_recyclebin_rcycanrestore - 1] = BoolGetDatum(desc->canrestore); + values[Anum_pg_recyclebin_rcycanpurge - 1] = BoolGetDatum(desc->canpurge); + values[Anum_pg_recyclebin_rcyfrozenxid - 1] = ShortTransactionIdGetDatum(desc->frozenxid); + values[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = TransactionIdGetDatum(desc->frozenxid64); + + rel = heap_open(RecyclebinRelationId, RowExclusiveLock); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + rbid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); + + heap_freetuple_ext(tup); + + heap_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); + + return rbid; +} + +static bool TrFetchOrinameImpl(Oid nspId, const char *oriname, TrObjType type, + TrObjDesc *desc, TrOperMode operMode) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[3]; + HeapTuple tup; + bool found = false; + + if (!OidIsValid(nspId)) { + return false; + } + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(nspId)); + ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + ScanKeyInit(&skey[2], Anum_pg_recyclebin_rcyoriginname, BTEqualStrategyNumber, + F_NAMEEQ, CStringGetDatum(oriname)); + + sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 3, skey); + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_TABLE) || + (rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_INDEX) || + (operMode == RB_OPER_RESTORE_DROP && rbForm->rcyoperation != 'd') || + (operMode == RB_OPER_RESTORE_TRUNCATE && rbForm->rcyoperation != 't')) { + continue; + } + + found = true; + TrDescRead(desc, tup); + break; + } + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return found; +} + +bool TrFetchName(const char *rcyname, TrObjType type, TrObjDesc *desc, TrOperMode operMode) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + bool found = false; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcyname, BTEqualStrategyNumber, + F_NAMEEQ, CStringGetDatum(rcyname)); + + sd = systable_beginscan(rbRel, RecyclebinNameIndexId, true, NULL, 1, skey); + if ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_TABLE) || + (rbForm->rcytype != type && rbForm->rcytype == RB_OBJ_INDEX)) { + ereport(ERROR, + (errmsg("The recycle object \"%s\" type mismatched.", rcyname))); + } + if ((operMode == RB_OPER_RESTORE_DROP && rbForm->rcyoperation != 'd') || + (operMode == RB_OPER_RESTORE_TRUNCATE && rbForm->rcyoperation != 't')) { + ereport(ERROR, + (errmsg("recycle object \"%s\" desired does not exist", rcyname))); + } + + found = true; + TrDescRead(desc, tup); + } + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return found; +} + +static bool TrFetchOriname(const char *schemaname, const char *relname, TrObjType type, + TrObjDesc *desc, TrOperMode operMode) +{ + bool found = false; + Oid nspId; + + if (schemaname) { + nspId = get_namespace_oid(schemaname, true); + found = TrFetchOrinameImpl(nspId, relname, type, desc, operMode); + } else { + List *activeSearchPath = NIL; + ListCell *l = NULL; + + recomputeNamespacePath(); + activeSearchPath = list_copy(u_sess->catalog_cxt.activeSearchPath); + foreach (l, activeSearchPath) { + nspId = lfirst_oid(l); + if (TrFetchOrinameImpl(nspId, relname, type, desc, operMode)) { + found = true; + break; + } + } + list_free_ext(activeSearchPath); + if (!found) { + nspId = PG_TOAST_NAMESPACE; + found = TrFetchOrinameImpl(nspId, relname, type, desc, operMode); + } + } + + return found; +} + +void TrUpdateBaseid(const TrObjDesc *desc) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + HeapTuple newtup; + Datum values[Natts_pg_recyclebin] = { 0 }; + bool nulls[Natts_pg_recyclebin] = { false }; + bool replaces[Natts_pg_recyclebin] = { false }; + + rbRel = heap_open(RecyclebinRelationId, RowExclusiveLock); + + ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(desc->id)); + + sd = systable_beginscan(rbRel, RecyclebinIdIndexId, true, NULL, 1, skey); + if ((tup = systable_getnext(sd)) == NULL) { + ereport(ERROR, (errmsg("recycle object %u does not exist", desc->id))); + } + + replaces[Anum_pg_recyclebin_rcybaseid - 1] = true; + values[Anum_pg_recyclebin_rcybaseid - 1] = ObjectIdGetDatum(desc->baseid); + + newtup = heap_modify_tuple(tup, RelationGetDescr(rbRel), values, nulls, replaces); + + simple_heap_update(rbRel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rbRel, newtup); + + heap_freetuple_ext(newtup); + + systable_endscan(sd); + heap_close(rbRel, RowExclusiveLock); + + return; +} + +static void TrLockRelationImpl(Oid relid, TrObjType type) +{ + /* + * Lock failed may due to concurrently purge/timecapsule/DQL + * on recycle object, or access on normal relation. + */ + if (!ConditionalLockRelationOid(relid, AccessExclusiveLock)) { + ereport(ERROR, + (errcode(ERRCODE_RBIN_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%u\"", relid))); + } + + /* + * Now that we have the lock, probe to see if the relation + * really exists or not. + */ + AcceptInvalidationMessages(); + if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)) && type != RB_OBJ_PARTITION) { + /* Clean already held locks if error return. */ + UnlockRelationOid(relid, AccessExclusiveLock); + ereport(ERROR, + (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), + errmsg("relation \"%u\" does not exist", relid))); + } else if (!SearchSysCacheExists1(PARTRELID, ObjectIdGetDatum(relid)) && type == RB_OBJ_PARTITION) { + /* Clean already held locks if error return. */ + UnlockRelationOid(relid, AccessExclusiveLock); + ereport(ERROR, + (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), + errmsg("partition \"%u\" does not exist", relid))); + } +} + +static void TrLockRelation(TrObjDesc *desc) +{ + Oid heapOid = InvalidOid; + + /* Lock heap relation for index first */ + if (desc->type == RB_OBJ_INDEX) { + heapOid = IndexGetRelation(desc->relid, true); + if (!OidIsValid(heapOid)) { + ereport(ERROR, + (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), + errmsg("relation \"%u\" does not exist", desc->relid))); + } + TrLockRelationImpl(heapOid, desc->type); + } + + /* Use TRY-CATCH block to clean locks already held if error. */ + PG_TRY(); + { + /* Lock relation self */ + TrLockRelationImpl(desc->relid, desc->type); + } + PG_CATCH(); + { + if (desc->type == RB_OBJ_INDEX) { + UnlockRelationOid(heapOid, AccessExclusiveLock); + } + PG_RE_THROW(); + } + PG_END_TRY(); +} + +static void TrUnlockTrItem(TrObjDesc *desc) +{ + UnlockDatabaseObject(RecyclebinRelationId, desc->id, 0, + AccessExclusiveLock); +} + +static void TrLockTrItem(TrObjDesc *desc) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + + /* 1. Try to lock rb item in AccessExclusiveLock */ + if (!ConditionalLockDatabaseObject(RecyclebinRelationId, desc->id, 0, AccessExclusiveLock)) { + ereport(ERROR, + (errcode(ERRCODE_RBIN_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on recycle object '%s'", desc->name))); + } + + /* + * 2. Now that we have the lock, probe to see if the rb item really + * exists or not. + */ + AcceptInvalidationMessages(); + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(desc->id)); + + sd = systable_beginscan(rbRel, RecyclebinIdIndexId, true, NULL, 1, skey); + if ((tup = systable_getnext(sd)) == NULL) { + UnlockDatabaseObject(RecyclebinRelationId, desc->id, 0, AccessExclusiveLock); + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + ereport(ERROR, + (errcode(ERRCODE_RBIN_UNDEFINED_OBJECT), + errmsg("recycle object \"%s\" does not exist", desc->name))); + } + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return; +} + +static void TrOperMatch(const TrObjDesc *desc, TrOperMode operMode) +{ + switch (operMode) { + case RB_OPER_PURGE: + if (!desc->canpurge && desc->type != RB_OBJ_PARTITION) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("recycle object \"%s\" cannot be purged", desc->name))); + } + break; + + case RB_OPER_RESTORE_DROP: + if ((!desc->canrestore && desc->type != RB_OBJ_PARTITION) || desc->operation != RB_OPER_DROP) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("recycle object \"%s\" cannot be restored", desc->name))); + } + break; + + case RB_OPER_RESTORE_TRUNCATE: + if ((!desc->canrestore && desc->type != RB_OBJ_PARTITION) || desc->operation != RB_OPER_TRUNCATE) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("recycle object \"%s\" cannot be restored", desc->name))); + } + break; + + default: + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("unrecognized recyclebin operation: %u", operMode))); + break; + } +} + +/* + * Fetch object from recycle bin for rb operations - purge, restore : + * Prefer to fetch as original name, then recycle name. + */ +void TrOperFetch(const RangeVar *purobj, TrObjType objtype, TrObjDesc *desc, TrOperMode operMode) +{ + bool found = false; + + AcceptInvalidationMessages(); + + /* Prefer to fetch as original name */ + found = TrFetchOriname(purobj->schemaname, purobj->relname, objtype, desc, operMode); + /* if not found, then fetch as recycle name */ + if (!found) { + found = TrFetchName(purobj->relname, objtype, desc, operMode); + } + + /* not found, throw error */ + if (!found) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("recycle object \"%s\" desired does not exist", purobj->relname))); + } + + TrOperMatch(desc, operMode); + + return; +} + +static void TrPermRestore(TrObjDesc *desc, TrOperMode operMode) +{ + AclResult aclCreateResult; + + /* Check namespace permissions. */ + aclCreateResult = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_CREATE); + if (aclCreateResult != ACLCHECK_OK) { + aclcheck_error(aclCreateResult, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); + } + + AclResult aclUsageResult = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_USAGE); + if (aclUsageResult != ACLCHECK_OK) { + aclcheck_error(aclUsageResult, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); + } + + /* Allow restore to either table owner or schema owner */ + if (!pg_class_ownercheck(desc->relid, desc->authid) && !pg_namespace_ownercheck(desc->nspace, desc->authid)) { + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, desc->name); + return; + } + + if (operMode == RB_OPER_RESTORE_TRUNCATE) { + AclResult aclTruncateResult = pg_class_aclcheck(desc->relid, desc->authid, ACL_TRUNCATE); + if (aclTruncateResult != ACLCHECK_OK) { + aclcheck_error(aclTruncateResult, ACL_KIND_CLASS, desc->name); + } + } +} + +static void TrPermPurge(TrObjDesc *desc, TrOperMode operMode) +{ + AclResult result; + + result = pg_namespace_aclcheck(desc->nspace, desc->authid, ACL_USAGE); + if (result != ACLCHECK_OK) { + aclcheck_error(result, ACL_KIND_NAMESPACE, get_namespace_name(desc->nspace)); + } + if (!pg_class_ownercheck(desc->relid, desc->authid) && !pg_namespace_ownercheck(desc->nspace, desc->authid)) { + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, desc->name); + } +} + +/* + * Check permission for rb operations - purge, restore + */ +static void TrPerm(TrObjDesc *desc, TrOperMode operMode) +{ + switch (operMode) { + case RB_OPER_RESTORE_DROP: + case RB_OPER_RESTORE_TRUNCATE: + TrPermRestore(desc, operMode); + break; + case RB_OPER_PURGE: + TrPermPurge(desc, operMode); + break; + default: + /* Never reached here. */ + Assert(0); + break; + } +} + +/* + * Prepare for rb operations - purge, restore : + * check permission, lock objects + */ +void TrOperPrep(TrObjDesc *desc, TrOperMode operMode) +{ + bool needLockRelation = false; + + /* + * 1. Check permission. + */ + TrPerm(desc, operMode); + + /* + * 2. Acquire lock on rb item, avoid concurrently purge, restore. + */ + TrLockTrItem(desc); + + /* + * 3. Acquire lock on relation, avoid concurrently DQL. + * Notice: ignore this step when we purge truncated relation + * as base relation may not exists. + */ + needLockRelation = !(operMode == RB_OPER_PURGE && desc->operation == RB_OPER_TRUNCATE); + if (needLockRelation) { + /* Use TRY-CATCH block to clean locks already held if error. */ + PG_TRY(); + { + TrLockRelation(desc); + } + PG_CATCH(); + { + TrUnlockTrItem(desc); + PG_RE_THROW(); + } + PG_END_TRY(); + } +} + +bool NeedTrComm(Oid relid) +{ + Relation rel; + Form_pg_class classForm; + + if (/* + *Disable Recyclebin-based-Drop/Truncate when + */ + /* recyclebin disabled, or */ + !u_sess->attr.attr_storage.enable_recyclebin || + /* target db is template1, or */ + u_sess->proc_cxt.MyDatabaseId == TemplateDbOid || + /* in maintenance mode, or */ + u_sess->attr.attr_common.xc_maintenance_mode || + /* in in-place upgrade mode, or */ + t_thrd.proc->workingVersionNum < 92350 || + /* in non-singlenode mode, or */ + (g_instance.role != VSINGLENODE) || + /* in bootstrap mode. */ + IsInitdb) { + return false; + } + + rel = relation_open(relid, NoLock); + classForm = rel->rd_rel; + if (/* + * Disable Recyclebin-based-Drop/Truncate if + */ + /* table is non ordinary table, or */ + classForm->relkind != RELKIND_RELATION || + /* is non heap table, or */ + rel->rd_tam_type == TAM_HEAP || + /* is non regular table, or */ + classForm->relpersistence != RELPERSISTENCE_PERMANENT || + /* is shared table across databases, or */ + classForm->relisshared || + /* has derived classes, or */ + classForm->relhassubclass || + /* has any PARTIAL CLUSTER KEY, or */ + classForm->relhasclusterkey || + /* is cstore table, or */ + (rel->rd_options && StdRelOptIsColStore(rel->rd_options)) || RelationIsColStore(rel) || + /* is hbkt table, or */ + (RELATION_HAS_BUCKET(rel) || RELATION_OWN_BUCKET(rel)) || + /* is dfs table, or */ + RelationIsPAXFormat(rel) || + /* is resizing, or */ + RelationInClusterResizing(rel) || + /* is in system namespace. */ + (IsSystemNamespace(classForm->relnamespace) || IsToastNamespace(classForm->relnamespace) || + IsCStoreNamespace(classForm->relnamespace))) { + relation_close(rel, NoLock); + return false; + } + + relation_close(rel, NoLock); + + return true; +} + +TrObjType TrGetObjType(Oid nspId, char relKind) +{ + TrObjType type = RB_OBJ_TABLE; + + switch (relKind) { + case RELKIND_INDEX: + type = IsToastNamespace(nspId) ? RB_OBJ_TOAST_INDEX : RB_OBJ_INDEX; + break; + case RELKIND_RELATION: + type = RB_OBJ_TABLE; + break; + case RELKIND_SEQUENCE: + case RELKIND_LARGE_SEQUENCE: + type = RB_OBJ_SEQUENCE; + break; + case RELKIND_TOASTVALUE: + type = RB_OBJ_TOAST; + break; + case PARTTYPE_PARTITIONED_RELATION: + type = RB_OBJ_PARTITION; + break; + case RELKIND_GLOBAL_INDEX: + type = RB_OBJ_GLOBAL_INDEX; + break; + case RELKIND_MATVIEW: + type = RB_OBJ_MATVIEW; + break; + default: + /* Never reached here. */ + Assert(0); + break; + } + + return type; +} + +static bool TrObjAddrExists(Oid classid, Oid objid, ObjectAddresses *objSet) +{ + int i; + + for (i = 0; i < objSet->numrefs; i++) { + if (TrObjIsEqualEx(classid, objid, &objSet->refs[i])) { + return true; + } + } + + return false; +} + +/* + * output: refobjs + */ +void TrFindAllRefObjs(Relation depRel, const ObjectAddress *subobj, + ObjectAddresses *refobjs, bool ignoreObjSubId) +{ + SysScanDesc sd; + HeapTuple tuple; + ScanKeyData key[3]; + int nkeys; + + ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(subobj->classId)); + ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(subobj->objectId)); + nkeys = 2; + if (!ignoreObjSubId && subobj->objectSubId != 0) { + ScanKeyInit(&key[2], Anum_pg_depend_objsubid, BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(subobj->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); + /* Cascaded clean rb object in `DROP SCHEMA` command. */ + if (depForm->refclassid == NamespaceRelationId) { + continue; + } + + /* We keep `objSet` unique when `ignoreObjSubId = true` to avoid circle recursive. */ + if (!ignoreObjSubId || !TrObjAddrExists(depForm->refclassid, depForm->refobjid, refobjs)) { + add_object_address_ext(depForm->refclassid, depForm->refobjid, + depForm->refobjsubid, depForm->deptype, refobjs); + } + } + + systable_endscan(sd); + return; +} + +static void TrFindAllInternalObjs(Relation depRel, const ObjectAddress *refobj, + ObjectAddresses *objSet, bool ignoreObjSubId = false) +{ + 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 (!ignoreObjSubId && 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); + if (depForm->deptype != 'i') { + continue; + } + + /* We keep `objSet` unique when `ignoreObjSubId = true` to avoid circle recursive. */ + if (!ignoreObjSubId || !TrObjAddrExists(depForm->classid, depForm->objid, objSet)) { + add_object_address_ext(depForm->classid, depForm->objid, + depForm->objsubid, depForm->deptype, objSet); + } + } + + systable_endscan(sd); + return; +} + +static void TrDoPurgeObject(TrObjDesc *desc) +{ + if (desc->operation == RB_OPER_DROP) { + TrDoPurgeObjectDrop(desc); + } else { + TrDoPurgeObjectTruncate(desc); + } +} + +void TrPurgeObject(RangeVar *purobj, TrObjType type) +{ + TrObjDesc desc; + + TrOperFetch(purobj, type, &desc, RB_OPER_PURGE); + + desc.authid = GetUserId(); + TrOperPrep(&desc, RB_OPER_PURGE); + + TrDoPurgeObject(&desc); + + return; +} + +const int PURGE_BATCH = 64; +const int PURGE_SINGL = 64; +typedef void (*TrFetchBeginHook)(SysScanDesc *sd, Oid objId); +typedef bool (*TrFetchMatchHook)(Relation rbRel, HeapTuple rbTup, Oid objId); + +static void TrFetchBegin(TrFetchBeginHook fetchHook, SysScanDesc *sd, Oid objId) +{ + fetchHook(sd, objId); +} + +// @return: true for eof +static bool TrFetchExec(TrFetchMatchHook matchHook, Oid objId, SysScanDesc sd, TrObjDesc *desc) +{ + HeapTuple tup; + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((rbForm->rcytype == RB_OBJ_TABLE) && matchHook(sd->heap_rel, tup, objId)) { + Assert (rbForm->rcycanpurge); + TrDescRead(desc, tup); + return false; + } else if ((rbForm->rcytype == RB_OBJ_PARTITION) && matchHook(sd->heap_rel, tup, objId)) { + Assert (!rbForm->rcycanpurge); + TrDescRead(desc, tup); + return false; + } + } + return true; +} + +static void TrFetchEnd(SysScanDesc sd) +{ + Relation rbRel = sd->heap_rel; + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); +} + +static bool TrPurgeBatch(TrFetchBeginHook beginHook, TrFetchMatchHook matchHook, + Oid objId, Oid roleid, uint32 maxBatch, PurgeMsgRes *localRes) +{ + SysScanDesc sd = NULL; + TrObjDesc desc; + uint32 count = 0; + bool eof = false; + + RbMsgResetRes(localRes); + + StartTransactionCommand(); + + TrFetchBegin(beginHook, &sd, objId); + while (!(eof = TrFetchExec(matchHook, objId, sd, &desc))) { + CHECK_FOR_INTERRUPTS(); + + PG_TRY(); + { + desc.authid = roleid; + TrOperPrep(&desc, RB_OPER_PURGE); + + TrDoPurgeObject(&desc); + localRes->purgedNum++; + } + PG_CATCH(); + { + int errcode = geterrcode(); + if (errcode == ERRCODE_RBIN_LOCK_NOT_AVAILABLE) { + errno_t rc; + rc = strncpy_s(localRes->errMsg, RB_MAX_ERRMSG_SIZE, Geterrmsg(), RB_MAX_ERRMSG_SIZE - 1); + securec_check(rc, "\0", "\0"); + localRes->skippedNum++; + } else if (errcode == ERRCODE_RBIN_UNDEFINED_OBJECT) { + localRes->undefinedNum++; + } else { + PG_RE_THROW(); + } + } + PG_END_TRY(); + + if (++count >= maxBatch) { + break; + } + } + + TrFetchEnd(sd); + + CommitTransactionCommand(); + + return eof; +} + +static void TrFetchBeginSpace(SysScanDesc *sd, Oid spcId) +{ + ScanKeyData skey[2]; + Relation rbRel; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(spcId)); + ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + *sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 2, skey); +} + +static bool TrFetchMatchSpace(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); + return rbForm->rcytablespace == objId; +} + +void TrPurgeTablespace(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + + do { + eof = TrPurgeBatch(TrFetchBeginSpace, TrFetchMatchSpace, req->objId, req->authId, PURGE_BATCH, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof && localRes.skippedNum == 0); +} + +void TrPurgeTablespaceDML(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + + do { + eof = TrPurgeBatch(TrFetchBeginSpace, TrFetchMatchSpace, req->objId, req->authId, PURGE_SINGL, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof && localRes.purgedNum == 0); +} + +static void TrFetchBeginRecyclebin(SysScanDesc *sd, Oid objId) +{ + ScanKeyData skey[2]; + Relation rbRel; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); +} + +static bool TrFetchMatchRecyclebin(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + return true; +} + +void TrPurgeRecyclebin(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + + do { + eof = TrPurgeBatch(TrFetchBeginRecyclebin, TrFetchMatchRecyclebin, + InvalidOid, req->authId, PURGE_BATCH, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof && localRes.skippedNum == 0); +} + +static void TrFetchBeginSchema(SysScanDesc *sd, Oid objId) +{ + ScanKeyData skey[2]; + Relation rbRel; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(objId)); + ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + *sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 2, skey); +} + +static bool TrFetchMatchSchema(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); + return rbForm->rcynamespace == objId; +} + +void TrPurgeSchema(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + + do { + eof = TrPurgeBatch(TrFetchBeginSchema, TrFetchMatchSchema, req->objId, req->authId, PURGE_BATCH, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof && localRes.skippedNum == 0); +} + +static void TrFetchBeginUser(SysScanDesc *sd, Oid objId) +{ + ScanKeyData skey[2]; + Relation rbRel; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); +} + +static bool TrFetchMatchUser(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); + return rbForm->rcyowner == objId; +} + +void TrPurgeUser(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + + do { + eof = TrPurgeBatch(TrFetchBeginUser, TrFetchMatchUser, req->objId, req->authId, PURGE_BATCH, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof && localRes.skippedNum == 0); +} + +static void TrFetchBeginAuto(SysScanDesc *sd, Oid objId) +{ + ScanKeyData skey[2]; + Relation rbRel; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + *sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); +} + +static bool TrFetchMatchAuto(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + bool isNull = false; + Datum datumRcyTime = heap_getattr(rbTup, Anum_pg_recyclebin_rcyrecycletime, + RelationGetDescr(rbRel), &isNull); + + long secs; + int msecs; + TimestampDifference(isNull ? 0 : DatumGetTimestampTz(datumRcyTime), + GetCurrentTimestamp(), &secs, &msecs); + + return secs > u_sess->attr.attr_storage.recyclebin_retention_time || secs < 0; +} + +void TrPurgeAuto(int64 id) +{ + PurgeMsgReq *req = &RbMsg(id)->req; + PurgeMsgRes localRes; + bool eof = false; + do { + eof = TrPurgeBatch(TrFetchBeginAuto, TrFetchMatchAuto, InvalidOid, req->authId, PURGE_BATCH, &localRes); + RbMsgSetStatistics(id, &localRes); + } while (!eof); +} + +void TrSwapRelfilenode(Relation rbRel, HeapTuple rbTup, bool isPart) +{ + Relation relRel; + HeapTuple relTup; + HeapTuple newTup; + TrObjDesc desc; + int maxNattr = 0; + Datum *values = NULL; + bool *nulls = NULL; + bool *replaces = NULL; + NameData name; + errno_t rc = EOK; + bool isNull = false; + int relfilenoIndex = 0; + int frozenxidIndex = 0; + int frozenxid64Index = 0; + bool isPartition = false; + + TrDescRead(&desc, rbTup); + + if (desc.type == RB_OBJ_PARTITION || (desc.type == RB_OBJ_INDEX && isPart)) { + isPartition = true; + } + if (isPartition) { + maxNattr = Max(Natts_pg_partition, Natts_pg_recyclebin); + relRel = heap_open(PartitionRelationId, RowExclusiveLock); + relTup = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(desc.relid)); + relfilenoIndex = Anum_pg_partition_relfilenode; + frozenxidIndex = Anum_pg_partition_relfrozenxid; + frozenxid64Index = Anum_pg_partition_relfrozenxid64; + } else { + maxNattr = Max(Natts_pg_class, Natts_pg_recyclebin); + relRel = heap_open(RelationRelationId, RowExclusiveLock); + relTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(desc.relid)); + relfilenoIndex = Anum_pg_class_relfilenode; + frozenxidIndex = Anum_pg_class_relfrozenxid; + frozenxid64Index = Anum_pg_class_relfrozenxid64; + } + + /* 1. Update pg_class or pg_partition */ + if (!HeapTupleIsValid(relTup)) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("cache lookup failed for relation %u", desc.relid))); + } + + values = (Datum *)palloc0(sizeof(Datum) * maxNattr); + nulls = (bool *)palloc0(sizeof(bool) * maxNattr); + replaces = (bool *)palloc0(sizeof(bool) * maxNattr); + + replaces[relfilenoIndex - 1] = true; + values[relfilenoIndex - 1] = ObjectIdGetDatum(desc.relfilenode); + + replaces[frozenxidIndex - 1] = true; + values[frozenxidIndex - 1] = ShortTransactionIdGetDatum(desc.frozenxid); + + replaces[frozenxid64Index - 1] = true; + values[frozenxid64Index - 1] = TransactionIdGetDatum(desc.frozenxid64); + + newTup = heap_modify_tuple(relTup, RelationGetDescr(relRel), values, nulls, replaces); + + simple_heap_update(relRel, &newTup->t_self, newTup); + + CatalogUpdateIndexes(relRel, newTup); + + heap_freetuple_ext(newTup); + + /* 2. Update pg_recyclebin */ + rc = memset_s(values, sizeof(Datum) * maxNattr, 0, sizeof(Datum) * maxNattr); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(bool) * maxNattr, false, sizeof(bool) * maxNattr); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(bool) * maxNattr, false, sizeof(bool) * maxNattr); + securec_check(rc, "\0", "\0"); + + (void)TrGenObjName(NameStr(name), RelationRelationId, desc.relid); + replaces[Anum_pg_recyclebin_rcyname - 1] = true; + values[Anum_pg_recyclebin_rcyname - 1] = NameGetDatum(&name); + + replaces[Anum_pg_recyclebin_rcyoriginname - 1] = true; + if (isPartition) { + values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&desc.originname); + } else { + values[Anum_pg_recyclebin_rcyoriginname - 1] = NameGetDatum(&((Form_pg_class)GETSTRUCT(relTup))->relname); + } + + replaces[Anum_pg_recyclebin_rcyrecyclecsn - 1] = true; + values[Anum_pg_recyclebin_rcyrecyclecsn - 1] = Int64GetDatum(t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo); + + replaces[Anum_pg_recyclebin_rcyrecycletime - 1] = true; + values[Anum_pg_recyclebin_rcyrecycletime - 1] = TimestampTzGetDatum(GetCurrentTimestamp()); + + replaces[Anum_pg_recyclebin_rcyrelfilenode - 1] = true; + if (isPartition) { + values[Anum_pg_recyclebin_rcyrelfilenode - 1] = + ObjectIdGetDatum(((Form_pg_partition)GETSTRUCT(relTup))->relfilenode); + } else { + values[Anum_pg_recyclebin_rcyrelfilenode - 1] = + ObjectIdGetDatum(((Form_pg_class)GETSTRUCT(relTup))->relfilenode); + } + + replaces[Anum_pg_recyclebin_rcyfrozenxid - 1] = true; + if (isPartition) { + values[Anum_pg_recyclebin_rcyfrozenxid - 1] = + ShortTransactionIdGetDatum(((Form_pg_partition)GETSTRUCT(relTup))->relfrozenxid); + } else { + values[Anum_pg_recyclebin_rcyfrozenxid - 1] = + ShortTransactionIdGetDatum(((Form_pg_class)GETSTRUCT(relTup))->relfrozenxid); + } + + replaces[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = true; + Datum xid64datum = heap_getattr(relTup, frozenxid64Index, RelationGetDescr(relRel), &isNull); + values[Anum_pg_recyclebin_rcyfrozenxid64 - 1] = DatumGetTransactionId(xid64datum); + + newTup = heap_modify_tuple(rbTup, RelationGetDescr(rbRel), values, nulls, replaces); + + simple_heap_update(rbRel, &newTup->t_self, newTup); + + CatalogUpdateIndexes(rbRel, newTup); + + heap_freetuple_ext(newTup); + + pfree(values); + pfree(nulls); + pfree(replaces); + + heap_freetuple_ext(relTup); + heap_close(relRel, RowExclusiveLock); + return; +} + +void TrBaseRelMatched(TrObjDesc *baseDesc) +{ + ObjectAddress obj = {RelationRelationId, baseDesc->relid}; + if (TrIsRefRbObject(&obj)) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("relation \"%s\" does not exist", baseDesc->originname))); + } + + Relation rel = RelationIdGetRelation(baseDesc->relid); + Assert(RelationIsValid(rel)); + if (RelationGetCreatecsn(rel) != (CommitSeqNo)baseDesc->createcsn) { + ereport(ERROR, + (errmsg("The recycle object \"%s\" and relation \"%s\" mismatched.", + baseDesc->name, RelationGetRelationName(rel)))); + } + + if (RelationGetChangecsn(rel) > (CommitSeqNo)baseDesc->changecsn) { + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("The table definition of \"%s\" has been changed.", + RelationGetRelationName(rel)))); + } + + RelationClose(rel); +} + +void TrAdjustFrozenXid64(Oid dbid, TransactionId *frozenXID) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple rbtup; + + if (!TcapFeatureAvail()) { + return; + } + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); + while ((rbtup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbtup); + TransactionId rcyfrozenxid64; + + if (rbForm->rcydbid != dbid || (rbForm->rcytype != RB_OBJ_TABLE && rbForm->rcytype != RB_OBJ_TOAST)) { + continue; + } + + rcyfrozenxid64 = TrRbGetRcyfrozenxid64(rbtup, rbRel); + Assert(TransactionIdIsNormal(rcyfrozenxid64)); + + if (TransactionIdPrecedes(rcyfrozenxid64, *frozenXID)) { + *frozenXID = rcyfrozenxid64; + } + } + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return; +} + +bool TrRbIsEmptyDb(Oid dbid) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple tup; + ScanKeyData skey[1]; + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(dbid)); + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 1, skey); + tup = systable_getnext(sd); + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return tup == NULL; +} + +bool TrRbIsEmptySpc(Oid spcId) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(spcId)); + + sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 1, skey); + tup = systable_getnext(sd); + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return tup == NULL; +} + +bool TrRbIsEmptySchema(Oid nspId) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[2]; + HeapTuple tup; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(nspId)); + ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + + sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 2, skey); + tup = systable_getnext(sd); + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return tup == NULL; +} + +bool TrRbIsEmptyUser(Oid roleId) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple tup; + bool found = false; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((TrObjType)rbForm->rcytype != RB_OBJ_TABLE || rbForm->rcyowner != roleId) { + continue; + } + + found = true; + break; + } + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return !found; +} + +static bool TrOidExists(const List *lOid, Oid oid) +{ + ListCell *cell = NULL; + if (lOid == NULL) { + return false; + } + + foreach (cell, lOid) { + if (oid == (*(Oid *)lfirst(cell))) { + return true; + } + } + return false; +} + +List *TrGetDbListRcy(void) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple tup; + + List *lName = NIL; + List *lOid = NIL; + char *dbname = NULL; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if (TrOidExists(lOid, rbForm->rcydbid)) { + continue; + } + Oid *oid = (Oid *)palloc0(sizeof(Oid)); + *oid = rbForm->rcydbid; + lOid = lappend(lOid, oid); + + dbname = get_database_name(rbForm->rcydbid); + if (dbname == NULL) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%u\" does not exist", rbForm->rcydbid))); + } + lName = lappend(lName, dbname); + } + + list_free_deep(lOid); + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return lName; +} + +List *TrGetDbListSpc(Oid spcId) +{ + List *lName = NIL; + List *lOid = NIL; + char *dbname = NULL; + + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcytablespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(spcId)); + + sd = systable_beginscan(rbRel, RecyclebinDbidSpcidRcycsnIndexId, true, NULL, 1, skey); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if (TrOidExists(lOid, rbForm->rcydbid)) { + continue; + } + Oid *oid = (Oid *)palloc0(sizeof(Oid)); + *oid = rbForm->rcydbid; + lOid = lappend(lOid, oid); + + dbname = get_database_name(rbForm->rcydbid); + if (dbname == NULL) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%u\" does not exist", rbForm->rcydbid))); + } + lName = lappend(lName, dbname); + } + + list_free_deep(lOid); + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return lName; +} + +List *TrGetDbListSchema(Oid nspId) +{ + Relation rbRel; + SysScanDesc sd; + ScanKeyData skey[1]; + HeapTuple tup; + + List *lName = NIL; + List *lOid = NIL; + char *dbname = NULL; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcynamespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(nspId)); + + sd = systable_beginscan(rbRel, RecyclebinDbidNspOrinameIndexId, true, NULL, 1, skey); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if (TrOidExists(lOid, rbForm->rcydbid)) { + continue; + } + Oid *oid = (Oid *)palloc0(sizeof(Oid)); + *oid = rbForm->rcydbid; + lOid = lappend(lOid, oid); + + dbname = get_database_name(rbForm->rcydbid); + if (dbname == NULL) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%u\" does not exist", rbForm->rcydbid))); + } + lName = lappend(lName, dbname); + } + + list_free_deep(lOid); + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return lName; +} + +List *TrGetDbListUser(Oid roleId) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple tup; + + List *lName = NIL; + List *lOid = NIL; + char *dbname = NULL; + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + + sd = systable_beginscan(rbRel, InvalidOid, false, NULL, 0, NULL); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((TrObjType)rbForm->rcytype != RB_OBJ_TABLE || rbForm->rcyowner != roleId) { + continue; + } + if (TrOidExists(lOid, rbForm->rcydbid)) { + continue; + } + Oid *oid = (Oid *)palloc0(sizeof(Oid)); + *oid = rbForm->rcydbid; + lOid = lappend(lOid, oid); + + dbname = get_database_name(rbForm->rcydbid); + if (dbname == NULL) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%u\" does not exist", rbForm->rcydbid))); + } + + lName = lappend(lName, dbname); + } + + list_free_deep(lOid); + + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return lName; +} + +/* + * TrGetDatabaseList + * Return a list of all databases found in pg_database. + */ +List *TrGetDbListAuto(void) +{ + List* dblist = NIL; + Relation rel; + SysScanDesc sd; + HeapTuple tup; + + rel = heap_open(DatabaseRelationId, AccessShareLock); + sd = systable_beginscan(rel, InvalidOid, false, NULL, 0, NULL); + + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_database pgdatabase = (Form_pg_database)GETSTRUCT(tup); + if (strcmp(NameStr(pgdatabase->datname), "template0") == 0 || + strcmp(NameStr(pgdatabase->datname), "template1") == 0) { + continue; + } + dblist = lappend(dblist, pstrdup(NameStr(pgdatabase->datname))); + } + + systable_endscan(sd); + heap_close(rel, AccessShareLock); + + return dblist; +} + +static bool TrObjInRecyclebin(const ObjectAddress *obj) +{ + Relation rbRel; + SysScanDesc sd; + HeapTuple tup; + ScanKeyData skey[2]; + bool found = false; + + if (getObjectClass(obj) != OCLASS_CLASS) { + return false; + } + + ScanKeyInit(&skey[0], Anum_pg_recyclebin_rcydbid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(u_sess->proc_cxt.MyDatabaseId)); + ScanKeyInit(&skey[1], Anum_pg_recyclebin_rcyrelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(obj->objectId)); + + rbRel = heap_open(RecyclebinRelationId, AccessShareLock); + sd = systable_beginscan(rbRel, RecyclebinDbidRelidIndexId, true, NULL, 2, skey); + while ((tup = systable_getnext(sd)) != NULL) { + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(tup); + if ((TrObjType)rbForm->rcyoperation == 'd') { + found = true; + break; + } + } + systable_endscan(sd); + heap_close(rbRel, AccessShareLock); + + return found; +} + +/* + * May this object be a recyclebin object? + * true: with "BIN$" prefix, or not a Relation\Type\Trigger\Constraint\Rule + * false: without "BIN$" prefix, or not exists + */ +static bool TrMaybeRbObject(Oid classid, Oid objid, const char *objname = NULL) +{ + HeapTuple tup; + + /* Note: we preserve rule origin name when RbDrop. */ + if (classid != RewriteRelationId && objname) { + return strncmp(objname, "BIN$", 4) == 0; + } + + switch (classid) { + case RelationRelationId: + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(objid)); + if (tup != NULL) { + objname = NameStr(((Form_pg_class)GETSTRUCT(tup))->relname); + ReleaseSysCache(tup); + } + break; + case TypeRelationId: + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objid)); + if (tup != NULL) { + objname = NameStr(((Form_pg_type)GETSTRUCT(tup))->typname); + ReleaseSysCache(tup); + } + break; + case TriggerRelationId: { + Relation relTrig; + ScanKeyData skey[1]; + SysScanDesc sd; + + relTrig = heap_open(TriggerRelationId, AccessShareLock); + ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(objid)); + sd = systable_beginscan(relTrig, TriggerOidIndexId, true, NULL, 1, skey); + if ((tup = systable_getnext(sd)) != NULL) { + objname = NameStr(((Form_pg_trigger)GETSTRUCT(tup))->tgname); + } + systable_endscan(sd); + heap_close(relTrig, AccessShareLock); + break; + } + case ConstraintRelationId: + tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(objid)); + if (tup != NULL) { + objname = NameStr(((Form_pg_constraint)GETSTRUCT(tup))->conname); + ReleaseSysCache(tup); + } + break; + case NamespaceRelationId: + /* Treate Namespace as non-recyclebin object. */ + return false; + default: + /* May be a recyclebin object. */ + return true; + } + + if (objname) { + return strncmp(objname, "BIN$", 4) == 0; + } + + return false; +} + +static bool TrIsRefRbObjectImpl(Relation depRel, const ObjectAddress *obj, ObjectAddresses *objSet) +{ + int startIdx; + + if (TrObjInRecyclebin(obj)) { + return true; + } + + startIdx = objSet->numrefs; + + if (!TrMaybeRbObject(obj->classId, obj->objectId)) { + return false; + } + + TrFindAllRefObjs(depRel, obj, objSet, true); + TrFindAllInternalObjs(depRel, obj, objSet, true); + + for (int i = startIdx; i < objSet->numrefs; i++) { + if (TrIsRefRbObjectImpl(depRel, &objSet->refs[i], objSet)) { + return true; + } + } + + return false; +} + +/* object is a rb object, or reference to a rb object. */ +static bool TrIsRefRbObject(const ObjectAddress *obj, Relation depRel) +{ + ObjectAddresses *objSet = new_object_addresses(); + bool relArgNull = depRel == NULL; + bool result = false; + + if (relArgNull) { + depRel = heap_open(DependRelationId, AccessShareLock); + } + + /* Note: we not care obj->deptype here. */ + add_object_address_ext1(obj, objSet); + + result = TrIsRefRbObjectImpl(depRel, obj, objSet); + + free_object_addresses(objSet); + + if (relArgNull) { + heap_close(depRel, AccessShareLock); + } + + return result; +} + +bool TrIsRefRbObjectEx(Oid classid, Oid objid, const char *objname) +{ + if (!TcapFeatureAvail()) { + return false; + } + + /* Note: we preserve rule origin name when RbDrop. */ + if (TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId)) { + return false; + } + + if (classid != RewriteRelationId && objname && strncmp(objname, "BIN$", 4) != 0) { + return false; + } + + ObjectAddress obj = {classid, objid}; + + return TrIsRefRbObject(&obj); +} + +void TrForbidAccessRbDependencies(Relation depRel, const ObjectAddress *depender, + const ObjectAddress *referenced, int nreferenced) +{ + if (!TcapFeatureAvail()) { + return; + } + + if (IsInitdb || TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId)) { + return; + } + + if (TrIsRefRbObject(depender, depRel)) { + elog (ERROR, "can not access recycle object."); + } + + for (int i = 0; i < nreferenced; i++, referenced++) { + if (TrIsRefRbObject(referenced, depRel)) { + elog (ERROR, "can not access recycle object."); + } + } + + return; +} + +void TrForbidAccessRbObject(Oid classid, Oid objid, const char *objname) +{ + if (!TcapFeatureAvail()) { + return; + } + + if (TrRbIsEmptyDb(u_sess->proc_cxt.MyDatabaseId) || !TrMaybeRbObject(classid, objid, objname)) { + return; + } + + ObjectAddress obj = {classid, objid}; + if (TrIsRefRbObject(&obj)) { + elog (ERROR, "can not access recycle object."); + } + + return; +} + +Datum gs_is_recycle_object(PG_FUNCTION_ARGS) +{ + int classid = PG_GETARG_INT32(0); + int objid = PG_GETARG_INT32(1); + Name objname = PG_GETARG_NAME(2); + bool result = false; + result = TrIsRefRbObjectEx(classid, objid, NameStr(*objname)); + PG_RETURN_BOOL(result); +} diff --git a/src/lib/alarm/alarm_log.cpp b/src/lib/alarm/alarm_log.cpp index ad80bebbb..ac54634fa 100644 --- a/src/lib/alarm/alarm_log.cpp +++ b/src/lib/alarm/alarm_log.cpp @@ -245,10 +245,17 @@ void create_system_alarm_log(const char* sys_log_path) } (void)closedir(dir); } - +/* + * function name : Cleans the system alarm log + * + * This function is responsible for cleaning the system alarm log file. + * + * param file_name:The name of the system alarm log file. + * param sys_log_path:The path to the system alarm log file. + */ void clean_system_alarm_log(const char* file_name, const char* sys_log_path) { - Assert(file_name != NULL); + Assert(file_name != NULL);// Ensure that the file name is not NULL. unsigned long filesize = 0; struct stat statbuff; diff --git a/src/test/grayscale_upgrade/upgradeCheck.py b/src/test/grayscale_upgrade/upgradeCheck.py index 48b5354c5..495d63171 100644 --- a/src/test/grayscale_upgrade/upgradeCheck.py +++ b/src/test/grayscale_upgrade/upgradeCheck.py @@ -211,12 +211,12 @@ class Pterodb(): def spliceSqlFile(self, fileDir, scriptType="_"): try: - NewVersionNum = self.getNewVersionNum() + NewVersionNum = self.getNewVersionNum() #Version number and file handling BaseVersionNum = self.upgrade_from - fileAllList = os.listdir(fileDir) + fileAllList = os.listdir(fileDir) #File and directory operations privateFileAllList = [] keyElement = [] - if fileDir != check_upgrade_path: + if fileDir != check_upgrade_path: #Filter the script privateFileAllList = os.listdir(private_dict[fileDir]) commonScriptList = list(set(fileAllList) & set(privateFileAllList)) commonScriptList = [script for script in commonScriptList if "407" not in script] @@ -237,7 +237,7 @@ class Pterodb(): if key not in name: errMsg = "The script {0} name does not meet the specifications, it needs to contain {1}".format(name, key) self.writeLogFile(errMsg) - raise Exception(errMsg) + raise Exception(errMsg) #Exception handling result = [] if len(allList) != 0: @@ -264,9 +264,9 @@ class Pterodb(): file.write("START TRANSACTION;") file.write(os.linesep) file.write("SET IsInplaceUpgrade = on;") - file.write(os.linesep) - self.writeLogFile("fileDir is {0}, The list of files being written is {1}".format(fileDir, fileList)) - for each_file in fileList: + file.write(os.linesep) #Open the file and write the transaction and setting statements + self.writeLogFile("fileDir is {0}, The list of files being written is {1}".format(fileDir, fileList)) #Write file information + for each_file in fileList: #Process each file if os.path.isfile("%s/%s" % (fileDir, each_file)): each_file_with_path = "%s/%s" % (fileDir, each_file) elif os.path.isfile("%s/%s" % (private_dict[fileDir], each_file)): @@ -278,27 +278,34 @@ class Pterodb(): for txt in open(each_file_with_path,'r'): file.write(txt) file.write(os.linesep) - file.write("COMMIT;") + file.write("COMMIT;") #Write a transaction commit statement file.write(os.linesep) - file.close() + file.close() #Close the file and complete the information record self.writeLogFile("Complate file {0} with the list:{1}".format(fileName, fileList)) - def checkSqlResult(self, Type = "upgrade"): + def checkSqlResult(self, Type = "upgrade"): #Check whether the SQL execution results contain ERROR cmd = "grep ERROR " + exec_sql_log (status, output) = commands.getstatusoutput(cmd) if(output.find("ERROR") != -1): raise Exception("Failed to execute catalog %s" % Type) - cmd = "grep PANIC " + exec_sql_log + cmd = "grep PANIC " + exec_sql_log #Check whether the SQL execution results contain PANIC (status, output) = commands.getstatusoutput(cmd) if(output.find("PANIC") != -1): raise Exception("Failed to execute catalog %s" % Type) - cmd = "grep FATAL " + exec_sql_log + cmd = "grep FATAL " + exec_sql_log #Check whether the execution result contains FATAL (status, output) = commands.getstatusoutput(cmd) - if(output.find("FATAL") != -1): + if(output.find("FATAL") != -1): raise Exception("Failed to execute catalog %s" % Type) def upgrade_one_database(self, db_name): """ + Upgrade a database + + Parameters : + db _ name ( str ) : Database name + + Anomaly : + Exception : An exception is thrown when the upgrade directory fails """ try: if db_name == "postgres": @@ -321,6 +328,13 @@ class Pterodb(): def rollback_one_database(self, db_name): """ + Roll back a database. + + Args: + db_name (str): The name of the database. + + Raises: + Exception: If rolling back the catalogs fails. """ try: if db_name == "postgres": diff --git a/src/test/locale/test-ctype.cpp b/src/test/locale/test-ctype.cpp index 71cd251cb..8c54eaee4 100644 --- a/src/test/locale/test-ctype.cpp +++ b/src/test/locale/test-ctype.cpp @@ -27,6 +27,7 @@ the author shall be liable for any damage, etc. char* flag(int b); void describe_char(int c); +// Function to return a flag string based on a boolean value #undef LONG_FLAG char* flag(int b) @@ -38,6 +39,7 @@ char* flag(int b) #endif } +// Function to describe the properties of a character void describe_char(int c) { unsigned char cp = c, up = toupper(c), lo = tolower(c); @@ -49,6 +51,7 @@ void describe_char(int c) if (!isprint(lo)) lo = ' '; + // Print the character's properties in a formatted line printf("chr#%-4d%2c%6s%6s%6s%6s%6s%6s%6s%6s%6s%6s%6s%4c%4c\n", c, cp, @@ -72,6 +75,7 @@ int main() short c; char* cur_locale = NULL; + // Set the locale to the user's environment setting cur_locale = setlocale(LC_ALL, ""); if (cur_locale) fprintf(stderr, "Successfully set locale to \"%s\"\n", cur_locale); @@ -82,7 +86,10 @@ int main() return 1; } + // Print the table header printf("char# char alnum alpha cntrl digit lower graph print punct space upper xdigit lo up\n"); + + // Iterate from 0 to 255 and describe each character for (c = 0; c <= 255; c++) describe_char(c); diff --git a/src/test/performance/results/PgSQL.970926 b/src/test/performance/results/PgSQL.970926 index 6efab3fd1..59faa0415 100644 --- a/src/test/performance/results/PgSQL.970926 +++ b/src/test/performance/results/PgSQL.970926 @@ -1,4 +1,7 @@ -DBMS: PostgreSQL 6.2b10 +//Information of the performance test results of the database system + + +DBMS: PostgreSQL 6.2b10 OS: FreeBSD 2.1.5-RELEASE HardWare: i586/90, 24M RAM, IDE StartUp: postmaster -B 256 '-o -S 2048' -S diff --git a/src/test/whitebox/knl_whitebox_test.cpp b/src/test/whitebox/knl_whitebox_test.cpp index cf7598c2a..ab07544c3 100644 --- a/src/test/whitebox/knl_whitebox_test.cpp +++ b/src/test/whitebox/knl_whitebox_test.cpp @@ -32,16 +32,23 @@ const uint32 BUFSIZE = 1024; bool VerifyBeforeTest (const char* newval) { + // Check if g_instance.whitebox_test_param_instance is already initialized with the same test file name if (g_instance.whitebox_test_param_instance && strcmp(newval, g_instance.whitebox_test_param_instance->test_file_name) == 0) { elog(LOG, "AssignUstoreUnitTest: g_instance.whitebox_test_param_instance is already initialized"); return false; } + + // If g_instance.whitebox_test_param_instance is not initialized, allocate memory for it if (!g_instance.whitebox_test_param_instance) { g_instance.whitebox_test_param_instance = (WhiteboxTestParam *)palloc(sizeof(WhiteboxTestParam) * (MAX_UNIT_TEST)); } + + // Set the error level to WARNING for whitebox_test_param_instance g_instance.whitebox_test_param_instance->elevel = WARNING; + + // Copy the test file name to whitebox_test_param_instance->test_file_name int rc = sprintf_s(g_instance.whitebox_test_param_instance->test_file_name, MAX_NAME_STR_LEN, "%s", newval); securec_check_ss_c(rc, "\0", "\0"); if (strcmp(newval, "") == 0) { @@ -243,6 +250,7 @@ bool WhiteboxTestSleep(const char* functionName, int timeout, int skipIteration, } } +//The WhiteboxTestSuspend function suspends execution at a breakpoint for a specified timeout if enabled. bool WhiteboxTestSuspend(const char* functionName, int timeout, const bool& enabled) { int elapseTime = 0; @@ -264,6 +272,7 @@ bool WhiteboxTestSuspend(const char* functionName, int timeout, const bool& enab } } +//this function iterates over the available test cases in the whitebox test parameters instance and searches for a matching functionName bool WhiteboxTestSetEnable(const char* functionName, bool enabled) { for (int i = 0; i < g_instance.whitebox_test_param_instance->num_testcase; i++) {