diff options
Diffstat (limited to 'src/backend/commands/typecmds.c')
-rw-r--r-- | src/backend/commands/typecmds.c | 395 |
1 files changed, 217 insertions, 178 deletions
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 8ffbc52fdef..54105f2c408 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -67,7 +67,6 @@ #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memutils.h" -#include "utils/rangetypes.h" #include "utils/rel.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -85,6 +84,8 @@ typedef struct /* Potentially set by contrib/pg_upgrade_support functions */ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid; +static void makeRangeConstructors(const char *name, Oid namespace, + Oid rangeOid, Oid subtype); static Oid findTypeInputFunction(List *procname, Oid typeOid); static Oid findTypeOutputFunction(List *procname, Oid typeOid); static Oid findTypeReceiveFunction(List *procname, Oid typeOid); @@ -92,9 +93,9 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid); static Oid findTypeTypmodinFunction(List *procname); static Oid findTypeTypmodoutFunction(List *procname); static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid); +static Oid findRangeSubOpclass(List *opcname, Oid subtype); static Oid findRangeCanonicalFunction(List *procname, Oid typeOid); -static Oid findRangeSubOpclass(List *procname, Oid typeOid); -static Oid findRangeSubtypeDiffFunction(List *procname, Oid typeOid); +static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype); static void validateDomainConstraint(Oid domainoid, char *ccbin); static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode); static void checkDomainOwner(HeapTuple tup); @@ -103,8 +104,6 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, int typMod, Constraint *constr, char *domainName); -static void makeRangeConstructor(char *name, Oid namespace, Oid rettype, - Oid subtype); /* @@ -1155,6 +1154,61 @@ DefineEnum(CreateEnumStmt *stmt) } /* + * AlterEnum + * Adds a new label to an existing enum. + */ +void +AlterEnum(AlterEnumStmt *stmt) +{ + Oid enum_type_oid; + TypeName *typename; + HeapTuple tup; + + /* Make a TypeName so we can use standard type lookup machinery */ + typename = makeTypeNameFromNameList(stmt->typeName); + enum_type_oid = typenameTypeId(NULL, typename); + + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for type %u", enum_type_oid); + + /* Check it's an enum and check user has permission to ALTER the enum */ + checkEnumOwner(tup); + + /* Add the new label */ + AddEnumLabel(enum_type_oid, stmt->newVal, + stmt->newValNeighbor, stmt->newValIsAfter); + + ReleaseSysCache(tup); +} + + +/* + * checkEnumOwner + * + * Check that the type is actually an enum and that the current user + * has permission to do ALTER TYPE on it. Throw an error if not. + */ +static void +checkEnumOwner(HeapTuple tup) +{ + Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); + + /* Check that this is actually an enum */ + if (typTup->typtype != TYPTYPE_ENUM) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s is not an enum", + format_type_be(HeapTupleGetOid(tup))))); + + /* Permission check: must own type */ + if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(HeapTupleGetOid(tup))); +} + + +/* * DefineRange * Registers a new range type. */ @@ -1162,20 +1216,21 @@ void DefineRange(CreateRangeStmt *stmt) { char *typeName; - char *rangeArrayName; Oid typeNamespace; Oid typoid; + char *rangeArrayName; Oid rangeArrayOid; - List *parameters = stmt->params; + Oid rangeSubtype = InvalidOid; List *rangeSubOpclassName = NIL; - List *rangeSubtypeDiffName = NIL; List *rangeCollationName = NIL; - Oid rangeCollation = InvalidOid; - regproc rangeAnalyze = InvalidOid; - Oid rangeSubtype = InvalidOid; - regproc rangeSubOpclass = InvalidOid; - regproc rangeCanonical = InvalidOid; - regproc rangeSubtypeDiff = InvalidOid; + List *rangeCanonicalName = NIL; + List *rangeSubtypeDiffName = NIL; + List *rangeAnalyzeName = NIL; + Oid rangeSubOpclass; + Oid rangeCollation; + regproc rangeCanonical; + regproc rangeSubtypeDiff; + regproc rangeAnalyze; int16 subtyplen; bool subtypbyval; char subtypalign; @@ -1194,8 +1249,7 @@ DefineRange(CreateRangeStmt *stmt) get_namespace_name(typeNamespace)); /* - * Look to see if type already exists (presumably as a shell; if not, - * TypeCreate will complain). + * Look to see if type already exists. */ typoid = GetSysCacheOid2(TYPENAMENSP, CStringGetDatum(typeName), @@ -1209,37 +1263,27 @@ DefineRange(CreateRangeStmt *stmt) { if (moveArrayTypeName(typoid, typeName, typeNamespace)) typoid = InvalidOid; + else + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); } /* * If it doesn't exist, create it as a shell, so that the OID is known for - * use in the I/O function definitions. + * use in the range function definitions. */ if (!OidIsValid(typoid)) { typoid = TypeShellMake(typeName, typeNamespace, GetUserId()); /* Make new shell type visible for modification below */ CommandCounterIncrement(); - - /* - * If the command was a parameterless CREATE TYPE, we're done --- - * creating the shell type was all we're supposed to do. - */ - if (parameters == NIL) - return; - } - else - { - /* Complain if dummy CREATE TYPE and entry already exists */ - if (parameters == NIL) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("type \"%s\" already exists", typeName))); } + /* Extract the parameters from the parameter list */ foreach(lc, stmt->params) { - DefElem *defel = lfirst(lc); + DefElem *defel = (DefElem *) lfirst(lc); if (pg_strcasecmp(defel->defname, "subtype") == 0) { @@ -1247,16 +1291,16 @@ DefineRange(CreateRangeStmt *stmt) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); + /* we can look up the subtype name immediately */ rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel)); } - else if (pg_strcasecmp(defel->defname, "canonical") == 0) + else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0) { - if (OidIsValid(rangeCanonical)) + if (rangeSubOpclassName != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - rangeCanonical = findRangeCanonicalFunction( - defGetQualifiedName(defel), typoid); + rangeSubOpclassName = defGetQualifiedName(defel); } else if (pg_strcasecmp(defel->defname, "collation") == 0) { @@ -1266,63 +1310,87 @@ DefineRange(CreateRangeStmt *stmt) errmsg("conflicting or redundant options"))); rangeCollationName = defGetQualifiedName(defel); } - else if (pg_strcasecmp(defel->defname, "analyze") == 0) + else if (pg_strcasecmp(defel->defname, "canonical") == 0) { - if (OidIsValid(rangeAnalyze)) + if (rangeCanonicalName != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel), - typoid); + rangeCanonicalName = defGetQualifiedName(defel); } - else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0) + else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0) { - if (rangeSubOpclassName != NIL) + if (rangeSubtypeDiffName != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - rangeSubOpclassName = defGetQualifiedName(defel); + rangeSubtypeDiffName = defGetQualifiedName(defel); } - else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0) + else if (pg_strcasecmp(defel->defname, "analyze") == 0) { - if (rangeSubtypeDiffName != NIL) + if (rangeAnalyzeName != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); - rangeSubtypeDiffName = defGetQualifiedName(defel); + rangeAnalyzeName = defGetQualifiedName(defel); } else - { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("type attribute \"%s\" not recognized", defel->defname))); - continue; - } } + /* Must have a subtype */ if (!OidIsValid(rangeSubtype)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("type attribute \"subtype\" is required"))); + /* disallow ranges of pseudotypes */ + if (get_typtype(rangeSubtype) == TYPTYPE_PSEUDO) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("range subtype cannot be %s", + format_type_be(rangeSubtype)))); + /* Identify subopclass */ + rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype); + + /* Identify collation to use, if any */ if (type_is_collatable(rangeSubtype)) { - if (rangeCollationName == NIL) - rangeCollation = get_typcollation(rangeSubtype); - else + if (rangeCollationName != NIL) rangeCollation = get_collation_oid(rangeCollationName, false); + else + rangeCollation = get_typcollation(rangeSubtype); + } + else + { + if (rangeCollationName != NIL) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("range collation specified but subtype does not support collation"))); + rangeCollation = InvalidOid; } - else if (rangeCollationName != NIL) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("range collation specified but subtype does not support collation"))); - rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype); + /* Identify support functions, if provided */ + if (rangeCanonicalName != NIL) + rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName, + typoid); + else + rangeCanonical = InvalidOid; if (rangeSubtypeDiffName != NIL) rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName, rangeSubtype); + else + rangeSubtypeDiff = InvalidOid; + + if (rangeAnalyzeName != NIL) + rangeAnalyze = findTypeAnalyzeFunction(rangeAnalyzeName, + typoid); + else + rangeAnalyze = InvalidOid; get_typlenbyvalalign(rangeSubtype, &subtyplen, &subtypbyval, &subtypalign); @@ -1358,16 +1426,16 @@ DefineRange(CreateRangeStmt *stmt) rangeArrayOid, /* array type we are about to create */ InvalidOid, /* base type ID (only for domains) */ NULL, /* never a default type value */ - NULL, /* binary default isn't sent either */ + 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); /* typcollation */ + InvalidOid); /* type's collation (ranges never have one) */ - /* create the entry in pg_range */ + /* Create the entry in pg_range */ RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass, rangeCanonical, rangeSubtypeDiff); @@ -1411,61 +1479,64 @@ DefineRange(CreateRangeStmt *stmt) pfree(rangeArrayName); /* And create the constructor functions for this range type */ - makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype); + makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype); } /* - * Because there may exist several range types over one subtype, the range type - * can't be determined from the subtype. This means that constructors can't be - * polymorphic, and so we must generate a new constructor for every range type - * defined. + * Because there may exist several range types over the same subtype, the + * range type can't be uniquely determined from the subtype. So it's + * impossible to define a polymorphic constructor; we have to generate new + * constructor functions explicitly for each range type. * - * We actually define 4 functions with 0 through 3 arguments. This is just to - * offer more convenience for the user. + * We actually define 4 functions, with 0 through 3 arguments. This is just + * to offer more convenience for the user. */ static void -makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype) +makeRangeConstructors(const char *name, Oid namespace, + Oid rangeOid, Oid subtype) { - ObjectAddress referenced; + static const char * const prosrc[4] = {"range_constructor0", + "range_constructor1", + "range_constructor2", + "range_constructor3"}; + static const int pronargs[4] = {0, 1, 2, 3}; + Oid constructorArgTypes[3]; + ObjectAddress myself, + referenced; int i; - referenced.classId = TypeRelationId; - referenced.objectId = rangeOid; - referenced.objectSubId = 0; - constructorArgTypes[0] = subtype; constructorArgTypes[1] = subtype; constructorArgTypes[2] = TEXTOID; - for (i = 0; i < 4; i++) + referenced.classId = TypeRelationId; + referenced.objectId = rangeOid; + referenced.objectSubId = 0; + + for (i = 0; i < lengthof(prosrc); i++) { oidvector *constructorArgTypesVector; - ObjectAddress myself; Oid procOid; - char *prosrc[4] = {"range_constructor0", - "range_constructor1", - "range_constructor2", - "range_constructor3"}; - constructorArgTypesVector = buildoidvector(constructorArgTypes, i); + constructorArgTypesVector = buildoidvector(constructorArgTypes, + pronargs[i]); - procOid = ProcedureCreate( - name, /* name */ + procOid = ProcedureCreate(name, /* name: same as range type */ namespace, /* namespace */ false, /* replace */ - false, /* return set */ + false, /* returns set */ rangeOid, /* return type */ INTERNALlanguageId, /* language */ F_FMGR_INTERNAL_VALIDATOR, /* language validator */ prosrc[i], /* prosrc */ - NULL, /* probin */ - false, /* agg */ - false, /* window */ - false, /* security definer */ - false, /* strict */ + NULL, /* probin */ + false, /* isAgg */ + false, /* isWindowFunc */ + false, /* security_definer */ + false, /* isStrict */ PROVOLATILE_IMMUTABLE, /* volatility */ - constructorArgTypesVector, /* param types */ + constructorArgTypesVector, /* parameterTypes */ PointerGetDatum(NULL), /* allParameterTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ @@ -1482,64 +1553,11 @@ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype) myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); } } -/* - * AlterEnum - * Adds a new label to an existing enum. - */ -void -AlterEnum(AlterEnumStmt *stmt) -{ - Oid enum_type_oid; - TypeName *typename; - HeapTuple tup; - - /* Make a TypeName so we can use standard type lookup machinery */ - typename = makeTypeNameFromNameList(stmt->typeName); - enum_type_oid = typenameTypeId(NULL, typename); - - tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid)); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "cache lookup failed for type %u", enum_type_oid); - - /* Check it's an enum and check user has permission to ALTER the enum */ - checkEnumOwner(tup); - - /* Add the new label */ - AddEnumLabel(enum_type_oid, stmt->newVal, - stmt->newValNeighbor, stmt->newValIsAfter); - - ReleaseSysCache(tup); -} - - -/* - * checkEnumOwner - * - * Check that the type is actually an enum and that the current user - * has permission to do ALTER TYPE on it. Throw an error if not. - */ -static void -checkEnumOwner(HeapTuple tup) -{ - Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); - - /* Check that this is actually an enum */ - if (typTup->typtype != TYPTYPE_ENUM) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("%s is not an enum", - format_type_be(HeapTupleGetOid(tup))))); - - /* Permission check: must own type */ - if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(HeapTupleGetOid(tup))); -} - /* * Find suitable I/O functions for a type. @@ -1802,98 +1820,119 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid) } /* + * Find suitable support functions and opclasses for a range type. + */ + +/* * Find named btree opclass for subtype, or default btree opclass if - * opcname is NIL. This will be used for comparing values of subtype. + * opcname is NIL. */ static Oid findRangeSubOpclass(List *opcname, Oid subtype) { Oid opcid; + Oid opInputType; + + if (opcname != NIL) + { + opcid = get_opclass_oid(BTREE_AM_OID, opcname, false); - if (opcname == NIL) + /* + * Verify that the operator class accepts this datatype. Note we will + * accept binary compatibility. + */ + opInputType = get_opclass_input_type(opcid); + if (!IsBinaryCoercible(subtype, opInputType)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("operator class \"%s\" does not accept data type %s", + NameListToString(opcname), + format_type_be(subtype)))); + } + else { opcid = GetDefaultOpClass(subtype, BTREE_AM_OID); if (!OidIsValid(opcid)) { + /* We spell the error message identically to GetIndexOpClass */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("data type %s has no default operator class for access method \"btree\"", - format_type_be(subtype)), - errhint("You must specify an operator class for the data type or define a default operator class for the data type."))); + errmsg("data type %s has no default operator class for access method \"%s\"", + format_type_be(subtype), "btree"), + errhint("You must specify an operator class for the range type or define a default operator class for the subtype."))); } - return opcid; } - opcid = get_opclass_oid(BTREE_AM_OID, opcname, false); - return opcid; } -/* - * Used to find a range's 'canonical' function. - */ static Oid -findRangeSubtypeDiffFunction(List *procname, Oid typeOid) +findRangeCanonicalFunction(List *procname, Oid typeOid) { - Oid argList[2]; + Oid argList[1]; Oid procOid; + /* + * Range canonical functions must take and return the range type, and must + * be immutable. + */ argList[0] = typeOid; - argList[1] = typeOid; - procOid = LookupFuncName(procname, 2, argList, true); + procOid = LookupFuncName(procname, 1, argList, true); if (!OidIsValid(procOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", - func_signature_string(procname, 2, NIL, argList)))); + func_signature_string(procname, 1, NIL, argList)))); - if (get_func_rettype(procOid) != FLOAT8OID) + if (get_func_rettype(procOid) != typeOid) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("range subtype diff function %s must return type \"float8\"", - func_signature_string(procname, 2, NIL, argList)))); + errmsg("range canonical function %s must return range type", + func_signature_string(procname, 1, NIL, argList)))); if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("range subtype diff function %s must be immutable", - func_signature_string(procname, 2, NIL, argList)))); + errmsg("range canonical function %s must be immutable", + func_signature_string(procname, 1, NIL, argList)))); return procOid; } -/* - * Used to find a range's 'canonical' function. - */ static Oid -findRangeCanonicalFunction(List *procname, Oid typeOid) +findRangeSubtypeDiffFunction(List *procname, Oid subtype) { - Oid argList[1]; + Oid argList[2]; Oid procOid; - argList[0] = typeOid; + /* + * Range subtype diff functions must take two arguments of the subtype, + * must return float8, and must be immutable. + */ + argList[0] = subtype; + argList[1] = subtype; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 2, argList, true); if (!OidIsValid(procOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", - func_signature_string(procname, 1, NIL, argList)))); + func_signature_string(procname, 2, NIL, argList)))); - if (get_func_rettype(procOid) != typeOid) + if (get_func_rettype(procOid) != FLOAT8OID) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("range canonical function %s must return range type", - NameListToString(procname)))); + errmsg("range subtype diff function %s must return type double precision", + func_signature_string(procname, 2, NIL, argList)))); if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("range canonical function %s must be immutable", - func_signature_string(procname, 1, NIL, argList)))); + errmsg("range subtype diff function %s must be immutable", + func_signature_string(procname, 2, NIL, argList)))); return procOid; } |