Convert *GetDatum() and DatumGet*() macros to inline functions

Started by Peter Eisentrautover 3 years ago21 messageshackers
Jump to latest
#1Peter Eisentraut
peter_e@gmx.net

I once wrote code like this:

char *oid = get_from_somewhere();
...

values[i++] = ObjectIdGetDatum(oid);

This compiles cleanly and even appears to work in practice, except of
course it doesn't.

The FooGetDatum() macros just cast whatever you give it to Datum,
without checking whether the input was really foo.

To address this, I converted these macros to inline functions, which
enables type checking of the input argument. For symmetry, I also
converted the corresponding DatumGetFoo() macros (but those are less
likely to cover mistakes, since the input argument is always Datum).
This is patch 0002.

(I left some of the DatumGet... of the varlena types in fmgr.h as
macros. These ultimately map to functions that do type checking, so
there would be little more to be learnt from that. But we could do
those for consistency as well.)

This whole thing threw up a bunch of compiler warnings and errors, which
revealed a number of existing misuses. These are fixed in patch 0001.
These include

- using FooGetDatum on things that are already Datum,

- using DatumGetPointer on things that are already pointers,

- using PG_RETURN_TYPE on things that are Datum,

- using PG_RETURN_TYPE of the wrong type,

and others, including my personal favorite:

- using PointerGetDatum where DatumGetPointer should be used.

(AFAICT, unlike my initial example, I don't think any of those would
cause wrong behavior.)

Attachments:

0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchtext/plain; charset=UTF-8; name=0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchDownload+33-34
0002-Convert-GetDatum-and-DatumGet-macros-to-inline-funct.patchtext/plain; charset=UTF-8; name=0002-Convert-GetDatum-and-DatumGet-macros-to-inline-funct.patchDownload+655-187
#2Aleksander Alekseev
aleksander@timescale.com
In reply to: Peter Eisentraut (#1)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi Peter,

To address this, I converted these macros to inline functions

This is a great change!

I encountered passing the wrong arguments to these macros many times,
and this is indeed pretty annoying. I wish we could forbid doing other
stupid things as well, e.g. comparing two Datum's directly, which for
Timestamps works just fine but only on 64-bit platforms. Although this
is certainly out of scope of this thread.

The patch looks good to me, I merely added a link to the discussion. I
added it to the CF application. Cfbot is making its mind at the moment
of writing.

Do you think this should be backported?

--
Best regards,
Aleksander Alekseev

Attachments:

v2-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchapplication/octet-stream; name=v2-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchDownload+33-34
v2-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchapplication/octet-stream; name=v2-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchDownload+655-187
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#2)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Aleksander Alekseev <aleksander@timescale.com> writes:

Do you think this should be backported?

Impossible, it's an ABI break.

regards, tom lane

#4Aleksander Alekseev
aleksander@timescale.com
In reply to: Tom Lane (#3)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi Tom,

Do you think this should be backported?

Impossible, it's an ABI break.

OK, got it.

Just to clarify, a break in this case is going to be the fact that we
are adding new functions, although inlined, correct? Or maybe
something else? I'm sorry this is the first time I encounter the
question of ABI compatibility in the context of Postgres, so I would
appreciate it if you could elaborate a bit.

--
Best regards,
Aleksander Alekseev

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#4)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Aleksander Alekseev <aleksander@timescale.com> writes:

Just to clarify, a break in this case is going to be the fact that we
are adding new functions, although inlined, correct? Or maybe
something else? I'm sorry this is the first time I encounter the
question of ABI compatibility in the context of Postgres, so I would
appreciate it if you could elaborate a bit.

After absorbing a bit more caffeine, I suppose that replacing a
macro with a "static inline" function would not be an ABI break,
at least not with most modern compilers, because the code should
end up the same. I'd still vote against back-patching though.
I don't think the risk-reward ratio is good, especially not for
the pre-C99 branches which don't necessarily have "inline".

regards, tom lane

#6Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#5)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On Tue, Aug 30, 2022 at 10:13 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Aleksander Alekseev <aleksander@timescale.com> writes:

Just to clarify, a break in this case is going to be the fact that we
are adding new functions, although inlined, correct? Or maybe
something else? I'm sorry this is the first time I encounter the
question of ABI compatibility in the context of Postgres, so I would
appreciate it if you could elaborate a bit.

After absorbing a bit more caffeine, I suppose that replacing a
macro with a "static inline" function would not be an ABI break,
at least not with most modern compilers, because the code should
end up the same. I'd still vote against back-patching though.
I don't think the risk-reward ratio is good, especially not for
the pre-C99 branches which don't necessarily have "inline".

Yeah, I don't see a reason to back-patch a change like this, certainly
not right away. If over time it turns out that the different
definitions on different branches cause too many headaches, we could
reconsider. However, I'm not sure that will happen, because the whole
point is that the static inline functions are intended to behave in
the same way as the macros, just with better type-checking.

--
Robert Haas
EDB: http://www.enterprisedb.com

#7Aleksander Alekseev
aleksander@timescale.com
In reply to: Robert Haas (#6)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Tom, Robert,

Yeah, I don't see a reason to back-patch a change like this

Maybe we should consider backporting at least 0001 patch, partially
perhaps? I believe if fixes pretty cursed pieces of code, e.g:

```
             pg_cryptohash_ctx *context =
-            (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+            (pg_cryptohash_ctx *) DatumGetPointer(foundres);
```

This being said, personally I don't have a strong opinion here. After
all, the code works and passes the tests. Maybe I'm just being a
perfectionist here.

--
Best regards,
Aleksander Alekseev

#8Robert Haas
robertmhaas@gmail.com
In reply to: Aleksander Alekseev (#7)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On Tue, Aug 30, 2022 at 10:25 AM Aleksander Alekseev
<aleksander@timescale.com> wrote:

Yeah, I don't see a reason to back-patch a change like this

Maybe we should consider backporting at least 0001 patch, partially
perhaps? I believe if fixes pretty cursed pieces of code, e.g:

```
pg_cryptohash_ctx *context =
-            (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+            (pg_cryptohash_ctx *) DatumGetPointer(foundres);
```

Sure, back-porting the bug fixes would make sense to me.

--
Robert Haas
EDB: http://www.enterprisedb.com

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#7)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Aleksander Alekseev <aleksander@timescale.com> writes:

Maybe we should consider backporting at least 0001 patch, partially
perhaps? I believe if fixes pretty cursed pieces of code, e.g:

Certainly if there are any parts of it that fix actual bugs,
we ought to backport those. I'm not in a big hurry to backport
cosmetic fixes though.

regards, tom lane

#10Aleksander Alekseev
aleksander@timescale.com
In reply to: Tom Lane (#9)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi hackers,

Cfbot is making its mind at the moment of writing.

Here is v3 with silenced compiler warnings.

--
Best regards,
Aleksander Alekseev

Attachments:

v3-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchapplication/octet-stream; name=v3-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchDownload+658-190
v3-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchapplication/octet-stream; name=v3-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchDownload+33-34
#11Aleksander Alekseev
aleksander@timescale.com
In reply to: Aleksander Alekseev (#10)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi hackers,

Here is v3 with silenced compiler warnings.

Some more warnings were reported by cfbot, so here is v4. Apologies
for the noise.

--
Best regards,
Aleksander Alekseev

Attachments:

v4-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchapplication/octet-stream; name=v4-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patchDownload+33-34
v4-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchapplication/octet-stream; name=v4-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchDownload+659-191
#12Peter Eisentraut
peter_e@gmx.net
In reply to: Aleksander Alekseev (#11)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On 30.08.22 20:15, Aleksander Alekseev wrote:

Here is v3 with silenced compiler warnings.

Some more warnings were reported by cfbot, so here is v4. Apologies
for the noise.

Looking at these warnings you are fixing, I think there is a small
problem we need to address.

I have defined PointerGetDatum() with a const argument:

PointerGetDatum(const void *X)

This is because in some places the thing that is being passed into that
is itself defined as const, so this is the clean way to avoid warnings
about dropping constness.

However, some support functions for gist and text search pass back
return values via pointer arguments, like

DirectFunctionCall3(g_int_same,
entry->key,
PointerGetDatum(query),
PointerGetDatum(&retval));

The compiler you are using apparently thinks that passing &retval to a
const pointer argument cannot change retval, which seems quite
reasonable. But that isn't actually what's happening here, so we're
lying a bit.

(Which compiler is that, by the way?)

I think to resolve that we could either

1. Not define PointerGetDatum() with a const argument, and just sprinkle
in a few unconstify calls where necessary.

2. Maybe add a NonconstPointerGetDatum() for those few cases where
pointer arguments are used for return values.

3. Go with your patch and just fix up the warnings about uninitialized
variables. But that seems the least principled to me.

#13Aleksander Alekseev
aleksander@timescale.com
In reply to: Peter Eisentraut (#12)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi Peter,

Which compiler is that, by the way?

The warnings were reported by cfbot during the "clang_warning" step.
According to the logs:

```
using compiler=Debian clang version 11.0.1-2
```

Personally I use Clang 14 on MacOS and I don't get these warnings.

I think to resolve that we could either

1. Not define PointerGetDatum() with a const argument, and just sprinkle
in a few unconstify calls where necessary.

2. Maybe add a NonconstPointerGetDatum() for those few cases where
pointer arguments are used for return values.

3. Go with your patch and just fix up the warnings about uninitialized
variables. But that seems the least principled to me.

IMO the 3rd option is the lesser evil. Initializing four bools/ints in
order to make Clang 11 happy doesn't strike me as such a big deal. At
least until somebody reports a bottleneck for this particular reason.
We can optimize the code when and if this will happen.

--
Best regards,
Aleksander Alekseev

#14Aleksander Alekseev
aleksander@timescale.com
In reply to: Aleksander Alekseev (#13)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi Peter,

3. Go with your patch and just fix up the warnings about uninitialized
variables. But that seems the least principled to me.

IMO the 3rd option is the lesser evil. Initializing four bools/ints in
order to make Clang 11 happy doesn't strike me as such a big deal. At
least until somebody reports a bottleneck for this particular reason.
We can optimize the code when and if this will happen.

Since the first patch was applied, cfbot now complains that it can't
apply the patchset. Here is the rebased version.

--
Best regards,
Aleksander Alekseev

Attachments:

v5-0001-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchapplication/octet-stream; name=v5-0001-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patchDownload+659-191
#15Peter Eisentraut
peter_e@gmx.net
In reply to: Aleksander Alekseev (#14)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On 08.09.22 11:26, Aleksander Alekseev wrote:

3. Go with your patch and just fix up the warnings about uninitialized
variables. But that seems the least principled to me.

IMO the 3rd option is the lesser evil. Initializing four bools/ints in
order to make Clang 11 happy doesn't strike me as such a big deal. At
least until somebody reports a bottleneck for this particular reason.
We can optimize the code when and if this will happen.

Since the first patch was applied, cfbot now complains that it can't
apply the patchset. Here is the rebased version.

committed, thanks

#16Julien Rouhaud
rjuju123@gmail.com
In reply to: Peter Eisentraut (#15)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Hi,

On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:

committed, thanks

FTR lapwing is complaining about this commit:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&amp;dt=2022-09-12%2016%3A40%3A18.

Snapper is also failing with similar problems:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&amp;dt=2022-09-12%2016%3A42%3A10

#17Peter Eisentraut
peter_e@gmx.net
In reply to: Julien Rouhaud (#16)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On 12.09.22 19:03, Julien Rouhaud wrote:

On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:

committed, thanks

FTR lapwing is complaining about this commit:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&amp;dt=2022-09-12%2016%3A40%3A18.

Snapper is also failing with similar problems:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&amp;dt=2022-09-12%2016%3A42%3A10

Ok, it has problems with 32-bit platforms. I can reproduce it locally.
I'll need to take another look at this. I have reverted the patch for now.

#18Peter Eisentraut
peter_e@gmx.net
In reply to: Peter Eisentraut (#17)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On 12.09.22 19:59, Peter Eisentraut wrote:

On 12.09.22 19:03, Julien Rouhaud wrote:

On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:

committed, thanks

FTR lapwing is complaining about this commit:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&amp;dt=2022-09-12%2016%3A40%3A18.

Snapper is also failing with similar problems:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&amp;dt=2022-09-12%2016%3A42%3A10

Ok, it has problems with 32-bit platforms.  I can reproduce it locally.
I'll need to take another look at this.  I have reverted the patch for now.

I have tried to analyze these issues, but I'm quite stuck. If anyone
else has any ideas, it would be helpful.

#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#18)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:

Ok, it has problems with 32-bit platforms.  I can reproduce it locally.
I'll need to take another look at this.  I have reverted the patch for now.

I have tried to analyze these issues, but I'm quite stuck. If anyone
else has any ideas, it would be helpful.

It looks to me like the problem is with the rewrite of Int64GetDatumFast
and Float8GetDatumFast:

+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+	return Int64GetDatum(X);
+#else
+	return PointerGetDatum(&X);
+#endif
+}

In the by-ref code path, this is going to return the address of the
parameter local variable, which of course is broken as soon as the
function exits. To test, I reverted the mods to those two macros,
and I got through check-world OK in a 32-bit VM.

I think we can do this while still having reasonable type-safety
by adding AssertVariableIsOfTypeMacro() checks to the macros.
An advantage of that solution is that we verify that the code
will be safe for a 32-bit build even in 64-bit builds. (Of
course, it's just checking the variable's type not its lifespan,
but this is still a step forward.)

0001 attached is what you committed, 0002 is a proposed delta
to fix the Fast macros.

regards, tom lane

Attachments:

0001-original-commit.patchtext/x-diff; charset=us-ascii; name=0001-original-commit.patchDownload+659-190
0002-fix-fast-macros.patchtext/x-diff; charset=us-ascii; name=0002-fix-fast-macros.patchDownload+11-19
#20Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#19)
Re: Convert *GetDatum() and DatumGet*() macros to inline functions

On 26.09.22 19:34, Tom Lane wrote:

I think we can do this while still having reasonable type-safety
by adding AssertVariableIsOfTypeMacro() checks to the macros.
An advantage of that solution is that we verify that the code
will be safe for a 32-bit build even in 64-bit builds. (Of
course, it's just checking the variable's type not its lifespan,
but this is still a step forward.)

0001 attached is what you committed, 0002 is a proposed delta
to fix the Fast macros.

Thanks, I committed it like that.

(I had looked into AssertVariableIsOfTypeMacro() for an earlier variant
of this patch, before I had the idea with the inline functions. It's in
general a bit too strict, such as with short vs int, and signed vs
unsigned, but it should work ok for this limited set of uses.)

#21Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#20)