!5768 处理issue:视图列为基表列的表达式计算结果,修改基表列的变量类型后,查询视图宕机
Merge pull request !5768 from lukeman/core_pr
This commit is contained in:
@ -1037,50 +1037,6 @@ static bool CheckRelationColumnExists(Oid rel_oid, int2 attnum, Form_pg_attribut
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CheckPgAttribute(Oid obj_oid, char* attName, Form_pg_attribute new_attribute)
|
||||
{
|
||||
const int keyNum = 2;
|
||||
Relation rel;
|
||||
ScanKeyData key[keyNum];
|
||||
SysScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
HeapTuple new_dep_tuple;
|
||||
Form_pg_attribute attForm;
|
||||
rel = heap_open(AttributeRelationId, RowExclusiveLock);
|
||||
ScanKeyInit(&key[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_oid));
|
||||
ScanKeyInit(&key[1], Anum_pg_attribute_attname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(attName));
|
||||
scan = systable_beginscan(rel, AttributeRelidNameIndexId, true, SnapshotSelf, keyNum, &key[0]);
|
||||
tuple = systable_getnext(scan);
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
systable_endscan(scan);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
elog(ERROR, "catalog lookup failed for column %s of relation %u", attName, obj_oid);
|
||||
}
|
||||
attForm = (Form_pg_attribute)GETSTRUCT(tuple);
|
||||
Datum values[Natts_pg_attribute] = { 0 };
|
||||
bool nulls[Natts_pg_attribute] = { 0 };
|
||||
bool replaces[Natts_pg_attribute] = { 0 };
|
||||
values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
|
||||
values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
|
||||
values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attribute->atttypmod);
|
||||
values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval);
|
||||
values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage);
|
||||
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
|
||||
replaces[Anum_pg_attribute_atttypid - 1] = true;
|
||||
replaces[Anum_pg_attribute_attlen - 1] = true;
|
||||
replaces[Anum_pg_attribute_atttypmod - 1] = true;
|
||||
replaces[Anum_pg_attribute_attbyval - 1] = true;
|
||||
replaces[Anum_pg_attribute_attstorage - 1] = true;
|
||||
replaces[Anum_pg_attribute_attcollation - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces);
|
||||
simple_heap_update(rel, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rel, new_dep_tuple);
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
systable_endscan(scan);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
static bool findDependentTable(Relation rel, Oid type_id)
|
||||
{
|
||||
bool found = false;
|
||||
@ -1119,6 +1075,8 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
bool existTable = false;
|
||||
Oid rw_objid = InvalidOid;
|
||||
Oid type_id = InvalidOid;
|
||||
List* originEvAction = NIL;
|
||||
List* freshedEvAction = NIL;
|
||||
// 1. filter the valid view
|
||||
if (GetPgObjectValid(view_oid, objType)) {
|
||||
return ValidateDependValid;
|
||||
@ -1129,7 +1087,6 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
return ValidateDependCircularDepend;
|
||||
}
|
||||
*list = lappend_oid(*list, view_oid);
|
||||
|
||||
// 2. find pg_rewrite/pg_type entry which depend on this view internally
|
||||
const int keyNum = 2;
|
||||
ScanKeyData key[keyNum];
|
||||
@ -1166,6 +1123,7 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
scan_dep = systable_beginscan(rel_dep, DependDependerIndexId, true, NULL, keyNum, key_dep);
|
||||
Form_pg_attribute newtuple = (Form_pg_attribute)palloc0(sizeof(FormData_pg_attribute));
|
||||
bool circularDependency = false;
|
||||
bool is_changed = false;
|
||||
while (HeapTupleIsValid((tup_dep = systable_getnext(scan_dep)))) {
|
||||
Form_pg_depend depform = (Form_pg_depend)GETSTRUCT(tup_dep);
|
||||
if (depform->refclassid != RelationRelationId || depform->deptype != DEPENDENCY_NORMAL ||
|
||||
@ -1181,21 +1139,21 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
isValid &= CheckRelationColumnExists(dep_objid, dep_objsubid, newtuple);
|
||||
if (newtuple->attnum > 0) {
|
||||
// change pg_depend
|
||||
Datum values[Natts_pg_depend] = { 0 };
|
||||
bool nulls[Natts_pg_depend] = { 0 };
|
||||
bool replaces[Natts_pg_depend] = { 0 };
|
||||
HeapTuple new_dep_tuple;
|
||||
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(newtuple->attnum);
|
||||
replaces[Anum_pg_depend_refobjsubid - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(tup_dep, RelationGetDescr(rel_dep), values, nulls, replaces);
|
||||
simple_heap_update(rel_dep, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rel_dep, new_dep_tuple);
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
// change pg_rewrite targetEntry
|
||||
CheckPgRewriteWithDroppedColumn(dep_objid, rw_objid, newtuple, dep_objsubid, &attName, &query_str);
|
||||
// change pg_attribute
|
||||
CheckPgAttribute(view_oid, attName, newtuple);
|
||||
if (newtuple->attnum != dep_objsubid) {
|
||||
Datum values[Natts_pg_depend] = { 0 };
|
||||
bool nulls[Natts_pg_depend] = { 0 };
|
||||
bool replaces[Natts_pg_depend] = { 0 };
|
||||
HeapTuple new_dep_tuple;
|
||||
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(newtuple->attnum);
|
||||
replaces[Anum_pg_depend_refobjsubid - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(tup_dep, RelationGetDescr(rel_dep), values, nulls, replaces);
|
||||
simple_heap_update(rel_dep, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rel_dep, new_dep_tuple);
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
is_changed |= UpdateChangedColumnForView(view_oid, dep_objid, dep_objsubid, rw_objid,
|
||||
&originEvAction, &freshedEvAction, newtuple);
|
||||
}
|
||||
} else if (relkind == RELKIND_VIEW || relkind == RELKIND_MATVIEW) {
|
||||
char type = relkind == RELKIND_VIEW ? OBJECT_TYPE_VIEW : OBJECT_TYPE_MATVIEW;
|
||||
@ -1211,10 +1169,8 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
// here means dep_objid is valid, we should keep the same view_oid.attr with dep_objid.dep_objsubid
|
||||
// find dep_objid.dep_objsubid
|
||||
CheckViewColumnExists(dep_objid, dep_objsubid, newtuple);
|
||||
// change pg_rewrite targetEntry
|
||||
CheckPgRewriteWithDroppedColumn(dep_objid, rw_objid, newtuple, dep_objsubid, &attName, &query_str);
|
||||
// change pg_attribute
|
||||
CheckPgAttribute(view_oid, attName, newtuple);
|
||||
is_changed |= UpdateChangedColumnForView(view_oid, dep_objid, dep_objsubid, rw_objid,
|
||||
&originEvAction, &freshedEvAction, newtuple);
|
||||
}
|
||||
circularDependency |= (result == ValidateDependCircularDepend);
|
||||
}
|
||||
@ -1237,15 +1193,48 @@ static ValidateDependResult ValidateDependView(Oid view_oid, char objType, List*
|
||||
elog(ERROR, "The view is invalid. There is a table dependent on the view so it cannot be recompiled.");
|
||||
}
|
||||
heap_close(rel_dep, RowExclusiveLock);
|
||||
// 3.3 change pg_rewrite's evAction
|
||||
if (is_changed) {
|
||||
UpdatePgrewriteForView(rw_objid, freshedEvAction, &query_str);
|
||||
}
|
||||
// 4. mark the current view valid
|
||||
if (!circularDependency) {
|
||||
SetPgObjectValid(view_oid, objType, true);
|
||||
}
|
||||
/* create or replace view */
|
||||
if (!circularDependency && objType == OBJECT_TYPE_VIEW) {
|
||||
if (!circularDependency && query_str != NIL) {
|
||||
ReplaceViewQueryFirstAfter(query_str);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
list_free_ext(query_str);
|
||||
if (objType == OBJECT_TYPE_MATVIEW) {
|
||||
HeapTuple tup = SearchSysCache1(RELOID, ObjectIdGetDatum(view_oid));
|
||||
Form_pg_class relform = (Form_pg_class)GETSTRUCT(tup);
|
||||
Oid toastid = relform->reltoastrelid;
|
||||
if (OidIsValid(toastid)) {
|
||||
HeapTuple toasttuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
|
||||
if (!HeapTupleIsValid(toasttuple)) {
|
||||
Relation rel_class = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
Datum values[Natts_pg_class] = { 0 };
|
||||
bool nulls[Natts_pg_class] = { 0 };
|
||||
bool replaces[Natts_pg_class] = { 0 };
|
||||
HeapTuple newtuple;
|
||||
values[Anum_pg_class_reltoastrelid - 1] = InvalidOid;
|
||||
replaces[Anum_pg_class_reltoastrelid - 1] = true;
|
||||
newtuple = heap_modify_tuple(tup, RelationGetDescr(rel_class), values, nulls, replaces);
|
||||
simple_heap_update(rel_class, &newtuple->t_self, newtuple);
|
||||
CatalogUpdateIndexes(rel_class, newtuple);
|
||||
heap_freetuple_ext(newtuple);
|
||||
CommandCounterIncrement();
|
||||
heap_close(rel_class, RowExclusiveLock);
|
||||
} else {
|
||||
ReleaseSysCache(toasttuple);
|
||||
}
|
||||
}
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
list_free(originEvAction);
|
||||
list_free(freshedEvAction);
|
||||
/* 0 or 1 */
|
||||
return (ValidateDependResult)isValid;
|
||||
}
|
||||
@ -1420,13 +1409,14 @@ Relation parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockm
|
||||
}
|
||||
|
||||
if (RelationGetRelkind(rel) == RELKIND_VIEW &&
|
||||
RelationGetRelid(rel) >= FirstNormalObjectId &&
|
||||
ValidateDependView(RelationGetRelid(rel), OBJECT_TYPE_VIEW) == ValidateDependInvalid) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("The view %s is invalid, please make it valid before operation.",
|
||||
RelationGetRelationName(rel)),
|
||||
errhint("Please re-add missing table fields.")));
|
||||
RelationGetRelid(rel) >= FirstNormalObjectId) {
|
||||
if (ValidateDependView(RelationGetRelid(rel), OBJECT_TYPE_VIEW) == ValidateDependInvalid) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("The view %s is invalid, please make it valid before operation.",
|
||||
RelationGetRelationName(rel)),
|
||||
errhint("Please re-add missing table fields.")));
|
||||
}
|
||||
}
|
||||
|
||||
if (!u_sess->attr.attr_common.XactReadOnly && rel->rd_id == UserStatusRelationId) {
|
||||
|
@ -243,7 +243,13 @@ typedef struct ViewInfoForAdd {
|
||||
char *query_string;
|
||||
} ViewInfoForAdd;
|
||||
|
||||
|
||||
/* Context for check whether the targetEntry of view's querytree has changed */
|
||||
typedef struct {
|
||||
Oid relid;
|
||||
int2 attnum;
|
||||
Query* query;
|
||||
Form_pg_attribute attForm;
|
||||
} ViewQueryCheck_context;
|
||||
|
||||
/* Struct describing one new constraint to check in Phase 3 scan */
|
||||
/* Note: new NOT NULL constraints are handled elsewhere */
|
||||
@ -12142,110 +12148,271 @@ static List *CheckPgRewriteFirstAfter(Relation rel)
|
||||
return query_str;
|
||||
}
|
||||
|
||||
|
||||
void CheckPgRewriteWithDroppedColumn(Oid rel_oid, Oid rw_oid, Form_pg_attribute attForm,
|
||||
int2 old_attnum, char** attName, List **old_query_str)
|
||||
static bool check_changed_tle_walker(Node* node, ViewQueryCheck_context* context)
|
||||
{
|
||||
List *query_str = NIL;
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var)) {
|
||||
Var* var = (Var*)node;
|
||||
Oid relid = InvalidOid;
|
||||
RangeTblEntry* rte = rt_fetch(var->varno, context->query->rtable);
|
||||
if (rte && rte->rtekind == RTE_RELATION) {
|
||||
relid = rte->relid;
|
||||
} else if (rte && rte->alias == NULL && rte->rtekind == RTE_JOIN && rte->joinaliasvars != NIL) {
|
||||
Var* aliasvar = (Var*)list_nth(rte->joinaliasvars, var->varattno - 1);
|
||||
RangeTblEntry* alias_rte = rt_fetch(aliasvar->varno, context->query->rtable);
|
||||
Assert(alias_rte->rtekind == RTE_RELATION);
|
||||
relid = alias_rte->relid;
|
||||
}
|
||||
if (relid == context->relid &&
|
||||
var->varattno == context->attnum) {
|
||||
return !(var->vartype == context->attForm->atttypid &&
|
||||
var->vartypmod == context->attForm->atttypmod &&
|
||||
var->varcollid == context->attForm->attcollation);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return expression_tree_walker(node, (bool (*)())check_changed_tle_walker, context);
|
||||
}
|
||||
|
||||
static void CheckPgAttribute(Oid obj_oid, char* attName, Form_pg_attribute new_attribute)
|
||||
{
|
||||
if (!OidIsValid(obj_oid) || attName == NULL) {
|
||||
return;
|
||||
}
|
||||
const int keyNum = 2;
|
||||
Relation rel;
|
||||
ScanKeyData key[keyNum];
|
||||
SysScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
HeapTuple new_dep_tuple;
|
||||
Form_pg_attribute attForm;
|
||||
rel = heap_open(AttributeRelationId, RowExclusiveLock);
|
||||
ScanKeyInit(&key[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(obj_oid));
|
||||
ScanKeyInit(&key[1], Anum_pg_attribute_attname, BTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(attName));
|
||||
scan = systable_beginscan(rel, AttributeRelidNameIndexId, true, SnapshotSelf, keyNum, &key[0]);
|
||||
tuple = systable_getnext(scan);
|
||||
if (!HeapTupleIsValid(tuple)) {
|
||||
systable_endscan(scan);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
elog(ERROR, "catalog lookup failed for column %s of relation %u", attName, obj_oid);
|
||||
}
|
||||
attForm = (Form_pg_attribute)GETSTRUCT(tuple);
|
||||
Datum values[Natts_pg_attribute] = { 0 };
|
||||
bool nulls[Natts_pg_attribute] = { 0 };
|
||||
bool replaces[Natts_pg_attribute] = { 0 };
|
||||
values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
|
||||
values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
|
||||
values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attribute->atttypmod);
|
||||
values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval);
|
||||
values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage);
|
||||
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
|
||||
replaces[Anum_pg_attribute_atttypid - 1] = true;
|
||||
replaces[Anum_pg_attribute_attlen - 1] = true;
|
||||
replaces[Anum_pg_attribute_atttypmod - 1] = true;
|
||||
replaces[Anum_pg_attribute_attbyval - 1] = true;
|
||||
replaces[Anum_pg_attribute_attstorage - 1] = true;
|
||||
replaces[Anum_pg_attribute_attcollation - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces);
|
||||
simple_heap_update(rel, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rel, new_dep_tuple);
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
systable_endscan(scan);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
static void UpdatePgAttributeForView(TargetEntry* old_tle, TargetEntry* new_tle, Oid view_oid, Form_pg_attribute att_form)
|
||||
{
|
||||
Node* old_node = (Node*)old_tle->expr;
|
||||
Node* new_node = (Node*)new_tle->expr;
|
||||
Assert(nodeTag(old_node) == nodeTag(new_node));
|
||||
char* col_name = old_tle->resname;
|
||||
Oid old_type = exprType(old_node);
|
||||
Oid new_type = exprType(new_node);
|
||||
int32 old_typmod = exprTypmod(old_node);
|
||||
int32 new_typmod = exprTypmod(new_node);
|
||||
if (old_type != new_type || old_typmod != new_typmod) {
|
||||
// get from pg_type
|
||||
HeapTuple tp;
|
||||
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(new_type));
|
||||
if (HeapTupleIsValid(tp)) {
|
||||
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
|
||||
att_form->atttypid = new_type;
|
||||
att_form->atttypmod = new_typmod;
|
||||
att_form->attlen = typtup->typlen;
|
||||
att_form->attbyval = typtup->typbyval;
|
||||
att_form->attstorage = typtup->typstorage;
|
||||
att_form->attcollation = typtup->typcollation;
|
||||
ReleaseSysCache(tp);
|
||||
} else {
|
||||
elog(ERROR, "Cannot find the type with oid %u.", new_type);
|
||||
}
|
||||
CheckPgAttribute(view_oid, col_name, att_form);
|
||||
}
|
||||
}
|
||||
|
||||
static List* GetOriginalViewQuery(Oid rw_oid)
|
||||
{
|
||||
List *evAction = NIL;
|
||||
ScanKeyData entry;
|
||||
ScanKeyInit(&entry, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(rw_oid));
|
||||
Relation rewrite_rel = heap_open(RewriteRelationId, RowExclusiveLock);
|
||||
SysScanDesc rewrite_scan = systable_beginscan(rewrite_rel, RewriteOidIndexId, true, NULL, 1, &entry);
|
||||
HeapTuple rewrite_tup = systable_getnext(rewrite_scan);
|
||||
if (!HeapTupleIsValid(rewrite_tup)) {
|
||||
systable_endscan(rewrite_scan);
|
||||
heap_close(rewrite_rel, RowExclusiveLock);
|
||||
return;
|
||||
elog(ERROR, "Cannot find the rewrite rule with oid %u.", rw_oid);
|
||||
}
|
||||
Form_pg_rewrite rewrite_form = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);
|
||||
if (strcmp(NameStr(rewrite_form->rulename), ViewSelectRuleName) != 0) {
|
||||
systable_endscan(rewrite_scan);
|
||||
heap_close(rewrite_rel, RowExclusiveLock);
|
||||
return;
|
||||
if (strcmp(NameStr(rewrite_form->rulename), "_RETURN") != 0) {
|
||||
elog(ERROR, "The rewrite rule with oid %u has unexpected name %s.", rw_oid, NameStr(rewrite_form->rulename));
|
||||
}
|
||||
bool is_null = false;
|
||||
Datum evActiomDatum = fastgetattr(rewrite_tup, Anum_pg_rewrite_ev_action, rewrite_rel->rd_att, &is_null);
|
||||
if (!is_null) {
|
||||
Datum values[Natts_pg_rewrite] = { 0 };
|
||||
bool nulls[Natts_pg_rewrite] = { 0 };
|
||||
bool replaces[Natts_pg_rewrite] = { 0 };
|
||||
char *evActionString = TextDatumGetCString(evActiomDatum);
|
||||
List *evAction = (List *)stringToNode(evActionString);
|
||||
Query* query = (Query*)linitial(evAction);
|
||||
// change querytree's targetEntry and RTE
|
||||
ListCell* lc = NULL;
|
||||
foreach (lc, query->targetList) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(lc);
|
||||
Index rtevarno = 0;
|
||||
AttrNumber rtevarattno = 0;
|
||||
if (nodeTag((Node*)tle->expr) == T_Var && tle->resorigtbl == rel_oid &&
|
||||
tle->resorigcol == old_attnum) {
|
||||
tle->resorigcol = attForm->attnum;
|
||||
Var *var = (Var *)tle->expr;
|
||||
rtevarno = var->varno;
|
||||
rtevarattno = var->varattno;
|
||||
var->vartype = attForm->atttypid;
|
||||
var->vartypmod = attForm->atttypmod;
|
||||
var->varcollid = attForm->attcollation;
|
||||
*attName = pstrdup(tle->resname);
|
||||
}
|
||||
// change rtable entry
|
||||
if (rtevarno == 0 || rtevarattno == 0) {
|
||||
continue;
|
||||
}
|
||||
RangeTblEntry* rte = rt_fetch(rtevarno, query->rtable);
|
||||
if (!rte || rte->alias != NULL || rte->rtekind != RTE_JOIN || rte->joinaliasvars == NIL) {
|
||||
Var *var = (Var *)tle->expr;
|
||||
var->varattno = attForm->attnum;
|
||||
var->varoattno = attForm->attnum;
|
||||
continue;
|
||||
}
|
||||
Var* aliasvar = (Var*)list_nth(rte->joinaliasvars, rtevarattno - 1);
|
||||
if (IsA(aliasvar, Var)) {
|
||||
aliasvar->varattno = attForm->attnum;
|
||||
aliasvar->varoattno = attForm->attnum;
|
||||
aliasvar->vartype = attForm->atttypid;
|
||||
aliasvar->vartypmod = attForm->atttypmod;
|
||||
aliasvar->varcollid = attForm->attcollation;
|
||||
}
|
||||
}
|
||||
char* actiontree = nodeToString((Node*)evAction);
|
||||
HeapTuple new_dep_tuple;
|
||||
values[Anum_pg_rewrite_ev_action - 1] = CStringGetTextDatum(actiontree);
|
||||
replaces[Anum_pg_rewrite_ev_action - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(rewrite_tup, RelationGetDescr(rewrite_rel), values, nulls, replaces);
|
||||
simple_heap_update(rewrite_rel, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rewrite_rel, new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
StringInfoData buf;
|
||||
initStringInfo(&buf);
|
||||
Relation ev_relation = heap_open(rewrite_form->ev_class, AccessShareLock);
|
||||
get_query_def(query,
|
||||
&buf,
|
||||
NIL,
|
||||
RelationGetDescr(ev_relation),
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
NULL,
|
||||
false,
|
||||
false);
|
||||
appendStringInfo(&buf, ";");
|
||||
ViewInfoForAdd * info = static_cast<ViewInfoForAdd *>(palloc(sizeof(ViewInfoForAdd)));
|
||||
info->ev_class = rewrite_form->ev_class;
|
||||
info->query_string = pstrdup(buf.data);
|
||||
heap_close(ev_relation, AccessShareLock);
|
||||
FreeStringInfo(&buf);
|
||||
query_str = lappend(query_str, info);
|
||||
*old_query_str = query_str;
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
pfree_ext(evActionString);
|
||||
pfree_ext(actiontree);
|
||||
evAction = (List *)stringToNode(evActionString);
|
||||
} else {
|
||||
elog(ERROR, "Cannot find the rewrite rule with oid %u.", rw_oid);
|
||||
}
|
||||
systable_endscan(rewrite_scan);
|
||||
heap_close(rewrite_rel, RowExclusiveLock);
|
||||
return evAction;
|
||||
}
|
||||
|
||||
List* GetRefreshedViewQuery(Oid view_oid, Oid rw_oid)
|
||||
{
|
||||
List* evAction = NIL;
|
||||
Query* query = NULL;
|
||||
HeapTuple tup = SearchSysCache1(RELOID, ObjectIdGetDatum(view_oid));
|
||||
if (!HeapTupleIsValid(tup)) {
|
||||
elog(ERROR, "Cannot find the view with oid %u.", view_oid);
|
||||
}
|
||||
Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tup);
|
||||
char* view_def = GetCreateViewCommand(NameStr(reltup->relname), tup, reltup, rw_oid, view_oid);
|
||||
List* raw_parsetree_list = raw_parser(view_def);
|
||||
Node* stmtNode = (Node*)linitial(raw_parsetree_list);
|
||||
Assert((IsA(stmtNode, ViewStmt) || IsA(stmtNode, CreateTableAsStmt)));
|
||||
if (IsA(stmtNode, ViewStmt)) {
|
||||
ViewStmt* stmt = (ViewStmt*)stmtNode;
|
||||
if (!IsA(stmt->query, Query)) {
|
||||
query = parse_analyze(stmt->query, view_def, NULL, 0);
|
||||
} else {
|
||||
query = (Query*)stmt->query;
|
||||
}
|
||||
} else if (IsA(stmtNode, CreateTableAsStmt)) {
|
||||
CreateTableAsStmt* stmt = (CreateTableAsStmt*)stmtNode;
|
||||
if (!IsA(stmt->query, Query)) {
|
||||
query = parse_analyze(stmt->query, view_def, NULL, 0);
|
||||
} else {
|
||||
query = (Query*)stmt->query;
|
||||
}
|
||||
}
|
||||
evAction = lappend(evAction, query);
|
||||
ReleaseSysCache(tup);
|
||||
pfree(view_def);
|
||||
list_free(raw_parsetree_list);
|
||||
return evAction;
|
||||
}
|
||||
|
||||
void UpdatePgrewriteForView(Oid rw_oid, List* evAction, List **query_str)
|
||||
{
|
||||
List *new_query_str = NIL;
|
||||
ScanKeyData entry;
|
||||
ScanKeyInit(&entry, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(rw_oid));
|
||||
Relation rewrite_rel = heap_open(RewriteRelationId, RowExclusiveLock);
|
||||
SysScanDesc rewrite_scan = systable_beginscan(rewrite_rel, RewriteOidIndexId, true, NULL, 1, &entry);
|
||||
HeapTuple rewrite_tup = systable_getnext(rewrite_scan);
|
||||
Form_pg_rewrite rewrite_form = (Form_pg_rewrite)GETSTRUCT(rewrite_tup);
|
||||
// update pg_rewrite
|
||||
char* actiontree = nodeToString((Node*)evAction);
|
||||
Datum values[Natts_pg_rewrite] = { 0 };
|
||||
bool nulls[Natts_pg_rewrite] = { 0 };
|
||||
bool replaces[Natts_pg_rewrite] = { 0 };
|
||||
HeapTuple new_dep_tuple;
|
||||
values[Anum_pg_rewrite_ev_action - 1] = CStringGetTextDatum(actiontree);
|
||||
replaces[Anum_pg_rewrite_ev_action - 1] = true;
|
||||
new_dep_tuple = heap_modify_tuple(rewrite_tup, RelationGetDescr(rewrite_rel), values, nulls, replaces);
|
||||
simple_heap_update(rewrite_rel, &new_dep_tuple->t_self, new_dep_tuple);
|
||||
CatalogUpdateIndexes(rewrite_rel, new_dep_tuple);
|
||||
CommandCounterIncrement();
|
||||
heap_freetuple_ext(new_dep_tuple);
|
||||
pfree_ext(actiontree);
|
||||
// get new_query_str from pg_rewrite
|
||||
Query* query = (Query*)linitial(evAction);
|
||||
StringInfoData buf;
|
||||
initStringInfo(&buf);
|
||||
Relation ev_relation = heap_open(rewrite_form->ev_class, AccessShareLock);
|
||||
get_query_def(query,
|
||||
&buf,
|
||||
NIL,
|
||||
RelationGetDescr(ev_relation),
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
NULL,
|
||||
false,
|
||||
false);
|
||||
appendStringInfo(&buf, ";");
|
||||
ViewInfoForAdd * info = static_cast<ViewInfoForAdd *>(palloc(sizeof(ViewInfoForAdd)));
|
||||
info->ev_class = rewrite_form->ev_class;
|
||||
info->query_string = pstrdup(buf.data);
|
||||
heap_close(ev_relation, AccessShareLock);
|
||||
FreeStringInfo(&buf);
|
||||
new_query_str = lappend(new_query_str, info);
|
||||
*query_str = new_query_str;
|
||||
systable_endscan(rewrite_scan);
|
||||
heap_close(rewrite_rel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
bool UpdateChangedColumnForView(Oid viewid, Oid relid, int2 attnum, Oid rw_objid,
|
||||
List **p_originEvAction, List **p_newEvAction, Form_pg_attribute attForm)
|
||||
{
|
||||
if (*p_originEvAction == NIL) {
|
||||
*p_originEvAction = GetOriginalViewQuery(rw_objid);
|
||||
}
|
||||
PG_TRY();
|
||||
{
|
||||
if (*p_newEvAction == NIL) {
|
||||
*p_newEvAction = GetRefreshedViewQuery(viewid, rw_objid);
|
||||
}
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("The view %s is invalid, please make it valid before operation.",
|
||||
get_rel_name(viewid)),
|
||||
errhint("Please re-add missing table fields.")));
|
||||
}
|
||||
PG_END_TRY();
|
||||
List* originEvAction = *p_originEvAction;
|
||||
List* newEvAction = *p_newEvAction;
|
||||
bool is_changed = false;
|
||||
Query* query = (Query*)linitial(originEvAction);
|
||||
Query* freshed_query = (Query*)linitial(newEvAction);
|
||||
ViewQueryCheck_context context;
|
||||
context.relid = relid;
|
||||
context.attnum = attnum;
|
||||
context.query = query;
|
||||
context.attForm = attForm;
|
||||
// check whether the querytree's targetEntry has changed,
|
||||
// and update the pg_attribute entry of the changed column if so
|
||||
ListCell* lc1 = NULL;
|
||||
ListCell* lc2 = NULL;
|
||||
forboth (lc1, query->targetList, lc2, freshed_query->targetList) {
|
||||
TargetEntry* tle = (TargetEntry*)lfirst(lc1);
|
||||
bool var_changed = check_changed_tle_walker((Node*)(tle->expr), &context);
|
||||
if (var_changed) {
|
||||
TargetEntry* tle2 = (TargetEntry*)lfirst(lc2);
|
||||
UpdatePgAttributeForView(tle, tle2, viewid, attForm);
|
||||
}
|
||||
is_changed |= var_changed;
|
||||
}
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -32758,7 +32925,7 @@ static void ATPrepAlterModifyColumn(List** wqueue, AlteredTableInfo* tab, Relati
|
||||
def->raw_default = tmp_expr;
|
||||
}
|
||||
|
||||
static char* GetCreateViewCommand(const char *rel_name, HeapTuple tup, Form_pg_class reltup, Oid pg_rewrite_oid, Oid view_oid)
|
||||
char* GetCreateViewCommand(const char *rel_name, HeapTuple tup, Form_pg_class reltup, Oid pg_rewrite_oid, Oid view_oid)
|
||||
{
|
||||
StringInfoData buf;
|
||||
ViewInfoForAdd* view_info = NULL;
|
||||
@ -32767,9 +32934,13 @@ static char* GetCreateViewCommand(const char *rel_name, HeapTuple tup, Form_pg_c
|
||||
const char* ns_name = quote_identifier(get_namespace_name(reltup->relnamespace));
|
||||
|
||||
initStringInfo(&buf);
|
||||
appendStringInfo(&buf, "CREATE OR REPLACE ");
|
||||
if (reltup->relpersistence == RELPERSISTENCE_TEMP) {
|
||||
appendStringInfo(&buf, "TEMPORARY ");
|
||||
if (reltup->relkind == RELKIND_MATVIEW) {
|
||||
appendStringInfo(&buf, "CREATE MATERIALIZED ");
|
||||
} else {
|
||||
appendStringInfo(&buf, "CREATE OR REPLACE ");
|
||||
if (reltup->relpersistence == RELPERSISTENCE_TEMP) {
|
||||
appendStringInfo(&buf, "TEMPORARY ");
|
||||
}
|
||||
}
|
||||
if (ns_name) {
|
||||
appendStringInfo(&buf, "VIEW %s.%s(", ns_name, quote_identifier(NameStr(reltup->relname)));
|
||||
|
@ -238,7 +238,12 @@ extern void CheckRelAutoIncrementIndex(Oid relid, LOCKMODE lockmode);
|
||||
extern void RebuildDependViewForProc(Oid proc_oid);
|
||||
extern void CheckPgRewriteWithDroppedColumn(Oid rel_oid, Oid rw_oid, Form_pg_attribute attForm, int2 old_attnum,
|
||||
char** attName, List **old_query_str);
|
||||
extern void UpdatePgrewriteForView(Oid rw_oid, List* evAction, List **query_str);
|
||||
extern List* GetRefreshedViewQuery(Oid view_oid, Oid rw_oid);
|
||||
extern bool UpdateChangedColumnForView(Oid viewid, Oid relid, int2 attnum, Oid rw_objid,
|
||||
List **originEvAction1, List **newEvAction1, Form_pg_attribute attForm);
|
||||
extern void ReplaceViewQueryFirstAfter(List *query_str);
|
||||
extern char* GetCreateViewCommand(const char *rel_name, HeapTuple tup, Form_pg_class reltup, Oid pg_rewrite_oid, Oid view_oid);
|
||||
#ifdef USE_SPQ
|
||||
extern void spq_btbuild_update_pg_class(Relation heap, Relation index);
|
||||
#endif
|
||||
|
@ -671,6 +671,23 @@ select * from t12;-- ok
|
||||
(3,4)
|
||||
(2 rows)
|
||||
|
||||
-- test6 expression dependency
|
||||
create table expr_dependency_t1(id int);
|
||||
create view expr_dependency_v1 as select id + 1 from expr_dependency_t1;
|
||||
alter table expr_dependency_t1 modify id int8;
|
||||
select * from expr_dependency_v1;
|
||||
?column?
|
||||
----------
|
||||
(0 rows)
|
||||
|
||||
create table expr_dependency_t2(name varchar(10));
|
||||
create view expr_dependency_v2 as select name || 'aa' from expr_dependency_t2;
|
||||
alter table expr_dependency_t2 modify name int;
|
||||
select * from expr_dependency_v2;
|
||||
?column?
|
||||
----------
|
||||
(0 rows)
|
||||
|
||||
--- clean
|
||||
drop schema dependent_view cascade;
|
||||
--?.*
|
||||
@ -687,3 +704,7 @@ drop cascades to view circular_dependency_v3
|
||||
drop cascades to table t11
|
||||
drop cascades to view v11
|
||||
drop cascades to table t12
|
||||
drop cascades to table expr_dependency_t1
|
||||
drop cascades to view expr_dependency_v1
|
||||
drop cascades to table expr_dependency_t2
|
||||
drop cascades to view expr_dependency_v2
|
||||
|
@ -245,6 +245,16 @@ alter table t11 modify b numeric;
|
||||
select * from t12;-- ok
|
||||
select * from v11;-- expect error
|
||||
select * from t12;-- ok
|
||||
-- test6 expression dependency
|
||||
create table expr_dependency_t1(id int);
|
||||
create view expr_dependency_v1 as select id + 1 from expr_dependency_t1;
|
||||
alter table expr_dependency_t1 modify id int8;
|
||||
select * from expr_dependency_v1;
|
||||
|
||||
create table expr_dependency_t2(name varchar(10));
|
||||
create view expr_dependency_v2 as select name || 'aa' from expr_dependency_t2;
|
||||
alter table expr_dependency_t2 modify name int;
|
||||
select * from expr_dependency_v2;
|
||||
|
||||
--- clean
|
||||
drop schema dependent_view cascade;
|
||||
|
Reference in New Issue
Block a user