diff options
-rw-r--r-- | src/backend/commands/typecmds.c | 395 | ||||
-rw-r--r-- | src/backend/utils/adt/rangetypes_gist.c | 113 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_amop.h | 22 | ||||
-rw-r--r-- | src/include/catalog/pg_operator.h | 12 | ||||
-rw-r--r-- | src/test/regress/expected/opr_sanity.out | 50 | ||||
-rw-r--r-- | src/test/regress/expected/plpgsql.out | 14 | ||||
-rw-r--r-- | src/test/regress/expected/rangetypes.out | 14 | ||||
-rw-r--r-- | src/test/regress/sql/opr_sanity.sql | 20 | ||||
-rw-r--r-- | src/test/regress/sql/plpgsql.sql | 10 | ||||
-rw-r--r-- | src/test/regress/sql/rangetypes.sql | 7 |
11 files changed, 342 insertions, 317 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; } diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c index 3eb177a5ced..3fc05d2650b 100644 --- a/src/backend/utils/adt/rangetypes_gist.c +++ b/src/backend/utils/adt/rangetypes_gist.c @@ -21,18 +21,19 @@ /* Operator strategy numbers used in the GiST range opclass */ -#define RANGESTRAT_EQ 1 -#define RANGESTRAT_NE 2 +/* Numbers are chosen to match up operator names with existing usages */ +#define RANGESTRAT_BEFORE 1 +#define RANGESTRAT_OVERLEFT 2 #define RANGESTRAT_OVERLAPS 3 -#define RANGESTRAT_CONTAINS_ELEM 4 -#define RANGESTRAT_ELEM_CONTAINED_BY 5 -#define RANGESTRAT_CONTAINS 6 -#define RANGESTRAT_CONTAINED_BY 7 -#define RANGESTRAT_BEFORE 8 -#define RANGESTRAT_AFTER 9 -#define RANGESTRAT_OVERLEFT 10 -#define RANGESTRAT_OVERRIGHT 11 -#define RANGESTRAT_ADJACENT 12 +#define RANGESTRAT_OVERRIGHT 4 +#define RANGESTRAT_AFTER 5 +#define RANGESTRAT_ADJACENT 6 +#define RANGESTRAT_CONTAINS 7 +#define RANGESTRAT_CONTAINED_BY 8 +#define RANGESTRAT_CONTAINS_ELEM 16 +#define RANGESTRAT_ELEM_CONTAINED_BY 17 +#define RANGESTRAT_EQ 18 +#define RANGESTRAT_NE 19 #define RangeIsEmpty(r) (range_get_flags(r) & RANGE_EMPTY) @@ -460,47 +461,33 @@ range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy, switch (strategy) { - case RANGESTRAT_EQ: - proc = range_contains; - break; - case RANGESTRAT_NE: - return true; - break; - case RANGESTRAT_OVERLAPS: - proc = range_overlaps; - break; - case RANGESTRAT_CONTAINS_ELEM: - case RANGESTRAT_CONTAINS: - proc = range_contains; - break; - case RANGESTRAT_ELEM_CONTAINED_BY: - case RANGESTRAT_CONTAINED_BY: - return true; - break; case RANGESTRAT_BEFORE: if (RangeIsEmpty(key)) return false; proc = range_overright; negate = true; break; - case RANGESTRAT_AFTER: - if (RangeIsEmpty(key)) - return false; - proc = range_overleft; - negate = true; - break; case RANGESTRAT_OVERLEFT: if (RangeIsEmpty(key)) return false; proc = range_after; negate = true; break; + case RANGESTRAT_OVERLAPS: + proc = range_overlaps; + break; case RANGESTRAT_OVERRIGHT: if (RangeIsEmpty(key)) return false; proc = range_before; negate = true; break; + case RANGESTRAT_AFTER: + if (RangeIsEmpty(key)) + return false; + proc = range_overleft; + negate = true; + break; case RANGESTRAT_ADJACENT: if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; @@ -510,6 +497,20 @@ range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy, return true; proc = range_overlaps; break; + case RANGESTRAT_CONTAINS: + case RANGESTRAT_CONTAINS_ELEM: + proc = range_contains; + break; + case RANGESTRAT_CONTAINED_BY: + case RANGESTRAT_ELEM_CONTAINED_BY: + return true; + break; + case RANGESTRAT_EQ: + proc = range_contains; + break; + case RANGESTRAT_NE: + return true; + break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); proc = NULL; /* keep compiler quiet */ @@ -536,48 +537,48 @@ range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy, switch (strategy) { - case RANGESTRAT_EQ: - proc = range_eq; - break; - case RANGESTRAT_NE: - proc = range_ne; - break; - case RANGESTRAT_OVERLAPS: - proc = range_overlaps; - break; - case RANGESTRAT_CONTAINS_ELEM: - case RANGESTRAT_CONTAINS: - proc = range_contains; - break; - case RANGESTRAT_ELEM_CONTAINED_BY: - case RANGESTRAT_CONTAINED_BY: - proc = range_contained_by; - break; case RANGESTRAT_BEFORE: if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; proc = range_before; break; - case RANGESTRAT_AFTER: - if (RangeIsEmpty(key) || RangeIsEmpty(query)) - return false; - proc = range_after; - break; case RANGESTRAT_OVERLEFT: if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; proc = range_overleft; break; + case RANGESTRAT_OVERLAPS: + proc = range_overlaps; + break; case RANGESTRAT_OVERRIGHT: if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; proc = range_overright; break; + case RANGESTRAT_AFTER: + if (RangeIsEmpty(key) || RangeIsEmpty(query)) + return false; + proc = range_after; + break; case RANGESTRAT_ADJACENT: if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; proc = range_adjacent; break; + case RANGESTRAT_CONTAINS: + case RANGESTRAT_CONTAINS_ELEM: + proc = range_contains; + break; + case RANGESTRAT_CONTAINED_BY: + case RANGESTRAT_ELEM_CONTAINED_BY: + proc = range_contained_by; + break; + case RANGESTRAT_EQ: + proc = range_eq; + break; + case RANGESTRAT_NE: + proc = range_ne; + break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); proc = NULL; /* keep compiler quiet */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 6ed527b23e6..c397151370c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201111171 +#define CATALOG_VERSION_NO 201111211 #endif diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 108ed89c20a..a240063a3fd 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -726,17 +726,17 @@ DATA(insert ( 3903 3831 3831 1 s 3882 405 0 )); /* * GiST range_ops */ -DATA(insert ( 3919 3831 3831 1 s 3882 783 0 )); -DATA(insert ( 3919 3831 3831 2 s 3883 783 0 )); +DATA(insert ( 3919 3831 3831 1 s 3893 783 0 )); +DATA(insert ( 3919 3831 3831 2 s 3895 783 0 )); DATA(insert ( 3919 3831 3831 3 s 3888 783 0 )); -DATA(insert ( 3919 3831 2283 4 s 3889 783 0 )); -DATA(insert ( 3919 2283 3831 5 s 3891 783 0 )); -DATA(insert ( 3919 3831 3831 6 s 3890 783 0 )); -DATA(insert ( 3919 3831 3831 7 s 3892 783 0 )); -DATA(insert ( 3919 3831 3831 8 s 3893 783 0 )); -DATA(insert ( 3919 3831 3831 9 s 3894 783 0 )); -DATA(insert ( 3919 3831 3831 10 s 3895 783 0 )); -DATA(insert ( 3919 3831 3831 11 s 3896 783 0 )); -DATA(insert ( 3919 3831 3831 12 s 3897 783 0 )); +DATA(insert ( 3919 3831 3831 4 s 3896 783 0 )); +DATA(insert ( 3919 3831 3831 5 s 3894 783 0 )); +DATA(insert ( 3919 3831 3831 6 s 3897 783 0 )); +DATA(insert ( 3919 3831 3831 7 s 3890 783 0 )); +DATA(insert ( 3919 3831 3831 8 s 3892 783 0 )); +DATA(insert ( 3919 3831 2283 16 s 3889 783 0 )); +DATA(insert ( 3919 2283 3831 17 s 3891 783 0 )); +DATA(insert ( 3919 3831 3831 18 s 3882 783 0 )); +DATA(insert ( 3919 3831 3831 19 s 3883 783 0 )); #endif /* PG_AMOP_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 2d1a2800a48..eac5cb94e6f 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1674,15 +1674,15 @@ DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range DESCR("greater than or equal"); DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt scalargtsel scalargtjoinsel )); DESCR("greater than"); -DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 range_overlaps - - )); +DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 range_overlaps areasel areajoinsel )); DESCR("overlaps"); -DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2283 16 3891 0 range_contains_elem - - )); +DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2283 16 3891 0 range_contains_elem contsel contjoinsel )); DESCR("contains"); -DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 range_contains - - )); +DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 range_contains contsel contjoinsel )); DESCR("contains"); -DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2283 3831 16 3889 0 elem_contained_by_range - - )); +DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2283 3831 16 3889 0 elem_contained_by_range contsel contjoinsel )); DESCR("is contained by"); -DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 range_contained_by - - )); +DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 range_contained_by contsel contjoinsel )); DESCR("is contained by"); DATA(insert OID = 3893 ( "<<" PGNSP PGUID b f f 3831 3831 16 3894 0 range_before scalarltsel scalarltjoinsel )); DESCR("is left of"); @@ -1692,7 +1692,7 @@ DATA(insert OID = 3895 ( "&<" PGNSP PGUID b f f 3831 3831 16 0 0 range_overl DESCR("overlaps or is left of"); DATA(insert OID = 3896 ( "&>" PGNSP PGUID b f f 3831 3831 16 0 0 range_overright scalargtsel scalargtjoinsel )); DESCR("overlaps or is right of"); -DATA(insert OID = 3897 ( "-|-" PGNSP PGUID b f f 3831 3831 16 3897 0 range_adjacent - - )); +DATA(insert OID = 3897 ( "-|-" PGNSP PGUID b f f 3831 3831 16 3897 0 range_adjacent contsel contjoinsel )); DESCR("is adjacent to"); DATA(insert OID = 3898 ( "+" PGNSP PGUID b f f 3831 3831 3831 3898 0 range_union - - )); DESCR("range union"); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 19b559ffa17..db74fcb9e69 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -140,16 +140,16 @@ WHERE p1.oid < p2.oid AND -- need to be modified whenever new pairs of types are made binary-equivalent, -- or when new polymorphic built-in functions are added! -- Note: ignore aggregate functions here, since they all point to the same --- dummy built-in function. +-- dummy built-in function. Likewise, ignore range constructor functions. SELECT DISTINCT p1.prorettype, p2.prorettype FROM pg_proc AS p1, pg_proc AS p2 WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.prorettype < p2.prorettype) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.prorettype < p2.prorettype) ORDER BY 1, 2; prorettype | prorettype ------------+------------ @@ -163,9 +163,9 @@ WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.proargtypes[0] < p2.proargtypes[0]) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.proargtypes[0] < p2.proargtypes[0]) ORDER BY 1, 2; proargtypes | proargtypes -------------+------------- @@ -182,9 +182,9 @@ WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.proargtypes[1] < p2.proargtypes[1]) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.proargtypes[1] < p2.proargtypes[1]) ORDER BY 1, 2; proargtypes | proargtypes -------------+------------- @@ -1021,34 +1021,28 @@ ORDER BY 1, 2, 3; 403 | 5 | ~>~ 405 | 1 | = 783 | 1 | << - 783 | 1 | = 783 | 1 | @@ 783 | 2 | &< - 783 | 2 | <> 783 | 3 | && 783 | 4 | &> - 783 | 4 | @> - 783 | 5 | <@ 783 | 5 | >> - 783 | 6 | @> + 783 | 6 | -|- 783 | 6 | ~= - 783 | 7 | <@ 783 | 7 | @> - 783 | 8 | << 783 | 8 | <@ 783 | 9 | &<| - 783 | 9 | >> - 783 | 10 | &< 783 | 10 | <<| 783 | 10 | <^ - 783 | 11 | &> 783 | 11 | >^ 783 | 11 | |>> - 783 | 12 | -|- 783 | 12 | |&> 783 | 13 | ~ 783 | 14 | @ 783 | 15 | <-> + 783 | 16 | @> + 783 | 17 | <@ + 783 | 18 | = + 783 | 19 | <> 783 | 27 | @> 783 | 28 | <@ 783 | 47 | @> @@ -1061,7 +1055,7 @@ ORDER BY 1, 2, 3; 2742 | 2 | @@@ 2742 | 3 | <@ 2742 | 4 | = -(51 rows) +(45 rows) -- Check that all opclass search operators have selectivity estimators. -- This is not absolutely required, but it seems a reasonable thing @@ -1070,15 +1064,9 @@ SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname FROM pg_amop AS p1, pg_operator AS p2 WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND (p2.oprrest = 0 OR p2.oprjoin = 0); - amopfamily | amopopr | oid | oprname -------------+---------+------+--------- - 3919 | 3888 | 3888 | && - 3919 | 3889 | 3889 | @> - 3919 | 3891 | 3891 | <@ - 3919 | 3890 | 3890 | @> - 3919 | 3892 | 3892 | <@ - 3919 | 3897 | 3897 | -|- -(6 rows) + amopfamily | amopopr | oid | oprname +------------+---------+-----+--------- +(0 rows) -- Check that each opclass in an opfamily has associated operators, that is -- ones whose oprleft matches opcintype (possibly by coercion). diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index fc9d4019444..238bf5f0aec 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -4571,17 +4571,3 @@ ERROR: value for domain orderedarray violates check constraint "sorted" CONTEXT: PL/pgSQL function "testoa" line 5 at assignment drop function arrayassign1(); drop function testoa(x1 int, x2 int, x3 int); --- Test resolve_polymorphic_argtypes() codepath. It is only taken when --- a function is invoked from a different backend from where it's defined, --- so we create the a function with polymorphic argument, reconnect, and --- and then call it. -create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray) - language plpgsql as - $$ begin a := upper(b) + c[1]; return; end; $$; -\c - -select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]); - rangetypes_plpgsql --------------------- - 12 -(1 row) - diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index dec748406f9..130446d0069 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -936,6 +936,20 @@ select range_add_bounds(numrange(1.0001, 123.123)); 124.1231 (1 row) +create function rangetypes_sql(q anyrange, b anyarray, out c anyelement) + as $$ select upper($1) + $2[1] $$ + language sql; +select rangetypes_sql(int4range(1,10), ARRAY[2,20]); + rangetypes_sql +---------------- + 12 +(1 row) + +select rangetypes_sql(numrange(1,10), ARRAY[2,20]); -- match failure +ERROR: function rangetypes_sql(numrange, integer[]) does not exist +LINE 1: select rangetypes_sql(numrange(1,10), ARRAY[2,20]); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- -- Arrays of ranges -- diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 7f936c81547..b0d143087e8 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -125,7 +125,7 @@ WHERE p1.oid < p2.oid AND -- need to be modified whenever new pairs of types are made binary-equivalent, -- or when new polymorphic built-in functions are added! -- Note: ignore aggregate functions here, since they all point to the same --- dummy built-in function. +-- dummy built-in function. Likewise, ignore range constructor functions. SELECT DISTINCT p1.prorettype, p2.prorettype FROM pg_proc AS p1, pg_proc AS p2 @@ -133,9 +133,9 @@ WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.prorettype < p2.prorettype) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.prorettype < p2.prorettype) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] @@ -144,9 +144,9 @@ WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.proargtypes[0] < p2.proargtypes[0]) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.proargtypes[0] < p2.proargtypes[0]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] @@ -155,9 +155,9 @@ WHERE p1.oid != p2.oid AND p1.prosrc = p2.prosrc AND p1.prolang = 12 AND p2.prolang = 12 AND NOT p1.proisagg AND NOT p2.proisagg AND - (p1.proargtypes[1] < p2.proargtypes[1]) AND - -- range constructor functions are shared by all range types. - NOT p1.prosrc LIKE 'range_constructor%' + p1.prosrc NOT LIKE E'range\\_constructor_' AND + p2.prosrc NOT LIKE E'range\\_constructor_' AND + (p1.proargtypes[1] < p2.proargtypes[1]) ORDER BY 1, 2; SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2] diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 2906943f06f..b47c2de312a 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -3600,13 +3600,3 @@ select testoa(1,2,1); -- fail at update drop function arrayassign1(); drop function testoa(x1 int, x2 int, x3 int); - --- Test resolve_polymorphic_argtypes() codepath. It is only taken when --- a function is invoked from a different backend from where it's defined, --- so we create the a function with polymorphic argument, reconnect, and --- and then call it. -create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray) - language plpgsql as - $$ begin a := upper(b) + c[1]; return; end; $$; -\c - -select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]); diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index bdd40cf5a9d..b34a0d7c347 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -327,6 +327,13 @@ create function range_add_bounds(anyrange) select range_add_bounds(numrange(1.0001, 123.123)); +create function rangetypes_sql(q anyrange, b anyarray, out c anyelement) + as $$ select upper($1) + $2[1] $$ + language sql; + +select rangetypes_sql(int4range(1,10), ARRAY[2,20]); +select rangetypes_sql(numrange(1,10), ARRAY[2,20]); -- match failure + -- -- Arrays of ranges -- |