Skip to content
Snippets Groups Projects
parse_oper.c 19.67 KiB
/*-------------------------------------------------------------------------
 *
 * parse_oper.h
 *		handle operator things for parser
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.11 1998/05/09 23:29:53 thomas Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>

#include "postgres.h"

#include "access/heapam.h"
#include "access/relscan.h"
#include "catalog/catname.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "parser/parse_coerce.h"
#include "storage/bufmgr.h"
#include "utils/syscache.h"

extern
Oid *
func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);

extern
Oid *
oper_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);

static int
binary_oper_get_candidates(char *opname,
						   Oid leftTypeId,
						   Oid rightTypeId,
						   CandidateList *candidates);
static CandidateList
binary_oper_select_candidate(Oid arg1,
							 Oid arg2,
							 CandidateList candidates);
static bool equivalentOpersAfterPromotion(CandidateList candidates);
static void op_error(char *op, Oid arg1, Oid arg2);
static int
unary_oper_get_candidates(char *op,
						  Oid typeId,
						  CandidateList *candidates,
						  char rightleft);


Oid
any_ordering_op(int restype)
{
	Operator	order_op;
	Oid			order_opid;

	order_op = oper("<", restype, restype, false);
	order_opid = oprid(order_op);

	return order_opid;
}

/* given operator, return the operator OID */
Oid
oprid(Operator op)
{
	return (op->t_oid);
}


/* binary_oper_get_candidates()
 *	given opname, leftTypeId and rightTypeId,
 *	find all possible (arg1, arg2) pairs for which an operator named
 *	opname exists, such that leftTypeId can be coerced to arg1 and
 *	rightTypeId can be coerced to arg2
 */
static int
binary_oper_get_candidates(char *opname,
						   Oid leftTypeId,
						   Oid rightTypeId,
						   CandidateList *candidates)
{
	CandidateList current_candidate;
	Relation	pg_operator_desc;
	HeapScanDesc pg_operator_scan;
	HeapTuple	tup;
	OperatorTupleForm oper;
	Buffer		buffer;
	int			nkeys;
	int			ncandidates = 0;
	ScanKeyData opKey[3];

	*candidates = NULL;

	ScanKeyEntryInitialize(&opKey[0], 0,
						   Anum_pg_operator_oprname,
						   F_NAMEEQ,
						   NameGetDatum(opname));

	ScanKeyEntryInitialize(&opKey[1], 0,
						   Anum_pg_operator_oprkind,
						   F_CHAREQ,
						   CharGetDatum('b'));

#if FALSE
	if (leftTypeId == UNKNOWNOID)
	{
		if (rightTypeId == UNKNOWNOID)
		{
			nkeys = 2;
		}
		else
		{
			nkeys = 3;

			ScanKeyEntryInitialize(&opKey[2], 0,
								   Anum_pg_operator_oprright,
								   ObjectIdEqualRegProcedure,
								   ObjectIdGetDatum(rightTypeId));
		}
	}
	else if (rightTypeId == UNKNOWNOID)
	{
		nkeys = 3;

		ScanKeyEntryInitialize(&opKey[2], 0,
							   Anum_pg_operator_oprleft,
							   ObjectIdEqualRegProcedure,
							   ObjectIdGetDatum(leftTypeId));
	}
	else
	{
		/* currently only "unknown" can be coerced */
		return 0;
#endif

	nkeys = 2;

	pg_operator_desc = heap_openr(OperatorRelationName);
	pg_operator_scan = heap_beginscan(pg_operator_desc,
									  0,
									  true,
									  nkeys,
									  opKey);

	do
	{
		tup = heap_getnext(pg_operator_scan, 0, &buffer);
		if (HeapTupleIsValid(tup))
		{
			current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
			current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));

			oper = (OperatorTupleForm) GETSTRUCT(tup);
			current_candidate->args[0] = oper->oprleft;
			current_candidate->args[1] = oper->oprright;
			current_candidate->next = *candidates;
			*candidates = current_candidate;
			ncandidates++;
			ReleaseBuffer(buffer);
		}
	} while (HeapTupleIsValid(tup));

	heap_endscan(pg_operator_scan);
	heap_close(pg_operator_desc);

	return ncandidates;
} /* binary_oper_get_candidates() */


#if FALSE
/* BinaryOperCandidates()
 *	Given opname, leftTypeId and rightTypeId,
 *	find all possible (arg1, arg2) pairs for which an operator named
 *	opname exists, such that leftTypeId can be coerced to arg1 and
 *	rightTypeId can be coerced to arg2.
 */
static int
BinaryOperCandidates(char *opname,
					 Oid lTypeId,
					 Oid rTypeId,
					 CandidateList *candidates)
{
	CandidateList current_candidate;
	Relation	pg_operator_desc;
	HeapScanDesc pg_operator_scan;
	HeapTuple	tup;
	OperatorTupleForm oper;
	Buffer		buffer;
	int			nkeys;
	int			ncandidates = 0;
	ScanKeyData opKey[3];

	/* Can we promote the lesser type and find a match? */
	lCandidateTypeId = lTypeId;
	rCandidateTypeId = rTypeId;
	higherTypeId = PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
	if (lTypeId != higherTypeId)
		lowerTypeId = lTypeId;
	else
		lowerTypeId = rTypeId;

	while (lCandidateTypeId != rCandidateTypeId)
		if ((lCandidateTypeId == InvalidOid) || (rCandidateTypeId == InvalidOid))
			break;

		tup = SearchSysCacheTuple(OPRNAME,
								  PointerGetDatum(op),
								  ObjectIdGetDatum(lCandidateTypeId),
								  ObjectIdGetDatum(rCandidateTypeId),
								  Int8GetDatum('b'));
		if (HeapTupleIsValid(tup))
			return ((Operator) tup);

		PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
	}

	/* Can we promote the lesser type directly to the other? */
	if (can_coerce_type(lowerTypeId, higherTypeId))
	{
		tup = SearchSysCacheTuple(OPRNAME,
								  PointerGetDatum(op),
								  ObjectIdGetDatum(higherTypeId),
								  ObjectIdGetDatum(higherTypeId),
								  Int8GetDatum('b'));
		if (HeapTupleIsValid(tup))
			return ((Operator) tup);
	}


	*candidates = NULL;

	ScanKeyEntryInitialize(&opKey[0], 0,
						   Anum_pg_operator_oprname,
						   NameEqualRegProcedure,
						   NameGetDatum(opname));

	ScanKeyEntryInitialize(&opKey[1], 0,
						   Anum_pg_operator_oprkind,
						   CharacterEqualRegProcedure,
						   CharGetDatum('b'));

#if FALSE
	if (leftTypeId == UNKNOWNOID)
	{
		if (rightTypeId == UNKNOWNOID)
		{
			nkeys = 2;
		}
		else
		{
			nkeys = 3;

			ScanKeyEntryInitialize(&opKey[2], 0,
								   Anum_pg_operator_oprright,
								   F_OIDEQ,
								   ObjectIdGetDatum(rightTypeId));
		}
	}
	else if (rightTypeId == UNKNOWNOID)
	{
		nkeys = 3;

		ScanKeyEntryInitialize(&opKey[2], 0,
							   Anum_pg_operator_oprleft,
							   F_OIDEQ,
							   ObjectIdGetDatum(leftTypeId));
	}
	else
	{
		/* currently only "unknown" can be coerced */
		return 0;
#endif

	nkeys = 2;

	pg_operator_desc = heap_openr(OperatorRelationName);
	pg_operator_scan = heap_beginscan(pg_operator_desc,
									  0,
									  true,
									  nkeys,
									  opKey);

	do
	{
		tup = heap_getnext(pg_operator_scan, 0, &buffer);
		if (HeapTupleIsValid(tup))
		{
			current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
			current_candidate->args = (Oid *) palloc(2 * sizeof(Oid));

			oper = (OperatorTupleForm) GETSTRUCT(tup);
			current_candidate->args[0] = oper->oprleft;
			current_candidate->args[1] = oper->oprright;
			current_candidate->next = *candidates;
			*candidates = current_candidate;
			ncandidates++;
			ReleaseBuffer(buffer);
		}
	} while (HeapTupleIsValid(tup));

	heap_endscan(pg_operator_scan);
	heap_close(pg_operator_desc);

	return ncandidates;
} /* BinaryOperCandidates() */
#endif


/*
 * equivalentOpersAfterPromotion -
 *	  checks if a list of candidate operators obtained from
 *	  binary_oper_get_candidates() contain equivalent operators. If
 *	  this routine is called, we have more than 1 candidate and need to
 *	  decided whether to pick one of them. This routine returns true if
 *	  all the candidates operate on the same data types after
 *	  promotion (int2, int4, float4 -> float8).
 */
static bool
equivalentOpersAfterPromotion(CandidateList candidates)
{
	CandidateList result;
	CandidateList promotedCandidates = NULL;
	Oid			leftarg,
				rightarg;

	for (result = candidates; result != NULL; result = result->next)
	{
		CandidateList c;

		c = (CandidateList) palloc(sizeof(*c));
		c->args = (Oid *) palloc(2 * sizeof(Oid));
		switch (result->args[0])
		{
			case FLOAT4OID:
			case INT4OID:
			case INT2OID:
			case CASHOID:
				c->args[0] = FLOAT8OID;
				break;
			default:
				c->args[0] = result->args[0];
				break;
		}
		switch (result->args[1])
		{
			case FLOAT4OID:
			case INT4OID:
			case INT2OID:
			case CASHOID:
				c->args[1] = FLOAT8OID;
				break;
			default:
				c->args[1] = result->args[1];
				break;
		}
		c->next = promotedCandidates;
		promotedCandidates = c;
	}

	/*
	 * if we get called, we have more than 1 candidates so we can do the
	 * following safely
	 */
	leftarg = promotedCandidates->args[0];
	rightarg = promotedCandidates->args[1];

	for (result = promotedCandidates->next; result != NULL; result = result->next)
	{
		if (result->args[0] != leftarg || result->args[1] != rightarg)

			/*
			 * this list contains operators that operate on different data
			 * types even after promotion. Hence we can't decide on which
			 * one to pick. The user must do explicit type casting.
			 */
			return FALSE;
	}

	/*
	 * all the candidates are equivalent in the following sense: they
	 * operate on equivalent data types and picking any one of them is as
	 * good.
	 */
	return TRUE;
}


/* binary_oper_select_candidate()
 * Given a choice of argument type pairs for a binary operator,
 *  try to choose a default pair.
 *
 * current wisdom holds that the default operator should be one in which
 * both operands have the same type (there will only be one such
 * operator)
 *
 * 7.27.93 - I have decided not to do this; it's too hard to justify, and
 * it's easy enough to typecast explicitly - avi
 * [the rest of this routine was commented out since then - ay]
 *
 * 6/23/95 - I don't complete agree with avi. In particular, casting
 * floats is a pain for users. Whatever the rationale behind not doing
 * this is, I need the following special case to work.
 *
 * In the WHERE clause of a query, if a float is specified without
 * quotes, we treat it as float8. I added the float48* operators so
 * that we can operate on float4 and float8. But now we have more than
 * one matching operator if the right arg is unknown (eg. float
 * specified with quotes). This break some stuff in the regression
 * test where there are floats in quotes not properly casted. Below is
 * the solution. In addition to requiring the operator operates on the
 * same type for both operands [as in the code Avi originally
 * commented out], we also require that the operators be equivalent in
 * some sense. (see equivalentOpersAfterPromotion for details.)
 * - ay 6/95
 */
static CandidateList
binary_oper_select_candidate(Oid arg1,
							 Oid arg2,
							 CandidateList candidates)
{
	CandidateList result;

	/*
	 * If both are "unknown", there is no way to select a candidate
	 */
	if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
		return (NULL);

	if (!equivalentOpersAfterPromotion(candidates))
		return NULL;

	/*
	 * if we get here, any one will do but we're more picky and require
	 * both operands be the same.
	 */
	for (result = candidates; result != NULL; result = result->next)
	{
		if (result->args[0] == result->args[1])
			return result;
	}

	return (NULL);
}

/* oper()
 * Given operator, types of arg1, and arg2, return oper struct.
 * Inputs:
 * arg1, arg2: Type IDs
 */
Operator
oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
{
	HeapTuple		tup;
	CandidateList	candidates;
	int				ncandidates;
	Oid			   *targetOids;
	Oid				inputOids[2];

	/* Unspecified type for one of the arguments? then use the other */
	if (arg2 == InvalidOid)
		arg2 = arg1;
	if (arg1 == InvalidOid)
		arg1 = arg2;

	tup = SearchSysCacheTuple(OPRNAME,
							  PointerGetDatum(op),
							  ObjectIdGetDatum(arg1),
							  ObjectIdGetDatum(arg2),
							  Int8GetDatum('b'));

	/* Did not find anything? then look more carefully... */
	if (!HeapTupleIsValid(tup))
	{
		ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);

		/* No operators found? Then throw error or return null... */
		if (ncandidates == 0)
		{
			if (!noWarnings)
				op_error(op, arg1, arg2);
			return (NULL);
		}

		/* Or found exactly one? Then proceed... */
		else if (ncandidates == 1)
		{
			tup = SearchSysCacheTuple(OPRNAME,
									  PointerGetDatum(op),
									  ObjectIdGetDatum(candidates->args[0]),
									  ObjectIdGetDatum(candidates->args[1]),
									  Int8GetDatum('b'));
			Assert(HeapTupleIsValid(tup));
		}

		/* Otherwise, multiple operators of the desired types found... */
		else
		{
#if FALSE
			candidates = binary_oper_select_candidate(arg1, arg2, candidates);
#endif
			inputOids[0] = arg1;
			inputOids[1] = arg2;
			targetOids = oper_select_candidate(2, inputOids, candidates);
#if FALSE
			targetOids = func_select_candidate(2, inputOids, candidates);
#endif
			if (targetOids != NULL)
			{
#if PARSEDEBUG
printf("oper: found candidate\n");
#endif
				tup = SearchSysCacheTuple(OPRNAME,
										  PointerGetDatum(op),
										  ObjectIdGetDatum(targetOids[0]),
										  ObjectIdGetDatum(targetOids[1]),
										  Int8GetDatum('b'));
			}
			else
			{
				tup = NULL;
			}

			/* Could not choose one, for whatever reason... */
			if (!HeapTupleIsValid(tup))
			{
				if (!noWarnings)
				{
					elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'"
						"\n\tYou will have to retype this query using an explicit cast",
						op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
				}
				return (NULL);
			}
		}
	}
	return ((Operator) tup);
} /* oper() */


/* unary_oper_get_candidates()
 *	given opname and typeId, find all possible types for which
 *	a right/left unary operator named opname exists,
 *	such that typeId can be coerced to it
 */
static int
unary_oper_get_candidates(char *op,
						  Oid typeId,
						  CandidateList *candidates,
						  char rightleft)
{
	CandidateList current_candidate;
	Relation	pg_operator_desc;
	HeapScanDesc pg_operator_scan;
	HeapTuple	tup;
	OperatorTupleForm oper;
	Buffer		buffer;
	int			ncandidates = 0;

	static ScanKeyData opKey[2] = {
		{0, Anum_pg_operator_oprname, F_NAMEEQ},
	{0, Anum_pg_operator_oprkind, F_CHAREQ}};

	*candidates = NULL;

	fmgr_info(F_NAMEEQ, (FmgrInfo *) &opKey[0].sk_func);
	opKey[0].sk_argument = NameGetDatum(op);
	fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
	opKey[1].sk_argument = CharGetDatum(rightleft);

#if FALSE
	/* currently, only "unknown" can be coerced */

	/*
	 * but we should allow types that are internally the same to be
	 * "coerced"
	 */
	if (typeId != UNKNOWNOID)
	{
		return 0;
	}
#endif

#ifdef PARSEDEBUG
printf("unary_oper_get_candidates: start scan for '%s'\n", op);
#endif
	pg_operator_desc = heap_openr(OperatorRelationName);
	pg_operator_scan = heap_beginscan(pg_operator_desc,
									  0,
									  true,
									  2,
									  opKey);

	do
	{
		tup = heap_getnext(pg_operator_scan, 0, &buffer);
		if (HeapTupleIsValid(tup))
		{
			current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList));
			current_candidate->args = (Oid *) palloc(sizeof(Oid));

			oper = (OperatorTupleForm) GETSTRUCT(tup);
			if (rightleft == 'r')
				current_candidate->args[0] = oper->oprleft;
			else
				current_candidate->args[0] = oper->oprright;
			current_candidate->next = *candidates;
			*candidates = current_candidate;
#ifdef PARSEDEBUG
printf("unary_oper_get_candidates: found candidate '%s' for type %s\n",
 op, typeidTypeName(current_candidate->args[0]));
#endif
			ncandidates++;
			ReleaseBuffer(buffer);
		}
	} while (HeapTupleIsValid(tup));

	heap_endscan(pg_operator_scan);
	heap_close(pg_operator_desc);

#ifdef PARSEDEBUG
printf("unary_oper_get_candidates: found %d candidates\n", ncandidates);
#endif
	return ncandidates;
} /* unary_oper_get_candidates() */


/* Given unary right-side operator (operator on right), return oper struct */
/* arg-- type id */
Operator
right_oper(char *op, Oid arg)
{
	HeapTuple		tup;
	CandidateList	candidates;
	int				ncandidates;
	Oid			   *targetOid;

	tup = SearchSysCacheTuple(OPRNAME,
							  PointerGetDatum(op),
							  ObjectIdGetDatum(arg),
							  ObjectIdGetDatum(InvalidOid),
							  Int8GetDatum('r'));

	if (!HeapTupleIsValid(tup))
	{
		ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
		if (ncandidates == 0)
		{
			elog(ERROR, "Can't find right op '%s' for type %d", op, arg);
			return (NULL);
		}
		else if (ncandidates == 1)
		{
			tup = SearchSysCacheTuple(OPRNAME,
									  PointerGetDatum(op),
								   ObjectIdGetDatum(candidates->args[0]),
									  ObjectIdGetDatum(InvalidOid),
									  Int8GetDatum('r'));
			Assert(HeapTupleIsValid(tup));
		}
		else
		{
#if FALSE
			elog(ERROR, "There is more than one right operator %s"
				"\n\tYou will have to retype this query using an explicit cast", op);
#endif
			targetOid = func_select_candidate(1, &arg, candidates);

			if (targetOid != NULL)
			{
				tup = SearchSysCacheTuple(OPRNAME,
										  PointerGetDatum(op),
										  ObjectIdGetDatum(InvalidOid),
										  ObjectIdGetDatum(*targetOid),
										  Int8GetDatum('r'));
			}
			else
			{
				tup = NULL;
			}

			if (!HeapTupleIsValid(tup))
			{
				elog(ERROR, "Unable to convert right operator '%s' from type %s to %s",
					 op, typeidTypeName(arg), typeidTypeName(*targetOid));
				return (NULL);
			}
		}
	}
	return ((Operator) tup);
} /* right_oper() */


/* Given unary left-side operator (operator on left), return oper struct */
/* arg--type id */
Operator
left_oper(char *op, Oid arg)
{
	HeapTuple		tup;
	CandidateList	candidates;
	int				ncandidates;
	Oid			   *targetOid;

	tup = SearchSysCacheTuple(OPRNAME,
							  PointerGetDatum(op),
							  ObjectIdGetDatum(InvalidOid),
							  ObjectIdGetDatum(arg),
							  Int8GetDatum('l'));

	if (!HeapTupleIsValid(tup))
	{
		ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
		if (ncandidates == 0)
		{
			elog(ERROR, "Can't find left op '%s' for type %d", op, arg);
			return (NULL);
		}
		else if (ncandidates == 1)
		{
			tup = SearchSysCacheTuple(OPRNAME,
									  PointerGetDatum(op),
									  ObjectIdGetDatum(InvalidOid),
									  ObjectIdGetDatum(candidates->args[0]),
									  Int8GetDatum('l'));
			Assert(HeapTupleIsValid(tup));
#ifdef PARSEDEBUG
printf("left_oper: searched cache for single left oper candidate '%s %s'\n",
 op, typeidTypeName((Oid) candidates->args[0]));
#endif
		}
		else
		{
#if FALSE
			elog(ERROR, "There is more than one left operator %s"
				"\n\tYou will have to retype this query using an explicit cast", op);
#endif
			targetOid = func_select_candidate(1, &arg, candidates);
			tup = SearchSysCacheTuple(OPRNAME,
									  PointerGetDatum(op),
									  ObjectIdGetDatum(InvalidOid),
									  ObjectIdGetDatum(*targetOid),
									  Int8GetDatum('l'));

			if (!HeapTupleIsValid(tup))
			{
				elog(ERROR, "Unable to convert left operator '%s' from type %s to %s",
					 op, typeidTypeName(arg), typeidTypeName(*targetOid));
				return (NULL);
			}
#ifdef PARSEDEBUG
printf("left_oper: searched cache for best left oper candidate '%s %s'\n",
 op, typeidTypeName(*targetOid));
#endif
		}
	}
	return ((Operator) tup);
} /* left_oper() */


/* op_error()
 * Give a somewhat useful error message when the operator for two types
 * is not found.
 */
static void
op_error(char *op, Oid arg1, Oid arg2)
{
	Type		tp1 = NULL,
				tp2 = NULL;

	if (typeidIsValid(arg1))
	{
		tp1 = typeidType(arg1);
	}
	else
	{
		elog(ERROR, "Left hand side of operator '%s' has an unknown type"
			"\n\tProbably a bad attribute name", op);
	}

	if (typeidIsValid(arg2))
	{
		tp2 = typeidType(arg2);
	}
	else
	{
		elog(ERROR, "Right hand side of operator %s has an unknown type"
			"\n\tProbably a bad attribute name", op);
	}

	elog(ERROR, "There is no operator '%s' for types '%s' and '%s'"
		 "\n\tYou will either have to retype this query using an explicit cast,"
	 "\n\tor you will have to define the operator using CREATE OPERATOR",
		 op, typeTypeName(tp1), typeTypeName(tp2));
}