Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wdsck-004PMI-0g for pgsql-hackers@arkaria.postgresql.org; Sun, 28 Jun 2026 16:44:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wdsci-00GlXn-2v for pgsql-hackers@arkaria.postgresql.org; Sun, 28 Jun 2026 16:44:48 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wdsci-00GlXf-1h for pgsql-hackers@lists.postgresql.org; Sun, 28 Jun 2026 16:44:48 +0000 Received: from mail-ed1-x530.google.com ([2a00:1450:4864:20::530]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wdscg-00000000dDw-0wKJ for pgsql-hackers@lists.postgresql.org; Sun, 28 Jun 2026 16:44:47 +0000 Received: by mail-ed1-x530.google.com with SMTP id 4fb4d7f45d1cf-6983f20a8bfso2780063a12.1 for ; Sun, 28 Jun 2026 09:44:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782665084; cv=none; d=google.com; s=arc-20260327; b=PjX5046AOsaxvLWIfFfp3oFxD3d29XnBjipz4NDh+kFkVWxZV7BNZ4+KWe9umZITgq IF5BUdJReYZf3v5vahYeQQ7oGJeyeDhbI5WvQaLtQ0Y1t0Q0dBUWessgTRaSZyV4TjPi fjUStbAARaLF0HvZRKAL24xtqDcj8kAjSrXUOkY07zYH1OkItGZGnrasL9BpkVEhtoGJ /MtLpGHjXLEKu8p9myTxPmEZoJ5JlKy13Ga2ipRgSfZm3/o0G/EesUNXv1j+9OIrfwbU kLjIQ5X1mwGtRQ826TMOz0wGd91WaSmLpML9NJ9ZDe8dC58n3HckiDjISy7tTG6Jz2De I0AA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=pTybDFAtxsaxU/r3NsPKHOVQyXGqI/pjh2G+6SmdFio=; fh=u0Cx073PSy9x4Dz9bfJZa5PmlFGL/cWz/PGEmTbctwk=; b=FCVlQTDEVgyPBuV2timh+XxMjAnFNJxKxvkqPX/x6Oa40p0WbhAxZhtDL+FaBWRfue 3HbFuQAQ3/gN/hwg35iIK1VDZ3APHsiavGg4twon7+ViQySX3MF5wjIDVSBMNZgrnfWN hbnhlVzrJwB8xsQbG8LhV4syaUipJ2/AlbqWWLRY7w0dO9KQC/14EckB0IEpawMW7Cmz p+UzQrO3eUBT19W88O5nLj2GFhMqXhAT0Xyv1Cg+e2b0yRgfT9eBkxak/MtTFJsGoG2I GxNmgKDK+GmQARG418unNZ4adPsEJsoWqYnDACNRkBg7QCf3ZOpQC7WAAHMwmIEWcnxr 2qiw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782665084; x=1783269884; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=pTybDFAtxsaxU/r3NsPKHOVQyXGqI/pjh2G+6SmdFio=; b=ZCj7KB5bp8qZzHf1R3YX8tgK0DYhf/SL9HjtXPorjlwa/PtlOhcVBEpI6a1hG5wHBR x2IReFwCtLoc3yHIH7o7Q4WMgti14XNCGHzCGs6PfzDORDLDPf9x1Cf8BB/vVLQPkCH3 WDs019JaxhSkzvzjHyz+As5YVUfBf3uXEU2QLNm5WfCUCcfwQxrnDjYu6ma+Zzc3FIGf +b7dq3mnNoV4eiclFc2qn2/mLEleRK7dQmsjfs+wzu6TWheGKpAxkmOyHOE1D5tcImta j0cH0JGLhvR8U5z3f9k53Diyh8dmb06BkF+tYNZEcZ9V0ImrJbEOf1NVOhiKso05g42k ZMzQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782665084; x=1783269884; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=pTybDFAtxsaxU/r3NsPKHOVQyXGqI/pjh2G+6SmdFio=; b=FfUfwZciKrqKP3QegALwD2X/1skTMqLR5qZvicmgNle4+1Z1xb3CjIBuWC59SZb1CL UCwgo8C0HrKVhWo4zmFql9gvlg0ngkmywLJgktIvW23rID1vZ7F5ArgP1b859HysEklS Czzo+UgYwq4psnJ2vyZPPnit1S6HUYTNtJL+V9Xezsx6aoXN1/aXXpMjKHtnNs7PR14H VZU/tgDi+x2DMqEGJBNjVbW+J+QKwcoaHzkv5nUOlgFwjfBS3VWDw0y0oeGBQZqRWIq1 sTMpvU8ppVkOr1TbVThT9BMgNEIPTvtuvajVENj5L/+8eQy6EpNmRfm/t1JCEmmCfVm5 ucKA== X-Gm-Message-State: AOJu0YwTxnnUiAhuT2V64ktULjZUPoFnEi9yCxHsOWLdAOTdIzpiWBMk NvxW3DWEAdJvqphBBrvku0Yd4ux1zCQ5k82rodaVo5NxHdM7BZSNfRsXCFGLcTxWu26r7WlMw5w jqsifp7yqZr+nrDH4LgVwdfMtaF7NZ1o= X-Gm-Gg: AfdE7clG1JA+DtGwffuKBUPRIAiCuEtlHuz3u3CfP2XRBKgkfDtrElkpN9c60n+DL4W vpRb57MJMwE++vZ4SuNf9E40c3cvUgbKX0Bl5IZp6g0S9D+5VSOzWmimi8tatVGGR30q0P/2/u6 PezzGWw/Emh/Q3z+x8uEwywSmyRVqyEyW4wgPdgyQijMcMQZUa7S7nRq7fF0VKKC0pOjLyzS3MV X0fx967V3qUEobzyz29vrkdYbju08XrVdMGwn+/f9a7w/p+EHxDcwuWvQeKxQ5QiMIxp1MAmjfI zM/9T/h5DSBClrsdvo+2heKvYWvcw9eE8Oj4nrT4GYnA6d3leg== X-Received: by 2002:a17:906:6a19:b0:c12:6822:d292 with SMTP id a640c23a62f3a-c126822d86emr46925466b.4.1782665083495; Sun, 28 Jun 2026 09:44:43 -0700 (PDT) MIME-Version: 1.0 References: <983C7B03-D9C3-4F15-8913-7616EC5E6414@gmail.com> <20260623.162104.775158751399277188.horikyota.ntt@gmail.com> In-Reply-To: From: Rui Zhao Date: Mon, 29 Jun 2026 00:44:31 +0800 X-Gm-Features: AVVi8CfD6kqEfkM1lTNA-Bl6QnQ-w40PDm7uW2Uw6GfFXsPfyQRCKIcMlMyeaW0 Message-ID: Subject: Re: [PATCH] Add pg_get_table_ddl() to reconstruct CREATE TABLE statements To: Akshay Joshi Cc: pgsql-hackers@lists.postgresql.org, marcos@f10.com.br, horikyota.ntt@gmail.com, li.evan.chao@gmail.com, Zsolt Parragi Content-Type: text/plain; charset="UTF-8" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi Akshay, Nice patch -- server-side CREATE TABLE reconstruction is something we want in production. I tested v11 on current master and it round-trips correctly across the documented coverage, including the recent grammar (VIRTUAL columns, NOT ENFORCED, temporal WITHOUT OVERLAPS / PERIOD) and state set later via ALTER. A few comments: 1. Real bug in schema_qualified => false. strip_schema_prefix() strips the base prefix anywhere in code position with no token-boundary check, so a cross-schema name whose schema ends with the base schema's name gets mangled: postgres=# CREATE SCHEMA xs; CREATE SCHEMA postgres=# CREATE TABLE xs.reftbl(id int PRIMARY KEY); CREATE TABLE postgres=# CREATE SCHEMA s; CREATE SCHEMA postgres=# CREATE TABLE s.orders(id int PRIMARY KEY, ref int REFERENCES xs.reftbl(id)); CREATE TABLE postgres=# SELECT d FROM pg_get_table_ddl('s.orders'::regclass,'schema_qualified','false') d; d --------------------------------------------------------------------------------------------- CREATE TABLE orders (id integer NOT NULL, ref integer); ALTER TABLE orders OWNER TO postgres; ALTER TABLE orders ADD CONSTRAINT orders_pkey PRIMARY KEY (id); ALTER TABLE orders ADD CONSTRAINT orders_ref_fkey FOREIGN KEY (ref) REFERENCES xreftbl(id); (4 rows) The last FK line says REFERENCES xreftbl(id); it should be xs.reftbl(id), and the result doesn't replay. Gating the match on a token boundary fixes it -- though see (2), which would remove this code path (and the bug) entirely. 2. Bigger picture: is schema_qualified needed at all? None of the existing pg_get_*def / pg_get_*ddl functions have such a knob, so this would be the lone exception. The established convention is to let search_path decide (generate_relation_name): pg_get_viewdef and pg_get_constraintdef already work that way, and pg_get_indexdef supports it too via pg_get_indexdef(idx, 0, true) (the PRETTYFLAG_SCHEMA code path). It is genuinely what we want in production -- the caller controls qualification through search_path. Set it to the table's schema for unqualified output, or to pg_catalog (or '') for fully-qualified schema.table; pg_dump itself dumps under an empty search_path (ALWAYS_SECURE_SEARCH_PATH_SQL) for exactly this reason. Following the convention would also drop the option and the strip_schema_prefix code (and this bug). That last point matters: there's no robust way to strip a schema prefix out of already-generated SQL by text processing. Doing it safely means re-tokenizing arbitrary SQL (string literals, quoted identifiers, dollar quotes, comments, casts, operators, ...), and strip_schema_prefix is a hand-rolled partial scanner of exactly that. It shows -- it has already needed several over-stripping fixes during review (the base name appearing inside a string literal, and inside a quoted identifier), and the token-boundary case in (1) is yet another. Deciding qualification at generation time (generate_relation_name) avoids the whole class: the backend's real deparser already gets this right, rather than a post-hoc string pass trying to re-derive it. 3. typedefs.list is missing TableDdlContext and LocalNotNullEntry, so pgindent leaves the "Type * var" pointer spacing in ~30 places -- for example the forward declarations at ddlutils.c:226-237: static void append_stmt(TableDdlContext * ctx); There is also a stale comment at ddlutils.c:1830 in append_column_defs(): inherited columns are described as "emitted by the INHERITS clause (once implemented)", but INHERITS is implemented now. 4. Related to (2): a temporary table's default output qualifies it with the session-private pg_temp_NN schema (e.g. CREATE TEMPORARY TABLE pg_temp_3.t ...), which won't replay anywhere else. A reconstructed temp table should just be CREATE TEMPORARY TABLE t (...) -- the TEMPORARY keyword already puts it in pg_temp, so the schema name should never be emitted. This also falls out for free under the search_path convention in (2): pg_temp is always in the effective search_path, so the table is visible and wouldn't be qualified in the first place. 5. The commit message is out of sync with the code and func-info.sgml on the option interface: it still says "include and exclude" and lists plural kind names (indexes, foreign_keys, triggers, policies, partitions), whereas the code and docs use only/except and singular names (index, foreign_key, trigger, policy, partition). Looks like a leftover from the include/exclude -> only/except rename. (FWIW on the naming itself, include/exclude is the more common convention for this kind of list parameter -- pg_dump has --exclude-table/-schema/-extension, etc. -- while only/except reads more like the SQL keywords.) Otherwise it looks good. Thanks, Rui