parse_coerce.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,605 行 · 第 1/3 页

C
1,605
字号
/*------------------------------------------------------------------------- * * parse_coerce.c *		handle type coercions/conversions for parser * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.111.2.1 2003/12/17 19:49:52 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_cast.h"#include "catalog/pg_proc.h"#include "nodes/makefuncs.h"#include "nodes/params.h"#include "optimizer/clauses.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_func.h"#include "parser/parse_type.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/syscache.h"static Node *coerce_type_typmod(Node *node,				   Oid targetTypeId, int32 targetTypMod,				   CoercionForm cformat, bool isExplicit);/* * coerce_to_target_type() *		Convert an expression to a target type and typmod. * * This is the general-purpose entry point for arbitrary type coercion * operations.	Direct use of the component operations can_coerce_type, * coerce_type, and coerce_type_typmod should be restricted to special * cases (eg, when the conversion is expected to succeed). * * Returns the possibly-transformed expression tree, or NULL if the type * conversion is not possible.	(We do this, rather than ereport'ing directly, * so that callers can generate custom error messages indicating context.) * * pstate - parse state (can be NULL, see coerce_type) * expr - input expression tree (already transformed by transformExpr) * exprtype - result type of expr * targettype - desired result type * targettypmod - desired result typmod * ccontext, cformat - context indicators to control coercions */Node *coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,					  Oid targettype, int32 targettypmod,					  CoercionContext ccontext,					  CoercionForm cformat){	if (can_coerce_type(1, &exprtype, &targettype, ccontext))		expr = coerce_type(pstate, expr, exprtype, targettype,						   ccontext, cformat);	else if (ccontext >= COERCION_ASSIGNMENT)	{		/*		 * String hacks to get transparent conversions for char and		 * varchar: if a coercion to text is available, use it for forced		 * coercions to char(n) or varchar(n) or domains thereof.		 *		 * This is pretty grotty, but seems easier to maintain than providing		 * entries in pg_cast that parallel all the ones for text.		 */		Oid			targetbasetype = getBaseType(targettype);		if (targetbasetype == BPCHAROID || targetbasetype == VARCHAROID)		{			Oid			text_id = TEXTOID;			if (can_coerce_type(1, &exprtype, &text_id, ccontext))			{				expr = coerce_type(pstate, expr, exprtype, text_id,								   ccontext, cformat);				if (targetbasetype != targettype)				{					/* need to coerce to domain over char or varchar */					expr = coerce_to_domain(expr, targetbasetype, targettype,											cformat);				}				else				{					/*					 * need a RelabelType if no typmod coercion will be					 * performed					 */					if (targettypmod < 0)						expr = (Node *) makeRelabelType((Expr *) expr,														targettype, -1,														cformat);				}			}			else				expr = NULL;		}		else			expr = NULL;	}	else		expr = NULL;	/*	 * If the target is a fixed-length type, it may need a length coercion	 * as well as a type coercion.	 */	if (expr != NULL)		expr = coerce_type_typmod(expr, targettype, targettypmod,								  cformat,								  (cformat != COERCE_IMPLICIT_CAST));	return expr;}/* * coerce_type() *		Convert an expression to a different type. * * 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. * (But if the target type is a domain, it may internally contain a * typmod constraint, which will be applied inside coerce_to_domain.) * * 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 * caller does not want type information updated for Params. */Node *coerce_type(ParseState *pstate, Node *node,			Oid inputTypeId, Oid targetTypeId,			CoercionContext ccontext, CoercionForm cformat){	Node	   *result;	Oid			funcId;	if (targetTypeId == inputTypeId ||		node == NULL)	{		/* no conversion needed */		return node;	}	if (targetTypeId == ANYOID ||		targetTypeId == ANYARRAYOID ||		targetTypeId == ANYELEMENTOID)	{		/* assume can_coerce_type verified that implicit coercion is okay */		/* NB: we do NOT want a RelabelType here */		return node;	}	if (inputTypeId == UNKNOWNOID && IsA(node, Const))	{		/*		 * Input is a string constant with previously undetermined type.		 * Apply the target type's typinput function to it to produce a		 * constant of the target type.		 *		 * NOTE: this case cannot be folded together with the other		 * constant-input case, since the typinput function does not		 * 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.		 *		 * XXX if the typinput function is not immutable, 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. (XXX		 * This *is* possible as of 7.3, do we want to do it?)		 */		Const	   *con = (Const *) node;		Const	   *newcon = makeNode(Const);		Type		targetType = typeidType(targetTypeId);		char		targetTyptype = typeTypType(targetType);		newcon->consttype = targetTypeId;		newcon->constlen = typeLen(targetType);		newcon->constbyval = typeByVal(targetType);		newcon->constisnull = con->constisnull;		if (!con->constisnull)		{			char	   *val = DatumGetCString(DirectFunctionCall1(unknownout,													   con->constvalue));			/*			 * We pass typmod -1 to the input routine, primarily because			 * existing input routines follow implicit-coercion semantics			 * for length checks, which is not always what we want here.			 * Any length constraint will be applied later by our caller.			 *			 * Note that we call stringTypeDatum using the domain's pg_type			 * row, if it's a domain.  This works because the domain row			 * has the same typinput and typelem as the base type ---			 * ugly...			 */			newcon->constvalue = stringTypeDatum(targetType, val, -1);			pfree(val);		}		result = (Node *) newcon;		/* If target is a domain, apply constraints. */		if (targetTyptype == 'd')			result = coerce_to_domain(result, InvalidOid, targetTypeId,									  cformat);		ReleaseSysCache(targetType);		return result;	}	if (inputTypeId == UNKNOWNOID && IsA(node, Param) &&		((Param *) node)->paramkind == PARAM_NUM &&		pstate != NULL && pstate->p_variableparams)	{		/*		 * Input is a Param of previously undetermined type, and we want		 * to update our knowledge of the Param's type.  Find the topmost		 * ParseState and update the state.		 */		Param	   *param = (Param *) node;		int			paramno = param->paramid;		ParseState *toppstate;		toppstate = pstate;		while (toppstate->parentParseState != NULL)			toppstate = toppstate->parentParseState;		if (paramno <= 0 ||		/* shouldn't happen, but... */			paramno > toppstate->p_numparams)			ereport(ERROR,					(errcode(ERRCODE_UNDEFINED_PARAMETER),					 errmsg("there is no parameter $%d", paramno)));		if (toppstate->p_paramtypes[paramno - 1] == UNKNOWNOID)		{			/* We've successfully resolved the type */			toppstate->p_paramtypes[paramno - 1] = targetTypeId;		}		else if (toppstate->p_paramtypes[paramno - 1] == targetTypeId)		{			/* We previously resolved the type, and it matches */		}		else		{			/* Ooops */			ereport(ERROR,					(errcode(ERRCODE_AMBIGUOUS_PARAMETER),				   errmsg("inconsistent types deduced for parameter $%d",						  paramno),					 errdetail("%s versus %s",					format_type_be(toppstate->p_paramtypes[paramno - 1]),							   format_type_be(targetTypeId))));		}		param->paramtype = targetTypeId;		return (Node *) param;	}	if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,							  &funcId))	{		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 = (Node *) makeFuncExpr(funcId, baseTypeId,										   makeList1(node),										   cformat);			/*			 * If domain, coerce to the domain type and relabel with			 * domain type ID			 */			if (targetTypeId != baseTypeId)				result = coerce_to_domain(result, baseTypeId, targetTypeId,										  cformat);		}		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.	If the destination is a domain			 * then we won't need a RelabelType node.			 */			result = coerce_to_domain(node, InvalidOid, targetTypeId,									  cformat);			if (result == node)			{				/*				 * 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.				 */				result = (Node *) makeRelabelType((Expr *) result,												  targetTypeId, -1,												  cformat);			}		}		return result;	}	if (typeInheritsFrom(inputTypeId, targetTypeId))	{		/*		 * Input class type is a subclass of target, so nothing to do ---		 * except relabel the type.  This is binary compatibility for		 * complex types.		 */		return (Node *) makeRelabelType((Expr *) node,										targetTypeId, -1,										cformat);	}	/* If we get here, caller blew it */	elog(ERROR, "failed to find conversion function from %s to %s",		 format_type_be(inputTypeId), format_type_be(targetTypeId));	return NULL;				/* keep compiler quiet */}/* * can_coerce_type() *		Can input_typeids be coerced to target_typeids? * * We must be told the context (CAST construct, assignment, implicit coercion) * as this determines the set of available casts. */boolcan_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,				CoercionContext ccontext){	bool		have_generics = false;	int			i;	/* run through argument list... */	for (i = 0; i < nargs; i++)	{		Oid			inputTypeId = input_typeids[i];		Oid			targetTypeId = target_typeids[i];		Oid			funcId;		/* no problem if same type */		if (inputTypeId == targetTypeId)			continue;		/* don't choke on references to no-longer-existing types */		if (!typeidIsValid(inputTypeId))			return false;		if (!typeidIsValid(targetTypeId))			return false;		/* accept if target is ANY */		if (targetTypeId == ANYOID)			continue;		/* accept if target is ANYARRAY or ANYELEMENT, for now */		if (targetTypeId == ANYARRAYOID ||			targetTypeId == ANYELEMENTOID)		{			have_generics = true;		/* do more checking later */			continue;		}		/*		 * If input is an untyped string constant, assume we can convert		 * it to anything except a class type.		 */		if (inputTypeId == UNKNOWNOID)		{			if (ISCOMPLEX(targetTypeId))				return false;			continue;		}		/*		 * If pg_cast shows that we can coerce, accept.  This test now		 * covers both binary-compatible and coercion-function cases.		 */		if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,								  &funcId))			continue;		/*		 * If input is a class type that inherits from target, accept		 */		if (typeInheritsFrom(inputTypeId, targetTypeId))			continue;		/*		 * Else, cannot coerce at this argument position		 */		return false;	}	/* If we found any generic argument types, cross-check them */	if (have_generics)	{		if (!check_generic_type_consistency(input_typeids, target_typeids,											nargs))			return false;	}	return true;}/* * Create an expression tree to represent coercion to a domain type. * * 'arg': input expression * 'baseTypeId': base type of domain, if known (pass InvalidOid if caller *		has not bothered to look this up) * 'typeId': target type to coerce to * 'cformat': coercion format * * 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){	CoerceToDomain *result;	int32		typmod;	/* Get the base type if it hasn't been supplied */	if (baseTypeId == InvalidOid)		baseTypeId = getBaseType(typeId);	/* If it isn't a domain, return the node as it was passed in */	if (baseTypeId == typeId)		return arg;	/*	 * If the domain applies a typmod to its base type, build the	 * appropriate coercion step.  Mark it implicit for display purposes,	 * because we don't want it shown separately by ruleutils.c; but the	 * isExplicit flag passed to the conversion function depends on the	 * manner in which the domain coercion is invoked, so that the	 * semantics of implicit and explicit coercion differ.	(Is that	 * really the behavior we want?)	 *	 * NOTE: because we apply this as part of the fixed expression structure,	 * ALTER DOMAIN cannot alter the typtypmod.  But it's unclear that	 * that would be safe to do anyway, without lots of knowledge about	 * what the base type thinks the typmod means.	 */	typmod = get_typtypmod(typeId);	if (typmod >= 0)		arg = coerce_type_typmod(arg, baseTypeId, typmod,								 COERCE_IMPLICIT_CAST,								 (cformat != COERCE_IMPLICIT_CAST));	/*	 * Now build the domain coercion node.	This represents run-time	 * checking of any constraints currently attached to the domain.  This	 * also ensures that the expression is properly labeled as to result	 * type.	 */	result = makeNode(CoerceToDomain);	result->arg = (Expr *) arg;	result->resulttype = typeId;	result->resulttypmod = -1;	/* currently, always -1 for domains */	result->coercionformat = cformat;	return (Node *) result;}/* * 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. * * 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){	Oid			funcId;	int			nargs;	/*	 * A negative typmod is assumed to mean that no coercion is wanted.	 */	if (targetTypMod < 0 || targetTypMod == exprTypmod(node))		return node;	funcId = find_typmod_coercion_function(targetTypeId, &nargs);	if (OidIsValid(funcId))	{		List	   *args;		Const	   *cons;		/* Pass given value, plus target typmod as an int4 constant */		cons = makeConst(INT4OID,						 sizeof(int32),						 Int32GetDatum(targetTypMod),						 false,						 true);		args = makeList2(node, cons);		if (nargs == 3)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?