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 1wHFFY-006zUM-1e for pgsql-hackers@arkaria.postgresql.org; Mon, 27 Apr 2026 06:15:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wHFFX-00CRNa-2G for pgsql-hackers@arkaria.postgresql.org; Mon, 27 Apr 2026 06:15:19 +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 1wHFFX-00CRNQ-0u for pgsql-hackers@lists.postgresql.org; Mon, 27 Apr 2026 06:15:19 +0000 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wHFFU-00000002yzh-3kir for pgsql-hackers@lists.postgresql.org; Mon, 27 Apr 2026 06:15:18 +0000 Received: by mail-lf1-x12d.google.com with SMTP id 2adb3069b0e04-5a40b2bc96dso11437079e87.3 for ; Sun, 26 Apr 2026 23:15:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1777270515; cv=none; d=google.com; s=arc-20240605; b=XdUSbp9CTZtBhRsSI0UFTQ9CWyhbeEw8YFxcltfitrE//hxELWEbGEPL0CJ12oiVOq oFblrXL9MfThJr8/H8vv+4cYCCDXmLLz2vpA9U5B4Ogzp0AUOlCsIJuAD5U3G5sdnvzc /HfVuXj2N3ycDN6ly03Mjm8ofA46vp1cQ7chUZc7YndKZvYEYChOE/JVLfy8tP3+oXnI f6BlhxOT0BbTdx3gPfHcaNi4I9QNP8C68aeKjQsem0dhmWnIhaWsvrztPx7+DEZKzOtc IiMykqWUKdB65kQhBcJfWPHtQSEvwRnN4Vsc0+NRpZARsgso+ptZJieTg4IX/2ZHwoXc ZroQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=iQIZrsHL7AeaNGudpyZ2gnrRqXgYwY3lwZqUJquRoRo=; fh=4sa2H99J4NQhDlWcVIBQ6/uX6o5TUeUib9vcjQa/MJc=; b=XXhwSGDuLIkqGx8SCXYXIQXmR2CnYM2dg9BUrN41Y3INIVuLOaaPUmxDIr82+MnvVj aVO7qWKM2ev1YJV6BWSdELCpHVIUpziPseFSFdoQ8YSS4fOiqpsz0Yspz11q3YrNW8xs mfGmeRgessyMHeTkEv7vFPGwt/cdva2mOqK9nSp2OtfRKpGh4aXhiJghFQCjvN/tcWnN t/qyJGDCuClo4Y9YVQY9ABFtNfovqUNpq5ln7Xx1pZqT8nnwgxfCNfJBq+Ng90eR9pUS MuV0DsOnQ5hrNf8pK2SGM8FxAJF8cxDIuYiGFQGplomXMTEmJCzH660S9ygqMX4wCsIj ELrw==; 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=1777270515; x=1777875315; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=iQIZrsHL7AeaNGudpyZ2gnrRqXgYwY3lwZqUJquRoRo=; b=WmxhQDQKgwe28/lFyteudWxP14SpuP5VO5nCKiGE26uIRqJbCtzGVfJKMHscRozidW rX3yb89REn5RUBt2L98aiBYzQp+cMKnfxIFXAg5OSBKaCd3XxHcjCZQZk3guzEJ68oKo TjaGFWJXDKbL0SbOOp/K3nVgLKZ3T2VbxZqIQki5AqgokLHwL04OHOLDLC/I2DGwFviF SFFQaJP8DSRDasA5UhqJQsoxkPJTk9MbKcFVDcS8c/O74usTh5VWokQWHjETCw6a29Ah B4WXwMUWWD/e0I957jKYqH0AQvXYePeAvxVW3mimZ5ZfcVe8A2pspxCRVkiUJCgc6O96 wHNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777270515; x=1777875315; h=content-transfer-encoding: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=iQIZrsHL7AeaNGudpyZ2gnrRqXgYwY3lwZqUJquRoRo=; b=WrlsrC53e0qPW7W3/xmp3t2G+4Qb2SUOb+8pJ3xEdTpv1/v8DRyYz9/jYr1f4kfimA FRet0vYU6jSuedi3+h/rFPWz1dO0Hww4b3CsguZwFvGzClB0pfGjtsCp5MaFn3/vgxaY Vk/Wmr3JAsCGu0ImXcFVO5tIS1RpM/7roGvbJZHuTukykdRSZ5XF8uZ6KZJkpBeftboA J0MNa03lNDnqEpdaE+adLRpPBt9uDbNRv5lnXarJQSzsj3cwCocQcTxayyPHMd6qornm JG6LEHlv9O3wUhmAmlpIJ5aJxDxmQxiW/7UAQafeBVsrBFLgDilbbzztsKnglsh36Mg3 fZkw== X-Forwarded-Encrypted: i=1; AFNElJ8LXzo4+a05e2zACNisnf3n8bSZYx2NmLM3zUfrZvDeseOp7iBIQCFknHfKHMXUGyin44UvVppiWVZ5aDiR@lists.postgresql.org X-Gm-Message-State: AOJu0YwE/45drvB6pqUPA6YZ8l6iIjW8BYpVrHl3WV+huB/Svo7ICq64 q3Wfq/UDOaPJaWBu09lo6a8e8ZvN+2r7+wGKe8sKIOMQ4hR6epdWSsq68nPw01dpFYfpTggTkkc cxIddFK5crVaxEHhkTgbBAkYcH/AjcvA= X-Gm-Gg: AeBDiev4BhMIGdFlMmLlmd79549OrpuQVbwKLVNHWjkxUTlrVElbWTEc80QrWDrW07t bw0UWdv5HXa5x7AqK3TukaUS7VP3uo5uZcVI9qIpYPT02J1TztjLmHv7pXxnKjzA+9h8CT2/1U0 POU2jEgQq57l7eEaZcldkBmCCqpvQ7qphQgX6f+Ar72xWag94fSMU+EboPJlF4nI8v5Yq8DXR/4 +9B9ZMp/aZtP3mFMmwwCyc4guYXsmGGsp5VxAYi5sc+fEIag6iL+BTdEAkF7jR+1xq6HzqNaLG1 z74QYYN63/MyU1X5G8LNzVuoTMHjlThp7/Xwov0cZQMuh6f1AO1gERYOkK0= X-Received: by 2002:a05:6512:1595:b0:5a4:52d:4abc with SMTP id 2adb3069b0e04-5a4172ba775mr12763192e87.8.1777270515100; Sun, 26 Apr 2026 23:15:15 -0700 (PDT) MIME-Version: 1.0 References: <080f9394-c127-4cef-865e-10f2f997125c@postgrespro.ru> <38690b0e-f91b-46fa-b72a-57775612e463@postgrespro.ru> In-Reply-To: From: Dilip Kumar Date: Mon, 27 Apr 2026 11:44:55 +0530 X-Gm-Features: AVHnY4JVhjlgWpEOvyKdZ9G243VIAJLKuDGpjQW0lACuZZbjfA5sB8AxsuLqS2k Message-ID: Subject: Re: Support logical replication of DDLs, take2 To: Masahiko Sawada Cc: Vitaly Davydov , Ashutosh Bapat , PostgreSQL Hackers Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Tue, Apr 21, 2026 at 4:45=E2=80=AFAM Masahiko Sawada wrote: > > Hi, > > On Mon, Feb 23, 2026 at 5:21=E2=80=AFPM Masahiko Sawada wrote: > > > > > > One idea I'm experimenting with is that we define an abstract data > > type that can represent a DDL (like CollectedCommand) and write it to > > a new WAL record so that logical decoding processes it. For CREATE > > DDLs, we can use pg_get_xxx_def() function while using a historical > > snapshot to get the DDLs. We would need to implement the codes to > > generate DROP and ALTER DDLs from the data. I believe DROP DDLs would > > not be hard. For ALTER DDLs, we would incur the initial implementation > > costs, but we would not change these codes often. > > > > DDL support for logical replication is one of the biggest missing > pieces in logical replication. I'd like to resume this work for PG20. Thanks for working on this. > We made a lot of effort on this feature through 2022 and 2023, but the > development is currently inactive. The last patch was submitted on Jul > 18, 2023. I've reviewed the previous patches and discussions, and I > would like to summarize how DDL replication was implemented, the main > reasons it stalled, and propose an alternative design to address those > problems. > > The overall idea of the previous patch set was to implement DDL > deparsing and utilize it for DDL replication. It converted a parse > tree into a JSON string. For instance, if a user executes "DROP TABLE > t1", the deparser generates from its parse tree: > > {DROPSTMT :objects (("t1")) :removeType 41 :behavior 0 :missing_ok > false :concurrent false} > > to: > > {"fmt": "DROP TABLE %{objidentity}s", "objidentity": "public.t1"} > > This JSON string is self-documenting, meaning someone who gets it can > easily reconstruct the original DDL with schema-qualified object > names. In a dedicated event trigger for logical replication, we > deparsed the parse tree of a DDL, wrote it into a WAL record, and then > the logical decoding processed it similarly to DML changes. I think there was also a discussion on whether to use JSON vs the existing infrastructure of converting nodes to strings. Although the JSON is standard format and might provide more flexibility using existing format avoid extra maintence burden. > While there are several benefits to the JSON data approach mentioned > in the wiki [1] -- most notably the flexibility to easily remap > schemas (e.g., mapping "schema A" on the publisher to "schema B" on > the subscriber) -- there was a major concern: the huge maintenance > burden. We would need to maintain the JSON serialization code whenever > creating or modifying parse nodes, regardless of whether the changes > were related to DDL replication. IIUC, this was the primary reason the > feature didn't cross the finish line. Do you mean that modifying any existing parse node requires changing the JSON serialization code? But why do we need to do that if that's not related to DDL? I don't think I understood this point clearly, can you explain it or point me to the discussion thread? > Additionally, I think there is another design issue: it is not > output-plugin agnostic. Since the deparsed DDL was written by a > logical-replication-specific event trigger, third-party logical > decoding plugins cannot easily detect DDL events. Ideally, we should > write DDL information into a WAL record natively when > wal_level=3D'logical' (or additionally when a GUC enables DDL events > WAL-logging) so that all decoding plugins can detect them. This also > allows us to test DDL logical decoding with test_decoding without > setting up a full logical replication subscription. Yeah, that's a valid point, but I think we could separate the event trigger logic from the decoding plugins so that it's available for any other output plugins? > To address these two points, I'd like to propose an alternative > approach: we introduce a new data type, say DDLCommand, that is > self-contained to represent a DDL (like CollectedCommand), and don't > rely on event triggers. It would have the command type (and subtype if > required), the OIDs of the target object and its namespace, and the > OID of the user who executed the DDL. We write it to a new WAL record > at appropriate places during DDL execution, and the logical decoding > layer passes the data to output plugins. That way, any logical > decoding plugin can detect DDL changes, and it's up to the plugins how > to decode the DDL information. Interesting. I'm trying to figure out exactly when we plan to construct this new DDLCommand data type? And how would we prepare this, by converting the internal DDL structures or from parsetree? > In pgoutput, for CREATE DDLs, we can use the pg_get_xxx_ddl() > functions while using a historical snapshot to get the DDLs, saving > maintenance costs. We would still need to implement the code to > generate DROP and ALTER DDLs from the data. I believe DROP DDLs would > not be hard. For ALTER DDLs, we would incur an initial implementation > cost, but we would not need to change this code often. We can > implement the DDL generation code in a way that improves ddlutils.c. > > Also, because DDLCommand is separated from parse nodes, we only need > to change the DDL deparse/replication code when it is actually needed. > Additionally, this approach would eliminate the code around the > two-step process (using DCT_TableDropStart and DCT_TableDropEnd) for > DROP TABLE. While it would miss the flexibility benefits that the JSON > deparsing approach has, I guess it would not be very hard to implement > the mapping in the deparse layer even without the JSON data. > > Regarding the publication syntax, previous patches proposed: > > CREATE PUBLICATION pub FOR ALL TABLES WITH (ddl =3D 'table'); > > While simple, it doesn't support critical enterprise use cases (e.g., > DWH environments) where users want to replicate CREATE and ALTER, but > explicitly filter out DROP TABLE to prevent accidental data loss. We > should consider introducing publish_ddl options to filter operations: > > CREATE PUBLICATION pub FOR ALL TABLES WITH (publish_ddl =3D 'create, alte= r'); +1 --=20 Regards, Dilip Kumar Google