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 1vuh8C-001sxi-36 for pgsql-hackers@arkaria.postgresql.org; Tue, 24 Feb 2026 01:22:33 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vuh8B-00GG1h-2Q for pgsql-hackers@arkaria.postgresql.org; Tue, 24 Feb 2026 01:22:31 +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 1vuh8B-00GG1Y-12 for pgsql-hackers@lists.postgresql.org; Tue, 24 Feb 2026 01:22:31 +0000 Received: from mail-lf1-x12f.google.com ([2a00:1450:4864:20::12f]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vuh87-00000000sp6-3T39 for pgsql-hackers@lists.postgresql.org; Tue, 24 Feb 2026 01:22:30 +0000 Received: by mail-lf1-x12f.google.com with SMTP id 2adb3069b0e04-59e5aa4ca41so4704953e87.2 for ; Mon, 23 Feb 2026 17:22:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1771896147; cv=none; d=google.com; s=arc-20240605; b=Di2X9299NPoPN5/QTNbTj4YINWfMBfUYfgEyoSaipS8E3MAbhoEV8xYBbOHbV2zSa7 0fcsWz4nfuLIfCaFtGFI1YovLx5gklewgzF+IRLFk76cvLZ63rICcS/k5/NQjx5eGW7m Ivv6VgPvZDa1hBmDJlUeeYcI4Gk44Zr0WUchr0KNz7WqddqCEj1s4ze6hcMp+yhOihGA /V2dyP37TvFnNtUTHKLY6+FpX8KFjdF6ShPkvKdSYqkeTEC3iUasiJgOCMAQH9EPb1b6 c7ztUd+ISF545nLTTG1HUdd69iNkYTAo8XLrmrxoiDW4tKozRmoRMNfJB+iXKCPrjTQT LEiA== 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=zROHrp6aEkZej8OSqKYbcEJWwz8WJfovnl0pMMgLjNQ=; fh=06iwjsw70BE4Z/se/v85OjmCZXl6E1WHXMGJw208NUo=; b=VlcP8kAZlwTsT1041Z7jAmwS7uODvzfRuh8FEKatiRZ9iyJC/wCSVN+2HHPJSDYF/s lbCw7S3XZ+Pi8uTXCQz0yKRSmTMVdI06trP8XtXqHR4C107wr+w5ckdLit7iQMxq07Hc LgR7o1yG/opPD9ExxpQyFOvjHpE0KJHqhNgzu3uF3ZL/cGo5R4b65nx/50wTKQfyZYtZ jN/aXi50HJbYi4Nfm0bEosncEusGQc0ra1BjzlTEU/IgZI8Sg84Nnb/JbQuVc4ODEH4Q vtp2l0eSXYCrgrFgwpTZ2tDG3eoGQPnHsVc+daaQZj41lwlY/weAsAbAR7eWfv3wa6tu 913A==; 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=20230601; t=1771896147; x=1772500947; 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=zROHrp6aEkZej8OSqKYbcEJWwz8WJfovnl0pMMgLjNQ=; b=DV4AjcDyz9aVZGmT5joBFLPZe/bBO4nG2rpg6KXy3j4snzF6YchJbFNrCB255imUDw Kq6H+sOxFw2UHRBQkosRVMnEKGLuXLClQ4TLKdTNMV1uCkYOLH8/Rf6BLeVbyn8bbi8F gLEmzbO3s6Ky90gmfG0R8SQnDBqnIbYlVv1a+leUUgGryldwhnK+BJch57ULSSTrYF/v TO5RRtiHcgeB76Df7E9oU+arTdmU8FGskPEO2CCNNMgRMnvx6pbaIr7WJoEzFSc8eGxR iUQP7tg07EFEePLG14cx/eOK/H6Xbl5pjXWUBcmzLEydRKrET1rbPb7Q6giRvTpfXozp /SuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771896147; x=1772500947; 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=zROHrp6aEkZej8OSqKYbcEJWwz8WJfovnl0pMMgLjNQ=; b=iBZMEpXZlrWN+PImC5Ams5Js6CcytEfSUy2i6LGr0Y8bO2SiP/eATrZXO3EXfeF2Cy CEjdewndshtIZH08LprgE8/mBFrP6tGhw6nhWj2EKm3+ZaEDCfnVWkWPp38bHyR8SzII T5aJ6s9DGfnfik82thW3S7FRNLidenmT1fwT9XV/IFCA7cDeiiccBZfKxlBJHfR8S08D w3PNWck2qOKavAUvm4ii2LAI1+7r+ToFczV+DXffWx8F3rXD8bgL9nuue3Dw4Nl8kihA yvmFdD0bLxEeLx9wdYj/kWo4NwWVafrPRgMlNZ35QR3/2NgOzA9bf/IARN7yg5RoKmyX Uyxg== X-Forwarded-Encrypted: i=1; AJvYcCWEyIeAAuvdbclTtrNJBFjZQfeFQz/eEV/n8Rgp3RQGosk7WL/kAtdVnQqVY8tX50TwHSW+63GDKfcakae8@lists.postgresql.org X-Gm-Message-State: AOJu0YzsYK5gFIkxd/eDfMvJq8g0XT7Gn23qAXli1ay3FlDKTcD3+jQG 2oQMTxFP/iN5PNBkiy/VPuMs5eiH86C7ny10Froef80eKcSOoxfwHEYvMwnON8TIailhg2orbZK tKxcGPyEQCc6PeMdTzCcXB4HcT4+YnqnWRpoj X-Gm-Gg: ATEYQzzqUbOxgQqqwYZOdKtD7fTLiqWdvt7+cKJHQJGCimlOl3dgAEMdV+NNQnWXb5z ljg8Q1sie3dt00reBOWe82SXwGBG8WYZgBgyoNhD/1fdliR80imOvfj1qblHFemSk3aLAz4Em4b Te/IFWyQt0KnEBaNecItijWzzb6uqKoulnFwCbj/bXTlaMBdabEV+hHmoXTzoPb0zOE40FNcrqI J+/t9QO5bvPZc1rutJ7H8KPtVGismN22vYJH+2D5HjGC114dS6MJyjDaMrdEeDws8zO6pkYH9ao acCGtHjJ X-Received: by 2002:a05:6512:4007:b0:5a0:5ff6:19fd with SMTP id 2adb3069b0e04-5a0ed87d981mr3294755e87.8.1771896147004; Mon, 23 Feb 2026 17:22:27 -0800 (PST) MIME-Version: 1.0 References: <080f9394-c127-4cef-865e-10f2f997125c@postgrespro.ru> <38690b0e-f91b-46fa-b72a-57775612e463@postgrespro.ru> In-Reply-To: <38690b0e-f91b-46fa-b72a-57775612e463@postgrespro.ru> From: Masahiko Sawada Date: Mon, 23 Feb 2026 17:21:50 -0800 X-Gm-Features: AaiRm52_aHByfjT7wTEV4Ymj77jNDp7xnMJ1h_7DmRwN-mVa1-bKauLUyD8-d2U Message-ID: Subject: Re: Support logical replication of DDLs To: Vitaly Davydov Cc: Ashutosh Bapat , pgsql-hackers@lists.postgresql.org 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 Mon, Feb 9, 2026 at 2:42=E2=80=AFAM Vitaly Davydov wrote: > > Dear Masahiko, Ashutosh, > > Thank you for the feedback! > > On 2/4/26 10:39, Masahiko Sawada wrote > > While it sounds challenging, it sounds promising. > > I consider decoding of catalog changes from the WAL promising as well. It= hides > all the complexity of DDL queries behind simple catalog changes. CTAS wit= h temp > tables, local context objects (like function variables and arguments) in = ddl, > SELECT somefunc() queries with functions where DDL commands are executed = - ddl > is pretty diverse and original queries too hard to handle. I see the only > suitable way - to decode WAL changes in system catalog. > > The idea to use a stable representation of DDL is reasonable, but creatin= g a new > representation would add more maintenance burden, than reusing some exist= ing > stuff. There is an idea to use existing data structures. A possible simpl= e way > to represent catalog changes as a list inserted/deleted system catalog tu= ples > logically separated into a number of simple "atomic" DDL commands. Using = xid and > cid may help to make a logical separation of system catalog changes into > a number of atomic ddl commands. > > In case of system table changes, we may add some handling of different ve= rsions. > Changes may be directly applied on a postgresql replica by inserting the = tuples > into system tables as is. We cannot apply the changes to system catalogs made in the publisher to the subscriber as is because we don't support system catalog compatibility across major versions. > > On 2/4/26 14:44, Ashutosh Bapat wrote: > > Consider an example of ALTER TABLE tab ADD PRIMARY KEY (id) where id > > is an existing column in table tab. This will add a constraint on the > > table and also create an index. Thus effectively two DDLs will be > > executed. If we decode catalog changes we need to make sure that only > > the original DDL is replicated; else the apply worker downstream will > > cause an ERROR, stalling the replication. Similar is the case with > > CREATE TABLE ... AS ... - we should only replicate the CREATE TABLE > > and let the regular replication handle data replication. I think we > > need to somehow annotate the WAL containing catalog changes to > > indicate whether those represent the original DDL or derived DDL and > > decode only the WAL corresponding to the original DDL. > > We may gather all the changed (inserted) system catalog tuples belonging = to > a command and then decide which command is represented by these changes. = For, > ALTER TABLE ADD PRIMARY KEY we gather inserted tuples in pg_constraints, > pg_index, pg_attribute and then make a decision that these tuples are rel= ated > to each other and represent a primary key. I think we need to check not only what system catalogs were changed but also how system catalog tuples were changed in order to determine what is the original DDL query. For example, both ALTER TABLE ... SET STORAGE EXTERNAL and ALTER TABLE ... SET COMPRESSION pglz change the corresponding pg_attribute tuple but we need to generate different DDL queries from them. Also, we need to consider whether we need to replicate the information that is not present in WAL records of system catalog changes, e.g., CASCADE or RESTRICT of DROP TABLE. The subscriber might have a view referring to the table being dropped on the publisher. If we send only a delete change of pg_class to delete the table, applying the delete change on the subscriber might fail due to depending objects. Similarly, witn the idea of sending DDL queries to subscribers, we would need to modify the executed DDL query to reflect the publication definition. For example, while the publisher has table T1, T2, and T3, the subscriber has only T1, and T1 is being replicated from the publisher to the subscriber. If user executes DROP TABLE T1, T2, T3, the logical replication should send DROP TABLE T2 (or equivalent information) instead of the original DDL query as it is. Also, a similar consideration would be required for publications with publish_via_partition_root being true. > When we decode final changes, it is much easier to handle CTAS because su= ch > statements may be too complex, like CREATE TABLE AS SELECT somefunc(). On= ly > final changes in the WAL will help us to decode table columns and its typ= es. > Attempt to create a derived ddl sql seems to be very hard to implement. There might be cases where we might want to send DDLs instead of system catalog changes. Taking an example from your recent mail, ALTER TABLE t ALTER COLUMN x TYPE double precision; command writes pg_attribute changes with inserting rewrote tuples. If we improve many ALTER TABLE sub commands so that we can execute them with a lesser lock level or without table writes, it would be better to delegate how to execute changing the column type to the subscriber that might be a newer version. 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. Regards, --=20 Masahiko Sawada Amazon Web Services: https://aws.amazon.com