aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/copy.c4
-rw-r--r--src/backend/commands/functioncmds.c31
-rw-r--r--src/backend/optimizer/prep/preptlist.c5
-rw-r--r--src/backend/parser/parse_clause.c12
-rw-r--r--src/backend/parser/parse_coerce.c315
-rw-r--r--src/backend/parser/parse_expr.c5
-rw-r--r--src/backend/parser/parse_func.c11
-rw-r--r--src/backend/rewrite/rewriteHandler.c5
-rw-r--r--src/backend/utils/adt/arrayfuncs.c197
-rw-r--r--src/backend/utils/adt/ruleutils.c73
-rw-r--r--src/backend/utils/adt/varbit.c137
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_cast.h21
-rw-r--r--src/include/catalog/pg_proc.h17
-rw-r--r--src/include/parser/parse_coerce.h8
-rw-r--r--src/include/utils/array.h5
-rw-r--r--src/test/regress/expected/opr_sanity.out36
-rw-r--r--src/test/regress/sql/opr_sanity.sql28
18 files changed, 531 insertions, 383 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 77e7d1b1855..4a05b40eea8 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.226 2004/06/06 00:41:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.227 2004/06/16 01:26:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1565,7 +1565,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
node = coerce_to_domain((Node *) prm,
prm->paramtype,
attr[attnum - 1]->atttypid,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST, false);
constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node,
estate);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 757869a925a..7747eb1d776 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.47 2004/05/26 04:41:11 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.48 2004/06/16 01:26:42 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -809,6 +809,7 @@ CreateCast(CreateCastStmt *stmt)
Oid sourcetypeid;
Oid targettypeid;
Oid funcid;
+ int nargs;
char castcontext;
Relation relation;
HeapTuple tuple;
@@ -831,11 +832,6 @@ CreateCast(CreateCastStmt *stmt)
errmsg("target data type %s does not exist",
TypeNameToString(stmt->targettype))));
- if (sourcetypeid == targettypeid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("source data type and target data type are the same")));
-
/* No shells, no pseudo-types allowed */
if (!get_typisdefined(sourcetypeid))
ereport(ERROR,
@@ -885,14 +881,23 @@ CreateCast(CreateCastStmt *stmt)
elog(ERROR, "cache lookup failed for function %u", funcid);
procstruct = (Form_pg_proc) GETSTRUCT(tuple);
- if (procstruct->pronargs != 1)
+ nargs = procstruct->pronargs;
+ if (nargs < 1 || nargs > 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cast function must take one argument")));
+ errmsg("cast function must take one to three arguments")));
if (procstruct->proargtypes[0] != sourcetypeid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("argument of cast function must match source data type")));
+ if (nargs > 1 && procstruct->proargtypes[1] != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("second argument of cast function must be type integer")));
+ if (nargs > 2 && procstruct->proargtypes[2] != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("third argument of cast function must be type boolean")));
if (procstruct->prorettype != targettypeid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -931,6 +936,7 @@ CreateCast(CreateCastStmt *stmt)
/* indicates binary coercibility */
funcid = InvalidOid;
+ nargs = 0;
/*
* Must be superuser to create binary-compatible casts, since
@@ -957,6 +963,15 @@ CreateCast(CreateCastStmt *stmt)
errmsg("source and target data types are not physically compatible")));
}
+ /*
+ * Allow source and target types to be same only for length coercion
+ * functions. We assume a multi-arg function does length coercion.
+ */
+ if (sourcetypeid == targettypeid && nargs < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("source data type and target data type are the same")));
+
/* convert CoercionContext enum to char value for castcontext */
switch (stmt->context)
{
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index a6b48450a49..cc0048972bf 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.68 2004/05/30 23:40:29 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.69 2004/06/16 01:26:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -199,7 +199,8 @@ expand_targetlist(List *tlist, int command_type,
new_expr = coerce_to_domain(new_expr,
InvalidOid,
atttype,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ false);
}
else
{
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index a2cd7dccc15..b3a6b67f5c3 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.133 2004/06/16 01:26:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -915,11 +915,11 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
* Insert coercion functions if needed. Note that a difference in
* typmod can only happen if input has typmod but outcoltypmod is -1.
* In that case we insert a RelabelType to clearly mark that result's
- * typmod is not same as input.
+ * typmod is not same as input. We never need coerce_type_typmod.
*/
if (l_colvar->vartype != outcoltype)
l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype,
- outcoltype,
+ outcoltype, outcoltypmod,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
else if (l_colvar->vartypmod != outcoltypmod)
l_node = (Node *) makeRelabelType((Expr *) l_colvar,
@@ -930,7 +930,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
if (r_colvar->vartype != outcoltype)
r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype,
- outcoltype,
+ outcoltype, outcoltypmod,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
else if (r_colvar->vartypmod != outcoltypmod)
r_node = (Node *) makeRelabelType((Expr *) r_colvar,
@@ -1276,7 +1276,7 @@ transformGroupClause(ParseState *pstate, List *grouplist,
if (restype == UNKNOWNOID)
{
tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
- restype, TEXTOID,
+ restype, TEXTOID, -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST);
restype = tle->resdom->restype = TEXTOID;
@@ -1528,7 +1528,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
if (restype == UNKNOWNOID && resolveUnknown)
{
tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
- restype, TEXTOID,
+ restype, TEXTOID, -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST);
restype = tle->resdom->restype = TEXTOID;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index b12d1854aa8..3878d07d5a1 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.118 2004/06/06 00:41:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -33,8 +33,13 @@
static Node *coerce_type_typmod(Node *node,
- Oid targetTypeId, int32 targetTypMod,
- CoercionForm cformat, bool isExplicit);
+ Oid targetTypeId, int32 targetTypMod,
+ CoercionForm cformat, bool isExplicit,
+ bool hideInputCoercion);
+static void hide_coercion_node(Node *node);
+static Node *build_coercion_expression(Node *node, Oid funcId,
+ Oid targetTypeId, int32 targetTypMod,
+ CoercionForm cformat, bool isExplicit);
static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
@@ -67,22 +72,27 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
CoercionContext ccontext,
CoercionForm cformat)
{
- if (can_coerce_type(1, &exprtype, &targettype, ccontext))
- expr = coerce_type(pstate, expr, exprtype, targettype,
- ccontext, cformat);
- else
- expr = NULL;
+ Node *result;
+
+ if (!can_coerce_type(1, &exprtype, &targettype, ccontext))
+ return NULL;
+
+ result = coerce_type(pstate, expr, exprtype,
+ targettype, targettypmod,
+ ccontext, cformat);
/*
* If the target is a fixed-length type, it may need a length coercion
- * as well as a type coercion.
+ * as well as a type coercion. If we find ourselves adding both,
+ * force the inner coercion node to implicit display form.
*/
- if (expr != NULL)
- expr = coerce_type_typmod(expr, targettype, targettypmod,
- cformat,
- (cformat != COERCE_IMPLICIT_CAST));
+ result = coerce_type_typmod(result,
+ targettype, targettypmod,
+ cformat,
+ (cformat != COERCE_IMPLICIT_CAST),
+ (result != expr && !IsA(result, Const)));
- return expr;
+ return result;
}
@@ -93,10 +103,13 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
* The caller should already have determined that the coercion is possible;
* see can_coerce_type.
*
- * No coercion to a typmod (length) is performed here. The caller must
- * call coerce_type_typmod as well, if a typmod constraint is wanted.
+ * Normally, no coercion to a typmod (length) is performed here. The caller
+ * must call coerce_type_typmod as well, if a typmod constraint is wanted.
* (But if the target type is a domain, it may internally contain a
* typmod constraint, which will be applied inside coerce_to_domain.)
+ * In some cases pg_cast specifies a type coercion function that also
+ * applies length conversion, and in those cases only, the result will
+ * already be properly coerced to the specified typmod.
*
* pstate is only used in the case that we are able to resolve the type of
* a previously UNKNOWN Param. It is okay to pass pstate = NULL if the
@@ -104,7 +117,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
*/
Node *
coerce_type(ParseState *pstate, Node *node,
- Oid inputTypeId, Oid targetTypeId,
+ Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat)
{
Node *result;
@@ -178,7 +191,7 @@ coerce_type(ParseState *pstate, Node *node,
/* If target is a domain, apply constraints. */
if (targetTyptype == 'd')
result = coerce_to_domain(result, InvalidOid, targetTypeId,
- cformat);
+ cformat, false);
ReleaseSysCache(targetType);
@@ -240,13 +253,14 @@ coerce_type(ParseState *pstate, Node *node,
* 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.
+ * yield the base type (and we assume targetTypeMod must be -1).
*/
Oid baseTypeId = getBaseType(targetTypeId);
- result = (Node *) makeFuncExpr(funcId, baseTypeId,
- list_make1(node),
- cformat);
+ result = build_coercion_expression(node, funcId,
+ baseTypeId, targetTypeMod,
+ cformat,
+ (cformat != COERCE_IMPLICIT_CAST));
/*
* If domain, coerce to the domain type and relabel with
@@ -254,7 +268,7 @@ coerce_type(ParseState *pstate, Node *node,
*/
if (targetTypeId != baseTypeId)
result = coerce_to_domain(result, baseTypeId, targetTypeId,
- cformat);
+ cformat, true);
}
else
{
@@ -269,7 +283,7 @@ coerce_type(ParseState *pstate, Node *node,
* then we won't need a RelabelType node.
*/
result = coerce_to_domain(node, InvalidOid, targetTypeId,
- cformat);
+ cformat, false);
if (result == node)
{
/*
@@ -409,11 +423,13 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
* has not bothered to look this up)
* 'typeId': target type to coerce to
* 'cformat': coercion format
+ * 'hideInputCoercion': if true, hide the input coercion under this one.
*
* If the target type isn't a domain, the given 'arg' is returned as-is.
*/
Node *
-coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
+coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+ CoercionForm cformat, bool hideInputCoercion)
{
CoerceToDomain *result;
int32 typmod;
@@ -426,6 +442,10 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
if (baseTypeId == typeId)
return arg;
+ /* Suppress display of nested coercion steps */
+ if (hideInputCoercion)
+ hide_coercion_node(arg);
+
/*
* If the domain applies a typmod to its base type, build the
* appropriate coercion step. Mark it implicit for display purposes,
@@ -444,7 +464,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
if (typmod >= 0)
arg = coerce_type_typmod(arg, baseTypeId, typmod,
COERCE_IMPLICIT_CAST,
- (cformat != COERCE_IMPLICIT_CAST));
+ (cformat != COERCE_IMPLICIT_CAST),
+ false);
/*
* Now build the domain coercion node. This represents run-time
@@ -473,57 +494,142 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
* The caller must have already ensured that the value is of the correct
* type, typically by applying coerce_type.
*
+ * cformat determines the display properties of the generated node (if any),
+ * while isExplicit may affect semantics. If hideInputCoercion is true
+ * *and* we generate a node, the input node is forced to IMPLICIT display
+ * form, so that only the typmod coercion node will be visible when
+ * displaying the expression.
+ *
* NOTE: this does not need to work on domain types, because any typmod
* coercion for a domain is considered to be part of the type coercion
* needed to produce the domain value in the first place. So, no getBaseType.
*/
static Node *
coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
- CoercionForm cformat, bool isExplicit)
+ CoercionForm cformat, bool isExplicit,
+ bool hideInputCoercion)
{
Oid funcId;
- int nargs;
/*
* A negative typmod is assumed to mean that no coercion is wanted.
+ * Also, skip coercion if already done.
*/
if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
return node;
- funcId = find_typmod_coercion_function(targetTypeId, &nargs);
+ funcId = find_typmod_coercion_function(targetTypeId);
if (OidIsValid(funcId))
{
- List *args;
- Const *cons;
+ /* Suppress display of nested coercion steps */
+ if (hideInputCoercion)
+ hide_coercion_node(node);
+
+ node = build_coercion_expression(node, funcId,
+ targetTypeId, targetTypMod,
+ cformat, isExplicit);
+ }
- /* Pass given value, plus target typmod as an int4 constant */
+ return node;
+}
+
+/*
+ * Mark a coercion node as IMPLICIT so it will never be displayed by
+ * ruleutils.c. We use this when we generate a nest of coercion nodes
+ * to implement what is logically one conversion; the inner nodes are
+ * forced to IMPLICIT_CAST format. This does not change their semantics,
+ * only display behavior.
+ *
+ * It is caller error to call this on something that doesn't have a
+ * CoercionForm field.
+ */
+static void
+hide_coercion_node(Node *node)
+{
+ if (IsA(node, FuncExpr))
+ ((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
+ else if (IsA(node, RelabelType))
+ ((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
+ else if (IsA(node, RowExpr))
+ ((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST;
+ else if (IsA(node, CoerceToDomain))
+ ((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST;
+ else
+ elog(ERROR, "unsupported node type: %d", (int) nodeTag(node));
+}
+
+/*
+ * build_coercion_expression()
+ * Construct a function-call expression for applying a pg_cast entry.
+ *
+ * This is used for both type-coercion and length-coercion functions,
+ * since there is no difference in terms of the calling convention.
+ */
+static Node *
+build_coercion_expression(Node *node, Oid funcId,
+ Oid targetTypeId, int32 targetTypMod,
+ CoercionForm cformat, bool isExplicit)
+{
+ HeapTuple tp;
+ Form_pg_proc procstruct;
+ int nargs;
+ List *args;
+ Const *cons;
+
+ tp = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcId);
+ procstruct = (Form_pg_proc) GETSTRUCT(tp);
+
+ /*
+ * Asserts essentially check that function is a legal coercion function.
+ * We can't make the seemingly obvious tests on prorettype and
+ * proargtypes[0], because of various binary-compatibility cases.
+ */
+ /* Assert(targetTypeId == procstruct->prorettype); */
+ Assert(!procstruct->proretset);
+ Assert(!procstruct->proisagg);
+ nargs = procstruct->pronargs;
+ Assert(nargs >= 1 && nargs <= 3);
+ /* Assert(procstruct->proargtypes[0] == exprType(node)); */
+ Assert(nargs < 2 || procstruct->proargtypes[1] == INT4OID);
+ Assert(nargs < 3 || procstruct->proargtypes[2] == BOOLOID);
+
+ ReleaseSysCache(tp);
+
+ args = list_make1(node);
+
+ if (nargs >= 2)
+ {
+ /* Pass target typmod as an int4 constant */
cons = makeConst(INT4OID,
sizeof(int32),
Int32GetDatum(targetTypMod),
false,
true);
- args = list_make2(node, cons);
+ args = lappend(args, cons);
+ }
- if (nargs == 3)
- {
- /* Pass it a boolean isExplicit parameter, too */
- cons = makeConst(BOOLOID,
- sizeof(bool),
- BoolGetDatum(isExplicit),
- false,
- true);
-
- args = lappend(args, cons);
- }
+ if (nargs == 3)
+ {
+ /* Pass it a boolean isExplicit parameter, too */
+ cons = makeConst(BOOLOID,
+ sizeof(bool),
+ BoolGetDatum(isExplicit),
+ false,
+ true);
- node = (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
+ args = lappend(args, cons);
}
- return node;
+ return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
}
+
/*
* coerce_record_to_complex
* Coerce a RECORD to a specific composite type.
@@ -803,7 +909,7 @@ coerce_to_common_type(ParseState *pstate, Node *node,
if (inputTypeId == targetTypeId)
return node; /* no work */
if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT))
- node = coerce_type(pstate, node, inputTypeId, targetTypeId,
+ node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
else
ereport(ERROR,
@@ -1528,8 +1634,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
{
/*
* If there's no pg_cast entry, perhaps we are dealing with a pair
- * of array types. If so, and if the element types have a
- * suitable cast, use array_type_coerce().
+ * of array types. If so, and if the element types have a suitable
+ * cast, use array_type_coerce() or array_type_length_coerce().
*/
Oid targetElemType;
Oid sourceElemType;
@@ -1541,7 +1647,23 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
if (find_coercion_pathway(targetElemType, sourceElemType,
ccontext, &elemfuncid))
{
- *funcid = F_ARRAY_TYPE_COERCE;
+ if (!OidIsValid(elemfuncid))
+ {
+ /* binary-compatible element type conversion */
+ *funcid = F_ARRAY_TYPE_COERCE;
+ }
+ else
+ {
+ /* does the function take a typmod arg? */
+ Oid argtypes[FUNC_MAX_ARGS];
+ int nargs;
+
+ (void) get_func_signature(elemfuncid, argtypes, &nargs);
+ if (nargs > 1)
+ *funcid = F_ARRAY_TYPE_LENGTH_COERCE;
+ else
+ *funcid = F_ARRAY_TYPE_COERCE;
+ }
result = true;
}
}
@@ -1554,14 +1676,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
/*
* find_typmod_coercion_function -- does the given type need length coercion?
*
- * If the target type possesses a function named for the type
- * and having parameter signature (targettype, int4), we assume that
- * the type requires coercion to its own length and that the said
- * function should be invoked to do that.
- *
- * Alternatively, the length-coercing function may have the signature
- * (targettype, int4, bool). On success, *nargs is set to report which
- * signature we found.
+ * 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.
*
@@ -1569,23 +1685,15 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
* function associated directly with the array type, but instead look for
* one associated with the element type. If one exists, we report
* array_length_coerce() as the coercion function to use.
- *
- * This mechanism may seem pretty grotty and in need of replacement by
- * something in pg_cast, but since typmod is only interesting for datatypes
- * that have special handling in the grammar, there's not really much
- * percentage in making it any easier to apply such coercions ...
*/
Oid
-find_typmod_coercion_function(Oid typeId, int *nargs)
+find_typmod_coercion_function(Oid typeId)
{
Oid funcid = InvalidOid;
bool isArray = false;
Type targetType;
Form_pg_type typeForm;
- char *typname;
- Oid typnamespace;
- Oid oid_array[FUNC_MAX_ARGS];
- HeapTuple ftup;
+ HeapTuple tuple;
targetType = typeidType(typeId);
typeForm = (Form_pg_type) GETSTRUCT(targetType);
@@ -1597,79 +1705,30 @@ find_typmod_coercion_function(Oid typeId, int *nargs)
{
/* Yes, switch our attention to the element type */
typeId = typeForm->typelem;
- ReleaseSysCache(targetType);
- targetType = typeidType(typeId);
- typeForm = (Form_pg_type) GETSTRUCT(targetType);
isArray = true;
}
+ ReleaseSysCache(targetType);
- /* Function name is same as type internal name, and in same namespace */
- typname = NameStr(typeForm->typname);
- typnamespace = typeForm->typnamespace;
-
- /* First look for parameters (type, int4) */
- MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
- oid_array[0] = typeId;
- oid_array[1] = INT4OID;
- *nargs = 2;
-
- ftup = SearchSysCache(PROCNAMENSP,
- CStringGetDatum(typname),
- Int16GetDatum(2),
- PointerGetDatum(oid_array),
- ObjectIdGetDatum(typnamespace));
- if (HeapTupleIsValid(ftup))
- {
- Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
-
- /* Make sure the function's result type is as expected */
- if (pform->prorettype == typeId && !pform->proretset &&
- !pform->proisagg)
- {
- /* Okay to use it */
- funcid = HeapTupleGetOid(ftup);
- }
- ReleaseSysCache(ftup);
- }
+ /* Look in pg_cast */
+ tuple = SearchSysCache(CASTSOURCETARGET,
+ ObjectIdGetDatum(typeId),
+ ObjectIdGetDatum(typeId),
+ 0, 0);
- if (!OidIsValid(funcid))
+ if (HeapTupleIsValid(tuple))
{
- /* Didn't find a function, so now try (type, int4, bool) */
- oid_array[2] = BOOLOID;
- *nargs = 3;
-
- ftup = SearchSysCache(PROCNAMENSP,
- CStringGetDatum(typname),
- Int16GetDatum(3),
- PointerGetDatum(oid_array),
- ObjectIdGetDatum(typnamespace));
- if (HeapTupleIsValid(ftup))
- {
- Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
+ Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
- /* Make sure the function's result type is as expected */
- if (pform->prorettype == typeId && !pform->proretset &&
- !pform->proisagg)
- {
- /* Okay to use it */
- funcid = HeapTupleGetOid(ftup);
- }
- ReleaseSysCache(ftup);
- }
+ funcid = castForm->castfunc;
+ ReleaseSysCache(tuple);
}
- ReleaseSysCache(targetType);
-
/*
* Now, if we did find a coercion function for an array element type,
- * report array_length_coerce() as the function to use. We know it
- * takes three arguments always.
+ * report array_length_coerce() as the function to use.
*/
if (isArray && OidIsValid(funcid))
- {
funcid = F_ARRAY_LENGTH_COERCE;
- *nargs = 3;
- }
return funcid;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 3b4ad7cf8a0..6df4547ba2d 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.173 2004/06/09 19:08:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.174 2004/06/16 01:26:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1578,6 +1578,9 @@ exprTypmod(Node *expr)
*
* If coercedTypmod is not NULL, the typmod is stored there if the expression
* is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
*/
bool
exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 781c94f8c2f..d5d71b67afd 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.170 2004/05/30 23:40:35 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -150,7 +150,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* these cases, so why duplicate code...
*/
return coerce_type(pstate, linitial(fargs),
- actual_arg_types[0], rettype,
+ actual_arg_types[0], rettype, -1,
COERCION_EXPLICIT, COERCE_EXPLICIT_CALL);
}
else if (fdresult == FUNCDETAIL_NORMAL)
@@ -726,11 +726,12 @@ func_get_detail(List *funcname,
{
Oid sourceType = argtypes[0];
Node *arg1 = linitial(fargs);
+ Oid cfuncid;
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
(find_coercion_pathway(targetType, sourceType,
- COERCION_EXPLICIT, funcid) &&
- *funcid == InvalidOid))
+ COERCION_EXPLICIT, &cfuncid) &&
+ cfuncid == InvalidOid))
{
/* Yup, it's a type coercion */
*funcid = InvalidOid;
@@ -1122,7 +1123,7 @@ make_fn_arguments(ParseState *pstate,
lfirst(current_fargs) = coerce_type(pstate,
lfirst(current_fargs),
actual_arg_types[i],
- declared_arg_types[i],
+ declared_arg_types[i], -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST);
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 768f8788b7e..bccc369dc09 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.139 2004/06/09 19:08:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.140 2004/06/16 01:26:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -367,7 +367,8 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
new_expr = coerce_to_domain(new_expr,
InvalidOid,
att_tup->atttypid,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ false);
}
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 62361932f98..e9951d839a3 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.104 2004/06/08 20:28:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.105 2004/06/16 01:26:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -109,6 +109,11 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
int *st, int *endp, char *srcPtr,
int typlen, bool typbyval, char typalign);
static int array_cmp(FunctionCallInfo fcinfo);
+static Datum array_type_length_coerce_internal(ArrayType *src,
+ int32 desttypmod,
+ bool isExplicit,
+ FmgrInfo *fmgr_info);
+
/*---------------------------------------------------------------------
* array_in :
@@ -1174,82 +1179,6 @@ array_send(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
-/*-------------------------------------------------------------------------
- * array_length_coerce :
- * Apply the element type's length-coercion routine to each element
- * of the given array.
- *-------------------------------------------------------------------------
- */
-Datum
-array_length_coerce(PG_FUNCTION_ARGS)
-{
- ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
- int32 len = PG_GETARG_INT32(1);
- bool isExplicit = PG_GETARG_BOOL(2);
- FmgrInfo *fmgr_info = fcinfo->flinfo;
- typedef struct
- {
- Oid elemtype;
- FmgrInfo coerce_finfo;
- } alc_extra;
- alc_extra *my_extra;
- FunctionCallInfoData locfcinfo;
-
- /* If no typmod is provided, shortcircuit the whole thing */
- if (len < 0)
- PG_RETURN_ARRAYTYPE_P(v);
-
- /*
- * We arrange to look up the element type's coercion function only
- * once per series of calls, assuming the element type doesn't change
- * underneath us.
- */
- my_extra = (alc_extra *) fmgr_info->fn_extra;
- if (my_extra == NULL)
- {
- fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
- sizeof(alc_extra));
- my_extra = (alc_extra *) fmgr_info->fn_extra;
- my_extra->elemtype = InvalidOid;
- }
-
- if (my_extra->elemtype != ARR_ELEMTYPE(v))
- {
- Oid funcId;
- int nargs;
-
- funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v), &nargs);
-
- if (OidIsValid(funcId))
- fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
- else
- my_extra->coerce_finfo.fn_oid = InvalidOid;
- my_extra->elemtype = ARR_ELEMTYPE(v);
- }
-
- /*
- * If we didn't find a coercion function, return the array unmodified
- * (this should not happen in the normal course of things, but might
- * happen if this function is called manually).
- */
- if (my_extra->coerce_finfo.fn_oid == InvalidOid)
- PG_RETURN_ARRAYTYPE_P(v);
-
- /*
- * Use array_map to apply the function to each array element.
- *
- * Note: we pass isExplicit whether or not the function wants it ...
- */
- MemSet(&locfcinfo, 0, sizeof(locfcinfo));
- locfcinfo.flinfo = &my_extra->coerce_finfo;
- locfcinfo.nargs = 3;
- locfcinfo.arg[0] = PointerGetDatum(v);
- locfcinfo.arg[1] = Int32GetDatum(len);
- locfcinfo.arg[2] = BoolGetDatum(isExplicit);
-
- return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v));
-}
-
/*-----------------------------------------------------------------------------
* array_dims :
* returns the dimensions of the array pointed to by "v", as a "text"
@@ -2879,6 +2808,9 @@ array_insert_slice(int ndim,
* array_type_coerce -- allow explicit or assignment coercion from
* one array type to another.
*
+ * array_type_length_coerce -- the same, for cases where both type and length
+ * coercion are done by a single function on the element type.
+ *
* Caller should have already verified that the source element type can be
* coerced into the target element type.
*/
@@ -2886,8 +2818,30 @@ Datum
array_type_coerce(PG_FUNCTION_ARGS)
{
ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
- Oid src_elem_type = ARR_ELEMTYPE(src);
FmgrInfo *fmgr_info = fcinfo->flinfo;
+
+ return array_type_length_coerce_internal(src, -1, false, fmgr_info);
+}
+
+Datum
+array_type_length_coerce(PG_FUNCTION_ARGS)
+{
+ ArrayType *src = PG_GETARG_ARRAYTYPE_P(0);
+ int32 desttypmod = PG_GETARG_INT32(1);
+ bool isExplicit = PG_GETARG_BOOL(2);
+ FmgrInfo *fmgr_info = fcinfo->flinfo;
+
+ return array_type_length_coerce_internal(src, desttypmod,
+ isExplicit, fmgr_info);
+}
+
+static Datum
+array_type_length_coerce_internal(ArrayType *src,
+ int32 desttypmod,
+ bool isExplicit,
+ FmgrInfo *fmgr_info)
+{
+ Oid src_elem_type = ARR_ELEMTYPE(src);
typedef struct
{
Oid srctype;
@@ -2946,7 +2900,8 @@ array_type_coerce(PG_FUNCTION_ARGS)
{
/* should never happen, but check anyway */
elog(ERROR, "no conversion function from %s to %s",
- format_type_be(src_elem_type), format_type_be(tgt_elem_type));
+ format_type_be(src_elem_type),
+ format_type_be(tgt_elem_type));
}
if (OidIsValid(funcId))
fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
@@ -2962,24 +2917,104 @@ array_type_coerce(PG_FUNCTION_ARGS)
*/
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
{
- ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
+ ArrayType *result;
+ result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src),
+ false, -1));
ARR_ELEMTYPE(result) = my_extra->desttype;
PG_RETURN_ARRAYTYPE_P(result);
}
/*
* Use array_map to apply the function to each array element.
+ *
+ * We pass on the desttypmod and isExplicit flags whether or not the
+ * function wants them.
*/
MemSet(&locfcinfo, 0, sizeof(locfcinfo));
locfcinfo.flinfo = &my_extra->coerce_finfo;
- locfcinfo.nargs = 1;
+ locfcinfo.nargs = 3;
locfcinfo.arg[0] = PointerGetDatum(src);
+ locfcinfo.arg[1] = Int32GetDatum(desttypmod);
+ locfcinfo.arg[2] = BoolGetDatum(isExplicit);
return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
}
/*
+ * array_length_coerce -- apply the element type's length-coercion routine
+ * to each element of the given array.
+ */
+Datum
+array_length_coerce(PG_FUNCTION_ARGS)
+{
+ ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
+ int32 desttypmod = PG_GETARG_INT32(1);
+ bool isExplicit = PG_GETARG_BOOL(2);
+ FmgrInfo *fmgr_info = fcinfo->flinfo;
+ typedef struct
+ {
+ Oid elemtype;
+ FmgrInfo coerce_finfo;
+ } alc_extra;
+ alc_extra *my_extra;
+ FunctionCallInfoData locfcinfo;
+
+ /* If no typmod is provided, shortcircuit the whole thing */
+ if (desttypmod < 0)
+ PG_RETURN_ARRAYTYPE_P(v);
+
+ /*
+ * We arrange to look up the element type's coercion function only
+ * once per series of calls, assuming the element type doesn't change
+ * underneath us.
+ */
+ my_extra = (alc_extra *) fmgr_info->fn_extra;
+ if (my_extra == NULL)
+ {
+ fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
+ sizeof(alc_extra));
+ my_extra = (alc_extra *) fmgr_info->fn_extra;
+ my_extra->elemtype = InvalidOid;
+ }
+
+ if (my_extra->elemtype != ARR_ELEMTYPE(v))
+ {
+ Oid funcId;
+
+ funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v));
+
+ if (OidIsValid(funcId))
+ fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
+ else
+ my_extra->coerce_finfo.fn_oid = InvalidOid;
+ my_extra->elemtype = ARR_ELEMTYPE(v);
+ }
+
+ /*
+ * If we didn't find a coercion function, return the array unmodified
+ * (this should not happen in the normal course of things, but might
+ * happen if this function is called manually).
+ */
+ if (my_extra->coerce_finfo.fn_oid == InvalidOid)
+ PG_RETURN_ARRAYTYPE_P(v);
+
+ /*
+ * Use array_map to apply the function to each array element.
+ *
+ * Note: we pass isExplicit whether or not the function wants it ...
+ */
+ MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+ locfcinfo.flinfo = &my_extra->coerce_finfo;
+ locfcinfo.nargs = 3;
+ locfcinfo.arg[0] = PointerGetDatum(v);
+ locfcinfo.arg[1] = Int32GetDatum(desttypmod);
+ locfcinfo.arg[2] = BoolGetDatum(isExplicit);
+
+ return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v));
+}
+
+/*
* accumArrayResult - accumulate one (more) Datum for an array result
*
* astate is working state (NULL on first call)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 589db025d29..4caaabd62cd 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.171 2004/06/09 19:08:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.172 2004/06/16 01:26:47 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -194,7 +194,6 @@ static void get_oper_expr(OpExpr *expr, deparse_context *context);
static void get_func_expr(FuncExpr *expr, deparse_context *context,
bool showimplicit);
static void get_agg_expr(Aggref *aggref, deparse_context *context);
-static Node *strip_type_coercion(Node *expr, Oid resultType);
static void get_const_expr(Const *constval, deparse_context *context);
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
static void get_from_clause(Query *query, deparse_context *context);
@@ -2983,22 +2982,13 @@ get_rule_expr(Node *node, deparse_context *context,
!showimplicit)
{
/* don't show the implicit cast */
- get_rule_expr_paren(arg, context, showimplicit, node);
+ get_rule_expr_paren(arg, context, false, node);
}
else
{
- /*
- * Strip off any type coercions on the input, so we
- * don't print redundancies like
- * x::bpchar::character(8).
- *
- * XXX Are there any cases where this is a bad idea?
- */
- arg = strip_type_coercion(arg, relabel->resulttype);
-
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, '(');
- get_rule_expr_paren(arg, context, showimplicit, node);
+ get_rule_expr_paren(arg, context, false, node);
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')');
appendStringInfo(buf, "::%s",
@@ -3206,11 +3196,6 @@ get_rule_expr(Node *node, deparse_context *context,
CoerceToDomain *ctest = (CoerceToDomain *) node;
Node *arg = (Node *) ctest->arg;
- /*
- * Any implicit coercion at the top level of the argument
- * is presumably due to the domain's own internal typmod
- * coercion, so do not force it to be shown.
- */
if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
!showimplicit)
{
@@ -3331,7 +3316,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
{
get_rule_expr_paren((Node *) linitial(expr->args), context,
- showimplicit, (Node *) expr);
+ false, (Node *) expr);
return;
}
@@ -3349,17 +3334,9 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
/* Get the typmod if this is a length-coercion function */
(void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
- /*
- * Strip off any type coercions on the input, so we don't print
- * redundancies like x::bpchar::character(8).
- *
- * XXX Are there any cases where this is a bad idea?
- */
- arg = strip_type_coercion(arg, rettype);
-
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, '(');
- get_rule_expr_paren(arg, context, showimplicit, (Node *) expr);
+ get_rule_expr_paren(arg, context, false, (Node *) expr);
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')');
appendStringInfo(buf, "::%s",
@@ -3413,46 +3390,6 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
}
-/*
- * strip_type_coercion
- * Strip any type coercion at the top of the given expression tree,
- * if it is a coercion to the given datatype.
- *
- * We use this to avoid printing two levels of coercion in situations where
- * the expression tree has a length-coercion node atop a type-coercion node.
- *
- * Note: avoid stripping a length-coercion node, since two successive
- * coercions to different lengths aren't a no-op. Also, never strip a
- * CoerceToDomain node, even though it might be effectively just RelabelType.
- */
-static Node *
-strip_type_coercion(Node *expr, Oid resultType)
-{
- if (expr == NULL || exprType(expr) != resultType)
- return expr;
-
- if (IsA(expr, RelabelType) &&
- ((RelabelType *) expr)->resulttypmod == -1)
- return (Node *) ((RelabelType *) expr)->arg;
-
- if (IsA(expr, FuncExpr))
- {
- FuncExpr *func = (FuncExpr *) expr;
-
- if (func->funcformat != COERCE_EXPLICIT_CAST &&
- func->funcformat != COERCE_IMPLICIT_CAST)
- return expr; /* don't absorb into upper coercion */
-
- if (exprIsLengthCoercion(expr, NULL))
- return expr;
-
- return (Node *) linitial(func->args);
- }
-
- return expr;
-}
-
-
/* ----------
* get_const_expr
*
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 81da4fc1749..7fd7727e421 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.38 2003/11/29 19:51:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.39 2004/06/16 01:26:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1166,32 +1166,58 @@ bitshiftright(PG_FUNCTION_ARGS)
PG_RETURN_VARBIT_P(result);
}
-/* This is not defined in any standard. We retain the natural ordering of
+/*
+ * This is not defined in any standard. We retain the natural ordering of
* bits here, as it just seems more intuitive.
*/
Datum
bitfromint4(PG_FUNCTION_ARGS)
{
int32 a = PG_GETARG_INT32(0);
+ int32 typmod = PG_GETARG_INT32(1);
VarBit *result;
bits8 *r;
- int len;
+ int rlen;
+ int destbitsleft,
+ srcbitsleft;
- /* allocate enough space for the bits in an int4 */
- len = VARBITTOTALLEN(sizeof(int4) * BITS_PER_BYTE);
- result = (VarBit *) palloc(len);
- VARATT_SIZEP(result) = len;
- VARBITLEN(result) = sizeof(int4) * BITS_PER_BYTE;
+ if (typmod <= 0)
+ typmod = 1; /* default bit length */
+
+ rlen = VARBITTOTALLEN(typmod);
+ result = (VarBit *) palloc(rlen);
+ VARATT_SIZEP(result) = rlen;
+ VARBITLEN(result) = typmod;
- /*
- * masks and shifts here are just too painful and we know that an int4
- * has got 4 bytes
- */
r = VARBITS(result);
- r[0] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK);
- r[1] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK);
- r[2] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK);
- r[3] = (bits8) (a & BITMASK);
+ destbitsleft = typmod;
+ srcbitsleft = 32;
+ /* drop any input bits that don't fit */
+ srcbitsleft = Min(srcbitsleft, destbitsleft);
+ /* sign-fill any excess bytes in output */
+ while (destbitsleft >= srcbitsleft + 8)
+ {
+ *r++ = (bits8) ((a < 0) ? BITMASK : 0);
+ destbitsleft -= 8;
+ }
+ /* store first fractional byte */
+ if (destbitsleft > srcbitsleft)
+ {
+ *r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK);
+ destbitsleft -= 8;
+ }
+ /* Now srcbitsleft and destbitsleft are the same, need not track both */
+ /* store whole bytes */
+ while (destbitsleft >= 8)
+ {
+ *r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK);
+ destbitsleft -= 8;
+ }
+ /* store last fractional byte */
+ if (destbitsleft > 0)
+ {
+ *r = (bits8) ((a << (8 - destbitsleft)) & BITMASK);
+ }
PG_RETURN_VARBIT_P(result);
}
@@ -1204,7 +1230,7 @@ bittoint4(PG_FUNCTION_ARGS)
bits8 *r;
/* Check that the bit string is not too long */
- if (VARBITLEN(arg) > sizeof(int4) * BITS_PER_BYTE)
+ if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1224,46 +1250,62 @@ bittoint4(PG_FUNCTION_ARGS)
Datum
bitfromint8(PG_FUNCTION_ARGS)
{
-#ifndef INT64_IS_BUSTED
int64 a = PG_GETARG_INT64(0);
+ int32 typmod = PG_GETARG_INT32(1);
VarBit *result;
bits8 *r;
- int len;
+ int rlen;
+ int destbitsleft,
+ srcbitsleft;
- /* allocate enough space for the bits in an int64 */
- len = VARBITTOTALLEN(sizeof(a) * BITS_PER_BYTE);
- result = (VarBit *) palloc(len);
- VARATT_SIZEP(result) = len;
- VARBITLEN(result) = sizeof(a) * BITS_PER_BYTE;
+ if (typmod <= 0)
+ typmod = 1; /* default bit length */
- /*
- * masks and shifts here are just too painful and we know that an
- * int64 has got 8 bytes
- */
- r = VARBITS(result);
- r[0] = (bits8) ((a >> (7 * BITS_PER_BYTE)) & BITMASK);
- r[1] = (bits8) ((a >> (6 * BITS_PER_BYTE)) & BITMASK);
- r[2] = (bits8) ((a >> (5 * BITS_PER_BYTE)) & BITMASK);
- r[3] = (bits8) ((a >> (4 * BITS_PER_BYTE)) & BITMASK);
- r[4] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK);
- r[5] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK);
- r[6] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK);
- r[7] = (bits8) (a & BITMASK);
+ rlen = VARBITTOTALLEN(typmod);
+ result = (VarBit *) palloc(rlen);
+ VARATT_SIZEP(result) = rlen;
+ VARBITLEN(result) = typmod;
- PG_RETURN_VARBIT_P(result);
+ r = VARBITS(result);
+ destbitsleft = typmod;
+#ifndef INT64_IS_BUSTED
+ srcbitsleft = 64;
#else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("64-bit integers not supported on this platform")));
-
- PG_RETURN_NULL();
+ srcbitsleft = 32; /* don't try to shift more than 32 */
#endif
+ /* drop any input bits that don't fit */
+ srcbitsleft = Min(srcbitsleft, destbitsleft);
+ /* sign-fill any excess bytes in output */
+ while (destbitsleft >= srcbitsleft + 8)
+ {
+ *r++ = (bits8) ((a < 0) ? BITMASK : 0);
+ destbitsleft -= 8;
+ }
+ /* store first fractional byte */
+ if (destbitsleft > srcbitsleft)
+ {
+ *r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK);
+ destbitsleft -= 8;
+ }
+ /* Now srcbitsleft and destbitsleft are the same, need not track both */
+ /* store whole bytes */
+ while (destbitsleft >= 8)
+ {
+ *r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK);
+ destbitsleft -= 8;
+ }
+ /* store last fractional byte */
+ if (destbitsleft > 0)
+ {
+ *r = (bits8) ((a << (8 - destbitsleft)) & BITMASK);
+ }
+
+ PG_RETURN_VARBIT_P(result);
}
Datum
bittoint8(PG_FUNCTION_ARGS)
{
-#ifndef INT64_IS_BUSTED
VarBit *arg = PG_GETARG_VARBIT_P(0);
uint64 result;
bits8 *r;
@@ -1284,13 +1326,6 @@ bittoint8(PG_FUNCTION_ARGS)
result >>= VARBITPAD(arg);
PG_RETURN_INT64(result);
-#else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("64-bit integers not supported on this platform")));
-
- PG_RETURN_NULL();
-#endif
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2bebe32b615..6fd25b54f7a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.235 2004/06/13 21:57:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.236 2004/06/16 01:26:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200406131
+#define CATALOG_VERSION_NO 200406151
#endif
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 549fa5f80db..069f2e8e60c 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -4,10 +4,13 @@
* definition of the system "type casts" relation (pg_cast)
* along with the relation's initial contents.
*
+ * As of Postgres 7.5, pg_cast describes not only type coercion functions
+ * but also length coercion functions.
+ *
*
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.11 2004/03/15 01:13:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.12 2004/06/16 01:26:49 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -43,7 +46,7 @@ typedef enum CoercionCodes
* expression */
COERCION_CODE_ASSIGNMENT = 'a', /* coercion in context of
* assignment */
- COERCION_CODE_EXPLICIT = 'e' /* explicit cast operation */
+ COERCION_CODE_EXPLICIT = 'e' /* explicit cast operation */
} CoercionCodes;
@@ -361,4 +364,18 @@ DATA(insert ( 1042 1266 938 e ));
DATA(insert ( 1700 1042 1688 a ));
DATA(insert ( 1042 1700 1686 e ));
+/*
+ * Length-coercion functions
+ */
+DATA(insert ( 1042 1042 668 i ));
+DATA(insert ( 1043 1043 669 i ));
+DATA(insert ( 1083 1083 1968 i ));
+DATA(insert ( 1114 1114 1961 i ));
+DATA(insert ( 1184 1184 1967 i ));
+DATA(insert ( 1186 1186 1200 i ));
+DATA(insert ( 1266 1266 1969 i ));
+DATA(insert ( 1560 1560 1685 i ));
+DATA(insert ( 1562 1562 1687 i ));
+DATA(insert ( 1700 1700 1703 i ));
+
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b7fad1fca52..ecaf82efab1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.337 2004/06/13 21:57:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.338 2004/06/16 01:26:49 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -1028,8 +1028,8 @@ DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283
DESCR("prepend element onto front of array");
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" _null_ array_cat - _null_ ));
DESCR("concatenate two arrays");
-DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" _null_ array_type_coerce - _null_ ));
-DESCR("coerce array type to another array type");
+DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f s 1 2277 "2277" _null_ array_type_coerce - _null_ ));
+DESCR("coerce array to another array type");
DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" _null_ text_to_array - _null_ ));
DESCR("split delimited text into text[]");
DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" _null_ array_to_text - _null_ ));
@@ -1587,8 +1587,8 @@ DESCR("convert int8 to text");
DATA(insert OID = 1290 ( int8 PGNSP PGUID 12 f f t f i 1 20 "25" _null_ text_int8 - _null_ ));
DESCR("convert text to int8");
-DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 f f t f i 3 2277 "2277 23 16" _null_ array_length_coerce - _null_ ));
-DESCR("adjust any array to element typmod length");
+DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_ array_length_coerce - _null_ ));
+DESCR("adjust any array to new element typmod");
DATA(insert OID = 1292 ( tideq PGNSP PGUID 12 f f t f i 2 16 "27 27" _null_ tideq - _null_ ));
DESCR("equal");
@@ -1722,6 +1722,9 @@ DESCR("convert time to interval");
DATA(insert OID = 1372 ( char_length PGNSP PGUID 12 f f t f i 1 23 "1042" _null_ bpcharlen - _null_ ));
DESCR("character length");
+DATA(insert OID = 1373 ( array_type_length_coerce PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_ array_type_length_coerce - _null_ ));
+DESCR("coerce array to another type and adjust element typmod");
+
DATA(insert OID = 1374 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "25" _null_ textoctetlen - _null_ ));
DESCR("octet length");
DATA(insert OID = 1375 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "1042" _null_ bpcharoctetlen - _null_ ));
@@ -2298,7 +2301,7 @@ DATA(insert OID = 1681 ( length PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ b
DESCR("bitstring length");
DATA(insert OID = 1682 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ bitoctetlength - _null_ ));
DESCR("octet length");
-DATA(insert OID = 1683 ( bit PGNSP PGUID 12 f f t f i 1 1560 "23" _null_ bitfromint4 - _null_ ));
+DATA(insert OID = 1683 ( bit PGNSP PGUID 12 f f t f i 2 1560 "23 23" _null_ bitfromint4 - _null_ ));
DESCR("int4 to bitstring");
DATA(insert OID = 1684 ( int4 PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ bittoint4 - _null_ ));
DESCR("bitstring to int4");
@@ -2968,7 +2971,7 @@ DESCR("extracts text matching regular expression");
DATA(insert OID = 2074 ( substring PGNSP PGUID 14 f f t f i 3 25 "25 25 25" _null_ "select pg_catalog.substring($1, pg_catalog.similar_escape($2, $3))" - _null_ ));
DESCR("extracts text matching SQL99 regular expression");
-DATA(insert OID = 2075 ( bit PGNSP PGUID 12 f f t f i 1 1560 "20" _null_ bitfromint8 - _null_ ));
+DATA(insert OID = 2075 ( bit PGNSP PGUID 12 f f t f i 2 1560 "20 23" _null_ bitfromint8 - _null_ ));
DESCR("int8 to bitstring");
DATA(insert OID = 2076 ( int8 PGNSP PGUID 12 f f t f i 1 20 "1560" _null_ bittoint8 - _null_ ));
DESCR("bitstring to int8");
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 8700200b51b..c1c83d514d9 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.56 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.57 2004/06/16 01:26:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,10 +47,10 @@ extern Node *coerce_to_target_type(ParseState *pstate,
extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
CoercionContext ccontext);
extern Node *coerce_type(ParseState *pstate, Node *node,
- Oid inputTypeId, Oid targetTypeId,
+ Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat);
extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
- CoercionForm cformat);
+ CoercionForm cformat, bool hideInputCoercion);
extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
const char *constructName);
@@ -76,6 +76,6 @@ extern Oid resolve_generic_type(Oid declared_type,
extern bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
CoercionContext ccontext,
Oid *funcid);
-extern Oid find_typmod_coercion_function(Oid typeId, int *nargs);
+extern Oid find_typmod_coercion_function(Oid typeId);
#endif /* PARSE_COERCE_H */
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 596cf79d4d3..3114aefe10b 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.47 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.48 2004/06/16 01:26:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -116,7 +116,6 @@ extern Datum array_in(PG_FUNCTION_ARGS);
extern Datum array_out(PG_FUNCTION_ARGS);
extern Datum array_recv(PG_FUNCTION_ARGS);
extern Datum array_send(PG_FUNCTION_ARGS);
-extern Datum array_length_coerce(PG_FUNCTION_ARGS);
extern Datum array_eq(PG_FUNCTION_ARGS);
extern Datum array_ne(PG_FUNCTION_ARGS);
extern Datum array_lt(PG_FUNCTION_ARGS);
@@ -128,6 +127,8 @@ extern Datum array_dims(PG_FUNCTION_ARGS);
extern Datum array_lower(PG_FUNCTION_ARGS);
extern Datum array_upper(PG_FUNCTION_ARGS);
extern Datum array_type_coerce(PG_FUNCTION_ARGS);
+extern Datum array_type_length_coerce(PG_FUNCTION_ARGS);
+extern Datum array_length_coerce(PG_FUNCTION_ARGS);
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
int arraylen, int elmlen, bool elmbyval, char elmalign,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 95b3a2b2ebb..f6cebd9a57a 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -225,13 +225,28 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT
(1 row)
-- **************** pg_cast ****************
--- Look for casts from and to the same type. This is not harmful, but
--- useless. Also catch bogus values in pg_cast columns (other than
--- cases detected by oidjoins test).
+-- Catch bogus values in pg_cast columns (other than cases detected by
+-- oidjoins test).
SELECT *
FROM pg_cast c
-WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
- OR castcontext NOT IN ('e', 'a', 'i');
+WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i');
+ castsource | casttarget | castfunc | castcontext
+------------+------------+----------+-------------
+(0 rows)
+
+-- Look for casts to/from the same type that aren't length coercion functions.
+-- (We assume they are length coercions if they take multiple arguments.)
+-- Such entries are not necessarily harmful, but they are useless.
+SELECT *
+FROM pg_cast c
+WHERE castsource = casttarget AND castfunc = 0;
+ castsource | casttarget | castfunc | castcontext
+------------+------------+----------+-------------
+(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
------------+------------+----------+-------------
(0 rows)
@@ -246,7 +261,7 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
SELECT c.*
FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND
- (p.pronargs <> 1
+ (p.pronargs < 1 OR p.pronargs > 3
OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
OR (c.castsource = 'character'::regtype AND
p.proargtypes[0] = 'text'::regtype))
@@ -255,6 +270,15 @@ WHERE c.castfunc = p.oid AND
------------+------------+----------+-------------
(0 rows)
+SELECT c.*
+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
+------------+------------+----------+-------------
+(0 rows)
+
-- Look for binary compatible casts that do not have the reverse
-- direction registered as well, or where the reverse direction is not
-- also binary compatible. This is legal, but usually not intended.
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9c17a0cd3e1..78ea8dc0cbf 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -184,14 +184,24 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT
-- **************** pg_cast ****************
--- Look for casts from and to the same type. This is not harmful, but
--- useless. Also catch bogus values in pg_cast columns (other than
--- cases detected by oidjoins test).
+-- Catch bogus values in pg_cast columns (other than cases detected by
+-- oidjoins test).
SELECT *
FROM pg_cast c
-WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
- OR castcontext NOT IN ('e', 'a', 'i');
+WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i');
+
+-- Look for casts to/from the same type that aren't length coercion functions.
+-- (We assume they are length coercions if they take multiple arguments.)
+-- Such entries are not necessarily harmful, but they are useless.
+
+SELECT *
+FROM pg_cast c
+WHERE castsource = casttarget AND castfunc = 0;
+
+SELECT c.*
+FROM pg_cast c, pg_proc p
+WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
-- Look for cast functions that don't have the right signature. The
-- argument and result types in pg_proc must be the same as, or binary
@@ -204,12 +214,18 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
SELECT c.*
FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND
- (p.pronargs <> 1
+ (p.pronargs < 1 OR p.pronargs > 3
OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
OR (c.castsource = 'character'::regtype AND
p.proargtypes[0] = 'text'::regtype))
OR NOT binary_coercible(p.prorettype, c.casttarget));
+SELECT c.*
+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));
+
-- Look for binary compatible casts that do not have the reverse
-- direction registered as well, or where the reverse direction is not
-- also binary compatible. This is legal, but usually not intended.