#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"
/* 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);
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);
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
-static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
- Oid subtype);
/*
pfree(enumArrayName);
}
+/*
+ * 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.
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;
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),
{
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)
{
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)
{
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);
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);
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 */
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.
return procOid;
}
+/*
+ * 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;
}