LCOV - code coverage report
Current view: top level - src/backend/parser - parse_oper.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 285 312 91.3 %
Date: 2017-09-29 15:12:54 Functions: 19 20 95.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * parse_oper.c
       4             :  *      handle operator things for parser
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/parser/parse_oper.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/htup_details.h"
      19             : #include "catalog/pg_operator.h"
      20             : #include "catalog/pg_type.h"
      21             : #include "lib/stringinfo.h"
      22             : #include "nodes/nodeFuncs.h"
      23             : #include "parser/parse_coerce.h"
      24             : #include "parser/parse_func.h"
      25             : #include "parser/parse_oper.h"
      26             : #include "parser/parse_type.h"
      27             : #include "utils/builtins.h"
      28             : #include "utils/inval.h"
      29             : #include "utils/lsyscache.h"
      30             : #include "utils/syscache.h"
      31             : #include "utils/typcache.h"
      32             : 
      33             : 
      34             : /*
      35             :  * The lookup key for the operator lookaside hash table.  Unused bits must be
      36             :  * zeroes to ensure hashing works consistently --- in particular, oprname
      37             :  * must be zero-padded and any unused entries in search_path must be zero.
      38             :  *
      39             :  * search_path contains the actual search_path with which the entry was
      40             :  * derived (minus temp namespace if any), or else the single specified
      41             :  * schema OID if we are looking up an explicitly-qualified operator name.
      42             :  *
      43             :  * search_path has to be fixed-length since the hashtable code insists on
      44             :  * fixed-size keys.  If your search path is longer than that, we just punt
      45             :  * and don't cache anything.
      46             :  */
      47             : 
      48             : /* If your search_path is longer than this, sucks to be you ... */
      49             : #define MAX_CACHED_PATH_LEN     16
      50             : 
      51             : typedef struct OprCacheKey
      52             : {
      53             :     char        oprname[NAMEDATALEN];
      54             :     Oid         left_arg;       /* Left input OID, or 0 if prefix op */
      55             :     Oid         right_arg;      /* Right input OID, or 0 if postfix op */
      56             :     Oid         search_path[MAX_CACHED_PATH_LEN];
      57             : } OprCacheKey;
      58             : 
      59             : typedef struct OprCacheEntry
      60             : {
      61             :     /* the hash lookup key MUST BE FIRST */
      62             :     OprCacheKey key;
      63             : 
      64             :     Oid         opr_oid;        /* OID of the resolved operator */
      65             : } OprCacheEntry;
      66             : 
      67             : 
      68             : static Oid  binary_oper_exact(List *opname, Oid arg1, Oid arg2);
      69             : static FuncDetailCode oper_select_candidate(int nargs,
      70             :                       Oid *input_typeids,
      71             :                       FuncCandidateList candidates,
      72             :                       Oid *operOid);
      73             : static const char *op_signature_string(List *op, char oprkind,
      74             :                     Oid arg1, Oid arg2);
      75             : static void op_error(ParseState *pstate, List *op, char oprkind,
      76             :          Oid arg1, Oid arg2,
      77             :          FuncDetailCode fdresult, int location);
      78             : static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key,
      79             :                     List *opname, Oid ltypeId, Oid rtypeId,
      80             :                     int location);
      81             : static Oid  find_oper_cache_entry(OprCacheKey *key);
      82             : static void make_oper_cache_entry(OprCacheKey *key, Oid opr_oid);
      83             : static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
      84             : 
      85             : 
      86             : /*
      87             :  * LookupOperName
      88             :  *      Given a possibly-qualified operator name and exact input datatypes,
      89             :  *      look up the operator.
      90             :  *
      91             :  * Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for
      92             :  * a postfix op.
      93             :  *
      94             :  * If the operator name is not schema-qualified, it is sought in the current
      95             :  * namespace search path.
      96             :  *
      97             :  * If the operator is not found, we return InvalidOid if noError is true,
      98             :  * else raise an error.  pstate and location are used only to report the
      99             :  * error position; pass NULL/-1 if not available.
     100             :  */
     101             : Oid
     102         105 : LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright,
     103             :                bool noError, int location)
     104             : {
     105             :     Oid         result;
     106             : 
     107         105 :     result = OpernameGetOprid(opername, oprleft, oprright);
     108         105 :     if (OidIsValid(result))
     109          86 :         return result;
     110             : 
     111             :     /* we don't use op_error here because only an exact match is wanted */
     112          19 :     if (!noError)
     113             :     {
     114             :         char        oprkind;
     115             : 
     116           3 :         if (!OidIsValid(oprleft))
     117           0 :             oprkind = 'l';
     118           3 :         else if (!OidIsValid(oprright))
     119           1 :             oprkind = 'r';
     120             :         else
     121           2 :             oprkind = 'b';
     122             : 
     123           3 :         ereport(ERROR,
     124             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     125             :                  errmsg("operator does not exist: %s",
     126             :                         op_signature_string(opername, oprkind,
     127             :                                             oprleft, oprright)),
     128             :                  parser_errposition(pstate, location)));
     129             :     }
     130             : 
     131          16 :     return InvalidOid;
     132             : }
     133             : 
     134             : /*
     135             :  * LookupOperWithArgs
     136             :  *      Like LookupOperName, but the argument types are specified by
     137             :  *      a ObjectWithArg node.
     138             :  */
     139             : Oid
     140          79 : LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
     141             : {
     142             :     TypeName   *oprleft,
     143             :                *oprright;
     144             :     Oid         leftoid,
     145             :                 rightoid;
     146             : 
     147          79 :     Assert(list_length(oper->objargs) == 2);
     148          79 :     oprleft = linitial(oper->objargs);
     149          79 :     oprright = lsecond(oper->objargs);
     150             : 
     151          79 :     if (oprleft == NULL)
     152           2 :         leftoid = InvalidOid;
     153             :     else
     154          77 :         leftoid = LookupTypeNameOid(NULL, oprleft, noError);
     155             : 
     156          78 :     if (oprright == NULL)
     157           1 :         rightoid = InvalidOid;
     158             :     else
     159          77 :         rightoid = LookupTypeNameOid(NULL, oprright, noError);
     160             : 
     161          77 :     return LookupOperName(NULL, oper->objname, leftoid, rightoid,
     162             :                           noError, -1);
     163             : }
     164             : 
     165             : /*
     166             :  * get_sort_group_operators - get default sorting/grouping operators for type
     167             :  *
     168             :  * We fetch the "<", "=", and ">" operators all at once to reduce lookup
     169             :  * overhead (knowing that most callers will be interested in at least two).
     170             :  * However, a given datatype might have only an "=" operator, if it is
     171             :  * hashable but not sortable.  (Other combinations of present and missing
     172             :  * operators shouldn't happen, unless the system catalogs are messed up.)
     173             :  *
     174             :  * If an operator is missing and the corresponding needXX flag is true,
     175             :  * throw a standard error message, else return InvalidOid.
     176             :  *
     177             :  * In addition to the operator OIDs themselves, this function can identify
     178             :  * whether the "=" operator is hashable.
     179             :  *
     180             :  * Callers can pass NULL pointers for any results they don't care to get.
     181             :  *
     182             :  * Note: the results are guaranteed to be exact or binary-compatible matches,
     183             :  * since most callers are not prepared to cope with adding any run-time type
     184             :  * coercion steps.
     185             :  */
     186             : void
     187        5633 : get_sort_group_operators(Oid argtype,
     188             :                          bool needLT, bool needEQ, bool needGT,
     189             :                          Oid *ltOpr, Oid *eqOpr, Oid *gtOpr,
     190             :                          bool *isHashable)
     191             : {
     192             :     TypeCacheEntry *typentry;
     193             :     int         cache_flags;
     194             :     Oid         lt_opr;
     195             :     Oid         eq_opr;
     196             :     Oid         gt_opr;
     197             :     bool        hashable;
     198             : 
     199             :     /*
     200             :      * Look up the operators using the type cache.
     201             :      *
     202             :      * Note: the search algorithm used by typcache.c ensures that the results
     203             :      * are consistent, ie all from matching opclasses.
     204             :      */
     205        5633 :     if (isHashable != NULL)
     206        4418 :         cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR |
     207             :             TYPECACHE_HASH_PROC;
     208             :     else
     209        1215 :         cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR;
     210             : 
     211        5633 :     typentry = lookup_type_cache(argtype, cache_flags);
     212        5633 :     lt_opr = typentry->lt_opr;
     213        5633 :     eq_opr = typentry->eq_opr;
     214        5633 :     gt_opr = typentry->gt_opr;
     215        5633 :     hashable = OidIsValid(typentry->hash_proc);
     216             : 
     217             :     /* Report errors if needed */
     218        5633 :     if ((needLT && !OidIsValid(lt_opr)) ||
     219         115 :         (needGT && !OidIsValid(gt_opr)))
     220           1 :         ereport(ERROR,
     221             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     222             :                  errmsg("could not identify an ordering operator for type %s",
     223             :                         format_type_be(argtype)),
     224             :                  errhint("Use an explicit ordering operator or modify the query.")));
     225        5632 :     if (needEQ && !OidIsValid(eq_opr))
     226           0 :         ereport(ERROR,
     227             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     228             :                  errmsg("could not identify an equality operator for type %s",
     229             :                         format_type_be(argtype))));
     230             : 
     231             :     /* Return results as needed */
     232        5632 :     if (ltOpr)
     233        5517 :         *ltOpr = lt_opr;
     234        5632 :     if (eqOpr)
     235        5632 :         *eqOpr = eq_opr;
     236        5632 :     if (gtOpr)
     237         115 :         *gtOpr = gt_opr;
     238        5632 :     if (isHashable)
     239        4417 :         *isHashable = hashable;
     240        5632 : }
     241             : 
     242             : 
     243             : /* given operator tuple, return the operator OID */
     244             : Oid
     245       23013 : oprid(Operator op)
     246             : {
     247       23013 :     return HeapTupleGetOid(op);
     248             : }
     249             : 
     250             : /* given operator tuple, return the underlying function's OID */
     251             : Oid
     252           0 : oprfuncid(Operator op)
     253             : {
     254           0 :     Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(op);
     255             : 
     256           0 :     return pgopform->oprcode;
     257             : }
     258             : 
     259             : 
     260             : /* binary_oper_exact()
     261             :  * Check for an "exact" match to the specified operand types.
     262             :  *
     263             :  * If one operand is an unknown literal, assume it should be taken to be
     264             :  * the same type as the other operand for this purpose.  Also, consider
     265             :  * the possibility that the other operand is a domain type that needs to
     266             :  * be reduced to its base type to find an "exact" match.
     267             :  */
     268             : static Oid
     269        2969 : binary_oper_exact(List *opname, Oid arg1, Oid arg2)
     270             : {
     271             :     Oid         result;
     272        2969 :     bool        was_unknown = false;
     273             : 
     274             :     /* Unspecified type for one of the arguments? then use the other */
     275        2969 :     if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid))
     276             :     {
     277          95 :         arg1 = arg2;
     278          95 :         was_unknown = true;
     279             :     }
     280        2874 :     else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid))
     281             :     {
     282         688 :         arg2 = arg1;
     283         688 :         was_unknown = true;
     284             :     }
     285             : 
     286        2969 :     result = OpernameGetOprid(opname, arg1, arg2);
     287        2969 :     if (OidIsValid(result))
     288        2204 :         return result;
     289             : 
     290         765 :     if (was_unknown)
     291             :     {
     292             :         /* arg1 and arg2 are the same here, need only look at arg1 */
     293         253 :         Oid         basetype = getBaseType(arg1);
     294             : 
     295         253 :         if (basetype != arg1)
     296             :         {
     297          12 :             result = OpernameGetOprid(opname, basetype, basetype);
     298          12 :             if (OidIsValid(result))
     299           0 :                 return result;
     300             :         }
     301             :     }
     302             : 
     303         765 :     return InvalidOid;
     304             : }
     305             : 
     306             : 
     307             : /* oper_select_candidate()
     308             :  *      Given the input argtype array and one or more candidates
     309             :  *      for the operator, attempt to resolve the conflict.
     310             :  *
     311             :  * Returns FUNCDETAIL_NOTFOUND, FUNCDETAIL_MULTIPLE, or FUNCDETAIL_NORMAL.
     312             :  * In the success case the Oid of the best candidate is stored in *operOid.
     313             :  *
     314             :  * Note that the caller has already determined that there is no candidate
     315             :  * exactly matching the input argtype(s).  Incompatible candidates are not yet
     316             :  * pruned away, however.
     317             :  */
     318             : static FuncDetailCode
     319         769 : oper_select_candidate(int nargs,
     320             :                       Oid *input_typeids,
     321             :                       FuncCandidateList candidates,
     322             :                       Oid *operOid) /* output argument */
     323             : {
     324             :     int         ncandidates;
     325             : 
     326             :     /*
     327             :      * Delete any candidates that cannot actually accept the given input
     328             :      * types, whether directly or by coercion.
     329             :      */
     330         769 :     ncandidates = func_match_argtypes(nargs, input_typeids,
     331             :                                       candidates, &candidates);
     332             : 
     333             :     /* Done if no candidate or only one candidate survives */
     334         769 :     if (ncandidates == 0)
     335             :     {
     336           7 :         *operOid = InvalidOid;
     337           7 :         return FUNCDETAIL_NOTFOUND;
     338             :     }
     339         762 :     if (ncandidates == 1)
     340             :     {
     341         458 :         *operOid = candidates->oid;
     342         458 :         return FUNCDETAIL_NORMAL;
     343             :     }
     344             : 
     345             :     /*
     346             :      * Use the same heuristics as for ambiguous functions to resolve the
     347             :      * conflict.
     348             :      */
     349         304 :     candidates = func_select_candidate(nargs, input_typeids, candidates);
     350             : 
     351         304 :     if (candidates)
     352             :     {
     353         303 :         *operOid = candidates->oid;
     354         303 :         return FUNCDETAIL_NORMAL;
     355             :     }
     356             : 
     357           1 :     *operOid = InvalidOid;
     358           1 :     return FUNCDETAIL_MULTIPLE; /* failed to select a best candidate */
     359             : }
     360             : 
     361             : 
     362             : /* oper() -- search for a binary operator
     363             :  * Given operator name, types of arg1 and arg2, return oper struct.
     364             :  *
     365             :  * IMPORTANT: the returned operator (if any) is only promised to be
     366             :  * coercion-compatible with the input datatypes.  Do not use this if
     367             :  * you need an exact- or binary-compatible match; see compatible_oper.
     368             :  *
     369             :  * If no matching operator found, return NULL if noError is true,
     370             :  * raise an error if it is false.  pstate and location are used only to report
     371             :  * the error position; pass NULL/-1 if not available.
     372             :  *
     373             :  * NOTE: on success, the returned object is a syscache entry.  The caller
     374             :  * must ReleaseSysCache() the entry when done with it.
     375             :  */
     376             : Operator
     377       22929 : oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
     378             :      bool noError, int location)
     379             : {
     380             :     Oid         operOid;
     381             :     OprCacheKey key;
     382             :     bool        key_ok;
     383       22929 :     FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
     384       22929 :     HeapTuple   tup = NULL;
     385             : 
     386             :     /*
     387             :      * Try to find the mapping in the lookaside cache.
     388             :      */
     389       22929 :     key_ok = make_oper_cache_key(pstate, &key, opname, ltypeId, rtypeId, location);
     390             : 
     391       22929 :     if (key_ok)
     392             :     {
     393       22929 :         operOid = find_oper_cache_entry(&key);
     394       22929 :         if (OidIsValid(operOid))
     395             :         {
     396       19960 :             tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     397       19960 :             if (HeapTupleIsValid(tup))
     398       19960 :                 return (Operator) tup;
     399             :         }
     400             :     }
     401             : 
     402             :     /*
     403             :      * First try for an "exact" match.
     404             :      */
     405        2969 :     operOid = binary_oper_exact(opname, ltypeId, rtypeId);
     406        2969 :     if (!OidIsValid(operOid))
     407             :     {
     408             :         /*
     409             :          * Otherwise, search for the most suitable candidate.
     410             :          */
     411             :         FuncCandidateList clist;
     412             : 
     413             :         /* Get binary operators of given name */
     414         765 :         clist = OpernameGetCandidates(opname, 'b', false);
     415             : 
     416             :         /* No operators found? Then fail... */
     417         765 :         if (clist != NULL)
     418             :         {
     419             :             /*
     420             :              * Unspecified type for one of the arguments? then use the other
     421             :              * (XXX this is probably dead code?)
     422             :              */
     423             :             Oid         inputOids[2];
     424             : 
     425         765 :             if (rtypeId == InvalidOid)
     426           0 :                 rtypeId = ltypeId;
     427         765 :             else if (ltypeId == InvalidOid)
     428           0 :                 ltypeId = rtypeId;
     429         765 :             inputOids[0] = ltypeId;
     430         765 :             inputOids[1] = rtypeId;
     431         765 :             fdresult = oper_select_candidate(2, inputOids, clist, &operOid);
     432             :         }
     433             :     }
     434             : 
     435        2969 :     if (OidIsValid(operOid))
     436        2961 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     437             : 
     438        2969 :     if (HeapTupleIsValid(tup))
     439             :     {
     440        2961 :         if (key_ok)
     441        2961 :             make_oper_cache_entry(&key, operOid);
     442             :     }
     443           8 :     else if (!noError)
     444           8 :         op_error(pstate, opname, 'b', ltypeId, rtypeId, fdresult, location);
     445             : 
     446        2961 :     return (Operator) tup;
     447             : }
     448             : 
     449             : /* compatible_oper()
     450             :  *  given an opname and input datatypes, find a compatible binary operator
     451             :  *
     452             :  *  This is tighter than oper() because it will not return an operator that
     453             :  *  requires coercion of the input datatypes (but binary-compatible operators
     454             :  *  are accepted).  Otherwise, the semantics are the same.
     455             :  */
     456             : Operator
     457          44 : compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2,
     458             :                 bool noError, int location)
     459             : {
     460             :     Operator    optup;
     461             :     Form_pg_operator opform;
     462             : 
     463             :     /* oper() will find the best available match */
     464          44 :     optup = oper(pstate, op, arg1, arg2, noError, location);
     465          44 :     if (optup == (Operator) NULL)
     466           0 :         return (Operator) NULL; /* must be noError case */
     467             : 
     468             :     /* but is it good enough? */
     469          44 :     opform = (Form_pg_operator) GETSTRUCT(optup);
     470          88 :     if (IsBinaryCoercible(arg1, opform->oprleft) &&
     471          44 :         IsBinaryCoercible(arg2, opform->oprright))
     472          44 :         return optup;
     473             : 
     474             :     /* nope... */
     475           0 :     ReleaseSysCache(optup);
     476             : 
     477           0 :     if (!noError)
     478           0 :         ereport(ERROR,
     479             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     480             :                  errmsg("operator requires run-time type coercion: %s",
     481             :                         op_signature_string(op, 'b', arg1, arg2)),
     482             :                  parser_errposition(pstate, location)));
     483             : 
     484           0 :     return (Operator) NULL;
     485             : }
     486             : 
     487             : /* compatible_oper_opid() -- get OID of a binary operator
     488             :  *
     489             :  * This is a convenience routine that extracts only the operator OID
     490             :  * from the result of compatible_oper().  InvalidOid is returned if the
     491             :  * lookup fails and noError is true.
     492             :  */
     493             : Oid
     494          44 : compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
     495             : {
     496             :     Operator    optup;
     497             :     Oid         result;
     498             : 
     499          44 :     optup = compatible_oper(NULL, op, arg1, arg2, noError, -1);
     500          44 :     if (optup != NULL)
     501             :     {
     502          44 :         result = oprid(optup);
     503          44 :         ReleaseSysCache(optup);
     504          44 :         return result;
     505             :     }
     506           0 :     return InvalidOid;
     507             : }
     508             : 
     509             : 
     510             : /* right_oper() -- search for a unary right operator (postfix operator)
     511             :  * Given operator name and type of arg, return oper struct.
     512             :  *
     513             :  * IMPORTANT: the returned operator (if any) is only promised to be
     514             :  * coercion-compatible with the input datatype.  Do not use this if
     515             :  * you need an exact- or binary-compatible match.
     516             :  *
     517             :  * If no matching operator found, return NULL if noError is true,
     518             :  * raise an error if it is false.  pstate and location are used only to report
     519             :  * the error position; pass NULL/-1 if not available.
     520             :  *
     521             :  * NOTE: on success, the returned object is a syscache entry.  The caller
     522             :  * must ReleaseSysCache() the entry when done with it.
     523             :  */
     524             : Operator
     525           1 : right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
     526             : {
     527             :     Oid         operOid;
     528             :     OprCacheKey key;
     529             :     bool        key_ok;
     530           1 :     FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
     531           1 :     HeapTuple   tup = NULL;
     532             : 
     533             :     /*
     534             :      * Try to find the mapping in the lookaside cache.
     535             :      */
     536           1 :     key_ok = make_oper_cache_key(pstate, &key, op, arg, InvalidOid, location);
     537             : 
     538           1 :     if (key_ok)
     539             :     {
     540           1 :         operOid = find_oper_cache_entry(&key);
     541           1 :         if (OidIsValid(operOid))
     542             :         {
     543           0 :             tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     544           0 :             if (HeapTupleIsValid(tup))
     545           0 :                 return (Operator) tup;
     546             :         }
     547             :     }
     548             : 
     549             :     /*
     550             :      * First try for an "exact" match.
     551             :      */
     552           1 :     operOid = OpernameGetOprid(op, arg, InvalidOid);
     553           1 :     if (!OidIsValid(operOid))
     554             :     {
     555             :         /*
     556             :          * Otherwise, search for the most suitable candidate.
     557             :          */
     558             :         FuncCandidateList clist;
     559             : 
     560             :         /* Get postfix operators of given name */
     561           1 :         clist = OpernameGetCandidates(op, 'r', false);
     562             : 
     563             :         /* No operators found? Then fail... */
     564           1 :         if (clist != NULL)
     565             :         {
     566             :             /*
     567             :              * We must run oper_select_candidate even if only one candidate,
     568             :              * otherwise we may falsely return a non-type-compatible operator.
     569             :              */
     570           1 :             fdresult = oper_select_candidate(1, &arg, clist, &operOid);
     571             :         }
     572             :     }
     573             : 
     574           1 :     if (OidIsValid(operOid))
     575           1 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     576             : 
     577           1 :     if (HeapTupleIsValid(tup))
     578             :     {
     579           1 :         if (key_ok)
     580           1 :             make_oper_cache_entry(&key, operOid);
     581             :     }
     582           0 :     else if (!noError)
     583           0 :         op_error(pstate, op, 'r', arg, InvalidOid, fdresult, location);
     584             : 
     585           1 :     return (Operator) tup;
     586             : }
     587             : 
     588             : 
     589             : /* left_oper() -- search for a unary left operator (prefix operator)
     590             :  * Given operator name and type of arg, return oper struct.
     591             :  *
     592             :  * IMPORTANT: the returned operator (if any) is only promised to be
     593             :  * coercion-compatible with the input datatype.  Do not use this if
     594             :  * you need an exact- or binary-compatible match.
     595             :  *
     596             :  * If no matching operator found, return NULL if noError is true,
     597             :  * raise an error if it is false.  pstate and location are used only to report
     598             :  * the error position; pass NULL/-1 if not available.
     599             :  *
     600             :  * NOTE: on success, the returned object is a syscache entry.  The caller
     601             :  * must ReleaseSysCache() the entry when done with it.
     602             :  */
     603             : Operator
     604          92 : left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
     605             : {
     606             :     Oid         operOid;
     607             :     OprCacheKey key;
     608             :     bool        key_ok;
     609          92 :     FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
     610          92 :     HeapTuple   tup = NULL;
     611             : 
     612             :     /*
     613             :      * Try to find the mapping in the lookaside cache.
     614             :      */
     615          92 :     key_ok = make_oper_cache_key(pstate, &key, op, InvalidOid, arg, location);
     616             : 
     617          92 :     if (key_ok)
     618             :     {
     619          92 :         operOid = find_oper_cache_entry(&key);
     620          92 :         if (OidIsValid(operOid))
     621             :         {
     622          43 :             tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     623          43 :             if (HeapTupleIsValid(tup))
     624          43 :                 return (Operator) tup;
     625             :         }
     626             :     }
     627             : 
     628             :     /*
     629             :      * First try for an "exact" match.
     630             :      */
     631          49 :     operOid = OpernameGetOprid(op, InvalidOid, arg);
     632          49 :     if (!OidIsValid(operOid))
     633             :     {
     634             :         /*
     635             :          * Otherwise, search for the most suitable candidate.
     636             :          */
     637             :         FuncCandidateList clist;
     638             : 
     639             :         /* Get prefix operators of given name */
     640           3 :         clist = OpernameGetCandidates(op, 'l', false);
     641             : 
     642             :         /* No operators found? Then fail... */
     643           3 :         if (clist != NULL)
     644             :         {
     645             :             /*
     646             :              * The returned list has args in the form (0, oprright). Move the
     647             :              * useful data into args[0] to keep oper_select_candidate simple.
     648             :              * XXX we are assuming here that we may scribble on the list!
     649             :              */
     650             :             FuncCandidateList clisti;
     651             : 
     652          13 :             for (clisti = clist; clisti != NULL; clisti = clisti->next)
     653             :             {
     654          10 :                 clisti->args[0] = clisti->args[1];
     655             :             }
     656             : 
     657             :             /*
     658             :              * We must run oper_select_candidate even if only one candidate,
     659             :              * otherwise we may falsely return a non-type-compatible operator.
     660             :              */
     661           3 :             fdresult = oper_select_candidate(1, &arg, clist, &operOid);
     662             :         }
     663             :     }
     664             : 
     665          49 :     if (OidIsValid(operOid))
     666          49 :         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     667             : 
     668          49 :     if (HeapTupleIsValid(tup))
     669             :     {
     670          49 :         if (key_ok)
     671          49 :             make_oper_cache_entry(&key, operOid);
     672             :     }
     673           0 :     else if (!noError)
     674           0 :         op_error(pstate, op, 'l', InvalidOid, arg, fdresult, location);
     675             : 
     676          49 :     return (Operator) tup;
     677             : }
     678             : 
     679             : /*
     680             :  * op_signature_string
     681             :  *      Build a string representing an operator name, including arg type(s).
     682             :  *      The result is something like "integer + integer".
     683             :  *
     684             :  * This is typically used in the construction of operator-not-found error
     685             :  * messages.
     686             :  */
     687             : static const char *
     688          11 : op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2)
     689             : {
     690             :     StringInfoData argbuf;
     691             : 
     692          11 :     initStringInfo(&argbuf);
     693             : 
     694          11 :     if (oprkind != 'l')
     695          11 :         appendStringInfo(&argbuf, "%s ", format_type_be(arg1));
     696             : 
     697          11 :     appendStringInfoString(&argbuf, NameListToString(op));
     698             : 
     699          11 :     if (oprkind != 'r')
     700          10 :         appendStringInfo(&argbuf, " %s", format_type_be(arg2));
     701             : 
     702          11 :     return argbuf.data;         /* return palloc'd string buffer */
     703             : }
     704             : 
     705             : /*
     706             :  * op_error - utility routine to complain about an unresolvable operator
     707             :  */
     708             : static void
     709           8 : op_error(ParseState *pstate, List *op, char oprkind,
     710             :          Oid arg1, Oid arg2,
     711             :          FuncDetailCode fdresult, int location)
     712             : {
     713           8 :     if (fdresult == FUNCDETAIL_MULTIPLE)
     714           1 :         ereport(ERROR,
     715             :                 (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
     716             :                  errmsg("operator is not unique: %s",
     717             :                         op_signature_string(op, oprkind, arg1, arg2)),
     718             :                  errhint("Could not choose a best candidate operator. "
     719             :                          "You might need to add explicit type casts."),
     720             :                  parser_errposition(pstate, location)));
     721             :     else
     722           7 :         ereport(ERROR,
     723             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     724             :                  errmsg("operator does not exist: %s",
     725             :                         op_signature_string(op, oprkind, arg1, arg2)),
     726             :                  (!arg1 || !arg2) ?
     727             :                  errhint("No operator matches the given name and argument type. "
     728             :                          "You might need to add an explicit type cast.") :
     729             :                  errhint("No operator matches the given name and argument types. "
     730             :                          "You might need to add explicit type casts."),
     731             :                  parser_errposition(pstate, location)));
     732             : }
     733             : 
     734             : /*
     735             :  * make_op()
     736             :  *      Operator expression construction.
     737             :  *
     738             :  * Transform operator expression ensuring type compatibility.
     739             :  * This is where some type conversion happens.
     740             :  *
     741             :  * last_srf should be a copy of pstate->p_last_srf from just before we
     742             :  * started transforming the operator's arguments; this is used for nested-SRF
     743             :  * detection.  If the caller will throw an error anyway for a set-returning
     744             :  * expression, it's okay to cheat and just pass pstate->p_last_srf.
     745             :  */
     746             : Expr *
     747       19671 : make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
     748             :         Node *last_srf, int location)
     749             : {
     750             :     Oid         ltypeId,
     751             :                 rtypeId;
     752             :     Operator    tup;
     753             :     Form_pg_operator opform;
     754             :     Oid         actual_arg_types[2];
     755             :     Oid         declared_arg_types[2];
     756             :     int         nargs;
     757             :     List       *args;
     758             :     Oid         rettype;
     759             :     OpExpr     *result;
     760             : 
     761             :     /* Select the operator */
     762       19671 :     if (rtree == NULL)
     763             :     {
     764             :         /* right operator */
     765           1 :         ltypeId = exprType(ltree);
     766           1 :         rtypeId = InvalidOid;
     767           1 :         tup = right_oper(pstate, opname, ltypeId, false, location);
     768             :     }
     769       19670 :     else if (ltree == NULL)
     770             :     {
     771             :         /* left operator */
     772          92 :         rtypeId = exprType(rtree);
     773          92 :         ltypeId = InvalidOid;
     774          92 :         tup = left_oper(pstate, opname, rtypeId, false, location);
     775             :     }
     776             :     else
     777             :     {
     778             :         /* otherwise, binary operator */
     779       19578 :         ltypeId = exprType(ltree);
     780       19578 :         rtypeId = exprType(rtree);
     781       19578 :         tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
     782             :     }
     783             : 
     784       19663 :     opform = (Form_pg_operator) GETSTRUCT(tup);
     785             : 
     786             :     /* Check it's not a shell */
     787       19663 :     if (!RegProcedureIsValid(opform->oprcode))
     788           0 :         ereport(ERROR,
     789             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     790             :                  errmsg("operator is only a shell: %s",
     791             :                         op_signature_string(opname,
     792             :                                             opform->oprkind,
     793             :                                             opform->oprleft,
     794             :                                             opform->oprright)),
     795             :                  parser_errposition(pstate, location)));
     796             : 
     797             :     /* Do typecasting and build the expression tree */
     798       19663 :     if (rtree == NULL)
     799             :     {
     800             :         /* right operator */
     801           1 :         args = list_make1(ltree);
     802           1 :         actual_arg_types[0] = ltypeId;
     803           1 :         declared_arg_types[0] = opform->oprleft;
     804           1 :         nargs = 1;
     805             :     }
     806       19662 :     else if (ltree == NULL)
     807             :     {
     808             :         /* left operator */
     809          92 :         args = list_make1(rtree);
     810          92 :         actual_arg_types[0] = rtypeId;
     811          92 :         declared_arg_types[0] = opform->oprright;
     812          92 :         nargs = 1;
     813             :     }
     814             :     else
     815             :     {
     816             :         /* otherwise, binary operator */
     817       19570 :         args = list_make2(ltree, rtree);
     818       19570 :         actual_arg_types[0] = ltypeId;
     819       19570 :         actual_arg_types[1] = rtypeId;
     820       19570 :         declared_arg_types[0] = opform->oprleft;
     821       19570 :         declared_arg_types[1] = opform->oprright;
     822       19570 :         nargs = 2;
     823             :     }
     824             : 
     825             :     /*
     826             :      * enforce consistency with polymorphic argument and return types,
     827             :      * possibly adjusting return type or declared_arg_types (which will be
     828             :      * used as the cast destination by make_fn_arguments)
     829             :      */
     830       19663 :     rettype = enforce_generic_type_consistency(actual_arg_types,
     831             :                                                declared_arg_types,
     832             :                                                nargs,
     833             :                                                opform->oprresult,
     834             :                                                false);
     835             : 
     836             :     /* perform the necessary typecasting of arguments */
     837       19663 :     make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
     838             : 
     839             :     /* and build the expression node */
     840       19663 :     result = makeNode(OpExpr);
     841       19663 :     result->opno = oprid(tup);
     842       19663 :     result->opfuncid = opform->oprcode;
     843       19663 :     result->opresulttype = rettype;
     844       19663 :     result->opretset = get_func_retset(opform->oprcode);
     845             :     /* opcollid and inputcollid will be set by parse_collate.c */
     846       19663 :     result->args = args;
     847       19663 :     result->location = location;
     848             : 
     849             :     /* if it returns a set, check that's OK */
     850       19663 :     if (result->opretset)
     851             :     {
     852           1 :         check_srf_call_placement(pstate, last_srf, location);
     853             :         /* ... and remember it for error checks at higher levels */
     854           1 :         pstate->p_last_srf = (Node *) result;
     855             :     }
     856             : 
     857       19663 :     ReleaseSysCache(tup);
     858             : 
     859       19663 :     return (Expr *) result;
     860             : }
     861             : 
     862             : /*
     863             :  * make_scalar_array_op()
     864             :  *      Build expression tree for "scalar op ANY/ALL (array)" construct.
     865             :  */
     866             : Expr *
     867         512 : make_scalar_array_op(ParseState *pstate, List *opname,
     868             :                      bool useOr,
     869             :                      Node *ltree, Node *rtree,
     870             :                      int location)
     871             : {
     872             :     Oid         ltypeId,
     873             :                 rtypeId,
     874             :                 atypeId,
     875             :                 res_atypeId;
     876             :     Operator    tup;
     877             :     Form_pg_operator opform;
     878             :     Oid         actual_arg_types[2];
     879             :     Oid         declared_arg_types[2];
     880             :     List       *args;
     881             :     Oid         rettype;
     882             :     ScalarArrayOpExpr *result;
     883             : 
     884         512 :     ltypeId = exprType(ltree);
     885         512 :     atypeId = exprType(rtree);
     886             : 
     887             :     /*
     888             :      * The right-hand input of the operator will be the element type of the
     889             :      * array.  However, if we currently have just an untyped literal on the
     890             :      * right, stay with that and hope we can resolve the operator.
     891             :      */
     892         512 :     if (atypeId == UNKNOWNOID)
     893          18 :         rtypeId = UNKNOWNOID;
     894             :     else
     895             :     {
     896         494 :         rtypeId = get_base_element_type(atypeId);
     897         494 :         if (!OidIsValid(rtypeId))
     898           1 :             ereport(ERROR,
     899             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     900             :                      errmsg("op ANY/ALL (array) requires array on right side"),
     901             :                      parser_errposition(pstate, location)));
     902             :     }
     903             : 
     904             :     /* Now resolve the operator */
     905         511 :     tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
     906         511 :     opform = (Form_pg_operator) GETSTRUCT(tup);
     907             : 
     908             :     /* Check it's not a shell */
     909         511 :     if (!RegProcedureIsValid(opform->oprcode))
     910           0 :         ereport(ERROR,
     911             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     912             :                  errmsg("operator is only a shell: %s",
     913             :                         op_signature_string(opname,
     914             :                                             opform->oprkind,
     915             :                                             opform->oprleft,
     916             :                                             opform->oprright)),
     917             :                  parser_errposition(pstate, location)));
     918             : 
     919         511 :     args = list_make2(ltree, rtree);
     920         511 :     actual_arg_types[0] = ltypeId;
     921         511 :     actual_arg_types[1] = rtypeId;
     922         511 :     declared_arg_types[0] = opform->oprleft;
     923         511 :     declared_arg_types[1] = opform->oprright;
     924             : 
     925             :     /*
     926             :      * enforce consistency with polymorphic argument and return types,
     927             :      * possibly adjusting return type or declared_arg_types (which will be
     928             :      * used as the cast destination by make_fn_arguments)
     929             :      */
     930         511 :     rettype = enforce_generic_type_consistency(actual_arg_types,
     931             :                                                declared_arg_types,
     932             :                                                2,
     933             :                                                opform->oprresult,
     934             :                                                false);
     935             : 
     936             :     /*
     937             :      * Check that operator result is boolean
     938             :      */
     939         511 :     if (rettype != BOOLOID)
     940           1 :         ereport(ERROR,
     941             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     942             :                  errmsg("op ANY/ALL (array) requires operator to yield boolean"),
     943             :                  parser_errposition(pstate, location)));
     944         510 :     if (get_func_retset(opform->oprcode))
     945           0 :         ereport(ERROR,
     946             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     947             :                  errmsg("op ANY/ALL (array) requires operator not to return a set"),
     948             :                  parser_errposition(pstate, location)));
     949             : 
     950             :     /*
     951             :      * Now switch back to the array type on the right, arranging for any
     952             :      * needed cast to be applied.  Beware of polymorphic operators here;
     953             :      * enforce_generic_type_consistency may or may not have replaced a
     954             :      * polymorphic type with a real one.
     955             :      */
     956         510 :     if (IsPolymorphicType(declared_arg_types[1]))
     957             :     {
     958             :         /* assume the actual array type is OK */
     959           5 :         res_atypeId = atypeId;
     960             :     }
     961             :     else
     962             :     {
     963         505 :         res_atypeId = get_array_type(declared_arg_types[1]);
     964         505 :         if (!OidIsValid(res_atypeId))
     965           0 :             ereport(ERROR,
     966             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
     967             :                      errmsg("could not find array type for data type %s",
     968             :                             format_type_be(declared_arg_types[1])),
     969             :                      parser_errposition(pstate, location)));
     970             :     }
     971         510 :     actual_arg_types[1] = atypeId;
     972         510 :     declared_arg_types[1] = res_atypeId;
     973             : 
     974             :     /* perform the necessary typecasting of arguments */
     975         510 :     make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
     976             : 
     977             :     /* and build the expression node */
     978         510 :     result = makeNode(ScalarArrayOpExpr);
     979         510 :     result->opno = oprid(tup);
     980         510 :     result->opfuncid = opform->oprcode;
     981         510 :     result->useOr = useOr;
     982             :     /* inputcollid will be set by parse_collate.c */
     983         510 :     result->args = args;
     984         510 :     result->location = location;
     985             : 
     986         510 :     ReleaseSysCache(tup);
     987             : 
     988         510 :     return (Expr *) result;
     989             : }
     990             : 
     991             : 
     992             : /*
     993             :  * Lookaside cache to speed operator lookup.  Possibly this should be in
     994             :  * a separate module under utils/cache/ ?
     995             :  *
     996             :  * The idea here is that the mapping from operator name and given argument
     997             :  * types is constant for a given search path (or single specified schema OID)
     998             :  * so long as the contents of pg_operator and pg_cast don't change.  And that
     999             :  * mapping is pretty expensive to compute, especially for ambiguous operators;
    1000             :  * this is mainly because there are a *lot* of instances of popular operator
    1001             :  * names such as "=", and we have to check each one to see which is the
    1002             :  * best match.  So once we have identified the correct mapping, we save it
    1003             :  * in a cache that need only be flushed on pg_operator or pg_cast change.
    1004             :  * (pg_cast must be considered because changes in the set of implicit casts
    1005             :  * affect the set of applicable operators for any given input datatype.)
    1006             :  *
    1007             :  * XXX in principle, ALTER TABLE ... INHERIT could affect the mapping as
    1008             :  * well, but we disregard that since there's no convenient way to find out
    1009             :  * about it, and it seems a pretty far-fetched corner-case anyway.
    1010             :  *
    1011             :  * Note: at some point it might be worth doing a similar cache for function
    1012             :  * lookups.  However, the potential gain is a lot less since (a) function
    1013             :  * names are generally not overloaded as heavily as operator names, and
    1014             :  * (b) we'd have to flush on pg_proc updates, which are probably a good
    1015             :  * deal more common than pg_operator updates.
    1016             :  */
    1017             : 
    1018             : /* The operator cache hashtable */
    1019             : static HTAB *OprCacheHash = NULL;
    1020             : 
    1021             : 
    1022             : /*
    1023             :  * make_oper_cache_key
    1024             :  *      Fill the lookup key struct given operator name and arg types.
    1025             :  *
    1026             :  * Returns TRUE if successful, FALSE if the search_path overflowed
    1027             :  * (hence no caching is possible).
    1028             :  *
    1029             :  * pstate/location are used only to report the error position; pass NULL/-1
    1030             :  * if not available.
    1031             :  */
    1032             : static bool
    1033       23022 : make_oper_cache_key(ParseState *pstate, OprCacheKey *key, List *opname,
    1034             :                     Oid ltypeId, Oid rtypeId, int location)
    1035             : {
    1036             :     char       *schemaname;
    1037             :     char       *opername;
    1038             : 
    1039             :     /* deconstruct the name list */
    1040       23022 :     DeconstructQualifiedName(opname, &schemaname, &opername);
    1041             : 
    1042             :     /* ensure zero-fill for stable hashing */
    1043       23022 :     MemSet(key, 0, sizeof(OprCacheKey));
    1044             : 
    1045             :     /* save operator name and input types into key */
    1046       23022 :     strlcpy(key->oprname, opername, NAMEDATALEN);
    1047       23022 :     key->left_arg = ltypeId;
    1048       23022 :     key->right_arg = rtypeId;
    1049             : 
    1050       23022 :     if (schemaname)
    1051             :     {
    1052             :         ParseCallbackState pcbstate;
    1053             : 
    1054             :         /* search only in exact schema given */
    1055         270 :         setup_parser_errposition_callback(&pcbstate, pstate, location);
    1056         270 :         key->search_path[0] = LookupExplicitNamespace(schemaname, false);
    1057         270 :         cancel_parser_errposition_callback(&pcbstate);
    1058             :     }
    1059             :     else
    1060             :     {
    1061             :         /* get the active search path */
    1062       22752 :         if (fetch_search_path_array(key->search_path,
    1063             :                                     MAX_CACHED_PATH_LEN) > MAX_CACHED_PATH_LEN)
    1064           0 :             return false;       /* oops, didn't fit */
    1065             :     }
    1066             : 
    1067       23022 :     return true;
    1068             : }
    1069             : 
    1070             : /*
    1071             :  * find_oper_cache_entry
    1072             :  *
    1073             :  * Look for a cache entry matching the given key.  If found, return the
    1074             :  * contained operator OID, else return InvalidOid.
    1075             :  */
    1076             : static Oid
    1077       23022 : find_oper_cache_entry(OprCacheKey *key)
    1078             : {
    1079             :     OprCacheEntry *oprentry;
    1080             : 
    1081       23022 :     if (OprCacheHash == NULL)
    1082             :     {
    1083             :         /* First time through: initialize the hash table */
    1084             :         HASHCTL     ctl;
    1085             : 
    1086         184 :         MemSet(&ctl, 0, sizeof(ctl));
    1087         184 :         ctl.keysize = sizeof(OprCacheKey);
    1088         184 :         ctl.entrysize = sizeof(OprCacheEntry);
    1089         184 :         OprCacheHash = hash_create("Operator lookup cache", 256,
    1090             :                                    &ctl, HASH_ELEM | HASH_BLOBS);
    1091             : 
    1092             :         /* Arrange to flush cache on pg_operator and pg_cast changes */
    1093         184 :         CacheRegisterSyscacheCallback(OPERNAMENSP,
    1094             :                                       InvalidateOprCacheCallBack,
    1095             :                                       (Datum) 0);
    1096         184 :         CacheRegisterSyscacheCallback(CASTSOURCETARGET,
    1097             :                                       InvalidateOprCacheCallBack,
    1098             :                                       (Datum) 0);
    1099             :     }
    1100             : 
    1101             :     /* Look for an existing entry */
    1102       23022 :     oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
    1103             :                                              (void *) key,
    1104             :                                              HASH_FIND, NULL);
    1105       23022 :     if (oprentry == NULL)
    1106        3019 :         return InvalidOid;
    1107             : 
    1108       20003 :     return oprentry->opr_oid;
    1109             : }
    1110             : 
    1111             : /*
    1112             :  * make_oper_cache_entry
    1113             :  *
    1114             :  * Insert a cache entry for the given key.
    1115             :  */
    1116             : static void
    1117        3011 : make_oper_cache_entry(OprCacheKey *key, Oid opr_oid)
    1118             : {
    1119             :     OprCacheEntry *oprentry;
    1120             : 
    1121        3011 :     Assert(OprCacheHash != NULL);
    1122             : 
    1123        3011 :     oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
    1124             :                                              (void *) key,
    1125             :                                              HASH_ENTER, NULL);
    1126        3011 :     oprentry->opr_oid = opr_oid;
    1127        3011 : }
    1128             : 
    1129             : /*
    1130             :  * Callback for pg_operator and pg_cast inval events
    1131             :  */
    1132             : static void
    1133         407 : InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
    1134             : {
    1135             :     HASH_SEQ_STATUS status;
    1136             :     OprCacheEntry *hentry;
    1137             : 
    1138         407 :     Assert(OprCacheHash != NULL);
    1139             : 
    1140             :     /* Currently we just flush all entries; hard to be smarter ... */
    1141         407 :     hash_seq_init(&status, OprCacheHash);
    1142             : 
    1143         407 :     while ((hentry = (OprCacheEntry *) hash_seq_search(&status)) != NULL)
    1144             :     {
    1145         933 :         if (hash_search(OprCacheHash,
    1146         933 :                         (void *) &hentry->key,
    1147             :                         HASH_REMOVE, NULL) == NULL)
    1148           0 :             elog(ERROR, "hash table corrupted");
    1149             :     }
    1150         407 : }

Generated by: LCOV version 1.11