summaryrefslogtreecommitdiffstats
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.c1066
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");
+ }
+}