解决缺陷:修改存在plsql依赖的类型的schema导致重建报错

This commit is contained in:
lukeman
2023-11-23 12:00:24 +08:00
parent e77505d35a
commit c945aff921
11 changed files with 342 additions and 38 deletions

View File

@ -321,8 +321,7 @@ static void out_drop_stmt(ArchiveHandle* AH, const TocEntry* te)
char* dropStmt = NULL;
char* dropStmtOrig = NULL;
errno_t rc = EOK;
if (*te->dropStmt != '\0')
{
if (*te->dropStmt != '\0') {
dropStmt = try_remove_nsname_in_drop_stmt(ropt, te->dropStmt, strlen(te->dropStmt) + PATCH_LEN);
dropStmtOrig = dropStmt;
if (strstr(te->dropStmt, "IF EXISTS") != NULL ||
@ -386,8 +385,8 @@ static void out_drop_stmt(ArchiveHandle* AH, const TocEntry* te)
}
ahprintf(AH, "%s", ftStmt->data);
destroyPQExpBuffer(ftStmt);
free(dropStmtOrig);
}
free(dropStmtOrig);
}
}
/* Public */

View File

@ -72,6 +72,65 @@ static inline Oid gsplsql_parse_pkg_var_and_attrs4(GsDependObjDesc* obj, List* v
static inline Oid gsplsql_parse_pkg_var_and_attrs3(GsDependObjDesc* obj, ListCell* var_name, ListCell** attr_list);
static inline Oid gsplsql_parse_pkg_var_obj2(GsDependObjDesc* obj, ListCell* var_name);
static bool gsplsql_exist_func_object_in_dependencies_obj(char* nsp_name, char* pkg_name,
const char* old_func_head_name, const char* old_func_name);
static bool gsplsql_exist_func_object_in_dependencies(char* nsp_name, char* pkg_name, const char* old_func_head_name);
bool gsplsql_exists_func_obj(Oid nsp_oid, Oid pkg_oid, const char* old_func_head_name, const char* old_func_name)
{
char* nsp_name = get_namespace_name(nsp_oid);
char* pkg_name = pstrdup("null");
if (OidIsValid(pkg_oid)) {
pfree_ext(pkg_name);
pkg_name = GetPackageName(pkg_oid);
}
bool exists_func = gsplsql_exist_func_object_in_dependencies_obj(nsp_name, pkg_name, old_func_head_name, old_func_name) ||
gsplsql_exist_func_object_in_dependencies(nsp_name, pkg_name, old_func_head_name);
pfree_ext(pkg_name);
pfree_ext(nsp_name);
return exists_func;
}
bool gsplsql_exists_schema_name(const char* schema_name)
{
bool has_schema = false;
Assert(schema_name != NULL);
// check gs_dependencies_obj
int key_num = 0;
ScanKeyData key[1];
ScanKeyInit(&key[key_num++], Anum_gs_dependencies_obj_schemaname, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(schema_name));
bool is_null = false;
HeapTuple tuple;
Relation obj_rel = heap_open(DependenciesObjRelationId, AccessShareLock);
SysScanDesc scan = systable_beginscan(obj_rel, DependenciesObjNameIndexId, true, SnapshotSelf, key_num, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
has_schema = true;
break;
}
systable_endscan(scan);
heap_close(obj_rel, AccessShareLock);
if (has_schema) {
return has_schema;
}
// check gs_dependencies
int key_num_dep = 0;
ScanKeyData key_dep[1];
ScanKeyInit(&key_dep[key_num_dep++], Anum_gs_dependencies_schemaname, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(schema_name));
HeapTuple tuple_dep;
Relation dep_rel = heap_open(DependenciesRelationId, AccessShareLock);
SysScanDesc scan_dep = systable_beginscan(dep_rel, DependenciesNameIndexId, true, SnapshotSelf,
key_num_dep, key_dep);
while (HeapTupleIsValid(tuple_dep = systable_getnext(scan_dep))) {
has_schema = true;
break;
}
systable_endscan(scan_dep);
heap_close(dep_rel, AccessShareLock);
return has_schema;
}
void gsplsql_init_gs_depend_obj_desc(GsDependObjDesc* object)
{
object->schemaName = NULL;
@ -1483,3 +1542,78 @@ static DependenciesDatum *gsplsql_make_var_depend_datum(const PLpgSQL_datum *dat
}
return (DependenciesDatum*)var_node;
}
static bool gsplsql_exist_func_object_in_dependencies_obj(char* nsp_name, char* pkg_name,
const char* old_func_head_name, const char* old_func_name)
{
int keyNum = 0;
Assert(nsp_name != NULL && pkg_name != NULL);
ScanKeyData key[2];
ScanKeyInit(&key[keyNum++], Anum_gs_dependencies_obj_schemaname, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(nsp_name));
ScanKeyInit(&key[keyNum++], Anum_gs_dependencies_obj_packagename, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(pkg_name));
bool is_null = false;
HeapTuple tuple;
Relation obj_rel = heap_open(DependenciesObjRelationId, AccessShareLock);
SysScanDesc scan = systable_beginscan(obj_rel, DependenciesObjNameIndexId, true, SnapshotSelf, keyNum, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
Datum name_datum = heap_getattr(tuple, Anum_gs_dependencies_obj_name,
RelationGetDescr(obj_rel), &is_null);
Assert(name_datum != 0);
char* obj_name = TextDatumGetCString(name_datum);
if (strcmp(old_func_head_name, obj_name) == 0 || strcmp(old_func_name, obj_name) == 0) {
Datum type_datum = heap_getattr(tuple, Anum_gs_dependencies_obj_type,
RelationGetDescr(obj_rel), &is_null);
int type = DatumGetInt32(type_datum);
if (type == GSDEPEND_OBJECT_TYPE_FUNCTION ||
type == GSDEPEND_OBJECT_TYPE_PROCHEAD || type == GSDEPEND_OBJECT_TYPE_UNDEFIND) {
pfree_ext(obj_name);
systable_endscan(scan);
heap_close(obj_rel, AccessShareLock);
return true;
}
}
pfree_ext(obj_name);
}
systable_endscan(scan);
heap_close(obj_rel, AccessShareLock);
return false;
}
static bool gsplsql_exist_func_object_in_dependencies(char* nsp_name, char* pkg_name, const char* old_func_head_name)
{
int keyNum = 0;
Assert(nsp_name != NULL && pkg_name != NULL);
ScanKeyData key[2];
ScanKeyInit(&key[keyNum++], Anum_gs_dependencies_schemaname, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(nsp_name));
ScanKeyInit(&key[keyNum++], Anum_gs_dependencies_packagename, BTEqualStrategyNumber,
F_NAMEEQ, NameGetDatum(pkg_name));
bool is_null = false;
HeapTuple tuple;
Relation dep_rel = heap_open(DependenciesRelationId, AccessShareLock);
SysScanDesc scan = systable_beginscan(dep_rel, DependenciesNameIndexId, true, SnapshotSelf, keyNum, key);
while (HeapTupleIsValid(tuple = systable_getnext(scan))) {
Datum name_datum = heap_getattr(tuple, Anum_gs_dependencies_objectname,
RelationGetDescr(dep_rel), &is_null);
Assert(name_datum != 0);
char* obj_name = TextDatumGetCString(name_datum);
if (strcmp(old_func_head_name, obj_name) == 0) {
Datum refobj_pos_datum = heap_getattr(tuple, Anum_gs_dependencies_refobjpos,
RelationGetDescr(dep_rel), &is_null);
int refobj_pos = DatumGetInt32(refobj_pos_datum);
if (refobj_pos == GSDEPEND_REFOBJ_POS_IN_PROCHEAD ||
refobj_pos == GSDEPEND_REFOBJ_POS_IN_PROCBODY) {
pfree_ext(obj_name);
systable_endscan(scan);
heap_close(dep_rel, AccessShareLock);
return true;
}
}
pfree_ext(obj_name);
}
systable_endscan(scan);
heap_close(dep_rel, AccessShareLock);
return false;
}

View File

@ -19,29 +19,6 @@
* IDENTIFICATION
* src/common/backend/utils/gsplsql/gsobject_gsdependencies.cpp
*
* ---------------------------------------------------------------------------------------
*/
/*
* Copyright (c) 2021 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* gsobject_gsdependencies.cpp
*
*
* IDENTIFICATION
* src/common/backend/utils/gsplsql/gsobject_gsdependencies.cpp
*
* ---------------------------------------------------------------------------------------
*/
@ -1086,10 +1063,6 @@ static bool gsplsql_update_proc_header(CreateFunctionStmt *stmt, Oid funcid)
prorettype = VOIDOID;
returnsSet = false;
}
if (has_undefined) {
ereport(
LOG, (errmsg("the function header has an unknown dependency. function oid is %u", funcid)));
}
char* parameter_defaults_str = NULL;
if (parameter_defaults != NIL) {
parameter_defaults_str = nodeToString(parameter_defaults);
@ -1139,8 +1112,6 @@ static bool gsplsql_update_proc_header(CreateFunctionStmt *stmt, Oid funcid)
CacheInvalidateFunction(funcid, InvalidOid);
if (!is_proc_info_changed(funcid, parameter_types, all_parameter_types, parameter_defaults_str,
prorettype, returnsSet)) {
ereport(LOG, (errcode(ERRCODE_PLPGSQL_ERROR),
errmsg("The header of function %u does not need to be modified.", funcid)));
pfree_ext(parameter_defaults_str);
return has_undefined;
}
@ -1252,7 +1223,7 @@ static bool is_proc_info_changed(Oid funcid, oidvector *new_parameter_types, Arr
}
size_tmp = allpara_count * sizeof(Oid);
Oid *p_argtypes = (Oid *)palloc(size_tmp);
rc = memcpy_s(p_argtypes, size_tmp, ARR_DATA_PTR(new_all_parameter_types), size_tmp);
rc = memcpy_s(p_argtypes, size_tmp, ARR_DATA_PTR(arr), size_tmp);
securec_check(rc, "\0", "\0");
oidvector *allpara_type = buildoidvector(p_argtypes, allpara_count);
if (!DatumGetBool(DirectFunctionCall2(oidvectoreq, PointerGetDatum(allpara_type),
@ -1485,8 +1456,6 @@ static void gsplsql_delete_dependencies_object_by_oid(Oid oid)
if (!HeapTupleIsValid(tuple)) {
systable_endscan(scan);
heap_close(dep_rel, RowExclusiveLock);
ereport(LOG, (errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("dependencies object %u does not exist.", oid)));
return;
}
simple_heap_delete(dep_rel, &tuple->t_self, 0, true);

View File

@ -66,6 +66,7 @@
#include "utils/rel_gs.h"
#include "utils/syscache.h"
#include "gs_policy/gs_policy_masking.h"
#include "catalog/gs_dependencies_fn.h"
/*
* Executes an ALTER OBJECT / RENAME TO statement. Based on the object
@ -736,6 +737,21 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
&proc->proargtypes, nspOid);
if (enable_plpgsql_gsdependency_guc()) {
const char* old_func_format = format_procedure_no_visible(objid);
const char* old_func_name = NameStr(proc->proname);
bool is_null = false;
Datum package_oid_datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_packageid, &is_null);
Oid pkg_oid = DatumGetObjectId(package_oid_datum);
if (gsplsql_exists_func_obj(oldNspOid, pkg_oid, old_func_format, old_func_name)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The set schema operator of %s is not allowed, "
"because it is referenced by the other object.",
NameStr(proc->proname))));
}
}
}
else if (classId == CollationRelationId) {
Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);

View File

@ -1977,6 +1977,20 @@ ObjectAddress RenameFunction(List* name, List* argtypes, const char* newname)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid));
if (enable_plpgsql_gsdependency_guc()) {
const char* old_func_format = format_procedure_no_visible(procOid);
const char* old_func_name = strVal(llast(name));
bool is_null = false;
Datum package_oid_datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_packageid, &is_null);
Oid pkg_oid = DatumGetObjectId(package_oid_datum);
if (gsplsql_exists_func_obj(namespaceOid, pkg_oid, old_func_format, old_func_name)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The rename operator of %s is not allowed, because it is referenced by the other object.",
NameStr(procForm->proname))));
}
}
/* rename */
(void)namestrcpy(&(procForm->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@ -3232,6 +3246,20 @@ Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
}
}
#endif
if (enable_plpgsql_gsdependency_guc()) {
const char* old_func_format = format_procedure_no_visible(procOid);
const char* old_func_name = NameStr(proc->proname);
bool is_null = false;
Datum package_oid_datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_packageid, &is_null);
Oid pkg_oid = DatumGetObjectId(package_oid_datum);
if (gsplsql_exists_func_obj(oldNspOid, pkg_oid, old_func_format, old_func_name)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The set schema operator of %s is not allowed, "
"because it is referenced by the other object.",
NameStr(proc->proname))));
}
}
/* OK, modify the pg_proc row */
/* tup is a copy, so we can scribble directly on it */
proc->pronamespace = nspOid;

View File

@ -41,6 +41,7 @@
#include "utils/snapmgr.h"
#include "gs_ledger/ledger_utils.h"
#include "gs_ledger/userchain.h"
#include "catalog/gs_dependencies_fn.h"
#ifdef PGXC
#include "pgxc/pgxc.h"
@ -589,6 +590,13 @@ ObjectAddress RenameSchema(const char* oldname, const char* newname)
existTimeSeriesTbl->data)));
}
if (enable_plpgsql_gsdependency_guc() && gsplsql_exists_schema_name(oldname)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The rename operator of %s is not allowed, because it is referenced by the other object.",
oldname)));
}
/* Before rename schema (with blockchain) rename related ledger tables first */
bool is_null = true;
Datum datum = SysCacheGetAttr(NAMESPACENAME, tup, Anum_pg_namespace_nspblockchain, &is_null);

View File

@ -21639,6 +21639,14 @@ void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, Object
Assert(objsMoved != NULL);
if (enable_plpgsql_gsdependency_guc() &&
gsplsql_is_object_depend(rel->rd_rel->reltype, GSDEPEND_OBJECT_TYPE_TYPE)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The set schema operator of %s is not allowed, "
"because it is referenced by another object.", NameStr(rel->rd_rel->relname))));
}
/* OK, modify the pg_class row and pg_depend entry */
classRel = heap_open(RelationRelationId, RowExclusiveLock);

View File

@ -3688,6 +3688,13 @@ Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses* objsMoved)
errmsg("cannot alter array type %s", format_type_be(typeOid)),
errhint("You can alter type %s, which will alter the array type as well.", format_type_be(elemOid))));
if (enable_plpgsql_gsdependency_guc() &&
gsplsql_is_object_depend(typeOid, GSDEPEND_OBJECT_TYPE_TYPE)) {
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("The set schema operator of %s is not allowed, because it is referenced by the other object.",
get_typename(typeOid))));
}
/* and do the work */
return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, objsMoved);
}

View File

@ -143,4 +143,7 @@ extern void gsplsql_delete_unrefer_depend_obj_oid(Oid ref_obj_oid, bool skip_in_
extern bool gsplsql_check_type_depend_ast_equal(Relation obj_rel, Oid obj_oid, const char* equal_ast);
extern bool gsplsql_search_depend_obj_by_oid(Oid oid, GsDependObjDesc* obj_desc);
extern void gsplsql_remove_type_gs_dependency(const GsDependObjDesc* obj_desc);
extern bool gsplsql_exists_func_obj(Oid nsp_oid, Oid pkg_oid, const char* old_func_head_name,
const char* old_func_name);
extern bool gsplsql_exists_schema_name(const char* schema_name);
#endif /* GS_DEPENDENCIES_FN_H */

View File

@ -387,13 +387,91 @@ INFO: call xxx: (1,aaa,aaaaa,,,), yyy: (2,bbb,bbbbb,,,), p_in: (1,zhang,M,1,2,3
(1 row)
-- test 6
drop type if exists r1;
drop table if exists stu;
create table stu(sno int, name varchar, sex varchar, cno int);
create type r1 as (a int, c stu%RowType);
create schema plpgsql_recompile_new;
create or replace procedure test_proc(p_in r1)
is
declare
v1 stu%RowType;
begin
RAISE INFO 'call p_in: %', p_in;
end;
/
call test_proc((1,(1,'zhang','M',1)));
INFO: call p_in: (1,"(1,zhang,M,1)")
test_proc
-----------
(1 row)
-- rename report error
ALTER TYPE r1 RENAME to r1_rename;
ERROR: The rename operator of r1 is not allowed, because it is referenced by the other object.
ALTER TYPE r1 RENAME ATTRIBUTE a TO new_a;
ERROR: cannot rename attribute of the type because it is dependent on another object.
ALTER TABLE stu RENAME TO stu_rename;
ERROR: The rename operator on stu is not allowed, because it is dependent on another object.
ALTER TABLE stu RENAME COLUMN cno TO new_cno;
ERROR: cannot rename attribute of the type because it is dependent on another object.
ALTER PROCEDURE test_proc(r1) RENAME TO test_proc_rename;
ERROR: The rename operator of test_proc is not allowed, because it is referenced by the other object.
ALTER SCHEMA plpgsql_recompile RENAME TO plpgsql_recompile_rename;
ERROR: The rename operator of plpgsql_recompile is not allowed, because it is referenced by the other object.
call test_proc((1.0,(1,'zhang','M',1)));
INFO: call p_in: (1,"(1,zhang,M,1)")
test_proc
-----------
(1 row)
-- set schema report error
ALTER TYPE r1 SET SCHEMA plpgsql_recompile_new;
ERROR: The set schema operator of r1 is not allowed, because it is referenced by the other object.
ALTER TABLE stu SET SCHEMA plpgsql_recompile_new;
ERROR: The set schema operator of stu is not allowed, because it is referenced by another object.
ALTER PROCEDURE test_proc(r1) SET SCHEMA plpgsql_recompile_new;
ERROR: The set schema operator of test_proc is not allowed, because it is referenced by the other object.
call test_proc((1.0,(1,'zhang','M',1)));
INFO: call p_in: (1,"(1,zhang,M,1)")
test_proc
-----------
(1 row)
-- drop and recreate
drop type r1;
drop table stu;
drop procedure test_proc;
create table stu(sno int, name varchar, sex varchar, cno int);
create type r1 as (a int, c stu%RowType);
create or replace procedure test_proc(p_in r1)
is
declare
v1 stu%RowType;
begin
RAISE INFO 'call p_in: %', p_in;
end;
/
call test_proc((1,(1,'zhang','M',1)));
INFO: call p_in: (1,"(1,zhang,M,1)")
test_proc
-----------
(1 row)
drop type r1;
drop table stu;
drop procedure test_proc;
-- clean
drop schema plpgsql_recompile_new cascade;
drop schema plpgsql_recompile cascade;
NOTICE: drop cascades to 7 other objects
--?.*
DETAIL: drop cascades to type s_type
drop cascades to function type_alter(s_type)
drop cascades to table stu
drop cascades to type r1
drop cascades to function test_subtype_proc()
--?.*
drop cascades to function plpgsql_recompile.proc1(s_type)

View File

@ -203,6 +203,60 @@ call pkg.proc1((1,'zhang','M',1,2,3));
alter package pkg compile;
call pkg.proc1((1,'zhang','M',1,2,3));
-- test 6
drop type if exists r1;
drop table if exists stu;
create table stu(sno int, name varchar, sex varchar, cno int);
create type r1 as (a int, c stu%RowType);
create schema plpgsql_recompile_new;
create or replace procedure test_proc(p_in r1)
is
declare
v1 stu%RowType;
begin
RAISE INFO 'call p_in: %', p_in;
end;
/
call test_proc((1,(1,'zhang','M',1)));
-- rename report error
ALTER TYPE r1 RENAME to r1_rename;
ALTER TYPE r1 RENAME ATTRIBUTE a TO new_a;
ALTER TABLE stu RENAME TO stu_rename;
ALTER TABLE stu RENAME COLUMN cno TO new_cno;
ALTER PROCEDURE test_proc(r1) RENAME TO test_proc_rename;
ALTER SCHEMA plpgsql_recompile RENAME TO plpgsql_recompile_rename;
call test_proc((1.0,(1,'zhang','M',1)));
-- set schema report error
ALTER TYPE r1 SET SCHEMA plpgsql_recompile_new;
ALTER TABLE stu SET SCHEMA plpgsql_recompile_new;
ALTER PROCEDURE test_proc(r1) SET SCHEMA plpgsql_recompile_new;
call test_proc((1.0,(1,'zhang','M',1)));
-- drop and recreate
drop type r1;
drop table stu;
drop procedure test_proc;
create table stu(sno int, name varchar, sex varchar, cno int);
create type r1 as (a int, c stu%RowType);
create or replace procedure test_proc(p_in r1)
is
declare
v1 stu%RowType;
begin
RAISE INFO 'call p_in: %', p_in;
end;
/
call test_proc((1,(1,'zhang','M',1)));
drop type r1;
drop table stu;
drop procedure test_proc;
-- clean
drop schema plpgsql_recompile_new cascade;
drop schema plpgsql_recompile cascade;
reset behavior_compat_options;