Fix MOT update indexed column check to be recursive

This commit is contained in:
Vinoth Veeraraghavan
2020-11-30 10:24:45 +08:00
parent 6a2918b6a3
commit 427d68ddba
3 changed files with 111 additions and 39 deletions

View File

@ -750,17 +750,18 @@ static Query* FindQueryContainForeignTbl(Query* qry)
}
/*
* brief: expression_tree_walker callback function.
* checks the entire RTE used by a query to identify their storage engine type (MOT or PAGE)
* @brief: Expression_tree_walker callback function.
* Checks the entire RTE used by a query to identify their storage engine type (MOT or PAGE).
*/
static bool StorageEngineUsedWalker(Node* node, RTEDetectorContext* context)
{
if (node == nullptr)
if (node == NULL) {
return false;
}
if (IsA(node, RangeTblRef)) {
RangeTblRef* rtr = (RangeTblRef*) node;
Query* qry = (Query*) llast(context->queryNodes);
RangeTblRef* rtr = (RangeTblRef*)node;
Query* qry = (Query*)llast(context->queryNodes);
RangeTblEntry* rte = rt_fetch(rtr->rtindex, qry->rtable);
if (rte->rtekind == RTE_RELATION) {
if (rte->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(rte->relid)) {
@ -775,27 +776,27 @@ static bool StorageEngineUsedWalker(Node* node, RTEDetectorContext* context)
if (IsA(node, Query)) {
/* Recurse into subselects */
bool result = false;
context->queryNodes = lappend(context->queryNodes, (Query*) node);
context->sublevels_up++;
result = query_tree_walker((Query*) node, (bool (*)())StorageEngineUsedWalker, (void*) context, 0);
context->sublevels_up--;
context->queryNodes = lappend(context->queryNodes, (Query*)node);
context->sublevelsUp++;
result = query_tree_walker((Query*)node, (bool (*)())StorageEngineUsedWalker, (void*)context, 0);
context->sublevelsUp--;
context->queryNodes = list_delete(context->queryNodes, llast(context->queryNodes));
return result;
}
return expression_tree_walker(node, (bool (*)())StorageEngineUsedWalker, (void*) context);
return expression_tree_walker(node, (bool (*)())StorageEngineUsedWalker, (void*)context);
}
/*
* brief: Analyze a query to check which storage engines are being used
* PG, MOT, mixed PG & MOT or Other type
* input: query to be analyzed
* output: type of storage engines
* @brief: Analyze a query to check which storage engines are being used,
* PG, MOT, mixed PG & MOT or Other type.
* @input: Query to be analyzed.
* @output: Type of storage engine.
*/
void CheckTablesStorageEngine(Query* qry, StorageEngineType* type)
{
RTEDetectorContext context;
context.queryNodes = NIL;
context.sublevels_up = 0;
context.sublevelsUp = 0;
context.isMotTable = false;
context.isPageTable = false;
@ -804,8 +805,8 @@ void CheckTablesStorageEngine(Query* qry, StorageEngineType* type)
/* check root node RTEs in case of non RangeTblRef nodes */
List* rtable = qry->rtable;
ListCell* lc = NULL;
foreach(lc, rtable) {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
foreach (lc, rtable) {
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
if (rte->rtekind == RTE_RELATION) {
if (rte->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(rte->relid)) {
context.isMotTable = true;
@ -815,11 +816,11 @@ void CheckTablesStorageEngine(Query* qry, StorageEngineType* type)
}
}
/* add root query to query stack list*/
/* add root query to query stack list */
context.queryNodes = lappend(context.queryNodes, qry);
/* recursive walk on the query */
(void)query_or_expression_tree_walker((Node*) qry, (bool (*)())StorageEngineUsedWalker, (void*) &context, 0);
(void)query_or_expression_tree_walker((Node*)qry, (bool (*)())StorageEngineUsedWalker, (void*)&context, 0);
if (context.isMotTable && context.isPageTable) {
*type = SE_TYPE_MIXED;
@ -830,33 +831,92 @@ void CheckTablesStorageEngine(Query* qry, StorageEngineType* type)
}
}
bool IsMOTIndexedColumnUpdate(Query* qry)
/*
* @brief: Expression_tree_walker callback function.
* Checks the query and all sub queries to identify if there is update on indexed column.
*/
static bool MotIndexedColumnUpdateWalker(Node* node, UpdateDetectorContext* context)
{
List* rtable = qry->rtable;
ListCell* lc = NULL;
/* we check only simple commands */
if (qry->commandType != CMD_UPDATE) {
if (node == NULL) {
return false;
}
foreach(lc, rtable) {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
if (rte->rtekind == RTE_RELATION && rte->relkind == RELKIND_FOREIGN_TABLE && isMOTFromTblOid(rte->relid)) {
if (IsA(node, RangeTblRef)) {
RangeTblRef* rtr = (RangeTblRef*)node;
Query* qry = (Query*)llast(context->queryNodes);
RangeTblEntry* rte = rt_fetch(rtr->rtindex, qry->rtable);
if (qry->commandType == CMD_UPDATE && rte->rtekind == RTE_RELATION && rte->relkind == RELKIND_FOREIGN_TABLE &&
isMOTFromTblOid(rte->relid)) {
Relation rel = relation_open(rte->relid, AccessShareLock);
Bitmapset* idx_bmps = RelationGetIndexAttrBitmap(rel,INDEX_ATTR_BITMAP_ALL);
Bitmapset* idx_bmps = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_ALL);
relation_close(rel, NoLock);
if (idx_bmps == NULL)
if (idx_bmps == NULL) {
return false;
}
ListCell* target = NULL;
foreach(target, qry->targetList) {
foreach (target, qry->targetList) {
TargetEntry* entry = (TargetEntry*)lfirst(target);
/* junk entry is temporary and won't affect the db. in addition, entry->resno might be wrong.
* We should ignore it.
if (entry->resjunk) {
continue;
}
if (bms_is_member(entry->resno - FirstLowInvalidHeapAttributeNumber, idx_bmps)) {
context->isIndexedColumnUpdate = true;
}
}
bms_free(idx_bmps);
}
return false;
}
if (IsA(node, Query)) {
/* Recurse into subselects */
bool result = false;
context->queryNodes = lappend(context->queryNodes, (Query*)node);
context->sublevelsUp++;
result = query_tree_walker((Query*)node, (bool (*)())MotIndexedColumnUpdateWalker, (void*)context, 0);
context->sublevelsUp--;
context->queryNodes = list_delete(context->queryNodes, llast(context->queryNodes));
return result;
}
return expression_tree_walker(node, (bool (*)())MotIndexedColumnUpdateWalker, (void*)context);
}
/*
* @brief: Analyze a query to check if there is update on indexed column of MOT table.
* @input: Query to be analyzed.
* @output: True/false.
*/
bool CheckMotIndexedColumnUpdate(Query* qry)
{
UpdateDetectorContext context;
context.queryNodes = NIL;
context.sublevelsUp = 0;
context.isIndexedColumnUpdate = false;
/* check root node RTEs in case of non RangeTblRef nodes */
List* rtable = qry->rtable;
ListCell* lc = NULL;
foreach (lc, rtable) {
RangeTblEntry* rte = (RangeTblEntry*)lfirst(lc);
if (qry->commandType == CMD_UPDATE && rte->rtekind == RTE_RELATION && rte->relkind == RELKIND_FOREIGN_TABLE &&
isMOTFromTblOid(rte->relid)) {
Relation rel = relation_open(rte->relid, AccessShareLock);
Bitmapset* idx_bmps = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_ALL);
relation_close(rel, NoLock);
if (idx_bmps == NULL) {
return false;
}
ListCell* target = NULL;
foreach (target, qry->targetList) {
TargetEntry* entry = (TargetEntry*)lfirst(target);
/* Junk entry is temporary and won't affect the db. in addition, entry->resno might be wrong.
* We should ignore it.
* More info in src/backend/executor/execJunk.cpp
*/
if (entry->resjunk)
if (entry->resjunk) {
continue;
}
if (bms_is_member(entry->resno - FirstLowInvalidHeapAttributeNumber, idx_bmps)) {
bms_free(idx_bmps);
return true;
@ -866,7 +926,13 @@ bool IsMOTIndexedColumnUpdate(Query* qry)
}
}
return false;
/* add root query to query stack list */
context.queryNodes = lappend(context.queryNodes, qry);
/* recursive walk on the query */
(void)query_or_expression_tree_walker((Node*)qry, (bool (*)())MotIndexedColumnUpdateWalker, (void*)&context, 0);
return context.isIndexedColumnUpdate;
}
static void CheckUnsupportInsertSelectClause(Query* query)

View File

@ -2243,7 +2243,7 @@ void exec_simple_query(const char* query_string, MessageType messageType, String
/* check for MOT update of indexed field. Can check only the querytree head, no need for drill down */
if (!IsTransactionExitStmt(parsetree) &&
(querytree_list != NULL && IsMOTIndexedColumnUpdate((Query*)linitial(querytree_list)))) {
(querytree_list != NULL && CheckMotIndexedColumnUpdate((Query*)linitial(querytree_list)))) {
ereport(ERROR, (errcode(ERRCODE_FDW_UPDATE_INDEXED_FIELD_NOT_SUPPORTED), errmodule(MOD_MOT),
errmsg("Update of indexed column is not supported for memory table")));
}
@ -3153,7 +3153,7 @@ void exec_parse_message(const char* query_string, /* string to execute */
}
/******************************* MOT LLVM *************************************/
if (!IsTransactionExitStmt(raw_parse_tree) && IsMOTIndexedColumnUpdate(query)) {
if (!IsTransactionExitStmt(raw_parse_tree) && CheckMotIndexedColumnUpdate(query)) {
ereport(ERROR, (errcode(ERRCODE_FDW_UPDATE_INDEXED_FIELD_NOT_SUPPORTED), errmodule(MOD_MOT),
errmsg("Update of indexed column is not supported for memory table")));
}

View File

@ -39,15 +39,21 @@ extern bool analyze_requires_snapshot(Node* parseTree);
extern void CheckSelectLocking(Query* qry);
extern void applyLockingClause(Query* qry, Index rtindex, bool forUpdate, bool noWait, bool pushedDown);
extern void CheckTablesStorageEngine(Query* qry, StorageEngineType* type);
extern bool IsMOTIndexedColumnUpdate(Query* qry);
extern bool CheckMotIndexedColumnUpdate(Query* qry);
typedef struct RTEDetectorContext {
bool isMotTable;
bool isPageTable;
List* queryNodes;
int sublevels_up;
int sublevelsUp;
} RTEDetectorContext;
typedef struct UpdateDetectorContext {
bool isIndexedColumnUpdate;
List* queryNodes;
int sublevelsUp;
} UpdateDetectorContext;
/* Record the rel name and corresponding columan name info */
typedef struct RelColumnInfo {
char* relname;