| From: | Japin Li <japinli(at)hotmail(dot)com> |
|---|---|
| To: | Mario González Troncoso <gonzalemario(at)gmail(dot)com> |
| Cc: | li carol <carol(dot)li2025(at)outlook(dot)com>, Bryan Green <dbryan(dot)green(at)gmail(dot)com>, Quan Zongliang <quanzongliang(at)yeah(dot)net>, "pgsql-hackers(at)lists(dot)postgresql(dot)org" <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Subject: | Re: [PATCH] Add pg_get_role_ddl() functions for role recreation |
| Date: | 2026-01-06 06:27:30 |
| Message-ID: | MEAPR01MB3031E24EABC43485FFFA1DEDB687A@MEAPR01MB3031.ausprd01.prod.outlook.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi, Mario González Troncoso
On Mon, 05 Jan 2026 at 07:29, Mario González Troncoso <gonzalemario(at)gmail(dot)com> wrote:
> On Mon, 5 Jan 2026 at 07:17, Mario González Troncoso
> <gonzalemario(at)gmail(dot)com> wrote:
>>
>> > I reviewed your patch and found one potential issue, please check it.
>> > In pg_get_role_ddl_internal, the variable rolname is assigned from
>> > NameStr(roleform->rolname) (line 588), which means it points
>> > directly into the tuple returned from pg_authid. After
>> > constructing the initial CREATE ROLE statement, the code calls
>> > ReleaseSysCache(tuple); (line 665), so the memory holding that
>> > NameData now belongs to the cache again. However, the function
>> > continues to use rolname when building the subsequent ALTER ROLE
>> > statements (lines 756–765). Because the tuple has already been
>> > released, rolname is a dangling pointer and we risk reading
>> > garbage or crashing later. To fix this, copy the role name before
>> > releasing the syscache, e.g. rolname =
>> > pstrdup(NameStr(roleform->rolname));, and free it at the end.
>> >
>>
>> Good catch, I didn't know NameStr returned a pointer, for some reason
>> I've assumed I was working with a copy. Attaching the patch with the
>> changes: (also I added you in "Reviewed-by")
>
> Hi, I'm reattaching after rebasing from master and fixing a conflict on:
>
> +++ b/src/test/regress/parallel_schedule
> @@ -125,6 +125,10 @@ test: plancache limit plpgsql copy2 temp domain
> rangefuncs prepare conversion tr
> # ----------
> - test: partition_join partition_prune reloptions hash_part indexing
> partition_aggregate partition_info tuplesort explain compression
> compression_lz4 memoize stats predicate numa eager_aggregate
> + test: partition_merge partition_split partition_join partition_prune
> reloptions hash_part indexing partition_aggregate partition_info
> tuplesort explain compression compression_lz4 memoize stats predicate
> numa eager_aggregate
>
> https://cirrus-ci.com/build/6142120160919552
>
> Thanks.
>
>
>> diff --git a/src/backend/utils/adt/ruleutils.c
>> b/src/backend/utils/adt/ruleutils.c
>> index 584438d05ad..41db9f10f5d 100644
>> --- a/src/backend/utils/adt/ruleutils.c
>> +++ b/src/backend/utils/adt/ruleutils.c
>> @@ -585,7 +585,7 @@ pg_get_role_ddl_internal(Oid roleid)
>> return NIL;
>>
>> roleform = (Form_pg_authid) GETSTRUCT(tuple);
>> - rolname = NameStr(roleform->rolname);
>> + rolname = pstrdup(NameStr(roleform->rolname));
>>
>> /*
>> * We don't support generating DDL for system roles. The primary reason
>> @@ -777,6 +777,7 @@ pg_get_role_ddl_internal(Oid roleid)
>> table_close(rel, AccessShareLock);
>>
>
>
Thanks for updating the patch. Some comments on v4
1.
+ const char *separator = " ";
+
...
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolcanlogin ? "LOGIN" : "NOLOGIN");
+
The separator is never changed in pg_get_role_ddl_internal(), so we can remove
the variable and hard-code it in appendStringInfo().
2.
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolcanlogin ? "LOGIN" : "NOLOGIN");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolsuper ? "SUPERUSER" : "NOSUPERUSER");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolcreatedb ? "CREATEDB" : "NOCREATEDB");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolcreaterole ? "CREATEROLE" : "NOCREATEROLE");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolinherit ? "INHERIT" : "NOINHERIT");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolreplication ? "REPLICATION" : "NOREPLICATION");
+
+ appendStringInfo(&buf, "%s%s", separator,
+ roleform->rolbypassrls ? "BYPASSRLS" : "NOBYPASSRLS");
For these options, I suggest preserving the same order as in the documentation.
postgres=# \h create role
Command: CREATE ROLE
Description: define a new database role
Syntax:
CREATE ROLE name [ [ WITH ] option [ ... ] ]
where option can be:
SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| BYPASSRLS | NOBYPASSRLS
| CONNECTION LIMIT connlimit
| [ ENCRYPTED ] PASSWORD 'password' | PASSWORD NULL
| VALID UNTIL 'timestamp'
| IN ROLE role_name [, ...]
| ROLE role_name [, ...]
| ADMIN role_name [, ...]
| SYSID uid
3.
+ foreach(lc, statements)
+ {
+ char *stmt = (char *) lfirst(lc);
+
+ if (!first)
+ appendStringInfoChar(&result, '\n');
+ appendStringInfoString(&result, stmt);
+ first = false;
+ }
The foreach() macro can be replaced by foreach_ptr(), allowing us to remove
the lc variable entirely.
foreach_ptr(char, stmt, statements)
{
if (!first)
appendStringInfoChar(&result, '\n');
appendStringInfoString(&result, stmt);
first = false;
}
--
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Joel Jacobson | 2026-01-06 06:43:07 | Re: Proposal: Add a UNIQUE NOT ENFORCED constraint |
| Previous Message | Mahendra Singh Thalor | 2026-01-06 06:26:36 | Re: Non-text mode for pg_dumpall |