CREATE TABLE .. LIKE copies comments to an unrelated table

Started by Jim Jones22 days ago7 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254020
psql -h localhost -U postgres

Built from patchset v4 (message #4), October 23, 2026 at 04:27 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t254020_4 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t254020_4 && git checkout t254020_4

Patchset v4 (message #4) is on t254020_4

Jump to latest
#1Jim Jones
jim.jones@uni-muenster.de

Hi

While working on another patch I found that CREATE TABLE ... LIKE
INCLUDING COMMENTS copies the comments to the wrong table when the
target is a temp table and a table with the same name exists in a schema
listed before pg_temp in search_path.

Example:

psql (18.3 (Debian 18.3-1.pgdg13+1))
Type "help" for help.

db=> CREATE TABLE src (a int);
CREATE TABLE
db=> COMMENT ON COLUMN src.a IS 'col comment';
COMMENT
db=> CREATE TABLE foo (a int); -- unrelated table
CREATE TABLE
db=> COMMENT ON COLUMN foo.a IS 'important comment';
COMMENT
db=> SET search_path = public, pg_temp;
SET
db=> CREATE TEMP TABLE foo (LIKE src INCLUDING COMMENTS);
CREATE TABLE
db=> SELECT n.nspname, relname, col_description(c.oid, 1) AS colcomment
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'foo';
nspname | relname | colcomment
------------+---------+-------------
public | foo | col comment
pg_temp_24 | foo |
(2 rows)

The unrelated public.foo got the column comment and the created
temporary table got none -- public.foo.a comment was overwritten. Is
this the expected behaviour?

Setting the schema to pg_temp in case of RELPERSISTENCE_TEMP in
transformCreateStmt seems to do the trick (but I didn't dive too deep in
the code just yet):

-       if (stmt->relation->schemaname == NULL
-               && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
-               stmt->relation->schemaname =
get_namespace_name(namespaceid);
+       if (stmt->relation->schemaname == NULL)
+       {
+               if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP)
+                       stmt->relation->schemaname = pstrdup("pg_temp");
+               else
+                       stmt->relation->schemaname =
get_namespace_name(namespaceid);
+       }

With my changes in HEAD:

db=> CREATE TABLE src (a int);
CREATE TABLE
db=> COMMENT ON COLUMN src.a IS 'col comment';
COMMENT
db=> CREATE TABLE foo (a int); -- unrelated table
CREATE TABLE
db=> COMMENT ON COLUMN foo.a IS 'important comment';
COMMENT
db=> SET search_path = public, pg_temp;
SET
db=> CREATE TEMP TABLE foo (LIKE src INCLUDING COMMENTS);
CREATE TABLE
db=> SELECT n.nspname, relname, col_description(c.oid, 1) AS colcomment
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'foo';
nspname | relname | colcomment
------------+---------+-------------------
public | foo | important comment
pg_temp_92 | foo | col comment
(2 rows)

WDYT?

Best, Jim

#2Junwang Zhao
zhjwpku@gmail.com
In reply to: Jim Jones (#1)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

On Thu, Oct 1, 2026 at 10:35 PM Jim Jones <jim.jones@uni-muenster.de> wrote:

Hi

While working on another patch I found that CREATE TABLE ... LIKE
INCLUDING COMMENTS copies the comments to the wrong table when the
target is a temp table and a table with the same name exists in a schema
listed before pg_temp in search_path.

Example:

psql (18.3 (Debian 18.3-1.pgdg13+1))
Type "help" for help.

db=> CREATE TABLE src (a int);
CREATE TABLE
db=> COMMENT ON COLUMN src.a IS 'col comment';
COMMENT
db=> CREATE TABLE foo (a int); -- unrelated table
CREATE TABLE
db=> COMMENT ON COLUMN foo.a IS 'important comment';
COMMENT
db=> SET search_path = public, pg_temp;
SET
db=> CREATE TEMP TABLE foo (LIKE src INCLUDING COMMENTS);
CREATE TABLE
db=> SELECT n.nspname, relname, col_description(c.oid, 1) AS colcomment
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'foo';
nspname | relname | colcomment
------------+---------+-------------
public | foo | col comment
pg_temp_24 | foo |
(2 rows)

The unrelated public.foo got the column comment and the created
temporary table got none -- public.foo.a comment was overwritten. Is
this the expected behaviour?

I think this is a bug, and I can reproduce it on master.

Setting the schema to pg_temp in case of RELPERSISTENCE_TEMP in
transformCreateStmt seems to do the trick (but I didn't dive too deep in
the code just yet):

-       if (stmt->relation->schemaname == NULL
-               && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
-               stmt->relation->schemaname =
get_namespace_name(namespaceid);
+       if (stmt->relation->schemaname == NULL)
+       {
+               if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP)
+                       stmt->relation->schemaname = pstrdup("pg_temp");
+               else
+                       stmt->relation->schemaname =
get_namespace_name(namespaceid);
+       }

The preceding RangeVarGetAndCheckCreationNamespace has already
initialized the temporary namespace and returned its OID, so simplified it
to:

if (stmt->relation->schemaname == NULL)
stmt->relation->schemaname = get_namespace_name(namespaceid);

should work, I did a quick test to verify this.

With my changes in HEAD:

db=> CREATE TABLE src (a int);
CREATE TABLE
db=> COMMENT ON COLUMN src.a IS 'col comment';
COMMENT
db=> CREATE TABLE foo (a int); -- unrelated table
CREATE TABLE
db=> COMMENT ON COLUMN foo.a IS 'important comment';
COMMENT
db=> SET search_path = public, pg_temp;
SET
db=> CREATE TEMP TABLE foo (LIKE src INCLUDING COMMENTS);
CREATE TABLE
db=> SELECT n.nspname, relname, col_description(c.oid, 1) AS colcomment
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'foo';
nspname | relname | colcomment
------------+---------+-------------------
public | foo | important comment
pg_temp_92 | foo | col comment
(2 rows)

WDYT?

Best, Jim

--
Regards
Junwang Zhao

#3Jim Jones
jim.jones@uni-muenster.de
In reply to: Junwang Zhao (#2)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

Hi Junwang

On 01/10/2026 17:31, Junwang Zhao wrote:

The preceding RangeVarGetAndCheckCreationNamespace has already
initialized the temporary namespace and returned its OID, so simplified it
to:

if (stmt->relation->schemaname == NULL)
stmt->relation->schemaname = get_namespace_name(namespaceid);

should work, I did a quick test to verify this.

+1

Thanks! I will write some tests and prepare a patch.

Best, Jim

#4Jim Jones
jim.jones@uni-muenster.de
In reply to: Jim Jones (#3)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

On 01/10/2026 18:02, Jim Jones wrote:

Thanks! I will write some tests and prepare a patch.

PFA this fix and some tests in create_table_like.sql.

Best, Jim

Attachments:

t254020_4
v1-0001-Fix-copied-comments-landing-on-wrong-table-in-CRE.patchtext/x-patch; charset=UTF-8; name=v1-0001-Fix-copied-comments-landing-on-wrong-table-in-CRE.patchDownload+37-5
#5Koshi Shibagaki (Fujitsu)
shibagaki.koshi@fujitsu.com
In reply to: Jim Jones (#4)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

Hi,

While reviewing v1, I noticed a behavior change when a temporary schema
is renamed concurrently. Would it make sense to qualify temporary table
names with pg_temp rather than the actual schema name?

The following sequence reproduces it with two connections to the same
database:

-- A
CREATE TABLE public.src (a int);
CREATE TEMP TABLE sentinel (a int);
SELECT nspname FROM pg_namespace WHERE oid = pg_my_temp_schema();

-- B
BEGIN;
LOCK TABLE public.src IN ACCESS EXCLUSIVE MODE;

-- A: wait until this statement is blocked before continuing in B.
CREATE TEMP TABLE dst (LIKE public.src);

-- B: replace pg_temp_N with the name returned by A's SELECT.
ALTER SCHEMA pg_temp_N RENAME TO renamed_temp_for_review;
COMMIT;

With v1, A fails with "schema \"pg_temp_N\" does not exist", because the
schema name saved by transformCreateStmt() becomes stale while waiting
for the lock. The same sequence succeeds without the patch.

This requires a superuser, and permanent tables can already encounter
the same error when their destination schema is renamed concurrently.
Therefore, if the intention is to match the name-based behavior of permanent
tables, I think this is reasonable.

Or would it be preferable to use pg_temp for temporary tables, so that
the generated COMMENT commands are schema-qualified while preserving
the previous behavior (i.e., the behavior of current master) in this case?

If we choose pg_temp, the additional change on top of v1 would be:
Permanent tables would still be qualified with the actual schema name.

diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -229,7 +229,9 @@
  * COMMENTS.
  */
  if (stmt->relation->schemaname == NULL)
- stmt->relation->schemaname = get_namespace_name(namespaceid);
+ stmt->relation->schemaname =
+ (stmt->relation->relpersistence == RELPERSISTENCE_TEMP) ?
+ pstrdup("pg_temp") : get_namespace_name(namespaceid);

/* Set up CreateStmtContext */

Regards,

Koshi Shibagaki
FUJITSU LIMITED
https://www.fujitsu.com/

#6Jim Jones
jim.jones@uni-muenster.de
In reply to: Koshi Shibagaki (Fujitsu) (#5)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

Hi Koshi

On 13/10/2026 13:57, Koshi Shibagaki (Fujitsu) wrote:

This requires a superuser, and permanent tables can already encounter
the same error when their destination schema is renamed concurrently.
Therefore, if the intention is to match the name-based behavior of permanent
tables, I think this is reasonable.

I see your point there. However, since this requires a superuser, I
don't think it needs to be fixed -- a superuser can do a lot of nasty
things. Trying to anticipate a superuser renaming a temp schema
mid-session is IMHO not something we should worry about. Please let me
know if I'm missing something here.

Thanks for the review. Much appreciated!

Best, Jim

#7Koshi Shibagaki (Fujitsu)
shibagaki.koshi@fujitsu.com
In reply to: Jim Jones (#6)
Re: CREATE TABLE .. LIKE copies comments to an unrelated table

Hi Jim,

I see your point there. However, since this requires a superuser, I
don't think it needs to be fixed -- a superuser can do a lot of nasty
things. Trying to anticipate a superuser renaming a temp schema
mid-session is IMHO not something we should worry about. Please let me
know if I'm missing something here.

That makes sense. I agree that we don't need to handle
a superuser renaming a temporary schema mid-session.

Thanks for the clarification!

Best,
Koshi Shibagaki
FUJITSU LIMITED
https://www.fujitsu.com/