*** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 1419,1424 **** --- 1419,1434 ---- + castexemptor + oid + pg_proc.oid + + The OID of the exemptor function used to identify when this cast is + unnecessary. Zero is stored if the cast has no exemptor function. + + + + castcontext char *** a/doc/src/sgml/ref/create_cast.sgml --- b/doc/src/sgml/ref/create_cast.sgml *************** *** 20,25 **** --- 20,26 ---- CREATE CAST (source_type AS target_type) WITH FUNCTION function_name (argument_type [, ...]) + [ EXEMPTOR function_name (argument_type [, ...]) ] [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_type AS target_type) *************** *** 204,209 **** SELECT CAST ( 2 AS numeric ) + 4.0; --- 205,221 ---- + EXEMPTOR function_name(argument_type [, ...]) + + + + The exemptor function used to identify when this cast is + unnecessary. + + + + + WITHOUT FUNCTION *************** *** 269,290 **** SELECT CAST ( 2 AS numeric ) + 4.0; ! Ordinarily a cast must have different source and target data types. ! However, it is allowed to declare a cast with identical source and ! target types if it has a cast implementation function with more than one ! argument. This is used to represent type-specific length coercion ! functions in the system catalogs. The named function is used to ! coerce a value of the type to the type modifier value given by its ! second argument. ! When a cast has different source and ! target types and a function that takes more than one argument, it ! supports converting from one type to another and applying a length ! coercion in a single step. When no such entry is available, coercion ! to a type that uses a type modifier involves two cast steps, one to ! convert between data types and a second to apply the modifier. --- 281,338 ---- ! Ordinarily a cast must have different source and target data types. However, ! it is allowed to declare a cast with identical source and target types if it ! has a cast implementation function with more than one argument. This is used ! to represent type-specific length coercion functions in the system catalogs. ! The named function is used to coerce a value of the type to the type modifier ! value given by its second argument. ! ! ! ! When a cast has different source and target types and a function that takes ! more than one argument, it supports converting from one type to another and ! applying a length coercion in a single step. When no such entry is ! available, coercion to a type that uses a type modifier involves two cast ! steps, one to convert between data types and a second to apply the modifier. ! These casts may also define an exemptor function, but it is currently unused. ! To minimize unnecessary calls its a cast function, the cast may specify an ! exemptor function. It must take three arguments: an integer source ! type modifier, an integer destination type modifier, and ! a boolean indicating whether the cast is explicit. Its result shall ! be authoritative for all calls to the cast function with the given ! destination type modifier, the given explicitness, and a value most-recently ! coerced to the given source type modifier. It returns an integer, ! which may contain the following bits: ! ! ! ! COERCE_EXEMPT_NOCHANGE (1) ! ! ! If the cast function completes without error, its result will be ! binary-equivalent to its input. ! ! ! ! ! COERCE_EXEMPT_NOERROR (2) ! ! ! The cast function will not throw an error. ! ! ! ! ! ! Consider a length coercion cast implementing a simple notion of length and ! throwing an error when the input value is too long for the destination. Its ! exemptor function might return COERCE_EXEMPT_NOCHANGE | ! COERCE_EXEMPT_NOERROR when the destination type modifier is at least as ! large as the source type modifier. *** a/src/backend/commands/functioncmds.c --- b/src/backend/commands/functioncmds.c *************** *** 1487,1492 **** CreateCast(CreateCastStmt *stmt) --- 1487,1493 ---- char sourcetyptype; char targettyptype; Oid funcid; + Oid exemptorid; int nargs; char castcontext; char castmethod; *************** *** 1599,1604 **** CreateCast(CreateCastStmt *stmt) --- 1600,1669 ---- nargs = 0; } + /* Validate the exemptor function. */ + if (stmt->exemptor != NULL) + { + Form_pg_proc procstruct; + + if (nargs == 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cast function must take two or three arguments to use an exemptor function"))); + + exemptorid = LookupFuncNameTypeNames(stmt->exemptor->funcname, + stmt->exemptor->funcargs, + false); + + tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(exemptorid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", exemptorid); + + procstruct = (Form_pg_proc) GETSTRUCT(tuple); + if (procstruct->pronargs != 3) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("exemptor function must take three arguments"))); + if (procstruct->proargtypes.values[0] != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("first argument of exemptor function must be type integer"))); + if (procstruct->proargtypes.values[1] != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("second argument of exemptor function must be type integer"))); + if (procstruct->proargtypes.values[2] != BOOLOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("third argument of exemptor function must be type boolean"))); + if (procstruct->prorettype != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("return data type of exemptor function must be type integer"))); + if (procstruct->provolatile == PROVOLATILE_VOLATILE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("exemptor function must not be volatile"))); + + if (procstruct->proisagg) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("exemptor function must not be an aggregate function"))); + if (procstruct->proiswindow) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("exemptor function must not be a window function"))); + if (procstruct->proretset) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("exemptor function must not return a set"))); + + ReleaseSysCache(tuple); + } + else + { + exemptorid = InvalidOid; + } + if (castmethod == COERCION_METHOD_BINARY) { int16 typ1len; *************** *** 1725,1730 **** CreateCast(CreateCastStmt *stmt) --- 1790,1796 ---- values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid); values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid); values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid); + values[Anum_pg_cast_castexemptor - 1] = ObjectIdGetDatum(exemptorid); values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext); values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod); *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 1133,1138 **** _copyFuncExpr(FuncExpr *from) --- 1133,1139 ---- COPY_SCALAR_FIELD(funcresulttype); COPY_SCALAR_FIELD(funcretset); COPY_SCALAR_FIELD(funcformat); + COPY_SCALAR_FIELD(funcexempt); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); *************** *** 3503,3508 **** _copyCreateCastStmt(CreateCastStmt *from) --- 3504,3510 ---- COPY_NODE_FIELD(sourcetype); COPY_NODE_FIELD(targettype); COPY_NODE_FIELD(func); + COPY_NODE_FIELD(exemptor); COPY_SCALAR_FIELD(context); COPY_SCALAR_FIELD(inout); *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 236,241 **** _equalFuncExpr(FuncExpr *a, FuncExpr *b) --- 236,242 ---- b->funcformat != COERCE_DONTCARE) return false; + COMPARE_SCALAR_FIELD(funcexempt); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); *************** *** 1883,1888 **** _equalCreateCastStmt(CreateCastStmt *a, CreateCastStmt *b) --- 1884,1890 ---- COMPARE_NODE_FIELD(sourcetype); COMPARE_NODE_FIELD(targettype); COMPARE_NODE_FIELD(func); + COMPARE_NODE_FIELD(exemptor); COMPARE_SCALAR_FIELD(context); COMPARE_SCALAR_FIELD(inout); *** a/src/backend/nodes/makefuncs.c --- b/src/backend/nodes/makefuncs.c *************** *** 447,452 **** makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat) --- 447,453 ---- funcexpr->funcresulttype = rettype; funcexpr->funcretset = false; /* only allowed case here */ funcexpr->funcformat = fformat; + funcexpr->funcexempt = 0; funcexpr->args = args; funcexpr->location = -1; *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 956,961 **** _outFuncExpr(StringInfo str, FuncExpr *node) --- 956,962 ---- WRITE_OID_FIELD(funcresulttype); WRITE_BOOL_FIELD(funcretset); WRITE_ENUM_FIELD(funcformat, CoercionForm); + WRITE_INT_FIELD(funcexempt); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); } *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 525,530 **** _readFuncExpr(void) --- 525,531 ---- READ_OID_FIELD(funcresulttype); READ_BOOL_FIELD(funcretset); READ_ENUM_FIELD(funcformat, CoercionForm); + READ_INT_FIELD(funcexempt); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** *** 2189,2194 **** eval_const_expressions_mutator(Node *node, --- 2189,2195 ---- newexpr->funcresulttype = expr->funcresulttype; newexpr->funcretset = expr->funcretset; newexpr->funcformat = expr->funcformat; + newexpr->funcexempt = 0; newexpr->args = args; newexpr->location = expr->location; return (Node *) newexpr; *************** *** 3663,3668 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args, --- 3664,3670 ---- newexpr->funcresulttype = result_type; newexpr->funcretset = false; newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */ + newexpr->funcexempt = 0; newexpr->args = args; newexpr->location = -1; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 489,495 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT ! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS --- 489,495 ---- DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT ! EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXEMPTOR EXISTS EXPLAIN EXTERNAL EXTRACT FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS *************** *** 5811,5827 **** CreateCastStmt: CREATE CAST '(' Typename AS Typename ')' --- 5811,5842 ---- n->sourcetype = $4; n->targettype = $6; n->func = $10; + n->exemptor = NULL; n->context = (CoercionContext) $11; n->inout = false; $$ = (Node *)n; } | CREATE CAST '(' Typename AS Typename ')' + WITH FUNCTION function_with_argtypes + EXEMPTOR function_with_argtypes cast_context + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = $10; + n->exemptor = $12; + n->context = (CoercionContext) $13; + n->inout = false; + $$ = (Node *)n; + } + | CREATE CAST '(' Typename AS Typename ')' WITHOUT FUNCTION cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); n->sourcetype = $4; n->targettype = $6; n->func = NULL; + n->exemptor = NULL; n->context = (CoercionContext) $10; n->inout = false; $$ = (Node *)n; *************** *** 5833,5838 **** CreateCastStmt: CREATE CAST '(' Typename AS Typename ')' --- 5848,5854 ---- n->sourcetype = $4; n->targettype = $6; n->func = NULL; + n->exemptor = NULL; n->context = (CoercionContext) $10; n->inout = true; $$ = (Node *)n; *************** *** 11371,11376 **** unreserved_keyword: --- 11387,11393 ---- | EXCLUDING | EXCLUSIVE | EXECUTE + | EXEMPTOR | EXPLAIN | EXTERNAL | FAMILY *** a/src/backend/parser/parse_coerce.c --- b/src/backend/parser/parse_coerce.c *************** *** 43,54 **** static Node *build_coercion_expression(Node *node, Oid funcId, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, int location, ! bool isExplicit); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, CoercionForm cformat, int location); static bool is_complex_array(Oid typid); static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId); --- 43,57 ---- Oid funcId, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, int location, ! bool isExplicit, CoerceExemptions exempt); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, CoercionForm cformat, int location); + static CoerceExemptions apply_exemptor_function(Oid exemptId, + int32 sourceTypMod, int32 targetTypMod, + bool isExplicit, CoercionPathType *pathtype); static bool is_complex_array(Oid typid); static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId); *************** *** 91,97 **** coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, ccontext, cformat, location); /* ! * If the target is a fixed-length type, it may need a length coercion as * well as a type coercion. If we find ourselves adding both, force the * inner coercion node to implicit display form. */ --- 94,100 ---- ccontext, cformat, location); /* ! * If the target is a variable-length type, it may need a length coercion as * well as a type coercion. If we find ourselves adding both, force the * inner coercion node to implicit display form. */ *************** *** 129,137 **** coerce_type(ParseState *pstate, Node *node, --- 132,144 ---- Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location) { + Oid baseTypeId; + int32 baseTypeMod; Node *result; CoercionPathType pathtype; Oid funcId; + Oid exemptId; + CoerceExemptions exempt; if (targetTypeId == inputTypeId || node == NULL) *************** *** 182,189 **** coerce_type(ParseState *pstate, Node *node, */ Const *con = (Const *) node; Const *newcon = makeNode(Const); - Oid baseTypeId; - int32 baseTypeMod; int32 inputTypeMod; Type targetType; ParseCallbackState pcbstate; --- 189,194 ---- *************** *** 278,306 **** coerce_type(ParseState *pstate, Node *node, if (result) return result; } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, ! &funcId); if (pathtype != COERCION_PATH_NONE) { if (pathtype != COERCION_PATH_RELABELTYPE) { /* * Generate an expression tree representing run-time application ! * of the conversion function. If we are dealing with a domain ! * target type, the conversion function will yield the base type, ! * and we need to extract the correct typmod to use from the ! * domain's typtypmod. */ - Oid baseTypeId; - int32 baseTypeMod; - - baseTypeMod = targetTypeMod; - baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); - result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, cformat, location, ! (cformat != COERCE_IMPLICIT_CAST)); /* * If domain, coerce to the domain type and relabel with domain --- 283,322 ---- if (result) return result; } + + /* Conventional case. */ pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, ! &funcId, &exemptId); ! if (pathtype != COERCION_PATH_NONE && pathtype != COERCION_PATH_RELABELTYPE) ! { ! /* ! * If we are dealing with a domain target type, the conversion function ! * will yield the base type, and we need to extract the correct typmod ! * to use from the domain's typtypmod. ! */ ! baseTypeMod = targetTypeMod; ! baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); ! ! /* Look for available optimizations. This may change the pathtype. */ ! exempt = apply_exemptor_function(exemptId, ! exprTypmod(node), baseTypeMod, ! cformat != COERCE_IMPLICIT_CAST, ! &pathtype); ! } ! if (pathtype != COERCION_PATH_NONE) { if (pathtype != COERCION_PATH_RELABELTYPE) { /* * Generate an expression tree representing run-time application ! * of the conversion function. */ result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, cformat, location, ! (cformat != COERCE_IMPLICIT_CAST), ! exempt); /* * If domain, coerce to the domain type and relabel with domain *************** *** 418,423 **** can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, --- 434,440 ---- Oid targetTypeId = target_typeids[i]; CoercionPathType pathtype; Oid funcId; + Oid exemptId; /* no problem if same type */ if (inputTypeId == targetTypeId) *************** *** 446,452 **** can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, * both binary-compatible and coercion-function cases. */ pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, ! &funcId); if (pathtype != COERCION_PATH_NONE) continue; --- 463,469 ---- * both binary-compatible and coercion-function cases. */ pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, ! &funcId, &exemptId); if (pathtype != COERCION_PATH_NONE) continue; *************** *** 611,616 **** coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, --- 628,635 ---- { CoercionPathType pathtype; Oid funcId; + Oid exemptId; + CoerceExemptions exempt; /* * A negative typmod is assumed to mean that no coercion is wanted. Also, *************** *** 619,627 **** coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; ! pathtype = find_typmod_coercion_function(targetTypeId, &funcId); ! if (pathtype != COERCION_PATH_NONE) { /* Suppress display of nested coercion steps */ if (hideInputCoercion) --- 638,648 ---- if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; ! pathtype = find_typmod_coercion_function(targetTypeId, &funcId, &exemptId); ! exempt = apply_exemptor_function(exemptId, exprTypmod(node), targetTypMod, ! isExplicit, &pathtype); ! if (pathtype != COERCION_PATH_RELABELTYPE) { /* Suppress display of nested coercion steps */ if (hideInputCoercion) *************** *** 630,636 **** coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, node = build_coercion_expression(node, pathtype, funcId, targetTypeId, targetTypMod, cformat, location, ! isExplicit); } return node; --- 651,657 ---- node = build_coercion_expression(node, pathtype, funcId, targetTypeId, targetTypMod, cformat, location, ! isExplicit, exempt); } return node; *************** *** 680,686 **** build_coercion_expression(Node *node, Oid funcId, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, int location, ! bool isExplicit) { int nargs = 0; --- 701,707 ---- Oid funcId, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, int location, ! bool isExplicit, CoerceExemptions exempt) { int nargs = 0; *************** *** 751,756 **** build_coercion_expression(Node *node, --- 772,778 ---- } fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat); + fexpr->funcexempt = exempt; fexpr->location = location; return (Node *) fexpr; } *************** *** 1821,1827 **** IsBinaryCoercible(Oid srctype, Oid targettype) * 3. A CoerceToDomain node inherits any always-noop invariant from its sole * argument. When GetDomainConstraints() == NIL, it also inherits * never-error. Otherwise, never-error becomes false. ! * 4. All other nodes have neither invariant. * * Returns a bit string that may contain the following bits: * COERCE_EXEMPT_NOCHANGE: expression result will always have the same binary --- 1843,1853 ---- * 3. A CoerceToDomain node inherits any always-noop invariant from its sole * argument. When GetDomainConstraints() == NIL, it also inherits * never-error. Otherwise, never-error becomes false. ! * 4. A FuncExpr has the intersection of the invariants noted in its funcexempt ! * field and the invariants from its first argument. These nodes will ! * represent calls to length coercion casts, so the first argument holds ! * the datum to coerce. ! * 5. All other nodes have neither invariant. * * Returns a bit string that may contain the following bits: * COERCE_EXEMPT_NOCHANGE: expression result will always have the same binary *************** *** 1857,1862 **** GetCoerceExemptions(Node *expr, --- 1883,1897 ---- ret &= ~COERCE_EXEMPT_NOERROR; expr = (Node *) d->arg; } + else if (IsA(expr, FuncExpr)) + { + FuncExpr *f = (FuncExpr *) expr; + + ret &= f->funcexempt; + expr = linitial(f->args); + if (expr == NULL) + return 0; + } else { return 0; *************** *** 1893,1906 **** GetCoerceExemptions(Node *expr, * conversion then use IsBinaryCoercible(). */ CoercionPathType ! find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, ! Oid *funcid) { CoercionPathType result = COERCION_PATH_NONE; HeapTuple tuple; *funcid = InvalidOid; /* Perhaps the types are domains; if so, look at their base types */ if (OidIsValid(sourceTypeId)) --- 1928,1943 ---- * conversion then use IsBinaryCoercible(). */ CoercionPathType ! find_coercion_pathway(Oid targetTypeId, ! Oid sourceTypeId, CoercionContext ccontext, ! Oid *funcid, Oid *exemptid) { CoercionPathType result = COERCION_PATH_NONE; HeapTuple tuple; *funcid = InvalidOid; + *exemptid = InvalidOid; /* Perhaps the types are domains; if so, look at their base types */ if (OidIsValid(sourceTypeId)) *************** *** 1949,1954 **** find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, --- 1986,1992 ---- case COERCION_METHOD_FUNCTION: result = COERCION_PATH_FUNC; *funcid = castForm->castfunc; + *exemptid = castForm->castexemptor; break; case COERCION_METHOD_INOUT: result = COERCION_PATH_COERCEVIAIO; *************** *** 1992,2006 **** find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, { CoercionPathType elempathtype; Oid elemfuncid; elempathtype = find_coercion_pathway(targetElem, sourceElem, ccontext, ! &elemfuncid); if (elempathtype != COERCION_PATH_NONE && elempathtype != COERCION_PATH_ARRAYCOERCE) { *funcid = elemfuncid; if (elempathtype == COERCION_PATH_COERCEVIAIO) result = COERCION_PATH_COERCEVIAIO; else --- 2030,2047 ---- { CoercionPathType elempathtype; Oid elemfuncid; + Oid elemexemptorid; elempathtype = find_coercion_pathway(targetElem, sourceElem, ccontext, ! &elemfuncid, ! &elemexemptorid); if (elempathtype != COERCION_PATH_NONE && elempathtype != COERCION_PATH_ARRAYCOERCE) { *funcid = elemfuncid; + *exemptid = elemexemptorid; if (elempathtype == COERCION_PATH_COERCEVIAIO) result = COERCION_PATH_COERCEVIAIO; else *************** *** 2038,2044 **** find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, * find_typmod_coercion_function -- does the given type need length coercion? * * If the target type possesses a pg_cast function from itself to itself, ! * it must need length coercion. * * "bpchar" (ie, char(N)) and "numeric" are examples of such types. * --- 2079,2085 ---- * find_typmod_coercion_function -- does the given type need length coercion? * * If the target type possesses a pg_cast function from itself to itself, ! * it needs length coercion at least some of the time. * * "bpchar" (ie, char(N)) and "numeric" are examples of such types. * *************** *** 2050,2061 **** find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, * We use the same result enum as find_coercion_pathway, but the only possible * result codes are: * COERCION_PATH_NONE: no length coercion needed ! * COERCION_PATH_FUNC: apply the function returned in *funcid * COERCION_PATH_ARRAYCOERCE: apply the function using ArrayCoerceExpr */ CoercionPathType find_typmod_coercion_function(Oid typeId, ! Oid *funcid) { CoercionPathType result; Type targetType; --- 2091,2103 ---- * We use the same result enum as find_coercion_pathway, but the only possible * result codes are: * COERCION_PATH_NONE: no length coercion needed ! * COERCION_PATH_FUNC: apply the function returned in *funcid, subject to ! * exemptions in *exempt * COERCION_PATH_ARRAYCOERCE: apply the function using ArrayCoerceExpr */ CoercionPathType find_typmod_coercion_function(Oid typeId, ! Oid *funcid, Oid *exemptid) { CoercionPathType result; Type targetType; *************** *** 2063,2068 **** find_typmod_coercion_function(Oid typeId, --- 2105,2111 ---- HeapTuple tuple; *funcid = InvalidOid; + *exemptid = InvalidOid; result = COERCION_PATH_FUNC; targetType = typeidType(typeId); *************** *** 2087,2101 **** find_typmod_coercion_function(Oid typeId, Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); *funcid = castForm->castfunc; ReleaseSysCache(tuple); } if (!OidIsValid(*funcid)) ! result = COERCION_PATH_NONE; return result; } /* * is_complex_array * Is this type an array of composite? --- 2130,2171 ---- Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); *funcid = castForm->castfunc; + *exemptid = castForm->castexemptor; ReleaseSysCache(tuple); } if (!OidIsValid(*funcid)) ! result = COERCION_PATH_RELABELTYPE; return result; } + + static CoerceExemptions + apply_exemptor_function(Oid exemptId, + int32 sourceTypMod, int32 targetTypMod, + bool isExplicit, CoercionPathType *pathtype) + { + CoerceExemptions ret = 0; + + /* + * Call the exemptor function, if any; its verdict may let us skip calls to + * the length coercion function entirely. + */ + if (OidIsValid(exemptId)) + { + ret = DatumGetInt32(OidFunctionCall3(exemptId, + Int32GetDatum(sourceTypMod), + Int32GetDatum(targetTypMod), + BoolGetDatum(isExplicit))); + if (ret == (COERCE_EXEMPT_NOCHANGE | COERCE_EXEMPT_NOERROR)) + *pathtype = COERCION_PATH_RELABELTYPE; + } + + return ret; + } + + /* * is_complex_array * Is this type an array of composite? *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** *** 382,387 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 382,388 ---- funcexpr->funcresulttype = rettype; funcexpr->funcretset = retset; funcexpr->funcformat = COERCE_EXPLICIT_CALL; + funcexpr->funcexempt = 0; funcexpr->args = fargs; funcexpr->location = location; *************** *** 1018,1027 **** func_get_detail(List *funcname, { CoercionPathType cpathtype; Oid cfuncid; cpathtype = find_coercion_pathway(targetType, sourceType, COERCION_EXPLICIT, ! &cfuncid); switch (cpathtype) { case COERCION_PATH_RELABELTYPE: --- 1019,1029 ---- { CoercionPathType cpathtype; Oid cfuncid; + Oid cexemptid; cpathtype = find_coercion_pathway(targetType, sourceType, COERCION_EXPLICIT, ! &cfuncid, &cexemptid); switch (cpathtype) { case COERCION_PATH_RELABELTYPE: *** a/src/backend/utils/adt/ri_triggers.c --- b/src/backend/utils/adt/ri_triggers.c *************** *** 3950,3956 **** ri_HashCompareOp(Oid eq_opr, Oid typeid) { Oid lefttype, righttype, ! castfunc; CoercionPathType pathtype; /* We always need to know how to call the equality operator */ --- 3950,3957 ---- { Oid lefttype, righttype, ! castfunc, ! castexempt; CoercionPathType pathtype; /* We always need to know how to call the equality operator */ *************** *** 3977,3983 **** ri_HashCompareOp(Oid eq_opr, Oid typeid) { pathtype = find_coercion_pathway(lefttype, typeid, COERCION_IMPLICIT, ! &castfunc); if (pathtype != COERCION_PATH_FUNC && pathtype != COERCION_PATH_RELABELTYPE) { --- 3978,3984 ---- { pathtype = find_coercion_pathway(lefttype, typeid, COERCION_IMPLICIT, ! &castfunc, &castexempt); if (pathtype != COERCION_PATH_FUNC && pathtype != COERCION_PATH_RELABELTYPE) { *** a/src/backend/utils/adt/varchar.c --- b/src/backend/utils/adt/varchar.c *************** *** 549,554 **** varcharsend(PG_FUNCTION_ARGS) --- 549,568 ---- /* + * Identify superfluous calls to our length coercion function. + */ + Datum + varchar_exemptor(PG_FUNCTION_ARGS) + { + int32 old_max = PG_GETARG_INT32(0) - VARHDRSZ; + int32 new_max = PG_GETARG_INT32(1) - VARHDRSZ; + + if (new_max < 0 || (old_max >= 0 && old_max <= new_max)) + PG_RETURN_INT32(COERCE_EXEMPT_NOCHANGE | COERCE_EXEMPT_NOERROR); + PG_RETURN_INT32(0); + } + + /* * Converts a VARCHAR type to the specified size. * * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. *** a/src/backend/utils/adt/xml.c --- b/src/backend/utils/adt/xml.c *************** *** 508,513 **** xmlconcat2(PG_FUNCTION_ARGS) --- 508,520 ---- } + /* texttoxml can only throw an error, never change the data. */ + Datum + xml_exemptor(PG_FUNCTION_ARGS) + { + PG_RETURN_INT32(COERCE_EXEMPT_NOCHANGE); + } + Datum texttoxml(PG_FUNCTION_ARGS) { *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 5031,5054 **** getCasts(int *numCasts) int i_castsource; int i_casttarget; int i_castfunc; int i_castcontext; int i_castmethod; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); ! if (g_fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT tableoid, oid, " "castsource, casttarget, castfunc, castcontext, " ! "castmethod " "FROM pg_cast ORDER BY 3,4"); } else if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, " "castsource, casttarget, castfunc, castcontext, " ! "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod " "FROM pg_cast ORDER BY 3,4"); } else --- 5031,5063 ---- int i_castsource; int i_casttarget; int i_castfunc; + int i_castexemptor; int i_castcontext; int i_castmethod; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); ! if (g_fout->remoteVersion >= 90100) ! { ! appendPQExpBuffer(query, "SELECT tableoid, oid, " ! "castsource, casttarget, castfunc, castcontext, " ! "castmethod, castexemptor " ! "FROM pg_cast ORDER BY 3,4"); ! } ! else if (g_fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT tableoid, oid, " "castsource, casttarget, castfunc, castcontext, " ! "castmethod, 0 AS castexemptor " "FROM pg_cast ORDER BY 3,4"); } else if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, " "castsource, casttarget, castfunc, castcontext, " ! "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END AS castmethod, " ! "0 AS castexemptor " "FROM pg_cast ORDER BY 3,4"); } else *************** *** 5056,5062 **** getCasts(int *numCasts) appendPQExpBuffer(query, "SELECT 0 AS tableoid, p.oid, " "t1.oid AS castsource, t2.oid AS casttarget, " "p.oid AS castfunc, 'e' AS castcontext, " ! "'f' AS castmethod " "FROM pg_type t1, pg_type t2, pg_proc p " "WHERE p.pronargs = 1 AND " "p.proargtypes[0] = t1.oid AND " --- 5065,5072 ---- appendPQExpBuffer(query, "SELECT 0 AS tableoid, p.oid, " "t1.oid AS castsource, t2.oid AS casttarget, " "p.oid AS castfunc, 'e' AS castcontext, " ! "'f' AS castmethod, " ! "0 AS castexemptor " "FROM pg_type t1, pg_type t2, pg_proc p " "WHERE p.pronargs = 1 AND " "p.proargtypes[0] = t1.oid AND " *************** *** 5078,5083 **** getCasts(int *numCasts) --- 5088,5094 ---- i_castsource = PQfnumber(res, "castsource"); i_casttarget = PQfnumber(res, "casttarget"); i_castfunc = PQfnumber(res, "castfunc"); + i_castexemptor = PQfnumber(res, "castexemptor"); i_castcontext = PQfnumber(res, "castcontext"); i_castmethod = PQfnumber(res, "castmethod"); *************** *** 5094,5099 **** getCasts(int *numCasts) --- 5105,5111 ---- castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource)); castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget)); castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc)); + castinfo[i].castexemptor = atooid(PQgetvalue(res, i, i_castexemptor)); castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext)); castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod)); *************** *** 5124,5129 **** getCasts(int *numCasts) --- 5136,5142 ---- addObjectDependency(&castinfo[i].dobj, funcInfo->dobj.dumpId); } + /* Exemptor functions postdate the dependency mechanism. */ } PQclear(res); *************** *** 8330,8335 **** dumpCast(Archive *fout, CastInfo *cast) --- 8343,8349 ---- PQExpBuffer delqry; PQExpBuffer castsig; FuncInfo *funcInfo = NULL; + FuncInfo *exemptInfo = NULL; TypeInfo *sourceInfo; TypeInfo *targetInfo; *************** *** 8340,8345 **** dumpCast(Archive *fout, CastInfo *cast) --- 8354,8367 ---- { funcInfo = findFuncByOid(cast->castfunc); if (funcInfo == NULL) + /* XXX: is silently bailing correct? */ + return; + } + if (OidIsValid(cast->castexemptor)) + { + exemptInfo = findFuncByOid(cast->castexemptor); + if (exemptInfo == NULL) + /* XXX: is silently bailing correct? */ return; } *************** *** 8360,8375 **** dumpCast(Archive *fout, CastInfo *cast) */ if ((funcInfo == NULL || strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) && strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) == 0 && strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) return; /* ! * Skip cast if function isn't from pg_ and is not to be dumped. */ ! if (funcInfo && ! strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 && ! !funcInfo->dobj.dump) return; /* --- 8382,8402 ---- */ if ((funcInfo == NULL || strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) && + (exemptInfo == NULL || + strncmp(exemptInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) && strncmp(sourceInfo->dobj.namespace->dobj.name, "pg_", 3) == 0 && strncmp(targetInfo->dobj.namespace->dobj.name, "pg_", 3) == 0) return; /* ! * Skip cast if a function isn't from pg_ and is not to be dumped. */ ! if ((funcInfo && ! strncmp(funcInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 && ! !funcInfo->dobj.dump) || ! (exemptInfo && ! strncmp(exemptInfo->dobj.namespace->dobj.name, "pg_", 3) != 0 && ! !exemptInfo->dobj.dump)) return; /* *************** *** 8419,8424 **** dumpCast(Archive *fout, CastInfo *cast) --- 8446,8459 ---- fmtId(funcInfo->dobj.namespace->dobj.name)); appendPQExpBuffer(defqry, "%s", format_function_signature(funcInfo, true)); + + if (exemptInfo != NULL) + { + appendPQExpBuffer(defqry, " EXEMPTOR %s.", + fmtId(exemptInfo->dobj.namespace->dobj.name)); + appendPQExpBuffer(defqry, "%s", + format_function_signature(exemptInfo, true)); + } break; default: write_msg(NULL, "WARNING: bogus value in pg_cast.castmethod field\n"); *** a/src/bin/pg_dump/pg_dump.h --- b/src/bin/pg_dump/pg_dump.h *************** *** 373,378 **** typedef struct _castInfo --- 373,379 ---- Oid castsource; Oid casttarget; Oid castfunc; + Oid castexemptor; char castcontext; char castmethod; } CastInfo; *** a/src/include/catalog/catversion.h --- b/src/include/catalog/catversion.h *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 201101081 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 201101101 #endif *** a/src/include/catalog/pg_cast.h --- b/src/include/catalog/pg_cast.h *************** *** 35,40 **** CATALOG(pg_cast,2605) --- 35,41 ---- Oid castsource; /* source datatype for cast */ Oid casttarget; /* destination datatype for cast */ Oid castfunc; /* cast function; 0 = binary coercible */ + Oid castexemptor; /* exemptor function; 0 = none */ char castcontext; /* contexts in which cast can be used */ char castmethod; /* cast method */ } FormData_pg_cast; *************** *** 74,85 **** typedef enum CoercionMethod * compiler constants for pg_cast * ---------------- */ ! #define Natts_pg_cast 5 #define Anum_pg_cast_castsource 1 #define Anum_pg_cast_casttarget 2 #define Anum_pg_cast_castfunc 3 ! #define Anum_pg_cast_castcontext 4 ! #define Anum_pg_cast_castmethod 5 /* ---------------- * initial contents of pg_cast --- 75,87 ---- * compiler constants for pg_cast * ---------------- */ ! #define Natts_pg_cast 6 #define Anum_pg_cast_castsource 1 #define Anum_pg_cast_casttarget 2 #define Anum_pg_cast_castfunc 3 ! #define Anum_pg_cast_castexemptor 4 ! #define Anum_pg_cast_castcontext 5 ! #define Anum_pg_cast_castmethod 6 /* ---------------- * initial contents of pg_cast *************** *** 94,135 **** typedef enum CoercionMethod * int2->int4->int8->numeric->float4->float8, while casts in the * reverse direction are assignment-only. */ ! DATA(insert ( 20 21 714 a f )); ! DATA(insert ( 20 23 480 a f )); ! DATA(insert ( 20 700 652 i f )); ! DATA(insert ( 20 701 482 i f )); ! DATA(insert ( 20 1700 1781 i f )); ! DATA(insert ( 21 20 754 i f )); ! DATA(insert ( 21 23 313 i f )); ! DATA(insert ( 21 700 236 i f )); ! DATA(insert ( 21 701 235 i f )); ! DATA(insert ( 21 1700 1782 i f )); ! DATA(insert ( 23 20 481 i f )); ! DATA(insert ( 23 21 314 a f )); ! DATA(insert ( 23 700 318 i f )); ! DATA(insert ( 23 701 316 i f )); ! DATA(insert ( 23 1700 1740 i f )); ! DATA(insert ( 700 20 653 a f )); ! DATA(insert ( 700 21 238 a f )); ! DATA(insert ( 700 23 319 a f )); ! DATA(insert ( 700 701 311 i f )); ! DATA(insert ( 700 1700 1742 a f )); ! DATA(insert ( 701 20 483 a f )); ! DATA(insert ( 701 21 237 a f )); ! DATA(insert ( 701 23 317 a f )); ! DATA(insert ( 701 700 312 a f )); ! DATA(insert ( 701 1700 1743 a f )); ! DATA(insert ( 1700 20 1779 a f )); ! DATA(insert ( 1700 21 1783 a f )); ! DATA(insert ( 1700 23 1744 a f )); ! DATA(insert ( 1700 700 1745 i f )); ! DATA(insert ( 1700 701 1746 i f )); ! DATA(insert ( 790 1700 3823 a f )); ! DATA(insert ( 1700 790 3824 a f )); /* Allow explicit coercions between int4 and bool */ ! DATA(insert ( 23 16 2557 e f )); ! DATA(insert ( 16 23 2558 e f )); /* * OID category: allow implicit conversion from any integral type (including --- 96,137 ---- * int2->int4->int8->numeric->float4->float8, while casts in the * reverse direction are assignment-only. */ ! DATA(insert ( 20 21 714 0 a f )); ! DATA(insert ( 20 23 480 0 a f )); ! DATA(insert ( 20 700 652 0 i f )); ! DATA(insert ( 20 701 482 0 i f )); ! DATA(insert ( 20 1700 1781 0 i f )); ! DATA(insert ( 21 20 754 0 i f )); ! DATA(insert ( 21 23 313 0 i f )); ! DATA(insert ( 21 700 236 0 i f )); ! DATA(insert ( 21 701 235 0 i f )); ! DATA(insert ( 21 1700 1782 0 i f )); ! DATA(insert ( 23 20 481 0 i f )); ! DATA(insert ( 23 21 314 0 a f )); ! DATA(insert ( 23 700 318 0 i f )); ! DATA(insert ( 23 701 316 0 i f )); ! DATA(insert ( 23 1700 1740 0 i f )); ! DATA(insert ( 700 20 653 0 a f )); ! DATA(insert ( 700 21 238 0 a f )); ! DATA(insert ( 700 23 319 0 a f )); ! DATA(insert ( 700 701 311 0 i f )); ! DATA(insert ( 700 1700 1742 0 a f )); ! DATA(insert ( 701 20 483 0 a f )); ! DATA(insert ( 701 21 237 0 a f )); ! DATA(insert ( 701 23 317 0 a f )); ! DATA(insert ( 701 700 312 0 a f )); ! DATA(insert ( 701 1700 1743 0 a f )); ! DATA(insert ( 1700 20 1779 0 a f )); ! DATA(insert ( 1700 21 1783 0 a f )); ! DATA(insert ( 1700 23 1744 0 a f )); ! DATA(insert ( 1700 700 1745 0 i f )); ! DATA(insert ( 1700 701 1746 0 i f )); ! DATA(insert ( 790 1700 3823 0 a f )); ! DATA(insert ( 1700 790 3824 0 a f )); /* Allow explicit coercions between int4 and bool */ ! DATA(insert ( 23 16 2557 0 e f )); ! DATA(insert ( 16 23 2558 0 e f )); /* * OID category: allow implicit conversion from any integral type (including *************** *** 141,307 **** DATA(insert ( 16 23 2558 e f )); * casts from text and varchar to regclass, which exist mainly to support * legacy forms of nextval() and related functions. */ ! DATA(insert ( 20 26 1287 i f )); ! DATA(insert ( 21 26 313 i f )); ! DATA(insert ( 23 26 0 i b )); ! DATA(insert ( 26 20 1288 a f )); ! DATA(insert ( 26 23 0 a b )); ! DATA(insert ( 26 24 0 i b )); ! DATA(insert ( 24 26 0 i b )); ! DATA(insert ( 20 24 1287 i f )); ! DATA(insert ( 21 24 313 i f )); ! DATA(insert ( 23 24 0 i b )); ! DATA(insert ( 24 20 1288 a f )); ! DATA(insert ( 24 23 0 a b )); ! DATA(insert ( 24 2202 0 i b )); ! DATA(insert ( 2202 24 0 i b )); ! DATA(insert ( 26 2202 0 i b )); ! DATA(insert ( 2202 26 0 i b )); ! DATA(insert ( 20 2202 1287 i f )); ! DATA(insert ( 21 2202 313 i f )); ! DATA(insert ( 23 2202 0 i b )); ! DATA(insert ( 2202 20 1288 a f )); ! DATA(insert ( 2202 23 0 a b )); ! DATA(insert ( 26 2203 0 i b )); ! DATA(insert ( 2203 26 0 i b )); ! DATA(insert ( 20 2203 1287 i f )); ! DATA(insert ( 21 2203 313 i f )); ! DATA(insert ( 23 2203 0 i b )); ! DATA(insert ( 2203 20 1288 a f )); ! DATA(insert ( 2203 23 0 a b )); ! DATA(insert ( 2203 2204 0 i b )); ! DATA(insert ( 2204 2203 0 i b )); ! DATA(insert ( 26 2204 0 i b )); ! DATA(insert ( 2204 26 0 i b )); ! DATA(insert ( 20 2204 1287 i f )); ! DATA(insert ( 21 2204 313 i f )); ! DATA(insert ( 23 2204 0 i b )); ! DATA(insert ( 2204 20 1288 a f )); ! DATA(insert ( 2204 23 0 a b )); ! DATA(insert ( 26 2205 0 i b )); ! DATA(insert ( 2205 26 0 i b )); ! DATA(insert ( 20 2205 1287 i f )); ! DATA(insert ( 21 2205 313 i f )); ! DATA(insert ( 23 2205 0 i b )); ! DATA(insert ( 2205 20 1288 a f )); ! DATA(insert ( 2205 23 0 a b )); ! DATA(insert ( 26 2206 0 i b )); ! DATA(insert ( 2206 26 0 i b )); ! DATA(insert ( 20 2206 1287 i f )); ! DATA(insert ( 21 2206 313 i f )); ! DATA(insert ( 23 2206 0 i b )); ! DATA(insert ( 2206 20 1288 a f )); ! DATA(insert ( 2206 23 0 a b )); ! DATA(insert ( 26 3734 0 i b )); ! DATA(insert ( 3734 26 0 i b )); ! DATA(insert ( 20 3734 1287 i f )); ! DATA(insert ( 21 3734 313 i f )); ! DATA(insert ( 23 3734 0 i b )); ! DATA(insert ( 3734 20 1288 a f )); ! DATA(insert ( 3734 23 0 a b )); ! DATA(insert ( 26 3769 0 i b )); ! DATA(insert ( 3769 26 0 i b )); ! DATA(insert ( 20 3769 1287 i f )); ! DATA(insert ( 21 3769 313 i f )); ! DATA(insert ( 23 3769 0 i b )); ! DATA(insert ( 3769 20 1288 a f )); ! DATA(insert ( 3769 23 0 a b )); ! DATA(insert ( 25 2205 1079 i f )); ! DATA(insert ( 1043 2205 1079 i f )); /* * String category */ ! DATA(insert ( 25 1042 0 i b )); ! DATA(insert ( 25 1043 0 i b )); ! DATA(insert ( 1042 25 401 i f )); ! DATA(insert ( 1042 1043 401 i f )); ! DATA(insert ( 1043 25 0 i b )); ! DATA(insert ( 1043 1042 0 i b )); ! DATA(insert ( 18 25 946 i f )); ! DATA(insert ( 18 1042 860 a f )); ! DATA(insert ( 18 1043 946 a f )); ! DATA(insert ( 19 25 406 i f )); ! DATA(insert ( 19 1042 408 a f )); ! DATA(insert ( 19 1043 1401 a f )); ! DATA(insert ( 25 18 944 a f )); ! DATA(insert ( 1042 18 944 a f )); ! DATA(insert ( 1043 18 944 a f )); ! DATA(insert ( 25 19 407 i f )); ! DATA(insert ( 1042 19 409 i f )); ! DATA(insert ( 1043 19 1400 i f )); /* Allow explicit coercions between int4 and "char" */ ! DATA(insert ( 18 23 77 e f )); ! DATA(insert ( 23 18 78 e f )); /* pg_node_tree can be coerced to, but not from, text */ ! DATA(insert ( 194 25 0 i b )); /* * Datetime category */ ! DATA(insert ( 702 1082 1179 a f )); ! DATA(insert ( 702 1083 1364 a f )); ! DATA(insert ( 702 1114 2023 i f )); ! DATA(insert ( 702 1184 1173 i f )); ! DATA(insert ( 703 1186 1177 i f )); ! DATA(insert ( 1082 1114 2024 i f )); ! DATA(insert ( 1082 1184 1174 i f )); ! DATA(insert ( 1083 1186 1370 i f )); ! DATA(insert ( 1083 1266 2047 i f )); ! DATA(insert ( 1114 702 2030 a f )); ! DATA(insert ( 1114 1082 2029 a f )); ! DATA(insert ( 1114 1083 1316 a f )); ! DATA(insert ( 1114 1184 2028 i f )); ! DATA(insert ( 1184 702 1180 a f )); ! DATA(insert ( 1184 1082 1178 a f )); ! DATA(insert ( 1184 1083 2019 a f )); ! DATA(insert ( 1184 1114 2027 a f )); ! DATA(insert ( 1184 1266 1388 a f )); ! DATA(insert ( 1186 703 1194 a f )); ! DATA(insert ( 1186 1083 1419 a f )); ! DATA(insert ( 1266 1083 2046 a f )); /* Cross-category casts between int4 and abstime, reltime */ ! DATA(insert ( 23 702 0 e b )); ! DATA(insert ( 702 23 0 e b )); ! DATA(insert ( 23 703 0 e b )); ! DATA(insert ( 703 23 0 e b )); /* * Geometric category */ ! DATA(insert ( 601 600 1532 e f )); ! DATA(insert ( 602 600 1533 e f )); ! DATA(insert ( 602 604 1449 a f )); ! DATA(insert ( 603 600 1534 e f )); ! DATA(insert ( 603 601 1541 e f )); ! DATA(insert ( 603 604 1448 a f )); ! DATA(insert ( 603 718 1479 e f )); ! DATA(insert ( 604 600 1540 e f )); ! DATA(insert ( 604 602 1447 a f )); ! DATA(insert ( 604 603 1446 e f )); ! DATA(insert ( 604 718 1474 e f )); ! DATA(insert ( 718 600 1416 e f )); ! DATA(insert ( 718 603 1480 e f )); ! DATA(insert ( 718 604 1544 e f )); /* * INET category */ ! DATA(insert ( 650 869 0 i b )); ! DATA(insert ( 869 650 1715 a f )); /* * BitString category */ ! DATA(insert ( 1560 1562 0 i b )); ! DATA(insert ( 1562 1560 0 i b )); /* Cross-category casts between bit and int4, int8 */ ! DATA(insert ( 20 1560 2075 e f )); ! DATA(insert ( 23 1560 1683 e f )); ! DATA(insert ( 1560 20 2076 e f )); ! DATA(insert ( 1560 23 1684 e f )); /* * Cross-category casts to and from TEXT --- 143,309 ---- * casts from text and varchar to regclass, which exist mainly to support * legacy forms of nextval() and related functions. */ ! DATA(insert ( 20 26 1287 0 i f )); ! DATA(insert ( 21 26 313 0 i f )); ! DATA(insert ( 23 26 0 0 i b )); ! DATA(insert ( 26 20 1288 0 a f )); ! DATA(insert ( 26 23 0 0 a b )); ! DATA(insert ( 26 24 0 0 i b )); ! DATA(insert ( 24 26 0 0 i b )); ! DATA(insert ( 20 24 1287 0 i f )); ! DATA(insert ( 21 24 313 0 i f )); ! DATA(insert ( 23 24 0 0 i b )); ! DATA(insert ( 24 20 1288 0 a f )); ! DATA(insert ( 24 23 0 0 a b )); ! DATA(insert ( 24 2202 0 0 i b )); ! DATA(insert ( 2202 24 0 0 i b )); ! DATA(insert ( 26 2202 0 0 i b )); ! DATA(insert ( 2202 26 0 0 i b )); ! DATA(insert ( 20 2202 1287 0 i f )); ! DATA(insert ( 21 2202 313 0 i f )); ! DATA(insert ( 23 2202 0 0 i b )); ! DATA(insert ( 2202 20 1288 0 a f )); ! DATA(insert ( 2202 23 0 0 a b )); ! DATA(insert ( 26 2203 0 0 i b )); ! DATA(insert ( 2203 26 0 0 i b )); ! DATA(insert ( 20 2203 1287 0 i f )); ! DATA(insert ( 21 2203 313 0 i f )); ! DATA(insert ( 23 2203 0 0 i b )); ! DATA(insert ( 2203 20 1288 0 a f )); ! DATA(insert ( 2203 23 0 0 a b )); ! DATA(insert ( 2203 2204 0 0 i b )); ! DATA(insert ( 2204 2203 0 0 i b )); ! DATA(insert ( 26 2204 0 0 i b )); ! DATA(insert ( 2204 26 0 0 i b )); ! DATA(insert ( 20 2204 1287 0 i f )); ! DATA(insert ( 21 2204 313 0 i f )); ! DATA(insert ( 23 2204 0 0 i b )); ! DATA(insert ( 2204 20 1288 0 a f )); ! DATA(insert ( 2204 23 0 0 a b )); ! DATA(insert ( 26 2205 0 0 i b )); ! DATA(insert ( 2205 26 0 0 i b )); ! DATA(insert ( 20 2205 1287 0 i f )); ! DATA(insert ( 21 2205 313 0 i f )); ! DATA(insert ( 23 2205 0 0 i b )); ! DATA(insert ( 2205 20 1288 0 a f )); ! DATA(insert ( 2205 23 0 0 a b )); ! DATA(insert ( 26 2206 0 0 i b )); ! DATA(insert ( 2206 26 0 0 i b )); ! DATA(insert ( 20 2206 1287 0 i f )); ! DATA(insert ( 21 2206 313 0 i f )); ! DATA(insert ( 23 2206 0 0 i b )); ! DATA(insert ( 2206 20 1288 0 a f )); ! DATA(insert ( 2206 23 0 0 a b )); ! DATA(insert ( 26 3734 0 0 i b )); ! DATA(insert ( 3734 26 0 0 i b )); ! DATA(insert ( 20 3734 1287 0 i f )); ! DATA(insert ( 21 3734 313 0 i f )); ! DATA(insert ( 23 3734 0 0 i b )); ! DATA(insert ( 3734 20 1288 0 a f )); ! DATA(insert ( 3734 23 0 0 a b )); ! DATA(insert ( 26 3769 0 0 i b )); ! DATA(insert ( 3769 26 0 0 i b )); ! DATA(insert ( 20 3769 1287 0 i f )); ! DATA(insert ( 21 3769 313 0 i f )); ! DATA(insert ( 23 3769 0 0 i b )); ! DATA(insert ( 3769 20 1288 0 a f )); ! DATA(insert ( 3769 23 0 0 a b )); ! DATA(insert ( 25 2205 1079 0 i f )); ! DATA(insert ( 1043 2205 1079 0 i f )); /* * String category */ ! DATA(insert ( 25 1042 0 0 i b )); ! DATA(insert ( 25 1043 0 0 i b )); ! DATA(insert ( 1042 25 401 0 i f )); ! DATA(insert ( 1042 1043 401 0 i f )); ! DATA(insert ( 1043 25 0 0 i b )); ! DATA(insert ( 1043 1042 0 0 i b )); ! DATA(insert ( 18 25 946 0 i f )); ! DATA(insert ( 18 1042 860 0 a f )); ! DATA(insert ( 18 1043 946 0 a f )); ! DATA(insert ( 19 25 406 0 i f )); ! DATA(insert ( 19 1042 408 0 a f )); ! DATA(insert ( 19 1043 1401 0 a f )); ! DATA(insert ( 25 18 944 0 a f )); ! DATA(insert ( 1042 18 944 0 a f )); ! DATA(insert ( 1043 18 944 0 a f )); ! DATA(insert ( 25 19 407 0 i f )); ! DATA(insert ( 1042 19 409 0 i f )); ! DATA(insert ( 1043 19 1400 0 i f )); /* Allow explicit coercions between int4 and "char" */ ! DATA(insert ( 18 23 77 0 e f )); ! DATA(insert ( 23 18 78 0 e f )); /* pg_node_tree can be coerced to, but not from, text */ ! DATA(insert ( 194 25 0 0 i b )); /* * Datetime category */ ! DATA(insert ( 702 1082 1179 0 a f )); ! DATA(insert ( 702 1083 1364 0 a f )); ! DATA(insert ( 702 1114 2023 0 i f )); ! DATA(insert ( 702 1184 1173 0 i f )); ! DATA(insert ( 703 1186 1177 0 i f )); ! DATA(insert ( 1082 1114 2024 0 i f )); ! DATA(insert ( 1082 1184 1174 0 i f )); ! DATA(insert ( 1083 1186 1370 0 i f )); ! DATA(insert ( 1083 1266 2047 0 i f )); ! DATA(insert ( 1114 702 2030 0 a f )); ! DATA(insert ( 1114 1082 2029 0 a f )); ! DATA(insert ( 1114 1083 1316 0 a f )); ! DATA(insert ( 1114 1184 2028 0 i f )); ! DATA(insert ( 1184 702 1180 0 a f )); ! DATA(insert ( 1184 1082 1178 0 a f )); ! DATA(insert ( 1184 1083 2019 0 a f )); ! DATA(insert ( 1184 1114 2027 0 a f )); ! DATA(insert ( 1184 1266 1388 0 a f )); ! DATA(insert ( 1186 703 1194 0 a f )); ! DATA(insert ( 1186 1083 1419 0 a f )); ! DATA(insert ( 1266 1083 2046 0 a f )); /* Cross-category casts between int4 and abstime, reltime */ ! DATA(insert ( 23 702 0 0 e b )); ! DATA(insert ( 702 23 0 0 e b )); ! DATA(insert ( 23 703 0 0 e b )); ! DATA(insert ( 703 23 0 0 e b )); /* * Geometric category */ ! DATA(insert ( 601 600 1532 0 e f )); ! DATA(insert ( 602 600 1533 0 e f )); ! DATA(insert ( 602 604 1449 0 a f )); ! DATA(insert ( 603 600 1534 0 e f )); ! DATA(insert ( 603 601 1541 0 e f )); ! DATA(insert ( 603 604 1448 0 a f )); ! DATA(insert ( 603 718 1479 0 e f )); ! DATA(insert ( 604 600 1540 0 e f )); ! DATA(insert ( 604 602 1447 0 a f )); ! DATA(insert ( 604 603 1446 0 e f )); ! DATA(insert ( 604 718 1474 0 e f )); ! DATA(insert ( 718 600 1416 0 e f )); ! DATA(insert ( 718 603 1480 0 e f )); ! DATA(insert ( 718 604 1544 0 e f )); /* * INET category */ ! DATA(insert ( 650 869 0 0 i b )); ! DATA(insert ( 869 650 1715 0 a f )); /* * BitString category */ ! DATA(insert ( 1560 1562 0 0 i b )); ! DATA(insert ( 1562 1560 0 0 i b )); /* Cross-category casts between bit and int4, int8 */ ! DATA(insert ( 20 1560 2075 0 e f )); ! DATA(insert ( 23 1560 1683 0 e f )); ! DATA(insert ( 1560 20 2076 0 e f )); ! DATA(insert ( 1560 23 1684 0 e f )); /* * Cross-category casts to and from TEXT *************** *** 315,360 **** DATA(insert ( 1560 23 1684 e f )); * behavior will ensue when the automatic cast is applied instead of the * pg_cast entry! */ ! DATA(insert ( 650 25 730 a f )); ! DATA(insert ( 869 25 730 a f )); ! DATA(insert ( 16 25 2971 a f )); ! DATA(insert ( 142 25 0 a b )); ! DATA(insert ( 25 142 2896 e f )); /* * Cross-category casts to and from VARCHAR * * We support all the same casts as for TEXT. */ ! DATA(insert ( 650 1043 730 a f )); ! DATA(insert ( 869 1043 730 a f )); ! DATA(insert ( 16 1043 2971 a f )); ! DATA(insert ( 142 1043 0 a b )); ! DATA(insert ( 1043 142 2896 e f )); /* * Cross-category casts to and from BPCHAR * * We support all the same casts as for TEXT. */ ! DATA(insert ( 650 1042 730 a f )); ! DATA(insert ( 869 1042 730 a f )); ! DATA(insert ( 16 1042 2971 a f )); ! DATA(insert ( 142 1042 0 a b )); ! DATA(insert ( 1042 142 2896 e f )); /* * Length-coercion functions */ ! DATA(insert ( 1042 1042 668 i f )); ! DATA(insert ( 1043 1043 669 i f )); ! DATA(insert ( 1083 1083 1968 i f )); ! DATA(insert ( 1114 1114 1961 i f )); ! DATA(insert ( 1184 1184 1967 i f )); ! DATA(insert ( 1186 1186 1200 i f )); ! DATA(insert ( 1266 1266 1969 i f )); ! DATA(insert ( 1560 1560 1685 i f )); ! DATA(insert ( 1562 1562 1687 i f )); ! DATA(insert ( 1700 1700 1703 i f )); #endif /* PG_CAST_H */ --- 317,362 ---- * behavior will ensue when the automatic cast is applied instead of the * pg_cast entry! */ ! DATA(insert ( 650 25 730 0 a f )); ! DATA(insert ( 869 25 730 0 a f )); ! DATA(insert ( 16 25 2971 0 a f )); ! DATA(insert ( 142 25 0 0 a b )); ! DATA(insert ( 25 142 2896 3831 e f )); /* * Cross-category casts to and from VARCHAR * * We support all the same casts as for TEXT. */ ! DATA(insert ( 650 1043 730 0 a f )); ! DATA(insert ( 869 1043 730 0 a f )); ! DATA(insert ( 16 1043 2971 0 a f )); ! DATA(insert ( 142 1043 0 0 a b )); ! DATA(insert ( 1043 142 2896 3831 e f )); /* * Cross-category casts to and from BPCHAR * * We support all the same casts as for TEXT. */ ! DATA(insert ( 650 1042 730 0 a f )); ! DATA(insert ( 869 1042 730 0 a f )); ! DATA(insert ( 16 1042 2971 0 a f )); ! DATA(insert ( 142 1042 0 0 a b )); ! DATA(insert ( 1042 142 2896 3831 e f )); /* * Length-coercion functions */ ! DATA(insert ( 1042 1042 668 0 i f )); ! DATA(insert ( 1043 1043 669 3829 i f )); ! DATA(insert ( 1083 1083 1968 0 i f )); ! DATA(insert ( 1114 1114 1961 0 i f )); ! DATA(insert ( 1184 1184 1967 0 i f )); ! DATA(insert ( 1186 1186 1200 0 i f )); ! DATA(insert ( 1266 1266 1969 0 i f )); ! DATA(insert ( 1560 1560 1685 0 i f )); ! DATA(insert ( 1562 1562 1687 0 i f )); ! DATA(insert ( 1700 1700 1703 0 i f )); #endif /* PG_CAST_H */ *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 937,942 **** DESCR("not equal"); --- 937,944 ---- DATA(insert OID = 668 ( bpchar PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1042 "1042 23 16" _null_ _null_ _null_ _null_ bpchar _null_ _null_ _null_ )); DESCR("adjust char() to typmod length"); + DATA(insert OID = 3829 ( varchar_exemptor PGNSP PGUID 12 1 0 0 f f f t f i 3 0 23 "23 23 16" _null_ _null_ _null_ _null_ varchar_exemptor _null_ _null_ _null_ )); + DESCR("varchar cast exemptor"); DATA(insert OID = 669 ( varchar PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1043 "1043 23 16" _null_ _null_ _null_ _null_ varchar _null_ _null_ _null_ )); DESCR("adjust varchar() to typmod length"); *************** *** 4437,4442 **** DATA(insert OID = 2894 ( xml_out PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 --- 4439,4446 ---- DESCR("I/O"); DATA(insert OID = 2895 ( xmlcomment PGNSP PGUID 12 1 0 0 f f f t f i 1 0 142 "25" _null_ _null_ _null_ _null_ xmlcomment _null_ _null_ _null_ )); DESCR("generate an XML comment"); + DATA(insert OID = 3831 ( xml_exemptor PGNSP PGUID 12 1 0 0 f f f t f i 3 0 23 "23 23 16" _null_ _null_ _null_ _null_ xml_exemptor _null_ _null_ _null_ )); + DESCR("texttoxml cast exemptor"); DATA(insert OID = 2896 ( xml PGNSP PGUID 12 1 0 0 f f f t f s 1 0 142 "25" _null_ _null_ _null_ _null_ texttoxml _null_ _null_ _null_ )); DESCR("perform a non-validating parse of a character string to produce an XML value"); DATA(insert OID = 2897 ( xmlvalidate PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "142 25" _null_ _null_ _null_ _null_ xmlvalidate _null_ _null_ _null_ )); *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 2439,2444 **** typedef struct CreateCastStmt --- 2439,2445 ---- TypeName *sourcetype; TypeName *targettype; FuncWithArgs *func; + FuncWithArgs *exemptor; CoercionContext context; bool inout; } CreateCastStmt; *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** *** 313,318 **** typedef enum CoercionForm --- 313,323 ---- COERCE_DONTCARE /* special case for planner */ } CoercionForm; + /* Bits in FuncExpr.funcexempt */ + typedef int CoerceExemptions; + #define COERCE_EXEMPT_NOCHANGE 0x1 /* expression never changes storage */ + #define COERCE_EXEMPT_NOERROR 0x2 /* expression never throws an error */ + /* * FuncExpr - expression node for a function call */ *************** *** 323,328 **** typedef struct FuncExpr --- 328,334 ---- Oid funcresulttype; /* PG_TYPE OID of result value */ bool funcretset; /* true if function returns set */ CoercionForm funcformat; /* how to display this function call */ + CoerceExemptions funcexempt; /* potential optimizations */ List *args; /* arguments to the function */ int location; /* token location, or -1 if unknown */ } FuncExpr; *** a/src/include/parser/kwlist.h --- b/src/include/parser/kwlist.h *************** *** 148,153 **** PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD) --- 148,154 ---- PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD) PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD) PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD) + PG_KEYWORD("exemptor", EXEMPTOR, UNRESERVED_KEYWORD) PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD) PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD) PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD) *** a/src/include/parser/parse_coerce.h --- b/src/include/parser/parse_coerce.h *************** *** 30,40 **** typedef enum CoercionPathType COERCION_PATH_COERCEVIAIO /* need a CoerceViaIO node */ } CoercionPathType; - /* Bits in the return value of GetCoerceExemptions. */ - typedef int CoerceExemptions; - - #define COERCE_EXEMPT_NOCHANGE 0x1 /* expression never changes storage */ - #define COERCE_EXEMPT_NOERROR 0x2 /* expression never throws an error */ extern bool IsBinaryCoercible(Oid srctype, Oid targettype); extern CoerceExemptions GetCoerceExemptions(Node *expr, --- 30,35 ---- *************** *** 90,97 **** extern Oid resolve_generic_type(Oid declared_type, extern CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, ! Oid *funcid); extern CoercionPathType find_typmod_coercion_function(Oid typeId, ! Oid *funcid); #endif /* PARSE_COERCE_H */ --- 85,92 ---- extern CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, ! Oid *funcid, Oid *exemptid); extern CoercionPathType find_typmod_coercion_function(Oid typeId, ! Oid *funcid, Oid *exemptid); #endif /* PARSE_COERCE_H */ *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 676,681 **** extern Datum varcharrecv(PG_FUNCTION_ARGS); --- 676,682 ---- extern Datum varcharsend(PG_FUNCTION_ARGS); extern Datum varchartypmodin(PG_FUNCTION_ARGS); extern Datum varchartypmodout(PG_FUNCTION_ARGS); + extern Datum varchar_exemptor(PG_FUNCTION_ARGS); extern Datum varchar(PG_FUNCTION_ARGS); /* varlena.c */ *** a/src/include/utils/xml.h --- b/src/include/utils/xml.h *************** *** 33,38 **** extern Datum xml_recv(PG_FUNCTION_ARGS); --- 33,39 ---- extern Datum xml_send(PG_FUNCTION_ARGS); extern Datum xmlcomment(PG_FUNCTION_ARGS); extern Datum xmlconcat2(PG_FUNCTION_ARGS); + extern Datum xml_exemptor(PG_FUNCTION_ARGS); extern Datum texttoxml(PG_FUNCTION_ARGS); extern Datum xmltotext(PG_FUNCTION_ARGS); extern Datum xmlvalidate(PG_FUNCTION_ARGS); *** a/src/test/regress/expected/alter_table.out --- b/src/test/regress/expected/alter_table.out *************** *** 2046,2084 **** DEBUG: Rewriting table "t" ERROR: numeric field overflow DETAIL: A field with precision 4, scale 3 must round to an absolute value less than 10^1. ALTER TABLE t ALTER string TYPE varchar(4); -- noop - DEBUG: Rewriting table "t" - DEBUG: Rebuilding index "t_daytimetz_key" - DEBUG: Rebuilding index "t_daytime_key" - DEBUG: Rebuilding index "t_stamptz_key" - DEBUG: Rebuilding index "t_stamp_key" - DEBUG: Rebuilding index "t_timegap_key" - DEBUG: Rebuilding index "t_bits_key" - DEBUG: Rebuilding index "t_network_key" - DEBUG: Rebuilding index "t_strarr_idx" - DEBUG: Rebuilding index "t_square_idx" - DEBUG: Rebuilding index "t_touchy_f_idx" - DEBUG: Rebuilding index "t_expr_idx" - DEBUG: Rebuilding index "t_constraint4_key" - DEBUG: Rebuilding index "t_integral_key" - DEBUG: Rebuilding index "t_rational_key" DEBUG: Rebuilding index "t_string_idx1" DEBUG: Rebuilding index "t_string_idx" ALTER TABLE t ALTER string TYPE lendom; -- noop - DEBUG: Rewriting table "t" - DEBUG: Rebuilding index "t_daytimetz_key" - DEBUG: Rebuilding index "t_daytime_key" - DEBUG: Rebuilding index "t_stamptz_key" - DEBUG: Rebuilding index "t_stamp_key" - DEBUG: Rebuilding index "t_timegap_key" - DEBUG: Rebuilding index "t_bits_key" - DEBUG: Rebuilding index "t_network_key" - DEBUG: Rebuilding index "t_strarr_idx" - DEBUG: Rebuilding index "t_square_idx" - DEBUG: Rebuilding index "t_touchy_f_idx" - DEBUG: Rebuilding index "t_expr_idx" - DEBUG: Rebuilding index "t_constraint4_key" - DEBUG: Rebuilding index "t_integral_key" - DEBUG: Rebuilding index "t_rational_key" DEBUG: Rebuilding index "t_string_idx" DEBUG: Rebuilding index "t_string_idx1" ALTER TABLE t ALTER string TYPE shortdom; -- rewrite-e --- 2046,2054 ---- *************** *** 2732,2754 **** DEBUG: Rebuilding index "t_bits_key" DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE text; -- noop ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Rewriting table "t" ! DEBUG: Rebuilding index "t_strarr_idx" ! DEBUG: Rebuilding index "t_square_idx" ! DEBUG: Rebuilding index "t_touchy_f_idx" ! DEBUG: Rebuilding index "t_expr_idx" ! DEBUG: Rebuilding index "t_constraint4_key" ! DEBUG: Rebuilding index "t_integral_key" ! DEBUG: Rebuilding index "t_rational_key" ! DEBUG: Rebuilding index "t_string_idx1" ! DEBUG: Rebuilding index "t_string_idx" ! DEBUG: Rebuilding index "t_daytimetz_key" ! DEBUG: Rebuilding index "t_daytime_key" ! DEBUG: Rebuilding index "t_stamptz_key" ! DEBUG: Rebuilding index "t_stamp_key" ! DEBUG: Rebuilding index "t_timegap_key" ! DEBUG: Rebuilding index "t_bits_key" ! DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE char(20); -- rewrite DEBUG: Rewriting table "t" DEBUG: Rebuilding index "t_strarr_idx" --- 2702,2708 ---- DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE text; -- noop ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Verifying table "t" ALTER TABLE t ALTER document TYPE char(20); -- rewrite DEBUG: Rewriting table "t" DEBUG: Rebuilding index "t_strarr_idx" *************** *** 2768,2790 **** DEBUG: Rebuilding index "t_timegap_key" DEBUG: Rebuilding index "t_bits_key" DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Rewriting table "t" ! DEBUG: Rebuilding index "t_strarr_idx" ! DEBUG: Rebuilding index "t_square_idx" ! DEBUG: Rebuilding index "t_touchy_f_idx" ! DEBUG: Rebuilding index "t_expr_idx" ! DEBUG: Rebuilding index "t_constraint4_key" ! DEBUG: Rebuilding index "t_integral_key" ! DEBUG: Rebuilding index "t_rational_key" ! DEBUG: Rebuilding index "t_string_idx1" ! DEBUG: Rebuilding index "t_string_idx" ! DEBUG: Rebuilding index "t_daytimetz_key" ! DEBUG: Rebuilding index "t_daytime_key" ! DEBUG: Rebuilding index "t_stamptz_key" ! DEBUG: Rebuilding index "t_stamp_key" ! DEBUG: Rebuilding index "t_timegap_key" ! DEBUG: Rebuilding index "t_bits_key" ! DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE varchar(30); -- rewrite-v DEBUG: Rewriting table "t" DEBUG: Rebuilding index "t_strarr_idx" --- 2722,2728 ---- DEBUG: Rebuilding index "t_bits_key" DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Verifying table "t" ALTER TABLE t ALTER document TYPE varchar(30); -- rewrite-v DEBUG: Rewriting table "t" DEBUG: Rebuilding index "t_strarr_idx" *************** *** 2804,2843 **** DEBUG: Rebuilding index "t_timegap_key" DEBUG: Rebuilding index "t_bits_key" DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Rewriting table "t" ! DEBUG: Rebuilding index "t_strarr_idx" ! DEBUG: Rebuilding index "t_square_idx" ! DEBUG: Rebuilding index "t_touchy_f_idx" ! DEBUG: Rebuilding index "t_expr_idx" ! DEBUG: Rebuilding index "t_constraint4_key" ! DEBUG: Rebuilding index "t_integral_key" ! DEBUG: Rebuilding index "t_rational_key" ! DEBUG: Rebuilding index "t_string_idx1" ! DEBUG: Rebuilding index "t_string_idx" ! DEBUG: Rebuilding index "t_daytimetz_key" ! DEBUG: Rebuilding index "t_daytime_key" ! DEBUG: Rebuilding index "t_stamptz_key" ! DEBUG: Rebuilding index "t_stamp_key" ! DEBUG: Rebuilding index "t_timegap_key" ! DEBUG: Rebuilding index "t_bits_key" ! DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER strarr TYPE varchar(4)[]; -- noop - DEBUG: Rewriting table "t" - DEBUG: Rebuilding index "t_square_idx" - DEBUG: Rebuilding index "t_touchy_f_idx" - DEBUG: Rebuilding index "t_expr_idx" - DEBUG: Rebuilding index "t_constraint4_key" - DEBUG: Rebuilding index "t_integral_key" - DEBUG: Rebuilding index "t_rational_key" - DEBUG: Rebuilding index "t_string_idx1" - DEBUG: Rebuilding index "t_string_idx" - DEBUG: Rebuilding index "t_daytimetz_key" - DEBUG: Rebuilding index "t_daytime_key" - DEBUG: Rebuilding index "t_stamptz_key" - DEBUG: Rebuilding index "t_stamp_key" - DEBUG: Rebuilding index "t_timegap_key" - DEBUG: Rebuilding index "t_bits_key" - DEBUG: Rebuilding index "t_network_key" DEBUG: Rebuilding index "t_strarr_idx" ALTER TABLE t ALTER strarr TYPE varchar(3)[]; -- rewrite-v DEBUG: Rewriting table "t" --- 2742,2749 ---- DEBUG: Rebuilding index "t_bits_key" DEBUG: Rebuilding index "t_network_key" ALTER TABLE t ALTER document TYPE xml USING document::xml; -- verify ! DEBUG: Verifying table "t" ALTER TABLE t ALTER strarr TYPE varchar(4)[]; -- noop DEBUG: Rebuilding index "t_strarr_idx" ALTER TABLE t ALTER strarr TYPE varchar(3)[]; -- rewrite-v DEBUG: Rewriting table "t" *** a/src/test/regress/expected/create_cast.out --- b/src/test/regress/expected/create_cast.out *************** *** 17,22 **** CREATE TYPE casttesttype ( --- 17,24 ---- internallength = variable, input = casttesttype_in, output = casttesttype_out, + typmod_in = varchartypmodin, + typmod_out = varchartypmodout, alignment = int4 ); -- a dummy function to test with *************** *** 49,54 **** SELECT casttestfunc('foo'::text); -- Should work now --- 51,57 ---- 1 (1 row) + DROP CAST (text AS casttesttype); -- cleanup -- Try I/O conversion cast. SELECT 1234::int4::casttesttype; -- No cast yet, should fail ERROR: cannot cast type integer to casttesttype *************** *** 72,74 **** SELECT 1234::int4::casttesttype; -- Should work now --- 75,110 ---- foo1234 (1 row) + -- Try an EXEMPTOR to skip work during ALTER TABLE. Make casttesttype behave + -- like varchar, but without the special trailing blank treatment during an + -- implicit cast. + SET client_min_messages = debug1; + CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; + CREATE FUNCTION casttest_lencoerce(casttesttype, int, boolean) + RETURNS casttesttype LANGUAGE SQL IMMUTABLE STRICT AS $$ + SELECT CASE + WHEN length($1::text) <= $2 THEN $1 + WHEN $3 THEN substring($1::text from 1 for $2)::casttesttype + ELSE ('f' || $1)::numeric::text::casttesttype -- throws an error + END + $$; + CREATE FUNCTION casttest_exemptor(oldtypmod int, newtypmod int, explicit bool) + RETURNS int LANGUAGE SQL IMMUTABLE STRICT AS $$ + SELECT CASE + WHEN $1 <= $2 THEN 3 -- no change, no error + WHEN $3 THEN 0 -- explicit shrink: can truncate + ELSE 1 -- implciit shrink: no change, might error + END + $$; + CREATE CAST (casttesttype AS casttesttype) + WITH FUNCTION casttest_lencoerce(casttesttype, int, boolean) + EXEMPTOR casttest_exemptor(int, int, boolean); + CREATE TABLE casttbl (c casttesttype(10)); + INSERT INTO casttbl VALUES ('foo'), ('bar'); + ALTER TABLE casttbl ALTER c TYPE casttesttype(11); -- noop + ALTER TABLE casttbl ALTER c TYPE casttesttype(10); -- verify + DEBUG: Verifying table "casttbl" + ALTER TABLE casttbl ALTER c TYPE casttesttype(11) USING c::casttesttype(11); -- noop + ALTER TABLE casttbl ALTER c TYPE casttesttype(10) USING c::casttesttype(10); -- rewrite + DEBUG: Rewriting table "casttbl" + RESET client_min_messages; *** a/src/test/regress/expected/opr_sanity.out --- b/src/test/regress/expected/opr_sanity.out *************** *** 331,338 **** SELECT * FROM pg_cast c WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i') OR castmethod NOT IN ('f', 'b' ,'i'); ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) -- Check that castfunc is nonzero only for cast methods that need a function, --- 331,338 ---- FROM pg_cast c WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i') OR castmethod NOT IN ('f', 'b' ,'i'); ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) -- Check that castfunc is nonzero only for cast methods that need a function, *************** *** 341,348 **** SELECT * FROM pg_cast c WHERE (castmethod = 'f' AND castfunc = 0) OR (castmethod IN ('b', 'i') AND castfunc <> 0); ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) -- Look for casts to/from the same type that aren't length coercion functions. --- 341,348 ---- FROM pg_cast c WHERE (castmethod = 'f' AND castfunc = 0) OR (castmethod IN ('b', 'i') AND castfunc <> 0); ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) -- Look for casts to/from the same type that aren't length coercion functions. *************** *** 351,365 **** WHERE (castmethod = 'f' AND castfunc = 0) SELECT * FROM pg_cast c WHERE castsource = casttarget AND castfunc = 0; ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) -- Look for cast functions that don't have the right signature. The --- 351,365 ---- SELECT * FROM pg_cast c WHERE castsource = casttarget AND castfunc = 0; ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) -- Look for cast functions that don't have the right signature. The *************** *** 377,384 **** WHERE c.castfunc = p.oid AND OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) OR NOT binary_coercible(p.prorettype, c.casttarget)); ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) SELECT c.* --- 377,384 ---- OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) OR NOT binary_coercible(p.prorettype, c.casttarget)); ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) SELECT c.* *************** *** 386,393 **** FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); ! castsource | casttarget | castfunc | castcontext | castmethod ! ------------+------------+----------+-------------+------------ (0 rows) -- Look for binary compatible casts that do not have the reverse --- 386,393 ---- WHERE c.castfunc = p.oid AND ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); ! castsource | casttarget | castfunc | castexemptor | castcontext | castmethod ! ------------+------------+----------+--------------+-------------+------------ (0 rows) -- Look for binary compatible casts that do not have the reverse *** a/src/test/regress/expected/sanity_check.out --- b/src/test/regress/expected/sanity_check.out *************** *** 27,32 **** SELECT relname, relhasindex --- 27,33 ---- bt_txt_heap | t c | f c_star | f + casttbl | f char_tbl | f check2_tbl | f check_tbl | f *************** *** 155,161 **** SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (144 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 156,162 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (145 rows) -- -- another sanity check: every system catalog that has OIDs should have *** a/src/test/regress/output/misc.source --- b/src/test/regress/output/misc.source *************** *** 585,590 **** SELECT user_relns() AS user_relns --- 585,591 ---- bt_txt_heap c c_star + casttbl char_tbl check2_tbl check_seq *************** *** 669,675 **** SELECT user_relns() AS user_relns toyemp varchar_tbl xacttest ! (102 rows) SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); name --- 670,676 ---- toyemp varchar_tbl xacttest ! (103 rows) SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); name *** a/src/test/regress/sql/create_cast.sql --- b/src/test/regress/sql/create_cast.sql *************** *** 18,23 **** CREATE TYPE casttesttype ( --- 18,25 ---- internallength = variable, input = casttesttype_in, output = casttesttype_out, + typmod_in = varchartypmodin, + typmod_out = varchartypmodout, alignment = int4 ); *************** *** 36,41 **** DROP CAST (text AS casttesttype); -- cleanup --- 38,44 ---- -- Try IMPLICIT binary coercion cast CREATE CAST (text AS casttesttype) WITHOUT FUNCTION AS IMPLICIT; SELECT casttestfunc('foo'::text); -- Should work now + DROP CAST (text AS casttesttype); -- cleanup -- Try I/O conversion cast. SELECT 1234::int4::casttesttype; -- No cast yet, should fail *************** *** 52,54 **** $$ SELECT ('foo'::text || $1::text)::casttesttype; $$; --- 55,94 ---- CREATE CAST (int4 AS casttesttype) WITH FUNCTION int4_casttesttype(int4) AS IMPLICIT; SELECT 1234::int4::casttesttype; -- Should work now + + -- Try an EXEMPTOR to skip work during ALTER TABLE. Make casttesttype behave + -- like varchar, but without the special trailing blank treatment during an + -- implicit cast. + SET client_min_messages = debug1; + CREATE CAST (text AS casttesttype) WITHOUT FUNCTION; + + CREATE FUNCTION casttest_lencoerce(casttesttype, int, boolean) + RETURNS casttesttype LANGUAGE SQL IMMUTABLE STRICT AS $$ + SELECT CASE + WHEN length($1::text) <= $2 THEN $1 + WHEN $3 THEN substring($1::text from 1 for $2)::casttesttype + ELSE ('f' || $1)::numeric::text::casttesttype -- throws an error + END + $$; + + CREATE FUNCTION casttest_exemptor(oldtypmod int, newtypmod int, explicit bool) + RETURNS int LANGUAGE SQL IMMUTABLE STRICT AS $$ + SELECT CASE + WHEN $1 <= $2 THEN 3 -- no change, no error + WHEN $3 THEN 0 -- explicit shrink: can truncate + ELSE 1 -- implciit shrink: no change, might error + END + $$; + + CREATE CAST (casttesttype AS casttesttype) + WITH FUNCTION casttest_lencoerce(casttesttype, int, boolean) + EXEMPTOR casttest_exemptor(int, int, boolean); + + CREATE TABLE casttbl (c casttesttype(10)); + INSERT INTO casttbl VALUES ('foo'), ('bar'); + ALTER TABLE casttbl ALTER c TYPE casttesttype(11); -- noop + ALTER TABLE casttbl ALTER c TYPE casttesttype(10); -- verify + ALTER TABLE casttbl ALTER c TYPE casttesttype(11) USING c::casttesttype(11); -- noop + ALTER TABLE casttbl ALTER c TYPE casttesttype(10) USING c::casttesttype(10); -- rewrite + + RESET client_min_messages;