LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginvalidate.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 76.7 % 116 89 27 89
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 2 2 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * ginvalidate.c
       4                 :  *    Opclass validator for GIN.
       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/gin/ginvalidate.c
      11                 :  *
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : #include "postgres.h"
      15                 : 
      16                 : #include "access/amvalidate.h"
      17                 : #include "access/gin_private.h"
      18                 : #include "access/htup_details.h"
      19                 : #include "catalog/pg_amop.h"
      20                 : #include "catalog/pg_amproc.h"
      21                 : #include "catalog/pg_opclass.h"
      22                 : #include "catalog/pg_opfamily.h"
      23                 : #include "catalog/pg_type.h"
      24                 : #include "utils/builtins.h"
      25                 : #include "utils/lsyscache.h"
      26                 : #include "utils/regproc.h"
      27                 : #include "utils/syscache.h"
      28                 : 
      29                 : /*
      30                 :  * Validator for a GIN opclass.
      31                 :  */
      32                 : bool
      33 CBC          44 : ginvalidate(Oid opclassoid)
      34                 : {
      35              44 :     bool        result = true;
      36                 :     HeapTuple   classtup;
      37                 :     Form_pg_opclass classform;
      38                 :     Oid         opfamilyoid;
      39                 :     Oid         opcintype;
      40                 :     Oid         opckeytype;
      41                 :     char       *opclassname;
      42                 :     HeapTuple   familytup;
      43                 :     Form_pg_opfamily familyform;
      44                 :     char       *opfamilyname;
      45                 :     CatCList   *proclist,
      46                 :                *oprlist;
      47                 :     List       *grouplist;
      48                 :     OpFamilyOpFuncGroup *opclassgroup;
      49                 :     int         i;
      50                 :     ListCell   *lc;
      51                 : 
      52                 :     /* Fetch opclass information */
      53              44 :     classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
      54              44 :     if (!HeapTupleIsValid(classtup))
      55 UBC           0 :         elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
      56 CBC          44 :     classform = (Form_pg_opclass) GETSTRUCT(classtup);
      57                 : 
      58              44 :     opfamilyoid = classform->opcfamily;
      59              44 :     opcintype = classform->opcintype;
      60              44 :     opckeytype = classform->opckeytype;
      61              44 :     if (!OidIsValid(opckeytype))
      62              29 :         opckeytype = opcintype;
      63              44 :     opclassname = NameStr(classform->opcname);
      64                 : 
      65                 :     /* Fetch opfamily information */
      66              44 :     familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
      67              44 :     if (!HeapTupleIsValid(familytup))
      68 UBC           0 :         elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
      69 CBC          44 :     familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
      70                 : 
      71              44 :     opfamilyname = NameStr(familyform->opfname);
      72                 : 
      73                 :     /* Fetch all operators and support functions of the opfamily */
      74              44 :     oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
      75              44 :     proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
      76                 : 
      77                 :     /* Check individual support functions */
      78             262 :     for (i = 0; i < proclist->n_members; i++)
      79                 :     {
      80             218 :         HeapTuple   proctup = &proclist->members[i]->tuple;
      81             218 :         Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
      82                 :         bool        ok;
      83                 : 
      84                 :         /*
      85                 :          * All GIN support functions should be registered with matching
      86                 :          * left/right types
      87                 :          */
      88             218 :         if (procform->amproclefttype != procform->amprocrighttype)
      89                 :         {
      90 UBC           0 :             ereport(INFO,
      91                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
      92                 :                      errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
      93                 :                             opfamilyname, "gin",
      94                 :                             format_procedure(procform->amproc))));
      95               0 :             result = false;
      96                 :         }
      97                 : 
      98                 :         /*
      99                 :          * We can't check signatures except within the specific opclass, since
     100                 :          * we need to know the associated opckeytype in many cases.
     101                 :          */
     102 CBC         218 :         if (procform->amproclefttype != opcintype)
     103 UBC           0 :             continue;
     104                 : 
     105                 :         /* Check procedure numbers and function signatures */
     106 CBC         218 :         switch (procform->amprocnum)
     107                 :         {
     108              41 :             case GIN_COMPARE_PROC:
     109              41 :                 ok = check_amproc_signature(procform->amproc, INT4OID, false,
     110                 :                                             2, 2, opckeytype, opckeytype);
     111              41 :                 break;
     112              44 :             case GIN_EXTRACTVALUE_PROC:
     113                 :                 /* Some opclasses omit nullFlags */
     114              44 :                 ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
     115                 :                                             2, 3, opcintype, INTERNALOID,
     116                 :                                             INTERNALOID);
     117              44 :                 break;
     118              44 :             case GIN_EXTRACTQUERY_PROC:
     119                 :                 /* Some opclasses omit nullFlags and searchMode */
     120              44 :                 ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
     121                 :                                             5, 7, opcintype, INTERNALOID,
     122                 :                                             INT2OID, INTERNALOID, INTERNALOID,
     123                 :                                             INTERNALOID, INTERNALOID);
     124              44 :                 break;
     125              44 :             case GIN_CONSISTENT_PROC:
     126                 :                 /* Some opclasses omit queryKeys and nullFlags */
     127              44 :                 ok = check_amproc_signature(procform->amproc, BOOLOID, false,
     128                 :                                             6, 8, INTERNALOID, INT2OID,
     129                 :                                             opcintype, INT4OID,
     130                 :                                             INTERNALOID, INTERNALOID,
     131                 :                                             INTERNALOID, INTERNALOID);
     132              44 :                 break;
     133              32 :             case GIN_COMPARE_PARTIAL_PROC:
     134              32 :                 ok = check_amproc_signature(procform->amproc, INT4OID, false,
     135                 :                                             4, 4, opckeytype, opckeytype,
     136                 :                                             INT2OID, INTERNALOID);
     137              32 :                 break;
     138              13 :             case GIN_TRICONSISTENT_PROC:
     139              13 :                 ok = check_amproc_signature(procform->amproc, CHAROID, false,
     140                 :                                             7, 7, INTERNALOID, INT2OID,
     141                 :                                             opcintype, INT4OID,
     142                 :                                             INTERNALOID, INTERNALOID,
     143                 :                                             INTERNALOID);
     144              13 :                 break;
     145 UBC           0 :             case GIN_OPTIONS_PROC:
     146               0 :                 ok = check_amoptsproc_signature(procform->amproc);
     147               0 :                 break;
     148               0 :             default:
     149               0 :                 ereport(INFO,
     150                 :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     151                 :                          errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
     152                 :                                 opfamilyname, "gin",
     153                 :                                 format_procedure(procform->amproc),
     154                 :                                 procform->amprocnum)));
     155               0 :                 result = false;
     156               0 :                 continue;       /* don't want additional message */
     157                 :         }
     158                 : 
     159 CBC         218 :         if (!ok)
     160                 :         {
     161 UBC           0 :             ereport(INFO,
     162                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     163                 :                      errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
     164                 :                             opfamilyname, "gin",
     165                 :                             format_procedure(procform->amproc),
     166                 :                             procform->amprocnum)));
     167               0 :             result = false;
     168                 :         }
     169                 :     }
     170                 : 
     171                 :     /* Check individual operators */
     172 CBC         251 :     for (i = 0; i < oprlist->n_members; i++)
     173                 :     {
     174             207 :         HeapTuple   oprtup = &oprlist->members[i]->tuple;
     175             207 :         Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
     176                 : 
     177                 :         /* TODO: Check that only allowed strategy numbers exist */
     178             207 :         if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
     179                 :         {
     180 UBC           0 :             ereport(INFO,
     181                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     182                 :                      errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
     183                 :                             opfamilyname, "gin",
     184                 :                             format_operator(oprform->amopopr),
     185                 :                             oprform->amopstrategy)));
     186               0 :             result = false;
     187                 :         }
     188                 : 
     189                 :         /* gin doesn't support ORDER BY operators */
     190 CBC         207 :         if (oprform->amoppurpose != AMOP_SEARCH ||
     191             207 :             OidIsValid(oprform->amopsortfamily))
     192                 :         {
     193 UBC           0 :             ereport(INFO,
     194                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     195                 :                      errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
     196                 :                             opfamilyname, "gin",
     197                 :                             format_operator(oprform->amopopr))));
     198               0 :             result = false;
     199                 :         }
     200                 : 
     201                 :         /* Check operator signature --- same for all gin strategies */
     202 CBC         207 :         if (!check_amop_signature(oprform->amopopr, BOOLOID,
     203                 :                                   oprform->amoplefttype,
     204                 :                                   oprform->amoprighttype))
     205                 :         {
     206 UBC           0 :             ereport(INFO,
     207                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     208                 :                      errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
     209                 :                             opfamilyname, "gin",
     210                 :                             format_operator(oprform->amopopr))));
     211               0 :             result = false;
     212                 :         }
     213                 :     }
     214                 : 
     215                 :     /* Now check for inconsistent groups of operators/functions */
     216 CBC          44 :     grouplist = identify_opfamily_groups(oprlist, proclist);
     217              44 :     opclassgroup = NULL;
     218             109 :     foreach(lc, grouplist)
     219                 :     {
     220              65 :         OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
     221                 : 
     222                 :         /* Remember the group exactly matching the test opclass */
     223              65 :         if (thisgroup->lefttype == opcintype &&
     224              62 :             thisgroup->righttype == opcintype)
     225              44 :             opclassgroup = thisgroup;
     226                 : 
     227                 :         /*
     228                 :          * There is not a lot we can do to check the operator sets, since each
     229                 :          * GIN opclass is more or less a law unto itself, and some contain
     230                 :          * only operators that are binary-compatible with the opclass datatype
     231                 :          * (meaning that empty operator sets can be OK).  That case also means
     232                 :          * that we shouldn't insist on nonempty function sets except for the
     233                 :          * opclass's own group.
     234                 :          */
     235                 :     }
     236                 : 
     237                 :     /* Check that the originally-named opclass is complete */
     238             352 :     for (i = 1; i <= GINNProcs; i++)
     239                 :     {
     240             308 :         if (opclassgroup &&
     241             308 :             (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
     242             218 :             continue;           /* got it */
     243              90 :         if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
     244                 :             i == GIN_OPTIONS_PROC)
     245              59 :             continue;           /* optional method */
     246              31 :         if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
     247              31 :             continue;           /* don't need both, see check below loop */
     248 UBC           0 :         ereport(INFO,
     249                 :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     250                 :                  errmsg("operator class \"%s\" of access method %s is missing support function %d",
     251                 :                         opclassname, "gin", i)));
     252               0 :         result = false;
     253                 :     }
     254 CBC          44 :     if (!opclassgroup ||
     255              44 :         ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
     256 UBC           0 :          (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
     257                 :     {
     258               0 :         ereport(INFO,
     259                 :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     260                 :                  errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
     261                 :                         opclassname, "gin",
     262                 :                         GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
     263               0 :         result = false;
     264                 :     }
     265                 : 
     266                 : 
     267 CBC          44 :     ReleaseCatCacheList(proclist);
     268              44 :     ReleaseCatCacheList(oprlist);
     269              44 :     ReleaseSysCache(familytup);
     270              44 :     ReleaseSysCache(classtup);
     271                 : 
     272              44 :     return result;
     273                 : }
     274                 : 
     275                 : /*
     276                 :  * Prechecking function for adding operators/functions to a GIN opfamily.
     277                 :  */
     278                 : void
     279              43 : ginadjustmembers(Oid opfamilyoid,
     280                 :                  Oid opclassoid,
     281                 :                  List *operators,
     282                 :                  List *functions)
     283                 : {
     284                 :     ListCell   *lc;
     285                 : 
     286                 :     /*
     287                 :      * Operator members of a GIN opfamily should never have hard dependencies,
     288                 :      * since their connection to the opfamily depends only on what the support
     289                 :      * functions think, and that can be altered.  For consistency, we make all
     290                 :      * soft dependencies point to the opfamily, though a soft dependency on
     291                 :      * the opclass would work as well in the CREATE OPERATOR CLASS case.
     292                 :      */
     293             231 :     foreach(lc, operators)
     294                 :     {
     295             188 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
     296                 : 
     297             188 :         op->ref_is_hard = false;
     298             188 :         op->ref_is_family = true;
     299             188 :         op->refobjid = opfamilyoid;
     300                 :     }
     301                 : 
     302                 :     /*
     303                 :      * Required support functions should have hard dependencies.  Preferably
     304                 :      * those are just dependencies on the opclass, but if we're in ALTER
     305                 :      * OPERATOR FAMILY, we leave the dependency pointing at the whole
     306                 :      * opfamily.  (Given that GIN opclasses generally don't share opfamilies,
     307                 :      * it seems unlikely to be worth working harder.)
     308                 :      */
     309             225 :     foreach(lc, functions)
     310                 :     {
     311             182 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
     312                 : 
     313             182 :         switch (op->number)
     314                 :         {
     315              76 :             case GIN_EXTRACTVALUE_PROC:
     316                 :             case GIN_EXTRACTQUERY_PROC:
     317                 :                 /* Required support function */
     318              76 :                 op->ref_is_hard = true;
     319              76 :                 break;
     320             106 :             case GIN_COMPARE_PROC:
     321                 :             case GIN_CONSISTENT_PROC:
     322                 :             case GIN_COMPARE_PARTIAL_PROC:
     323                 :             case GIN_TRICONSISTENT_PROC:
     324                 :             case GIN_OPTIONS_PROC:
     325                 :                 /* Optional, so force it to be a soft family dependency */
     326             106 :                 op->ref_is_hard = false;
     327             106 :                 op->ref_is_family = true;
     328             106 :                 op->refobjid = opfamilyoid;
     329             106 :                 break;
     330 UBC           0 :             default:
     331               0 :                 ereport(ERROR,
     332                 :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     333                 :                          errmsg("support function number %d is invalid for access method %s",
     334                 :                                 op->number, "gin")));
     335                 :                 break;
     336                 :         }
     337                 :     }
     338 CBC          43 : }
        

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