Add hooks to let plugins override the planner's lookups in pg_statistic.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Sep 2008 19:51:40 +0000 (19:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Sep 2008 19:51:40 +0000 (19:51 +0000)
Simon Riggs, with some editorialization by me.

src/backend/utils/adt/selfuncs.c
src/backend/utils/cache/lsyscache.c
src/include/utils/lsyscache.h
src/include/utils/selfuncs.h

index 7524e2d5809b0e2a78fa9755a0f207d9cc99c194..e2336ea4f21c0cacccb10b29b7e8f8265037f854 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.253 2008/08/25 22:42:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.254 2008/09/28 19:51:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/syscache.h"
 
 
+/* Hooks for plugins to get control when we ask for stats */
+get_relation_stats_hook_type get_relation_stats_hook = NULL;
+get_index_stats_hook_type get_index_stats_hook = NULL;
+
 static double var_eq_const(VariableStatData *vardata, Oid operator,
                         Datum constval, bool constisnull,
                         bool varonleft);
@@ -2935,7 +2939,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows)
                 * complicated.
                 */
                examine_variable(root, groupexpr, 0, &vardata);
-               if (vardata.statsTuple != NULL || vardata.isunique)
+               if (HeapTupleIsValid(vardata.statsTuple) || vardata.isunique)
                {
                        varinfos = add_unique_group_var(root, varinfos,
                                                                                        groupexpr, &vardata);
@@ -3942,6 +3946,7 @@ get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo,
  *             subquery, not one in the current query).
  *     statsTuple: the pg_statistic entry for the variable, if one exists;
  *             otherwise NULL.
+ *     freefunc: pointer to a function to release statsTuple with.
  *     vartype: exposed type of the expression; this should always match
  *             the declared input type of the operator we are estimating for.
  *     atttype, atttypmod: type data to pass to get_attstatsslot().  This is
@@ -3986,7 +3991,18 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
 
                rte = root->simple_rte_array[var->varno];
 
-               if (rte->inh)
+               if (get_relation_stats_hook &&
+                       (*get_relation_stats_hook) (root, rte, var->varattno, vardata))
+               {
+                       /*
+                        * The hook took control of acquiring a stats tuple.  If it
+                        * did supply a tuple, it'd better have supplied a freefunc.
+                        */
+                       if (HeapTupleIsValid(vardata->statsTuple) &&
+                               !vardata->freefunc)
+                               elog(ERROR, "no function provided to release variable stats with");
+               }
+               else if (rte->inh)
                {
                        /*
                         * XXX This means the Var represents a column of an append
@@ -4000,6 +4016,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
                                                                                                 ObjectIdGetDatum(rte->relid),
                                                                                                 Int16GetDatum(var->varattno),
                                                                                                 0, 0);
+                       vardata->freefunc = ReleaseSysCache;
                }
                else
                {
@@ -4116,10 +4133,28 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
                                                        index->indpred == NIL)
                                                        vardata->isunique = true;
                                                /* Has it got stats? */
-                                               vardata->statsTuple = SearchSysCache(STATRELATT,
-                                                                                  ObjectIdGetDatum(index->indexoid),
-                                                                                                         Int16GetDatum(pos + 1),
-                                                                                                                        0, 0);
+                                               if (get_index_stats_hook &&
+                                                       (*get_index_stats_hook) (root, index->indexoid,
+                                                                                                        pos + 1, vardata))
+                                               {
+                                                       /*
+                                                        * The hook took control of acquiring a stats
+                                                        * tuple.  If it did supply a tuple, it'd better
+                                                        * have supplied a freefunc.
+                                                        */
+                                                       if (HeapTupleIsValid(vardata->statsTuple) &&
+                                                               !vardata->freefunc)
+                                                               elog(ERROR, "no function provided to release variable stats with");
+                                               }
+                                               else
+                                               {
+                                                       vardata->statsTuple =
+                                                               SearchSysCache(STATRELATT,
+                                                                                          ObjectIdGetDatum(index->indexoid),
+                                                                                          Int16GetDatum(pos + 1),
+                                                                                          0, 0);
+                                                       vardata->freefunc = ReleaseSysCache;
+                                               }
                                                if (vardata->statsTuple)
                                                        break;
                                        }
@@ -5551,7 +5586,7 @@ btcostestimate(PG_FUNCTION_ARGS)
        double     *indexCorrelation = (double *) PG_GETARG_POINTER(7);
        Oid                     relid;
        AttrNumber      colnum;
-       HeapTuple       tuple;
+       VariableStatData vardata;
        double          numIndexTuples;
        List       *indexBoundQuals;
        int                     indexcol;
@@ -5756,17 +5791,34 @@ btcostestimate(PG_FUNCTION_ARGS)
                colnum = 1;
        }
 
-       tuple = SearchSysCache(STATRELATT,
-                                                  ObjectIdGetDatum(relid),
-                                                  Int16GetDatum(colnum),
-                                                  0, 0);
+       MemSet(&vardata, 0, sizeof(vardata));
 
-       if (HeapTupleIsValid(tuple))
+       if (get_index_stats_hook &&
+               (*get_index_stats_hook) (root, relid, colnum, &vardata))
+       {
+               /*
+                * The hook took control of acquiring a stats tuple.  If it did supply
+                * a tuple, it'd better have supplied a freefunc.
+                */
+               if (HeapTupleIsValid(vardata.statsTuple) &&
+                       !vardata.freefunc)
+                       elog(ERROR, "no function provided to release variable stats with");
+       }
+       else
+       {
+               vardata.statsTuple = SearchSysCache(STATRELATT,
+                                                                                       ObjectIdGetDatum(relid),
+                                                                                       Int16GetDatum(colnum),
+                                                                                       0, 0);
+               vardata.freefunc = ReleaseSysCache;
+       }
+
+       if (HeapTupleIsValid(vardata.statsTuple))
        {
                float4     *numbers;
                int                     nnumbers;
 
-               if (get_attstatsslot(tuple, InvalidOid, 0,
+               if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
                                                         STATISTIC_KIND_CORRELATION,
                                                         index->fwdsortop[0],
                                                         NULL, NULL, &numbers, &nnumbers))
@@ -5783,7 +5835,7 @@ btcostestimate(PG_FUNCTION_ARGS)
 
                        free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
                }
-               else if (get_attstatsslot(tuple, InvalidOid, 0,
+               else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
                                                                  STATISTIC_KIND_CORRELATION,
                                                                  index->revsortop[0],
                                                                  NULL, NULL, &numbers, &nnumbers))
@@ -5800,9 +5852,10 @@ btcostestimate(PG_FUNCTION_ARGS)
 
                        free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
                }
-               ReleaseSysCache(tuple);
        }
 
+       ReleaseVariableStats(vardata);
+
        PG_RETURN_VOID();
 }
 
index ed94af6d494e963dafeb625be7ed731ef673528a..60ec8e5894106af45aa876a36166794b5c82c617 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.159 2008/08/02 21:32:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.160 2008/09/28 19:51:40 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
@@ -35,6 +35,9 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
+/* Hook for plugins to get control in get_attavgwidth() */
+get_attavgwidth_hook_type get_attavgwidth_hook = NULL;
+
 
 /*                             ---------- AMOP CACHES ----------                                                */
 
@@ -2492,20 +2495,30 @@ get_typmodout(Oid typid)
  *
  *       Given the table and attribute number of a column, get the average
  *       width of entries in the column.  Return zero if no data available.
+ *
+ * Calling a hook at this point looks somewhat strange, but is required
+ * because the optimizer calls this function without any other way for
+ * plug-ins to control the result.
  */
 int32
 get_attavgwidth(Oid relid, AttrNumber attnum)
 {
        HeapTuple       tp;
+       int32           stawidth;
 
+       if (get_attavgwidth_hook)
+       {
+               stawidth = (*get_attavgwidth_hook) (relid, attnum);
+               if (stawidth > 0)
+                       return stawidth;
+       }
        tp = SearchSysCache(STATRELATT,
                                                ObjectIdGetDatum(relid),
                                                Int16GetDatum(attnum),
                                                0, 0);
        if (HeapTupleIsValid(tp))
        {
-               int32           stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
-
+               stawidth = ((Form_pg_statistic) GETSTRUCT(tp))->stawidth;
                ReleaseSysCache(tp);
                if (stawidth > 0)
                        return stawidth;
@@ -2523,6 +2536,9 @@ get_attavgwidth(Oid relid, AttrNumber attnum)
  * already-looked-up tuple in the pg_statistic cache.  We do this since
  * most callers will want to extract more than one value from the cache
  * entry, and we don't want to repeat the cache lookup unnecessarily.
+ * Also, this API allows this routine to be used with statistics tuples
+ * that have been provided by a stats hook and didn't really come from
+ * pg_statistic.
  *
  * statstuple: pg_statistics tuple to be examined.
  * atttype: type OID of attribute (can be InvalidOid if values == NULL).
index 0538e0ff56989ff2904d4ce34079239785eff41f..48ecd276c7390883458171fa2ece84f769197838 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.125 2008/08/02 21:32:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.126 2008/09/28 19:51:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,10 @@ typedef enum IOFuncSelector
        IOFunc_send
 } IOFuncSelector;
 
+/* Hook for plugins to get control in get_attavgwidth() */
+typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum);
+extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook;
+
 extern bool op_in_opfamily(Oid opno, Oid opfamily);
 extern int     get_op_opfamily_strategy(Oid opno, Oid opfamily);
 extern void get_op_opfamily_properties(Oid opno, Oid opfamily,
index 120269eee5dcf6a8b8a4c7499d37a136487218fb..74ee9cd197eba6f0899d2812a970fa2e08b500ae 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.46 2008/08/16 00:01:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/selfuncs.h,v 1.47 2008/09/28 19:51:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 
 /* Return data from examine_variable and friends */
-typedef struct
+typedef struct VariableStatData
 {
        Node       *var;                        /* the Var or expression tree */
        RelOptInfo *rel;                        /* Relation, or NULL if not identifiable */
        HeapTuple       statsTuple;             /* pg_statistic tuple, or NULL if none */
        /* NB: if statsTuple!=NULL, it must be freed when caller is done */
+       void            (*freefunc) (HeapTuple tuple);  /* how to free statsTuple */
        Oid                     vartype;                /* exposed type of expression */
        Oid                     atttype;                /* type to pass to get_attstatsslot */
        int32           atttypmod;              /* typmod to pass to get_attstatsslot */
@@ -79,7 +80,7 @@ typedef struct
 #define ReleaseVariableStats(vardata)  \
        do { \
                if (HeapTupleIsValid((vardata).statsTuple)) \
-                       ReleaseSysCache((vardata).statsTuple); \
+                       (* (vardata).freefunc) ((vardata).statsTuple); \
        } while(0)
 
 
@@ -97,6 +98,18 @@ typedef enum
 
 /* selfuncs.c */
 
+/* Hooks for plugins to get control when we ask for stats */
+typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
+                                                                                         RangeTblEntry *rte,
+                                                                                         AttrNumber attnum,
+                                                                                         VariableStatData *vardata);
+extern PGDLLIMPORT get_relation_stats_hook_type get_relation_stats_hook;
+typedef bool (*get_index_stats_hook_type) (PlannerInfo *root,
+                                                                                  Oid indexOid,
+                                                                                  AttrNumber indexattnum,
+                                                                                  VariableStatData *vardata);
+extern PGDLLIMPORT get_index_stats_hook_type get_index_stats_hook;
+
 extern void examine_variable(PlannerInfo *root, Node *node, int varRelid,
                                 VariableStatData *vardata);
 extern bool get_restriction_variable(PlannerInfo *root, List *args,