1
0
mirror of https://github.com/postgres/postgres.git synced 2026-01-26 09:41:40 +03:00

Record range constructor functions in pg_range

When a range type is created, several construction functions are also
created, two for the range type and three for the multirange type.
These have an internal dependency, so they "belong" to the range type.
But there was no way to identify those functions when given a range
type.  An upcoming patch needs access to the two- or possibly the
three-argument range constructor function for a given range type.  The
only way to do that would be with fragile workarounds like matching
names and argument types.  The correct way to do that kind of thing is
to record to the links in the system catalogs.  This is what this
patch does, it records the OIDs of these five constructor functions in
the pg_range catalog.  (Currently, there is no code that makes use of
this.)

Reviewed-by: Paul A Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://www.postgresql.org/message-id/7d63ddfa-c735-4dfe-8c7a-4f1e2a621058%40eisentraut.org
This commit is contained in:
Peter Eisentraut
2026-01-22 15:17:12 +01:00
parent a5b40d156e
commit c257ba8397
9 changed files with 231 additions and 18 deletions

View File

@@ -6676,6 +6676,60 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngconstruct2</structfield> <type>regproc</type>
(references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the 2-argument range constructor function (lower and upper)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngconstruct3</structfield> <type>regproc</type>
(references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the 3-argument range constructor function (lower, upper, and
flags)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngmltconstruct0</structfield> <type>regproc</type>
(references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the 0-argument multirange constructor function (constructs empty
range)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngmltconstruct1</structfield> <type>regproc</type>
(references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the 1-argument multirange constructor function (constructs
multirange from single range, also used as cast function)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngmltconstruct2</structfield> <type>regproc</type>
(references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the 2-argument multirange constructor function (constructs
multirange from array of ranges)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngcanonical</structfield> <type>regproc</type>

View File

@@ -35,7 +35,9 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
RegProcedure rangeSubDiff, Oid multirangeTypeOid)
RegProcedure rangeSubDiff, Oid multirangeTypeOid,
RegProcedure rangeConstruct2, RegProcedure rangeConstruct3,
RegProcedure mltrngConstruct0, RegProcedure mltrngConstruct1, RegProcedure mltrngConstruct2)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -57,6 +59,11 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
values[Anum_pg_range_rngconstruct2 - 1] = ObjectIdGetDatum(rangeConstruct2);
values[Anum_pg_range_rngconstruct3 - 1] = ObjectIdGetDatum(rangeConstruct3);
values[Anum_pg_range_rngmltconstruct0 - 1] = ObjectIdGetDatum(mltrngConstruct0);
values[Anum_pg_range_rngmltconstruct1 - 1] = ObjectIdGetDatum(mltrngConstruct1);
values[Anum_pg_range_rngmltconstruct2 - 1] = ObjectIdGetDatum(mltrngConstruct2);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);

View File

@@ -111,10 +111,12 @@ Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
Oid rangeOid, Oid subtype,
Oid *rangeConstruct2_p, Oid *rangeConstruct3_p);
static void makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeOid,
Oid rangeArrayOid, Oid *castFuncOid);
Oid rangeArrayOid,
Oid *mltrngConstruct0_p, Oid *mltrngConstruct1_p, Oid *mltrngConstruct2_p);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -1406,6 +1408,11 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
ListCell *lc;
ObjectAddress address;
ObjectAddress mltrngaddress PG_USED_FOR_ASSERTS_ONLY;
Oid rangeConstruct2Oid = InvalidOid;
Oid rangeConstruct3Oid = InvalidOid;
Oid mltrngConstruct0Oid = InvalidOid;
Oid mltrngConstruct1Oid = InvalidOid;
Oid mltrngConstruct2Oid = InvalidOid;
Oid castFuncOid;
/* Convert list of names to a name and namespace */
@@ -1661,10 +1668,6 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(multirangeOid == mltrngaddress.objectId);
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
*/
@@ -1746,10 +1749,18 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
CommandCounterIncrement();
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype,
&rangeConstruct2Oid, &rangeConstruct3Oid);
makeMultirangeConstructors(multirangeTypeName, typeNamespace,
multirangeOid, typoid, rangeArrayOid,
&castFuncOid);
&mltrngConstruct0Oid, &mltrngConstruct1Oid, &mltrngConstruct2Oid);
castFuncOid = mltrngConstruct1Oid;
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
rangeCanonical, rangeSubtypeDiff, multirangeOid,
rangeConstruct2Oid, rangeConstruct3Oid,
mltrngConstruct0Oid, mltrngConstruct1Oid, mltrngConstruct2Oid);
/* Create cast from the range type to its multirange type */
CastCreate(typoid, multirangeOid, castFuncOid, InvalidOid, InvalidOid,
@@ -1769,10 +1780,14 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
*
* We actually define 2 functions, with 2 through 3 arguments. This is just
* to offer more convenience for the user.
*
* The OIDs of the created functions are returned through the pointer
* arguments.
*/
static void
makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype)
Oid rangeOid, Oid subtype,
Oid *rangeConstruct2_p, Oid *rangeConstruct3_p)
{
static const char *const prosrc[2] = {"range_constructor2",
"range_constructor3"};
@@ -1833,6 +1848,11 @@ makeRangeConstructors(const char *name, Oid namespace,
* pg_dump depends on this choice to avoid dumping the constructors.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
if (pronargs[i] == 2)
*rangeConstruct2_p = myself.objectId;
else if (pronargs[i] == 3)
*rangeConstruct3_p = myself.objectId;
}
}
@@ -1842,13 +1862,13 @@ makeRangeConstructors(const char *name, Oid namespace,
* If we had an anyrangearray polymorphic type we could use it here,
* but since each type has its own constructor name there's no need.
*
* Sets castFuncOid to the oid of the new constructor that can be used
* to cast from a range to a multirange.
* The OIDs of the created functions are returned through the pointer
* arguments.
*/
static void
makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
Oid *castFuncOid)
Oid *mltrngConstruct0_p, Oid *mltrngConstruct1_p, Oid *mltrngConstruct2_p)
{
ObjectAddress myself,
referenced;
@@ -1899,6 +1919,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
* depends on this choice to avoid dumping the constructors.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
*mltrngConstruct0_p = myself.objectId;
pfree(argtypes);
/*
@@ -1939,8 +1960,8 @@ makeMultirangeConstructors(const char *name, Oid namespace,
0.0); /* prorows */
/* ditto */
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
*mltrngConstruct1_p = myself.objectId;
pfree(argtypes);
*castFuncOid = myself.objectId;
/* n-arg constructor - vararg */
argtypes = buildoidvector(&rangeArrayOid, 1);
@@ -1978,6 +1999,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
0.0); /* prorows */
/* ditto */
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
*mltrngConstruct2_p = myself.objectId;
pfree(argtypes);
pfree(allParameterTypes);
pfree(parameterModes);

View File

@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202601161
#define CATALOG_VERSION_NO 202601221
#endif

View File

@@ -14,21 +14,33 @@
{ rngtypid => 'int4range', rngsubtype => 'int4',
rngmultitypid => 'int4multirange', rngsubopc => 'btree/int4_ops',
rngconstruct2 => 'int4range(int4,int4)', rngconstruct3 => 'int4range(int4,int4,text)',
rngmltconstruct0 => 'int4multirange()', rngmltconstruct1 => 'int4multirange(int4range)', rngmltconstruct2 => 'int4multirange(_int4range)',
rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngmultitypid => 'nummultirange', rngsubopc => 'btree/numeric_ops',
rngconstruct2 => 'numrange(numeric,numeric)', rngconstruct3 => 'numrange(numeric,numeric,text)',
rngmltconstruct0 => 'nummultirange()', rngmltconstruct1 => 'nummultirange(numrange)', rngmltconstruct2 => 'nummultirange(_numrange)',
rngcanonical => '-', rngsubdiff => 'numrange_subdiff' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngmultitypid => 'tsmultirange', rngsubopc => 'btree/timestamp_ops',
rngconstruct2 => 'tsrange(timestamp,timestamp)', rngconstruct3 => 'tsrange(timestamp,timestamp,text)',
rngmltconstruct0 => 'tsmultirange()', rngmltconstruct1 => 'tsmultirange(tsrange)', rngmltconstruct2 => 'tsmultirange(_tsrange)',
rngcanonical => '-', rngsubdiff => 'tsrange_subdiff' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngmultitypid => 'tstzmultirange', rngsubopc => 'btree/timestamptz_ops',
rngconstruct2 => 'tstzrange(timestamptz,timestamptz)', rngconstruct3 => 'tstzrange(timestamptz,timestamptz,text)',
rngmltconstruct0 => 'tstzmultirange()', rngmltconstruct1 => 'tstzmultirange(tstzrange)', rngmltconstruct2 => 'tstzmultirange(_tstzrange)',
rngcanonical => '-', rngsubdiff => 'tstzrange_subdiff' },
{ rngtypid => 'daterange', rngsubtype => 'date',
rngmultitypid => 'datemultirange', rngsubopc => 'btree/date_ops',
rngconstruct2 => 'daterange(date,date)', rngconstruct3 => 'daterange(date,date,text)',
rngmltconstruct0 => 'datemultirange()', rngmltconstruct1 => 'datemultirange(daterange)', rngmltconstruct2 => 'datemultirange(_daterange)',
rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
{ rngtypid => 'int8range', rngsubtype => 'int8',
rngmultitypid => 'int8multirange', rngsubopc => 'btree/int8_ops',
rngconstruct2 => 'int8range(int8,int8)', rngconstruct3 => 'int8range(int8,int8,text)',
rngmltconstruct0 => 'int8multirange()', rngmltconstruct1 => 'int8multirange(int8range)', rngmltconstruct2 => 'int8multirange(_int8range)',
rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
]

View File

@@ -43,6 +43,15 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype's btree opclass */
Oid rngsubopc BKI_LOOKUP(pg_opclass);
/* range constructor functions */
regproc rngconstruct2 BKI_LOOKUP(pg_proc);
regproc rngconstruct3 BKI_LOOKUP(pg_proc);
/* multirange constructor functions */
regproc rngmltconstruct0 BKI_LOOKUP(pg_proc);
regproc rngmltconstruct1 BKI_LOOKUP(pg_proc);
regproc rngmltconstruct2 BKI_LOOKUP(pg_proc);
/* canonicalize range, or 0 */
regproc rngcanonical BKI_LOOKUP_OPT(pg_proc);
@@ -69,7 +78,9 @@ MAKE_SYSCACHE(RANGEMULTIRANGE, pg_range_rngmultitypid_index, 4);
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
RegProcedure rangeSubDiff, Oid multirangeTypeOid);
RegProcedure rangeSubDiff, Oid multirangeTypeOid,
RegProcedure rangeConstruct2, RegProcedure rangeConstruct3,
RegProcedure mltrngConstruct0, RegProcedure mltrngConstruct1, RegProcedure mltrngConstruct2);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */

View File

@@ -249,6 +249,11 @@ NOTICE: checking pg_range {rngsubtype} => pg_type {oid}
NOTICE: checking pg_range {rngmultitypid} => pg_type {oid}
NOTICE: checking pg_range {rngcollation} => pg_collation {oid}
NOTICE: checking pg_range {rngsubopc} => pg_opclass {oid}
NOTICE: checking pg_range {rngconstruct2} => pg_proc {oid}
NOTICE: checking pg_range {rngconstruct3} => pg_proc {oid}
NOTICE: checking pg_range {rngmltconstruct0} => pg_proc {oid}
NOTICE: checking pg_range {rngmltconstruct1} => pg_proc {oid}
NOTICE: checking pg_range {rngmltconstruct2} => pg_proc {oid}
NOTICE: checking pg_range {rngcanonical} => pg_proc {oid}
NOTICE: checking pg_range {rngsubdiff} => pg_proc {oid}
NOTICE: checking pg_transform {trftype} => pg_type {oid}

View File

@@ -610,7 +610,9 @@ WHERE (is_catalog_text_unique_index_oid(indexrelid) <>
-- Look for illegal values in pg_range fields.
SELECT r.rngtypid, r.rngsubtype
FROM pg_range as r
WHERE r.rngtypid = 0 OR r.rngsubtype = 0 OR r.rngsubopc = 0;
WHERE r.rngtypid = 0 OR r.rngsubtype = 0 OR r.rngsubopc = 0
OR r.rngconstruct2 = 0 OR r.rngconstruct3 = 0
OR r.rngmltconstruct0 = 0 OR r.rngmltconstruct1 = 0 OR r.rngmltconstruct2 = 0;
rngtypid | rngsubtype
----------+------------
(0 rows)
@@ -663,6 +665,61 @@ WHERE r.rngmultitypid IS NULL OR r.rngmultitypid = 0;
----------+------------+---------------
(0 rows)
-- check constructor function arguments and return types
--
-- proname and prosrc are not required to have these particular
-- values, but this matches what DefineRange() produces and serves to
-- sanity-check the catalog entries for built-in types.
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngconstruct2 JOIN pg_type t ON r.rngtypid = t.oid
WHERE p.pronargs != 2
OR p.proargtypes[0] != r.rngsubtype OR p.proargtypes[1] != r.rngsubtype
OR p.prorettype != r.rngtypid
OR p.proname != t.typname OR p.prosrc != 'range_constructor2';
rngtypid | rngsubtype | proname
----------+------------+---------
(0 rows)
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngconstruct3 JOIN pg_type t ON r.rngtypid = t.oid
WHERE p.pronargs != 3
OR p.proargtypes[0] != r.rngsubtype OR p.proargtypes[1] != r.rngsubtype OR p.proargtypes[2] != 'pg_catalog.text'::regtype
OR p.prorettype != r.rngtypid
OR p.proname != t.typname OR p.prosrc != 'range_constructor3';
rngtypid | rngsubtype | proname
----------+------------+---------
(0 rows)
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct0 JOIN pg_type t ON r.rngmultitypid = t.oid
WHERE p.pronargs != 0
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor0';
rngtypid | rngsubtype | proname
----------+------------+---------
(0 rows)
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct1 JOIN pg_type t ON r.rngmultitypid = t.oid
WHERE p.pronargs != 1
OR p.proargtypes[0] != r.rngtypid
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor1';
rngtypid | rngsubtype | proname
----------+------------+---------
(0 rows)
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct2 JOIN pg_type t ON r.rngmultitypid = t.oid JOIN pg_type t2 ON r.rngtypid = t2.oid
WHERE p.pronargs != 1
OR p.proargtypes[0] != t2.typarray
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor2';
rngtypid | rngsubtype | proname
----------+------------+---------
(0 rows)
-- ******************************************
-- Create a table that holds all the known in-core data types and leave it
-- around so as pg_upgrade is able to test their binary compatibility.
CREATE TABLE tab_core_types AS SELECT

View File

@@ -451,7 +451,9 @@ WHERE (is_catalog_text_unique_index_oid(indexrelid) <>
SELECT r.rngtypid, r.rngsubtype
FROM pg_range as r
WHERE r.rngtypid = 0 OR r.rngsubtype = 0 OR r.rngsubopc = 0;
WHERE r.rngtypid = 0 OR r.rngsubtype = 0 OR r.rngsubopc = 0
OR r.rngconstruct2 = 0 OR r.rngconstruct3 = 0
OR r.rngmltconstruct0 = 0 OR r.rngmltconstruct1 = 0 OR r.rngmltconstruct2 = 0;
-- rngcollation should be specified iff subtype is collatable
@@ -491,6 +493,49 @@ SELECT r.rngtypid, r.rngsubtype, r.rngmultitypid
FROM pg_range r
WHERE r.rngmultitypid IS NULL OR r.rngmultitypid = 0;
-- check constructor function arguments and return types
--
-- proname and prosrc are not required to have these particular
-- values, but this matches what DefineRange() produces and serves to
-- sanity-check the catalog entries for built-in types.
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngconstruct2 JOIN pg_type t ON r.rngtypid = t.oid
WHERE p.pronargs != 2
OR p.proargtypes[0] != r.rngsubtype OR p.proargtypes[1] != r.rngsubtype
OR p.prorettype != r.rngtypid
OR p.proname != t.typname OR p.prosrc != 'range_constructor2';
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngconstruct3 JOIN pg_type t ON r.rngtypid = t.oid
WHERE p.pronargs != 3
OR p.proargtypes[0] != r.rngsubtype OR p.proargtypes[1] != r.rngsubtype OR p.proargtypes[2] != 'pg_catalog.text'::regtype
OR p.prorettype != r.rngtypid
OR p.proname != t.typname OR p.prosrc != 'range_constructor3';
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct0 JOIN pg_type t ON r.rngmultitypid = t.oid
WHERE p.pronargs != 0
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor0';
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct1 JOIN pg_type t ON r.rngmultitypid = t.oid
WHERE p.pronargs != 1
OR p.proargtypes[0] != r.rngtypid
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor1';
SELECT r.rngtypid, r.rngsubtype, p.proname
FROM pg_range r JOIN pg_proc p ON p.oid = r.rngmltconstruct2 JOIN pg_type t ON r.rngmultitypid = t.oid JOIN pg_type t2 ON r.rngtypid = t2.oid
WHERE p.pronargs != 1
OR p.proargtypes[0] != t2.typarray
OR p.prorettype != r.rngmultitypid
OR p.proname != t.typname OR p.prosrc != 'multirange_constructor2';
-- ******************************************
-- Create a table that holds all the known in-core data types and leave it
-- around so as pg_upgrade is able to test their binary compatibility.
CREATE TABLE tab_core_types AS SELECT