diff options
Diffstat (limited to 'src/backend/commands/typecmds.c')
-rw-r--r-- | src/backend/commands/typecmds.c | 357 |
1 files changed, 354 insertions, 3 deletions
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 7c0b2c3bf02..0fcd8c8f16e 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -107,9 +107,14 @@ typedef struct /* Potentially set by pg_upgrade_support functions */ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid; +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); +static void makeMultirangeConstructors(const char *name, Oid namespace, + Oid multirangeOid, Oid rangeOid, + Oid rangeArrayOid, Oid *castFuncOid); static Oid findTypeInputFunction(List *procname, Oid typeOid); static Oid findTypeOutputFunction(List *procname, Oid typeOid); static Oid findTypeReceiveFunction(List *procname, Oid typeOid); @@ -772,7 +777,8 @@ DefineDomain(CreateDomainStmt *stmt) typtype != TYPTYPE_COMPOSITE && typtype != TYPTYPE_DOMAIN && typtype != TYPTYPE_ENUM && - typtype != TYPTYPE_RANGE) + typtype != TYPTYPE_RANGE && + typtype != TYPTYPE_MULTIRANGE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("\"%s\" is not a valid base type for a domain", @@ -1323,6 +1329,11 @@ checkEnumOwner(HeapTuple tup) /* * DefineRange * Registers a new range type. + * + * Perhaps it might be worthwhile to set pg_type.typelem to the base type, + * and likewise on multiranges to set it to the range type. But having a + * non-zero typelem is treated elsewhere as a synonym for being an array, + * and users might have queries with that same assumption. */ ObjectAddress DefineRange(CreateRangeStmt *stmt) @@ -1331,7 +1342,12 @@ DefineRange(CreateRangeStmt *stmt) Oid typeNamespace; Oid typoid; char *rangeArrayName; + char *multirangeTypeName = NULL; + char *multirangeArrayName; + Oid multirangeNamespace = InvalidOid; Oid rangeArrayOid; + Oid multirangeOid; + Oid multirangeArrayOid; Oid rangeSubtype = InvalidOid; List *rangeSubOpclassName = NIL; List *rangeCollationName = NIL; @@ -1348,6 +1364,8 @@ DefineRange(CreateRangeStmt *stmt) AclResult aclresult; ListCell *lc; ObjectAddress address; + ObjectAddress mltrngaddress; + Oid castFuncOid; /* Convert list of names to a name and namespace */ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, @@ -1431,6 +1449,16 @@ DefineRange(CreateRangeStmt *stmt) errmsg("conflicting or redundant options"))); rangeSubtypeDiffName = defGetQualifiedName(defel); } + else if (strcmp(defel->defname, "multirange_type_name") == 0) + { + if (multirangeTypeName != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + /* we can look up the subtype name immediately */ + multirangeNamespace = QualifiedNameGetCreationNamespace(defGetQualifiedName(defel), + &multirangeTypeName); + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -1496,8 +1524,10 @@ DefineRange(CreateRangeStmt *stmt) /* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */ alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT; - /* Allocate OID for array type */ + /* Allocate OID for array type, its multirange, and its multirange array */ rangeArrayOid = AssignTypeArrayOid(); + multirangeOid = AssignTypeMultirangeOid(); + multirangeArrayOid = AssignTypeMultirangeArrayOid(); /* Create the pg_type entry */ address = @@ -1536,9 +1566,75 @@ DefineRange(CreateRangeStmt *stmt) Assert(typoid == InvalidOid || typoid == address.objectId); typoid = address.objectId; + /* Create the multirange that goes with it */ + if (multirangeTypeName) + { + Oid old_typoid; + + /* + * Look to see if multirange type already exists. + */ + old_typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, + CStringGetDatum(multirangeTypeName), + ObjectIdGetDatum(multirangeNamespace)); + + /* + * If it's not a shell, see if it's an autogenerated array type, and if so + * rename it out of the way. + */ + if (OidIsValid(old_typoid) && get_typisdefined(old_typoid)) + { + if (!moveArrayTypeName(old_typoid, multirangeTypeName, multirangeNamespace)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", multirangeTypeName))); + } + } + else + { + /* Generate multirange name automatically */ + multirangeNamespace = typeNamespace; + multirangeTypeName = makeMultirangeTypeName(typeName, multirangeNamespace); + } + + mltrngaddress = + TypeCreate(multirangeOid, /* force assignment of this type OID */ + multirangeTypeName, /* type name */ + multirangeNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + GetUserId(), /* owner's ID */ + -1, /* internal size (always varlena) */ + TYPTYPE_MULTIRANGE, /* type-type (multirange type) */ + TYPCATEGORY_RANGE, /* type-category (range type) */ + false, /* multirange types are never preferred */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_MULTIRANGE_IN, /* input procedure */ + F_MULTIRANGE_OUT, /* output procedure */ + F_MULTIRANGE_RECV, /* receive procedure */ + F_MULTIRANGE_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_MULTIRANGE_TYPANALYZE, /* analyze procedure */ + InvalidOid, /* subscript procedure - none */ + InvalidOid, /* element type ID - none */ + false, /* this is not an array type */ + multirangeArrayOid, /* array type we are about to create */ + InvalidOid, /* base type ID (only for domains) */ + NULL, /* never a default type value */ + NULL, /* no binary form available either */ + false, /* never passed by value */ + alignment, /* alignment */ + 'x', /* TOAST strategy (always extended) */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + 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); + rangeCanonical, rangeSubtypeDiff, multirangeOid); /* * Create the array type that goes with it. @@ -1580,8 +1676,54 @@ DefineRange(CreateRangeStmt *stmt) pfree(rangeArrayName); + /* Create the multirange's array type */ + + multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace); + + TypeCreate(multirangeArrayOid, /* force assignment of this type OID */ + multirangeArrayName, /* type name */ + multirangeNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + GetUserId(), /* owner's ID */ + -1, /* internal size (always varlena) */ + TYPTYPE_BASE, /* type-type (base type) */ + TYPCATEGORY_ARRAY, /* type-category (array) */ + false, /* array types are never preferred */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_ARRAY_IN, /* input procedure */ + F_ARRAY_OUT, /* output procedure */ + F_ARRAY_RECV, /* receive procedure */ + F_ARRAY_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_ARRAY_TYPANALYZE, /* analyze procedure */ + F_ARRAY_SUBSCRIPT_HANDLER, /* array subscript procedure */ + multirangeOid, /* element type ID */ + true, /* yes this is an array type */ + InvalidOid, /* no further array type */ + InvalidOid, /* base type ID */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + false, /* never passed by value */ + alignment, /* alignment - same as range's */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ + /* And create the constructor functions for this range type */ makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype); + makeMultirangeConstructors(multirangeTypeName, typeNamespace, + multirangeOid, typoid, rangeArrayOid, + &castFuncOid); + + /* Create cast from the range type to its multirange type */ + CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL); + + pfree(multirangeTypeName); + pfree(multirangeArrayName); return address; } @@ -1659,6 +1801,149 @@ makeRangeConstructors(const char *name, Oid namespace, } } +/* + * We make a separate multirange constructor for each range type + * so its name can include the base type, like range constructors do. + * 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. + */ +static void +makeMultirangeConstructors(const char *name, Oid namespace, + Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid, + Oid *castFuncOid) +{ + ObjectAddress myself, + referenced; + oidvector *argtypes; + Datum allParamTypes; + ArrayType *allParameterTypes; + Datum paramModes; + ArrayType *parameterModes; + + referenced.classId = TypeRelationId; + referenced.objectId = multirangeOid; + referenced.objectSubId = 0; + + /* 0-arg constructor - for empty multiranges */ + argtypes = buildoidvector(NULL, 0); + myself = ProcedureCreate(name, /* name: same as multirange type */ + namespace, + false, /* replace */ + false, /* returns set */ + multirangeOid, /* return type */ + BOOTSTRAP_SUPERUSERID, /* proowner */ + INTERNALlanguageId, /* language */ + F_FMGR_INTERNAL_VALIDATOR, + "multirange_constructor0", /* prosrc */ + NULL, /* probin */ + PROKIND_FUNCTION, + false, /* security_definer */ + false, /* leakproof */ + false, /* isStrict */ + PROVOLATILE_IMMUTABLE, /* volatility */ + PROPARALLEL_SAFE, /* parallel safety */ + argtypes, /* parameterTypes */ + PointerGetDatum(NULL), /* allParameterTypes */ + PointerGetDatum(NULL), /* parameterModes */ + PointerGetDatum(NULL), /* parameterNames */ + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* trftypes */ + PointerGetDatum(NULL), /* proconfig */ + InvalidOid, /* prosupport */ + 1.0, /* procost */ + 0.0); /* prorows */ + + /* + * Make the constructor internally-dependent on the multirange type so + * that they go away silently when the type is dropped. Note that pg_dump + * depends on this choice to avoid dumping the constructors. + */ + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + pfree(argtypes); + + /* + * 1-arg constructor - for casts + * + * In theory we shouldn't need both this and the vararg (n-arg) + * constructor, but having a separate 1-arg function lets us define casts + * against it. + */ + argtypes = buildoidvector(&rangeOid, 1); + myself = ProcedureCreate(name, /* name: same as multirange type */ + namespace, + false, /* replace */ + false, /* returns set */ + multirangeOid, /* return type */ + BOOTSTRAP_SUPERUSERID, /* proowner */ + INTERNALlanguageId, /* language */ + F_FMGR_INTERNAL_VALIDATOR, + "multirange_constructor1", /* prosrc */ + NULL, /* probin */ + PROKIND_FUNCTION, + false, /* security_definer */ + false, /* leakproof */ + true, /* isStrict */ + PROVOLATILE_IMMUTABLE, /* volatility */ + PROPARALLEL_SAFE, /* parallel safety */ + argtypes, /* parameterTypes */ + PointerGetDatum(NULL), /* allParameterTypes */ + PointerGetDatum(NULL), /* parameterModes */ + PointerGetDatum(NULL), /* parameterNames */ + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* trftypes */ + PointerGetDatum(NULL), /* proconfig */ + InvalidOid, /* prosupport */ + 1.0, /* procost */ + 0.0); /* prorows */ + /* ditto */ + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + pfree(argtypes); + *castFuncOid = myself.objectId; + + /* n-arg constructor - vararg */ + argtypes = buildoidvector(&rangeArrayOid, 1); + allParamTypes = ObjectIdGetDatum(rangeArrayOid); + allParameterTypes = construct_array(&allParamTypes, + 1, OIDOID, + sizeof(Oid), true, 'i'); + paramModes = CharGetDatum(FUNC_PARAM_VARIADIC); + parameterModes = construct_array(¶mModes, 1, CHAROID, + 1, true, 'c'); + myself = ProcedureCreate(name, /* name: same as multirange type */ + namespace, + false, /* replace */ + false, /* returns set */ + multirangeOid, /* return type */ + BOOTSTRAP_SUPERUSERID, /* proowner */ + INTERNALlanguageId, /* language */ + F_FMGR_INTERNAL_VALIDATOR, + "multirange_constructor2", /* prosrc */ + NULL, /* probin */ + PROKIND_FUNCTION, + false, /* security_definer */ + false, /* leakproof */ + false, /* isStrict */ + PROVOLATILE_IMMUTABLE, /* volatility */ + PROPARALLEL_SAFE, /* parallel safety */ + argtypes, /* parameterTypes */ + PointerGetDatum(allParameterTypes), /* allParameterTypes */ + PointerGetDatum(parameterModes), /* parameterModes */ + PointerGetDatum(NULL), /* parameterNames */ + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* trftypes */ + PointerGetDatum(NULL), /* proconfig */ + InvalidOid, /* prosupport */ + 1.0, /* procost */ + 0.0); /* prorows */ + /* ditto */ + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + pfree(argtypes); + pfree(allParameterTypes); + pfree(parameterModes); +} /* * Find suitable I/O and other support functions for a type. @@ -2152,6 +2437,72 @@ AssignTypeArrayOid(void) return type_array_oid; } +/* + * AssignTypeMultirangeOid + * + * Pre-assign the range type's multirange OID for use in pg_type.oid + */ +Oid +AssignTypeMultirangeOid(void) +{ + Oid type_multirange_oid; + + /* Use binary-upgrade override for pg_type.oid? */ + if (IsBinaryUpgrade) + { + if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_type multirange OID value not set when in binary upgrade mode"))); + + type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid; + binary_upgrade_next_mrng_pg_type_oid = InvalidOid; + } + else + { + Relation pg_type = table_open(TypeRelationId, AccessShareLock); + + type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId, + Anum_pg_type_oid); + table_close(pg_type, AccessShareLock); + } + + return type_multirange_oid; +} + +/* + * AssignTypeMultirangeArrayOid + * + * Pre-assign the range type's multirange array OID for use in pg_type.typarray + */ +Oid +AssignTypeMultirangeArrayOid(void) +{ + Oid type_multirange_array_oid; + + /* Use binary-upgrade override for pg_type.oid? */ + if (IsBinaryUpgrade) + { + if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_type multirange array OID value not set when in binary upgrade mode"))); + + type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid; + binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid; + } + else + { + Relation pg_type = table_open(TypeRelationId, AccessShareLock); + + type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId, + Anum_pg_type_oid); + table_close(pg_type, AccessShareLock); + } + + return type_multirange_array_oid; +} + /*------------------------------------------------------------------- * DefineCompositeType |