LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistvalidate.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.0 % 123 96
Test Date: 2026-03-03 00:14:38 Functions: 100.0 % 2 2
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1