diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index be75ef21430795ea8bebd6db81c2018e5bffb5d0..42bad2deee62b1e9a4cf67ca933732a9a6c69319 100644 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -8531,9 +8531,10 @@ AddBuiltinFunc(_0(2560), _1("pg_postmaster_start_time"), _2(0), _3(true), _4(false), _5(pg_postmaster_start_time), _6(1184), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_postmaster_start_time"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("postmaster start time"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), AddFuncGroup( - "pg_prepared_statement", 1, - AddBuiltinFunc(_0(2510), _1("pg_prepared_statement"), _2(0), _3(true), _4(true), _5(pg_prepared_statement), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1000), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(5, 25, 25, 1184, 2211, 16), _22(5, 'o', 'o', 'o', 'o', 'o'), _23(5, "name", "statement", "prepare_time", "parameter_types", "from_sql"), _24(NULL), _25("pg_prepared_statement"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("get the prepared statements for this session"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) - ), + "pg_prepared_statement", 2, + AddBuiltinFunc(_0(2510), _1("pg_prepared_statement"), _2(0), _3(true), _4(true), _5(pg_prepared_statement), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1000), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(0), _21(5, 25, 25, 1184, 2211, 16), _22(5, 'o', 'o', 'o', 'o', 'o'), _23(5, "name", "statement", "prepare_time", "parameter_types", "from_sql"), _24(NULL), _25("pg_prepared_statement"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(true), _33("get the prepared statements for this session"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(3702), _1("pg_prepared_statement"), _2(1), _3(true), _4(true), _5(pg_prepared_statement_global), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1000), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('s'), _19(0), _20(1, 20), _21(8, 20, 20, 25, 25, 25, 1184, 2211, 16), _22(8, 'i', 'o', 'o', 'o', 'o', 'o', 'o', 'o'), _23(8,"in_sessionid", "sessionid", "username", "name", "statement", "prepare_time", "parameter_types", "from_sql"), _24(NULL), _25("pg_prepared_statement_global"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(true), _33("get the prepared statements for specified session"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), AddFuncGroup( "pg_prepared_xact", 1, AddBuiltinFunc(_0(1065), _1("pg_prepared_xact"), _2(0), _3(true), _4(true), _5(pg_prepared_xact), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1000), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(0), _21(5, 28, 25, 1184, 26, 26), _22(5, 'o', 'o', 'o', 'o', 'o'), _23(5, "transaction", "gid", "prepared", "ownerid", "dbid"), _24(NULL), _25("pg_prepared_xact"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("view two-phase transactions"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 5d018c1913fe2e73cb02a83abc52ac914a75055d..9c4e10f05646483d19b090754d507040257af674 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -75,7 +75,8 @@ bool will_shutdown = false; * NEXT | 92899 | ? | ? * ********************************************/ -const uint32 GRAND_VERSION_NUM = 92918; +const uint32 GRAND_VERSION_NUM = 92919; + /******************************************** * 2.VERSION NUM FOR EACH FEATURE diff --git a/src/gausskernel/optimizer/commands/prepare.cpp b/src/gausskernel/optimizer/commands/prepare.cpp index 4dd3624296008816c84a20abeb5465c69802fa40..1e4a013cf265e3616d12a4d6b64185acdd279634 100755 --- a/src/gausskernel/optimizer/commands/prepare.cpp +++ b/src/gausskernel/optimizer/commands/prepare.cpp @@ -32,6 +32,7 @@ #include "parser/parse_collate.h" #include "parser/parse_expr.h" #include "parser/parse_type.h" +#include "pgstat.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" #include "tcop/utility.h" @@ -636,8 +637,19 @@ void InitQueryHashTable(void) hash_ctl.keysize = NAMEDATALEN; hash_ctl.entrysize = sizeof(PreparedStatement); hash_ctl.hcxt = u_sess->cache_mem_cxt; - - u_sess->pcache_cxt.prepared_queries = hash_create("Prepared Queries", 32, &hash_ctl, HASH_ELEM | HASH_CONTEXT); + + PG_TRY(); + { + (void)syscalllockAcquire(&u_sess->pcache_cxt.pstmt_htbl_lock); + u_sess->pcache_cxt.prepared_queries = hash_create("Prepared Queries", 32, &hash_ctl, HASH_ELEM | HASH_CONTEXT); + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + } + PG_CATCH(); + { + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + PG_RE_THROW(); + } + PG_END_TRY(); #ifdef PGXC if (IS_PGXC_COORDINATOR) { @@ -651,9 +663,59 @@ void InitQueryHashTable(void) u_sess->pcache_cxt.datanode_queries = hash_create("Datanode Queries", 64, &hash_ctl, HASH_ELEM | HASH_CONTEXT); } #endif + Assert(u_sess->pcache_cxt.prepared_queries); + + if (!ENABLE_THREAD_POOL) { + Assert(t_thrd.shemem_ptr_cxt.MyBEEntry->my_prepared_queries == NULL); + t_thrd.shemem_ptr_cxt.MyBEEntry->my_prepared_queries = u_sess->pcache_cxt.prepared_queries; + t_thrd.shemem_ptr_cxt.MyBEEntry->my_pstmt_htbl_lock = &u_sess->pcache_cxt.pstmt_htbl_lock; + } +} + + +static void InsertIntoQueryHashTable(const char* stmt_name, CachedPlanSource* plansource, bool from_sql, bool* found) +{ + PreparedStatement* entry = NULL; + PG_TRY(); + { + (void)syscalllockAcquire(&u_sess->pcache_cxt.pstmt_htbl_lock); + entry = (PreparedStatement*)hash_search(u_sess->pcache_cxt.prepared_queries, stmt_name, HASH_ENTER, found); + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + } + PG_CATCH(); + { + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + PG_RE_THROW(); + } + PG_END_TRY(); + + if (!(*found)) { + entry->plansource = plansource; + entry->from_sql = from_sql; + entry->prepare_time = GetCurrentStatementStartTimestamp(); + entry->has_prepare_dn_stmt = false; + } + Assert(entry->plansource->magic == CACHEDPLANSOURCE_MAGIC); +} + +static void DropFromQueryHashTable(const char* stmt_name) +{ + PG_TRY(); + { + (void)syscalllockAcquire(&u_sess->pcache_cxt.pstmt_htbl_lock); + hash_search(u_sess->pcache_cxt.prepared_queries, stmt_name, HASH_REMOVE, NULL); + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + } + PG_CATCH(); + { + (void)syscalllockRelease(&u_sess->pcache_cxt.pstmt_htbl_lock); + PG_RE_THROW(); + } + PG_END_TRY(); } #ifdef PGXC + /* * Assign the statement name for all the RemoteQueries in the plan tree, so * they use Datanode statements @@ -803,8 +865,7 @@ void StorePreparedStatementCNGPC(const char *stmt_name, CachedPlanSource *planso InitQueryHashTable(); /* Add entry to hash table */ - PreparedStatement* entry = (PreparedStatement*)hash_search(u_sess->pcache_cxt.prepared_queries, stmt_name, - HASH_ENTER, &found); + InsertIntoQueryHashTable(stmt_name, plansource, from_sql, &found); CN_GPC_LOG("entry preparedstatement", plansource, stmt_name); /* Shouldn't get a duplicate entry */ @@ -817,12 +878,6 @@ void StorePreparedStatementCNGPC(const char *stmt_name, CachedPlanSource *planso ereport(ERROR, (errcode(ERRCODE_DUPLICATE_PSTATEMENT), errmsg("prepared statement \"%s\" already exists", stmt_name))); } - /* Fill in the hash table entry */ - entry->plansource = plansource; - entry->from_sql = from_sql; - entry->prepare_time = cur_ts; - entry->has_prepare_dn_stmt = false; - Assert (entry->plansource->magic == CACHEDPLANSOURCE_MAGIC); /* Now it's safe to move the CachedPlanSource to permanent memory */ if (!is_share) { @@ -859,22 +914,17 @@ void StorePreparedStatement(const char* stmt_name, CachedPlanSource* plansource, bool found = false; /* Initialize the hash table, if necessary */ - if (!u_sess->pcache_cxt.prepared_queries) + if (unlikely(!u_sess->pcache_cxt.prepared_queries)) InitQueryHashTable(); /* Add entry to hash table */ - entry = (PreparedStatement*)hash_search(u_sess->pcache_cxt.prepared_queries, stmt_name, HASH_ENTER, &found); + InsertIntoQueryHashTable(stmt_name, plansource, from_sql, &found); /* Shouldn't get a duplicate entry */ if (found) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_PSTATEMENT), errmsg("prepared statement \"%s\" already exists", stmt_name))); - /* Fill in the hash table entry */ - entry->plansource = plansource; - entry->from_sql = from_sql; - entry->prepare_time = cur_ts; - /* Now it's safe to move the CachedPlanSource to permanent memory */ SaveCachedPlan(plansource); } @@ -1072,7 +1122,7 @@ void DropPreparedStatement(const char* stmt_name, bool showError) } CN_GPC_LOG("remove prepare statment", 0, entry->stmt_name); /* Now we can remove the hash table entry */ - hash_search(u_sess->pcache_cxt.prepared_queries, entry->stmt_name, HASH_REMOVE, NULL); + DropFromQueryHashTable(entry->stmt_name); } if (NULL == originalOwner && t_thrd.utils_cxt.CurrentResourceOwner) { @@ -1175,7 +1225,7 @@ void DropAllPreparedStatements(void) } /* Now we can remove the hash table entry */ - hash_search(u_sess->pcache_cxt.prepared_queries, entry->stmt_name, HASH_REMOVE, NULL); + DropFromQueryHashTable(entry->stmt_name); } ReleaseTempResourceOwner(); CN_GPC_LOG("remove prepare statment all", 0, 0); @@ -1506,6 +1556,138 @@ Datum pg_prepared_statement(PG_FUNCTION_ARGS) return (Datum)0; } +Datum pg_prepared_statement_global(PG_FUNCTION_ARGS) +{ + if (!superuser()) { + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_PROC, "pg_prepared_statements"); + } + + uint64 sessionid = (uint64)PG_GETARG_INT64(0); + ReturnSetInfo *rsinfo = (ReturnSetInfo*)fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate* tupstore = NULL; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " + "allowed in this context"))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* + * build tupdesc for result tuples. This must match the definition of the + * pg_prepared_statements view in system_views.sql + */ + tupdesc = CreateTemplateTupleDesc(7, false); + + TupleDescInitEntry(tupdesc, (AttrNumber)1, "sessionid", INT8OID, -1, 0 ); + TupleDescInitEntry(tupdesc, (AttrNumber)2, "username", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)3, "name", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)4, "statement", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)5, "prepare_time", TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)6, "parameter_types", REGTYPEARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)7, "from_sql", BOOLOID, -1, 0); + + /* + * We put all the tuples into a tuplestore in one scan of the hashtable. + * This avoids any issue of the hashtable possibly changing between calls. + */ + tupstore = + tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, false, u_sess->attr.attr_memory.work_mem); + + /* generate junk in short-term context */ + MemoryContextSwitchTo(oldcontext); + + /* total number of tuples to be returned */ + if (ENABLE_THREAD_POOL) { + g_threadPoolControler->GetSessionCtrl()->GetSessionPreparedStatements(tupstore, tupdesc, sessionid); + } else { + GetThreadPreparedStatements(tupstore, tupdesc, sessionid); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + return (Datum)0; +} + +void GetPreparedStatements(HTAB* htbl, Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionId, char* userName) +{ + HASH_SEQ_STATUS hash_seq; + PreparedStatement *prep_stmt = NULL; + hash_seq_init(&hash_seq, htbl); + while ((prep_stmt = (PreparedStatement*)hash_seq_search(&hash_seq)) != NULL) { + Datum values[7]; + bool nulls[7]; + + errno_t rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + values[0] = UInt64GetDatum(sessionId); + values[1] = CStringGetTextDatum(userName); + values[2] = CStringGetTextDatum(prep_stmt->stmt_name); + char* maskquery = maskPassword(prep_stmt->plansource->query_string); + const char* query = (maskquery == NULL) ? prep_stmt->plansource->query_string : maskquery; + values[3] = CStringGetTextDatum(query); + if (query != maskquery) + pfree_ext(maskquery); + values[4] = TimestampTzGetDatum(prep_stmt->prepare_time); + values[5] = build_regtype_array(prep_stmt->plansource->param_types, prep_stmt->plansource->num_params); + values[6] = BoolGetDatum(prep_stmt->from_sql); + + tuplestore_putvalues(tupStore, tupDesc, values, nulls); + } +} + +void GetThreadPreparedStatements(Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionId) +{ + Assert(!ENABLE_THREAD_POOL); + PgBackendStatus *beentry = t_thrd.shemem_ptr_cxt.BackendStatusArray; + char* userName = NULL; + + PG_TRY(); + { + for(int i = 0; i < BackendStatusArray_size; i++){ + HTAB* htbl = beentry->my_prepared_queries; + + if (beentry->my_pstmt_htbl_lock != NULL) + if ((beentry->st_procpid > 0 || beentry -> st_sessionid > 0) && + (beentry->st_sessionid == sessionId || sessionId == 0)) { + Oid userid = beentry->st_userid; + userName = GetUserNameFromId(userid); + if (htbl) { + (void)syscalllockAcquire(beentry->my_pstmt_htbl_lock); + GetPreparedStatements(htbl, tupStore, tupDesc, beentry->st_sessionid, userName); + (void)syscalllockRelease(beentry->my_pstmt_htbl_lock); + } + } + + pfree_ext(userName); + + beentry++; + } + } + PG_CATCH(); + { + (void)syscalllockRelease(beentry->my_pstmt_htbl_lock); + pfree_ext(userName); + PG_RE_THROW(); + } + PG_END_TRY(); +} + /* * This utility function takes a C array of Oids, and returns a Datum * pointing to a one-dimensional Postgres array of regtypes. An empty diff --git a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp index 3eda87103eca36bfdd9b0b98473443244f7ed751..72be8c68b70dc53168f12a7e90220f8b5f35567f 100755 --- a/src/gausskernel/process/threadpool/threadpool_sessctl.cpp +++ b/src/gausskernel/process/threadpool/threadpool_sessctl.cpp @@ -30,6 +30,7 @@ #include "access/xact.h" #include "catalog/pg_authid.h" #include "catalog/pg_collation.h" +#include "commands/prepare.h" #include "commands/user.h" #include "gssignal/gs_signal.h" #include "lib/dllist.h" @@ -903,3 +904,48 @@ bool ThreadPoolSessControl::IsActiveListEmpty() alock.unLock(); return res; } + +void ThreadPoolSessControl::GetSessionPreparedStatements(Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionId) +{ + AutoMutexLock alock(&m_sessCtrlock); + knl_sess_control* ctrl = NULL; + knl_session_context* session = NULL; + + PG_TRY(); + { + HOLD_INTERRUPTS(); + alock.lock(); + + Dlelem* elem = DLGetHead(&m_activelist); + while (elem != NULL) { + ctrl = (knl_sess_control*)DLE_VAL(elem); + session = ctrl->sess; + + if ((session->session_id == sessionId || sessionId == 0) && + (session->misc_cxt.CurrentUserName != NULL)) { + char userName[NAMEDATALEN]; + errno_t rc = memset_s(userName, NAMEDATALEN, '\0', sizeof(userName)); + securec_check(rc, "\0", "\0"); + rc = strcpy_s(userName, NAMEDATALEN, session->misc_cxt.CurrentUserName); + securec_check(rc, "\0", "\0"); + HTAB* htbl = session->pcache_cxt.prepared_queries; + if (htbl) { + (void)syscalllockAcquire(&session->pcache_cxt.pstmt_htbl_lock); + GetPreparedStatements(htbl, tupStore, tupDesc, session->session_id, userName); + (void)syscalllockRelease(&session->pcache_cxt.pstmt_htbl_lock); + } + } + + elem = DLGetSucc(elem); + } + alock.unLock(); + RESUME_INTERRUPTS(); + } + PG_CATCH(); + { + (void)syscalllockRelease(&session->pcache_cxt.pstmt_htbl_lock); + alock.unLock(); + PG_RE_THROW(); + } + PG_END_TRY(); +} diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_919.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_919.sql new file mode 100644 index 0000000000000000000000000000000000000000..35a6300e8671086d75d220f98f3942bb3868b95f --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_919.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) CASCADE; + diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_919.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_919.sql new file mode 100644 index 0000000000000000000000000000000000000000..35a6300e8671086d75d220f98f3942bb3868b95f --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_919.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) CASCADE; + diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_919.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_919.sql new file mode 100644 index 0000000000000000000000000000000000000000..6daa79c3697924b5a659db4d781beb7a687275a0 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_919.sql @@ -0,0 +1,9 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) CASCADE; + +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 3702; + +CREATE OR REPLACE FUNCTION pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) + RETURNS SETOF record + LANGUAGE internal + STABLE STRICT NOT FENCED NOT SHIPPABLE +AS $function$pg_prepared_statement_global$function$; diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_919.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_919.sql new file mode 100644 index 0000000000000000000000000000000000000000..6daa79c3697924b5a659db4d781beb7a687275a0 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_919.sql @@ -0,0 +1,9 @@ +DROP FUNCTION IF EXISTS pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) CASCADE; + +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 3702; + +CREATE OR REPLACE FUNCTION pg_catalog.pg_prepared_statement(in_sessionid bigint,OUT sessionid bigint, OUT username text,OUT name text, OUT statement text, OUT prepare_time timestamp with time zone, OUT parameter_types regtype[], OUT from_sql boolean) + RETURNS SETOF record + LANGUAGE internal + STABLE STRICT NOT FENCED NOT SHIPPABLE +AS $function$pg_prepared_statement_global$function$; diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h index c58176923f851cb20a0a3d1662b8c51451a30759..f703ef97f70168ad2e44ed0bc4721a9d42756123 100644 --- a/src/include/commands/prepare.h +++ b/src/include/commands/prepare.h @@ -69,6 +69,8 @@ extern void PlanTreeWalker( Plan* plan, void (*walker)(Plan*, void*, const char*), void* context, const char* queryString); extern DatanodeStatement* light_set_datanode_queries(const char* stmt_name); +extern void GetPreparedStatements(HTAB* htbl, Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionId, char* userName); +extern void GetThreadPreparedStatements(Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionId); extern bool quickPlanner(List* querytree_list, Node* parsetree, const char*queryString, CommandDest dest, char* completionTag); #ifdef ENABLE_MOT extern void TryMotJitCodegenQuery(const char* queryString, CachedPlanSource* psrc, Query* query); diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 5c424701b023a7d79606721f515e55748263c611..082b7745ac387ff58e9d3d970d0f797dd3f54c3f 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -984,6 +984,8 @@ typedef struct knl_u_plancache_context { * The keys for this hash table are the arguments to PREPARE and EXECUTE * (statement names); the entries are PreparedStatement structs. */ + pthread_mutex_t pstmt_htbl_lock; + HTAB* prepared_queries; HTAB* stmt_lightproxy_htab; /* mapping statement name and lightproxy obj, only for gpc */ diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 368ea84df3c031606fa0c0d426ea2b9d538ef230..660a2bb2d8b2c59100d36d85383924d54a8992ae 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -1662,6 +1662,9 @@ typedef struct PgBackendStatus { syscalllock statement_cxt_lock; /* mutex for statement context(between session and statement flush thread) */ void* statement_cxt; /* statement context of full sql */ knl_u_trace_context trace_cxt; /* request trace id */ + + HTAB* my_prepared_queries; + pthread_mutex_t* my_pstmt_htbl_lock; } PgBackendStatus; typedef struct PgBackendStatusNode { diff --git a/src/include/threadpool/threadpool_sessctl.h b/src/include/threadpool/threadpool_sessctl.h index 842f0061cc1a5ee391200935733581e016b24fce..79dbc48c3084a66e80371800255e2ab1f7f0f258 100644 --- a/src/include/threadpool/threadpool_sessctl.h +++ b/src/include/threadpool/threadpool_sessctl.h @@ -74,6 +74,7 @@ public: int FindCtrlIdxBySessId(uint64 id); TransactionId ListAllSessionGttFrozenxids(int maxSize, ThreadId *pids, TransactionId *xids, int *n); bool IsActiveListEmpty(); + void GetSessionPreparedStatements(Tuplestorestate* tupStore, TupleDesc tupDesc, uint64 sessionid); void releaseLockIfNecessary(); inline int GetActiveSessionCount() { diff --git a/src/test/regress/expected/function_pg_prepared_statement1.out b/src/test/regress/expected/function_pg_prepared_statement1.out new file mode 100644 index 0000000000000000000000000000000000000000..064dc48a2bc8db8f0b537dd8b8bc06264af7881d --- /dev/null +++ b/src/test/regress/expected/function_pg_prepared_statement1.out @@ -0,0 +1,11 @@ +drop table if exists table_test_prepared_statement1; +NOTICE: table "table_test_prepared_statement1" does not exist, skipping +create table table_test_prepared_statement1 (a int); +prepare stmt1_in_session1 as insert into table_test_prepared_statement1 values (1); +select pg_sleep(2); + pg_sleep +---------- + +(1 row) + +drop table table_test_prepared_statement1; diff --git a/src/test/regress/expected/function_pg_prepared_statement2.out b/src/test/regress/expected/function_pg_prepared_statement2.out new file mode 100644 index 0000000000000000000000000000000000000000..046ba176da677e9eaf025282ccf18c28fd52ae60 --- /dev/null +++ b/src/test/regress/expected/function_pg_prepared_statement2.out @@ -0,0 +1,31 @@ +drop table if exists table_test_prepared_statement2; +NOTICE: table "table_test_prepared_statement2" does not exist, skipping +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +create table table_test_prepared_statement2 (a int); +prepare stmt2_in_session2 as insert into table_test_prepared_statement2 values (2); +select name, statement, parameter_types, from_sql from pg_prepared_statement( + ( + select sessionid + from pg_stat_activity + where application_name='gsql' and query like 'select pg_sleep%' + limit 1 + ) +) order by prepare_time; + name | statement | parameter_types | from_sql +-------------------+-------------------------------------------------------------------------------------+-----------------+---------- + stmt1_in_session1 | prepare stmt1_in_session1 as insert into table_test_prepared_statement1 values (1); | {} | t +(1 row) + +select name, statement, parameter_types, from_sql from pg_prepared_statement(0) order by prepare_time; + name | statement | parameter_types | from_sql +-------------------+-------------------------------------------------------------------------------------+-----------------+---------- + stmt1_in_session1 | prepare stmt1_in_session1 as insert into table_test_prepared_statement1 values (1); | {} | t + stmt2_in_session2 | prepare stmt2_in_session2 as insert into table_test_prepared_statement2 values (2); | {} | t +(2 rows) + +drop table table_test_prepared_statement2; diff --git a/src/test/regress/parallel_schedule0B b/src/test/regress/parallel_schedule0B index 3c20d4ebaa7880ca9b62d442a05f5b10d81cedc2..6ce031e1ae984b5737c2e56b8ba373c591c0b779 100644 --- a/src/test/regress/parallel_schedule0B +++ b/src/test/regress/parallel_schedule0B @@ -75,6 +75,9 @@ test: temp__2 test: vec_prepare_001 vec_prepare_002 test: vec_prepare_003 +#test function pg_prepared_statement(sessionid) +test: function_pg_prepared_statement1 function_pg_prepared_statement2 + #test sort optimize test: sort_optimize_row sort_optimize_column sort_optimize_001 #test early free diff --git a/src/test/regress/sql/function_pg_prepared_statement1.sql b/src/test/regress/sql/function_pg_prepared_statement1.sql new file mode 100644 index 0000000000000000000000000000000000000000..da45d43075a4ea093e256baa26dcff2558fddea5 --- /dev/null +++ b/src/test/regress/sql/function_pg_prepared_statement1.sql @@ -0,0 +1,9 @@ +drop table if exists table_test_prepared_statement1; + +create table table_test_prepared_statement1 (a int); + +prepare stmt1_in_session1 as insert into table_test_prepared_statement1 values (1); + +select pg_sleep(2); + +drop table table_test_prepared_statement1; diff --git a/src/test/regress/sql/function_pg_prepared_statement2.sql b/src/test/regress/sql/function_pg_prepared_statement2.sql new file mode 100644 index 0000000000000000000000000000000000000000..8268e3ec7bb8429068b4d241bf02504bb57793c7 --- /dev/null +++ b/src/test/regress/sql/function_pg_prepared_statement2.sql @@ -0,0 +1,21 @@ +drop table if exists table_test_prepared_statement2; + +select pg_sleep(1); + +create table table_test_prepared_statement2 (a int); + +prepare stmt2_in_session2 as insert into table_test_prepared_statement2 values (2); + +select name, statement, parameter_types, from_sql from pg_prepared_statement( + ( + select sessionid + from pg_stat_activity + where application_name='gsql' and query like 'select pg_sleep%' + limit 1 + ) +) order by prepare_time; + +select name, statement, parameter_types, from_sql from pg_prepared_statement(0) order by prepare_time; + +drop table table_test_prepared_statement2; +