aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_oper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_oper.c')
-rw-r--r--src/backend/parser/parse_oper.c469
1 files changed, 341 insertions, 128 deletions
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index e2eb6b901d4..2e43b1380fc 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.10 1998/04/27 04:06:09 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.11 1998/05/09 23:29:53 thomas Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,9 +23,18 @@
#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,
@@ -63,7 +72,8 @@ 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
@@ -97,7 +107,145 @@ binary_oper_get_candidates(char *opname,
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)
@@ -124,8 +272,12 @@ binary_oper_get_candidates(char *opname,
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,
@@ -156,7 +308,9 @@ binary_oper_get_candidates(char *opname,
heap_close(pg_operator_desc);
return ncandidates;
-}
+} /* BinaryOperCandidates() */
+#endif
+
/*
* equivalentOpersAfterPromotion -
@@ -164,7 +318,7 @@ binary_oper_get_candidates(char *opname,
* 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
- * the all the candidates operate on the same data types after
+ * all the candidates operate on the same data types after
* promotion (int2, int4, float4 -> float8).
*/
static bool
@@ -237,9 +391,33 @@ equivalentOpersAfterPromotion(CandidateList candidates)
}
-/*
- * given a choice of argument type pairs for a binary operator,
- * try to choose a default pair
+/* 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,
@@ -249,37 +427,11 @@ binary_oper_select_candidate(Oid arg1,
CandidateList result;
/*
- * if both are "unknown", there is no way to select a candidate
- *
- * 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 were commented out since then -ay]
+ * If both are "unknown", there is no way to select a candidate
*/
-
if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
return (NULL);
- /*
- * 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
- */
if (!equivalentOpersAfterPromotion(candidates))
return NULL;
@@ -296,90 +448,102 @@ binary_oper_select_candidate(Oid arg1,
return (NULL);
}
-/* Given operator, types of arg1, and arg2, return oper struct */
-/* arg1, arg2 --typeids */
+/* 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;
-
- if (!arg2)
+ 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)
+ if (arg1 == InvalidOid)
arg1 = arg2;
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(arg1),
- ObjectIdGetDatum(arg2),
- Int8GetDatum('b'))))
+ 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)
{
-
- /*
- * no operators of the desired types found
- */
if (!noWarnings)
op_error(op, arg1, arg2);
return (NULL);
}
+
+ /* Or found exactly one? Then proceed... */
else if (ncandidates == 1)
{
-
- /*
- * exactly one operator of the desired types found
- */
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
- ObjectIdGetDatum(candidates->args[0]),
- ObjectIdGetDatum(candidates->args[1]),
+ ObjectIdGetDatum(candidates->args[0]),
+ ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
}
+
+ /* Otherwise, multiple operators of the desired types found... */
else
{
-
- /*
- * multiple operators of the desired types found
- */
+#if FALSE
candidates = binary_oper_select_candidate(arg1, arg2, candidates);
- if (candidates != NULL)
+#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)
{
- /* we chose one of them */
+#if PARSEDEBUG
+printf("oper: found candidate\n");
+#endif
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
- ObjectIdGetDatum(candidates->args[0]),
- ObjectIdGetDatum(candidates->args[1]),
+ ObjectIdGetDatum(targetOids[0]),
+ ObjectIdGetDatum(targetOids[1]),
Int8GetDatum('b'));
- Assert(HeapTupleIsValid(tup));
}
else
{
- Type tp1,
- tp2;
+ tup = NULL;
+ }
- /* we chose none of them */
- tp1 = typeidType(arg1);
- tp2 = typeidType(arg2);
+ /* Could not choose one, for whatever reason... */
+ if (!HeapTupleIsValid(tup))
+ {
if (!noWarnings)
{
- elog(NOTICE, "there is more than one operator %s for types", op);
- elog(NOTICE, "%s and %s. You will have to retype this query",
- typeTypeName(tp1), typeTypeName(tp2));
- elog(ERROR, "using an explicit cast");
+ 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
@@ -409,6 +573,7 @@ unary_oper_get_candidates(char *op,
fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
opKey[1].sk_argument = CharGetDatum(rightleft);
+#if FALSE
/* currently, only "unknown" can be coerced */
/*
@@ -419,7 +584,11 @@ unary_oper_get_candidates(char *op,
{
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,
@@ -442,6 +611,10 @@ unary_oper_get_candidates(char *op,
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);
}
@@ -450,32 +623,35 @@ unary_oper_get_candidates(char *op,
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;
-
- /*
- * if (!OpCache) { init_op_cache(); }
- */
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(arg),
- ObjectIdGetDatum(InvalidOid),
- Int8GetDatum('r'))))
+ 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);
+ elog(ERROR, "Can't find right op '%s' for type %d", op, arg);
return (NULL);
}
else if (ncandidates == 1)
@@ -489,38 +665,59 @@ right_oper(char *op, Oid arg)
}
else
{
- elog(NOTICE, "there is more than one right operator %s", op);
- elog(NOTICE, "you will have to retype this query");
- elog(ERROR, "using an explicit cast");
- return (NULL);
+#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;
-
- /*
- * if (!OpCache) { init_op_cache(); }
- */
- if (!(tup = SearchSysCacheTuple(OPRNAME,
- PointerGetDatum(op),
- ObjectIdGetDatum(InvalidOid),
- ObjectIdGetDatum(arg),
- Int8GetDatum('l'))))
+ 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);
+ elog(ERROR, "Can't find left op '%s' for type %d", op, arg);
return (NULL);
}
else if (ncandidates == 1)
@@ -528,22 +725,44 @@ left_oper(char *op, Oid arg)
tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op),
ObjectIdGetDatum(InvalidOid),
- ObjectIdGetDatum(candidates->args[0]),
+ 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
{
- elog(NOTICE, "there is more than one left operator %s", op);
- elog(NOTICE, "you will have to retype this query");
- elog(ERROR, "using an explicit cast");
- return (NULL);
+#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.
*/
@@ -559,7 +778,8 @@ op_error(char *op, Oid arg1, Oid arg2)
}
else
{
- elog(ERROR, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ elog(ERROR, "Left hand side of operator '%s' has an unknown type"
+ "\n\tProbably a bad attribute name", op);
}
if (typeidIsValid(arg2))
@@ -568,17 +788,10 @@ op_error(char *op, Oid arg1, Oid arg2)
}
else
{
- elog(ERROR, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
+ elog(ERROR, "Right hand side of operator %s has an unknown type"
+ "\n\tProbably a bad attribute name", op);
}
-#if FALSE
- elog(NOTICE, "there is no operator %s for types %s and %s",
- op, typeTypeName(tp1), typeTypeName(tp2));
- elog(NOTICE, "You will either have to retype this query using an");
- elog(NOTICE, "explicit cast, or you will have to define the operator");
- elog(ERROR, "%s for %s and %s using CREATE OPERATOR",
- op, typeTypeName(tp1), typeTypeName(tp2));
-#endif
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",