From ae4bca82a07f3f6e1f4161e1869cc67359e93537 Mon Sep 17 00:00:00 2001 From: totaj Date: Tue, 9 May 2023 20:12:34 +0800 Subject: [PATCH] Rebuild view when replace proc. --- src/common/backend/catalog/pg_proc.cpp | 4 + .../optimizer/commands/tablecmds.cpp | 76 +++++++++++++++++-- src/include/commands/tablecmds.h | 1 + src/test/regress/expected/dependency.out | 46 +++++++++++ src/test/regress/sql/dependency.sql | 33 ++++++++ 5 files changed, 153 insertions(+), 7 deletions(-) diff --git a/src/common/backend/catalog/pg_proc.cpp b/src/common/backend/catalog/pg_proc.cpp index a2a947ad27..5ca671a7a8 100644 --- a/src/common/backend/catalog/pg_proc.cpp +++ b/src/common/backend/catalog/pg_proc.cpp @@ -72,6 +72,7 @@ #include "access/heapam.h" #include "postmaster/postmaster.h" #include "commands/dbcommands.h" +#include "commands/tablecmds.h" #include "storage/lmgr.h" #include "libpq/md5.h" @@ -1646,6 +1647,9 @@ ObjectAddress ProcedureCreate(const char* procedureName, Oid procNamespace, Oid /* send invalid message for for relation holding replaced function as trigger */ InvalidRelcacheForTriggerFunction(retval, ((Form_pg_proc)GETSTRUCT(tup))->prorettype); + /* rebuild view depend on this proc */ + RebuildDependViewForProc(retval); + #ifdef ENABLE_MOT if (proIsProcedure && !package && JitExec::IsMotSPCodegenEnabled()) { /* Notify MOT that current function is about to be modified */ diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index d5babf1e87..46f6505f19 100755 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -573,7 +573,7 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat bool is_modified, bool *hasPartition); static void UpdatePgTriggerFirstAfter(Relation rel, int startattnum, int endattnum, bool is_increase); static void UpdatePgRlspolicyFirstAfter(Relation rel, int startattnum, int endattnum, bool is_increase); -static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_star = false); +static ViewInfoForAdd *GetViewInfoFirstAfter(const char *rel_name, Oid objid, bool keep_star = false); static List *CheckPgRewriteFirstAfter(Relation rel); static void ReplaceViewQueryFirstAfter(List *query_str); static void UpdateDependRefobjsubidFirstAfter(Relation rel, Oid myrelid, int curattnum, int newattnum, @@ -11579,7 +11579,7 @@ static void UpdatePgPartitionFirstAfter(Relation rel, int startattnum, int endat } -static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_star) +static ViewInfoForAdd *GetViewInfoFirstAfter(const char *rel_name, Oid objid, bool keep_star) { ScanKeyData entry; ViewInfoForAdd *info = NULL; @@ -11639,7 +11639,7 @@ static ViewInfoForAdd *GetViewInfoFirstAfter(Relation rel, Oid objid, bool keep_ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Un-supported feature"), errdetail("rule %s depend on %s, alter table %s add ... first|after colname is not supported", - NameStr(rewrite_form->rulename), NameStr(rel->rd_rel->relname), NameStr(rel->rd_rel->relname)))); + NameStr(rewrite_form->rulename), rel_name, rel_name))); } } systable_endscan(rewrite_scan); @@ -11684,7 +11684,7 @@ static List *CheckPgRewriteFirstAfter(Relation rel) pre_objid = dep_form->objid; - ViewInfoForAdd *info = GetViewInfoFirstAfter(rel, dep_form->objid); + ViewInfoForAdd *info = GetViewInfoFirstAfter(NameStr(rel->rd_rel->relname), dep_form->objid); foreach (viewinfo, query_str) { ViewInfoForAdd *oldInfo = (ViewInfoForAdd *)lfirst(viewinfo); @@ -31635,7 +31635,7 @@ static void ATPrepAlterModifyColumn(List** wqueue, AlteredTableInfo* tab, Relati def->raw_default = tmp_expr; } -static char* GetCreateViewCommand(Relation rel, HeapTuple tup, Form_pg_class reltup, Oid pg_rewrite_oid, Oid view_oid) +static 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; @@ -31672,7 +31672,7 @@ static char* GetCreateViewCommand(Relation rel, HeapTuple tup, Form_pg_class rel } pfree_ext(view_options); /* concat CREATE VIEW command with query */ - view_info = GetViewInfoFirstAfter(rel, pg_rewrite_oid, true); + view_info = GetViewInfoFirstAfter(rel_name, pg_rewrite_oid, true); if (view_info == NULL) { pfree_ext(buf.data); return NULL; /* should not happen */ @@ -31706,7 +31706,7 @@ static void ATAlterRecordRebuildView(AlteredTableInfo* tab, Relation rel, Oid pg errdetail("modify or change a column used by materialized view or rule is not supported"))); } /* print CREATE VIEW command */ - view_def = GetCreateViewCommand(rel, tup, reltup, pg_rewrite_oid, view_oid); + view_def = GetCreateViewCommand(NameStr(rel->rd_rel->relname), tup, reltup, pg_rewrite_oid, view_oid); ReleaseSysCache(tup); if (view_def) { /* record it */ @@ -32308,3 +32308,65 @@ static Node* RecookAutoincAttrDefault(Relation rel, int attrno, Oid targettype, -1); return (Node*)aexpr; } + +/* + * findout view which depend on proc, then rebuild it. It will check view's + * column type and name(checkViewTupleDesc) when rebuild the view. + */ +void RebuildDependViewForProc(Oid proc_oid) +{ + ScanKeyData key[2]; + SysScanDesc scan = NULL; + HeapTuple tup = NULL; + List *oid_list = NIL; + + /* open pg_depend to find which view depend on this proc */ + Relation depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ProcedureRelationId)); + ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(proc_oid)); + + scan = systable_beginscan(depRel, DependReferenceIndexId, true, NULL, 2, key); + while (HeapTupleIsValid((tup = systable_getnext(scan)))) { + Form_pg_depend depform = (Form_pg_depend)GETSTRUCT(tup); + + if (depform->classid == RewriteRelationId && depform->deptype == DEPENDENCY_NORMAL) { + oid_list = lappend_oid(oid_list, depform->objid); + } + } + systable_endscan(scan); + heap_close(depRel, AccessShareLock); + + /* rebuild view by rewrite oid */ + ListCell *cell = NULL; + foreach(cell, oid_list) { + Oid objid = lfirst_oid(cell); + Oid view_oid = get_rewrite_relid(objid, true); + if (!OidIsValid(view_oid)) { + continue; + } + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(view_oid)); + if (!HeapTupleIsValid(tup)) { + continue; + } + Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tup); + if (reltup->relkind != RELKIND_VIEW) { + ReleaseSysCache(tup); + continue; + } + + /* get rebuild view sql */ + char *view_def = GetCreateViewCommand(NameStr(reltup->relname), tup, reltup, objid, view_oid); + ReleaseSysCache(tup); + + CommandCounterIncrement(); + List* raw_parsetree_list = raw_parser(view_def); + Node* stmt = (Node*)linitial(raw_parsetree_list); + Assert(IsA(stmt, ViewStmt)); + DefineView((ViewStmt*)stmt, view_def); + pfree(view_def); + list_free(raw_parsetree_list); + } + list_free_ext(oid_list); +} diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index fdaa73c377..14ab73ce23 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -166,4 +166,5 @@ extern int getPartitionElementsIndexByOid(Relation partTableRel, Oid partOid); extern void SetPartionIndexType(IndexStmt* stmt, Relation rel, bool is_alter_table); extern bool ConstraintSatisfyAutoIncrement(HeapTuple tuple, TupleDesc desc, AttrNumber attrnum, char contype); extern void CheckRelAutoIncrementIndex(Oid relid, LOCKMODE lockmode); +extern void RebuildDependViewForProc(Oid proc_oid); #endif /* TABLECMDS_H */ diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out index 780f6ddd6e..635aacd003 100644 --- a/src/test/regress/expected/dependency.out +++ b/src/test/regress/expected/dependency.out @@ -119,3 +119,49 @@ ERROR: role "regression_user2" cannot be dropped because some objects depend on DROP OWNED BY regression_user2, regression_user0; DROP USER regression_user2; DROP USER regression_user0; +-- test view depend on proc +CREATE OR REPLACE procedure depend_p1(var1 varchar,var2 out varchar) +as +p_num varchar:='aaa'; +begin +var2:=var1||p_num; +END; +/ +CREATE OR REPLACE VIEW depend_v1 AS select depend_p1('aa'); +select * from depend_v1; + depend_p1 +----------- + aaaaa +(1 row) + +select definition from pg_views where viewname='depend_v1'; + definition +--------------------------------------------------------- + SELECT depend_p1('aa'::character varying) AS depend_p1; +(1 row) + +-- failed, can't change var2's type +CREATE OR REPLACE procedure depend_p1(var1 int,var2 out int) +as +begin +var2:=var1+1; +END; +/ +ERROR: cannot change data type of view column "depend_p1" from character varying to integer +--success, change var1's type only, but it will failed when select depend_v1 +CREATE OR REPLACE procedure depend_p1(var1 int,var2 out varchar) +as +begin +var2:=var1||'bbb'; +END; +/ +select * from depend_v1; +ERROR: invalid input syntax for integer: "aa" +select definition from pg_views where viewname='depend_v1'; + definition +-------------------------------------------------------------------- + SELECT depend_p1(('aa'::character varying)::integer) AS depend_p1; +(1 row) + +drop view depend_v1; +drop procedure depend_p1; diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql index 2597b53e6d..0b1104a77f 100644 --- a/src/test/regress/sql/dependency.sql +++ b/src/test/regress/sql/dependency.sql @@ -98,3 +98,36 @@ DROP USER regression_user2; DROP OWNED BY regression_user2, regression_user0; DROP USER regression_user2; DROP USER regression_user0; + +-- test view depend on proc +CREATE OR REPLACE procedure depend_p1(var1 varchar,var2 out varchar) +as +p_num varchar:='aaa'; +begin +var2:=var1||p_num; +END; +/ +CREATE OR REPLACE VIEW depend_v1 AS select depend_p1('aa'); +select * from depend_v1; +select definition from pg_views where viewname='depend_v1'; + +-- failed, can't change var2's type +CREATE OR REPLACE procedure depend_p1(var1 int,var2 out int) +as +begin +var2:=var1+1; +END; +/ + +--success, change var1's type only, but it will failed when select depend_v1 +CREATE OR REPLACE procedure depend_p1(var1 int,var2 out varchar) +as +begin +var2:=var1||'bbb'; +END; +/ +select * from depend_v1; +select definition from pg_views where viewname='depend_v1'; + +drop view depend_v1; +drop procedure depend_p1; \ No newline at end of file -- Gitee