为src/common/backend/parser下的文件添加了部分注释 #29
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -1959,6 +1984,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;
|
||||
|
|
@ -1997,6 +2025,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;
|
||||
|
|
@ -3752,6 +3785,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;
|
||||
|
|
@ -3771,6 +3807,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;
|
||||
|
|
@ -3800,6 +3853,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) &&
|
||||
|
|
@ -3875,6 +3931,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;
|
||||
|
|
@ -3915,6 +3974,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) {
|
||||
|
|
@ -4007,6 +4069,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;
|
||||
|
|
@ -4706,8 +4771,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) {
|
||||
|
|
@ -4719,7 +4790,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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ typedef struct 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;
|
||||
} OprCacheEntry;
|
||||
|
||||
static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion);
|
||||
|
|
@ -90,12 +90,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;
|
||||
|
||||
|
|
@ -127,6 +133,7 @@ Oid LookupOperNameTypeNames(
|
|||
{
|
||||
Oid leftoid, rightoid;
|
||||
|
||||
/* fetch type's OID by given TypeNames */
|
||||
if (oprleft == NULL)
|
||||
leftoid = InvalidOid;
|
||||
else
|
||||
|
|
@ -166,9 +173,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 eq_opr; // '='
|
||||
Oid gt_opr; // '>'
|
||||
bool hashable = false;
|
||||
|
||||
/*
|
||||
|
|
@ -183,9 +190,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 */
|
||||
|
|
@ -210,7 +217,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);
|
||||
|
|
@ -224,6 +235,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);
|
||||
|
|
@ -237,6 +254,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;
|
||||
|
|
@ -487,12 +511,17 @@ 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;
|
||||
}
|
||||
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
|
|
@ -529,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.
|
||||
|
|
@ -681,7 +711,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 +722,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 +761,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 +769,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 +784,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 +792,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 +800,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
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -103,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];
|
||||
|
|
@ -265,10 +273,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,7 +296,7 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,17 +171,21 @@ 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';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* scanner_isspace() --- return TRUE if flex scanner considers char whitespace
|
||||
*
|
||||
|
|
@ -197,4 +203,4 @@ bool scanner_isspace(char ch)
|
|||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
@ -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 {
|
||||
|
|
@ -2282,6 +2307,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);
|
||||
|
|
@ -2501,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;
|
||||
|
||||
|
|
@ -2522,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;
|
||||
|
|
@ -2926,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)
|
||||
{
|
||||
|
|
@ -2959,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;
|
||||
|
|
@ -2992,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)
|
||||
|
|
@ -3032,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;
|
||||
|
|
@ -3580,6 +3632,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 +3663,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)) {
|
||||
|
|
|
|||
|
|
@ -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 and try to reduce redundant orderby clauses.
|
||||
*
|
||||
* @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 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 redundant orderby clauses for it.
|
||||
*/
|
||||
static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce)
|
||||
{
|
||||
/* query isn't in a from-where clause, 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 */
|
||||
/* Try to drop orderby-clause on 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,30 @@ static void reduce_orderby_recurse(Query* query, Node* jtnode, bool reduce)
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
FromExpr* f = (FromExpr*)jtnode;
|
||||
ListCell* l = NULL;
|
||||
bool flag = false;
|
||||
|
||||
/*
|
||||
* 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 from-clause to reduce its orderby. */
|
||||
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 +2201,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 +2212,58 @@ 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.
|
||||
*
|
||||
* @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
|
||||
* reduce_orderby_final() ---
|
||||
* Reduce orderby clause for subquery if it has a removable 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
|
||||
*/
|
||||
static void reduce_orderby_final(RangeTblEntry* rte, bool reduce)
|
||||
{
|
||||
/* Reduce orderby clause in subquery for join or from clause of more than one rte */
|
||||
/*
|
||||
* 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) {
|
||||
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 identifier 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 +2273,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 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.
|
||||
*/
|
||||
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-where clause, join clause or aggregation
|
||||
* structure, and to decide whether to drop its orderby.
|
||||
*/
|
||||
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 +2307,5 @@ void reduce_orderby(Query* query, bool reduce)
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* Reduce orderby : end */
|
||||
/* Reduce orderby : end */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue