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 1wc3RU-0033QO-2w for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 15:53:41 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wc3RT-00CjoP-2F for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 15:53:39 +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 1wc3RT-00CjoH-0U for pgsql-hackers@lists.postgresql.org; Tue, 23 Jun 2026 15:53:39 +0000 Received: from mail-dl1-x1233.google.com ([2607:f8b0:4864:20::1233]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wc3RP-00000001lbN-2eRC for pgsql-hackers@postgresql.org; Tue, 23 Jun 2026 15:53:38 +0000 Received: by mail-dl1-x1233.google.com with SMTP id a92af1059eb24-139986373b8so6379045c88.0 for ; Tue, 23 Jun 2026 08:53:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782230014; cv=none; d=google.com; s=arc-20240605; b=GkTNqK3WL+fQREfpZjRYwdsITWis0+DC8bAcaceavfxGPeehpaXyPc6MIkxntaisrc lFsSYKjhZmnTnWRzm0tonO/0e4D6w71FW3pcQ9d7g5hyrMMabulOocH+C/ZeKHKZEsE8 Gw5Qnq/0A5JzV/N3tGMxFM5PvhOTEwRoFHnlSKtHF2qTcyUYEksNn17wzqUHyyKIX4It UBWlmVxOi7oW2+WOl9wlYWI64+Bz+VZ7dGaqUMEX/6AdDDrhErufoYUdUiE7kE4WkZ6h LCOunGkFQkw4TQoNSEmmyriNb6IbXl90TcQ1AlXh4M9xKdrkvA1Yt0jNYCE1bs2LcKFW YuWA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=nhL99Cyufg1fk97wEWF9MGC4oVZp/yr3b1Td3B1ABQA=; fh=ulyIIqp4rYqa2EqjOJb3h/tozXXwB/4lp9QQzcqK4Zg=; b=ADtWERc8WExSiJ48+GlcNlSZnp5zII3JM2CmrUz/H0ve+2nA7BtPmhvc4Hod5DjLOh GB8KNHqbIVytnBlbCQmR4UNGEK9T6JPFr+Xa2hXHdB4RapMevjxsmXXP6MahhJ6Yj5cM 9GH66Chv/QDlrHo8/Mbv6Vqm7BZeoknnq97CioUL0Sg3VcTsf4BSWV7+Jd4qA4uEW1zW ayZP7QZySh44+SLVuTKEI7FQwVBcDVysY0qrJ/LE2dgbAmBUCRa5YEq67SM+ZVAPIiYf 2ehiBCty5BnBCksmyEfs13cXP4ogKAp/kfs5GOs0CrotU2U73y2IFJDSOrSWc71aIn1o 6JaA==; darn=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=1782230014; x=1782834814; darn=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=nhL99Cyufg1fk97wEWF9MGC4oVZp/yr3b1Td3B1ABQA=; b=eHeVeZwCAwGGWV1xAu7BhRorZ/UlGW0bQ/ZwB4nJ2LTKNvqD6sTi/t/iJ7FkLjEZTs ue/fQoVWeJZdk75J9+7SpVJy3LfgE6KwLvZAfXignoh2DH/AfcRe3yFISg044Rv+Iyoe 7WDiTCt6uIObXkyip1xMi51gURK5dMo9Nm3w/7qJ/MQeWJ1ZQOlMeCgpBQ6fPshSScJC zN4cTuxfu71qcArhJjtaNHOyVMPhXXf/QeGOLRxggNYAl+sKE8b+jCTWMifNUqqDRpFl ZtPkB7ddMAZw7adMA8Lbxmk69WX66Ck1ankowyYgOKwHQ6KMrWP9ZHG48uO8EUR5JPzd Uwig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782230014; x=1782834814; 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=nhL99Cyufg1fk97wEWF9MGC4oVZp/yr3b1Td3B1ABQA=; b=Ew6qGiKCi1LUnsFwiQGFWz5bPRKwk9pPZFRLimu8hDTMH+rChcpjrXHg5tw7w7LSNj TGPEHBUdwGYtSoZW2LU0YR9DRmVlghMkkN1I6ibjOQC7aVxHJ+90jA+XzKFbwh0Um/JU ffmz1n49lMrL1OlJudDSEHp5oF2yRBhD2a+2xS7Lmez6OD/TWD4jVLNnBeiXYu4zZCSv DjanhCVj2k74FN8EFWWoc2ejJGsWsenyETMiSGXNFfQo/jnNZIXElyvgxZCuTMJZc4tJ MFnVnvyrb9YvF+oteyIy3mLcw+/nleuBqh7hkgxuU2djWaIv7zY2fbiwb+lzrBdbnMOy MBmQ== X-Forwarded-Encrypted: i=1; AFNElJ+7pjOxDk+2XHI9lL+b1uVITAW2kJP/Dp7KhUNOXjJJD+Bcb4jrgZwpC/bo7PJc6xjbpxgvEFP/ZMpqgaoG@postgresql.org X-Gm-Message-State: AOJu0YwrIYyQ1sjzqTwSFo+BsUNWcqJWuLo5Tb+xRkQ57dtqfGP0bOa4 TKwdlyEGg5f02ANWf1QTZN0wyRtYfTSSqVf+qIo4/IeuhV68a9xhS68QIIpO0mU4abafnii5GY/ +6owUF8uyumwvUlvK4+oMrYDfFN1+ruU= X-Gm-Gg: AfdE7ckp2EXU+dDACtRtvWiwByG8HPODgBXMytwuRhXOMEbezDDk2Q6jBVlXB5fWvGU s1x05iwFFn2mQVhHyl3KwzW/hMWHwBWj5bzmd2Jd5MUeDFdecJ7q/hcN1qbf8Ii1rtCpqoBBL+F h1iIJwTTOrODjQm/N/r/hEVk/50z3SYpjT+5iPqc1UqQnjwZKO4VLjiANqrWate5yTxHMLza03P BdNW7yN6HnOzuuYyfMYh5lM3zZ8nITkw9wWhs4A6G+v4kTDoqzWiXodDE0PXfG3AZlRgfpBpRbs 1HY1P1JPL7jedhy3drM9+CyNREpRVnD3O5At1X+1TtuGiJ8YsoNRJx1ntpXTqezE1uqeKOo0d86 gI0V/fZT/HbdSCQ== X-Received: by 2002:a05:7022:60a1:b0:138:6c:477f with SMTP id a92af1059eb24-139c7051c51mr2181348c88.34.1782230014139; Tue, 23 Jun 2026 08:53:34 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Corey Huinker Date: Tue, 23 Jun 2026 11:53:22 -0400 X-Gm-Features: AVVi8Ce2g_4vfRF_33K8Jv8pi-6vPfu1idLi1AD1QQX8qCDF-ZWM48SlZkUWQ7Q Message-ID: Subject: Re: use of SPI by postgresImportForeignStatistics To: Robert Haas Cc: Etsuro Fujita , "pgsql-hackers@postgresql.org" Content-Type: multipart/alternative; boundary="000000000000e6a15a0654edc2b5" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000e6a15a0654edc2b5 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Jun 23, 2026 at 8:11=E2=80=AFAM Robert Haas = wrote: > On Mon, Jun 22, 2026 at 3:08=E2=80=AFPM Corey Huinker > wrote: > > The first bit of dissonance comes from the SQL-level functions having > schemaname+relname parameters, and the reloid is resolved via > RangeVarGetRelidExtended() which has a callback to check for correct > permissions and setting the proper lock level. However, the C-caller woul= d > either have the reloid already, or an already open Relation, but no > assurance that the caller has the correct permissions for that table or t= he > correct lock level on the table. So either we make an equivalent to > RangeVarGetRelidExtended() that takes an oid, or the C-caller has to deri= ve > a RangeVar, call the existing RangeVarGetRelidExtended() function, and > verify the result reloid against the supplied parameter. I went with > deriving the RangeVar and putting an Assert on the before/after reloids, > but perhaps the smarter play is to make a function that checks for > ShareUpdateExclusiveLock on the Relation, and then does the equivalent of > RangeVarCallbackForStats(). > > I don't think this is really a problem. If the caller is specifying > the OID, they should have called RangeVarGetRelidExtended themselves. > Permissions-checking, locking, and opening the relation should all > happen simultaneously, and the logic shouldn't be duplicated later. > It seems dangerous to me to provide an externally visible function that assumes the caller has already self-verified that they have permission to modify stats for an object. It's possible that the existing analyze() code already has verified these permissions, but at this moment I'm unsure. > > A smaller bit of dissonance was with RecoveryInProgress(), which if I > recall we're checking before RangeVarGetRelidExtended() to avoid trying t= o > take a lock that will fail. That check might not be meaningful if the C > call takes a relation, thus ensuring that some level of locking worked, > thus we aren't in recovery. > > I don't quite follow this part. > I mean that I don't know if that check is required in situations where the caller already has an open relation with the right lock, and if we can avoid the call by having the API take a Relation rather than an Oid, then that would be preferable. > > > Next is the existing validation functions stats_check_required_arg(), > stats_check_arg_array(), and stats_check_arg_pairs() all work on values > indexed by the positional functioncallinfo and the corresponding > relstatsinfo/attstatsinfo structure, and this makes a lot of type checkin= g > and value checking compact and uniform. If we want to keep this sort of > uniformity, the resulting StatsData structure will end up looking a lot > like the FunctionCallInfo that we already had. > > Yeah, this is worth thinking about. You could consider putting an > array inside the struct and use #defines for the indexes. And instead > of having a separate Boolean for each index, you could use the same > index to reference the N'th bit of a single integer flag variable. That's basically what I was trying, though I wasn't brave enough to use bitflags in a v1 patch. The #defines already exist now in the form of enums that act as indexes into the FunctionCallInfo array. But if my data structure is an array of Datums with an array of is-not-nulls, then I'm just not that far away from the FunctionCallInfo structure we already have. > > > Next is the fact that the end-destination for every value passed in is = a > Datum for a pg_statistic heaptuple. Most Datum values are checked only fo= r > their null-ness and if they're the correct type, so the value itself is > usually just passed directly from fcinfo into the heap tuple values[] > array. The float[] values are checked for number of elements and whether > any elements are NULL, but that is done via array functions that take a > Datum input. Only in a few cases do we actually look at the actual intern= al > value of the Datum (reltuples, attname, attnum, the anyarrays), so there'= s > little to gain there. > > Right, so the question is whether it makes more sense to pass down C > strings or Datums. > Requiring Datums simplifies things greatly inside the existing functions, but pushes that complexity to the caller. My first proposal was to keep complexity to an absolute minimum on the caller side, but your comments make me think there's considerable tolerance for more complexity on the caller side. > > There's some additional hassle in the fact that > pg_restore_attribute_stats() can take an attnum parameter OR an attname > parameter, but not both. I was able to resolve that in a semi-elegant > fashion, but the other issues have convinced me that we're probably bette= r > off continuing to use the FunctionCallInfo version of > attribute_stats_update(), though perhaps with a different name, allowing = us > to use that name for the new API call instead of > import_attribute_statistics(). > > On that particular point, I think I'm still unconvinced, but I also > haven't looked deeply into this just yet, so maybe I'm wrong. > The issue becomes moot if we require the caller to construct their own isnull and values arrays as was suggested above. --000000000000e6a15a0654edc2b5 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Tue, Jun 23, 2026 at 8:11=E2=80=AFAM Rober= t Haas <robertmhaas@gmail.com> wrote:
On= Mon, Jun 22, 2026 at 3:08=E2=80=AFPM Corey Huinker <corey.huinker@gmail.com> w= rote:
> The first bit of dissonance comes from the SQL-level functions having = schemaname+relname parameters, and the reloid is resolved via RangeVarGetRe= lidExtended() which has a callback to check for correct permissions and set= ting the proper lock level. However, the C-caller would either have the rel= oid already, or an already open Relation, but no assurance that the caller = has the correct permissions for that table or the correct lock level on the= table. So either we make an equivalent to RangeVarGetRelidExtended() that = takes an oid, or the C-caller has to derive a RangeVar, call the existing R= angeVarGetRelidExtended() function, and verify the result reloid against th= e supplied parameter. I went with deriving the RangeVar and putting an Asse= rt on the before/after reloids, but perhaps the smarter play is to make a f= unction that checks for ShareUpdateExclusiveLock on the Relation, and then = does the equivalent of RangeVarCallbackForStats().

I don't think this is really a problem. If the caller is specifying
the OID, they should have called RangeVarGetRelidExtended themselves.
Permissions-checking, locking, and opening the relation should all
happen simultaneously, and the logic shouldn't be duplicated later.
=

It seems dangerous to me to provide an ext= ernally visible function that assumes the caller has already self-verified = that they have permission to modify stats for an object.

It's possible that the existing analyze() code already has verif= ied these permissions, but at this moment I'm unsure.
<= div>=C2=A0
> A smaller bit of dissonance was with RecoveryInProgress(), which if I = recall we're checking before RangeVarGetRelidExtended() to avoid trying= to take a lock that will fail. That check might not be meaningful if the C= call takes a relation, thus ensuring that some level of locking worked, th= us we aren't in recovery.

I don't quite follow this part.

I m= ean that I don't know if that check is required in situations where the= caller already has an open relation with the right lock, and if we can avo= id the call by having the API take a Relation rather than an Oid, then that= would be preferable.
=C2=A0

> Next is the existing validation functions stats_check_required_arg(), = stats_check_arg_array(), and stats_check_arg_pairs() all work on values ind= exed by the positional functioncallinfo and the corresponding relstatsinfo/= attstatsinfo structure, and this makes a lot of type checking and value che= cking compact and uniform.=C2=A0 If we want to keep this sort of uniformity= , the resulting StatsData structure will end up looking a lot like the Func= tionCallInfo that we already had.

Yeah, this is worth thinking about. You could consider putting an
array inside the struct and use #defines for the indexes. And instead
of having a separate Boolean for each index, you could use the same
index to reference the N'th bit of a single integer flag variable.

That's basically what I was trying, though = I wasn't brave enough to use bitflags in a v1 patch.

The #defines already exist now in the form of enums that act as inde= xes into the FunctionCallInfo array. But if my data structure is an array o= f Datums with an array of is-not-nulls, then I'm just not that far away= from the FunctionCallInfo structure we already have.
= =C2=A0

> Next is the fact that the end-destination for every value passed in is= a Datum for a pg_statistic heaptuple. Most Datum values are checked only f= or their null-ness and if they're the correct type, so the value itself= is usually just passed directly from fcinfo into the heap tuple values[] a= rray. The float[] values are checked for number of elements and whether any= elements are NULL, but that is done via array functions that take a Datum = input. Only in a few cases do we actually look at the actual internal value= of the Datum (reltuples, attname, attnum, the anyarrays), so there's l= ittle to gain there.

Right, so the question is whether it makes more sense to pass down C
strings or Datums.

Requiring Datums sim= plifies things greatly inside the existing functions, but pushes that compl= exity to the caller. My first proposal was to keep complexity to an absolut= e minimum on the caller side, but your comments make me think there's c= onsiderable tolerance for more complexity on the caller side.


> There's some additional hassle in the fact that pg_restore_attribu= te_stats() can take an attnum parameter OR an attname parameter, but not bo= th. I was able to resolve that in a semi-elegant fashion, but the other iss= ues have convinced me that we're probably better off continuing to use = the FunctionCallInfo version of attribute_stats_update(), though perhaps wi= th a different name, allowing us to use that name for the new API call inst= ead of import_attribute_statistics().

On that particular point, I think I'm still unconvinced, but I also
haven't looked deeply into this just yet, so maybe I'm wrong.

The issue becomes moot if we require the cal= ler to construct their own isnull and values arrays as was suggested above.=
--000000000000e6a15a0654edc2b5--