diff options
Diffstat (limited to 'src/backend/parser/parse_oper.c')
-rw-r--r-- | src/backend/parser/parse_oper.c | 469 |
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", |