aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/typecmds.c
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2011-11-03 13:16:28 +0200
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2011-11-03 13:42:15 +0200
commit4429f6a9e3e12bb4af6e3677fbc78cd80f160252 (patch)
treea2e272129e5515f7ef2f4e09989bddf0fd8158ea /src/backend/commands/typecmds.c
parent43342891861cc2d08dea2b1c8b190e15e5a36551 (diff)
downloadpostgresql-4429f6a9e3e12bb4af6e3677fbc78cd80f160252.tar.gz
postgresql-4429f6a9e3e12bb4af6e3677fbc78cd80f160252.zip
Support range data types.
Selectivity estimation functions are missing for some range type operators, which is a TODO. Jeff Davis
Diffstat (limited to 'src/backend/commands/typecmds.c')
-rw-r--r--src/backend/commands/typecmds.c445
1 files changed, 441 insertions, 4 deletions
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5069c5759ec..91488bbbf5c 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,7 +42,11 @@
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_proc_fn.h"
+#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "commands/defrem.h"
@@ -63,6 +67,7 @@
#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"
@@ -87,6 +92,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 findRangeCanonicalFunction(List *procname, Oid typeOid);
+static Oid findRangeSubOpclass(List *procname, Oid typeOid);
+static Oid findRangeSubtypeDiffFunction(List *procname, Oid typeOid);
static void validateDomainConstraint(Oid domainoid, char *ccbin);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup);
@@ -95,6 +103,8 @@ 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);
/*
@@ -643,6 +653,14 @@ RemoveTypeById(Oid typeOid)
if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
EnumValuesDelete(typeOid);
+ /*
+ * If it is a range type, delete the pg_range entries too; we
+ * don't bother with making dependency entries for those, so it
+ * has to be done "by hand" here.
+ */
+ if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
+ RangeDelete(typeOid);
+
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
@@ -724,14 +742,15 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeoid = HeapTupleGetOid(typeTup);
/*
- * Base type must be a plain base type, another domain or an enum. Domains
- * over pseudotypes would create a security hole. Domains over composite
- * types might be made to work in the future, but not today.
+ * Base type must be a plain base type, another domain, an enum or a range
+ * type. Domains over pseudotypes would create a security hole. Domains
+ * over composite types might be made to work in the future, but not today.
*/
typtype = baseType->typtype;
if (typtype != TYPTYPE_BASE &&
typtype != TYPTYPE_DOMAIN &&
- typtype != TYPTYPE_ENUM)
+ typtype != TYPTYPE_ENUM &&
+ typtype != TYPTYPE_RANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1135,6 +1154,327 @@ DefineEnum(CreateEnumStmt *stmt)
}
/*
+ * DefineRange
+ * Registers a new range type.
+ */
+void
+DefineRange(CreateRangeStmt *stmt)
+{
+ char *typeName;
+ char *rangeArrayName;
+ Oid typeNamespace;
+ Oid typoid;
+ Oid rangeArrayOid;
+ List *parameters = stmt->params;
+
+ ListCell *lc;
+ 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;
+
+ AclResult aclresult;
+
+ /* Convert list of names to a name and namespace */
+ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
+ &typeName);
+
+ /* Check we have creation rights in target namespace */
+ aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(typeNamespace));
+
+ /*
+ * Look to see if type already exists (presumably as a shell; if not,
+ * TypeCreate will complain).
+ */
+ typoid = GetSysCacheOid2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace));
+
+ /*
+ * 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(typoid) && get_typisdefined(typoid))
+ {
+ if (moveArrayTypeName(typoid, typeName, typeNamespace))
+ typoid = InvalidOid;
+ }
+
+ /*
+ * If it doesn't exist, create it as a shell, so that the OID is known for
+ * use in the I/O 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)));
+ }
+
+ foreach(lc, stmt->params)
+ {
+ DefElem *defel = lfirst(lc);
+
+ if (pg_strcasecmp(defel->defname, "subtype") == 0)
+ {
+ if (OidIsValid(rangeSubtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
+ }
+ else if (pg_strcasecmp(defel->defname, "canonical") == 0)
+ {
+ if (OidIsValid(rangeCanonical))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeCanonical = findRangeCanonicalFunction(
+ defGetQualifiedName(defel), typoid);
+ }
+ else if (pg_strcasecmp(defel->defname, "collation") == 0)
+ {
+ if (rangeCollationName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeCollationName = defGetQualifiedName(defel);
+ }
+ else if (pg_strcasecmp(defel->defname, "analyze") == 0)
+ {
+ if (OidIsValid(rangeAnalyze))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel),
+ typoid);
+ }
+ else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
+ {
+ if (rangeSubOpclassName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubOpclassName = defGetQualifiedName(defel);
+ }
+ else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0)
+ {
+ if (rangeSubtypeDiffName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ rangeSubtypeDiffName = defGetQualifiedName(defel);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("type attribute \"%s\" not recognized",
+ defel->defname)));
+ continue;
+ }
+ }
+
+ if (!OidIsValid(rangeSubtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("type attribute \"subtype\" is required")));
+
+ if (type_is_collatable(rangeSubtype))
+ {
+ if (rangeCollationName == NIL)
+ rangeCollation = get_typcollation(rangeSubtype);
+ else
+ rangeCollation = get_collation_oid(rangeCollationName, false);
+ }
+ else if (rangeCollationName != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("range collation provided but subtype does not support collation")));
+
+ rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
+
+ if (rangeSubtypeDiffName != NIL)
+ rangeSubtypeDiff = findRangeSubtypeDiffFunction(
+ rangeSubtypeDiffName, rangeSubtype);
+
+ rangeArrayOid = AssignTypeArrayOid();
+
+ /* Create the pg_type entry */
+ typoid =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ typeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size */
+ TYPTYPE_RANGE, /* type-type (range type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* range types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_RANGE_IN, /* input procedure */
+ F_RANGE_OUT, /* output procedure */
+ F_RANGE_RECV, /* receive procedure */
+ F_RANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ rangeAnalyze, /* analyze procedure - default */
+ InvalidOid, /* element type ID */
+ false, /* this is not an array type */
+ 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 */
+ false, /* never passed by value */
+ 'i', /* int alignment */
+ 'x', /* TOAST strategy always plain */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
+ /* create the entry in pg_range */
+ RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
+ rangeCanonical, rangeSubtypeDiff);
+
+ /*
+ * Create the array type that goes with it.
+ */
+ rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
+
+ TypeCreate(rangeArrayOid, /* force assignment of this type OID */
+ rangeArrayName, /* type name */
+ typeNamespace, /* 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 */
+ InvalidOid, /* analyze procedure - default */
+ typoid, /* 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 */
+ 'i', /* align 'i' */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
+ pfree(rangeArrayName);
+
+ makeRangeConstructor(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.
+ *
+ * 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)
+{
+ ObjectAddress referenced;
+ Oid constructorArgTypes[3];
+ 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++)
+ {
+ oidvector *constructorArgTypesVector;
+ ObjectAddress myself;
+ Oid procOid;
+ char *prosrc[4] = { "range_constructor0",
+ "range_constructor1",
+ "range_constructor2",
+ "range_constructor3"};
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes, i);
+
+ procOid = ProcedureCreate(
+ name, /* name */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* return 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 */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ constructorArgTypesVector, /* param types */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* proconfig */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructor internally-dependent on the range type so that
+ * the user doesn't have to treat them as separate objects.
+ */
+ myself.classId = ProcedureRelationId;
+ myself.objectId = procOid;
+ myself.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
+
+/*
* AlterEnum
* Adds a new label to an existing enum.
*/
@@ -1450,6 +1790,103 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
}
/*
+ * Find named btree opclass for subtype, or default btree opclass if
+ * opcname is NIL. This will be used for comparing values of subtype.
+ */
+static Oid
+findRangeSubOpclass(List *opcname, Oid subtype)
+{
+ Oid opcid;
+
+ if (opcname == NIL)
+ {
+ opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
+ if (!OidIsValid(opcid))
+ {
+ 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.")));
+ }
+ 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)
+{
+ Oid argList[2];
+ Oid procOid;
+
+ argList[0] = typeOid;
+ argList[1] = typeOid;
+
+ 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, 2, NIL, argList))));
+
+ if (get_func_rettype(procOid) != FLOAT8OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range subtype diff function %s must return type \"float8\"",
+ func_signature_string(procname, 2, 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))));
+
+ return procOid;
+}
+
+/*
+ * Used to find a range's 'canonical' function.
+ */
+static Oid
+findRangeCanonicalFunction(List *procname, Oid typeOid)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ argList[0] = typeOid;
+
+ 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, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != typeOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("range canonical function %s must return range type",
+ NameListToString(procname))));
+
+ 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))));
+
+ return procOid;
+}
+
+/*
* AssignTypeArrayOid
*
* Pre-assign the type's array OID for use in pg_type.typarray