Hi, On 2019-11-02 08:46:12 +0100, Peter Eisentraut wrote: > On 2019-11-01 15:41, Robert Haas wrote: > > On a related note, why do we store typbyval in the catalog anyway > > instead of inferring it from typlen and maybe typalign? It seems like > > a bad idea to record on disk the way we pass around values in memory, > > because it means that a change to how values are passed around in > > memory has ramifications for on-disk compatibility. > > This sounds interesting. It would remove a pg_upgrade hazard (in the long > run). > > There is some backward compatibility to be concerned about. This change > would require extension authors to change their code to insert #ifdef > USE_FLOAT8_BYVAL or similar, where currently their code might only support > one method or the other.
I think we really ought to remove the difference behind macros. That is, for types that could be either, provide macros that fetch function arguments into local memory, independent of whether the argument is a byval or byref type. I.e. instead of having separate #ifdef USE_FLOAT8_BYVALs for DatumGetFloat8(), DatumGetInt64(), ... we should provide that logic in one centralized set of macros. The fact that USE_FLOAT8_BYVAL has to creep into various functions imo is the reasons why people are unhappy about it. One way to do this would be something roughly like this sketch: /* allow to force types to be byref, for testing purposes */ #if USE_FLOAT8_BYVAL #define DatumForTypeIsByval(type) (sizeof(type) <= SIZEOF_DATUM) #else #define DatumForTypeIsByval(type) (sizeof(type) <= sizeof(int)) #endif /* yes, I dream of being C++ once grown up */ #define DefineSmallFixedWidthDatumTypeConversions(type, TypeToDatumName, DatumToTypeName) \ static inline type \ TypeToDatumName (type value) \ { \ if (DatumForTypeIsByval(type)) \ { \ Datum tmp; \ \ /* ensure correct conversion, consider e.g. float */ \ memcpy(&tmp, &value, sizeof(type)); \ \ return tmp; \ } \ else \ { \ type *tmp = (type *) palloc(sizeof(datum)); \ *tmp = value; return PointerGetDatum(tmp); \ } \ } \ \ static inline type \ DatumToTypeName (Datum datum) \ { \ if (DatumForTypeIsByval(type)) \ { \ type tmp; \ \ /* ensure correct conversion */ \ memcpy(&tmp, &datum, sizeof(type)); \ \ return tmp; \ } \ else \ return *(type *) DatumGetPointer(type); \ } \ And then have DefineSmallFixedWidthDatumTypeConversions( float8, Float8GetDatum, DatumGetFloat8); DefineSmallFixedWidthDatumTypeConversions( int64, Int64GetDatum, DatumGetInt64); And now also DefineSmallFixedWidthDatumTypeConversions( macaddr, Macaddr8GetDatum, DatumGetMacaddr8); (there's probably plenty of bugs in the above, it's just a sketch) We don't have to break types / extensions with inferring byval for fixed width types. Instead we can change CREATE TYPE's PASSEDBYVALUE to accept an optional parameter 'auto', allowing to opt in. > > rhaas=# select typname, typlen, typbyval, typalign from pg_type where > > typlen in (1,2,4,8) != typbyval; > > There are also typlen=6 types. Who knew. ;-) There's a recent thread about them :) > > typname | typlen | typbyval | typalign > > ----------+--------+----------+---------- > > macaddr8 | 8 | f | i > > (1 row) > > This might be a case of the above issue: It's easier to just make it pass by > reference always than deal with a bunch of #ifdefs. Indeed. And that's a bad sign imo. Greetings, Andres Freund