LCOV - code coverage report
Current view: top level - src/backend/utils/adt - amutils.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 91 104 87.5 %
Date: 2017-09-29 13:40:31 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * amutils.c
       4             :  *    SQL-level APIs related to index access methods.
       5             :  *
       6             :  * Copyright (c) 2016-2017, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/amutils.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/amapi.h"
      17             : #include "access/htup_details.h"
      18             : #include "catalog/pg_class.h"
      19             : #include "catalog/pg_index.h"
      20             : #include "utils/builtins.h"
      21             : #include "utils/syscache.h"
      22             : 
      23             : 
      24             : /* Convert string property name to enum, for efficiency */
      25             : struct am_propname
      26             : {
      27             :     const char *name;
      28             :     IndexAMProperty prop;
      29             : };
      30             : 
      31             : static const struct am_propname am_propnames[] =
      32             : {
      33             :     {
      34             :         "asc", AMPROP_ASC
      35             :     },
      36             :     {
      37             :         "desc", AMPROP_DESC
      38             :     },
      39             :     {
      40             :         "nulls_first", AMPROP_NULLS_FIRST
      41             :     },
      42             :     {
      43             :         "nulls_last", AMPROP_NULLS_LAST
      44             :     },
      45             :     {
      46             :         "orderable", AMPROP_ORDERABLE
      47             :     },
      48             :     {
      49             :         "distance_orderable", AMPROP_DISTANCE_ORDERABLE
      50             :     },
      51             :     {
      52             :         "returnable", AMPROP_RETURNABLE
      53             :     },
      54             :     {
      55             :         "search_array", AMPROP_SEARCH_ARRAY
      56             :     },
      57             :     {
      58             :         "search_nulls", AMPROP_SEARCH_NULLS
      59             :     },
      60             :     {
      61             :         "clusterable", AMPROP_CLUSTERABLE
      62             :     },
      63             :     {
      64             :         "index_scan", AMPROP_INDEX_SCAN
      65             :     },
      66             :     {
      67             :         "bitmap_scan", AMPROP_BITMAP_SCAN
      68             :     },
      69             :     {
      70             :         "backward_scan", AMPROP_BACKWARD_SCAN
      71             :     },
      72             :     {
      73             :         "can_order", AMPROP_CAN_ORDER
      74             :     },
      75             :     {
      76             :         "can_unique", AMPROP_CAN_UNIQUE
      77             :     },
      78             :     {
      79             :         "can_multi_col", AMPROP_CAN_MULTI_COL
      80             :     },
      81             :     {
      82             :         "can_exclude", AMPROP_CAN_EXCLUDE
      83             :     }
      84             : };
      85             : 
      86             : static IndexAMProperty
      87         252 : lookup_prop_name(const char *name)
      88             : {
      89             :     int         i;
      90             : 
      91        2400 :     for (i = 0; i < lengthof(am_propnames); i++)
      92             :     {
      93        2372 :         if (pg_strcasecmp(am_propnames[i].name, name) == 0)
      94         224 :             return am_propnames[i].prop;
      95             :     }
      96             : 
      97             :     /* We do not throw an error, so that AMs can define their own properties */
      98          28 :     return AMPROP_UNKNOWN;
      99             : }
     100             : 
     101             : /*
     102             :  * Common code for properties that are just bit tests of indoptions.
     103             :  *
     104             :  * relid/attno: identify the index column to test the indoptions of.
     105             :  * guard: if false, a boolean false result is forced (saves code in caller).
     106             :  * iopt_mask: mask for interesting indoption bit.
     107             :  * iopt_expect: value for a "true" result (should be 0 or iopt_mask).
     108             :  *
     109             :  * Returns false to indicate a NULL result (for "unknown/inapplicable"),
     110             :  * otherwise sets *res to the boolean value to return.
     111             :  */
     112             : static bool
     113          48 : test_indoption(Oid relid, int attno, bool guard,
     114             :                int16 iopt_mask, int16 iopt_expect,
     115             :                bool *res)
     116             : {
     117             :     HeapTuple   tuple;
     118             :     Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
     119             :     Datum       datum;
     120             :     bool        isnull;
     121             :     int2vector *indoption;
     122             :     int16       indoption_val;
     123             : 
     124          48 :     if (!guard)
     125             :     {
     126          24 :         *res = false;
     127          24 :         return true;
     128             :     }
     129             : 
     130          24 :     tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relid));
     131          24 :     if (!HeapTupleIsValid(tuple))
     132           0 :         return false;
     133          24 :     rd_index = (Form_pg_index) GETSTRUCT(tuple);
     134             : 
     135          24 :     Assert(relid == rd_index->indexrelid);
     136          24 :     Assert(attno > 0 && attno <= rd_index->indnatts);
     137             : 
     138          24 :     datum = SysCacheGetAttr(INDEXRELID, tuple,
     139             :                             Anum_pg_index_indoption, &isnull);
     140          24 :     Assert(!isnull);
     141             : 
     142          24 :     indoption = ((int2vector *) DatumGetPointer(datum));
     143          24 :     indoption_val = indoption->values[attno - 1];
     144             : 
     145          24 :     *res = (indoption_val & iopt_mask) == iopt_expect;
     146             : 
     147          24 :     ReleaseSysCache(tuple);
     148             : 
     149          24 :     return true;
     150             : }
     151             : 
     152             : 
     153             : /*
     154             :  * Test property of an index AM, index, or index column.
     155             :  *
     156             :  * This is common code for different SQL-level funcs, so the amoid and
     157             :  * index_oid parameters are mutually exclusive; we look up the amoid from the
     158             :  * index_oid if needed, or if no index oid is given, we're looking at AM-wide
     159             :  * properties.
     160             :  */
     161             : static Datum
     162         252 : indexam_property(FunctionCallInfo fcinfo,
     163             :                  const char *propname,
     164             :                  Oid amoid, Oid index_oid, int attno)
     165             : {
     166         252 :     bool        res = false;
     167         252 :     bool        isnull = false;
     168         252 :     int         natts = 0;
     169             :     IndexAMProperty prop;
     170             :     IndexAmRoutine *routine;
     171             : 
     172             :     /* Try to convert property name to enum (no error if not known) */
     173         252 :     prop = lookup_prop_name(propname);
     174             : 
     175             :     /* If we have an index OID, look up the AM, and get # of columns too */
     176         252 :     if (OidIsValid(index_oid))
     177             :     {
     178             :         HeapTuple   tuple;
     179             :         Form_pg_class rd_rel;
     180             : 
     181         186 :         Assert(!OidIsValid(amoid));
     182         186 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(index_oid));
     183         186 :         if (!HeapTupleIsValid(tuple))
     184           0 :             PG_RETURN_NULL();
     185         186 :         rd_rel = (Form_pg_class) GETSTRUCT(tuple);
     186         186 :         if (rd_rel->relkind != RELKIND_INDEX)
     187             :         {
     188           0 :             ReleaseSysCache(tuple);
     189           0 :             PG_RETURN_NULL();
     190             :         }
     191         186 :         amoid = rd_rel->relam;
     192         186 :         natts = rd_rel->relnatts;
     193         186 :         ReleaseSysCache(tuple);
     194             :     }
     195             : 
     196             :     /*
     197             :      * At this point, either index_oid == InvalidOid or it's a valid index
     198             :      * OID.  Also, after this test, either attno == 0 for index-wide or
     199             :      * AM-wide tests, or it's a valid column number in a valid index.
     200             :      */
     201         252 :     if (attno < 0 || attno > natts)
     202           0 :         PG_RETURN_NULL();
     203             : 
     204             :     /*
     205             :      * Get AM information.  If we don't have a valid AM OID, return NULL.
     206             :      */
     207         252 :     routine = GetIndexAmRoutineByAmId(amoid, true);
     208         252 :     if (routine == NULL)
     209           0 :         PG_RETURN_NULL();
     210             : 
     211             :     /*
     212             :      * If there's an AM property routine, give it a chance to override the
     213             :      * generic logic.  Proceed if it returns false.
     214             :      */
     215         424 :     if (routine->amproperty &&
     216         172 :         routine->amproperty(index_oid, attno, prop, propname,
     217             :                             &res, &isnull))
     218             :     {
     219           6 :         if (isnull)
     220           0 :             PG_RETURN_NULL();
     221           6 :         PG_RETURN_BOOL(res);
     222             :     }
     223             : 
     224         246 :     if (attno > 0)
     225             :     {
     226             :         /* Handle column-level properties */
     227         114 :         switch (prop)
     228             :         {
     229             :             case AMPROP_ASC:
     230          12 :                 if (test_indoption(index_oid, attno, routine->amcanorder,
     231             :                                    INDOPTION_DESC, 0, &res))
     232          12 :                     PG_RETURN_BOOL(res);
     233           0 :                 PG_RETURN_NULL();
     234             : 
     235             :             case AMPROP_DESC:
     236          12 :                 if (test_indoption(index_oid, attno, routine->amcanorder,
     237             :                                    INDOPTION_DESC, INDOPTION_DESC, &res))
     238          12 :                     PG_RETURN_BOOL(res);
     239           0 :                 PG_RETURN_NULL();
     240             : 
     241             :             case AMPROP_NULLS_FIRST:
     242          12 :                 if (test_indoption(index_oid, attno, routine->amcanorder,
     243             :                                    INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
     244          12 :                     PG_RETURN_BOOL(res);
     245           0 :                 PG_RETURN_NULL();
     246             : 
     247             :             case AMPROP_NULLS_LAST:
     248          12 :                 if (test_indoption(index_oid, attno, routine->amcanorder,
     249             :                                    INDOPTION_NULLS_FIRST, 0, &res))
     250          12 :                     PG_RETURN_BOOL(res);
     251           0 :                 PG_RETURN_NULL();
     252             : 
     253             :             case AMPROP_ORDERABLE:
     254          12 :                 PG_RETURN_BOOL(routine->amcanorder);
     255             : 
     256             :             case AMPROP_DISTANCE_ORDERABLE:
     257             : 
     258             :                 /*
     259             :                  * The conditions for whether a column is distance-orderable
     260             :                  * are really up to the AM (at time of writing, only GiST
     261             :                  * supports it at all).  The planner has its own idea based on
     262             :                  * whether it finds an operator with amoppurpose 'o', but
     263             :                  * getting there from just the index column type seems like a
     264             :                  * lot of work.  So instead we expect the AM to handle this in
     265             :                  * its amproperty routine.  The generic result is to return
     266             :                  * false if the AM says it never supports this, and null
     267             :                  * otherwise (meaning we don't know).
     268             :                  */
     269           6 :                 if (!routine->amcanorderbyop)
     270           6 :                     PG_RETURN_BOOL(false);
     271           0 :                 PG_RETURN_NULL();
     272             : 
     273             :             case AMPROP_RETURNABLE:
     274           4 :                 if (!routine->amcanreturn)
     275           3 :                     PG_RETURN_BOOL(false);
     276             : 
     277             :                 /*
     278             :                  * If possible, the AM should handle this test in its
     279             :                  * amproperty function without opening the rel.  But this is
     280             :                  * the generic fallback if it does not.
     281             :                  */
     282             :                 {
     283           1 :                     Relation    indexrel = index_open(index_oid, AccessShareLock);
     284             : 
     285           1 :                     res = index_can_return(indexrel, attno);
     286           1 :                     index_close(indexrel, AccessShareLock);
     287             :                 }
     288             : 
     289           1 :                 PG_RETURN_BOOL(res);
     290             : 
     291             :             case AMPROP_SEARCH_ARRAY:
     292           8 :                 PG_RETURN_BOOL(routine->amsearcharray);
     293             : 
     294             :             case AMPROP_SEARCH_NULLS:
     295           8 :                 PG_RETURN_BOOL(routine->amsearchnulls);
     296             : 
     297             :             default:
     298          28 :                 PG_RETURN_NULL();
     299             :         }
     300             :     }
     301             : 
     302         132 :     if (OidIsValid(index_oid))
     303             :     {
     304             :         /*
     305             :          * Handle index-level properties.  Currently, these only depend on the
     306             :          * AM, but that might not be true forever, so we make users name an
     307             :          * index not just an AM.
     308             :          */
     309          66 :         switch (prop)
     310             :         {
     311             :             case AMPROP_CLUSTERABLE:
     312           8 :                 PG_RETURN_BOOL(routine->amclusterable);
     313             : 
     314             :             case AMPROP_INDEX_SCAN:
     315           8 :                 PG_RETURN_BOOL(routine->amgettuple ? true : false);
     316             : 
     317             :             case AMPROP_BITMAP_SCAN:
     318           8 :                 PG_RETURN_BOOL(routine->amgetbitmap ? true : false);
     319             : 
     320             :             case AMPROP_BACKWARD_SCAN:
     321           8 :                 PG_RETURN_BOOL(routine->amcanbackward);
     322             : 
     323             :             default:
     324          34 :                 PG_RETURN_NULL();
     325             :         }
     326             :     }
     327             : 
     328             :     /*
     329             :      * Handle AM-level properties (those that control what you can say in
     330             :      * CREATE INDEX).
     331             :      */
     332          66 :     switch (prop)
     333             :     {
     334             :         case AMPROP_CAN_ORDER:
     335           8 :             PG_RETURN_BOOL(routine->amcanorder);
     336             : 
     337             :         case AMPROP_CAN_UNIQUE:
     338           8 :             PG_RETURN_BOOL(routine->amcanunique);
     339             : 
     340             :         case AMPROP_CAN_MULTI_COL:
     341           8 :             PG_RETURN_BOOL(routine->amcanmulticol);
     342             : 
     343             :         case AMPROP_CAN_EXCLUDE:
     344           8 :             PG_RETURN_BOOL(routine->amgettuple ? true : false);
     345             : 
     346             :         default:
     347          34 :             PG_RETURN_NULL();
     348             :     }
     349             : }
     350             : 
     351             : /*
     352             :  * Test property of an AM specified by AM OID
     353             :  */
     354             : Datum
     355          66 : pg_indexam_has_property(PG_FUNCTION_ARGS)
     356             : {
     357          66 :     Oid         amoid = PG_GETARG_OID(0);
     358          66 :     char       *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
     359             : 
     360          66 :     return indexam_property(fcinfo, propname, amoid, InvalidOid, 0);
     361             : }
     362             : 
     363             : /*
     364             :  * Test property of an index specified by index OID
     365             :  */
     366             : Datum
     367          66 : pg_index_has_property(PG_FUNCTION_ARGS)
     368             : {
     369          66 :     Oid         relid = PG_GETARG_OID(0);
     370          66 :     char       *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
     371             : 
     372          66 :     return indexam_property(fcinfo, propname, InvalidOid, relid, 0);
     373             : }
     374             : 
     375             : /*
     376             :  * Test property of an index column specified by index OID and column number
     377             :  */
     378             : Datum
     379         120 : pg_index_column_has_property(PG_FUNCTION_ARGS)
     380             : {
     381         120 :     Oid         relid = PG_GETARG_OID(0);
     382         120 :     int32       attno = PG_GETARG_INT32(1);
     383         120 :     char       *propname = text_to_cstring(PG_GETARG_TEXT_PP(2));
     384             : 
     385             :     /* Reject attno 0 immediately, so that attno > 0 identifies this case */
     386         120 :     if (attno <= 0)
     387           0 :         PG_RETURN_NULL();
     388             : 
     389         120 :     return indexam_property(fcinfo, propname, InvalidOid, relid, attno);
     390             : }

Generated by: LCOV version 1.11