From 7b9a65f8d95955cf01baef8a1cbcd7a07de66b60 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:10:52 +0800 Subject: [PATCH 01/26] Update analyze.cpp --- src/common/backend/parser/analyze.cpp | 36 +++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 2baa41849..3f64e8084 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -152,31 +152,55 @@ static const char* NOKEYUPDATE_KEYSHARE_ERRMSG = ""; * transformation, while utility-type statements are simply hung off * a dummy CMD_UTILITY Query node. */ + + /* + * @Description:Analyze the raw syntax tree, do semantic analysis and output query tree. + * @Param[IN] parseTree: a raw parse tree(abstract syntax tree). + * @Param[IN] sourceText: The source text of the query statement. + * @Param[IN] numParams: number of params. + * @Param[IN] paramTypes: stores the type OID of each parameter. + * @Return:an analyzed query tree(of Query class). + */ Query* parse_analyze( Node* parseTree, const char* sourceText, Oid* paramTypes, int numParams, bool isFirstNode, bool isCreateView) { + // Initialization of ParseState object. ParseState* pstate = make_parsestate(NULL); + // Declaration of the query tree. Query* query = NULL; - /* required as of 8.4 */ + // sourceText can't be NULL. AssertEreport(sourceText != NULL, MOD_OPT, "para cannot be NULL"); pstate->p_sourcetext = sourceText; if (numParams > 0) { + /* + * parse_fixed_parameters() was defined in parse_param.cpp. + * + * This function converts the reference contained in the query into a + * fixed parameter structure, that is, pass in the reference parameters + * of the query statement and constructs a FixedParamState structure + * for it. + */ parse_fixed_parameters(pstate, paramTypes, numParams); } PUSH_SKIP_UNIQUE_SQL_HOOK(); + + // Converts the raw parse tree into a query tree. query = transformTopLevelStmt(pstate, parseTree, isFirstNode, isCreateView); - POP_SKIP_UNIQUE_SQL_HOOK(); - + + POP_SKIP_UNIQUE_SQL_HOOK(); + /* it's unsafe to deal with plugins hooks as dynamic lib may be released */ if (post_parse_analyze_hook && !(g_instance.status > NoShutdown)) { (*post_parse_analyze_hook)(pstate, query); } pfree_ext(pstate->p_ref_hook_state); + + // release the ParseState object pstate. free_parsestate(pstate); /* For plpy CTAS query. CTAS is a recursive call. CREATE query is the first rewrited. @@ -185,7 +209,8 @@ Query* parse_analyze( */ query->fixed_paramTypes = paramTypes; query->fixed_numParams = numParams; - + + // return the analyzed raw parse tree, that is, the query tree. return query; } @@ -228,6 +253,7 @@ Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** pa /* * parse_sub_analyze * Entry point for recursively analyzing a sub-statement. + * 递归分析子查询的入口函数 */ Query* parse_sub_analyze(Node* parseTree, ParseState* parentParseState, CommonTableExpr* parentCTE, bool locked_from_parent, bool resolve_unknowns) @@ -4732,4 +4758,4 @@ static bool checkAllowedTableCombination(ParseState* pstate) Assert(has_ustore || has_else); return !(has_ustore && has_else); -} +} \ No newline at end of file -- 2.34.1 From 8e01f2eeebf38d53f4a86bca1cacc0e52af12531 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:11:19 +0800 Subject: [PATCH 02/26] Update keywords.cpp --- src/common/backend/parser/keywords.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/backend/parser/keywords.cpp b/src/common/backend/parser/keywords.cpp index 785635e25..c01845142 100644 --- a/src/common/backend/parser/keywords.cpp +++ b/src/common/backend/parser/keywords.cpp @@ -21,9 +21,11 @@ #define PG_KEYWORD(a, b, c) {a, b, c}, +// A table that stores keywords information. +// This table will be used in keyword-matching process. const ScanKeyword ScanKeywords[] = { #include "parser/kwlist.h" }; +// The number of table items, which is also the total number of keywords const int NumScanKeywords = lengthof(ScanKeywords); - -- 2.34.1 From 488c48fab009ea37b7b0b504c0d1faea471e9e37 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:11:42 +0800 Subject: [PATCH 03/26] Update kwlookup.cpp --- src/common/backend/parser/kwlookup.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/common/backend/parser/kwlookup.cpp b/src/common/backend/parser/kwlookup.cpp index ba028baee..899c96a10 100644 --- a/src/common/backend/parser/kwlookup.cpp +++ b/src/common/backend/parser/kwlookup.cpp @@ -28,7 +28,7 @@ * * Returns a pointer to the ScanKeyword table entry, or NULL if no match. * - * The match is done case-insensitively. Note that we deliberately use a + * The match is done case-insensitively. Note that we deliberately use a * dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z', * even if we are in a locale where tolower() would produce more or different * translations. This is to conform to the SQL99 spec, which says that @@ -36,13 +36,20 @@ * receive a different case-normalization mapping. */ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywords, int num_keywords) -{ +{ /* + * @param[IN] text:The identifier to be matched. + * @param[IN] keywords:A pointer to the keyword table. + * @param[IN] num_keywords: The number of keywords table items (keywords). + * @param[OUT]:A pointer to a keywords table item. Null is returned if the match fails. + */ + int len, i; - char word[NAMEDATALEN] = {0}; - const ScanKeyword* low = NULL; - const ScanKeyword* high = NULL; + char word[NAMEDATALEN] = {0}; + const ScanKeyword* low = NULL; // Pointer used in the binary search. + const ScanKeyword* high = NULL; // Pointer used in the binary search. - if (text == NULL) { + // The input is NULL, no match is made, and NULL is returned. + if (text == NULL) { return NULL; } @@ -64,7 +71,7 @@ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywor } word[i] = ch; } - word[len] = '\0'; + word[len] = '\0'; // The converted text should ends with '\0'. /* * Now do a binary search using plain strcmp() comparison. @@ -85,6 +92,6 @@ const ScanKeyword* ScanKeywordLookup(const char* text, const ScanKeyword* keywor high = middle - 1; } } - + // The binary search fails and returns a null value. return NULL; -} +} \ No newline at end of file -- 2.34.1 From af24481deb31a2bfa1466bfc9d97a26f3bf14208 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:11:58 +0800 Subject: [PATCH 04/26] Update parser.cpp --- src/common/backend/parser/parser.cpp | 36 +++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index d91fb17cc..d7541e831 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -9,7 +9,6 @@ * the grammar are "raw" parsetrees that still need to be analyzed by * analyze.c and related files. * - * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -40,15 +39,15 @@ static void resetCreateFuncFlag() /* * raw_parser - * Given a query in string form, do lexical and grammatical analysis. + * Given a query in string form, do lexical and grammatical analysis. * * Returns a list of raw (un-analyzed) parse trees. */ List* raw_parser(const char* str, List** query_string_locationlist) { - core_yyscan_t yyscanner; - base_yy_extra_type yyextra; - int yyresult; + core_yyscan_t yyscanner; + base_yy_extra_type yyextra; // An intermediate variable that holds the returned syntax tree. + int yyresult; // The calling result of base_yyparse(). /* reset u_sess->parser_cxt.stmt_contains_operator_plus */ resetOperatorPlusFlag(); @@ -58,7 +57,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) /* reset u_sess->parser_cxt.isCreateFuncOrProc */ resetCreateFuncFlag(); - + /* initialize the flex scanner */ yyscanner = scanner_init(str, &yyextra.core_yy_extra, ScanKeywords, NumScanKeywords); @@ -89,6 +88,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) } } + // Returns the generated syntax tree. return yyextra.parsetree; } @@ -127,7 +127,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) * words. Furthermore it's not clear how to do it without re-introducing * scanner backtrack, which would cost more performance than this filter * layer does. - * + * * The filter also provides a convenient place to translate between * the core_YYSTYPE and YYSTYPE representations (which are really the * same thing anyway, but notationally they're different). @@ -489,8 +489,8 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) /* * @Description: Check whether its a empty query with only comments and semicolon. - * @Param query_string: the query need check. - * @retrun:true or false. + * @Param[IN] query_string: the query need check. + * @return:the bool value of the check result. */ static bool is_empty_query(char* query_string) { @@ -513,6 +513,7 @@ static bool is_empty_query(char* query_string) end_comment_postion = strstr(query_string, end_comment); query_string = end_comment_postion + 2; while (isspace((unsigned char)*query_string)) { + // Trim the spaces after comments. query_string++; } } @@ -537,11 +538,12 @@ static bool is_empty_query(char* query_string) char** get_next_snippet( char** query_string_single, const char* query_string, List* query_string_locationlist, int* stmt_num) { - int query_string_location_start = 0; - int query_string_location_end = -1; - char* query_string_single_p = NULL; - int single_query_string_len = 0; - + int query_string_location_start = 0; // The starting position of the query statement. + int query_string_location_end = -1; // The ending position of the query statement. + char* query_string_single_p = NULL; // An intermediate variable used to copy the string. + int single_query_string_len = 0; // The length of the query statement. + + // Count the number of query statements. int stmt_count = list_length(query_string_locationlist); /* Malloc memory for single query here just for the first time. */ @@ -558,11 +560,13 @@ char** get_next_snippet( * Notice : The locationlist only store the end postion of each single query but not any * start postion. */ + // Calculate the starting position of the specified query statement. if (*stmt_num == 0) { query_string_location_start = 0; } else { query_string_location_start = list_nth_int(query_string_locationlist, *stmt_num - 1) + 1; } + // Calculate the ending position of the specified query statement. query_string_location_end = list_nth_int(query_string_locationlist, (*stmt_num)++); /* Malloc memory for each single query string. */ @@ -583,10 +587,10 @@ char** get_next_snippet( */ if (is_empty_query(query_string_single[*stmt_num - 1])) { continue; - } else { + } else { // The obtained query statement is not a null query, exit the loop, and return this statement. break; } } return query_string_single; -} +} \ No newline at end of file -- 2.34.1 From 70cd993234b6c11a30c2dbf498ae76d7b0b75cb3 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:12:14 +0800 Subject: [PATCH 05/26] Update scansup.cpp --- src/common/backend/parser/scansup.cpp | 53 +++++++++++++++------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/common/backend/parser/scansup.cpp b/src/common/backend/parser/scansup.cpp index dc11a225d..3ee8ba1c1 100644 --- a/src/common/backend/parser/scansup.cpp +++ b/src/common/backend/parser/scansup.cpp @@ -21,30 +21,29 @@ #include "parser/scansup.h" #include "mb/pg_wchar.h" -/* ---------------- - * scanstr - * - * if the string passed in has escaped codes, map the escape codes to actual - * chars +/* + * scanstr() --- if the string passed in has escaped codes, map the escape + * codes to actual chars. * * the string returned is palloc'd and should eventually be pfree'd by the * caller! - * ---------------- */ char* scanstr(const char* s) { char* newStr = NULL; int len, i, j; - if (s == NULL || s[0] == '\0') + if (s == NULL || s[0] == '\0') // The input string is empty. return pstrdup(""); len = strlen(s); - newStr = (char*)palloc(len + 1); /* string cannot get longer */ + // The new string constructed does not exceed the length of the original string. + // That is because the processes of the escape code would get the string shorter. + newStr = (char*)palloc(len + 1); /* string cannot get longer */ for (i = 0, j = 0; i < len; i++) { - if (s[i] == '\'') { + if (s[i] == '\'') { /* * Note: if scanner is working right, unescaped quotes can only * appear in pairs, so there should be another character. @@ -52,11 +51,11 @@ char* scanstr(const char* s) i++; newStr[j] = s[i]; } else if (s[i] == '\\') { - i++; + i++; // "\" and a next character are translated together into an escape symbol. switch (s[i]) { case 'b': newStr[j] = '\b'; - break; + break; case 'f': newStr[j] = '\f'; break; @@ -76,18 +75,20 @@ char* scanstr(const char* s) case '4': case '5': case '6': - case '7': { + case '7': { + // case '0'~'7', represents this is the highest bit of an octal number. int k; unsigned long octVal = 0; - + // Converts the next consecutive digits of up to three 0-7 digits into an octal number. for (k = 0; s[i + k] >= '0' && s[i + k] <= '7' && k < 3; k++){ octVal = (octVal << 3) + (s[i + k] - '0'); } i += k - 1; - newStr[j] = ((char)octVal); + // According to the octal numeric value corresponding to the ASCII code to determine the specific characters. + newStr[j] = ((char)octVal); } break; default: - newStr[j] = s[i]; + newStr[j] = s[i]; break; } /* switch */ } /* s[i] == '\\' */ @@ -96,7 +97,7 @@ char* scanstr(const char* s) } j++; } - newStr[j] = '\0'; + newStr[j] = '\0'; // The new string constructed should end with '\0'. return newStr; } @@ -120,6 +121,8 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) bool enc_is_single_byte = false; result = (char*)palloc(len + 1); + // Gets the maximum byte of the database encoding. + // enc_is_single_byte = TRUE IF it is single-byte encoding. enc_is_single_byte = pg_database_encoding_max_length() == 1; /* @@ -131,7 +134,7 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) * the high bit set, as long as they aren't part of a multi-byte character, * and use an ASCII-only downcasing for 7-bit characters. */ - for (i = 0; i < len; i++) { + for (i = 0; i < len; i++) { // Implementation of downcasing. unsigned char ch = (unsigned char)ident[i]; if (ch >= 'A' && ch <= 'Z') { @@ -143,7 +146,8 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) } result[i] = '\0'; - if (i >= NAMEDATALEN) + // The length exceeds the identifier's maximum length, so truncate. + if (i >= NAMEDATALEN) truncate_identifier(result, i, warn); return result; @@ -152,15 +156,13 @@ char* downcase_truncate_identifier(const char* ident, int len, bool warn) /* * truncate_identifier() --- truncate an identifier to NAMEDATALEN-1 bytes. * - * The given string is modified in-place, if necessary. A warning is - * issued if requested. - * * We require the caller to pass in the string length since this saves a * strlen() call in some common usages. */ void truncate_identifier(char* ident, int len, bool warn) { if (len >= NAMEDATALEN) { + // Calculate the appropriate length after truncation. len = pg_mbcliplen(ident, len, NAMEDATALEN - 1); if (warn) { /* @@ -169,14 +171,17 @@ void truncate_identifier(char* ident, int len, bool warn) */ char buf[NAMEDATALEN]; errno_t rc; - + rc = memcpy_s(buf, NAMEDATALEN, ident, len); + securec_check(rc, "\0", "\0"); buf[len] = '\0'; + ereport(NOTICE, (errcode(ERRCODE_NAME_TOO_LONG), errmsg("identifier \"%s\" will be truncated to \"%s\"", ident, buf))); } - ident[len] = '\0'; + // Truncate by setting the end character of the identifier to '\0'. + ident[len] = '\0'; } } @@ -197,4 +202,4 @@ bool scanner_isspace(char ch) } return false; -} +} \ No newline at end of file -- 2.34.1 From 6743204011dff7b9fe1a84ca61ea2657333f49a6 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:12:40 +0800 Subject: [PATCH 06/26] Update parse_param.cpp --- src/common/backend/parser/parse_param.cpp | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/common/backend/parser/parse_param.cpp b/src/common/backend/parser/parse_param.cpp index 661ec4a81..c9de7007e 100644 --- a/src/common/backend/parser/parse_param.cpp +++ b/src/common/backend/parser/parse_param.cpp @@ -3,7 +3,7 @@ * parse_param.cpp * handle parameters in parser * - * This code covers two cases that are used within the core backend: + * This code covers two cases that are used within the core backend: * * a fixed list of parameters with known types * * an expandable list of parameters whose types can optionally * be determined from context @@ -33,7 +33,8 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" -typedef struct FixedParamState { +// Stmt of fixed parameters. +typedef struct FixedParamState { Oid* paramTypes; /* array of parameter type OIDs */ int numParams; /* number of array entries */ } FixedParamState; @@ -44,6 +45,7 @@ typedef struct FixedParamState { * hasn't been seen, while UNKNOWNOID means the parameter has been used but * its type is not yet known. */ +// Stmt of variable parameters. typedef struct VarParamState { Oid** paramTypes; /* array of parameter type OIDs */ int* numParams; /* number of array entries */ @@ -64,10 +66,12 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate); */ void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams) { + // palloc for FixedParamState. FixedParamState* parstate = (FixedParamState*)palloc(sizeof(FixedParamState)); - + // Construct the FixedParamState structure. parstate->paramTypes = paramTypes; parstate->numParams = numParams; + pstate->p_ref_hook_state = (void*)parstate; pstate->p_paramref_hook = fixed_paramref_hook; /* no need to use p_coerce_param_hook */ @@ -78,10 +82,12 @@ void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams) */ void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams) { + // palloc for VarParamState VarParamState* parstate = (VarParamState*)palloc(sizeof(VarParamState)); - + // Construct the VarParamState structure. parstate->paramTypes = paramTypes; parstate->numParams = numParams; + pstate->p_ref_hook_state = (void*)parstate; pstate->p_paramref_hook = variable_paramref_hook; pstate->p_coerce_param_hook = variable_coerce_param_hook; @@ -265,10 +271,10 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate) if (node == NULL) { return false; } - if (IsA(node, Param)) { - Param* param = (Param*)node; + if (IsA(node, Param)) { // If the current node is an object of the Param class + Param* param = (Param*)node; - if (param->paramkind == PARAM_EXTERN) { + if (param->paramkind == PARAM_EXTERN) { // If param is external VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state; int paramno = param->paramid; @@ -288,9 +294,9 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate) } return false; } - if (IsA(node, Query)) { + if (IsA(node, Query)) { // If the current node is an object of the Query class /* Recurse into RTE subquery or not-yet-planned sublink subquery */ return query_tree_walker((Query*)node, (bool (*)())check_parameter_resolution_walker, (void*)pstate, 0); } return expression_tree_walker(node, (bool (*)())check_parameter_resolution_walker, (void*)pstate); -} +} \ No newline at end of file -- 2.34.1 From 52fe47cf55edbac41b750065b85edb63f63ffd19 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:12:56 +0800 Subject: [PATCH 07/26] Update parse_oper.cpp --- src/common/backend/parser/parse_oper.cpp | 94 ++++++++++++++---------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index cf35bc263..98c16b33c 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -2,6 +2,7 @@ * * parse_oper.cpp * handle operator things for parser + * 处理表达式中的操作符 * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -45,21 +46,24 @@ */ /* If your search_path is longer than this, sucks to be you ... */ +// search_path的最大长度 #define MAX_CACHED_PATH_LEN 16 typedef struct OprCacheKey { - char oprname[NAMEDATALEN]; + char oprname[NAMEDATALEN]; // operator名称 Oid left_arg; /* Left input OID, or 0 if prefix op */ + // 左侧操作符的OID,如果这是一个前缀操作符则此项为0. Oid right_arg; /* Right input OID, or 0 if postfix op */ - Oid search_path[MAX_CACHED_PATH_LEN]; + // 右侧操作符的OID,如果这是一个后缀操作符则此项为0. + Oid search_path[MAX_CACHED_PATH_LEN]; // 搜索路径 bool use_a_style_coercion; } OprCacheKey; typedef struct OprCacheEntry { /* the hash lookup key MUST BE FIRST */ - OprCacheKey key; - - Oid opr_oid; /* OID of the resolved operator */ + OprCacheKey key; // 哈希查找值必须是第一项 + /* OID of the resolved operator */ + Oid opr_oid; // 解决后的OID } OprCacheEntry; static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion); @@ -90,12 +94,18 @@ static void make_oper_cache_entry(OprCacheKey* key, Oid opr_oid); Oid LookupOperName(ParseState* pstate, List* opername, Oid oprleft, Oid oprright, bool noError, int location) { Oid result; - + // Search the operator Oid according to the value of OprCacheKey.left_arg.oprname, + // OprCacheKey.left_arg and OprCacheKey.right_arg. + // Returns result if the lookup is successful. result = OpernameGetOprid(opername, oprleft, oprright); if (OidIsValid(result)) return result; /* we don't use op_error here because only an exact match is wanted */ + + // The lookup fails, and passed-in param noError = false, + // Indicates that the caller allows a lookup failure to be treated as an error. + // This if-branch is used to report the error messages. if (!noError) { char oprkind; @@ -166,9 +176,9 @@ void get_sort_group_operators( { TypeCacheEntry* typentry = NULL; int cache_flags; - Oid lt_opr; - Oid eq_opr; - Oid gt_opr; + Oid lt_opr; // Oid of '<' + Oid eq_opr; // Oid of '=' + Oid gt_opr; // Oid of '>' bool hashable = false; /* @@ -183,9 +193,9 @@ void get_sort_group_operators( cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR; typentry = lookup_type_cache(argtype, cache_flags); - lt_opr = typentry->lt_opr; - eq_opr = typentry->eq_opr; - gt_opr = typentry->gt_opr; + lt_opr = typentry->lt_opr; + eq_opr = typentry->eq_opr; + gt_opr = typentry->gt_opr; hashable = OidIsValid(typentry->hash_proc); /* Report errors if needed */ @@ -681,7 +691,8 @@ static void op_error( } /* - * Operator expression construction. + * make_op + * Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. @@ -691,19 +702,25 @@ static void op_error( */ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int location, bool inNumeric) { - Oid ltypeId, rtypeId; - Operator tup; - Form_pg_operator opform; - Oid actual_arg_types[2]; - Oid declared_arg_types[2]; - int nargs; - List* args = NIL; - Oid rettype; - OpExpr* result = NULL; + // Enter the operator name and the structure tree of the left and right operators, + // and then construct the expression structure for them. + + Oid ltypeId, rtypeId; // Oid of left operator and right operator + Operator tup; // The Operator structure to get before constructing OpExpr + Form_pg_operator opform; // Related to type conversion between Pg_Operator and Operator + Oid actual_arg_types[2]; // Oid of the actual left, right operator determined by ltree, rtree + Oid declared_arg_types[2]; // Oid declared in Pg + int nargs; // the number of left and right operators(0/1/2). + List* args = NIL; // tree structure representing the left and right operators + Oid rettype; // Oid of the operator type returned + OpExpr* result = NULL; // The expression structure returned /* Select the operator */ + + // Get the OID of the left and right operators, and then obtain + // the Operator structure of the left and right operators accordingly if (rtree == NULL) { - /* right operator */ + /* right operator */ ltypeId = exprType(ltree); rtypeId = InvalidOid; tup = right_oper(pstate, opname, ltypeId, false, location); @@ -724,7 +741,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo } tup = oper(pstate, opname, ltypeId, rtypeId, false, location, inNumeric); } - + // Related to type conversion between Pg_Operator and Operator opform = check_operator_is_shell(opname, pstate, location, tup); /* Do typecasting and build the expression tree */ @@ -732,13 +749,13 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo /* right operator */ args = list_make1(ltree); actual_arg_types[0] = ltypeId; - declared_arg_types[0] = opform->oprleft; - nargs = 1; + declared_arg_types[0] = opform->oprleft; + nargs = 1; } else if (ltree == NULL) { /* left operator */ - args = list_make1(rtree); - actual_arg_types[0] = rtypeId; - declared_arg_types[0] = opform->oprright; + args = list_make1(rtree); + actual_arg_types[0] = rtypeId; + declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ @@ -747,7 +764,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; - nargs = 2; + nargs = 2; // Both the left operator and the right operator are valid. } /* @@ -755,6 +772,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) */ + // Oid of the operator type returned rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ @@ -762,17 +780,17 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, int lo /* and build the expression node */ result = makeNode(OpExpr); - result->opno = oprid(tup); - result->opfuncid = opform->oprcode; - result->opresulttype = rettype; + result->opno = oprid(tup); + result->opfuncid = opform->oprcode; + result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ - result->args = args; - result->location = location; + result->args = args; + result->location = location; - ReleaseSysCache(tup); + ReleaseSysCache(tup); // Release the cache space - return (Expr*)result; + return (Expr*)result; // Returned as an Expr* type } /* @@ -1026,4 +1044,4 @@ void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) if (hash_search(u_sess->parser_cxt.opr_cache_hash, (void*)&hentry->key, HASH_REMOVE, NULL) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("hash table corrupted"))); } -} +} \ No newline at end of file -- 2.34.1 From ed22d5c2e22560ae08ab79b7437c2cbeb53d641c Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:15:06 +0800 Subject: [PATCH 08/26] Update rewriteHandler.cpp --- .../optimizer/rewrite/rewriteHandler.cpp | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp index c5cabcf01..94951397e 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp @@ -127,6 +127,15 @@ static bool pull_qual_vars_walker(Node* node, pull_qual_vars_context* context); */ void AcquireRewriteLocks(Query* parsetree, bool forUpdatePushedDown) { + /* + * @Param[IN] parsetree: A query tree. We need to acquire suitable + * locks on all the relations mentioned in the Query. + * @Param[IN] forUpdatePushedDown: Indicates that whether there is a + * pushed-down FOR UPDATE/SHARE statement that applies to the + * current subquery. It should always be false at the start + * of the recursion. + * @Return[OUT]: None. + */ ListCell* l = NULL; int rt_index; @@ -1712,6 +1721,7 @@ static bool fireRIRonSubLink(Node* node, List* activeRIRs) /* * fireRIRrules - * Apply all RIR rules on each rangetable entry in a query + * */ static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePushedDown) { @@ -1733,7 +1743,8 @@ static Query* fireRIRrules(Query* parsetree, List* activeRIRs, bool forUpdatePus int i; ++rt_index; - + + // fetch RTE of current parsetree node. rte = rt_fetch(rt_index, parsetree->rtable); /* @@ -2699,14 +2710,17 @@ static List* RewriteQuery(Query* parsetree, List* rewrite_events) return rewritten; } + /* - * QueryRewrite - - * Primary entry point to the query rewriter. - * Rewrite one query via query rewrite system, possibly returning 0 - * or many queries. - * - * NOTE: the parsetree must either have come straight from the parser, - * or have been scanned by AcquireRewriteLocks to acquire suitable locks. + * QueryRewrite()--- + * + * @Description:Primary entry point to the query rewriter. + * Rewrite one query via query rewrite system, possibly returning 0 + * or many queries. + * @Param[IN] parsetree:A Query Tree. It must either have come straight + * from the parser, or have been scanned by AcquireRewriteLocks to + * acquire suitable locks. + * @Return[OUT]: Rewrited queries(0 or several queries). */ List* QueryRewrite(Query* parsetree) { @@ -2722,6 +2736,7 @@ List* QueryRewrite(Query* parsetree) * This function is only applied to top-level original queries */ AssertEreport(parsetree->querySource == QSRC_ORIGINAL, MOD_OPT, ""); + AssertEreport(parsetree->canSetTag, MOD_OPT, ""); /* * Step 1 @@ -2733,11 +2748,12 @@ List* QueryRewrite(Query* parsetree) /* * Step 2 * - * Apply all the RIR rules on each query + * Apply all the RIR rules on each query * * This is also a handy place to mark each query with the original queryId */ results = NIL; + foreach (l, querylist) { Query* query = (Query*)lfirst(l); @@ -3270,4 +3286,4 @@ List* QueryRewriteCTAS(Query* parsetree) } } } -#endif +#endif \ No newline at end of file -- 2.34.1 From 3916fbd442b4aa0f1999d2b27f67e1cba2fe6d24 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:15:23 +0800 Subject: [PATCH 09/26] Update rewriteRemove.cpp --- .../optimizer/rewrite/rewriteRemove.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp b/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp index b01f1b38b..81e3dac7a 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteRemove.cpp @@ -33,7 +33,7 @@ #include "utils/snapmgr.h" /* - * Guts of rule deletion. + * Deletes an existing rewrite rule. */ void RemoveRewriteRuleById(Oid ruleOid) { @@ -45,12 +45,15 @@ void RemoveRewriteRuleById(Oid ruleOid) Oid eventRelationOid; /* - * Open the pg_rewrite relation. + * Open the pg_rewrite relation, and impose an RowExclusiveLock. + * + * RewriteRelationId is external. The caller specifies a specific + * relation by setting its value. */ RewriteRelation = heap_open(RewriteRelationId, RowExclusiveLock); /* - * Find the tuple for the target rule. + * Fetch the tuple for the target rule. */ ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ruleOid)); @@ -58,6 +61,9 @@ void RemoveRewriteRuleById(Oid ruleOid) tuple = systable_getnext(rcscan); + /* + * If the fetch fails, error information is reported + */ if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find tuple for rule %u", ruleOid))); @@ -76,14 +82,15 @@ void RemoveRewriteRuleById(Oid ruleOid) systable_endscan(rcscan); - heap_close(RewriteRelation, RowExclusiveLock); /* * Issue shared-inval notice to force all backends (including me!) to * update relcache entries with the new rule set. */ + heap_close(RewriteRelation, RowExclusiveLock); + CacheInvalidateRelcache(event_relation); /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); -} +} \ No newline at end of file -- 2.34.1 From 6a0a170d45369dc0a89d551aa8d1a73fef5ba6de Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 28 Aug 2023 23:15:33 +0800 Subject: [PATCH 10/26] Update rewriteSupport.cpp --- .../optimizer/rewrite/rewriteSupport.cpp | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp b/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp index 46db8ef86..0da708e74 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteSupport.cpp @@ -31,7 +31,12 @@ #include "utils/snapmgr.h" /* - * Is there a rule by the given name? + * IsDefinedRewriteRule()--- + * + * @Description: Verifies that a relationship has an rewrite rule by a given name. + * @Param[IN] owningRel: Oid of a specified relation. + * @Param[IN] ruleName: Name of the rewrite rule. + * @Return[OUT]: A bool value representing the validation result. */ bool IsDefinedRewriteRule(Oid owningRel, const char* ruleName) { @@ -90,11 +95,15 @@ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingV } /* - * Find rule oid. + * get_rewrite_oid()--- * - * If missing_ok is false, throw an error if rule name not found. If - * true, just return InvalidOid. - */ + * @Description: Find the Oid of a rewrite rule by given its name and rel Oid. + * @Param[IN] relid: Oid of a specified relation. + * @Param[IN] ruleName: Name of the rewrite rule. + * @Param[IN] missing_ok: If missing_ok is false, throw an error if rule name + * not found. If true, just return InvalidOid. + * @Return[OUT]: Rule Oid. + */ Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok) { HeapTuple tuple; @@ -115,6 +124,17 @@ Oid get_rewrite_oid(Oid relid, const char* rulename, bool missing_ok) return ruleoid; } +/* + * get_rewrite_rulename()--- + * + * @Description: Like get_rewrite_oid(), find the name of a rewrite rule by + * given its Oid and relid. Relid is specified by the caller by setting + * the value of RewriteRelationId, which is an external variable. + * @Param[IN] ruleid: Oid of the specified rewrite rule. + * @Param[IN] missing_ok: If missing_ok is false, throw an error if rule Oid + * not found. If true, just return InvalidOid. + * @Return[OUT]: Rule name. + */ char* get_rewrite_rulename(Oid ruleid, bool missing_ok) { ScanKeyData entry; @@ -146,10 +166,16 @@ char* get_rewrite_rulename(Oid ruleid, bool missing_ok) } /* - * input parameter relid is ev_class, ev_type is pg_rewrite->ev_type, CMD_UTILITY means notify, copy, alter rule, + * rel_has_rule()--- + * + * @Description: Given a specific relation and check if it has defined rewriting rule of ev_type. + * @Param[IN] relid: ev_class, Oid of the specified relation. + * @Param[IN] ev_type: CmdType, transfer it to char beacuse it is char in system catalog pg_rewrite. + * @Return[OUT]: A bool value representing the validation result. + * + * Note: ev_type is pg_rewrite->ev_type, CMD_UTILITY means notify, copy, alter rule, * latter two is only used in timeseries table redistribution - * ev_type is CmdType, transfer it to char beacuse it is char in system catalog pg_rewrite - */ + */ bool rel_has_rule(Oid relid, char ev_type) { bool has_rule = false; @@ -172,13 +198,20 @@ bool rel_has_rule(Oid relid, char ev_type) } /* - * Find rule oid, given only a rule name but no rel OID. + * get_rewrite_oid_without_relid()--- * - * If there's more than one, it's an error. If there aren't any, that's an + * @Description: Find rule oid, given only a rule name but no rel OID. + * @Param[IN] ruleName: Name of the rewrite rule. + * @Param[IN] reloid: A pointer to Oid List of all relations. + * @Param[IN] missing_ok: If missing_ok is false, throw an error if rule Oid + * not found. If true, just return InvalidOid. + * @Return[OUT]: Rule Oid or InvalidOid if not found. + * + * Note: If there's more than one, it's an error. If there aren't any, that's an * error, too. In general, this should be avoided - it is provided to support * syntax that is compatible with pre-7.3 versions of PG, where rule names * were unique across the entire database. - */ + */ Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missing_ok) { Relation RewriteRelation; @@ -214,4 +247,4 @@ Oid get_rewrite_oid_without_relid(const char* rulename, Oid* reloid, bool missin heap_close(RewriteRelation, AccessShareLock); return ruleoid; -} +} \ No newline at end of file -- 2.34.1 From ee97a711ee66e3a7d9018b41645fa72266fc1b8d Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Wed, 30 Aug 2023 23:30:30 +0800 Subject: [PATCH 11/26] Update parse_oper.cpp --- src/common/backend/parser/parse_oper.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index 98c16b33c..972651308 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -46,7 +46,6 @@ */ /* If your search_path is longer than this, sucks to be you ... */ -// search_path的最大长度 #define MAX_CACHED_PATH_LEN 16 typedef struct OprCacheKey { @@ -61,9 +60,9 @@ typedef struct OprCacheKey { typedef struct OprCacheEntry { /* the hash lookup key MUST BE FIRST */ - OprCacheKey key; // 哈希查找值必须是第一项 + OprCacheKey key; /* OID of the resolved operator */ - Oid opr_oid; // 解决后的OID + Oid opr_oid; } OprCacheEntry; static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion); @@ -1044,4 +1043,4 @@ void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) if (hash_search(u_sess->parser_cxt.opr_cache_hash, (void*)&hentry->key, HASH_REMOVE, NULL) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("hash table corrupted"))); } -} \ No newline at end of file +} -- 2.34.1 From 44dcab4ab7fa19516fde5e81962169518669cd39 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Wed, 30 Aug 2023 23:51:33 +0800 Subject: [PATCH 12/26] Update parse_oper.cpp --- src/common/backend/parser/parse_oper.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index 972651308..3eb0d40bd 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -2,7 +2,6 @@ * * parse_oper.cpp * handle operator things for parser - * 处理表达式中的操作符 * * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -233,6 +232,12 @@ Oid oprfuncid(Operator op) return pgopform->oprcode; } +/* + * Given operator tuple, check if the operator is a shell. + * + * Returns tup after casting to Form_pg_operator, or raise + * an error if op passed in is only a shell. + */ static Form_pg_operator check_operator_is_shell(List* opname, ParseState* pstate, int location, Operator tup) { Form_pg_operator opform = (Form_pg_operator)GETSTRUCT(tup); @@ -246,6 +251,13 @@ static Form_pg_operator check_operator_is_shell(List* opname, ParseState* pstate return opform; } +/* + * Find mapping tuple in SysCache by given cache key. + * + * Look for a cache entry matching the given key and get the + * contained operator OID. If found, call SearchSysCache1() + * to lookup mapping HeapTuple in cache by operator OID. + */ static HeapTuple find_mapping_in_cache(OprCacheKey key, bool key_ok) { HeapTuple tup = NULL; -- 2.34.1 From 519962b095756f8341f399dd9f2c8720a84c7237 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Thu, 31 Aug 2023 00:13:17 +0800 Subject: [PATCH 13/26] Update parse_oper.cpp --- src/common/backend/parser/parse_oper.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index 3eb0d40bd..1994f0cdc 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -135,6 +135,7 @@ Oid LookupOperNameTypeNames( { Oid leftoid, rightoid; + /* fetch type's OID by given TypeNames */ if (oprleft == NULL) leftoid = InvalidOid; else @@ -174,9 +175,9 @@ void get_sort_group_operators( { TypeCacheEntry* typentry = NULL; int cache_flags; - Oid lt_opr; // Oid of '<' - Oid eq_opr; // Oid of '=' - Oid gt_opr; // Oid of '>' + Oid lt_opr; // '<' + Oid eq_opr; // '=' + Oid gt_opr; // '>' bool hashable = false; /* @@ -218,7 +219,11 @@ void get_sort_group_operators( *isHashable = hashable; } -/* given operator tuple, return the operator OID */ +/* + * given operator tuple, return the operator OID. + * + * This is a wrapper of HeapTupleGetOid(). + */ Oid oprid(Operator op) { return HeapTupleGetOid(op); @@ -508,9 +513,13 @@ Oid compatible_oper_opid(List* op, Oid arg1, Oid arg2, bool noError) Operator optup; Oid result; + /* given binary opname and OIDs of its args, find the operator tuple. */ optup = compatible_oper(NULL, op, arg1, arg2, noError, -1); if (optup != NULL) { + /* given operator tuple, return the operator OID. */ result = oprid(optup); + + /* release the unit of optup in SysCache. */ ReleaseSysCache(optup); return result; } -- 2.34.1 From afe278d1629e226b8b16decc3a64cc98af4a3c2d Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Thu, 31 Aug 2023 00:17:19 +0800 Subject: [PATCH 14/26] Update parse_param.cpp --- src/common/backend/parser/parse_param.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/backend/parser/parse_param.cpp b/src/common/backend/parser/parse_param.cpp index c9de7007e..e365dd051 100644 --- a/src/common/backend/parser/parse_param.cpp +++ b/src/common/backend/parser/parse_param.cpp @@ -109,7 +109,9 @@ static Node* fixed_paramref_hook(ParseState* pstate, ParamRef* pref) errmsg("there is no parameter $%d", paramno), parser_errposition(pstate, pref->location))); } + param = makeNode(Param); + /* construct the Param structure for pref. */ param->paramkind = PARAM_EXTERN; param->paramid = paramno; param->paramtype = parstate->paramTypes[paramno - 1]; @@ -299,4 +301,4 @@ static bool check_parameter_resolution_walker(Node* node, ParseState* pstate) return query_tree_walker((Query*)node, (bool (*)())check_parameter_resolution_walker, (void*)pstate, 0); } return expression_tree_walker(node, (bool (*)())check_parameter_resolution_walker, (void*)pstate); -} \ No newline at end of file +} -- 2.34.1 From 5d259833d0186b2aa6fec915cd5caef92be37393 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Thu, 31 Aug 2023 11:04:44 +0800 Subject: [PATCH 15/26] Update analyze.cpp --- src/common/backend/parser/analyze.cpp | 61 +++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 3f64e8084..2a41848e2 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -1985,6 +1985,9 @@ List* BuildExcludedTargetlist(Relation targetrel, Index exclRelIndex) return result; } +/* + * check Rls policy for DUPLICATE KEY UPDATE clause. + */ static bool CheckRlsPolicyForUpsert(Relation targetrel) { ListCell *item = NULL; @@ -2023,6 +2026,11 @@ static bool ContainSubLink(Node* clause) } #endif /* ENABLE_MULTIPLE_NODES */ +/* + * process the DUPLICATE KEY UPDATE clause. + * + * DUPLICATE KEY UPDATE clause: perform update command if the insert already exists. + */ static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, RangeVar* relation) { UpsertExpr* result = NULL; @@ -3778,6 +3786,9 @@ static bool checkExecDirectVariableSetStmt(const Node* node) } #endif +/* + * Given a nodename, fetch its index and nodetype. + */ static void get_index_and_type_from_nodename(const char* node_name, int* node_index, char* node_type) { Oid node_oid; @@ -3797,6 +3808,23 @@ static void get_index_and_type_from_nodename(const char* node_name, int* node_in return; } +/* + * Determines the execution type of a remote query node by given nodetype and + * execution direct option ExecDirectOption. + * + * exec_type could only take the following four values: + * ---- EXEC_ON_COORDS: Indicates that the query will be executed on the coordinator. + * Under this execution type, the query is sent to the coordinator + * node for execution and the results are returned from the + * coordinator node. + * ---- EXEC_ON_DATANODES: Indicates that the query will execute on the data node. + * Under this execution type, the query is executed on a remote + * data node and returns results from that node. + * ---- EXEC_ON_NONE: Indicates that the query does not need to be executed remotely. + * Under this execution type, the query is executed on the current + * node and does not need to be sent to the remote node. + * ---- EXEC_ON_ALL_NODES: Indicates that the query may execute on any type of node. + */ RemoteQueryExecType fill_exec_type(char node_type, ExecDirectOption option) { RemoteQueryExecType exec_type; @@ -3826,6 +3854,9 @@ RemoteQueryExecType fill_exec_type(char node_type, ExecDirectOption option) return exec_type; } +/* + * raise an error if the node's index is illegal. + */ void check_node_index(int node_index, char* node_flag, int length_node_flag) { if ((node_flag != NULL) && @@ -3901,6 +3932,9 @@ static void fill_node_list_and_exec_type(const List* nodenames_list, ExecDirectO return; } +/* + * Set ExecDirectType by given command type. + */ ExecDirectType set_exec_direct_type(bool is_local, CmdType command_type) { ExecDirectType type = EXEC_DIRECT_NONE; @@ -3941,6 +3975,9 @@ ExecDirectType set_exec_direct_type(bool is_local, CmdType command_type) return type; } +/* + * raise an error if command type is CMD_MERGE or CMD_NOTHING. + */ void check_command_type(CmdType command_type) { if (command_type == CMD_MERGE) { @@ -4033,6 +4070,9 @@ static Query* generate_query_execdirect_statement(const ExecDirectStmt* stmt) return result; } +/* + * Needed initialization work by planner. + */ static void init_execdirect_utility_stmt(RemoteQuery* step, const char* statement) { step->cursor = NULL; @@ -4732,8 +4772,14 @@ static bool checkAllowedTableCombination(ParseState* pstate) bool has_ustore = false; bool has_else = false; - // Check range table list for the presence of - // relations with different storages + /* + * Check range table list for the presence of relations with different storages. + * + * p_rtable is an array of pointers to a RangeTblEntry that stores information about + * all the tables that appear in the query during the parsing phase. The RangeTblEntry + * structure stores table aliases, table names, column information, and so on. With + * p_rtable, we can get the details of all the tables involved in the query. + */ foreach(lc, pstate->p_rtable) { rte = (RangeTblEntry*)lfirst(lc); if (rte && rte->rtekind == RTE_RELATION) { @@ -4745,7 +4791,14 @@ static bool checkAllowedTableCombination(ParseState* pstate) } } - // Check target table for the type of storage + /* + * Check target table for the type of storage + * + * p_target_rangetblentry points to the RangeTblEntry structure of the target + * table being parsed in p_rtable. During parsing, p_target_rangetblentry is + * used to track the target table that is currently being parsed so that subsequent + * processing can be properly applied to that table. + */ rte = pstate->p_target_rangetblentry; if (rte && rte->rtekind == RTE_RELATION) { if (rte->is_ustore) { @@ -4758,4 +4811,4 @@ static bool checkAllowedTableCombination(ParseState* pstate) Assert(has_ustore || has_else); return !(has_ustore && has_else); -} \ No newline at end of file +} -- 2.34.1 From 550305c80a110914a653c6e6a7b56934c44ac6c3 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Fri, 15 Sep 2023 13:39:43 +0800 Subject: [PATCH 16/26] Update scansup.cpp --- src/common/backend/parser/scansup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/backend/parser/scansup.cpp b/src/common/backend/parser/scansup.cpp index 3ee8ba1c1..36b98cac8 100644 --- a/src/common/backend/parser/scansup.cpp +++ b/src/common/backend/parser/scansup.cpp @@ -185,6 +185,7 @@ void truncate_identifier(char* ident, int len, bool warn) } } + /* * scanner_isspace() --- return TRUE if flex scanner considers char whitespace * -- 2.34.1 From 2951191031c4d1d132f1448797d30ac37166b832 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Tue, 19 Sep 2023 18:25:15 +0800 Subject: [PATCH 17/26] Update prepnonjointree.cpp --- .../optimizer/prep/prepnonjointree.cpp | 110 +++++++++++++----- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/src/gausskernel/optimizer/prep/prepnonjointree.cpp b/src/gausskernel/optimizer/prep/prepnonjointree.cpp index 505a4e7ba..2bcf88eac 100755 --- a/src/gausskernel/optimizer/prep/prepnonjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepnonjointree.cpp @@ -2113,36 +2113,50 @@ Query* lazyagg_main(Query* parse) /* ------------------------------------------------------------ */ /* Lazy Agg : end */ /* ------------------------------------------------------------ */ + + /* ------------------------------------------------------------ */ -/* Reduce orderby : begin */ +/* Reduce orderby : begin */ /* ------------------------------------------------------------ */ + static void reduce_orderby_final(RangeTblEntry* rte, bool reduce); static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce); -/* Reduce orderby clause in subquery for join and setop */ /* - * reduce_orderby_recurse - * recurse check join node and reduce order by final for RangeTblRef. + * reduce_orderby_recurse() --- + * Recurse in a jointree or a setop tree to reduce orderby clause in it. * - * @param (in) query: the query tree for reduce orderby - * @param (in) jtnode: join node for RangeTblRef/FromExpr/JoinExpr/SetOperationStmt + * @ Caller: reduce_orderby() * - * @return: void + * @ Param [IN] query: current node of query tree. + * @ Param [IN] jtnode: jointree node for RangeTblRef/FromExpr/JoinExpr or setop tree + * node for SetOperationStmt. + * @ Param [IN] reduce: the flag identify that runs through the recursing + * process that decide whether to reduce the orderby clause for subquery. + * @ Returns: void + * + * Note: this routine is the main recursive procedure through the orderby reducing + * process. It will recursively traverses a jointree or a setop tree in an attempt + * to reduce sortClauses for it. */ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) { + /* query isn't in a subquery, JOIN-structure or an aggregation. */ if (jtnode == NULL) return; + /* jtnode point to an entity relation table that is referred by subquery. */ if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef*)jtnode)->rtindex; RangeTblEntry* rte = rt_fetch(varno, query->rtable); - /* Reduce orderby clause in subquery for join or from clause of more than one rte */ + /* Reduce sort procedure for this RTE. */ reduce_orderby_final(rte, reduce); - } else if (IsA(jtnode, FromExpr)) { + }else if (IsA(jtnode, FromExpr)) { + #ifndef ENABLE_MULTIPLE_NODES - /* If there is ROWNUM, can not reduce orderby clause in subquery from fromlist. + /* + * If there is ROWNUM, can not reduce orderby clause in subquery from fromlist. * For example, If there is a SQL {select * from table_name where rownum < n union * select * from (select * from table_name order by column_name desc) where rownum < n;}, * can not reduce orderby clause here. @@ -2151,20 +2165,26 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) return; } #endif + FromExpr* f = (FromExpr*)jtnode; ListCell* l = NULL; bool flag = false; + + /* If length of fromlist > 1, then certainly we can reduce the sort times. */ if (1 == list_length(f->fromlist)) flag = reduce; else flag = true; + /* Recurse into each reference of query to reduce its sortClauses. */ foreach (l, f->fromlist) reduce_orderby_recurse(query, (Node*)lfirst(l), flag); } else if (IsA(jtnode, JoinExpr)) { + /* top level of a join-clause. */ JoinExpr* j = (JoinExpr*)jtnode; + /* Recurse into each argument to reduce its orderby. */ if ((JOIN_INNER == j->jointype) || (JOIN_LEFT == j->jointype) || (JOIN_SEMI == j->jointype) || (JOIN_ANTI == j->jointype) || (JOIN_FULL == j->jointype) || (JOIN_RIGHT == j->jointype) || (JOIN_LEFT_ANTI_FULL == j->jointype) || (JOIN_RIGHT_ANTI_FULL == j->jointype)) { @@ -2177,8 +2197,10 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) errmsg("unrecognized join type: %d", (int)j->jointype))); } } else if (IsA(jtnode, SetOperationStmt)) { + /* top level of a aggregate clause. */ SetOperationStmt* op = (SetOperationStmt*)jtnode; + /* Recurse into each argument to reduce its orderby. */ reduce_orderby_recurse(query, op->larg, true); reduce_orderby_recurse(query, op->rarg, true); } @@ -2186,38 +2208,57 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) return; } + /* - * reduce_orderby_final - * Reduce order by clause for subquery for join and setop if the subquery have sortClause. + * reduce_orderby_final() --- + * Reduce orderby clause for subquery if the subquery has a sortClause. + * + * @ Caller: reduce_orderby_recurse() + * + * @ Param [IN] rte: the RTE of subquery whose orderby clause needs to be reduced. + * @ Param [IN] reduce: the flag identify that runs through the recursing + * process that decide whether to reduce the orderby clause for subquery. + * @ Returns: void * - * @param (in) rte: the RTE of subquery for reduce orderby. - * @param (in) reduce: the flag identify if reduce the orderby clause or not. - * - * @return: void + * Note: if the subquery has a sortClause and the passed-in parameter reduce + * is true, then drop this orderby clause for the subquery. */ static void reduce_orderby_final(RangeTblEntry* rte, bool reduce) { - /* Reduce orderby clause in subquery for join or from clause of more than one rte */ + /* Drop the orderby clause for subquery if reduce is true. */ if (rte->rtekind == RTE_SUBQUERY) { if (reduce && rte->subquery->sortClause && !rte->subquery->limitOffset && !rte->subquery->limitCount) { pfree_ext(rte->subquery->sortClause); rte->subquery->sortClause = NULL; } + /* Recurse into subquery of current subquery to reduce its orderby. */ reduce_orderby(rte->subquery, reduce); } return; } + /* - * reduce_orderby - * The entry of reduce orderby which called by subquery_planner. + * reduce_orderby() --- + * The entry point of orderby reducing process. + * + * @ Caller: subquery_planner() + * + * @ Param [IN] query: current node of query tree. + * @ Param [IN] reduce: the flag identify that runs through the recursing + * process that decide whether to reduce the orderby clause for subquery. + * @ Returns: void * - * @param (in) query: the query tree for reduce orderby. - * @param (in) reduce: the flag identify if reduce the orderby clause or not. - * - * @return: void + * Note: when this routine called by subquery_planner(), the passed-in + * parameter reduce would always be FALSE. And this argument would be used + * to decide whether to reduce a orderby clause for subquery in function + * reduce_orderby_final(). + * + * Note: through the recursing process, function reduce_orderby_final() + * would also call this routine. When it's called by reduce_orderby_final(), + * the value of reduce could be TRUE or FALSE. */ void reduce_orderby(Query* query, bool reduce) { @@ -2227,21 +2268,33 @@ void reduce_orderby(Query* query, bool reduce) if (query == NULL) return; - /* If subquery in insert or update, it should find select query and deside whether reduce order by in subquery or - * not. */ + /* + * If subquery is not a SELECT, we should find SELECT query and deside + * whether to reduce order by in subquery or not. + */ if (query->commandType != CMD_SELECT) { foreach (l, query->rtable) { rte = (RangeTblEntry*)lfirst(l); + /* + * Recurse into subqueries to locate orderby and decide + * whether to reduce it or not. + */ if (rte->rtekind == RTE_SUBQUERY) reduce_orderby(rte->subquery, reduce); } return; } - /* Recurse find subquery in form,join or subquery, and deside whether reduce order by in subquery or not. */ + /* + * Recurse to find subquerys in FROM, JOIN or subqueries, and to decide + * whether to reduce orderby or not. + */ reduce_orderby_recurse(query, (Node*)query->jointree, reduce); - /* If there has setop, it should optimize orderby clause. */ + /* + * If this is the top-level node of a setop tree, we shall recurse into + * its arguments and try to reduce orderby for each argument. + */ if (query->setOperations) { reduce_orderby_recurse(query, ((SetOperationStmt*)query->setOperations)->larg, true); reduce_orderby_recurse(query, ((SetOperationStmt*)query->setOperations)->rarg, true); @@ -2249,4 +2302,5 @@ void reduce_orderby(Query* query, bool reduce) } /* ------------------------------------------------------------ */ -/* Reduce orderby : end */ +/* Reduce orderby : end */ +/* ------------------------------------------------------------ */ -- 2.34.1 From 304749b6ef40781513e0d5841e657f84f04d10f6 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Tue, 19 Sep 2023 19:25:05 +0800 Subject: [PATCH 18/26] Update prepnonjointree.cpp --- .../optimizer/prep/prepnonjointree.cpp | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/gausskernel/optimizer/prep/prepnonjointree.cpp b/src/gausskernel/optimizer/prep/prepnonjointree.cpp index 2bcf88eac..8eb780f2e 100755 --- a/src/gausskernel/optimizer/prep/prepnonjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepnonjointree.cpp @@ -2124,7 +2124,7 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce); /* * reduce_orderby_recurse() --- - * Recurse in a jointree or a setop tree to reduce orderby clause in it. + * Recurse in a jointree or a setop tree and try to reduce NULL orderby-clause. * * @ Caller: reduce_orderby() * @@ -2137,7 +2137,7 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce); * * Note: this routine is the main recursive procedure through the orderby reducing * process. It will recursively traverses a jointree or a setop tree in an attempt - * to reduce sortClauses for it. + * to drop NULL-returns orderby for it. */ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) { @@ -2170,14 +2170,19 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) ListCell* l = NULL; bool flag = false; - - /* If length of fromlist > 1, then certainly we can reduce the sort times. */ + /* + * If the number of tables referenced by the from statement is 1, then there are two + * situations: If the from-clause is at the top of the query, we do not need to drop + * its orderby, even if the orderby-clause returns NULL; Otherwise, it indicates that + * the query refers to more than one table, at which point we can try to drop the + * orderby-clause of the only table referenced by the current from-clause. + */ if (1 == list_length(f->fromlist)) flag = reduce; else flag = true; - /* Recurse into each reference of query to reduce its sortClauses. */ + /* Recurse into each reference of query to reduce its NULL orderby. */ foreach (l, f->fromlist) reduce_orderby_recurse(query, (Node*)lfirst(l), flag); } else if (IsA(jtnode, JoinExpr)) { @@ -2211,7 +2216,7 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) /* * reduce_orderby_final() --- - * Reduce orderby clause for subquery if the subquery has a sortClause. + * Reduce orderby clause for subquery if it has a NULL-returns orderby. * * @ Caller: reduce_orderby_recurse() * @@ -2220,12 +2225,17 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) * process that decide whether to reduce the orderby clause for subquery. * @ Returns: void * - * Note: if the subquery has a sortClause and the passed-in parameter reduce - * is true, then drop this orderby clause for the subquery. + * Note: if the subquery has NULL-returns orderby and the passed-in parameter + * reduce is true, drop this orderby clause for the subquery. */ static void reduce_orderby_final(RangeTblEntry* rte, bool reduce) { - /* Drop the orderby clause for subquery if reduce is true. */ + + /* + * Both subquery's limitOffset and limitCount are zero, which shows + * that this orderby actually doesn't return any item as the result + * of a query or used in join operation. + */ if (rte->rtekind == RTE_SUBQUERY) { if (reduce && rte->subquery->sortClause && !rte->subquery->limitOffset && !rte->subquery->limitCount) { pfree_ext(rte->subquery->sortClause); @@ -2287,7 +2297,7 @@ void reduce_orderby(Query* query, bool reduce) /* * Recurse to find subquerys in FROM, JOIN or subqueries, and to decide - * whether to reduce orderby or not. + * whether to drop its orderby or not. */ reduce_orderby_recurse(query, (Node*)query->jointree, reduce); -- 2.34.1 From 363bbba83a99d6980d3016872b0349c4d0d0ec5f Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Tue, 19 Sep 2023 21:55:00 +0800 Subject: [PATCH 19/26] Update prepnonjointree.cpp --- .../optimizer/prep/prepnonjointree.cpp | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/gausskernel/optimizer/prep/prepnonjointree.cpp b/src/gausskernel/optimizer/prep/prepnonjointree.cpp index 8eb780f2e..835d9dd51 100755 --- a/src/gausskernel/optimizer/prep/prepnonjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepnonjointree.cpp @@ -2124,24 +2124,24 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce); /* * reduce_orderby_recurse() --- - * Recurse in a jointree or a setop tree and try to reduce NULL orderby-clause. + * Recurse in a jointree or a setop tree and try to reduce redundant orderby clauses. * * @ Caller: reduce_orderby() * * @ Param [IN] query: current node of query tree. * @ Param [IN] jtnode: jointree node for RangeTblRef/FromExpr/JoinExpr or setop tree * node for SetOperationStmt. - * @ Param [IN] reduce: the flag identify that runs through the recursing + * @ Param [IN] reduce: the flag identifier that runs through the recursing * process that decide whether to reduce the orderby clause for subquery. * @ Returns: void * * Note: this routine is the main recursive procedure through the orderby reducing * process. It will recursively traverses a jointree or a setop tree in an attempt - * to drop NULL-returns orderby for it. + * to drop redundant orderby clauses for it. */ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) { - /* query isn't in a subquery, JOIN-structure or an aggregation. */ + /* query isn't in a from-where clause, join-structure or an aggregation. */ if (jtnode == NULL) return; @@ -2150,7 +2150,7 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) int varno = ((RangeTblRef*)jtnode)->rtindex; RangeTblEntry* rte = rt_fetch(varno, query->rtable); - /* Reduce sort procedure for this RTE. */ + /* Try to drop orderby-clause on this RTE. */ reduce_orderby_final(rte, reduce); }else if (IsA(jtnode, FromExpr)) { @@ -2171,18 +2171,17 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) bool flag = false; /* - * If the number of tables referenced by the from statement is 1, then there are two - * situations: If the from-clause is at the top of the query, we do not need to drop - * its orderby, even if the orderby-clause returns NULL; Otherwise, it indicates that - * the query refers to more than one table, at which point we can try to drop the - * orderby-clause of the only table referenced by the current from-clause. + * If the number of tables referenced by the from-where clause is 1, then there are two + * situations: If the from-clause is at the top of the query, then we don't need to drop + * this orderby; Otherwise the query refers to more than one table, at which point we + * can try to drop the orderby-clause of the tables referenced by current from-clause. */ if (1 == list_length(f->fromlist)) flag = reduce; else flag = true; - /* Recurse into each reference of query to reduce its NULL orderby. */ + /* Recurse into each reference of from-clause to reduce its orderby. */ foreach (l, f->fromlist) reduce_orderby_recurse(query, (Node*)lfirst(l), flag); } else if (IsA(jtnode, JoinExpr)) { @@ -2216,7 +2215,7 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) /* * reduce_orderby_final() --- - * Reduce orderby clause for subquery if it has a NULL-returns orderby. + * Reduce orderby clause for subquery if it has a removable sortClause. * * @ Caller: reduce_orderby_recurse() * @@ -2224,17 +2223,13 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce) * @ Param [IN] reduce: the flag identify that runs through the recursing * process that decide whether to reduce the orderby clause for subquery. * @ Returns: void - * - * Note: if the subquery has NULL-returns orderby and the passed-in parameter - * reduce is true, drop this orderby clause for the subquery. */ static void reduce_orderby_final(RangeTblEntry* rte, bool reduce) { - /* - * Both subquery's limitOffset and limitCount are zero, which shows - * that this orderby actually doesn't return any item as the result - * of a query or used in join operation. + * Both subquery's limitOffset and limitCount are NULL, which shows that + * the orderby-clause on this RTE does not use LIMIT-clause to specify + * a subset of the query results. */ if (rte->rtekind == RTE_SUBQUERY) { if (reduce && rte->subquery->sortClause && !rte->subquery->limitOffset && !rte->subquery->limitCount) { @@ -2257,7 +2252,7 @@ static void reduce_orderby_final(RangeTblEntry* rte, bool reduce) * @ Caller: subquery_planner() * * @ Param [IN] query: current node of query tree. - * @ Param [IN] reduce: the flag identify that runs through the recursing + * @ Param [IN] reduce: the flag identifier that runs through the recursing * process that decide whether to reduce the orderby clause for subquery. * @ Returns: void * @@ -2280,14 +2275,14 @@ void reduce_orderby(Query* query, bool reduce) /* * If subquery is not a SELECT, we should find SELECT query and deside - * whether to reduce order by in subquery or not. + * whether to reduce orderby in subquery or not. */ if (query->commandType != CMD_SELECT) { foreach (l, query->rtable) { rte = (RangeTblEntry*)lfirst(l); /* * Recurse into subqueries to locate orderby and decide - * whether to reduce it or not. + * whether to reduce it. */ if (rte->rtekind == RTE_SUBQUERY) reduce_orderby(rte->subquery, reduce); @@ -2296,8 +2291,8 @@ void reduce_orderby(Query* query, bool reduce) } /* - * Recurse to find subquerys in FROM, JOIN or subqueries, and to decide - * whether to drop its orderby or not. + * Recurse to find subquerys in from-where clause, join clause or aggregation + * structure, and to decide whether to drop its orderby. */ reduce_orderby_recurse(query, (Node*)query->jointree, reduce); -- 2.34.1 From c95b0248840b38d5619f6e5f148eb704d6ff490c Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Tue, 26 Sep 2023 21:00:48 +0800 Subject: [PATCH 20/26] Update prepjointree.cpp --- src/gausskernel/optimizer/prep/prepjointree.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gausskernel/optimizer/prep/prepjointree.cpp b/src/gausskernel/optimizer/prep/prepjointree.cpp index 2187138e8..93442882c 100755 --- a/src/gausskernel/optimizer/prep/prepjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepjointree.cpp @@ -93,7 +93,7 @@ static bool jointree_contains_lateral_outer_refs(Node *jtnode, bool restricted, Relids safe_upper_varnos); static void replace_vars_in_jointree(Node* jtnode, pullup_replace_vars_context* context, JoinExpr* lowest_nulling_outer_join); static Node* pullup_replace_vars(Node* expr, pullup_replace_vars_context* context); -static Node* pullup_replace_vars_callback(Var* var, replace_rte_variables_context* context); +static Node* pullup_replace_vars_callback(Var* var, replace_rte_variables_context* contextreduce_outer_joins; static Query *pullup_replace_vars_subquery(Query *query, pullup_replace_vars_context *context); static reduce_outer_joins_state* reduce_outer_joins_pass1(Node* jtnode); static void reduce_outer_joins_pass2(Node* jtnode, reduce_outer_joins_state* state, PlannerInfo* root, @@ -2282,6 +2282,11 @@ void flatten_simple_union_all(PlannerInfo* root) while (leftmostjtnode && IsA(leftmostjtnode, SetOperationStmt)) leftmostjtnode = ((SetOperationStmt*)leftmostjtnode)->larg; + /* + * The leaf nodes in setop tree must be a RangeTblRef type and the members + * of UNION must be subqueries specified in SELECT statement. Else, raise + * an err. + */ if (leftmostjtnode && IsA(leftmostjtnode, RangeTblRef)) { leftmostRTI = ((RangeTblRef*)leftmostjtnode)->rtindex; leftmostRTE = rt_fetch(leftmostRTI, parse->rtable); @@ -3580,6 +3585,9 @@ static Node* reduce_inequality_fulljoins_jointree_recurse(PlannerInfo* root, Nod return jtnode; } +/* + * Look into the plan tree and see if any qual involves with rownum. + */ extern bool find_rownum_in_quals(PlannerInfo *root) { if (root->parse == NULL) { @@ -3608,7 +3616,9 @@ extern bool find_rownum_in_quals(PlannerInfo *root) return hasRownum; } - +/* + * Check if current node of Query tree has a rownum qual. + */ bool ContainRownumQual(const Query *parse) { if (!IsA(parse->jointree, FromExpr)) { -- 2.34.1 From 5de15c1e086645e5ddee4c66b887c15a45ac0554 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Thu, 28 Sep 2023 17:26:51 +0800 Subject: [PATCH 21/26] Update prepjointree.cpp --- .../optimizer/prep/prepjointree.cpp | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/src/gausskernel/optimizer/prep/prepjointree.cpp b/src/gausskernel/optimizer/prep/prepjointree.cpp index 93442882c..9bb964cc3 100755 --- a/src/gausskernel/optimizer/prep/prepjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepjointree.cpp @@ -157,6 +157,7 @@ void replace_empty_jointree(Query *parse) } #ifndef ENABLE_MULTIPLE_NODES + /* * helper function to check if SWCB ctes contaisn in current SubQuery, normally help us to * idenfity if it is OK to appy SWCB related optimization steps @@ -182,6 +183,7 @@ static bool contains_swctes(const PlannerInfo *root) return found; } + #endif /* @@ -280,10 +282,14 @@ Node* assign_qual_clause(Node* new_node, Node* old_node, Node* qual, Relids old_ if (qual == NULL) { return new_node; } + /* + * Extracts the varnos (the relationship to which the variables belong) of all the vars in the expression. + * The collection containing these varnos is preserved in qual_varnos. + */ Relids qual_varnos = pull_varnos(qual); /* - * We need add this quals to new_node if old_node_relids can not include qual_varnos. - * than can happend when or_clause pull up. + * We need add this quals to new_node if old_node_relids can not include qual_varnos, + * which can happen when or_clause pull up. */ if (!bms_is_subset(qual_varnos, old_node_relids)) { if (IsA(new_node, FromExpr)) { @@ -858,10 +864,11 @@ void inline_set_returning_functions(PlannerInfo* root) } /* - * This recursively processes the jointree and returns a modified jointree. + * pull_up_subqueries() --- + * Wrapper function of pull_up_subqueries_recurse(). This function + * recursively processes the jointree and returns a modified jointree. */ -Node * -pull_up_subqueries(PlannerInfo *root, Node *jtnode) +Node * pull_up_subqueries(PlannerInfo *root, Node *jtnode) { /* Start off with no containing join nor appendrel */ return pull_up_subqueries_recurse(root, jtnode, NULL, NULL, NULL); @@ -1039,6 +1046,16 @@ void pull_up_subquery_hint(PlannerInfo* root, Query* parse, HintState* hint_stat } } +/* + * is_subquery_partial_push() + * + * Determines whether a subquery can be partially pushed optimized, which refers + * to pushing part of the computation operation of the subquery to an upper query + * to reduce the computation of the subquery and improve the query performance. + * + * This routine checks the conditions and expressions in the subquery to see if + * they can be pushed to an external query for processing. + */ static bool is_subquery_partial_push(PlannerInfo* root, RangeTblEntry* rte) { @@ -1783,6 +1800,10 @@ static bool is_simple_subquery(Query* subquery, RangeTblEntry *rte, JoinExpr *lo * We require all the setops to be UNION ALL (no mixing) and there can't be * any datatype coercions involved, ie, all the leaf queries must emit the * same datatypes. + * + * This routine does some simple pre-judging processes and error checking, + * and calls is_simple_union_all_recurse() to recursively check through the + * setop tree to see if it's a simple UNION ALL setop structure. */ static bool is_simple_union_all(Query* subquery) { @@ -1815,13 +1836,17 @@ static bool is_simple_union_all(Query* subquery) return is_simple_union_all_recurse((Node*)topop, subquery, topop->colTypes); } +/* + * is_simple_union_all_recurse + * Recursively check through the setop tree to see if it's a simple UNION ALL + */ static bool is_simple_union_all_recurse(Node* setOp, Query* setOpQuery, List* colTypes) { if (IsA(setOp, RangeTblRef)) { RangeTblRef* rtr = (RangeTblRef*)setOp; RangeTblEntry* rte = rt_fetch(rtr->rtindex, setOpQuery->rtable); Query* subquery = rte->subquery; - + /* Leaf nodes in setop tree must be subqueries. */ AssertEreport(subquery != NULL, MOD_OPT_REWRITE, "subquery should not be NULL in is_simple_union_all_recurse"); /* Leaf nodes are OK if they match the toplevel column types */ @@ -1834,7 +1859,7 @@ static bool is_simple_union_all_recurse(Node* setOp, Query* setOpQuery, List* co if (op->op != SETOP_UNION || !op->all) return false; - /* Recurse to check inputs */ + /* Recurse into each argument to check. */ return is_simple_union_all_recurse(op->larg, setOpQuery, colTypes) && is_simple_union_all_recurse(op->rarg, setOpQuery, colTypes); } else { @@ -2506,12 +2531,15 @@ static reduce_outer_joins_state* reduce_outer_joins_pass1(Node* jtnode) return result; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef*)jtnode)->rtindex; - + /* only include rtindex of current rtr node. */ result->relids = bms_make_singleton(varno); } else if (IsA(jtnode, FromExpr)) { FromExpr* f = (FromExpr*)jtnode; ListCell* l = NULL; - + /* + * Recursively process each member in jointree and merge + * the information gathered. + */ foreach (l, f->fromlist) { reduce_outer_joins_state* sub_state = NULL; @@ -2527,7 +2555,10 @@ static reduce_outer_joins_state* reduce_outer_joins_pass1(Node* jtnode) /* join's own RT index is not wanted in result->relids */ if (IS_OUTER_JOIN(j->jointype)) result->contains_outer = true; - + /* + * Process each argument of JOIN expression and merge + * the information gathered. + */ sub_state = reduce_outer_joins_pass1(j->larg); result->relids = bms_add_members(result->relids, sub_state->relids); result->contains_outer |= sub_state->contains_outer; @@ -2931,6 +2962,10 @@ void remove_result_refs(PlannerInfo *root, int varno, Node *newjtloc) */ } +/* + * Traverses the query tree to find the PlaceholderVar node associated + * with the specified external parameter. + */ bool find_dependent_phvs_walker(Node *node, find_dependent_phvs_context *context) { @@ -2964,6 +2999,10 @@ bool find_dependent_phvs_walker(Node *node, (void *) context); } +/* + * Wrapper function of query_or_expression_tree_walker. Find the + * PlaceholderVar node associated with the specified external parameter. + */ bool find_dependent_phvs(Node *node, int varno) { find_dependent_phvs_context context; @@ -2997,6 +3036,9 @@ typedef struct { Relids subrelids; } substitute_multiple_relids_context; +/* + * Recursively replace multiple relids in the query plan with new relids. + */ static bool substitute_multiple_relids_walker(Node* node, substitute_multiple_relids_context* context) { if (node == NULL) @@ -3037,6 +3079,11 @@ static bool substitute_multiple_relids_walker(Node* node, substitute_multiple_re return expression_tree_walker(node, (bool (*)())substitute_multiple_relids_walker, (void*)context); } +/* + * Replace multiple relids in the query plan with new relids. This function is typically + * used to process query statements that contain multiple relationships. It ensures that + * the query plan references the relevant relationships correctly. + */ static void substitute_multiple_relids(Node* node, int varno, Relids subrelids) { substitute_multiple_relids_context context; -- 2.34.1 From e649815e3a81aff685cc287488bd79cd057a58ae Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Sun, 1 Oct 2023 15:45:37 +0800 Subject: [PATCH 22/26] Update parse_oper.cpp --- src/common/backend/parser/parse_oper.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index 1994f0cdc..f2cdc80f5 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -48,12 +48,10 @@ #define MAX_CACHED_PATH_LEN 16 typedef struct OprCacheKey { - char oprname[NAMEDATALEN]; // operator名称 + char oprname[NAMEDATALEN]; Oid left_arg; /* Left input OID, or 0 if prefix op */ - // 左侧操作符的OID,如果这是一个前缀操作符则此项为0. Oid right_arg; /* Right input OID, or 0 if postfix op */ - // 右侧操作符的OID,如果这是一个后缀操作符则此项为0. - Oid search_path[MAX_CACHED_PATH_LEN]; // 搜索路径 + Oid search_path[MAX_CACHED_PATH_LEN]; bool use_a_style_coercion; } OprCacheKey; @@ -523,6 +521,7 @@ Oid compatible_oper_opid(List* op, Oid arg1, Oid arg2, bool noError) ReleaseSysCache(optup); return result; } + return InvalidOid; } @@ -559,6 +558,7 @@ Operator right_oper(ParseState* pstate, List* op, Oid arg, bool noError, int loc * First try for an "exact" match. */ operOid = OpernameGetOprid(op, arg, InvalidOid); + if (!OidIsValid(operOid)) { /* * Otherwise, search for the most suitable candidate. -- 2.34.1 From d064984625a4d0081824ed288de4110c88cc20bc Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Sun, 1 Oct 2023 18:42:32 +0800 Subject: [PATCH 23/26] Update tcap_drop.cpp --- src/gausskernel/storage/tcap/tcap_drop.cpp | 2913 +++++++++++--------- 1 file changed, 1586 insertions(+), 1327 deletions(-) diff --git a/src/gausskernel/storage/tcap/tcap_drop.cpp b/src/gausskernel/storage/tcap/tcap_drop.cpp index a6cb8515c..65318ba31 100644 --- a/src/gausskernel/storage/tcap/tcap_drop.cpp +++ b/src/gausskernel/storage/tcap/tcap_drop.cpp @@ -1,1327 +1,1586 @@ -/* - * 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" + +/* + * TrRenameClass() --- + * Renames the specified category. + * + * This routine can update the category information in the database by + * changing the name of the category. This is useful when you need to + * modify the database schema or change the category name. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Param [IN] newName: name after renamed. + * Returns [OUT] : void. + */ +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); +} + +/* + * TrRenameCommon() --- + * Performs general operations related to renaming objects, typically + * tables or classes. + * + * Param [IN] baseDesc: a pointer to the object descriptor. + * Param [IN] object: a pointer to the address information of the object, + * typically includes information such as an object's schema, name, and so on. + * Param [IN] relid: unique identifier(OID) for a table or class. + * Param [IN] natts: the number of attributes associated with a table or class. + * Param [IN] oidAttrNum: the number of the OID attribute. + * Param [IN] oidIndexId: OID index. + * Param [IN] objTag: label or identity of objects. + * Returns [OUT] : void. + */ +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); +} + +/* + * TrDeleteBaseid() --- + * Deletes a specific base OID in the database. + * + * In a database management system, the base oids are typically associated + * with the underlying storage structure of the database which are used to + * identify and manage different objects (for example, tables, indexes, + * schemas, and so on) in the database. Specifically, this function might be + * used to delete a database object with the specified base OID. + * + * Param [IN] baseid: base OID of the object that needs to be deleted. + * Returns [OUT] : void. + */ +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(); +} + +/* + * TrDeleteId() --- + * Deletes an Object in the database with the specified OID. + * + * Specifically this routine needs to: + * 1. Delete an object with the given OID, such as a table, an index, a view, + * or a function. + * 2. Update system catalog and metadata: After deleting an object, it is necessary + * to update the system catalog and metadata of the database to reflect the + * delete operation of the object and ensure the consistency of the database + * structure and metadata information. + * 3. Cleanup. Deleting an object may require some additional cleanup operations, + * such as releasing related resources, deleting the object's physical storage, + * and so on. + * + * Param [IN] baseid: OID of the object that needs to be deleted. + * Returns [OUT] : void. + */ +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(); +} + +/* + * TrNeedLogicDrop() --- + * Determine whether a logical deletion operation is required. + * + * Note that a logical deletion usually refers to marking database objects as + * “Deleted” rather than physically deleting them. + * + * Param [IN] object: a pointer to the address information of the object, + * typically includes information such as an object's schema, name, and so on. + */ +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; +} + +/* + * TrDoDropIndex() --- + * Drop index of a specified object. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropTable() --- + * Delete a table by given information. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Param [IN] relKind: an identifier of relation kind. + * Returns [OUT] : void. + * + */ +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; +} + +/* + * TrDoDropType() --- + * Drop a custom data type. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropConstraint() --- + * Remove a constraint by given object information. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * This routine is used to delete a constraint object from a database, which + * typically defines rules or integrity conditions for the data in the table, + * such as primary key constraints, foreign key constraints, unique constraints, + * check constraints, and so on. + */ +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; +} + +/* + * TrDoDropTrigger() --- + * Drop a trigger. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropRewrite() --- + * Drop an attribute definition. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * This routine is used to delete a property definition object from a database, + * which are typically used to specify the columns of a table, including property + * information such as column names, data types, constraints, and so on. + */ +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; +} + +/* + * TrDoDropProc() --- + * Deletes a stored procedure object from the database + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropCast() --- + * Drop a type cast. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropCollation() --- + * Drop a collation, which is typically used to define how text data + * is compared and sorted to meet specific collation requirements, + * such as case-sensitive or case-insensitive + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropConversion() --- + * Drop a type-conversion, which is typically used to define how to + * convert values of one data type to another to meet specific data + * processing needs + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropProceduralLanguage() --- + * Drop a procedural language, which is typically used to define and + * execute stored procedures, triggers, functions, and other executable + * blocks in a database. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropLargeObject() --- + * Drop a large object, which is usually referred to as binary data + * (such as images, audio files, documents, etc.) , which are stored + * in binary form in a database. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropOperator() --- + * Drop an operator, which defines the behavior for performing operations + * on different data types in a database. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropOpClass() --- + * Drop an operator class, which is typically used to define the + * behavior of operators on specific data types to support index + * and query optimization. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropOpFamily() --- + * Drop an "Operator family", which is typically used to define + * relationships between operators to support index and query optimization. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + + +/* + * TrDoDropAmOp() --- + * Drop an "Access Method Operator", which is typically associated with + * index operations in a database, used to support different query + * operations such as equal, less than, greater than, and so on of + * indexes. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + + +/* + * TrDoDropAmProc() --- + * Drop an "Access Method Procedure", which is usually associated + * with storage access methods in a database to support operations + * and optimizations for specific access methods, such as b-trees, + * hashes, and so on. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrDoDropCast() --- + * Drop a type cast. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ + + + +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; +} + + +/* + * TrDoDropCast() --- + * Drop a type cast. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ + + + +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; +} -- 2.34.1 From d5d6a7a17e178a4105dbe67f3e4ccb48c49fd54a Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Sun, 1 Oct 2023 23:12:48 +0800 Subject: [PATCH 24/26] Update analyze.cpp --- src/common/backend/parser/analyze.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 2a41848e2..8cf772c0c 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -253,7 +253,6 @@ Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** pa /* * parse_sub_analyze * Entry point for recursively analyzing a sub-statement. - * 递归分析子查询的入口函数 */ Query* parse_sub_analyze(Node* parseTree, ParseState* parentParseState, CommonTableExpr* parentCTE, bool locked_from_parent, bool resolve_unknowns) -- 2.34.1 From ffd5d15617e2f24c43cc4a3a858cca4587b27ad5 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 2 Oct 2023 00:37:49 +0800 Subject: [PATCH 25/26] Update tcap_drop.cpp --- src/gausskernel/storage/tcap/tcap_drop.cpp | 410 ++++++++++++++++++++- 1 file changed, 393 insertions(+), 17 deletions(-) diff --git a/src/gausskernel/storage/tcap/tcap_drop.cpp b/src/gausskernel/storage/tcap/tcap_drop.cpp index 65318ba31..8f215cfd3 100644 --- a/src/gausskernel/storage/tcap/tcap_drop.cpp +++ b/src/gausskernel/storage/tcap/tcap_drop.cpp @@ -778,16 +778,14 @@ static void TrDoDropAmProc(TrObjDesc *baseDesc, ObjectAddress *object) } /* - * TrDoDropCast() --- - * Drop a type cast. + * TrDoDropSchema() --- + * Drop a schema, which is a way to organize and manage database objects, + * such as tables, views, functions, and so on. * * Param [IN] baseDesc:the basic structure of object description information. * Param [IN] object: address information for the object to be renamed. * Returns [OUT] : void. */ - - - static void TrDoDropSchema(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -802,16 +800,16 @@ static void TrDoDropSchema(TrObjDesc *baseDesc, ObjectAddress *object) /* - * TrDoDropCast() --- - * Drop a type cast. + * TrDoDropTSParser() --- + * Drop a text-search-parser, which is used to parse and process + * query statements entered by the user in a text search. It analyzes + * the search terms and keywords entered by the user and translates + * them into a format that the database can understand and process. * * Param [IN] baseDesc:the basic structure of object description information. * Param [IN] object: address information for the object to be renamed. * Returns [OUT] : void. */ - - - static void TrDoDropTSParser(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -824,6 +822,23 @@ static void TrDoDropTSParser(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropTSDictionary() --- + * Drop a text-search-dictionary. + * + * This routine will identify the text-search-dictionary which to be deleted + * according to the passed description information and object address information, + * and perform the corresponding deletion operation. + * + * A text-search-dictionary is used to provide text search capabilities. It + * helps users quickly find and locate records in a database that contains + * specific keywords or phrases. By using a text-search-dictionary, users + * can conduct information retrieval and data analysis more efficiently. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropTSDictionary(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -836,6 +851,22 @@ static void TrDoDropTSDictionary(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropTSTemplate() --- + * Drop a text-search-template. + * + * This routine identifies the template to be deleted based on the passed + * description information and object address information, and performs + * the corresponding deletion operation. + * + * A text-search-template is a tool for finding data records in a database + * that contain specific text or patterns, usually used to retrieve and + * filter data, perform data analysis, and so on. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropTSTemplate(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -848,6 +879,15 @@ static void TrDoDropTSTemplate(TrObjDesc *baseDesc, ObjectAddress *object) return; } + +/* + * TrDoDropTSConfiguration() --- + * Drop a text-search-configuration. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropTSConfiguration(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -860,6 +900,19 @@ static void TrDoDropTSConfiguration(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropForeignDataWrappe() --- + * Drop a Foreign-Data-Wrapper. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * A Foreign-Data-Wrapper is a tool for connecting to and querying external + * data sources and is typically used with Foreign Tables, allows the database + * engine to interact with other data storage systems, such as other databases, + * files, Web services, and so on. + */ static void TrDoDropForeignDataWrapper(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -872,6 +925,23 @@ static void TrDoDropForeignDataWrapper(TrObjDesc *baseDesc, ObjectAddress *objec return; } +/* + * TrDoDropForeignServer() --- + * Drop a Foreign Server. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * Foreign Server is a part of the Foreign-Data-Wrapper, which defines the + * parameters that connect to the external Data source, allows the database + * engine to interact with other data storage systems, such as other databases, + * files, Web services, and so on. + * + * This routine identifies the server to be deleted based on the passed description + * information and object address information, and performs the corresponding + * deletion operation. + */ static void TrDoDropForeignServer(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -884,6 +954,18 @@ static void TrDoDropForeignServer(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropUserMapping() --- + * Drop a User Mapping. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * User Mapping is a mechanism for mapping database users to external data source + * users. It allows database users to use different credentials for authentication + * and access control when interacting with external data sources. + */ static void TrDoDropUserMapping(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -895,7 +977,29 @@ static void TrDoDropUserMapping(TrObjDesc *baseDesc, ObjectAddress *object) return; } - +/* + * TrDoDropDefaultACL() --- + * Drop a default ACL(Access Control List). + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * A default ACL defines default permission rules for newly created database + * objects that determine which users or roles have specific permissions + * on objects. + * + * This routine identifies the default ACL to be deleted based on the passed + * description information and object address information, and performs the + * corresponding deletion operation. It deletes the default access control + * list configuration in the database to allow an administrator or user to + * manage the default permission rules for newly created objects. + * + * Note that this is an internal function of the OpenGauss database and is + * not normally called directly by ordinary users. If you need to manage the + * default ACL, you should generally use the appropriate administrative tools + * or SQL commands instead of calling this function directly. + */ static void TrDoDropDefaultACL(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -907,6 +1011,27 @@ static void TrDoDropDefaultACL(TrObjDesc *baseDesc, ObjectAddress *object) return; } + +/* + * TrDoDropPgxcClass() --- + * Drop a distributed table class(PgxcClass). + * + * PgxcClass is an object used to represent a distributed table, which includes + * meta-information and configuration information about the table in a + * distributed architecture. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * This function drops a distributed table class from the distributed database + * to allow an administrator or user to manage the configuration and meta + * information of the distributed table. Note that this is an internal function + * of the OpenGauss database and is not normally called directly by ordinary + * users. If you need to manage distributed table classes, you should generally + * use the appropriate administrative tools or SQL commands rather than calling + * this function directly. + */ static void TrDoDropPgxcClass(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -918,6 +1043,20 @@ static void TrDoDropPgxcClass(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropExtension() --- + * Drop an extension. + * + * Extensions are a way to add additional functionality and modules to a database + * system to extend the functionality of the database. Typically, extensions are + * created by third-party developers or database administrator to add custom functions, + * types, operators, indexes, external data source connectors, and so on. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + */ static void TrDoDropExtension(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -930,6 +1069,19 @@ static void TrDoDropExtension(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropDataSource() --- + * Drop a data source. + * + * A data source is a configuration for connecting to and querying external data + * and is typically used with Foreign Tables, allows the database engine to interact + * with other data storage systems, such as other databases, files, Web services, + * and so on. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropDataSource(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -942,6 +1094,14 @@ static void TrDoDropDataSource(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropDirectory() --- + * Drop a directory obj. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropDirectory(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -954,6 +1114,17 @@ static void TrDoDropDirectory(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropRlsPolicy() --- + * Drop a R.L.S policy (Row-Level Security Policy). + * + * R.L.S policy allows administrators to define rules and policies to + * determine which users or roles can access which row data in a table. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropRlsPolicy(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -966,6 +1137,20 @@ static void TrDoDropRlsPolicy(TrObjDesc *baseDesc, ObjectAddress *object) return; } + +/* + * TrDoDropJob() --- + * Drop a Job. + * + * A Job is usually a task or program that is scheduled for execution, and can + * be a series of database operations, scripts, stored procedures, and so on. + * Jobs can be created by database administrator or schedulers and can be executed + * on schedule. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropJob(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -977,6 +1162,15 @@ static void TrDoDropJob(TrObjDesc *baseDesc, ObjectAddress *object) return; } +/* + * TrDoDropJob() --- + * Drop a synonym, which is a database object that defines an alias + * for one or more database objects to simplify queries and access. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + */ static void TrDoDropSynonym(TrObjDesc *baseDesc, ObjectAddress *object) { if (TrNeedLogicDrop(object)) { @@ -989,10 +1183,29 @@ static void TrDoDropSynonym(TrObjDesc *baseDesc, ObjectAddress *object) return; } + /* - * doDeletion: delete a single object - * return false if logic deleted, - * return true if physical deleted, + * TrDoDrop() --- + * A common operation for handling the deletion of database objects. + * + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Returns [OUT] : void. + * + * This routine is a general-purpose delete function that handles the deletion + * of various database objects, includes, but is not limited to, tables, views, + * indexes, functions, stored procedures, triggers, external tables, synonyms, + * and so on. Its role is to remove objects from the database to allow an + * administrator or user to manage the structure and data of the database. + * + * This function identifies the object to be deleted based on the passed + * description information and object address information, and performs the + * corresponding deletion operation. + * + * Note that this is an internal function of the OpenGauss database and is not + * normally called directly by ordinary users. If you need to delete a particular + * type of database object, you should usually use the appropriate administrative + * tool or SQL command, rather than calling this function directly. */ static void TrDoDrop(TrObjDesc *baseDesc, ObjectAddress *object) { @@ -1156,10 +1369,22 @@ static void TrDoDrop(TrObjDesc *baseDesc, ObjectAddress *object) return; } + /* - * deleteOneObject: delete a single object for TrDrop. + * TrDropOneObject() --- + * Delete a single object for TrDrop. * - * *depRel is the already-open pg_depend relation. + * Param [IN] baseDesc:the basic structure of object description information. + * Param [IN] object: address information for the object to be renamed. + * Param [IN] depRel: the already-open pg_depend relation. + * Returns [OUT] : void. + * + * This routine can handle the deletion of various database objects, including + * but not limited to tables, views, indexes, functions, stored procedures, + * triggers, external tables, synonyms, and so on. + * + * Note that typically, this function is used to delete a single object, rather + * than a set of related objects. */ static void TrDropOneObject(TrObjDesc *baseDesc, ObjectAddress *object, Relation *depRel) { @@ -1257,6 +1482,20 @@ static void TrDropOneObject(TrObjDesc *baseDesc, ObjectAddress *object, Relation */ } + +/* + * TrObjIsInList() --- + * Check that the given object address (thisobj) is included in the object + * address list (targetObjects). + * + * This function can be used to ensure that object dependencies and references + * are resolved before performing database operations, such as deleting an object + * or changing its properties. If the object exists in a dependency, you may need + * to delete or change the dependent before you can safely perform the operation. + * + * Traverses the 'targetObjects' list to see if there is an object address identical + * to 'thisobj' . If 'thisobj' exists in the list, returns true, else returns false. + */ static bool TrObjIsInList(const ObjectAddresses *targetObjects, const ObjectAddress *thisobj) { ObjectAddress *item = NULL; @@ -1270,6 +1509,14 @@ static bool TrObjIsInList(const ObjectAddresses *targetObjects, const ObjectAddr return false; } +/* + * TrFindIdxInTarget() --- + * Used to find the location index of the specified object's address ('item') + * in the object's address list ('targetObjects') . + * + * This function is typically used to determine where an object is in a dependency + * or reference relationship for subsequent database operations. + */ static ObjectAddress *TrFindIdxInTarget(ObjectAddresses *targetObjects, ObjectAddress *item) { ObjectAddress *thisobj = NULL; @@ -1285,7 +1532,9 @@ static ObjectAddress *TrFindIdxInTarget(ObjectAddresses *targetObjects, ObjectAd } /* - * output: refthisobjs + * TrFindAllSubObjs() --- + * finds all child objects that depend on the specified object and adds them + * to an object address list. */ static void TrFindAllSubObjs(Relation depRel, const ObjectAddress *refobj, ObjectAddresses *refthisobjs) { @@ -1315,6 +1564,25 @@ static void TrFindAllSubObjs(Relation depRel, const ObjectAddress *refobj, Objec return; } +/* + * TrTagPhyDeleteSubObjs() --- + * Used to mark child objects to be physically deleted, and to delete them. + * + * Params [IN] : + * + * 'deprel' : a pointer to a database Relation, usually used to represent + * dependencies between objects. It contains dependency metadata information, allowing + * functions to find child objects that depend on 'thisobj'. + * + * 'targetObjects' : a pointer to a list of object addresses containing a list of target + * objects to delete. Typically, this list includes 'thisobj' and other objects associated + * with it. + * + * 'thisobj' : a pointer to the address of the current object (usually the object to be + * deleted). + * + * Returns: void + */ static void TrTagPhyDeleteSubObjs(Relation depRel, ObjectAddresses *targetObjects, ObjectAddress *thisobj) { ObjectAddress *item = NULL; @@ -1342,6 +1610,27 @@ static void TrTagPhyDeleteSubObjs(Relation depRel, ObjectAddresses *targetObject return; } +/* + * TrNeedPhyDelete() --- + * Determines whether the given object and its associated child objects + * need to be physically deleted. + * + * Params [IN] : + * + * 'deprel' : a pointer to a database Relation, usually used to represent + * dependencies between objects. It contains dependency metadata information, allowing + * functions to find child objects that depend on 'thisobj'. + * + * 'targetObjects' : a pointer to a list of object addresses containing a list of target + * objects to delete. Typically, this list includes 'thisobj' and other objects associated + * with it. + * + * 'thisobj' : a pointer to the address of the current object (usually the object to be + * deleted). + * + * Returns: true if need to physically delete the given object and its associated child + * objects, else returns false. + */ static bool TrNeedPhyDelete(Relation depRel, ObjectAddresses *targetObjects, ObjectAddress *thisobj) { ObjectAddress *item = NULL; @@ -1381,6 +1670,20 @@ static bool TrNeedPhyDelete(Relation depRel, ObjectAddresses *targetObjects, Obj return result; } +/* + * TrResetDropMode() --- + * Reset the Drop Mode so that the dependent object associated with the target + * object is explicitly specified in the database operation. + * + * Params [IN] : + * + * 'targetObjects' : a pointer to a list of object addresses containing a list of target + * objects to delete. + * + * 'baseObj' : the address of the base object to delete or modify. + * + * Returns: void + */ static void TrResetDropMode(const ObjectAddresses *targetObjects, const ObjectAddress *baseObj) { ObjectAddress *thisobj = NULL; @@ -1396,6 +1699,21 @@ static void TrResetDropMode(const ObjectAddresses *targetObjects, const ObjectAd return; } +/* + * TrTagDependentObjects() --- + * Marks the dependent object associated with the target object. + * + * Params [IN] : + * + * 'deprel' : a pointer to a database Relation. + * + * 'targetObjects' : a pointer to a list of object addresses containing a list of target + * objects to delete. + * + * 'baseObj' : the address of the base object to delete or modify. + * + * Returns: void + */ static void TrTagDependentObjects(Relation depRel, ObjectAddresses *targetObjects, const ObjectAddress *baseObj) { ObjectAddress *thisobj = NULL; @@ -1417,6 +1735,25 @@ static void TrTagDependentObjects(Relation depRel, ObjectAddresses *targetObject return; } +/* + * TrCheckRecyclebinDrop() --- + * Check if objects in the DROP statement can be moved to the Recycle + * Bin instead of being deleted immediately. + * + * The Recycle Bin is a feature in OpenGauss that allows the user to move objects + * that are no longer needed into a special area instead of permanently deleting + * them. This function determines whether an object should be moved to the recycle + * bin. + * + * This routine determines whether the specified object should be moved to the Recycle + * Bin, providing a safe way to delete the object for future use. + * + * Params [IN] stmt: a pointer to the 'DROP' statement that contains information about the + * object to be deleted. + * Params [IN] objects: a pointer to an object address list that stores the address + * information of the object to be deleted. + * Returns: true if the DROP statement can be moved to the Recycle Bin, else returns false. + */ bool TrCheckRecyclebinDrop(const DropStmt *stmt, ObjectAddresses *objects) { Relation depRel; @@ -1450,6 +1787,31 @@ bool TrCheckRecyclebinDrop(const DropStmt *stmt, ObjectAddresses *objects) return rbDrop; } +/* + * TrDrop() --- + * Process the DROP statement. + * + * This routine is used to perform the deletion specified in the DROP statement + * and to determine how to delete based on the specified behavior. + * + * Params [IN] drop: a pointer to the 'DROP' statement that contains information about the + * object to be deleted. + * Params [IN] objects: a pointer to an object address list that stores the address + * information of the object to be deleted. + * Params [IN] behavior: This is an enumerated value that indicates the behavior of + * the deletion, which can be one of the following: + * + * 1. DROP_CASCADE: represents a cascading deletion, which deletes the specified + * object and recursively deletes all objects that depend on it. + * + * 2. DROP_RESTRICT: represents a restricted deletion, which is not allowed if + * there are other objects that depend on the specified object. + * + * 3. DROP_CASCADE2: similar to DROP_CASCADE, but deletes are checked to see if + * the object can be moved to the recycle bin instead of being deleted immediately. + * + * Returns: void. + */ void TrDrop(const DropStmt* drop, const ObjectAddresses *objects, DropBehavior behavior) { Relation depRel; @@ -1527,6 +1889,20 @@ void TrDrop(const DropStmt* drop, const ObjectAddresses *objects, DropBehavior b heap_close(depRel, RowExclusiveLock); } +/* + * TrDoPurgeObjectDrop() --- + * Clears traces of database object deletion. + * + * This function's role is to perform some subsequent cleanup after the object + * has been deleted to ensure that the metadata information and dependencies + * associated with the deleted object no longer exist in the database. + * + * Params [IN] desc: a pointer to the database object description information, including + * the description of the object to be cleared. This description information + * typically includes information such as the name of the object, the schema + * to which it belongs, the database to which it belongs, and so on. + * Returns: void. + */ void TrDoPurgeObjectDrop(TrObjDesc *desc) { ObjectAddresses *objects; -- 2.34.1 From 8e1236d5193e5b93b869dd477a586b96314afc61 Mon Sep 17 00:00:00 2001 From: XL_up <3066352084@qq.com> Date: Mon, 2 Oct 2023 14:12:23 +0800 Subject: [PATCH 26/26] Update tcap_manager.cpp --- src/gausskernel/storage/tcap/tcap_manager.cpp | 4081 +++++++++-------- 1 file changed, 2185 insertions(+), 1896 deletions(-) diff --git a/src/gausskernel/storage/tcap/tcap_manager.cpp b/src/gausskernel/storage/tcap/tcap_manager.cpp index da0bee6fb..4eef20634 100644 --- a/src/gausskernel/storage/tcap/tcap_manager.cpp +++ b/src/gausskernel/storage/tcap/tcap_manager.cpp @@ -1,1896 +1,2185 @@ -/* - * 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); + +/* + * TrGenObjName() --- + * Generates an object name. + * + * Param [IN] rbname: a pointer to an array of characters that specifies + the location of the generated object name. + * Param [IN] classId: identifier representing the class of the object. + * Param [IN] objid: object OID. + * Returns [OUT] : a pointer to the object name. + */ +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; +} + +/* + * TrRbGetRcyfrozenxid64() --- + * Gets the Transaction ID of a transaction. + * + * Param [IN] rbtup: a row in a database. + * Param [IN] rbRel: an optional parameter that represents a relational object + * for a database table. + * Returns [OUT] : Transaction ID obtained. + */ +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; +} + +/* + * TrDescInit() --- + * Initializes an object description (TrObjDesc) structure. + * + * Param [IN] rel: a Relation obj of a entity table. + * Param [IN] desc: a pointer to a TrObjDesc obj that needs to be initialized. + * Param [IN] operType: the type of operation for an object, such as create, + update, delete, and so on. + * Param [IN] objType: the type of database objects, such as table, index, and view. + * Param [IN] canpurge: indicates whether the object can be cleared or deleted. + * Param [IN] isBaseObj: indicates whether the obj is a baseObj. + * Returns [OUT] : void + */ +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; +} + +/* + * TrPartDescInit() --- + * Initializes a partitioned table or a partitioned object description + * (TrObjDesc structure). + * + * A partition table is a special type of database table that typically contains + * multiple partitions, each of which can manage data separately. + * + * Param [IN] rel: a Relation obj of a entity table. + * Param [IN] part: a partitioned obj. + * Param [IN] desc: a pointer to a TrObjDesc structure that contains the + * information of the obj. + * Param [IN] operType: the type of operation for an object, such as create, + update, delete, and so on. + * Param [IN] objType: the type of database objects, such as table, index, and view. + * Param [IN] canpurge: indicates whether the object can be cleared or deleted. + * Param [IN] isBaseObj: indicates whether the obj is a baseObj. + * Returns [OUT] : void + */ +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; +} + +/* + * TrDescRead() --- + * Reads the object description information from a given HeapTuple and + * populates it into a TrObjDesc structure. + * + * Param [IN] desc: a pointer to a TrObjDesc obj that needs to be filled. + * Param [IN] rbtup: a row in a database. + * Returns [OUT] : void + */ +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); +} + +/* + * TrDescWrite() --- + * Writes object description information to the system catalog table. + * + * Param [IN] desc: object description information. + * Returns [OUT] : OID of the resulting new tuple. + */ +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; +} + +/* + * TrFetchOrinameImpl() --- + * Gets the object description information of object by given + * given original name. + * + * Param [IN] nspId: a namespace identifier that used to determine the namespace + * to which an object belongs. + * Param [IN] oriname: the original name of the object to get. + * Param [IN] type: the type of database objects, such as table, index, and view. + * Param [IN] operMode: operation Modes, which includes read, write, or other + * operation modes. + * Returns [OUT] : true if found the obj. + */ +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; +} + +/* + * TrFetchName() --- + * get the object description information by given obj's name. + * + * Param [IN] rcyname: obj's name. + * Param [IN] type: the type of database objects, such as table, index, and view. + * Param [IN] operMode: operation Modes, which includes read, write, or other + * operation modes. + * Returns [OUT] : true if found the obj. + */ +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; +} + +/* + * TrFetchOriname() --- + * + * Wrapper func of TrFetchOrinameImpl.Gets the object description information of + * object by given original name. + */ +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; +} + + +/* + * TrUpdateBaseid() --- + * Updates the base Id of an obj. + * + * Param [IN] desc: the obj description information of an obj. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrLockRelationImpl() --- + * Locks a relation in the database. + * + * This func is a implementation of TrLockRelation(). + * + * Param [IN] relid: OID of a relation obj that needs to be locked. + * Param [IN] type: type of relation obj, such as table, index and view. + * Returns [OUT] : void. + */ +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))); + } +} + +/* + * TrLockRelation() --- + * Locks a relation in the database. + * + * Param [IN] desc: the obj description information of a relation that needs + * to be locked. + * Returns [OUT] : void. + */ +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(); +} + +/* + * TrUnlockTrItem() --- + * Unlocks a transaction item. + * + * Param [IN] desc: the obj description information of a transaction + * item that needs to be unlocked. + * Returns [OUT] : void. + */ +static void TrUnlockTrItem(TrObjDesc *desc) +{ + UnlockDatabaseObject(RecyclebinRelationId, desc->id, 0, + AccessExclusiveLock); +} + +/* + * TrLockTrItem() --- + * Locks a transaction item. + * + * Param [IN] desc: the obj description information of a transaction + * item that needs to be locked. + * Returns [OUT] : void. + */ +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; +} + +/* + * TrGetObjType() --- + * Gets the type of the database object. + * + * Param [IN] nspId:OID of a namespace that the obj belongs to. + * Param [IN] relKind: the type of the obj, such as table, index, and view. + * Returns [OUT] : an enumeration value of TrObjType. + * + */ +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; +} + +/* + * TrObjAddrExists() --- + * Checks whether the specified object exist in the object + * address collection. + * + * Param [IN] classid:class OID of the obj. + * Param [IN] objid: OID of the obj. + * Param [IN] objset: a pointer to a ObjectAddresses structure that + * represents a collection of object addresses. + * Returns [OUT] : true if exists. + */ +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; +} + +/* + * TrFindAllRefObjs() --- + * Finds all reference objects associated with a given object address (subobj) + * and adds them to the object address collection (refobjs). + * + * Param [IN] depRel: a dependency relation. + * Param [IN] subobj: the address of the object to be checked. + * Param [IN & OUT] refobjs: the address collection of reference objs. + * Param [IN] ignoreObjSubId: indicates whether the child identifier of the + * object is ignored. + * Returns [OUT] : void. + */ +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); +} + +/* + * TrFetchMatchSpace() --- + * Look for objects associated with a given object identifier (objId) + * in a specific relationship (rbrel) and check if they match. + */ +static bool TrFetchMatchSpace(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); + return rbForm->rcytablespace == objId; +} + +/* + * TrPurgeTablespace() --- + * Purge a specified table space. + * + * A table space is a logical space for storing data objects in the database. + * It is the largest logical unit in the database to store data, including + * tables, segments, extents, data blocks and other logical data types. A + * database can consist of multiple table spaces through which the database + * can be tuned and managed. + * + * Param [in] id: identifier of the table space to be cleared. + * Returns [out] : void. + */ +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); +} + +/* + * TrPurgeTablespaceDML() --- + * Clears the DML (data manipulation language) on the specified table space. + * + * Param [in] id: identifier of the table space to be cleared. + * Returns [out] : void. + */ +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); +} + +/* + * Begins an obj extraction operation related to the specified schema + * identifier (objId) and provides the necessary system scan descriptor + * (sd) for this operation. + */ +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); +} + +/* + * Look for objects related to objId in rbRel and check if they match. + */ +static bool TrFetchMatchSchema(Relation rbRel, HeapTuple rbTup, Oid objId) +{ + Form_pg_recyclebin rbForm = (Form_pg_recyclebin)GETSTRUCT(rbTup); + return rbForm->rcynamespace == objId; +} + +/* + * TrPurgeSchema() --- + * Purge a specified schema. + * + * Param [in] id: identifier of a schema to be purged. + * Returns [out] : void. + */ +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; +} + +/* + * TrPurgeUser() --- + * Purge a specified user. + * + * Param [in] id: identifier of a user to be purged. + * Returns [out] : void. + */ +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; +} + +/* + * TrPurgeAuto() --- + * Purge a specified automation. + * + * An automation is a set of tasks set up and executed in a database to automatically + * process and manage various operations in the database. These tasks can be periodic, + * event-based, or condition-specific. Automations can include, but are not limited + * to, data backup, data synchronization, data cleansing, performance optimization, + * and so on. By automations, we can reduce human intervention and improve the + * efficiency and stability of the database + * + * Param [in] id: identifier of a automation to be purged. + * Returns [out] : void. + */ +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); +} + +/* + * TrAdjustFrozenXid64() --- + * Adjusts the Frozen Transaction ID of the specified database. + * + * Param [IN] dbid: OID of a database. + * Param [IN] frozenXID: a frozen transaction ID. + * Returns [OUT] : void. + * + * This routine adjusts the frozen transaction id for a specified database. + * The freeze transaction ID is commonly used to identify committed + * transactions in the database, and all transactions that are earlier than + * the freeze transaction ID are considered committed, so some database + * maintenance and cleanup can be performed. + */ +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; +} + +/* + * TrRbIsEmptyDb() --- + * Checks whether the specified database is empty. + * + * Param [IN] dbid: OID of a database. + * Returns [OUT] : true if the db is empty. + */ +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; +} + +/* + * TrRbIsEmptySpc() --- + * Checks whether the specified tablespace is empty. + * + * A table space is empty if it does not contain any user tables or + * other persistent database objects. + * + * Param [IN] spcId: OID of a tablespace. + * Returns [OUT] : true if the tablespace is empty. + */ +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; +} + +/* + * TrRbIsEmptySchema() --- + * Checks whether the specified schema is empty. + * + * Param [IN] nspId: OID of a schema. + * Returns [OUT] : true if the schema is empty. + */ +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; +} + +/* + * TrRbIsEmptyUser() --- + * Checks whether the specified user is null, that is, whether the user does + * not own or create any database objects. + * + * Param [IN] roleId: user OID. + * Returns [OUT] : true if the user is empty. + */ +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; +} + +/* + * TrOidExists() --- + * Checks for the existence of a specific OID in a given OID list. + */ +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; +} + +/* + * Gets a list of databases related to the Recyclebin. + */ +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; +} + +/* + * Gets a list of databases related to a specified tablespace. + */ +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; +} + +/* + * Gets a list of dbs related to a specified schema. + */ +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; +} + +/* + * Gets a list of dbs related to a specified user. + */ +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; +} + +/* + * Gets a list of dbs related to the automation. + */ +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); +} -- 2.34.1