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.94.2) (envelope-from ) id 1v8j7s-002ASj-US for pgsql-hackers@arkaria.postgresql.org; Tue, 14 Oct 2025 17:47:56 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1v8j7r-001gB6-QY for pgsql-hackers@arkaria.postgresql.org; Tue, 14 Oct 2025 17:47:54 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1v8j7q-001gAM-Vp for pgsql-hackers@lists.postgresql.org; Tue, 14 Oct 2025 17:47:54 +0000 Received: from fout-a8-smtp.messagingengine.com ([103.168.172.151]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1v8j6G-0029t4-0i for pgsql-hackers@postgresql.org; Tue, 14 Oct 2025 17:47:53 +0000 Received: from phl-compute-09.internal (phl-compute-09.internal [10.202.2.49]) by mailfout.phl.internal (Postfix) with ESMTP id 2AECBEC0214; Tue, 14 Oct 2025 13:46:13 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-09.internal (MEProxy); Tue, 14 Oct 2025 13:46:13 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=burd.me; h=cc:cc :content-transfer-encoding:content-type:content-type:date:date :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:subject:subject:to:to; s=fm2; t=1760463973; x=1760550373; bh=Lx9Ty7WDrsxtUVwYxI/5gy4ZMB8BMQ/v0AO+o7pJQxE=; b= sDBbbLDV6imEBYzXsAupyUN3NwXC4asYalLeJr6WMjWNrKNfD57JrTtk17r7+YAb Sl/mkGeoMCRExBT64K2Hd1UYQJpvHUcKNQxGdy5ah8yJtTMO8PbzVMdSwhECdU4X uCOyLNdWdIRR/6j80r6loAgqzMzc4ntl9VNyTIVQDlhH15vyf+bdgDnB/P0lD1R9 W0989uaorwIiBTZvzj2lAMBnSiC2r3FB5vRaqL1/R9oAy3BW7nRFKQEV094r5alz cpJy/CGiN9dH6BtGYtDBHdj96Wcjl1yL28dXT8l2l06wtBVxMd78kbuE8uXHmxjS 2/ne862MkyNIguGhbjdztg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:content-type:date:date:feedback-id:feedback-id :from:from:in-reply-to:in-reply-to:message-id:mime-version :references:reply-to:subject:subject:to:to:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t=1760463973; x= 1760550373; bh=Lx9Ty7WDrsxtUVwYxI/5gy4ZMB8BMQ/v0AO+o7pJQxE=; b=q YyJ/HtbLNYYZe8J821sMkdKNS+wIiR1CrRmr1ZehfHrR99s/e3yVkJmz6OUXTNAN 4pDmF/juk4lAN8/dvhHGf2syqk59I24ofQQIZjVcwKZYHGOOXnAE8+0wDO1I8gH6 3R8gYDuAZS3Mmz9KSFnGy2wI+XZkCiPR4GyDUWQki8cS3SuFwa0IGAlc+LQGFWMK bKAvUs4jAvOu6dIbbycpA5qxSYRg5H8nF69CHGj0a+oIH+7nBtIKoKACsSHc5zln zqn7GWil0w3WES2S+IDH09sR6LekhHi+kEv/xCHK0qVyq0bRjRflfoaHT60h3Gdt fQRxtMzK672flMOgYDIQA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggdduvdduudejucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucenucfjughrpegtggfuhfgjffevgffkfhfvofesthhqmh dthhdtjeenucfhrhhomhepifhrvghguceuuhhrugcuoehgrhgvghessghurhgurdhmvgeq necuggftrfgrthhtvghrnhepieduvddvieeijeeivdethffhteevhfevleffveevtdduge eukeduledtveetjeehnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghi lhhfrhhomhepghhrvghgsegsuhhrugdrmhgvpdhnsggprhgtphhtthhopedvpdhmohguvg epshhmthhpohhuthdprhgtphhtthhopehpghhsqhhlsehjqdgurghvihhsrdgtohhmpdhr tghpthhtohepphhgshhqlhdqhhgrtghkvghrshesphhoshhtghhrvghsqhhlrdhorhhg X-ME-Proxy: Feedback-ID: i675e48f3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue, 14 Oct 2025 13:46:12 -0400 (EDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.700.81\)) Subject: Re: Expanding HOT updates for expression and partial indexes From: Greg Burd In-Reply-To: Date: Tue, 14 Oct 2025 13:46:01 -0400 Cc: "pgsql-hackers@postgresql.org" Content-Transfer-Encoding: quoted-printable Message-Id: References: <97f0aa72-f172-4673-8b04-533f022c3149@app.fastmail.com> To: Jeff Davis X-Mailer: Apple Mail (2.3826.700.81) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > On Oct 9, 2025, at 3:27=E2=80=AFPM, Jeff Davis = wrote: >=20 > On Tue, 2025-10-07 at 17:36 -0400, Greg Burd wrote: >> After reviewing how updates work in the executor, I discovered that >> during execution the new tuple slot is populated with the information >> from ExecBuildUpdateProjection() and the old tuple, but that most >> importantly for this use case that function created a bitmap of the >> modified columns (the columns specified in the update). This bitmap >> isn't the same as the one produced by HeapDetermineColumnsInfo() as >> the >> latter excludes attributes that are not changed after testing >> equality >> with the helper function heap_attr_equals() where as the former will >> include attributes that appear in the update but are the same value >> as >> before. This, happily, is immaterial for the purposes of my function >> ExecExprIndexesRequireUpdates() which simply needs to check to see if >> index tuples generated are unchanged. So I had all I needed to run >> the >> checks ahead of acquiring the lock on the buffer. >=20 > You're still calling ExecExprIndexesRequireUpdates() from within > heap_update(). Can't you do that inside of ExecUpdatePrologue() or > thereabouts? Hey Jeff, I'm trying to knit this into the executor layer but that is tricky = because the concept of HOT is very heap-specific, so the executor should be ignorant of the heap's specific needs (right?). Right now, I am = considering adding a step in ExecUpdatePrologue() just after opening the indexes. The idea I'm toying with is to have a new function on all = TupleTableSlots that examines the before/after slots for an update and the set of = updated attributes and returns a Bitmapset of the changed attributes that = overlap with indexes and so should trigger index updates in = ExecUpdateEpilogue(). That way for heap we'd have something like: Bitmapset *tts_heap_getidxattr(ResultRelInfo *info, TupleTableSlot *updated, TupleTableSlot *existing, Bitmapset *updated_attrs) { some combo of HeapDeterminColumnsInfo() and ExecExprIndexesRequireUpdates() returns the set of indexed attrs that this update changed } So, attributes only referenced by expressions where the expression produces the same value for the updated and existing slots would be removed from the set. Interestingly, summarizing indexes that don't overlap with changed attributes won't be updated (and that's a good thing). Problem is we're not yet accounting for what is about to happen in ExecUpdateAct() when calling into the heap_update(). That's where heap tries to fit the new tuple onto the same page. That might be possible with large tuples thanks to TOAST, it's impossible to say before getting into this function with the page locked. So, for updates we include the modified_attrs in the UpdateContext which is available to heap_update(). If the heap code decides to go HOT, great unset all attributes in the modified_attrs except any that are only summarizing. If the heap can't go HOT, fine, add the indexed attrs back into modified_attrs which should trigger all indexes to be updated. This gets rid of TU_UpdateIndexes enum and allows only modified summarizing indexes to be updated on the HOT path. Two additional benefits IMO. at least, that's what I'm trying out now, -greg > Regards, > Jeff Davis