*** 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.
+
+
+
+ castcontextchar
*** 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 ----
+ EXEMPTORfunction_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;