diff options
Diffstat (limited to 'src/backend/parser/parse_oper.c')
-rw-r--r-- | src/backend/parser/parse_oper.c | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c new file mode 100644 index 0000000..4e46079 --- /dev/null +++ b/src/backend/parser/parse_oper.c @@ -0,0 +1,1066 @@ +/*------------------------------------------------------------------------- + * + * parse_oper.c + * handle operator things for parser + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/parser/parse_oper.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_type.h" +#include "lib/stringinfo.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" +#include "parser/parse_func.h" +#include "parser/parse_oper.h" +#include "parser/parse_type.h" +#include "utils/builtins.h" +#include "utils/inval.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include "utils/typcache.h" + + +/* + * The lookup key for the operator lookaside hash table. Unused bits must be + * zeroes to ensure hashing works consistently --- in particular, oprname + * must be zero-padded and any unused entries in search_path must be zero. + * + * search_path contains the actual search_path with which the entry was + * derived (minus temp namespace if any), or else the single specified + * schema OID if we are looking up an explicitly-qualified operator name. + * + * search_path has to be fixed-length since the hashtable code insists on + * fixed-size keys. If your search path is longer than that, we just punt + * and don't cache anything. + */ + +/* If your search_path is longer than this, sucks to be you ... */ +#define MAX_CACHED_PATH_LEN 16 + +typedef struct OprCacheKey +{ + char oprname[NAMEDATALEN]; + Oid left_arg; /* Left input OID, or 0 if prefix op */ + Oid right_arg; /* Right input OID */ + Oid search_path[MAX_CACHED_PATH_LEN]; +} OprCacheKey; + +typedef struct OprCacheEntry +{ + /* the hash lookup key MUST BE FIRST */ + OprCacheKey key; + + Oid opr_oid; /* OID of the resolved operator */ +} OprCacheEntry; + + +static Oid binary_oper_exact(List *opname, Oid arg1, Oid arg2); +static FuncDetailCode oper_select_candidate(int nargs, + Oid *input_typeids, + FuncCandidateList candidates, + Oid *operOid); +static const char *op_signature_string(List *op, char oprkind, + Oid arg1, Oid arg2); +static void op_error(ParseState *pstate, List *op, char oprkind, + Oid arg1, Oid arg2, + FuncDetailCode fdresult, int location); +static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key, + List *opname, Oid ltypeId, Oid rtypeId, + int location); +static Oid find_oper_cache_entry(OprCacheKey *key); +static void make_oper_cache_entry(OprCacheKey *key, Oid opr_oid); +static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue); + + +/* + * LookupOperName + * Given a possibly-qualified operator name and exact input datatypes, + * look up the operator. + * + * Pass oprleft = InvalidOid for a prefix op. + * + * If the operator name is not schema-qualified, it is sought in the current + * namespace search path. + * + * If the operator is not found, we return InvalidOid if noError is true, + * else raise an error. pstate and location are used only to report the + * error position; pass NULL/-1 if not available. + */ +Oid +LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, + bool noError, int location) +{ + Oid result; + + result = OpernameGetOprid(opername, oprleft, oprright); + if (OidIsValid(result)) + return result; + + /* we don't use op_error here because only an exact match is wanted */ + if (!noError) + { + char oprkind; + + if (!OidIsValid(oprleft)) + oprkind = 'l'; + else if (OidIsValid(oprright)) + oprkind = 'b'; + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("postfix operators are not supported"), + parser_errposition(pstate, location))); + oprkind = 0; /* keep compiler quiet */ + } + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator does not exist: %s", + op_signature_string(opername, oprkind, + oprleft, oprright)), + parser_errposition(pstate, location))); + } + + return InvalidOid; +} + +/* + * LookupOperWithArgs + * Like LookupOperName, but the argument types are specified by + * a ObjectWithArgs node. + */ +Oid +LookupOperWithArgs(ObjectWithArgs *oper, bool noError) +{ + TypeName *oprleft, + *oprright; + Oid leftoid, + rightoid; + + Assert(list_length(oper->objargs) == 2); + oprleft = linitial_node(TypeName, oper->objargs); + oprright = lsecond_node(TypeName, oper->objargs); + + if (oprleft == NULL) + leftoid = InvalidOid; + else + leftoid = LookupTypeNameOid(NULL, oprleft, noError); + + if (oprright == NULL) + rightoid = InvalidOid; + else + rightoid = LookupTypeNameOid(NULL, oprright, noError); + + return LookupOperName(NULL, oper->objname, leftoid, rightoid, + noError, -1); +} + +/* + * get_sort_group_operators - get default sorting/grouping operators for type + * + * We fetch the "<", "=", and ">" operators all at once to reduce lookup + * overhead (knowing that most callers will be interested in at least two). + * However, a given datatype might have only an "=" operator, if it is + * hashable but not sortable. (Other combinations of present and missing + * operators shouldn't happen, unless the system catalogs are messed up.) + * + * If an operator is missing and the corresponding needXX flag is true, + * throw a standard error message, else return InvalidOid. + * + * In addition to the operator OIDs themselves, this function can identify + * whether the "=" operator is hashable. + * + * Callers can pass NULL pointers for any results they don't care to get. + * + * Note: the results are guaranteed to be exact or binary-compatible matches, + * since most callers are not prepared to cope with adding any run-time type + * coercion steps. + */ +void +get_sort_group_operators(Oid argtype, + bool needLT, bool needEQ, bool needGT, + Oid *ltOpr, Oid *eqOpr, Oid *gtOpr, + bool *isHashable) +{ + TypeCacheEntry *typentry; + int cache_flags; + Oid lt_opr; + Oid eq_opr; + Oid gt_opr; + bool hashable; + + /* + * Look up the operators using the type cache. + * + * Note: the search algorithm used by typcache.c ensures that the results + * are consistent, ie all from matching opclasses. + */ + if (isHashable != NULL) + cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR | + TYPECACHE_HASH_PROC; + else + cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR; + + typentry = lookup_type_cache(argtype, cache_flags); + lt_opr = typentry->lt_opr; + eq_opr = typentry->eq_opr; + gt_opr = typentry->gt_opr; + hashable = OidIsValid(typentry->hash_proc); + + /* Report errors if needed */ + if ((needLT && !OidIsValid(lt_opr)) || + (needGT && !OidIsValid(gt_opr))) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify an ordering operator for type %s", + format_type_be(argtype)), + errhint("Use an explicit ordering operator or modify the query."))); + if (needEQ && !OidIsValid(eq_opr)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify an equality operator for type %s", + format_type_be(argtype)))); + + /* Return results as needed */ + if (ltOpr) + *ltOpr = lt_opr; + if (eqOpr) + *eqOpr = eq_opr; + if (gtOpr) + *gtOpr = gt_opr; + if (isHashable) + *isHashable = hashable; +} + + +/* given operator tuple, return the operator OID */ +Oid +oprid(Operator op) +{ + return ((Form_pg_operator) GETSTRUCT(op))->oid; +} + +/* given operator tuple, return the underlying function's OID */ +Oid +oprfuncid(Operator op) +{ + Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(op); + + return pgopform->oprcode; +} + + +/* 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. Also, consider + * the possibility that the other operand is a domain type that needs to + * be reduced to its base type to find an "exact" match. + */ +static Oid +binary_oper_exact(List *opname, Oid arg1, Oid arg2) +{ + Oid result; + bool was_unknown = false; + + /* Unspecified type for one of the arguments? then use the other */ + if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid)) + { + arg1 = arg2; + was_unknown = true; + } + else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid)) + { + arg2 = arg1; + was_unknown = true; + } + + result = OpernameGetOprid(opname, arg1, arg2); + if (OidIsValid(result)) + return result; + + if (was_unknown) + { + /* arg1 and arg2 are the same here, need only look at arg1 */ + Oid basetype = getBaseType(arg1); + + if (basetype != arg1) + { + result = OpernameGetOprid(opname, basetype, basetype); + if (OidIsValid(result)) + return result; + } + } + + return InvalidOid; +} + + +/* oper_select_candidate() + * Given the input argtype array and one or more candidates + * for the operator, attempt to resolve the conflict. + * + * Returns FUNCDETAIL_NOTFOUND, FUNCDETAIL_MULTIPLE, or FUNCDETAIL_NORMAL. + * In the success case the Oid of the best candidate is stored in *operOid. + * + * Note that the caller has already determined that there is no candidate + * exactly matching the input argtype(s). Incompatible candidates are not yet + * pruned away, however. + */ +static FuncDetailCode +oper_select_candidate(int nargs, + Oid *input_typeids, + FuncCandidateList candidates, + Oid *operOid) /* output argument */ +{ + int ncandidates; + + /* + * Delete any candidates that cannot actually accept the given input + * types, whether directly or by coercion. + */ + ncandidates = func_match_argtypes(nargs, input_typeids, + candidates, &candidates); + + /* Done if no candidate or only one candidate survives */ + if (ncandidates == 0) + { + *operOid = InvalidOid; + return FUNCDETAIL_NOTFOUND; + } + if (ncandidates == 1) + { + *operOid = candidates->oid; + return FUNCDETAIL_NORMAL; + } + + /* + * Use the same heuristics as for ambiguous functions to resolve the + * conflict. + */ + candidates = func_select_candidate(nargs, input_typeids, candidates); + + if (candidates) + { + *operOid = candidates->oid; + return FUNCDETAIL_NORMAL; + } + + *operOid = InvalidOid; + return FUNCDETAIL_MULTIPLE; /* failed to select a best candidate */ +} + + +/* oper() -- search for a binary operator + * Given operator name, types of arg1 and arg2, return oper struct. + * + * IMPORTANT: the returned operator (if any) is only promised to be + * coercion-compatible with the input datatypes. Do not use this if + * you need an exact- or binary-compatible match; see compatible_oper. + * + * If no matching operator found, return NULL if noError is true, + * raise an error if it is false. pstate and location are used only to report + * the error position; pass NULL/-1 if not available. + * + * NOTE: on success, the returned object is a syscache entry. The caller + * must ReleaseSysCache() the entry when done with it. + */ +Operator +oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, + bool noError, int location) +{ + Oid operOid; + OprCacheKey key; + bool key_ok; + FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND; + HeapTuple tup = NULL; + + /* + * Try to find the mapping in the lookaside cache. + */ + key_ok = make_oper_cache_key(pstate, &key, opname, ltypeId, rtypeId, location); + + if (key_ok) + { + operOid = find_oper_cache_entry(&key); + if (OidIsValid(operOid)) + { + tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); + if (HeapTupleIsValid(tup)) + return (Operator) tup; + } + } + + /* + * First try for an "exact" match. + */ + operOid = binary_oper_exact(opname, ltypeId, rtypeId); + if (!OidIsValid(operOid)) + { + /* + * Otherwise, search for the most suitable candidate. + */ + FuncCandidateList clist; + + /* Get binary operators of given name */ + clist = OpernameGetCandidates(opname, 'b', false); + + /* No operators found? Then fail... */ + if (clist != NULL) + { + /* + * Unspecified type for one of the arguments? then use the other + * (XXX this is probably dead code?) + */ + Oid inputOids[2]; + + if (rtypeId == InvalidOid) + rtypeId = ltypeId; + else if (ltypeId == InvalidOid) + ltypeId = rtypeId; + inputOids[0] = ltypeId; + inputOids[1] = rtypeId; + fdresult = oper_select_candidate(2, inputOids, clist, &operOid); + } + } + + if (OidIsValid(operOid)) + tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); + + if (HeapTupleIsValid(tup)) + { + if (key_ok) + make_oper_cache_entry(&key, operOid); + } + else if (!noError) + op_error(pstate, opname, 'b', ltypeId, rtypeId, fdresult, location); + + return (Operator) tup; +} + +/* compatible_oper() + * given an opname and input datatypes, find a compatible binary operator + * + * This is tighter than oper() because it will not return an operator that + * requires coercion of the input datatypes (but binary-compatible operators + * are accepted). Otherwise, the semantics are the same. + */ +Operator +compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2, + bool noError, int location) +{ + Operator optup; + Form_pg_operator opform; + + /* oper() will find the best available match */ + optup = oper(pstate, op, arg1, arg2, noError, location); + if (optup == (Operator) NULL) + return (Operator) NULL; /* must be noError case */ + + /* but is it good enough? */ + opform = (Form_pg_operator) GETSTRUCT(optup); + if (IsBinaryCoercible(arg1, opform->oprleft) && + IsBinaryCoercible(arg2, opform->oprright)) + return optup; + + /* nope... */ + ReleaseSysCache(optup); + + if (!noError) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator requires run-time type coercion: %s", + op_signature_string(op, 'b', arg1, arg2)), + parser_errposition(pstate, location))); + + return (Operator) NULL; +} + +/* compatible_oper_opid() -- get OID of a binary operator + * + * This is a convenience routine that extracts only the operator OID + * from the result of compatible_oper(). InvalidOid is returned if the + * lookup fails and noError is true. + */ +Oid +compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError) +{ + Operator optup; + Oid result; + + optup = compatible_oper(NULL, op, arg1, arg2, noError, -1); + if (optup != NULL) + { + result = oprid(optup); + ReleaseSysCache(optup); + return result; + } + return InvalidOid; +} + + +/* left_oper() -- search for a unary left operator (prefix operator) + * Given operator name and type of arg, return oper struct. + * + * IMPORTANT: the returned operator (if any) is only promised to be + * coercion-compatible with the input datatype. Do not use this if + * you need an exact- or binary-compatible match. + * + * If no matching operator found, return NULL if noError is true, + * raise an error if it is false. pstate and location are used only to report + * the error position; pass NULL/-1 if not available. + * + * NOTE: on success, the returned object is a syscache entry. The caller + * must ReleaseSysCache() the entry when done with it. + */ +Operator +left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) +{ + Oid operOid; + OprCacheKey key; + bool key_ok; + FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND; + HeapTuple tup = NULL; + + /* + * Try to find the mapping in the lookaside cache. + */ + key_ok = make_oper_cache_key(pstate, &key, op, InvalidOid, arg, location); + + if (key_ok) + { + operOid = find_oper_cache_entry(&key); + if (OidIsValid(operOid)) + { + tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); + if (HeapTupleIsValid(tup)) + return (Operator) tup; + } + } + + /* + * First try for an "exact" match. + */ + operOid = OpernameGetOprid(op, InvalidOid, arg); + if (!OidIsValid(operOid)) + { + /* + * Otherwise, search for the most suitable candidate. + */ + FuncCandidateList clist; + + /* Get prefix operators of given name */ + clist = OpernameGetCandidates(op, 'l', false); + + /* No operators found? Then fail... */ + if (clist != NULL) + { + /* + * 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]; + } + + /* + * We must run oper_select_candidate even if only one candidate, + * otherwise we may falsely return a non-type-compatible operator. + */ + fdresult = oper_select_candidate(1, &arg, clist, &operOid); + } + } + + if (OidIsValid(operOid)) + tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); + + if (HeapTupleIsValid(tup)) + { + if (key_ok) + make_oper_cache_entry(&key, operOid); + } + else if (!noError) + op_error(pstate, op, 'l', InvalidOid, arg, fdresult, location); + + return (Operator) tup; +} + +/* + * op_signature_string + * Build a string representing an operator name, including arg type(s). + * The result is something like "integer + integer". + * + * This is typically used in the construction of operator-not-found error + * messages. + */ +static const char * +op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2) +{ + StringInfoData argbuf; + + initStringInfo(&argbuf); + + if (oprkind != 'l') + appendStringInfo(&argbuf, "%s ", format_type_be(arg1)); + + appendStringInfoString(&argbuf, NameListToString(op)); + + appendStringInfo(&argbuf, " %s", format_type_be(arg2)); + + return argbuf.data; /* return palloc'd string buffer */ +} + +/* + * op_error - utility routine to complain about an unresolvable operator + */ +static void +op_error(ParseState *pstate, List *op, char oprkind, + Oid arg1, Oid arg2, + FuncDetailCode fdresult, int location) +{ + if (fdresult == FUNCDETAIL_MULTIPLE) + ereport(ERROR, + (errcode(ERRCODE_AMBIGUOUS_FUNCTION), + errmsg("operator is not unique: %s", + op_signature_string(op, oprkind, arg1, arg2)), + errhint("Could not choose a best candidate operator. " + "You might need to add explicit type casts."), + parser_errposition(pstate, location))); + else + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator does not exist: %s", + op_signature_string(op, oprkind, arg1, arg2)), + (!arg1 || !arg2) ? + errhint("No operator matches the given name and argument type. " + "You might need to add an explicit type cast.") : + errhint("No operator matches the given name and argument types. " + "You might need to add explicit type casts."), + parser_errposition(pstate, location))); +} + +/* + * make_op() + * Operator expression construction. + * + * Transform operator expression ensuring type compatibility. + * This is where some type conversion happens. + * + * last_srf should be a copy of pstate->p_last_srf from just before we + * started transforming the operator's arguments; this is used for nested-SRF + * detection. If the caller will throw an error anyway for a set-returning + * expression, it's okay to cheat and just pass pstate->p_last_srf. + */ +Expr * +make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, + Node *last_srf, int location) +{ + Oid ltypeId, + rtypeId; + Operator tup; + Form_pg_operator opform; + Oid actual_arg_types[2]; + Oid declared_arg_types[2]; + int nargs; + List *args; + Oid rettype; + OpExpr *result; + + /* Check it's not a postfix operator */ + if (rtree == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("postfix operators are not supported"))); + + /* Select the operator */ + if (ltree == NULL) + { + /* prefix operator */ + rtypeId = exprType(rtree); + ltypeId = InvalidOid; + tup = left_oper(pstate, opname, rtypeId, false, location); + } + else + { + /* otherwise, binary operator */ + ltypeId = exprType(ltree); + rtypeId = exprType(rtree); + tup = oper(pstate, opname, ltypeId, rtypeId, false, location); + } + + opform = (Form_pg_operator) GETSTRUCT(tup); + + /* Check it's not a shell */ + if (!RegProcedureIsValid(opform->oprcode)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator is only a shell: %s", + op_signature_string(opname, + opform->oprkind, + opform->oprleft, + opform->oprright)), + parser_errposition(pstate, location))); + + /* Do typecasting and build the expression tree */ + if (ltree == NULL) + { + /* prefix operator */ + args = list_make1(rtree); + actual_arg_types[0] = rtypeId; + declared_arg_types[0] = opform->oprright; + nargs = 1; + } + else + { + /* otherwise, binary operator */ + args = list_make2(ltree, rtree); + actual_arg_types[0] = ltypeId; + actual_arg_types[1] = rtypeId; + declared_arg_types[0] = opform->oprleft; + declared_arg_types[1] = opform->oprright; + nargs = 2; + } + + /* + * enforce consistency with polymorphic argument and return types, + * possibly adjusting return type or declared_arg_types (which will be + * used as the cast destination by make_fn_arguments) + */ + rettype = enforce_generic_type_consistency(actual_arg_types, + declared_arg_types, + nargs, + opform->oprresult, + false); + + /* perform the necessary typecasting of arguments */ + make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + + /* and build the expression node */ + result = makeNode(OpExpr); + result->opno = oprid(tup); + result->opfuncid = opform->oprcode; + result->opresulttype = rettype; + result->opretset = get_func_retset(opform->oprcode); + /* opcollid and inputcollid will be set by parse_collate.c */ + result->args = args; + result->location = location; + + /* if it returns a set, check that's OK */ + if (result->opretset) + { + check_srf_call_placement(pstate, last_srf, location); + /* ... and remember it for error checks at higher levels */ + pstate->p_last_srf = (Node *) result; + } + + ReleaseSysCache(tup); + + return (Expr *) result; +} + +/* + * make_scalar_array_op() + * Build expression tree for "scalar op ANY/ALL (array)" construct. + */ +Expr * +make_scalar_array_op(ParseState *pstate, List *opname, + bool useOr, + Node *ltree, Node *rtree, + int location) +{ + Oid ltypeId, + rtypeId, + atypeId, + res_atypeId; + Operator tup; + Form_pg_operator opform; + Oid actual_arg_types[2]; + Oid declared_arg_types[2]; + List *args; + Oid rettype; + ScalarArrayOpExpr *result; + + ltypeId = exprType(ltree); + atypeId = exprType(rtree); + + /* + * The right-hand input of the operator will be the element type of the + * array. However, if we currently have just an untyped literal on the + * right, stay with that and hope we can resolve the operator. + */ + if (atypeId == UNKNOWNOID) + rtypeId = UNKNOWNOID; + else + { + rtypeId = get_base_element_type(atypeId); + if (!OidIsValid(rtypeId)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("op ANY/ALL (array) requires array on right side"), + parser_errposition(pstate, location))); + } + + /* Now resolve the operator */ + tup = oper(pstate, opname, ltypeId, rtypeId, false, location); + opform = (Form_pg_operator) GETSTRUCT(tup); + + /* Check it's not a shell */ + if (!RegProcedureIsValid(opform->oprcode)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator is only a shell: %s", + op_signature_string(opname, + opform->oprkind, + opform->oprleft, + opform->oprright)), + parser_errposition(pstate, location))); + + args = list_make2(ltree, rtree); + actual_arg_types[0] = ltypeId; + actual_arg_types[1] = rtypeId; + declared_arg_types[0] = opform->oprleft; + declared_arg_types[1] = opform->oprright; + + /* + * enforce consistency with polymorphic argument and return types, + * possibly adjusting return type or declared_arg_types (which will be + * used as the cast destination by make_fn_arguments) + */ + rettype = enforce_generic_type_consistency(actual_arg_types, + declared_arg_types, + 2, + opform->oprresult, + false); + + /* + * Check that operator result is boolean + */ + if (rettype != BOOLOID) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("op ANY/ALL (array) requires operator to yield boolean"), + parser_errposition(pstate, location))); + if (get_func_retset(opform->oprcode)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("op ANY/ALL (array) requires operator not to return a set"), + parser_errposition(pstate, location))); + + /* + * Now switch back to the array type on the right, arranging for any + * needed cast to be applied. Beware of polymorphic operators here; + * enforce_generic_type_consistency may or may not have replaced a + * polymorphic type with a real one. + */ + if (IsPolymorphicType(declared_arg_types[1])) + { + /* assume the actual array type is OK */ + res_atypeId = atypeId; + } + else + { + res_atypeId = get_array_type(declared_arg_types[1]); + if (!OidIsValid(res_atypeId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(declared_arg_types[1])), + parser_errposition(pstate, location))); + } + actual_arg_types[1] = atypeId; + declared_arg_types[1] = res_atypeId; + + /* perform the necessary typecasting of arguments */ + make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + + /* and build the expression node */ + result = makeNode(ScalarArrayOpExpr); + result->opno = oprid(tup); + result->opfuncid = opform->oprcode; + result->hashfuncid = InvalidOid; + result->useOr = useOr; + /* inputcollid will be set by parse_collate.c */ + result->args = args; + result->location = location; + + ReleaseSysCache(tup); + + return (Expr *) result; +} + + +/* + * Lookaside cache to speed operator lookup. Possibly this should be in + * a separate module under utils/cache/ ? + * + * The idea here is that the mapping from operator name and given argument + * types is constant for a given search path (or single specified schema OID) + * so long as the contents of pg_operator and pg_cast don't change. And that + * mapping is pretty expensive to compute, especially for ambiguous operators; + * this is mainly because there are a *lot* of instances of popular operator + * names such as "=", and we have to check each one to see which is the + * best match. So once we have identified the correct mapping, we save it + * in a cache that need only be flushed on pg_operator or pg_cast change. + * (pg_cast must be considered because changes in the set of implicit casts + * affect the set of applicable operators for any given input datatype.) + * + * XXX in principle, ALTER TABLE ... INHERIT could affect the mapping as + * well, but we disregard that since there's no convenient way to find out + * about it, and it seems a pretty far-fetched corner-case anyway. + * + * Note: at some point it might be worth doing a similar cache for function + * lookups. However, the potential gain is a lot less since (a) function + * names are generally not overloaded as heavily as operator names, and + * (b) we'd have to flush on pg_proc updates, which are probably a good + * deal more common than pg_operator updates. + */ + +/* The operator cache hashtable */ +static HTAB *OprCacheHash = NULL; + + +/* + * make_oper_cache_key + * Fill the lookup key struct given operator name and arg types. + * + * Returns true if successful, false if the search_path overflowed + * (hence no caching is possible). + * + * pstate/location are used only to report the error position; pass NULL/-1 + * if not available. + */ +static bool +make_oper_cache_key(ParseState *pstate, OprCacheKey *key, List *opname, + Oid ltypeId, Oid rtypeId, int location) +{ + char *schemaname; + char *opername; + + /* deconstruct the name list */ + DeconstructQualifiedName(opname, &schemaname, &opername); + + /* ensure zero-fill for stable hashing */ + MemSet(key, 0, sizeof(OprCacheKey)); + + /* save operator name and input types into key */ + strlcpy(key->oprname, opername, NAMEDATALEN); + key->left_arg = ltypeId; + key->right_arg = rtypeId; + + if (schemaname) + { + ParseCallbackState pcbstate; + + /* search only in exact schema given */ + setup_parser_errposition_callback(&pcbstate, pstate, location); + key->search_path[0] = LookupExplicitNamespace(schemaname, false); + cancel_parser_errposition_callback(&pcbstate); + } + else + { + /* get the active search path */ + if (fetch_search_path_array(key->search_path, + MAX_CACHED_PATH_LEN) > MAX_CACHED_PATH_LEN) + return false; /* oops, didn't fit */ + } + + return true; +} + +/* + * find_oper_cache_entry + * + * Look for a cache entry matching the given key. If found, return the + * contained operator OID, else return InvalidOid. + */ +static Oid +find_oper_cache_entry(OprCacheKey *key) +{ + OprCacheEntry *oprentry; + + if (OprCacheHash == NULL) + { + /* First time through: initialize the hash table */ + HASHCTL ctl; + + ctl.keysize = sizeof(OprCacheKey); + ctl.entrysize = sizeof(OprCacheEntry); + OprCacheHash = hash_create("Operator lookup cache", 256, + &ctl, HASH_ELEM | HASH_BLOBS); + + /* Arrange to flush cache on pg_operator and pg_cast changes */ + CacheRegisterSyscacheCallback(OPERNAMENSP, + InvalidateOprCacheCallBack, + (Datum) 0); + CacheRegisterSyscacheCallback(CASTSOURCETARGET, + InvalidateOprCacheCallBack, + (Datum) 0); + } + + /* Look for an existing entry */ + oprentry = (OprCacheEntry *) hash_search(OprCacheHash, + (void *) key, + HASH_FIND, NULL); + if (oprentry == NULL) + return InvalidOid; + + return oprentry->opr_oid; +} + +/* + * make_oper_cache_entry + * + * Insert a cache entry for the given key. + */ +static void +make_oper_cache_entry(OprCacheKey *key, Oid opr_oid) +{ + OprCacheEntry *oprentry; + + Assert(OprCacheHash != NULL); + + oprentry = (OprCacheEntry *) hash_search(OprCacheHash, + (void *) key, + HASH_ENTER, NULL); + oprentry->opr_oid = opr_oid; +} + +/* + * Callback for pg_operator and pg_cast inval events + */ +static void +InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) +{ + HASH_SEQ_STATUS status; + OprCacheEntry *hentry; + + Assert(OprCacheHash != NULL); + + /* Currently we just flush all entries; hard to be smarter ... */ + hash_seq_init(&status, OprCacheHash); + + while ((hentry = (OprCacheEntry *) hash_seq_search(&status)) != NULL) + { + if (hash_search(OprCacheHash, + (void *) &hentry->key, + HASH_REMOVE, NULL) == NULL) + elog(ERROR, "hash table corrupted"); + } +} |