diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-16 23:08:12 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-04-16 23:08:12 +0000 |
commit | 6cef5d2549110c6c0abb92215c2593e652024493 (patch) | |
tree | 7481a5b0bec7227c23f4b846cd7a1e40b47bf20e /src/backend/parser/parse_oper.c | |
parent | 4da51bfd6d89762f0a3cacde6edf1ac63c09349e (diff) | |
download | postgresql-6cef5d2549110c6c0abb92215c2593e652024493.tar.gz postgresql-6cef5d2549110c6c0abb92215c2593e652024493.zip |
Operators live in namespaces. CREATE/DROP/COMMENT ON OPERATOR take
qualified operator names directly, for example CREATE OPERATOR myschema.+
( ... ). To qualify an operator name in an expression you need to write
OPERATOR(myschema.+) (thanks to Peter for suggesting an escape hatch).
I also took advantage of having to reformat pg_operator to fix something
that'd been bugging me for a while: mergejoinable operators should have
explicit links to the associated cross-data-type comparison operators,
rather than hardwiring an assumption that they are named < and >.
Diffstat (limited to 'src/backend/parser/parse_oper.c')
-rw-r--r-- | src/backend/parser/parse_oper.c | 534 |
1 files changed, 254 insertions, 280 deletions
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 028bfab4319..52ae39cccd5 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.54 2002/04/11 20:00:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.55 2002/04/16 23:08:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/indexing.h" +#include "catalog/namespace.h" #include "catalog/pg_operator.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" @@ -28,17 +29,106 @@ #include "utils/fmgroids.h" #include "utils/syscache.h" -static Oid *oper_select_candidate(int nargs, Oid *input_typeids, - CandidateList candidates); -static Operator oper_exact(char *op, Oid arg1, Oid arg2); -static Operator oper_inexact(char *op, Oid arg1, Oid arg2); -static int binary_oper_get_candidates(char *opname, - CandidateList *candidates); -static int unary_oper_get_candidates(char *opname, - CandidateList *candidates, - char rightleft); -static void op_error(char *op, Oid arg1, Oid arg2); -static void unary_op_error(char *op, Oid arg, bool is_left_op); +static Oid binary_oper_exact(Oid arg1, Oid arg2, + FuncCandidateList candidates); +static Oid oper_select_candidate(int nargs, Oid *input_typeids, + FuncCandidateList candidates); +static void op_error(List *op, Oid arg1, Oid arg2); +static void unary_op_error(List *op, Oid arg, bool is_left_op); + + +/* + * LookupOperName + * Given a possibly-qualified operator name and exact input datatypes, + * look up the operator. Returns InvalidOid if no such operator. + * + * Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for + * a postfix op. + * + * If the operator name is not schema-qualified, it is sought in the current + * namespace search path. + */ +Oid +LookupOperName(List *opername, Oid oprleft, Oid oprright) +{ + FuncCandidateList clist; + char oprkind; + + if (!OidIsValid(oprleft)) + oprkind = 'l'; + else if (!OidIsValid(oprright)) + oprkind = 'r'; + else + oprkind = 'b'; + + clist = OpernameGetCandidates(opername, oprkind); + + while (clist) + { + if (clist->args[0] == oprleft && clist->args[1] == oprright) + return clist->oid; + clist = clist->next; + } + + return InvalidOid; +} + +/* + * LookupOperNameTypeNames + * Like LookupOperName, but the argument types are specified by + * TypeName nodes. Also, if we fail to find the operator + * and caller is not NULL, then an error is reported. + * + * Pass oprleft = NULL for a prefix op, oprright = NULL for a postfix op. + */ +Oid +LookupOperNameTypeNames(List *opername, TypeName *oprleft, + TypeName *oprright, const char *caller) +{ + Oid operoid; + Oid leftoid, + rightoid; + + if (oprleft == NULL) + leftoid = InvalidOid; + else + { + leftoid = LookupTypeName(oprleft); + if (!OidIsValid(leftoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(oprleft)); + } + if (oprright == NULL) + rightoid = InvalidOid; + else + { + rightoid = LookupTypeName(oprright); + if (!OidIsValid(rightoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(oprright)); + } + + operoid = LookupOperName(opername, leftoid, rightoid); + + if (!OidIsValid(operoid) && caller != NULL) + { + if (oprleft == NULL) + elog(ERROR, "%s: Prefix operator '%s' for type '%s' does not exist", + caller, NameListToString(opername), + TypeNameToString(oprright)); + else if (oprright == NULL) + elog(ERROR, "%s: Postfix operator '%s' for type '%s' does not exist", + caller, NameListToString(opername), + TypeNameToString(oprleft)); + else + elog(ERROR, "%s: Operator '%s' for types '%s' and '%s' does not exist", + caller, NameListToString(opername), + TypeNameToString(oprleft), + TypeNameToString(oprright)); + } + + return operoid; +} /* Select an ordering operator for the given datatype */ @@ -47,7 +137,8 @@ any_ordering_op(Oid argtype) { Oid order_opid; - order_opid = compatible_oper_opid("<", argtype, argtype, true); + order_opid = compatible_oper_opid(makeList1(makeString("<")), + argtype, argtype, true); if (!OidIsValid(order_opid)) elog(ERROR, "Unable to identify an ordering operator '%s' for type '%s'" "\n\tUse an explicit ordering operator or modify the query", @@ -72,116 +163,32 @@ oprfuncid(Operator op) } -/* binary_oper_get_candidates() - * given opname, find all possible input type pairs for which an operator - * named opname exists. - * Build a list of the candidate input types. - * Returns number of candidates found. +/* binary_oper_exact() + * Check for an "exact" match to the specified operand types. + * + * If one operand is an unknown literal, assume it should be taken to be + * the same type as the other operand for this purpose. */ -static int -binary_oper_get_candidates(char *opname, - CandidateList *candidates) +static Oid +binary_oper_exact(Oid arg1, Oid arg2, + FuncCandidateList candidates) { - Relation pg_operator_desc; - SysScanDesc pg_operator_scan; - HeapTuple tup; - int ncandidates = 0; - ScanKeyData opKey[1]; - - *candidates = NULL; - - ScanKeyEntryInitialize(&opKey[0], 0, - Anum_pg_operator_oprname, - F_NAMEEQ, - NameGetDatum(opname)); - - pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); - pg_operator_scan = systable_beginscan(pg_operator_desc, - OperatorNameIndex, true, - SnapshotNow, - 1, opKey); - - while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan))) - { - Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup); - - if (oper->oprkind == 'b') - { - CandidateList current_candidate; - - current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); - - current_candidate->args[0] = oper->oprleft; - current_candidate->args[1] = oper->oprright; - current_candidate->next = *candidates; - *candidates = current_candidate; - ncandidates++; - } - } - - systable_endscan(pg_operator_scan); - heap_close(pg_operator_desc, AccessShareLock); - - return ncandidates; -} /* binary_oper_get_candidates() */ + /* Unspecified type for one of the arguments? then use the other */ + if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid)) + arg1 = arg2; + else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid)) + arg2 = arg1; -/* unary_oper_get_candidates() - * given opname, find all possible types for which - * a right/left unary operator named opname exists. - * Build a list of the candidate input types. - * Returns number of candidates found. - */ -static int -unary_oper_get_candidates(char *opname, - CandidateList *candidates, - char rightleft) -{ - Relation pg_operator_desc; - SysScanDesc pg_operator_scan; - HeapTuple tup; - int ncandidates = 0; - ScanKeyData opKey[1]; - - *candidates = NULL; - - ScanKeyEntryInitialize(&opKey[0], 0, - Anum_pg_operator_oprname, - F_NAMEEQ, - NameGetDatum(opname)); - - pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); - pg_operator_scan = systable_beginscan(pg_operator_desc, - OperatorNameIndex, true, - SnapshotNow, - 1, opKey); - - while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan))) + while (candidates != NULL) { - Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup); - - if (oper->oprkind == rightleft) - { - CandidateList current_candidate; - - current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) palloc(sizeof(Oid)); - - if (rightleft == 'r') - current_candidate->args[0] = oper->oprleft; - else - current_candidate->args[0] = oper->oprright; - current_candidate->next = *candidates; - *candidates = current_candidate; - ncandidates++; - } + if (arg1 == candidates->args[0] && + arg2 == candidates->args[1]) + return candidates->oid; + candidates = candidates->next; } - systable_endscan(pg_operator_scan); - heap_close(pg_operator_desc, AccessShareLock); - - return ncandidates; -} /* unary_oper_get_candidates() */ + return InvalidOid; +} /* oper_select_candidate() @@ -234,13 +241,13 @@ unary_oper_get_candidates(char *opname, * some sense. (see equivalentOpersAfterPromotion for details.) * - ay 6/95 */ -static Oid * +static Oid oper_select_candidate(int nargs, Oid *input_typeids, - CandidateList candidates) + FuncCandidateList candidates) { - CandidateList current_candidate; - CandidateList last_candidate; + FuncCandidateList current_candidate; + FuncCandidateList last_candidate; Oid *current_typeids; Oid current_type; int unknownOids; @@ -289,9 +296,9 @@ oper_select_candidate(int nargs, /* Done if no candidate or only one candidate survives */ if (ncandidates == 0) - return NULL; + return InvalidOid; if (ncandidates == 1) - return candidates->args; + return candidates->oid; /* * Run through all candidates and keep those with the most matches on @@ -335,7 +342,7 @@ oper_select_candidate(int nargs, last_candidate->next = NULL; if (ncandidates == 1) - return candidates->args; + return candidates->oid; /* * Still too many candidates? Run through all candidates and keep @@ -382,7 +389,7 @@ oper_select_candidate(int nargs, last_candidate->next = NULL; if (ncandidates == 1) - return candidates->args; + return candidates->oid; /* * Still too many candidates? Now look for candidates which are @@ -428,7 +435,7 @@ oper_select_candidate(int nargs, last_candidate->next = NULL; if (ncandidates == 1) - return candidates->args; + return candidates->oid; /* * Still too many candidates? Try assigning types for the unknown @@ -467,7 +474,7 @@ oper_select_candidate(int nargs, nmatch++; } if (nmatch == nargs) - return current_typeids; + return current_candidate->oid; } } @@ -602,87 +609,12 @@ oper_select_candidate(int nargs, } if (ncandidates == 1) - return candidates->args; + return candidates->oid; - return NULL; /* failed to determine a unique candidate */ + return InvalidOid; /* failed to determine a unique candidate */ } /* oper_select_candidate() */ -/* oper_exact() - * Given operator, types of arg1 and arg2, return oper struct or NULL. - * - * NOTE: on success, the returned object is a syscache entry. The caller - * must ReleaseSysCache() the entry when done with it. - */ -static Operator -oper_exact(char *op, Oid arg1, Oid arg2) -{ - HeapTuple tup; - - /* Unspecified type for one of the arguments? then use the other */ - if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid)) - arg1 = arg2; - else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid)) - arg2 = arg1; - - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(arg1), - ObjectIdGetDatum(arg2), - CharGetDatum('b')); - - return (Operator) tup; -} - - -/* oper_inexact() - * Given operator, types of arg1 and arg2, return oper struct or NULL. - * - * NOTE: on success, the returned object is a syscache entry. The caller - * must ReleaseSysCache() the entry when done with it. - */ -static Operator -oper_inexact(char *op, Oid arg1, Oid 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 == InvalidOid) - arg1 = arg2; - - ncandidates = binary_oper_get_candidates(op, &candidates); - - /* No operators found? Then return null... */ - if (ncandidates == 0) - return NULL; - - /* - * Otherwise, check for compatible datatypes, and then try to resolve - * the conflict if more than one candidate remains. - */ - inputOids[0] = arg1; - inputOids[1] = arg2; - targetOids = oper_select_candidate(2, inputOids, candidates); - if (targetOids != NULL) - { - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(targetOids[0]), - ObjectIdGetDatum(targetOids[1]), - CharGetDatum('b')); - } - else - tup = NULL; - return (Operator) tup; -} - - /* oper() -- search for a binary operator * Given operator name, types of arg1 and arg2, return oper struct. * @@ -697,22 +629,48 @@ oper_inexact(char *op, Oid arg1, Oid arg2) * must ReleaseSysCache() the entry when done with it. */ Operator -oper(char *opname, Oid ltypeId, Oid rtypeId, bool noError) +oper(List *opname, Oid ltypeId, Oid rtypeId, bool noError) { - HeapTuple tup; + FuncCandidateList clist; + Oid operOid; + Oid inputOids[2]; + HeapTuple tup = NULL; - /* check for exact match on this operator... */ - if (HeapTupleIsValid(tup = oper_exact(opname, ltypeId, rtypeId))) - return (Operator) tup; + /* Get binary operators of given name */ + clist = OpernameGetCandidates(opname, 'b'); - /* try to find a match on likely candidates... */ - if (HeapTupleIsValid(tup = oper_inexact(opname, ltypeId, rtypeId))) - return (Operator) tup; + /* No operators found? Then fail... */ + if (clist != NULL) + { + /* + * Check for an "exact" match. + */ + operOid = binary_oper_exact(ltypeId, rtypeId, clist); + if (!OidIsValid(operOid)) + { + /* + * Otherwise, search for the most suitable candidate. + */ - if (!noError) + /* Unspecified type for one of the arguments? then use the other */ + if (rtypeId == InvalidOid) + rtypeId = ltypeId; + else if (ltypeId == InvalidOid) + ltypeId = rtypeId; + inputOids[0] = ltypeId; + inputOids[1] = rtypeId; + operOid = oper_select_candidate(2, inputOids, clist); + } + if (OidIsValid(operOid)) + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); + } + + if (!HeapTupleIsValid(tup) && !noError) op_error(opname, ltypeId, rtypeId); - return (Operator) NULL; + return (Operator) tup; } /* compatible_oper() @@ -723,7 +681,7 @@ oper(char *opname, Oid ltypeId, Oid rtypeId, bool noError) * are accepted). Otherwise, the semantics are the same. */ Operator -compatible_oper(char *op, Oid arg1, Oid arg2, bool noError) +compatible_oper(List *op, Oid arg1, Oid arg2, bool noError) { Operator optup; Form_pg_operator opform; @@ -755,7 +713,7 @@ compatible_oper(char *op, Oid arg1, Oid arg2, bool noError) * lookup fails and noError is true. */ Oid -compatible_oper_opid(char *op, Oid arg1, Oid arg2, bool noError) +compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError) { Operator optup; Oid result; @@ -777,7 +735,7 @@ compatible_oper_opid(char *op, Oid arg1, Oid arg2, bool noError) * lookup fails and noError is true. */ Oid -compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError) +compatible_oper_funcid(List *op, Oid arg1, Oid arg2, bool noError) { Operator optup; Oid result; @@ -805,46 +763,50 @@ compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError) * must ReleaseSysCache() the entry when done with it. */ Operator -right_oper(char *op, Oid arg) +right_oper(List *op, Oid arg) { - HeapTuple tup; - CandidateList candidates; - int ncandidates; - Oid *targetOid; + FuncCandidateList clist; + Oid operOid = InvalidOid; + HeapTuple tup = NULL; - /* Try for exact match */ - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(arg), - ObjectIdGetDatum(InvalidOid), - CharGetDatum('r')); + /* Find candidates */ + clist = OpernameGetCandidates(op, 'r'); - if (!HeapTupleIsValid(tup)) + if (clist != NULL) { - /* Try for inexact matches */ - ncandidates = unary_oper_get_candidates(op, &candidates, 'r'); - if (ncandidates == 0) - unary_op_error(op, arg, FALSE); - else + /* + * First, quickly check to see if there is an exactly matching + * operator (there can be only one such entry in the list). + */ + FuncCandidateList clisti; + + for (clisti = clist; clisti != NULL; clisti = clisti->next) + { + if (arg == clisti->args[0]) + { + operOid = clisti->oid; + break; + } + } + + if (!OidIsValid(operOid)) { /* * We must run oper_select_candidate even if only one * candidate, otherwise we may falsely return a * non-type-compatible operator. */ - targetOid = oper_select_candidate(1, &arg, candidates); - if (targetOid != NULL) - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(targetOid[0]), - ObjectIdGetDatum(InvalidOid), - CharGetDatum('r')); + operOid = oper_select_candidate(1, &arg, clist); } - - if (!HeapTupleIsValid(tup)) - unary_op_error(op, arg, FALSE); + if (OidIsValid(operOid)) + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); } + if (!HeapTupleIsValid(tup)) + unary_op_error(op, arg, FALSE); + return (Operator) tup; } /* right_oper() */ @@ -861,46 +823,55 @@ right_oper(char *op, Oid arg) * must ReleaseSysCache() the entry when done with it. */ Operator -left_oper(char *op, Oid arg) +left_oper(List *op, Oid arg) { - HeapTuple tup; - CandidateList candidates; - int ncandidates; - Oid *targetOid; + FuncCandidateList clist; + Oid operOid = InvalidOid; + HeapTuple tup = NULL; - /* Try for exact match */ - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(arg), - CharGetDatum('l')); + /* Find candidates */ + clist = OpernameGetCandidates(op, 'l'); - if (!HeapTupleIsValid(tup)) + if (clist != NULL) { - /* Try for inexact matches */ - ncandidates = unary_oper_get_candidates(op, &candidates, 'l'); - if (ncandidates == 0) - unary_op_error(op, arg, TRUE); - else + /* + * First, quickly check to see if there is an exactly matching + * operator (there can be only one such entry in the list). + * + * The returned list has args in the form (0, oprright). Move the + * useful data into args[0] to keep oper_select_candidate simple. + * XXX we are assuming here that we may scribble on the list! + */ + FuncCandidateList clisti; + + for (clisti = clist; clisti != NULL; clisti = clisti->next) + { + clisti->args[0] = clisti->args[1]; + if (arg == clisti->args[0]) + { + operOid = clisti->oid; + break; + } + } + + if (!OidIsValid(operOid)) { /* * We must run oper_select_candidate even if only one * candidate, otherwise we may falsely return a * non-type-compatible operator. */ - targetOid = oper_select_candidate(1, &arg, candidates); - if (targetOid != NULL) - tup = SearchSysCache(OPERNAME, - PointerGetDatum(op), - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(targetOid[0]), - CharGetDatum('l')); + operOid = oper_select_candidate(1, &arg, clist); } - - if (!HeapTupleIsValid(tup)) - unary_op_error(op, arg, TRUE); + if (OidIsValid(operOid)) + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(operOid), + 0, 0, 0); } + if (!HeapTupleIsValid(tup)) + unary_op_error(op, arg, TRUE); + return (Operator) tup; } /* left_oper() */ @@ -910,19 +881,22 @@ left_oper(char *op, Oid arg) * is not found. */ static void -op_error(char *op, Oid arg1, Oid arg2) +op_error(List *op, Oid arg1, Oid arg2) { if (!typeidIsValid(arg1)) elog(ERROR, "Left hand side of operator '%s' has an unknown type" - "\n\tProbably a bad attribute name", op); + "\n\tProbably a bad attribute name", + NameListToString(op)); if (!typeidIsValid(arg2)) elog(ERROR, "Right hand side of operator %s has an unknown type" - "\n\tProbably a bad attribute name", op); + "\n\tProbably a bad attribute name", + NameListToString(op)); elog(ERROR, "Unable to identify an operator '%s' for types '%s' and '%s'" "\n\tYou will have to retype this query using an explicit cast", - op, format_type_be(arg1), format_type_be(arg2)); + NameListToString(op), + format_type_be(arg1), format_type_be(arg2)); } /* unary_op_error() @@ -930,28 +904,28 @@ op_error(char *op, Oid arg1, Oid arg2) * is not found. */ static void -unary_op_error(char *op, Oid arg, bool is_left_op) +unary_op_error(List *op, Oid arg, bool is_left_op) { if (!typeidIsValid(arg)) { if (is_left_op) elog(ERROR, "operand of prefix operator '%s' has an unknown type" "\n\t(probably an invalid column reference)", - op); + NameListToString(op)); else elog(ERROR, "operand of postfix operator '%s' has an unknown type" "\n\t(probably an invalid column reference)", - op); + NameListToString(op)); } else { if (is_left_op) elog(ERROR, "Unable to identify a prefix operator '%s' for type '%s'" "\n\tYou may need to add parentheses or an explicit cast", - op, format_type_be(arg)); + NameListToString(op), format_type_be(arg)); else elog(ERROR, "Unable to identify a postfix operator '%s' for type '%s'" "\n\tYou may need to add parentheses or an explicit cast", - op, format_type_be(arg)); + NameListToString(op), format_type_be(arg)); } } |