diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 123332e80..176e8d370 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -4490,7 +4490,22 @@ static Query* transformDeclareCursorStmt(ParseState* pstate, DeclareCursorStmt* ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot specify both SCROLL and NO SCROLL"))); } - result = transformStmt(pstate, stmt->query); + PG_TRY(); + { + /* according to DeclareCursorName to form a dependency on the used ROW type */ + if (!(stmt->options & CURSOR_OPT_HOLD)) { + u_sess->analyze_cxt.DeclareCursorName = stmt->portalname; + } + result = transformStmt(pstate, stmt->query); + } + PG_CATCH(); + { + u_sess->analyze_cxt.DeclareCursorName = NULL; + PG_RE_THROW(); + } + PG_END_TRY(); + + u_sess->analyze_cxt.DeclareCursorName = NULL; /* Grammar should not have allowed anything but SELECT */ if (!IsA(result, Query) || result->commandType != CMD_SELECT || result->utilityStmt != NULL) { diff --git a/src/common/backend/utils/cache/typcache.cpp b/src/common/backend/utils/cache/typcache.cpp index 380f5199b..48777cd5f 100644 --- a/src/common/backend/utils/cache/typcache.cpp +++ b/src/common/backend/utils/cache/typcache.cpp @@ -121,6 +121,7 @@ static void cache_record_field_properties(TypeCacheEntry* typentry); static void load_enum_cache_data(TypeCacheEntry* tcache); static EnumItem* find_enumitem(TypeCacheEnumData* enum_data, Oid arg); static int enum_oid_cmp(const void* left, const void* right); +static void record_cursor_rowtype(const char* CursorName, TypeCacheEntry* typentry); void init_type_cache() { @@ -393,8 +394,13 @@ TypeCacheEntry* lookup_type_cache(Oid type_id, int flags) /* * If it's a composite type (row type), get tupdesc if requested */ - if ((flags & TYPECACHE_TUPDESC) && typentry->tupDesc == NULL && typentry->typtype == TYPTYPE_COMPOSITE) { - load_typcache_tupdesc(typentry); + if ((flags & TYPECACHE_TUPDESC) && typentry->typtype == TYPTYPE_COMPOSITE) { + if (typentry->tupDesc == NULL) { + load_typcache_tupdesc(typentry); + } + if (u_sess->analyze_cxt.DeclareCursorName) { + record_cursor_rowtype(u_sess->analyze_cxt.DeclareCursorName, typentry); + } } /* @@ -1156,3 +1162,16 @@ static int enum_oid_cmp(const void* left, const void* right) return 0; } } + +static void record_cursor_rowtype(const char* CursorName, TypeCacheEntry* typentry) +{ + MemoryContext old = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt); + Relation rel = relation_open(typentry->typrelid,AccessShareLock); + RelationIncrementReferenceCount(rel); + relation_close(rel,AccessShareLock); + CursorRecordType* var = (CursorRecordType*)palloc(sizeof(CursorRecordType)); + var->cursor_name = pstrdup(CursorName); + var->type_oid = typentry->typrelid; + u_sess->plsql_cxt.CursorRecordTypeList = lappend(u_sess->plsql_cxt.CursorRecordTypeList,var); + (void)MemoryContextSwitchTo(old); +} \ No newline at end of file diff --git a/src/common/backend/utils/mmgr/portalmem.cpp b/src/common/backend/utils/mmgr/portalmem.cpp index 3e7df1ede..53780e8da 100755 --- a/src/common/backend/utils/mmgr/portalmem.cpp +++ b/src/common/backend/utils/mmgr/portalmem.cpp @@ -44,6 +44,7 @@ extern void ReleaseSharedCachedPlan(CachedPlan* plan, bool useResOwner); +static void CursorRecordTypeUnbind(const char* portal_name); /* * Estimate of the maximum number of open portals a user would have, * used in initially sizing the PortalHashTable in EnablePortalManager(). @@ -568,6 +569,11 @@ void PortalDrop(Portal portal, bool isTopCommit) /* drop cached plan reference, if any */ PortalReleaseCachedPlan(portal); + /*if cursor record row type*/ + if (portal->name[0] != '\0' && u_sess->plsql_cxt.CursorRecordTypeList && IsTransactionBlock()) { + CursorRecordTypeUnbind(portal->name); + } + /* * Release any resources still attached to the portal. There are several * cases being covered here: @@ -1396,3 +1402,40 @@ HoldPinnedPortals(bool is_rollback) } } } + +/* Release the dependency between CURSOR and ROW type */ +static void CursorRecordTypeUnbind(const char* portal_name) +{ + ListCell* cell = NULL; + ListCell* pnext = NULL; + MemoryContext old = MemoryContextSwitchTo(u_sess->top_transaction_mem_cxt); + ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; + PG_TRY(); + { + t_thrd.utils_cxt.CurrentResourceOwner = t_thrd.utils_cxt.TopTransactionResourceOwner; + for (cell = list_head(u_sess->plsql_cxt.CursorRecordTypeList); cell != NULL; cell = pnext) { + pnext = lnext(cell); + CursorRecordType* var = (CursorRecordType*)lfirst(cell); + if (strcmp(portal_name,var->cursor_name) == 0) { + Relation rel = relation_open(var->type_oid,AccessShareLock); + if (rel->rd_refcnt > 1) { + RelationDecrementReferenceCount(rel); + } + relation_close(rel,AccessShareLock); + u_sess->plsql_cxt.CursorRecordTypeList = list_delete_ptr(u_sess->plsql_cxt.CursorRecordTypeList,var); + pfree(var->cursor_name); + pfree(var); + } + } + } + PG_CATCH(); + { + t_thrd.utils_cxt.CurrentResourceOwner = save; + (void)MemoryContextSwitchTo(old); + PG_RE_THROW(); + } + PG_END_TRY(); + + t_thrd.utils_cxt.CurrentResourceOwner = save; + (void)MemoryContextSwitchTo(old); +} diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index 487acd36d..1752b26ab 100755 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -89,6 +89,7 @@ static void knl_u_analyze_init(knl_u_analyze_context* anl_cxt) anl_cxt->autoanalyze_process = NULL; anl_cxt->autoanalyze_timeinfo = NULL; anl_cxt->vac_strategy = (BufferAccessStrategyData*)palloc0(sizeof(BufferAccessStrategyData)); + anl_cxt->DeclareCursorName = NULL; } static void knl_u_attr_init(knl_session_attr* attr) @@ -869,6 +870,7 @@ static void knl_u_plpgsql_init(knl_u_plpgsql_context* plsql_cxt) plsql_cxt->cur_exception_cxt = NULL; plsql_cxt->pragma_autonomous = false; plsql_cxt->is_insert_gs_source = false; + plsql_cxt->CursorRecordTypeList = NIL; } static void knl_u_stat_init(knl_u_stat_context* stat_cxt) diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 7faf02a5e..9217f7a3a 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -304,6 +304,8 @@ typedef struct knl_u_analyze_context { struct StringInfoData* autoanalyze_timeinfo; struct BufferAccessStrategyData* vac_strategy; + + char* DeclareCursorName; } knl_u_analyze_context; #define PATH_SEED_FACTOR_LEN 3 @@ -1667,6 +1669,7 @@ typedef struct knl_u_plpgsql_context { bool pragma_autonomous; /* save autonomous flag */ char* debug_query_string; bool is_insert_gs_source; /* is doing insert gs_source? */ + List* CursorRecordTypeList; /*Save the type recorded during the cursor definition*/ } knl_u_plpgsql_context; //this is used to define functions in package diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index 1768581ef..22f4e7a99 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -1942,6 +1942,12 @@ typedef struct ExceptionContext { PLpgSQL_declare_handler handler_type; } ExceptionContext; +/*Save the type recorded during the cursor definition*/ +typedef struct CursorRecordType { + char* cursor_name; + Oid type_oid; +} CursorRecordType; + /* Quick access array state */ #define IS_ARRAY_STATE(state_list, state) ((state_list && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) ? \ (linitial_int(state_list) == state) : false) diff --git a/src/test/regress/expected/plpgsql_cursor_rowtype.out b/src/test/regress/expected/plpgsql_cursor_rowtype.out index 31a2e3422..8b7665a1f 100644 --- a/src/test/regress/expected/plpgsql_cursor_rowtype.out +++ b/src/test/regress/expected/plpgsql_cursor_rowtype.out @@ -845,6 +845,120 @@ INFO: aa set behavior_compat_options=''; drop procedure check_compile; +--游标依赖row type,后续alter type +create type foo as (a int, b int); +--游标依赖type,alter type报错 +begin; +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; + row +---------------- + (1,1073741824) +(1 row) + +fetch c; + row +---------------- + (2,1073741824) +(1 row) + +alter type foo alter attribute b type text;--error +ERROR: cannot ALTER TABLE "foo" because it is being used by active queries in this session +end; +--第二次开始从缓存中获取type +begin; +cursor c for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; + row +---------------- + (1,1073741824) +(1 row) + +fetch c; + row +---------------- + (2,1073741824) +(1 row) + +alter type foo alter attribute b type text;--error +ERROR: cannot ALTER TABLE "foo" because it is being used by active queries in this session +end; +--close后,可以成功alter +begin; +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; + row +---------------- + (1,1073741824) +(1 row) + +fetch c; + row +---------------- + (2,1073741824) +(1 row) + +close c; +alter type foo alter attribute b type text;--success +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; + row +---------------- + (1,1073741824) +(1 row) + +fetch c; + row +---------------- + (2,1073741824) +(1 row) + +rollback; +begin; +cursor c for select (i,2^30)::foo from generate_series(1,10) i; +close c; +alter type foo alter attribute b type text;--success +end; +--多个游标依赖,只关闭一个 +begin; +cursor c1 for select (i,2^30)::foo from generate_series(1,10) i; +cursor c2 for select (i,2^30)::foo from generate_series(1,10) i; +close c1; +alter type foo alter attribute b type text;--error +ERROR: cannot ALTER TABLE "foo" because it is being used by active queries in this session +end; +--多个游标依赖,都关闭 +begin; +cursor c1 for select (i,2^30)::foo from generate_series(1,10) i; +cursor c2 for select (i,2^30)::foo from generate_series(1,10) i; +close c1; +close c2; +alter type foo alter attribute b type text;--success +end; +--WITH HOLD游标,事务结束继续保留 +begin; +cursor c3 WITH HOLD for select (i,2^30)::foo from generate_series(1,10) i; +fetch c3; + row +---------------- + (1,1073741824) +(1 row) + +end; +fetch c3; + row +---------------- + (2,1073741824) +(1 row) + +alter type foo alter attribute b type text;--success +fetch c3; + row +---------------- + (3,1073741824) +(1 row) + +close c3; ---- clean ---- drop package pck1; NOTICE: drop cascades to function plpgsql_cursor_rowtype.p1() @@ -874,7 +988,7 @@ NOTICE: drop cascades to function plpgsql_cursor_rowtype.p1() drop package pckg_test2; NOTICE: drop cascades to function plpgsql_cursor_rowtype.p1() drop schema plpgsql_cursor_rowtype cascade; -NOTICE: drop cascades to 13 other objects +NOTICE: drop cascades to 14 other objects DETAIL: drop cascades to table emp drop cascades to function pro_cursor_no_args_1() drop cascades to function pro_cursor_no_args_2() @@ -888,5 +1002,6 @@ drop cascades to function test_forloop_001() drop cascades to function pro_close_cursor1() drop cascades to function pro_close_cursor2() drop cascades to function check_compile_1() +drop cascades to type foo drop schema schema1 cascade; NOTICE: drop cascades to table schema1.t11 diff --git a/src/test/regress/sql/plpgsql_cursor_rowtype.sql b/src/test/regress/sql/plpgsql_cursor_rowtype.sql index 9b82a565f..7698c70e6 100644 --- a/src/test/regress/sql/plpgsql_cursor_rowtype.sql +++ b/src/test/regress/sql/plpgsql_cursor_rowtype.sql @@ -694,6 +694,70 @@ set behavior_compat_options=''; drop procedure check_compile; +--游标依赖row type,后续alter type +create type foo as (a int, b int); + +--游标依赖type,alter type报错 +begin; +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; +fetch c; +alter type foo alter attribute b type text;--error +end; + +--第二次开始从缓存中获取type +begin; +cursor c for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; +fetch c; +alter type foo alter attribute b type text;--error +end; + +--close后,可以成功alter +begin; +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; +fetch c; +close c; +alter type foo alter attribute b type text;--success +declare c cursor for select (i,2^30)::foo from generate_series(1,10) i; +fetch c; +fetch c; +rollback; + +begin; +cursor c for select (i,2^30)::foo from generate_series(1,10) i; +close c; +alter type foo alter attribute b type text;--success +end; + +--多个游标依赖,只关闭一个 +begin; +cursor c1 for select (i,2^30)::foo from generate_series(1,10) i; +cursor c2 for select (i,2^30)::foo from generate_series(1,10) i; +close c1; +alter type foo alter attribute b type text;--error +end; + +--多个游标依赖,都关闭 +begin; +cursor c1 for select (i,2^30)::foo from generate_series(1,10) i; +cursor c2 for select (i,2^30)::foo from generate_series(1,10) i; +close c1; +close c2; +alter type foo alter attribute b type text;--success +end; + +--WITH HOLD游标,事务结束继续保留 +begin; +cursor c3 WITH HOLD for select (i,2^30)::foo from generate_series(1,10) i; +fetch c3; +end; +fetch c3; +alter type foo alter attribute b type text;--success +fetch c3; +close c3; + ---- clean ---- drop package pck1; drop package pck2;