LCOV - differential code coverage report
Current view: top level - src/backend/access/hash - hashvalidate.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 73.2 % 138 101 37 101
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * hashvalidate.c
       4                 :  *    Opclass validator for hash.
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  * IDENTIFICATION
      10                 :  *    src/backend/access/hash/hashvalidate.c
      11                 :  *
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : #include "postgres.h"
      15                 : 
      16                 : #include "access/amvalidate.h"
      17                 : #include "access/hash.h"
      18                 : #include "access/htup_details.h"
      19                 : #include "access/xact.h"
      20                 : #include "catalog/pg_am.h"
      21                 : #include "catalog/pg_amop.h"
      22                 : #include "catalog/pg_amproc.h"
      23                 : #include "catalog/pg_opclass.h"
      24                 : #include "catalog/pg_opfamily.h"
      25                 : #include "catalog/pg_proc.h"
      26                 : #include "catalog/pg_type.h"
      27                 : #include "parser/parse_coerce.h"
      28                 : #include "utils/builtins.h"
      29                 : #include "utils/fmgroids.h"
      30                 : #include "utils/lsyscache.h"
      31                 : #include "utils/regproc.h"
      32                 : #include "utils/syscache.h"
      33                 : 
      34                 : 
      35                 : static bool check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype);
      36                 : 
      37                 : 
      38                 : /*
      39                 :  * Validator for a hash opclass.
      40                 :  *
      41                 :  * Some of the checks done here cover the whole opfamily, and therefore are
      42                 :  * redundant when checking each opclass in a family.  But they don't run long
      43                 :  * enough to be much of a problem, so we accept the duplication rather than
      44                 :  * complicate the amvalidate API.
      45                 :  */
      46                 : bool
      47 CBC         139 : hashvalidate(Oid opclassoid)
      48                 : {
      49             139 :     bool        result = true;
      50                 :     HeapTuple   classtup;
      51                 :     Form_pg_opclass classform;
      52                 :     Oid         opfamilyoid;
      53                 :     Oid         opcintype;
      54                 :     char       *opclassname;
      55                 :     HeapTuple   familytup;
      56                 :     Form_pg_opfamily familyform;
      57                 :     char       *opfamilyname;
      58                 :     CatCList   *proclist,
      59                 :                *oprlist;
      60                 :     List       *grouplist;
      61                 :     OpFamilyOpFuncGroup *opclassgroup;
      62             139 :     List       *hashabletypes = NIL;
      63                 :     int         i;
      64                 :     ListCell   *lc;
      65                 : 
      66                 :     /* Fetch opclass information */
      67             139 :     classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
      68             139 :     if (!HeapTupleIsValid(classtup))
      69 UBC           0 :         elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
      70 CBC         139 :     classform = (Form_pg_opclass) GETSTRUCT(classtup);
      71                 : 
      72             139 :     opfamilyoid = classform->opcfamily;
      73             139 :     opcintype = classform->opcintype;
      74             139 :     opclassname = NameStr(classform->opcname);
      75                 : 
      76                 :     /* Fetch opfamily information */
      77             139 :     familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
      78             139 :     if (!HeapTupleIsValid(familytup))
      79 UBC           0 :         elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
      80 CBC         139 :     familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
      81                 : 
      82             139 :     opfamilyname = NameStr(familyform->opfname);
      83                 : 
      84                 :     /* Fetch all operators and support functions of the opfamily */
      85             139 :     oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
      86             139 :     proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
      87                 : 
      88                 :     /* Check individual support functions */
      89             525 :     for (i = 0; i < proclist->n_members; i++)
      90                 :     {
      91             386 :         HeapTuple   proctup = &proclist->members[i]->tuple;
      92             386 :         Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
      93                 : 
      94                 :         /*
      95                 :          * All hash functions should be registered with matching left/right
      96                 :          * types
      97                 :          */
      98             386 :         if (procform->amproclefttype != procform->amprocrighttype)
      99                 :         {
     100 UBC           0 :             ereport(INFO,
     101                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     102                 :                      errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
     103                 :                             opfamilyname, "hash",
     104                 :                             format_procedure(procform->amproc))));
     105               0 :             result = false;
     106                 :         }
     107                 : 
     108                 :         /* Check procedure numbers and function signatures */
     109 CBC         386 :         switch (procform->amprocnum)
     110                 :         {
     111             386 :             case HASHSTANDARD_PROC:
     112                 :             case HASHEXTENDED_PROC:
     113             386 :                 if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
     114                 :                                                procform->amproclefttype))
     115                 :                 {
     116 UBC           0 :                     ereport(INFO,
     117                 :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     118                 :                              errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
     119                 :                                     opfamilyname, "hash",
     120                 :                                     format_procedure(procform->amproc),
     121                 :                                     procform->amprocnum)));
     122               0 :                     result = false;
     123                 :                 }
     124                 :                 else
     125                 :                 {
     126                 :                     /* Remember which types we can hash */
     127                 :                     hashabletypes =
     128 CBC         386 :                         list_append_unique_oid(hashabletypes,
     129                 :                                                procform->amproclefttype);
     130                 :                 }
     131             386 :                 break;
     132 UBC           0 :             case HASHOPTIONS_PROC:
     133               0 :                 if (!check_amoptsproc_signature(procform->amproc))
     134               0 :                     result = false;
     135               0 :                 break;
     136               0 :             default:
     137               0 :                 ereport(INFO,
     138                 :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     139                 :                          errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
     140                 :                                 opfamilyname, "hash",
     141                 :                                 format_procedure(procform->amproc),
     142                 :                                 procform->amprocnum)));
     143               0 :                 result = false;
     144               0 :                 break;
     145                 :         }
     146                 :     }
     147                 : 
     148                 :     /* Check individual operators */
     149 CBC         611 :     for (i = 0; i < oprlist->n_members; i++)
     150                 :     {
     151             472 :         HeapTuple   oprtup = &oprlist->members[i]->tuple;
     152             472 :         Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
     153                 : 
     154                 :         /* Check that only allowed strategy numbers exist */
     155             472 :         if (oprform->amopstrategy < 1 ||
     156             472 :             oprform->amopstrategy > HTMaxStrategyNumber)
     157                 :         {
     158 UBC           0 :             ereport(INFO,
     159                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     160                 :                      errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
     161                 :                             opfamilyname, "hash",
     162                 :                             format_operator(oprform->amopopr),
     163                 :                             oprform->amopstrategy)));
     164               0 :             result = false;
     165                 :         }
     166                 : 
     167                 :         /* hash doesn't support ORDER BY operators */
     168 CBC         472 :         if (oprform->amoppurpose != AMOP_SEARCH ||
     169             472 :             OidIsValid(oprform->amopsortfamily))
     170                 :         {
     171 UBC           0 :             ereport(INFO,
     172                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     173                 :                      errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
     174                 :                             opfamilyname, "hash",
     175                 :                             format_operator(oprform->amopopr))));
     176               0 :             result = false;
     177                 :         }
     178                 : 
     179                 :         /* Check operator signature --- same for all hash strategies */
     180 CBC         472 :         if (!check_amop_signature(oprform->amopopr, BOOLOID,
     181                 :                                   oprform->amoplefttype,
     182                 :                                   oprform->amoprighttype))
     183                 :         {
     184 UBC           0 :             ereport(INFO,
     185                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     186                 :                      errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
     187                 :                             opfamilyname, "hash",
     188                 :                             format_operator(oprform->amopopr))));
     189               0 :             result = false;
     190                 :         }
     191                 : 
     192                 :         /* There should be relevant hash functions for each datatype */
     193 CBC         472 :         if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
     194             472 :             !list_member_oid(hashabletypes, oprform->amoprighttype))
     195                 :         {
     196 UBC           0 :             ereport(INFO,
     197                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     198                 :                      errmsg("operator family \"%s\" of access method %s lacks support function for operator %s",
     199                 :                             opfamilyname, "hash",
     200                 :                             format_operator(oprform->amopopr))));
     201               0 :             result = false;
     202                 :         }
     203                 :     }
     204                 : 
     205                 :     /* Now check for inconsistent groups of operators/functions */
     206 CBC         139 :     grouplist = identify_opfamily_groups(oprlist, proclist);
     207             139 :     opclassgroup = NULL;
     208             611 :     foreach(lc, grouplist)
     209                 :     {
     210             472 :         OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
     211                 : 
     212                 :         /* Remember the group exactly matching the test opclass */
     213             472 :         if (thisgroup->lefttype == opcintype &&
     214             192 :             thisgroup->righttype == opcintype)
     215             139 :             opclassgroup = thisgroup;
     216                 : 
     217                 :         /*
     218                 :          * Complain if there seems to be an incomplete set of operators for
     219                 :          * this datatype pair (implying that we have a hash function but no
     220                 :          * operator).
     221                 :          */
     222             472 :         if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
     223                 :         {
     224 UBC           0 :             ereport(INFO,
     225                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     226                 :                      errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
     227                 :                             opfamilyname, "hash",
     228                 :                             format_type_be(thisgroup->lefttype),
     229                 :                             format_type_be(thisgroup->righttype))));
     230               0 :             result = false;
     231                 :         }
     232                 :     }
     233                 : 
     234                 :     /* Check that the originally-named opclass is supported */
     235                 :     /* (if group is there, we already checked it adequately above) */
     236 CBC         139 :     if (!opclassgroup)
     237                 :     {
     238 UBC           0 :         ereport(INFO,
     239                 :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     240                 :                  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
     241                 :                         opclassname, "hash")));
     242               0 :         result = false;
     243                 :     }
     244                 : 
     245                 :     /*
     246                 :      * Complain if the opfamily doesn't have entries for all possible
     247                 :      * combinations of its supported datatypes.  While missing cross-type
     248                 :      * operators are not fatal, it seems reasonable to insist that all
     249                 :      * built-in hash opfamilies be complete.
     250                 :      */
     251 CBC         139 :     if (list_length(grouplist) !=
     252             139 :         list_length(hashabletypes) * list_length(hashabletypes))
     253                 :     {
     254               8 :         ereport(INFO,
     255                 :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     256                 :                  errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
     257                 :                         opfamilyname, "hash")));
     258               8 :         result = false;
     259                 :     }
     260                 : 
     261             139 :     ReleaseCatCacheList(proclist);
     262             139 :     ReleaseCatCacheList(oprlist);
     263             139 :     ReleaseSysCache(familytup);
     264             139 :     ReleaseSysCache(classtup);
     265                 : 
     266             139 :     return result;
     267                 : }
     268                 : 
     269                 : 
     270                 : /*
     271                 :  * We need a custom version of check_amproc_signature because of assorted
     272                 :  * hacks in the core hash opclass definitions.
     273                 :  */
     274                 : static bool
     275             386 : check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
     276                 : {
     277             386 :     bool        result = true;
     278                 :     Oid         restype;
     279                 :     int16       nargs;
     280                 :     HeapTuple   tp;
     281                 :     Form_pg_proc procform;
     282                 : 
     283             386 :     switch (amprocnum)
     284                 :     {
     285             222 :         case HASHSTANDARD_PROC:
     286             222 :             restype = INT4OID;
     287             222 :             nargs = 1;
     288             222 :             break;
     289                 : 
     290             164 :         case HASHEXTENDED_PROC:
     291             164 :             restype = INT8OID;
     292             164 :             nargs = 2;
     293             164 :             break;
     294                 : 
     295 UBC           0 :         default:
     296               0 :             elog(ERROR, "invalid amprocnum");
     297                 :     }
     298                 : 
     299 CBC         386 :     tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
     300             386 :     if (!HeapTupleIsValid(tp))
     301 UBC           0 :         elog(ERROR, "cache lookup failed for function %u", funcid);
     302 CBC         386 :     procform = (Form_pg_proc) GETSTRUCT(tp);
     303                 : 
     304             386 :     if (procform->prorettype != restype || procform->proretset ||
     305             386 :         procform->pronargs != nargs)
     306 UBC           0 :         result = false;
     307                 : 
     308 CBC         386 :     if (!IsBinaryCoercible(argtype, procform->proargtypes.values[0]))
     309                 :     {
     310                 :         /*
     311                 :          * Some of the built-in hash opclasses cheat by using hash functions
     312                 :          * that are different from but physically compatible with the opclass
     313                 :          * datatype.  In some of these cases, even a "binary coercible" check
     314                 :          * fails because there's no relevant cast.  For the moment, fix it by
     315                 :          * having a list of allowed cases.  Test the specific function
     316                 :          * identity, not just its input type, because hashvarlena() takes
     317                 :          * INTERNAL and allowing any such function seems too scary.
     318                 :          */
     319              42 :         if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
     320              12 :             (argtype == DATEOID ||
     321               6 :              argtype == XIDOID || argtype == CIDOID))
     322                 :              /* okay, allowed use of hashint4() */ ;
     323              24 :         else if ((funcid == F_HASHINT8 || funcid == F_HASHINT8EXTENDED) &&
     324                 :                  (argtype == XID8OID))
     325                 :              /* okay, allowed use of hashint8() */ ;
     326              18 :         else if ((funcid == F_TIMESTAMP_HASH ||
     327               6 :                   funcid == F_TIMESTAMP_HASH_EXTENDED) &&
     328                 :                  argtype == TIMESTAMPTZOID)
     329                 :              /* okay, allowed use of timestamp_hash() */ ;
     330              12 :         else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
     331                 :                  argtype == BOOLOID)
     332                 :              /* okay, allowed use of hashchar() */ ;
     333               6 :         else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
     334                 :                  argtype == BYTEAOID)
     335                 :              /* okay, allowed use of hashvarlena() */ ;
     336                 :         else
     337 UBC           0 :             result = false;
     338                 :     }
     339                 : 
     340                 :     /* If function takes a second argument, it must be for a 64-bit salt. */
     341 CBC         386 :     if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
     342 UBC           0 :         result = false;
     343                 : 
     344 CBC         386 :     ReleaseSysCache(tp);
     345             386 :     return result;
     346                 : }
     347                 : 
     348                 : /*
     349                 :  * Prechecking function for adding operators/functions to a hash opfamily.
     350                 :  */
     351                 : void
     352              70 : hashadjustmembers(Oid opfamilyoid,
     353                 :                   Oid opclassoid,
     354                 :                   List *operators,
     355                 :                   List *functions)
     356                 : {
     357                 :     Oid         opcintype;
     358                 :     ListCell   *lc;
     359                 : 
     360                 :     /*
     361                 :      * Hash operators and required support functions are always "loose"
     362                 :      * members of the opfamily if they are cross-type.  If they are not
     363                 :      * cross-type, we prefer to tie them to the appropriate opclass ... but if
     364                 :      * the user hasn't created one, we can't do that, and must fall back to
     365                 :      * using the opfamily dependency.  (We mustn't force creation of an
     366                 :      * opclass in such a case, as leaving an incomplete opclass laying about
     367                 :      * would be bad.  Throwing an error is another undesirable alternative.)
     368                 :      *
     369                 :      * This behavior results in a bit of a dump/reload hazard, in that the
     370                 :      * order of restoring objects could affect what dependencies we end up
     371                 :      * with.  pg_dump's existing behavior will preserve the dependency choices
     372                 :      * in most cases, but not if a cross-type operator has been bound tightly
     373                 :      * into an opclass.  That's a mistake anyway, so silently "fixing" it
     374                 :      * isn't awful.
     375                 :      *
     376                 :      * Optional support functions are always "loose" family members.
     377                 :      *
     378                 :      * To avoid repeated lookups, we remember the most recently used opclass's
     379                 :      * input type.
     380                 :      */
     381              70 :     if (OidIsValid(opclassoid))
     382                 :     {
     383                 :         /* During CREATE OPERATOR CLASS, need CCI to see the pg_opclass row */
     384              50 :         CommandCounterIncrement();
     385              50 :         opcintype = get_opclass_input_type(opclassoid);
     386                 :     }
     387                 :     else
     388              20 :         opcintype = InvalidOid;
     389                 : 
     390                 :     /*
     391                 :      * We handle operators and support functions almost identically, so rather
     392                 :      * than duplicate this code block, just join the lists.
     393                 :      */
     394             172 :     foreach(lc, list_concat_copy(operators, functions))
     395                 :     {
     396             102 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
     397                 : 
     398             102 :         if (op->is_func && op->number != HASHSTANDARD_PROC)
     399                 :         {
     400                 :             /* Optional support proc, so always a soft family dependency */
     401              24 :             op->ref_is_hard = false;
     402              24 :             op->ref_is_family = true;
     403              24 :             op->refobjid = opfamilyoid;
     404                 :         }
     405              78 :         else if (op->lefttype != op->righttype)
     406                 :         {
     407                 :             /* Cross-type, so always a soft family dependency */
     408              20 :             op->ref_is_hard = false;
     409              20 :             op->ref_is_family = true;
     410              20 :             op->refobjid = opfamilyoid;
     411                 :         }
     412                 :         else
     413                 :         {
     414                 :             /* Not cross-type; is there a suitable opclass? */
     415              58 :             if (op->lefttype != opcintype)
     416                 :             {
     417                 :                 /* Avoid repeating this expensive lookup, even if it fails */
     418 UBC           0 :                 opcintype = op->lefttype;
     419               0 :                 opclassoid = opclass_for_family_datatype(HASH_AM_OID,
     420                 :                                                          opfamilyoid,
     421                 :                                                          opcintype);
     422                 :             }
     423 CBC          58 :             if (OidIsValid(opclassoid))
     424                 :             {
     425                 :                 /* Hard dependency on opclass */
     426              58 :                 op->ref_is_hard = true;
     427              58 :                 op->ref_is_family = false;
     428              58 :                 op->refobjid = opclassoid;
     429                 :             }
     430                 :             else
     431                 :             {
     432                 :                 /* We're stuck, so make a soft dependency on the opfamily */
     433 UBC           0 :                 op->ref_is_hard = false;
     434               0 :                 op->ref_is_family = true;
     435               0 :                 op->refobjid = opfamilyoid;
     436                 :             }
     437                 :         }
     438                 :     }
     439 CBC          70 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a