diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 054d7d68ffd635ab84faafe0f42f162abc48d46f..abf1de99420c0fbcba61df2cb6d3afc349d91c68 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -3749,6 +3749,7 @@ InitTempTableNamespace(void) Oid namespaceId; Oid toastspaceId; int session_suffix; + const char *session_infix; /* * First, do permission check to see if we are authorized to make temp @@ -3776,16 +3777,34 @@ InitTempTableNamespace(void) case GP_ROLE_DISPATCH: case GP_ROLE_EXECUTE: session_suffix = gp_session_id; + session_infix = ""; break; case GP_ROLE_UTILITY: session_suffix = MyBackendId; + + /* + * Backend id is used as the suffix of schema name in utility mode + * while session id is used in normal mode. It is possible for a + * utility-mode session's backend id to be equal to a normal-mode + * session's session id at runtime, if we use the same name pattern + * for them then they would conflict with each other and corrupt + * the catalog on the segment. So a different name pattern must be + * used in utility mode. However a temp schema name is expected to + * match the pattern "pg_temp_[0-9]+", so we put a 0 before the + * backend id in utility mode to distinct with normal mode: + * + * - utility mode: pg_temp_0[0-9]+ + * - normal mode: pg_temp_[1-9][0-9]* + */ + session_infix = "0"; break; default: /* Should never hit this */ elog(ERROR, "invalid backend temp schema creation"); session_suffix = -1; /* keep compiler quiet */ + session_infix = NULL; /* keep compiler quiet */ break; } @@ -3810,7 +3829,8 @@ InitTempTableNamespace(void) (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("cannot create temporary tables in parallel mode"))); - snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", session_suffix); + snprintf(namespaceName, sizeof(namespaceName), + "pg_temp_%s%d", session_infix, session_suffix); namespaceId = get_namespace_oid(namespaceName, true); @@ -3857,8 +3877,8 @@ InitTempTableNamespace(void) * (in GPDB, though, we drop and recreate it anyway, to make sure it has * the same OID on master and segments.) */ - snprintf(namespaceName, sizeof(namespaceName), "pg_toast_temp_%d", - session_suffix); + snprintf(namespaceName, sizeof(namespaceName), + "pg_toast_temp_%s%d", session_infix, session_suffix); toastspaceId = get_namespace_oid(namespaceName, true); if (OidIsValid(toastspaceId)) diff --git a/src/test/isolation2/expected/misc.out b/src/test/isolation2/expected/misc.out index 601ebe75ca55a370dd2cb118474e9461542787a4..1b0b71b8d1dd11e2cbf52fac33fc6c4bb985f1a4 100644 --- a/src/test/isolation2/expected/misc.out +++ b/src/test/isolation2/expected/misc.out @@ -23,3 +23,25 @@ DROP Optimizer: Postgres query optimizer Execution time: 0.357 ms (8 rows) + +-- +-- Temp tables should have a different schema name pattern in utility mode. +-- +-- A temp table's schema name used to be pg_temp_ in normal mode +-- and pg_temp_ in utility mode, once the normal-mode session id +-- equals to the utility-mode backend id they will conflict with each other and +-- cause catalog corruption on the segment. +-- +-- We have changed the name to pg_temp_0 in utility mode. +0U: CREATE TEMP TABLE utilitymode_tmp_tab (c1 int) DISTRIBUTED BY (c1); +CREATE +0U: SELECT substring(n.nspname FROM 1 FOR 9) FROM pg_namespace n JOIN pg_class c ON n.oid = c.relnamespace WHERE c.relname = 'utilitymode_tmp_tab'; + substring +----------- + pg_temp_0 +(1 row) +0U: SELECT substring(n2.nspname FROM 1 FOR 15) FROM pg_namespace n1 JOIN pg_class c ON n1.oid = c.relnamespace JOIN pg_namespace n2 ON n2.nspname = 'pg_toast_temp_0' || substring(n1.nspname FROM 10) WHERE c.relname = 'utilitymode_tmp_tab'; + substring +----------------- + pg_toast_temp_0 +(1 row) diff --git a/src/test/isolation2/sql/misc.sql b/src/test/isolation2/sql/misc.sql index 161cb21d321305a84e77b7ed04efdb3c4d59a20b..dd693d3886a8c7928b78bc7d19da5daae485f60f 100644 --- a/src/test/isolation2/sql/misc.sql +++ b/src/test/isolation2/sql/misc.sql @@ -9,3 +9,26 @@ -1U: drop table utilitymode_primary_key_tab; 0U: explain analyze select * from gp_segment_configuration order by dbid; + +-- +-- Temp tables should have a different schema name pattern in utility mode. +-- +-- A temp table's schema name used to be pg_temp_ in normal mode +-- and pg_temp_ in utility mode, once the normal-mode session id +-- equals to the utility-mode backend id they will conflict with each other and +-- cause catalog corruption on the segment. +-- +-- We have changed the name to pg_temp_0 in utility mode. +0U: CREATE TEMP TABLE utilitymode_tmp_tab (c1 int) DISTRIBUTED BY (c1); +0U: SELECT substring(n.nspname FROM 1 FOR 9) + FROM pg_namespace n + JOIN pg_class c + ON n.oid = c.relnamespace + WHERE c.relname = 'utilitymode_tmp_tab'; +0U: SELECT substring(n2.nspname FROM 1 FOR 15) + FROM pg_namespace n1 + JOIN pg_class c + ON n1.oid = c.relnamespace + JOIN pg_namespace n2 + ON n2.nspname = 'pg_toast_temp_0' || substring(n1.nspname FROM 10) + WHERE c.relname = 'utilitymode_tmp_tab';