commit 4376af40df1 Author: Andrew Dunstan Date: Fri Mar 6 09:53:23 2026 -0500 Allow NULL defaults for pseudo-type parameters in pg_proc.dat InsertOneProargdefaultsValue() called boot_get_type_io_data() for every default value entry, including NULLs. This failed for types not present in the bootstrap TypInfo[] array (such as pseudo-type "any"), even though NULL defaults never need a type input function. Fix by handling NULL defaults separately: look up the type metadata (typlen, typbyval, typcollation) from TypInfo or the Typ cache if available, so that known types get correct Const node metadata matching what the SQL parser would produce. For types not found (e.g. "any" during early bootstrap), fall back to "unknown" type, which matches the SQL parser's treatment of untyped NULL literals in CREATE FUNCTION ... DEFAULT NULL. This enables functions with VARIADIC "any" DEFAULT NULL to specify their defaults directly in pg_proc.dat, without needing a system_functions.sql wrapper. diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e7699be55aa..f979d44a640 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -772,19 +772,70 @@ InsertOneProargdefaultsValue(char *value) bool defnull; Const *defConst; - boot_get_type_io_data(argtype, - &typlen, &typbyval, &typalign, - &typdelim, &typioparam, - &typinput, &typoutput, - &typcollation); - defnull = array_nulls[i]; if (defnull) + { + /* + * For NULL defaults, we need the type metadata (typlen etc) + * but not the I/O functions. Look up the type if possible; + * if it's not available yet (e.g. pseudo-types not in + * TypInfo[]), fall back to "unknown" type, matching what + * the SQL parser produces for untyped NULL literals. + */ + bool found = false; + defval = (Datum) 0; + if (Typ != NIL) + { + ListCell *lc; + + foreach(lc, Typ) + { + struct typmap *ap = lfirst(lc); + + if (ap->am_oid == argtype) + { + typlen = ap->am_typ.typlen; + typbyval = ap->am_typ.typbyval; + typcollation = ap->am_typ.typcollation; + found = true; + break; + } + } + } + else + { + for (int j = 0; j < n_types; j++) + { + if (TypInfo[j].oid == argtype) + { + typlen = TypInfo[j].len; + typbyval = TypInfo[j].byval; + typcollation = TypInfo[j].collation; + found = true; + break; + } + } + } + if (!found) + { + argtype = UNKNOWNOID; + typlen = -2; + typbyval = false; + typcollation = InvalidOid; + } + } else + { + boot_get_type_io_data(argtype, + &typlen, &typbyval, &typalign, + &typdelim, &typioparam, + &typinput, &typoutput, + &typcollation); defval = OidInputFunctionCall(typinput, DatumGetCString(array_datums[i]), typioparam, -1); + } defConst = makeConst(argtype, -1, /* never any typmod */