1998-05-10 01:31:34 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* parse_coerce.c
|
2000-02-20 22:32:16 +01:00
|
|
|
* handle type coercions/conversions for parser
|
1998-05-10 01:31:34 +02:00
|
|
|
*
|
2002-06-20 22:29:54 +02:00
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1998-05-10 01:31:34 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-09-01 04:27:32 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.82 2002/09/01 02:27:32 tgl Exp $
|
1998-05-10 01:31:34 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2002-07-19 01:11:32 +02:00
|
|
|
#include "catalog/pg_cast.h"
|
2000-03-19 01:15:39 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
2002-03-20 20:45:13 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
1999-10-03 01:29:19 +02:00
|
|
|
#include "optimizer/clauses.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "parser/parse_coerce.h"
|
|
|
|
#include "parser/parse_expr.h"
|
1999-06-18 00:21:41 +02:00
|
|
|
#include "parser/parse_func.h"
|
2000-06-15 05:33:12 +02:00
|
|
|
#include "parser/parse_type.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
2002-03-20 20:45:13 +01:00
|
|
|
#include "utils/lsyscache.h"
|
1998-05-10 01:31:34 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
static Oid PreferredType(CATEGORY category, Oid type);
|
2002-09-01 04:27:32 +02:00
|
|
|
static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
|
|
|
|
bool isExplicit,
|
|
|
|
Oid *funcid);
|
2002-07-19 01:11:32 +02:00
|
|
|
static Oid find_typmod_coercion_function(Oid typeId);
|
2002-09-01 04:27:32 +02:00
|
|
|
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
|
2002-09-01 00:10:48 +02:00
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
/*
|
|
|
|
* coerce_type()
|
|
|
|
* Convert a function argument to a different type.
|
|
|
|
*
|
|
|
|
* The caller should already have determined that the coercion is possible;
|
|
|
|
* see can_coerce_type.
|
1998-05-10 01:31:34 +02:00
|
|
|
*/
|
|
|
|
Node *
|
2000-01-17 01:14:49 +01:00
|
|
|
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
2002-04-11 22:00:18 +02:00
|
|
|
Oid targetTypeId, int32 atttypmod, bool isExplicit)
|
1998-05-10 01:31:34 +02:00
|
|
|
{
|
2000-02-20 22:32:16 +01:00
|
|
|
Node *result;
|
2002-09-01 04:27:32 +02:00
|
|
|
Oid funcId;
|
1998-05-10 01:31:34 +02:00
|
|
|
|
2000-03-23 08:36:03 +01:00
|
|
|
if (targetTypeId == inputTypeId ||
|
|
|
|
node == NULL)
|
1999-08-05 04:33:54 +02:00
|
|
|
{
|
2002-09-01 00:10:48 +02:00
|
|
|
/* no conversion needed */
|
1998-05-10 01:31:34 +02:00
|
|
|
result = node;
|
1999-08-05 04:33:54 +02:00
|
|
|
}
|
|
|
|
else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
|
|
|
|
{
|
1999-10-03 01:29:19 +02:00
|
|
|
/*
|
|
|
|
* Input is a string constant with previously undetermined type.
|
2000-04-12 19:17:23 +02:00
|
|
|
* Apply the target type's typinput function to it to produce a
|
|
|
|
* constant of the target type.
|
1999-08-05 04:33:54 +02:00
|
|
|
*
|
|
|
|
* NOTE: this case cannot be folded together with the other
|
|
|
|
* constant-input case, since the typinput function does not
|
2000-04-12 19:17:23 +02:00
|
|
|
* necessarily behave the same as a type conversion function. For
|
|
|
|
* example, int4's typinput function will reject "1.2", whereas
|
|
|
|
* float-to-int type conversion will round to integer.
|
1999-10-03 01:29:19 +02:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* XXX if the typinput function is not cachable, we really ought to
|
|
|
|
* postpone evaluation of the function call until runtime. But
|
|
|
|
* there is no way to represent a typinput function call as an
|
|
|
|
* expression tree, because C-string values are not Datums.
|
2002-09-01 00:10:48 +02:00
|
|
|
* (XXX This *is* possible as of 7.3, do we want to do it?)
|
1998-09-01 06:40:42 +02:00
|
|
|
*/
|
1999-08-05 04:33:54 +02:00
|
|
|
Const *con = (Const *) node;
|
1999-08-24 02:09:56 +02:00
|
|
|
Const *newcon = makeNode(Const);
|
1999-08-05 04:33:54 +02:00
|
|
|
Type targetType = typeidType(targetTypeId);
|
2002-09-01 00:10:48 +02:00
|
|
|
char targetTyptype = typeTypType(targetType);
|
1999-08-05 04:33:54 +02:00
|
|
|
|
1999-08-24 02:09:56 +02:00
|
|
|
newcon->consttype = targetTypeId;
|
|
|
|
newcon->constlen = typeLen(targetType);
|
|
|
|
newcon->constbyval = typeByVal(targetType);
|
|
|
|
newcon->constisnull = con->constisnull;
|
|
|
|
newcon->constisset = false;
|
1999-08-05 04:33:54 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!con->constisnull)
|
1999-08-24 02:09:56 +02:00
|
|
|
{
|
2002-04-25 04:56:56 +02:00
|
|
|
char *val = DatumGetCString(DirectFunctionCall1(unknownout,
|
2001-03-22 05:01:46 +01:00
|
|
|
con->constvalue));
|
2002-09-01 00:10:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If target is a domain, use the typmod it applies to the base
|
|
|
|
* type. Note that we call stringTypeDatum using the domain's
|
|
|
|
* pg_type row, though. This works because the domain row has
|
|
|
|
* the same typinput and typelem as the base type --- ugly...
|
|
|
|
*/
|
|
|
|
if (targetTyptype == 'd')
|
|
|
|
atttypmod = getBaseTypeMod(targetTypeId, atttypmod);
|
|
|
|
|
1999-08-24 02:09:56 +02:00
|
|
|
newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
|
|
|
|
pfree(val);
|
|
|
|
}
|
1998-05-10 01:31:34 +02:00
|
|
|
|
1999-08-24 02:09:56 +02:00
|
|
|
result = (Node *) newcon;
|
2002-09-01 00:10:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If target is a domain, apply constraints (except for typmod,
|
|
|
|
* which we assume the input routine took care of).
|
|
|
|
*/
|
|
|
|
if (targetTyptype == 'd')
|
|
|
|
result = coerce_type_constraints(pstate, result, targetTypeId,
|
|
|
|
false);
|
|
|
|
|
|
|
|
ReleaseSysCache(targetType);
|
1999-08-05 04:33:54 +02:00
|
|
|
}
|
2002-08-22 02:01:51 +02:00
|
|
|
else if (targetTypeId == ANYOID ||
|
|
|
|
targetTypeId == ANYARRAYOID)
|
|
|
|
{
|
|
|
|
/* assume can_coerce_type verified that implicit coercion is okay */
|
|
|
|
result = node;
|
|
|
|
}
|
2002-09-01 04:27:32 +02:00
|
|
|
else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
|
|
|
|
&funcId))
|
2000-02-20 22:32:16 +01:00
|
|
|
{
|
2002-09-01 04:27:32 +02:00
|
|
|
if (OidIsValid(funcId))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
Oid baseTypeId = getBaseType(targetTypeId);
|
|
|
|
|
|
|
|
result = build_func_call(funcId, baseTypeId, makeList1(node));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If domain, test against domain constraints and relabel with
|
|
|
|
* domain type ID
|
|
|
|
*/
|
|
|
|
if (targetTypeId != baseTypeId)
|
|
|
|
{
|
|
|
|
result = coerce_type_constraints(pstate, result,
|
|
|
|
targetTypeId, true);
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the input is a constant, apply the type conversion function
|
|
|
|
* now instead of delaying to runtime. (We could, of course, just
|
|
|
|
* leave this to be done during planning/optimization; but it's a
|
|
|
|
* very frequent special case, and we save cycles in the rewriter
|
|
|
|
* if we fold the expression now.)
|
|
|
|
*
|
|
|
|
* Note that no folding will occur if the conversion function is
|
|
|
|
* not marked 'immutable'.
|
|
|
|
*
|
|
|
|
* HACK: if constant is NULL, don't fold it here. This is needed
|
|
|
|
* by make_subplan(), which calls this routine on placeholder
|
|
|
|
* Const nodes that mustn't be collapsed. (It'd be a lot cleaner
|
|
|
|
* to make a separate node type for that purpose...)
|
|
|
|
*/
|
|
|
|
if (IsA(node, Const) &&
|
|
|
|
!((Const *) node)->constisnull)
|
|
|
|
result = eval_const_expressions(result);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We don't need to do a physical conversion, but we do need to
|
|
|
|
* attach a RelabelType node so that the expression will be seen
|
|
|
|
* to have the intended type when inspected by higher-level code.
|
|
|
|
*
|
|
|
|
* Also, domains may have value restrictions beyond the base type
|
|
|
|
* that must be accounted for.
|
|
|
|
*/
|
|
|
|
result = coerce_type_constraints(pstate, node,
|
|
|
|
targetTypeId, true);
|
|
|
|
/*
|
|
|
|
* XXX could we label result with exprTypmod(node) instead of
|
|
|
|
* default -1 typmod, to save a possible length-coercion later?
|
|
|
|
* Would work if both types have same interpretation of typmod,
|
|
|
|
* which is likely but not certain (wrong if target is a domain,
|
|
|
|
* in any case).
|
|
|
|
*/
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1);
|
|
|
|
}
|
2000-02-20 22:32:16 +01:00
|
|
|
}
|
2000-03-16 07:35:07 +01:00
|
|
|
else if (typeInheritsFrom(inputTypeId, targetTypeId))
|
|
|
|
{
|
2002-03-20 20:45:13 +01:00
|
|
|
/*
|
|
|
|
* Input class type is a subclass of target, so nothing to do
|
|
|
|
* --- except relabel the type. This is binary compatibility
|
|
|
|
* for complex types.
|
|
|
|
*/
|
|
|
|
result = (Node *) makeRelabelType(node, targetTypeId, -1);
|
2000-03-16 07:35:07 +01:00
|
|
|
}
|
1999-08-05 04:33:54 +02:00
|
|
|
else
|
|
|
|
{
|
2002-09-01 04:27:32 +02:00
|
|
|
/* If we get here, caller blew it */
|
|
|
|
elog(ERROR, "coerce_type: no conversion function from %s to %s",
|
|
|
|
format_type_be(inputTypeId), format_type_be(targetTypeId));
|
|
|
|
result = NULL; /* keep compiler quiet */
|
1998-05-10 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
1999-05-22 06:12:29 +02:00
|
|
|
}
|
1998-05-10 01:31:34 +02:00
|
|
|
|
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
/*
|
|
|
|
* can_coerce_type()
|
|
|
|
* Can input_typeids be coerced to func_typeids?
|
2002-04-11 22:00:18 +02:00
|
|
|
*
|
|
|
|
* We must be told whether this is an implicit or explicit coercion
|
|
|
|
* (explicit being a CAST construct, explicit function call, etc).
|
|
|
|
* We will accept a wider set of coercion cases for an explicit coercion.
|
1998-05-10 01:31:34 +02:00
|
|
|
*/
|
|
|
|
bool
|
2002-04-11 22:00:18 +02:00
|
|
|
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
|
|
|
|
bool isExplicit)
|
1998-05-10 01:31:34 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* run through argument list... */
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Oid inputTypeId = input_typeids[i];
|
|
|
|
Oid targetTypeId = func_typeids[i];
|
2002-03-20 20:45:13 +01:00
|
|
|
Oid funcId;
|
1998-09-01 06:40:42 +02:00
|
|
|
|
2000-03-16 07:35:07 +01:00
|
|
|
/* no problem if same type */
|
|
|
|
if (inputTypeId == targetTypeId)
|
|
|
|
continue;
|
1998-05-10 01:31:34 +02:00
|
|
|
|
2002-08-22 02:01:51 +02:00
|
|
|
/* don't choke on references to no-longer-existing types */
|
|
|
|
if (!typeidIsValid(inputTypeId))
|
2000-03-16 07:35:07 +01:00
|
|
|
return false;
|
2002-08-22 02:01:51 +02:00
|
|
|
if (!typeidIsValid(targetTypeId))
|
2000-03-16 07:35:07 +01:00
|
|
|
return false;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2000-03-16 07:35:07 +01:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* If input is an untyped string constant, assume we can convert
|
|
|
|
* it to anything except a class type.
|
2000-03-16 07:35:07 +01:00
|
|
|
*/
|
|
|
|
if (inputTypeId == UNKNOWNOID)
|
|
|
|
{
|
|
|
|
if (ISCOMPLEX(targetTypeId))
|
1998-05-10 01:31:34 +02:00
|
|
|
return false;
|
2000-03-16 07:35:07 +01:00
|
|
|
continue;
|
1998-05-10 01:31:34 +02:00
|
|
|
}
|
2000-03-16 07:35:07 +01:00
|
|
|
|
2002-08-22 02:01:51 +02:00
|
|
|
/* accept if target is ANY */
|
|
|
|
if (targetTypeId == ANYOID)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* if target is ANYARRAY and source is a varlena array type, accept */
|
|
|
|
if (targetTypeId == ANYARRAYOID)
|
|
|
|
{
|
|
|
|
Oid typOutput;
|
|
|
|
Oid typElem;
|
|
|
|
bool typIsVarlena;
|
|
|
|
|
|
|
|
if (getTypeOutputInfo(inputTypeId, &typOutput, &typElem,
|
|
|
|
&typIsVarlena))
|
|
|
|
{
|
|
|
|
if (OidIsValid(typElem) && typIsVarlena)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Otherwise reject; this assumes there are no explicit coercions
|
|
|
|
* to ANYARRAY. If we don't reject then parse_coerce would have
|
|
|
|
* to repeat the above test.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-09-01 04:27:32 +02:00
|
|
|
* If pg_cast shows that we can coerce, accept. This test now
|
|
|
|
* covers both binary-compatible and coercion-function cases.
|
2002-08-22 02:01:51 +02:00
|
|
|
*/
|
2002-09-01 04:27:32 +02:00
|
|
|
if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
|
|
|
|
&funcId))
|
2002-08-22 02:01:51 +02:00
|
|
|
continue;
|
|
|
|
|
2000-03-16 07:35:07 +01:00
|
|
|
/*
|
2002-09-01 04:27:32 +02:00
|
|
|
* If input is a class type that inherits from target, accept
|
2000-03-16 07:35:07 +01:00
|
|
|
*/
|
|
|
|
if (typeInheritsFrom(inputTypeId, targetTypeId))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
2002-09-01 04:27:32 +02:00
|
|
|
* Else, cannot coerce at this argument position
|
2000-03-16 07:35:07 +01:00
|
|
|
*/
|
2002-09-01 04:27:32 +02:00
|
|
|
return false;
|
1998-05-10 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
1999-05-22 06:12:29 +02:00
|
|
|
}
|
1998-05-10 01:31:34 +02:00
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create an expression tree to enforce the constraints (if any)
|
|
|
|
* that should be applied by the type. Currently this is only
|
|
|
|
* interesting for domain types.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
coerce_type_constraints(ParseState *pstate, Node *arg,
|
|
|
|
Oid typeId, bool applyTypmod)
|
|
|
|
{
|
|
|
|
char *notNull = NULL;
|
|
|
|
int32 typmod = -1;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
HeapTuple tup;
|
|
|
|
Form_pg_type typTup;
|
|
|
|
|
|
|
|
tup = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(typeId),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
elog(ERROR, "coerce_type_constraints: failed to lookup type %u",
|
|
|
|
typeId);
|
|
|
|
typTup = (Form_pg_type) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/* Test for NOT NULL Constraint */
|
|
|
|
if (typTup->typnotnull && notNull == NULL)
|
|
|
|
notNull = pstrdup(NameStr(typTup->typname));
|
|
|
|
|
|
|
|
/* TODO: Add CHECK Constraints to domains */
|
|
|
|
|
|
|
|
if (typTup->typtype != 'd')
|
|
|
|
{
|
|
|
|
/* Not a domain, so done */
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(typmod < 0);
|
|
|
|
|
|
|
|
typeId = typTup->typbasetype;
|
|
|
|
typmod = typTup->typtypmod;
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If domain applies a typmod to its base type, do length coercion.
|
|
|
|
*/
|
|
|
|
if (applyTypmod && typmod >= 0)
|
|
|
|
arg = coerce_type_typmod(pstate, arg, typeId, typmod);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only need to add one NOT NULL check regardless of how many
|
|
|
|
* domains in the stack request it. The topmost domain that
|
|
|
|
* requested it is used as the constraint name.
|
|
|
|
*/
|
|
|
|
if (notNull)
|
|
|
|
{
|
|
|
|
ConstraintTest *r = makeNode(ConstraintTest);
|
|
|
|
|
|
|
|
r->arg = arg;
|
|
|
|
r->testtype = CONSTR_TEST_NOTNULL;
|
|
|
|
r->name = notNull;
|
|
|
|
r->check_expr = NULL;
|
|
|
|
|
|
|
|
arg = (Node *) r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-01-17 01:14:49 +01:00
|
|
|
/* coerce_type_typmod()
|
|
|
|
* Force a value to a particular typmod, if meaningful and possible.
|
|
|
|
*
|
|
|
|
* This is applied to values that are going to be stored in a relation
|
|
|
|
* (where we have an atttypmod for the column) as well as values being
|
|
|
|
* explicitly CASTed (where the typmod comes from the target type spec).
|
|
|
|
*
|
|
|
|
* The caller must have already ensured that the value is of the correct
|
|
|
|
* type, typically by applying coerce_type.
|
|
|
|
*
|
2002-09-01 00:10:48 +02:00
|
|
|
* 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
|
2002-09-01 04:27:32 +02:00
|
|
|
* needed to produce the domain value in the first place. So, no getBaseType.
|
2000-01-17 01:14:49 +01:00
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
coerce_type_typmod(ParseState *pstate, Node *node,
|
|
|
|
Oid targetTypeId, int32 atttypmod)
|
|
|
|
{
|
2002-03-20 20:45:13 +01:00
|
|
|
Oid funcId;
|
2000-01-17 01:14:49 +01:00
|
|
|
|
|
|
|
/*
|
2001-10-04 19:52:24 +02:00
|
|
|
* A negative typmod is assumed to mean that no coercion is wanted.
|
2000-01-17 01:14:49 +01:00
|
|
|
*/
|
2001-10-04 19:52:24 +02:00
|
|
|
if (atttypmod < 0 || atttypmod == exprTypmod(node))
|
2000-01-17 01:14:49 +01:00
|
|
|
return node;
|
|
|
|
|
2002-09-01 00:10:48 +02:00
|
|
|
funcId = find_typmod_coercion_function(targetTypeId);
|
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
if (OidIsValid(funcId))
|
2000-01-17 01:14:49 +01:00
|
|
|
{
|
2002-03-20 20:45:13 +01:00
|
|
|
Const *cons;
|
2000-01-17 01:14:49 +01:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
cons = makeConst(INT4OID,
|
|
|
|
sizeof(int32),
|
|
|
|
Int32GetDatum(atttypmod),
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
false);
|
2000-01-17 01:14:49 +01:00
|
|
|
|
2002-09-01 00:10:48 +02:00
|
|
|
node = build_func_call(funcId, targetTypeId, makeList2(node, cons));
|
2000-01-17 01:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
|
2001-06-20 00:39:12 +02:00
|
|
|
/* coerce_to_boolean()
|
|
|
|
* Coerce an argument of a construct that requires boolean input
|
2002-05-13 01:43:04 +02:00
|
|
|
* (AND, OR, NOT, etc). Also check that input is not a set.
|
2001-06-20 00:39:12 +02:00
|
|
|
*
|
2002-05-13 01:43:04 +02:00
|
|
|
* Returns the possibly-transformed node tree.
|
2001-06-20 00:39:12 +02:00
|
|
|
*/
|
2002-05-13 01:43:04 +02:00
|
|
|
Node *
|
|
|
|
coerce_to_boolean(Node *node, const char *constructName)
|
2001-06-20 00:39:12 +02:00
|
|
|
{
|
2002-05-13 01:43:04 +02:00
|
|
|
Oid inputTypeId = exprType(node);
|
2001-06-20 00:39:12 +02:00
|
|
|
Oid targetTypeId;
|
|
|
|
|
2002-05-13 01:43:04 +02:00
|
|
|
if (inputTypeId != BOOLOID)
|
|
|
|
{
|
|
|
|
targetTypeId = BOOLOID;
|
|
|
|
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
|
|
|
{
|
|
|
|
/* translator: first %s is name of a SQL construct, eg WHERE */
|
|
|
|
elog(ERROR, "Argument of %s must be type boolean, not type %s",
|
|
|
|
constructName, format_type_be(inputTypeId));
|
|
|
|
}
|
|
|
|
node = coerce_type(NULL, node, inputTypeId, targetTypeId, -1,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expression_returns_set(node))
|
|
|
|
{
|
|
|
|
/* translator: %s is name of a SQL construct, eg WHERE */
|
|
|
|
elog(ERROR, "Argument of %s must not be a set function",
|
|
|
|
constructName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
2001-06-20 00:39:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-10-05 21:11:39 +02:00
|
|
|
/* select_common_type()
|
|
|
|
* Determine the common supertype of a list of input expression types.
|
|
|
|
* This is used for determining the output type of CASE and UNION
|
|
|
|
* constructs.
|
|
|
|
*
|
|
|
|
* typeids is a nonempty integer list of type OIDs. Note that earlier items
|
|
|
|
* in the list will be preferred if there is doubt.
|
|
|
|
* 'context' is a phrase to use in the error message if we fail to select
|
|
|
|
* a usable type.
|
|
|
|
*
|
|
|
|
* XXX this code is WRONG, since (for example) given the input (int4,int8)
|
|
|
|
* it will select int4, whereas according to SQL92 clause 9.3 the correct
|
2001-03-22 05:01:46 +01:00
|
|
|
* answer is clearly int8. To fix this we need a notion of a promotion
|
2000-10-05 21:11:39 +02:00
|
|
|
* hierarchy within type categories --- something more complete than
|
|
|
|
* just a single preferred type.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
select_common_type(List *typeids, const char *context)
|
|
|
|
{
|
|
|
|
Oid ptype;
|
|
|
|
CATEGORY pcategory;
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
Assert(typeids != NIL);
|
|
|
|
ptype = (Oid) lfirsti(typeids);
|
|
|
|
pcategory = TypeCategory(ptype);
|
|
|
|
foreach(l, lnext(typeids))
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
Oid ntype = (Oid) lfirsti(l);
|
2000-10-05 21:11:39 +02:00
|
|
|
|
|
|
|
/* move on to next one if no new information... */
|
2001-10-03 07:29:27 +02:00
|
|
|
if ((ntype != InvalidOid) && (ntype != UNKNOWNOID) && (ntype != ptype))
|
2000-10-05 21:11:39 +02:00
|
|
|
{
|
2001-10-03 07:29:27 +02:00
|
|
|
if ((ptype == InvalidOid) || ptype == UNKNOWNOID)
|
2000-10-05 21:11:39 +02:00
|
|
|
{
|
|
|
|
/* so far, only nulls so take anything... */
|
|
|
|
ptype = ntype;
|
|
|
|
pcategory = TypeCategory(ptype);
|
|
|
|
}
|
|
|
|
else if (TypeCategory(ntype) != pcategory)
|
|
|
|
{
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* both types in different categories? then not much
|
|
|
|
* hope...
|
2000-10-05 21:11:39 +02:00
|
|
|
*/
|
2002-05-18 00:35:13 +02:00
|
|
|
elog(ERROR, "%s types '%s' and '%s' not matched",
|
|
|
|
context, format_type_be(ptype), format_type_be(ntype));
|
2000-10-05 21:11:39 +02:00
|
|
|
}
|
|
|
|
else if (IsPreferredType(pcategory, ntype)
|
2000-12-17 05:32:29 +01:00
|
|
|
&& !IsPreferredType(pcategory, ptype)
|
2002-04-11 22:00:18 +02:00
|
|
|
&& can_coerce_type(1, &ptype, &ntype, false))
|
2000-10-05 21:11:39 +02:00
|
|
|
{
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* new one is preferred and can convert? then take it...
|
2000-10-05 21:11:39 +02:00
|
|
|
*/
|
|
|
|
ptype = ntype;
|
|
|
|
pcategory = TypeCategory(ptype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-11-09 05:14:32 +01:00
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* If all the inputs were UNKNOWN type --- ie, unknown-type literals
|
|
|
|
* --- then resolve as type TEXT. This situation comes up with
|
|
|
|
* constructs like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END);
|
|
|
|
* SELECT 'foo' UNION SELECT 'bar'; It might seem desirable to leave
|
|
|
|
* the construct's output type as UNKNOWN, but that really doesn't
|
|
|
|
* work, because we'd probably end up needing a runtime coercion from
|
|
|
|
* UNKNOWN to something else, and we usually won't have it. We need
|
|
|
|
* to coerce the unknown literals while they are still literals, so a
|
|
|
|
* decision has to be made now.
|
2000-11-09 05:14:32 +01:00
|
|
|
*/
|
|
|
|
if (ptype == UNKNOWNOID)
|
|
|
|
ptype = TEXTOID;
|
|
|
|
|
2000-10-05 21:11:39 +02:00
|
|
|
return ptype;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* coerce_to_common_type()
|
|
|
|
* Coerce an expression to the given type.
|
|
|
|
*
|
|
|
|
* This is used following select_common_type() to coerce the individual
|
|
|
|
* expressions to the desired type. 'context' is a phrase to use in the
|
|
|
|
* error message if we fail to coerce.
|
|
|
|
*
|
|
|
|
* NOTE: pstate may be NULL.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
coerce_to_common_type(ParseState *pstate, Node *node,
|
|
|
|
Oid targetTypeId,
|
|
|
|
const char *context)
|
|
|
|
{
|
|
|
|
Oid inputTypeId = exprType(node);
|
|
|
|
|
|
|
|
if (inputTypeId == targetTypeId)
|
|
|
|
return node; /* no work */
|
2002-04-11 22:00:18 +02:00
|
|
|
if (can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
|
|
|
node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
|
|
|
|
false);
|
2000-10-05 21:11:39 +02:00
|
|
|
else
|
|
|
|
{
|
2002-05-18 00:35:13 +02:00
|
|
|
elog(ERROR, "%s unable to convert to type %s",
|
|
|
|
context, format_type_be(targetTypeId));
|
2000-10-05 21:11:39 +02:00
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
/* TypeCategory()
|
|
|
|
* Assign a category to the specified OID.
|
2001-10-03 07:29:27 +02:00
|
|
|
* XXX This should be moved to system catalog lookups
|
|
|
|
* to allow for better type extensibility.
|
|
|
|
* - thomas 2001-09-30
|
1998-05-10 01:31:34 +02:00
|
|
|
*/
|
|
|
|
CATEGORY
|
|
|
|
TypeCategory(Oid inType)
|
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
CATEGORY result;
|
1998-05-10 01:31:34 +02:00
|
|
|
|
|
|
|
switch (inType)
|
|
|
|
{
|
|
|
|
case (BOOLOID):
|
|
|
|
result = BOOLEAN_TYPE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (CHAROID):
|
2000-03-12 00:19:50 +01:00
|
|
|
case (NAMEOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (BPCHAROID):
|
|
|
|
case (VARCHAROID):
|
|
|
|
case (TEXTOID):
|
|
|
|
result = STRING_TYPE;
|
|
|
|
break;
|
|
|
|
|
2001-05-22 18:37:17 +02:00
|
|
|
case (BITOID):
|
2000-04-08 21:29:40 +02:00
|
|
|
case (VARBITOID):
|
2000-11-17 20:57:48 +01:00
|
|
|
result = BITSTRING_TYPE;
|
2000-04-08 21:29:40 +02:00
|
|
|
break;
|
|
|
|
|
1998-08-14 18:07:00 +02:00
|
|
|
case (OIDOID):
|
2000-02-27 19:54:43 +01:00
|
|
|
case (REGPROCOID):
|
2002-04-25 04:56:56 +02:00
|
|
|
case (REGPROCEDUREOID):
|
|
|
|
case (REGOPEROID):
|
|
|
|
case (REGOPERATOROID):
|
|
|
|
case (REGCLASSOID):
|
|
|
|
case (REGTYPEOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (INT2OID):
|
|
|
|
case (INT4OID):
|
1998-07-08 16:04:11 +02:00
|
|
|
case (INT8OID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (FLOAT4OID):
|
|
|
|
case (FLOAT8OID):
|
Implement "date/time grand unification".
Transform datetime and timespan into timestamp and interval.
Deprecate datetime and timespan, though translate to new types in gram.y.
Transform all datetime and timespan catalog entries into new types.
Make "INTERVAL" reserved word allowed as a column identifier in gram.y.
Remove dt.h, dt.c files, and retarget datetime.h, datetime.c as utility
routines for all date/time types.
date.{h,c} now deals with date, time types.
timestamp.{h,c} now deals with timestamp, interval types.
nabstime.{h,c} now deals with abstime, reltime, tinterval types.
Make NUMERIC a known native type for purposes of type coersion. Not tested.
2000-02-16 18:26:26 +01:00
|
|
|
case (NUMERICOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (CASHOID):
|
|
|
|
result = NUMERIC_TYPE;
|
|
|
|
break;
|
|
|
|
|
2000-02-20 07:28:42 +01:00
|
|
|
case (DATEOID):
|
|
|
|
case (TIMEOID):
|
2000-03-15 00:06:59 +01:00
|
|
|
case (TIMETZOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (ABSTIMEOID):
|
|
|
|
case (TIMESTAMPOID):
|
2001-09-28 10:09:14 +02:00
|
|
|
case (TIMESTAMPTZOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
result = DATETIME_TYPE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (RELTIMEOID):
|
2000-02-20 07:28:42 +01:00
|
|
|
case (TINTERVALOID):
|
Implement "date/time grand unification".
Transform datetime and timespan into timestamp and interval.
Deprecate datetime and timespan, though translate to new types in gram.y.
Transform all datetime and timespan catalog entries into new types.
Make "INTERVAL" reserved word allowed as a column identifier in gram.y.
Remove dt.h, dt.c files, and retarget datetime.h, datetime.c as utility
routines for all date/time types.
date.{h,c} now deals with date, time types.
timestamp.{h,c} now deals with timestamp, interval types.
nabstime.{h,c} now deals with abstime, reltime, tinterval types.
Make NUMERIC a known native type for purposes of type coersion. Not tested.
2000-02-16 18:26:26 +01:00
|
|
|
case (INTERVALOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
result = TIMESPAN_TYPE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (POINTOID):
|
|
|
|
case (LSEGOID):
|
|
|
|
case (PATHOID):
|
2000-03-12 00:19:50 +01:00
|
|
|
case (BOXOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
case (POLYGONOID):
|
2000-03-12 00:19:50 +01:00
|
|
|
case (LINEOID):
|
|
|
|
case (CIRCLEOID):
|
1998-05-10 01:31:34 +02:00
|
|
|
result = GEOMETRIC_TYPE;
|
|
|
|
break;
|
|
|
|
|
1998-10-22 15:51:07 +02:00
|
|
|
case (INETOID):
|
|
|
|
case (CIDROID):
|
|
|
|
result = NETWORK_TYPE;
|
|
|
|
break;
|
|
|
|
|
2000-03-12 00:19:50 +01:00
|
|
|
case (UNKNOWNOID):
|
|
|
|
case (InvalidOid):
|
|
|
|
result = UNKNOWN_TYPE;
|
|
|
|
break;
|
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
default:
|
|
|
|
result = USER_TYPE;
|
|
|
|
break;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1998-09-01 06:40:42 +02:00
|
|
|
} /* TypeCategory() */
|
1998-05-10 01:31:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* IsPreferredType()
|
1998-12-04 16:34:49 +01:00
|
|
|
* Check if this type is a preferred type.
|
2001-10-03 07:29:27 +02:00
|
|
|
* XXX This should be moved to system catalog lookups
|
|
|
|
* to allow for better type extensibility.
|
|
|
|
* - thomas 2001-09-30
|
1998-05-10 01:31:34 +02:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
IsPreferredType(CATEGORY category, Oid type)
|
|
|
|
{
|
2001-10-03 07:29:27 +02:00
|
|
|
return (type == PreferredType(category, type));
|
1998-09-01 06:40:42 +02:00
|
|
|
} /* IsPreferredType() */
|
1998-05-10 01:31:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* PreferredType()
|
1998-05-29 16:00:24 +02:00
|
|
|
* Return the preferred type OID for the specified category.
|
2001-10-03 07:29:27 +02:00
|
|
|
* XXX This should be moved to system catalog lookups
|
|
|
|
* to allow for better type extensibility.
|
|
|
|
* - thomas 2001-09-30
|
1998-05-10 01:31:34 +02:00
|
|
|
*/
|
1998-10-08 20:30:52 +02:00
|
|
|
static Oid
|
1998-05-10 01:31:34 +02:00
|
|
|
PreferredType(CATEGORY category, Oid type)
|
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
Oid result;
|
1998-05-10 01:31:34 +02:00
|
|
|
|
|
|
|
switch (category)
|
|
|
|
{
|
|
|
|
case (BOOLEAN_TYPE):
|
|
|
|
result = BOOLOID;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (STRING_TYPE):
|
|
|
|
result = TEXTOID;
|
|
|
|
break;
|
|
|
|
|
2000-11-17 20:57:48 +01:00
|
|
|
case (BITSTRING_TYPE):
|
|
|
|
result = VARBITOID;
|
|
|
|
break;
|
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
case (NUMERIC_TYPE):
|
2002-09-01 04:27:32 +02:00
|
|
|
if (type == OIDOID ||
|
|
|
|
type == REGPROCOID ||
|
|
|
|
type == REGPROCEDUREOID ||
|
|
|
|
type == REGOPEROID ||
|
|
|
|
type == REGOPERATOROID ||
|
|
|
|
type == REGCLASSOID ||
|
|
|
|
type == REGTYPEOID)
|
1998-08-14 18:07:00 +02:00
|
|
|
result = OIDOID;
|
Implement "date/time grand unification".
Transform datetime and timespan into timestamp and interval.
Deprecate datetime and timespan, though translate to new types in gram.y.
Transform all datetime and timespan catalog entries into new types.
Make "INTERVAL" reserved word allowed as a column identifier in gram.y.
Remove dt.h, dt.c files, and retarget datetime.h, datetime.c as utility
routines for all date/time types.
date.{h,c} now deals with date, time types.
timestamp.{h,c} now deals with timestamp, interval types.
nabstime.{h,c} now deals with abstime, reltime, tinterval types.
Make NUMERIC a known native type for purposes of type coersion. Not tested.
2000-02-16 18:26:26 +01:00
|
|
|
else if (type == NUMERICOID)
|
|
|
|
result = NUMERICOID;
|
1998-08-14 18:07:00 +02:00
|
|
|
else
|
|
|
|
result = FLOAT8OID;
|
1998-05-10 01:31:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case (DATETIME_TYPE):
|
2001-09-28 10:09:14 +02:00
|
|
|
if (type == DATEOID)
|
|
|
|
result = TIMESTAMPOID;
|
|
|
|
else
|
|
|
|
result = TIMESTAMPTZOID;
|
1998-05-10 01:31:34 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case (TIMESPAN_TYPE):
|
Implement "date/time grand unification".
Transform datetime and timespan into timestamp and interval.
Deprecate datetime and timespan, though translate to new types in gram.y.
Transform all datetime and timespan catalog entries into new types.
Make "INTERVAL" reserved word allowed as a column identifier in gram.y.
Remove dt.h, dt.c files, and retarget datetime.h, datetime.c as utility
routines for all date/time types.
date.{h,c} now deals with date, time types.
timestamp.{h,c} now deals with timestamp, interval types.
nabstime.{h,c} now deals with abstime, reltime, tinterval types.
Make NUMERIC a known native type for purposes of type coersion. Not tested.
2000-02-16 18:26:26 +01:00
|
|
|
result = INTERVALOID;
|
1998-05-10 01:31:34 +02:00
|
|
|
break;
|
|
|
|
|
1998-10-22 15:51:07 +02:00
|
|
|
case (NETWORK_TYPE):
|
|
|
|
result = INETOID;
|
|
|
|
break;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-05-10 01:31:34 +02:00
|
|
|
case (GEOMETRIC_TYPE):
|
|
|
|
case (USER_TYPE):
|
|
|
|
result = type;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
result = UNKNOWNOID;
|
|
|
|
break;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1998-09-01 06:40:42 +02:00
|
|
|
} /* PreferredType() */
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
|
|
|
|
/* IsBinaryCompatible()
|
|
|
|
* Check if two types are binary-compatible.
|
2002-03-20 20:45:13 +01:00
|
|
|
*
|
2002-09-01 04:27:32 +02:00
|
|
|
* This notion allows us to cheat and directly exchange values without
|
|
|
|
* going through the trouble of calling a conversion function.
|
2002-03-20 20:45:13 +01:00
|
|
|
*
|
2002-09-01 04:27:32 +02:00
|
|
|
* As of 7.3, binary compatibility isn't hardwired into the code anymore.
|
|
|
|
* We consider two types binary-compatible if there is an implicit,
|
|
|
|
* no-function-needed pg_cast entry. NOTE that we assume that such
|
|
|
|
* entries are symmetric, ie, it doesn't matter which type we consider
|
|
|
|
* source and which target. (cf. checks in opr_sanity regression test)
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
IsBinaryCompatible(Oid type1, Oid type2)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_cast castForm;
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
/* Fast path if same type */
|
|
|
|
if (type1 == type2)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Perhaps the types are domains; if so, look at their base types */
|
|
|
|
if (OidIsValid(type1))
|
|
|
|
type1 = getBaseType(type1);
|
|
|
|
if (OidIsValid(type2))
|
|
|
|
type2 = getBaseType(type2);
|
|
|
|
|
|
|
|
/* Somewhat-fast path if same base type */
|
|
|
|
if (type1 == type2)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Else look in pg_cast */
|
|
|
|
tuple = SearchSysCache(CASTSOURCETARGET,
|
|
|
|
ObjectIdGetDatum(type1),
|
|
|
|
ObjectIdGetDatum(type2),
|
|
|
|
0, 0);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
return false; /* no cast */
|
|
|
|
castForm = (Form_pg_cast) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
result = (castForm->castfunc == InvalidOid) && castForm->castimplicit;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find_coercion_pathway
|
|
|
|
* Look for a coercion pathway between two types.
|
2002-03-20 20:45:13 +01:00
|
|
|
*
|
2002-09-01 04:27:32 +02:00
|
|
|
* If we find a matching entry in pg_cast, return TRUE, and set *funcid
|
|
|
|
* to the castfunc value (which may be InvalidOid for a binary-compatible
|
|
|
|
* coercion).
|
2002-03-19 03:18:25 +01:00
|
|
|
*/
|
2002-09-01 04:27:32 +02:00
|
|
|
static bool
|
|
|
|
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit,
|
|
|
|
Oid *funcid)
|
2002-07-19 01:11:32 +02:00
|
|
|
{
|
2002-09-01 04:27:32 +02:00
|
|
|
bool result = false;
|
2002-07-19 01:11:32 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
*funcid = InvalidOid;
|
|
|
|
|
|
|
|
/* Perhaps the types are domains; if so, look at their base types */
|
|
|
|
if (OidIsValid(sourceTypeId))
|
|
|
|
sourceTypeId = getBaseType(sourceTypeId);
|
|
|
|
if (OidIsValid(targetTypeId))
|
|
|
|
targetTypeId = getBaseType(targetTypeId);
|
|
|
|
|
|
|
|
/* Domains are automatically binary-compatible with their base type */
|
|
|
|
if (sourceTypeId == targetTypeId)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Else look in pg_cast */
|
2002-07-19 01:11:32 +02:00
|
|
|
tuple = SearchSysCache(CASTSOURCETARGET,
|
|
|
|
ObjectIdGetDatum(sourceTypeId),
|
|
|
|
ObjectIdGetDatum(targetTypeId),
|
|
|
|
0, 0);
|
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
{
|
2002-09-01 04:27:32 +02:00
|
|
|
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
|
2002-07-19 01:11:32 +02:00
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
if (isExplicit || castForm->castimplicit)
|
|
|
|
{
|
|
|
|
*funcid = castForm->castfunc;
|
|
|
|
result = true;
|
|
|
|
}
|
2002-07-19 01:11:32 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
return result;
|
2002-07-19 01:11:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-01 04:27:32 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* "bpchar" (ie, char(N)) and "numeric" are examples of such types.
|
|
|
|
*
|
|
|
|
* 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 ...
|
|
|
|
*/
|
2002-07-19 01:11:32 +02:00
|
|
|
static Oid
|
|
|
|
find_typmod_coercion_function(Oid typeId)
|
2002-03-19 03:18:25 +01:00
|
|
|
{
|
2002-04-11 22:00:18 +02:00
|
|
|
Oid funcid = InvalidOid;
|
2002-04-09 22:35:55 +02:00
|
|
|
Type targetType;
|
|
|
|
char *typname;
|
|
|
|
Oid typnamespace;
|
2002-03-20 20:45:13 +01:00
|
|
|
Oid oid_array[FUNC_MAX_ARGS];
|
|
|
|
HeapTuple ftup;
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-07-19 01:11:32 +02:00
|
|
|
targetType = typeidType(typeId);
|
2002-04-09 22:35:55 +02:00
|
|
|
typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname);
|
|
|
|
typnamespace = ((Form_pg_type) GETSTRUCT(targetType))->typnamespace;
|
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
2002-07-19 01:11:32 +02:00
|
|
|
oid_array[0] = typeId;
|
|
|
|
oid_array[1] = INT4OID;
|
2002-03-20 20:45:13 +01:00
|
|
|
|
2002-04-09 22:35:55 +02:00
|
|
|
ftup = SearchSysCache(PROCNAMENSP,
|
|
|
|
CStringGetDatum(typname),
|
2002-07-19 01:11:32 +02:00
|
|
|
Int16GetDatum(2),
|
2002-03-20 20:45:13 +01:00
|
|
|
PointerGetDatum(oid_array),
|
2002-04-09 22:35:55 +02:00
|
|
|
ObjectIdGetDatum(typnamespace));
|
2002-04-11 22:00:18 +02:00
|
|
|
if (HeapTupleIsValid(ftup))
|
2002-03-20 20:45:13 +01:00
|
|
|
{
|
2002-04-11 22:00:18 +02:00
|
|
|
Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
|
|
|
|
|
|
|
|
/* Make sure the function's result type is as expected */
|
2002-07-19 01:11:32 +02:00
|
|
|
if (pform->prorettype == typeId && !pform->proretset &&
|
2002-04-11 22:00:18 +02:00
|
|
|
!pform->proisagg)
|
|
|
|
{
|
2002-07-19 01:11:32 +02:00
|
|
|
/* Okay to use it */
|
2002-07-20 07:29:01 +02:00
|
|
|
funcid = HeapTupleGetOid(ftup);
|
2002-04-11 22:00:18 +02:00
|
|
|
}
|
2002-03-20 20:45:13 +01:00
|
|
|
ReleaseSysCache(ftup);
|
|
|
|
}
|
2002-04-11 22:00:18 +02:00
|
|
|
|
2002-04-09 22:35:55 +02:00
|
|
|
ReleaseSysCache(targetType);
|
2002-09-01 04:27:32 +02:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
return funcid;
|
|
|
|
}
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
/*
|
|
|
|
* Build an expression tree representing a function call.
|
|
|
|
*
|
|
|
|
* The argument expressions must have been transformed already.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
build_func_call(Oid funcid, Oid rettype, List *args)
|
|
|
|
{
|
|
|
|
Func *funcnode;
|
|
|
|
Expr *expr;
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
funcnode = makeNode(Func);
|
|
|
|
funcnode->funcid = funcid;
|
2002-05-13 01:43:04 +02:00
|
|
|
funcnode->funcresulttype = rettype;
|
|
|
|
funcnode->funcretset = false; /* only possible case here */
|
2002-03-20 20:45:13 +01:00
|
|
|
funcnode->func_fcache = NULL;
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
expr = makeNode(Expr);
|
|
|
|
expr->typeOid = rettype;
|
|
|
|
expr->opType = FUNC_EXPR;
|
|
|
|
expr->oper = (Node *) funcnode;
|
|
|
|
expr->args = args;
|
2002-03-19 03:18:25 +01:00
|
|
|
|
2002-03-20 20:45:13 +01:00
|
|
|
return (Node *) expr;
|
2002-03-19 03:18:25 +01:00
|
|
|
}
|