mirror of
https://github.com/postgres/postgres.git
synced 2025-10-16 17:07:43 +03:00
More code review for rangetypes patch.
Fix up some infelicitous coding in DefineRange, and add some missing error checks. Rearrange operator strategy number assignments for GiST anyrange opclass so that they don't make such a mess of opr_sanity's table of operator names associated with different strategy numbers. Assign hopefully-temporary selectivity estimators to range operators that didn't have one --- poor as the estimates are, they're still a lot better than the default 0.5 estimate, and they'll shut up the opr_sanity test that wants to see selectivity estimators on all built-in operators.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
/*
|
||||
@@ -1154,338 +1153,6 @@ DefineEnum(CreateEnumStmt *stmt)
|
||||
pfree(enumArrayName);
|
||||
}
|
||||
|
||||
/*
|
||||
* DefineRange
|
||||
* Registers a new range type.
|
||||
*/
|
||||
void
|
||||
DefineRange(CreateRangeStmt *stmt)
|
||||
{
|
||||
char *typeName;
|
||||
char *rangeArrayName;
|
||||
Oid typeNamespace;
|
||||
Oid typoid;
|
||||
Oid rangeArrayOid;
|
||||
List *parameters = stmt->params;
|
||||
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;
|
||||
int16 subtyplen;
|
||||
bool subtypbyval;
|
||||
char subtypalign;
|
||||
char alignment;
|
||||
AclResult aclresult;
|
||||
ListCell *lc;
|
||||
|
||||
/* 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 specified but subtype does not support collation")));
|
||||
|
||||
rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
|
||||
|
||||
if (rangeSubtypeDiffName != NIL)
|
||||
rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
|
||||
rangeSubtype);
|
||||
|
||||
get_typlenbyvalalign(rangeSubtype,
|
||||
&subtyplen, &subtypbyval, &subtypalign);
|
||||
|
||||
/* alignment must be 'i' or 'd' for ranges */
|
||||
alignment = (subtypalign == 'd') ? 'd' : 'i';
|
||||
|
||||
/* Allocate OID for array type */
|
||||
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 (always varlena) */
|
||||
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 */
|
||||
InvalidOid, /* element type ID - none */
|
||||
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 */
|
||||
alignment, /* alignment */
|
||||
'x', /* TOAST strategy (always extended) */
|
||||
-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 */
|
||||
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 */
|
||||
|
||||
pfree(rangeArrayName);
|
||||
|
||||
/* And create the constructor functions for this range type */
|
||||
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 constructors internally-dependent on the range 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.
|
||||
*/
|
||||
myself.classId = ProcedureRelationId;
|
||||
myself.objectId = procOid;
|
||||
myself.objectSubId = 0;
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AlterEnum
|
||||
* Adds a new label to an existing enum.
|
||||
@@ -1541,6 +1208,357 @@ checkEnumOwner(HeapTuple tup)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DefineRange
|
||||
* Registers a new range type.
|
||||
*/
|
||||
void
|
||||
DefineRange(CreateRangeStmt *stmt)
|
||||
{
|
||||
char *typeName;
|
||||
Oid typeNamespace;
|
||||
Oid typoid;
|
||||
char *rangeArrayName;
|
||||
Oid rangeArrayOid;
|
||||
Oid rangeSubtype = InvalidOid;
|
||||
List *rangeSubOpclassName = NIL;
|
||||
List *rangeCollationName = NIL;
|
||||
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;
|
||||
char alignment;
|
||||
AclResult aclresult;
|
||||
ListCell *lc;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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;
|
||||
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 range function definitions.
|
||||
*/
|
||||
if (!OidIsValid(typoid))
|
||||
{
|
||||
typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
|
||||
/* Make new shell type visible for modification below */
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
/* Extract the parameters from the parameter list */
|
||||
foreach(lc, stmt->params)
|
||||
{
|
||||
DefElem *defel = (DefElem *) lfirst(lc);
|
||||
|
||||
if (pg_strcasecmp(defel->defname, "subtype") == 0)
|
||||
{
|
||||
if (OidIsValid(rangeSubtype))
|
||||
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, "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, "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, "canonical") == 0)
|
||||
{
|
||||
if (rangeCanonicalName != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
rangeCanonicalName = 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 if (pg_strcasecmp(defel->defname, "analyze") == 0)
|
||||
{
|
||||
if (rangeAnalyzeName != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
rangeAnalyzeName = defGetQualifiedName(defel);
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("type attribute \"%s\" not recognized",
|
||||
defel->defname)));
|
||||
}
|
||||
|
||||
/* 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_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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* alignment must be 'i' or 'd' for ranges */
|
||||
alignment = (subtypalign == 'd') ? 'd' : 'i';
|
||||
|
||||
/* Allocate OID for array type */
|
||||
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 (always varlena) */
|
||||
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 */
|
||||
InvalidOid, /* element type ID - none */
|
||||
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, /* 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) */
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
|
||||
pfree(rangeArrayName);
|
||||
|
||||
/* And create the constructor functions for this range type */
|
||||
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static void
|
||||
makeRangeConstructors(const char *name, Oid namespace,
|
||||
Oid rangeOid, Oid subtype)
|
||||
{
|
||||
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;
|
||||
|
||||
constructorArgTypes[0] = subtype;
|
||||
constructorArgTypes[1] = subtype;
|
||||
constructorArgTypes[2] = TEXTOID;
|
||||
|
||||
referenced.classId = TypeRelationId;
|
||||
referenced.objectId = rangeOid;
|
||||
referenced.objectSubId = 0;
|
||||
|
||||
for (i = 0; i < lengthof(prosrc); i++)
|
||||
{
|
||||
oidvector *constructorArgTypesVector;
|
||||
Oid procOid;
|
||||
|
||||
constructorArgTypesVector = buildoidvector(constructorArgTypes,
|
||||
pronargs[i]);
|
||||
|
||||
procOid = ProcedureCreate(name, /* name: same as range type */
|
||||
namespace, /* namespace */
|
||||
false, /* replace */
|
||||
false, /* returns set */
|
||||
rangeOid, /* return type */
|
||||
INTERNALlanguageId, /* language */
|
||||
F_FMGR_INTERNAL_VALIDATOR, /* language validator */
|
||||
prosrc[i], /* prosrc */
|
||||
NULL, /* probin */
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
PROVOLATILE_IMMUTABLE, /* volatility */
|
||||
constructorArgTypesVector, /* parameterTypes */
|
||||
PointerGetDatum(NULL), /* allParameterTypes */
|
||||
PointerGetDatum(NULL), /* parameterModes */
|
||||
PointerGetDatum(NULL), /* parameterNames */
|
||||
NIL, /* parameterDefaults */
|
||||
PointerGetDatum(NULL), /* proconfig */
|
||||
1.0, /* procost */
|
||||
0.0); /* prorows */
|
||||
|
||||
/*
|
||||
* Make the constructors internally-dependent on the range 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.
|
||||
*/
|
||||
myself.classId = ProcedureRelationId;
|
||||
myself.objectId = procOid;
|
||||
myself.objectSubId = 0;
|
||||
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find suitable I/O functions for a type.
|
||||
*
|
||||
@@ -1801,78 +1819,63 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
|
||||
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)
|
||||
if (opcname != NIL)
|
||||
{
|
||||
opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
|
||||
/*
|
||||
* Range canonical functions must take and return the range type, and must
|
||||
* be immutable.
|
||||
*/
|
||||
argList[0] = typeOid;
|
||||
|
||||
procOid = LookupFuncName(procname, 1, argList, true);
|
||||
@@ -1887,7 +1890,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("range canonical function %s must return range type",
|
||||
NameListToString(procname))));
|
||||
func_signature_string(procname, 1, NIL, argList))));
|
||||
|
||||
if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
|
||||
ereport(ERROR,
|
||||
@@ -1898,6 +1901,42 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
|
||||
return procOid;
|
||||
}
|
||||
|
||||
static Oid
|
||||
findRangeSubtypeDiffFunction(List *procname, Oid subtype)
|
||||
{
|
||||
Oid argList[2];
|
||||
Oid procOid;
|
||||
|
||||
/*
|
||||
* 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, 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 double precision",
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* AssignTypeArrayOid
|
||||
*
|
||||
|
Reference in New Issue
Block a user