OSDN Git Service

Support PostgreSQL 10 beta 1
[pgdbmsstats/pg_dbms_stats.git] / pg_dbms_stats.c
1 /*
2  * pg_dbms_stats.c
3  *
4  * Copyright (c) 2009-2016, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  */
8 #include "postgres.h"
9
10 #include "access/transam.h"
11 #include "catalog/pg_statistic.h"
12 #include "catalog/pg_type.h"
13 #include "catalog/namespace.h"
14 #include "catalog/pg_authid.h"
15 #include "commands/trigger.h"
16 #include "executor/spi.h"
17 #include "funcapi.h"
18 #include "optimizer/plancat.h"
19 #include "parser/parse_oper.h"
20 #include "storage/bufmgr.h"
21 #include "utils/builtins.h"
22 #include "utils/elog.h"
23 #include "utils/guc.h"
24 #include "utils/inval.h"
25 #include "utils/lsyscache.h"
26 #include "utils/selfuncs.h"
27 #include "utils/syscache.h"
28 #include "miscadmin.h"
29 #if PG_VERSION_NUM >= 90200
30 #include "utils/rel.h"
31 #endif
32 #if PG_VERSION_NUM >= 90300
33 #include "access/htup_details.h"
34 #include "utils/catcache.h"
35 #endif
36 #if PG_VERSION_NUM >= 100000
37 #include <math.h>
38 #endif
39
40 #include "pg_dbms_stats.h"
41
42 PG_MODULE_MAGIC;
43
44 /* Error levels used by pg_dbms_stats */
45 #define ELEVEL_DEBUG            DEBUG3  /* log level for debug information */
46 #define ELEVEL_BADSTATS         LOG             /* log level for invalid statistics */
47
48 #define MAX_REL_CACHE           50              /* expected max # of rel stats entries */
49
50 /* Relation statistics cache entry */
51 typedef struct StatsRelationEntry
52 {
53         Oid                                     relid;          /* hash key must be at the head */
54
55         bool                            valid;          /* T if the entry has valid stats */
56
57         BlockNumber                     relpages;       /* # of pages as of last ANALYZE */
58         double                          reltuples;      /* # of tuples as of last ANALYZE */
59         BlockNumber                     relallvisible;  /* # of all-visible pages as of last
60                                                                                  * ANALYZE */
61         BlockNumber                     curpages;       /* # of pages as of lock/restore */
62
63         List                       *col_stats;  /* list of StatsColumnEntry, each element
64                                                                            of which is pg_statistic record of this
65                                                                            relation. */
66 } StatsRelationEntry;
67
68 /*
69  * Column statistics cache entry. This is for list item for
70  * StatsRelationEntry.col_stats.
71  */
72 typedef struct StatsColumnEntry
73 {
74   bool          negative;
75   int32         attnum;
76   bool          inh;
77   HeapTuple tuple;
78 } StatsColumnEntry;
79
80 /* Saved hook functions */
81 get_relation_info_hook_type             prev_get_relation_info = NULL;
82 get_attavgwidth_hook_type               prev_get_attavgwidth = NULL;
83 get_relation_stats_hook_type    prev_get_relation_stats = NULL;
84 get_index_stats_hook_type               prev_get_index_stats = NULL;
85
86 /* namings */
87 #define NSPNAME "dbms_stats"
88 #define RELSTAT_TBLNAME "relation_stats_locked"
89 #define COLSTAT_TBLNAME "column_stats_locked"
90
91 /* rows_query(oid) RETURNS int4, float4, int4 */
92 static const char  *rows_query =
93         "SELECT relpages, reltuples, curpages"
94 #if PG_VERSION_NUM >= 90200
95         ", relallvisible"
96 #endif
97         "  FROM " NSPNAME "." RELSTAT_TBLNAME
98         " WHERE relid = $1";
99 static SPIPlanPtr       rows_plan = NULL;
100
101 /* tuple_query(oid, int2, bool) RETURNS pg_statistic */
102 static const char  *tuple_query =
103         "SELECT * "
104         "  FROM " NSPNAME "." COLSTAT_TBLNAME
105         " WHERE starelid = $1 "
106         "   AND staattnum = $2 "
107         "   AND stainherit = $3";
108 static SPIPlanPtr       tuple_plan = NULL;
109
110 /* GUC variables */
111 static bool                     pg_dbms_stats_use_locked_stats = true;
112
113 /* Current nesting depth of SPI calls, used to prevent recursive calls */
114 static int                      nested_level = 0;
115
116 /*
117  * The relation_stats_effective statistic cache is stored in hash table.
118  */
119 static HTAB        *rel_stats;
120
121 /*
122  * The owner of pg_dbms_stats statistic tables.
123  */
124 static Oid      stats_table_owner = InvalidOid;
125 static char *stats_table_owner_name = "";
126
127 #define get_pg_statistic(tuple) ((Form_pg_statistic) GETSTRUCT(tuple))
128
129 PG_FUNCTION_INFO_V1(dbms_stats_merge);
130 PG_FUNCTION_INFO_V1(dbms_stats_invalidate_relation_cache);
131 PG_FUNCTION_INFO_V1(dbms_stats_invalidate_column_cache);
132 PG_FUNCTION_INFO_V1(dbms_stats_is_system_schema);
133 PG_FUNCTION_INFO_V1(dbms_stats_is_system_catalog);
134 PG_FUNCTION_INFO_V1(dbms_stats_anyary_anyary);
135 PG_FUNCTION_INFO_V1(dbms_stats_type_is_analyzable);
136 PG_FUNCTION_INFO_V1(dbms_stats_anyarray_basetype);
137
138 extern Datum dbms_stats_merge(PG_FUNCTION_ARGS);
139 extern Datum dbms_stats_invalidate_relation_cache(PG_FUNCTION_ARGS);
140 extern Datum dbms_stats_invalidate_column_cache(PG_FUNCTION_ARGS);
141 extern Datum dbms_stats_is_system_schema(PG_FUNCTION_ARGS);
142 extern Datum dbms_stats_is_system_catalog(PG_FUNCTION_ARGS);
143 extern Datum dbms_stats_anyary_anyary(PG_FUNCTION_ARGS);
144 extern Datum dbms_stats_type_is_analyzable(PG_FUNCTION_ARGS);
145 extern Datum dbms_stats_anyarray_basetype(PG_FUNCTION_ARGS);
146
147 static HeapTuple dbms_stats_merge_internal(HeapTuple lhs, HeapTuple rhs,
148         TupleDesc tupledesc);
149 static void dbms_stats_check_tg_event(FunctionCallInfo fcinfo,
150         TriggerData *trigdata, HeapTuple *invtup, HeapTuple *rettup);
151 static void dbms_stats_invalidate_cache_internal(Oid relid, bool sta_col);
152
153 /* Module callbacks */
154 void    _PG_init(void);
155 void    _PG_fini(void);
156
157 static void dbms_stats_get_relation_info(PlannerInfo *root, Oid relid,
158         bool inhparent, RelOptInfo *rel);
159 static int32 dbms_stats_get_attavgwidth(Oid relid, AttrNumber attnum);
160 static bool dbms_stats_get_relation_stats(PlannerInfo *root, RangeTblEntry *rte,
161         AttrNumber attnum, VariableStatData *vardata);
162 static bool dbms_stats_get_index_stats(PlannerInfo *root, Oid indexOid,
163         AttrNumber indexattnum, VariableStatData *vardata);
164
165 static void get_merged_relation_stats(Oid relid, BlockNumber *pages,
166         double *tuples, double *allvisfrac, bool estimate);
167 static int32 get_merged_avgwidth(Oid relid, AttrNumber attnum);
168 static HeapTuple get_merged_column_stats(Oid relid, AttrNumber attnum,
169         bool inh);
170 static HeapTuple column_cache_search(Oid relid, AttrNumber attnum,
171                                                                          bool inh, bool*negative);
172 static HeapTuple column_cache_enter(Oid relid, int32 attnum, bool inh,
173                                                                         HeapTuple tuple);
174 static bool execute_plan(SPIPlanPtr *plan, const char *query, Oid relid,
175         const AttrNumber *attnum, bool inh);
176 static void StatsCacheRelCallback(Datum arg, Oid relid);
177 static void init_rel_stats(void);
178 static void init_rel_stats_entry(StatsRelationEntry *entry, Oid relid);
179 /* copied from PG core source tree */
180 static void dbms_stats_estimate_rel_size(Relation rel, int32 *attr_widths,
181                                   BlockNumber *pages, double *tuples, double *allvisfrac,
182                                   BlockNumber curpages);
183 static int32 dbms_stats_get_rel_data_width(Relation rel, int32 *attr_widths);
184
185 /* Unit test suit functions */
186 #ifdef UNIT_TEST
187 extern void test_import(int *passed, int *total);
188 extern void test_dump(int *passed, int *total);
189 extern void test_pg_dbms_stats(int *passed, int *total);
190 #endif
191
192 /* SPI_keepplan() is since 9.2  */
193 #if PG_VERSION_NUM < 90200
194 #define SPI_keepplan(pplan) {\
195 SPIPlanPtr tp = *plan;\
196         *plan = SPI_saveplan(tp);\
197         SPI_freeplan(tp);\
198 }
199 #endif
200
201 /*
202  * Module load callback
203  */
204 void
205 _PG_init(void)
206 {
207         /* Execute unit test cases */
208 #ifdef UNIT_TEST
209         {
210                 int passed = 0;
211                 int total = 0;
212
213                 test_import(&passed, &total);
214                 test_dump(&passed, &total);
215                 test_pg_dbms_stats(&passed, &total);
216
217                 elog(WARNING, "TOTAL %d/%d passed", passed, total);
218         }
219 #endif
220
221         /* Define custom GUC variables. */
222         DefineCustomBoolVariable("pg_dbms_stats.use_locked_stats",
223                                                          "Enable user defined statistics.",
224                                                          NULL,
225                                                          &pg_dbms_stats_use_locked_stats,
226                                                          true,
227                                                          PGC_USERSET,
228                                                          0,
229                                                          NULL,
230                                                          NULL,
231                                                          NULL);
232
233         EmitWarningsOnPlaceholders("pg_dbms_stats");
234
235         /* Back up old hooks, and install ours. */
236         prev_get_relation_info = get_relation_info_hook;
237         get_relation_info_hook = dbms_stats_get_relation_info;
238         prev_get_attavgwidth = get_attavgwidth_hook;
239         get_attavgwidth_hook = dbms_stats_get_attavgwidth;
240         prev_get_relation_stats = get_relation_stats_hook;
241         get_relation_stats_hook = dbms_stats_get_relation_stats;
242         prev_get_index_stats = get_index_stats_hook;
243         get_index_stats_hook = dbms_stats_get_index_stats;
244
245         /* Initialize hash table for statistics caching. */
246         init_rel_stats();
247
248         /* Also set up a callback for relcache SI invalidations */
249         CacheRegisterRelcacheCallback(StatsCacheRelCallback, (Datum) 0);
250 }
251
252 /*
253  * Module unload callback
254  */
255 void
256 _PG_fini(void)
257 {
258         /* Restore old hooks. */
259         get_relation_info_hook = prev_get_relation_info;
260         get_attavgwidth_hook = prev_get_attavgwidth;
261         get_relation_stats_hook = prev_get_relation_stats;
262         get_index_stats_hook = prev_get_index_stats;
263
264         /* A function to unregister callback for relcache is NOT provided. */
265 }
266
267 /*
268  * Function to convert from any array from dbms_stats.anyarray.
269  */
270 Datum
271 dbms_stats_anyary_anyary(PG_FUNCTION_ARGS)
272 {
273   ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0);
274   if (ARR_NDIM(arr) != 1)
275           elog(ERROR, "array must be one-dimentional.");
276
277   PG_RETURN_ARRAYTYPE_P(arr);
278 }
279
280 /*
281  * Function to check if the type can have statistics.
282  */
283 Datum
284 dbms_stats_type_is_analyzable(PG_FUNCTION_ARGS)
285 {
286         Oid typid = PG_GETARG_OID(0);
287         Oid     eqopr;
288
289         if (!OidIsValid(typid))
290                 PG_RETURN_BOOL(false);
291
292         get_sort_group_operators(typid, false, false, false,
293                                                          NULL, &eqopr, NULL,
294                                                          NULL);
295         PG_RETURN_BOOL(OidIsValid(eqopr));
296 }
297
298 /*
299  * Function to get base type of the value of the type dbms_stats.anyarray.
300  */
301 Datum
302 dbms_stats_anyarray_basetype(PG_FUNCTION_ARGS)
303 {
304         ArrayType  *arr = PG_GETARG_ARRAYTYPE_P(0);
305         Oid                     elemtype = arr->elemtype;
306         HeapTuple       tp;
307         Form_pg_type typtup;
308         Name            result;
309
310         if (!OidIsValid(elemtype))
311                 elog(ERROR, "invalid base type oid: %u", elemtype);
312
313         tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elemtype));
314         if (!HeapTupleIsValid(tp))  /* I trust you. */
315                 elog(ERROR, "invalid base type oid: %u", elemtype);
316
317         typtup = (Form_pg_type) GETSTRUCT(tp);
318         result = (Name) palloc0(NAMEDATALEN);
319         StrNCpy(NameStr(*result), NameStr(typtup->typname), NAMEDATALEN);
320
321         ReleaseSysCache(tp);
322         PG_RETURN_NAME(result);
323 }
324
325 /*
326  * Find and store the owner of the dummy statistics table.
327  *
328  * We will access statistics tables using this owner
329  */
330 static Oid
331 get_stats_table_owner(void)
332 {
333         HeapTuple tp;
334
335         if (!OidIsValid(stats_table_owner))
336         {
337                 tp = SearchSysCache2(RELNAMENSP,
338                                          PointerGetDatum(RELSTAT_TBLNAME),
339                                          ObjectIdGetDatum(get_namespace_oid(NSPNAME, false)));
340                 if (!HeapTupleIsValid(tp))
341                         elog(ERROR, "table \"%s.%s\" not found in pg_class",
342                                  NSPNAME, RELSTAT_TBLNAME);
343                 stats_table_owner =     ((Form_pg_class) GETSTRUCT(tp))->relowner;
344                 if (!OidIsValid(stats_table_owner))
345                         elog(ERROR, "owner uid of table \"%s.%s\" is invalid",
346                                  NSPNAME, RELSTAT_TBLNAME);
347                 ReleaseSysCache(tp);
348
349                 tp = SearchSysCache1(AUTHOID, ObjectIdGetDatum(stats_table_owner));
350                 if (!HeapTupleIsValid(tp))
351                 {
352                         elog(ERROR,
353                                  "role id %u for the owner of the relation \"%s.%s\"is invalid",
354                                  stats_table_owner, NSPNAME, RELSTAT_TBLNAME);
355                 }
356                 /* This will be done once for the session, so not pstrdup. */
357                 stats_table_owner_name =
358                         strdup(NameStr(((Form_pg_authid) GETSTRUCT(tp))->rolname));
359                 ReleaseSysCache(tp);
360         }
361         return stats_table_owner;
362 }
363
364 /*
365  * Store heap tuple header into given heap tuple.
366  */
367 static void
368 AssignHeapTuple(HeapTuple htup, HeapTupleHeader header)
369 {
370         htup->t_len = HeapTupleHeaderGetDatumLength(header);
371         ItemPointerSetInvalid(&htup->t_self);
372         htup->t_tableOid = InvalidOid;
373         htup->t_data = header;
374 }
375
376 /*
377  * dbms_stats_merge
378  *   called by sql function 'dbms_stats.merge', and return the execution result
379  *   of the function 'dbms_stats_merge_internal'.
380  */
381 Datum
382 dbms_stats_merge(PG_FUNCTION_ARGS)
383 {
384         HeapTupleData   lhs;
385         HeapTupleData   rhs;
386         TupleDesc               tupdesc;
387         HeapTuple               ret = NULL;
388
389         /* assign HeapTuple of the left statistics data unless null. */
390         if (PG_ARGISNULL(0))
391                 lhs.t_data = NULL;
392         else
393                 AssignHeapTuple(&lhs, PG_GETARG_HEAPTUPLEHEADER(0));
394
395         /* assign HeapTuple of the right statistics data unless null. */
396         if (PG_ARGISNULL(1))
397                 rhs.t_data = NULL;
398         else
399                 AssignHeapTuple(&rhs, PG_GETARG_HEAPTUPLEHEADER(1));
400
401         /* fast path for one-side is null */
402         if (lhs.t_data == NULL && rhs.t_data == NULL)
403                 PG_RETURN_NULL();
404
405         /* build a tuple descriptor for our result type */
406         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
407                 elog(ERROR, "return type must be a row type");
408
409         /* merge two statistics tuples into one, and return it */
410         ret = dbms_stats_merge_internal(&lhs, &rhs, tupdesc);
411
412         if (ret)
413                 PG_RETURN_DATUM(HeapTupleGetDatum(ret));
414         else
415                 PG_RETURN_NULL();
416 }
417
418 /*
419  * dbms_stats_merge_internal
420  *   merge the dummy statistic (lhs) and the true statistic (rhs), on the basis
421  *   of given TupleDesc.
422  *
423  *   this function doesn't become an error level of ERROR to meet that the 
424  *   result of the SQL is not affected by the query plan.
425  */
426 static HeapTuple
427 dbms_stats_merge_internal(HeapTuple lhs, HeapTuple rhs, TupleDesc tupdesc)
428 {
429         Datum                   values[Natts_pg_statistic];
430         bool                    nulls[Natts_pg_statistic];
431         int                             i;
432         Oid                             atttype = InvalidOid;
433         Oid                             relid;
434         AttrNumber              attnum;
435
436         /* fast path for both-sides are null */
437         if ((lhs == NULL || lhs->t_data == NULL) &&
438                 (rhs == NULL || rhs->t_data == NULL))
439                 return NULL;
440
441         /* fast path for one-side is null */
442         if (lhs == NULL || lhs->t_data == NULL)
443         {
444                 /* use right tuple */
445                 heap_deform_tuple(rhs, tupdesc, values, nulls);
446                 for (i = 0; i < Anum_pg_statistic_staop1 + STATISTIC_NUM_SLOTS - 1; i++)
447                         if (nulls[i])
448                                 return NULL;    /* check null constraints */
449         }
450         else if (rhs == NULL || rhs->t_data == NULL)
451         {
452                 /* use left tuple */
453                 heap_deform_tuple(lhs, tupdesc, values, nulls);
454                 for (i = 0; i < Anum_pg_statistic_staop1 + STATISTIC_NUM_SLOTS - 1; i++)
455                         if (nulls[i])
456                                 return NULL;    /* check null constraints */
457         }
458         else
459         {
460                 /*
461                  * If the column value of the dummy statistic is not NULL, in the
462                  * statistics except the slot, use it.  Otherwise we use the column
463                  * value of the true statistic.
464                  */
465                 heap_deform_tuple(lhs, tupdesc, values, nulls);
466                 for (i = 0; i < Anum_pg_statistic_stakind1 - 1; i++)
467                 {
468                         if (nulls[i])
469                         {
470                                 values[i] = fastgetattr(rhs, i + 1, tupdesc, &nulls[i]);
471                                 if (nulls[i])
472                                 {
473                                         ereport(ELEVEL_BADSTATS,
474                                                 (errmsg("pg_dbms_stats: bad statistics"),
475                                                  errdetail("column \"%s\" should not be null",
476                                                         get_attname(StatisticRelationId,
477                                                                                 tupdesc->attrs[i]->attnum))));
478                                         return NULL;    /* should not be null */
479                                 }
480                         }
481                 }
482
483                 /*
484                  * If the column value of the dummy statistic is not all NULL, in the
485                  * statistics the slot, use it.  Otherwise we use the column
486                  * value of the true statistic.
487                  */
488                 for (; i < Anum_pg_statistic_staop1 + STATISTIC_NUM_SLOTS - 1; i++)
489                 {
490                         if (nulls[i])
491                         {
492                                 for (i = Anum_pg_statistic_stakind1 - 1;
493                                          i < Anum_pg_statistic_stavalues1 + STATISTIC_NUM_SLOTS - 1;
494                                          i++)
495                                 {
496                                         values[i] = fastgetattr(rhs, i + 1, tupdesc, &nulls[i]);
497                                         if (i < Anum_pg_statistic_staop1 + STATISTIC_NUM_SLOTS - 1 &&
498                                                 nulls[i])
499                                         {
500                                                 ereport(ELEVEL_BADSTATS,
501                                                         (errmsg("pg_dbms_stats: bad statistics"),
502                                                          errdetail("column \"%s\" should not be null",
503                                                                 get_attname(StatisticRelationId,
504                                                                                         tupdesc->attrs[i]->attnum))));
505                                                 return NULL;    /* should not be null */
506                                         }
507                                 }
508
509                                 break;
510                         }
511                 }
512         }
513
514         /*
515          * Verify types to work around for ALTER COLUMN TYPE.
516          *
517          * Note: We don't need to retrieve atttype when the attribute doesn't have
518          * neither Most-Common-Value nor Histogram, but we retrieve it always
519          * because it's not usual.
520          */
521         relid = DatumGetObjectId(values[0]);
522         attnum = DatumGetInt16(values[1]);
523         atttype = get_atttype(relid, attnum);
524         if (atttype == InvalidOid)
525         {
526                 ereport(WARNING,
527                 (errmsg("pg_dbms_stats: no-longer-existent column"),
528                  errdetail("relid \"%d\" or its column whose attnum is \"%d\" might be deleted",
529                                 relid, attnum),
530                          errhint("dbms_stats.clean_up_stats() would fix this.")));
531                 return NULL;
532         }
533         for (i = 0; i < STATISTIC_NUM_SLOTS; i++)
534         {
535                 if ((i + 1 == STATISTIC_KIND_MCV ||
536                          i + 1 == STATISTIC_KIND_HISTOGRAM) &&
537                         !nulls[Anum_pg_statistic_stavalues1 + i - 1])
538                 {
539                         ArrayType  *arr;
540
541                         arr = DatumGetArrayTypeP(
542                                         values[Anum_pg_statistic_stavalues1 + i - 1]);
543                         if (arr == NULL || arr->elemtype != atttype)
544                         {
545                                 const char         *attname = get_attname(relid, attnum);
546
547                                 /*
548                                  * relid and attnum must be valid here because valid atttype
549                                  * has been gotten already.
550                                  */
551                                 Assert(attname);
552                                 ereport(ELEVEL_BADSTATS,
553                                         (errmsg("pg_dbms_stats: bad column type"),
554                                          errdetail("type of column \"%s\" has been changed",
555                                                 attname),
556                                          errhint("need to execute dbms_stats.unlock('%s', '%s')",
557                                                 get_rel_name(relid), attname)));
558                                 return NULL;
559                         }
560                 }
561         }
562
563         return heap_form_tuple(tupdesc, values, nulls);
564 }
565
566 /*
567  * dbms_stats_invalidate_relation_cache
568  *   Register invalidation of the specified relation's relcache.
569  *
570  * CREATE TRIGGER dbms_stats.relation_stats_locked FOR INSERT, UPDATE, DELETE FOR EACH
571  * ROWS EXECUTE ...
572  */
573 Datum
574 dbms_stats_invalidate_relation_cache(PG_FUNCTION_ARGS)
575 {
576         TriggerData                *trigdata = (TriggerData *) fcinfo->context;
577         HeapTuple                       invtup; /* tuple to be invalidated */
578         HeapTuple                       rettup; /* tuple to be returned */
579         Datum                           value;
580         bool                            isnull;
581
582         /* make sure it's called as a before/after trigger */
583         dbms_stats_check_tg_event(fcinfo, trigdata, &invtup, &rettup);
584
585         /*
586          * assume that position of dbms_stats.relation_stats_locked.relid is head value of
587          * tuple.
588          */
589         value = fastgetattr(invtup, 1, trigdata->tg_relation->rd_att, &isnull);
590
591         /*
592          * invalidate prepared statements and force re-planning with pg_dbms_stats.
593          */
594         dbms_stats_invalidate_cache_internal((Oid)value, false);
595
596         PG_RETURN_POINTER(rettup);
597 }
598
599 /*
600  * dbms_stats_invalidate_column_cache
601  *   Register invalidation of the specified relation's relcache.
602  *
603  * CREATE TRIGGER dbms_stats.column_stats_locked FOR INSERT, UPDATE, DELETE FOR EACH
604  * ROWS EXECUTE ...
605  */
606 Datum
607 dbms_stats_invalidate_column_cache(PG_FUNCTION_ARGS)
608 {
609         TriggerData                *trigdata = (TriggerData *) fcinfo->context;
610         Form_pg_statistic       form;
611         HeapTuple                       invtup; /* tuple to be invalidated */
612         HeapTuple                       rettup; /* tuple to be returned */
613
614         /* make sure it's called as a before/after trigger */
615         dbms_stats_check_tg_event(fcinfo, trigdata, &invtup, &rettup);
616
617         /*
618          * assume that both pg_statistic and dbms_stats.column_stats_locked have the same
619          * definition.
620          */
621         form = get_pg_statistic(invtup);
622
623         /*
624          * invalidate prepared statements and force re-planning with pg_dbms_stats.
625          */
626         dbms_stats_invalidate_cache_internal(form->starelid, true);
627
628         PG_RETURN_POINTER(rettup);
629 }
630
631 static void
632 dbms_stats_check_tg_event(FunctionCallInfo fcinfo,
633                                                   TriggerData *trigdata,
634                                                   HeapTuple *invtup,
635                                                   HeapTuple *rettup)
636 {
637         /* make sure it's called as a before/after trigger */
638         if (!CALLED_AS_TRIGGER(fcinfo) ||
639                 !TRIGGER_FIRED_BEFORE(trigdata->tg_event) ||
640                 !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
641                 elog(ERROR, "pg_dbms_stats: invalid trigger call");
642
643         if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
644         {
645                 /* INSERT */
646                 *rettup = *invtup = trigdata->tg_trigtuple;
647         }
648         else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
649         {
650                 /* DELETE */
651                 *rettup = *invtup = trigdata->tg_trigtuple;
652         }
653         else
654         {
655                 /* UPDATE */
656                 *invtup = trigdata->tg_trigtuple;
657                 *rettup = trigdata->tg_newtuple;
658         }
659 }
660
661 static void
662 dbms_stats_invalidate_cache_internal(Oid relid, bool sta_col)
663 {
664         Relation        rel;
665
666         /*
667          * invalidate prepared statements and force re-planning with pg_dbms_stats.
668          */
669         rel = try_relation_open(relid, NoLock);
670         if (rel != NULL)
671         {
672                 if (sta_col &&
673                         rel->rd_rel->relkind == RELKIND_INDEX &&
674                         (rel->rd_indextuple == NULL ||
675                          heap_attisnull(rel->rd_indextuple, Anum_pg_index_indexprs)))
676                         ereport(ERROR,
677                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
678                                          errmsg("\"%s\" is an index except an index expression",
679                                                         RelationGetRelationName(rel))));
680                 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
681                         ereport(ERROR,
682                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
683                                          errmsg("\"%s\" is a composite type",
684                                                         RelationGetRelationName(rel))));
685
686                 /*
687                  * We need to invalidate relcache of underlying table too, because
688                  * CachedPlan mechanism decides to do re-planning when any relcache of
689                  * used tables was invalid at EXECUTE.
690                  */
691                 if (rel->rd_rel->relkind == RELKIND_INDEX &&
692                         rel->rd_index && OidIsValid(rel->rd_index->indrelid))
693                         CacheInvalidateRelcacheByRelid(rel->rd_index->indrelid);
694
695                 CacheInvalidateRelcache(rel);
696                 relation_close(rel, NoLock);
697         }
698 }
699
700 /*
701  * dbms_stats_is_system_schema
702  *   called by sql function 'dbms_stats.is_system_schema', and return the
703  *   result of the function 'dbms_stats_is_system_internal'.
704  */
705 Datum
706 dbms_stats_is_system_schema(PG_FUNCTION_ARGS)
707 {
708         text   *arg0;
709         char   *schema_name;
710         bool    result;
711
712         arg0 = PG_GETARG_TEXT_PP(0);
713         schema_name = text_to_cstring(arg0);
714         result = dbms_stats_is_system_schema_internal(schema_name);
715
716         PG_FREE_IF_COPY(arg0, 0);
717
718         PG_RETURN_BOOL(result);
719 }
720
721 /*
722  * dbms_stats_is_system_schema_internal
723  *   return whether the given schema contains any system catalog.  Here we
724  *   treat dbms_stats objects as system catalogs to avoid infinite loop.
725  */
726 bool
727 dbms_stats_is_system_schema_internal(char *schema_name)
728 {
729         Assert(schema_name != NULL);
730
731         /* if the schema is system_schema, return true */
732         if (strcmp(schema_name, "pg_catalog") == 0 ||
733                 strcmp(schema_name, "pg_toast") == 0 ||
734                 strcmp(schema_name, "information_schema") == 0 ||
735                 strcmp(schema_name, NSPNAME) == 0)
736                 return true;
737
738         return false;
739 }
740
741 /*
742  * dbms_stats_is_system_catalog
743  *   called by sql function 'dbms_stats.is_system_catalog', and return the
744  *   result of the function 'dbms_stats_is_system_catalog_internal'.
745  */
746 Datum
747 dbms_stats_is_system_catalog(PG_FUNCTION_ARGS)
748 {
749         Oid             relid;
750         bool    result;
751
752         if (PG_ARGISNULL(0))
753                 PG_RETURN_BOOL(true);
754
755         relid = PG_GETARG_OID(0);
756         result = dbms_stats_is_system_catalog_internal(relid);
757
758         PG_RETURN_BOOL(result);
759 }
760
761 /*
762  * dbms_stats_is_system_catalog_internal
763  *   Check whether the given relation is one of system catalogs.
764  */
765 bool
766 dbms_stats_is_system_catalog_internal(Oid relid)
767 {
768         Relation        rel;
769         char       *schema_name;
770         bool            result;
771
772         /* relid is InvalidOid */
773         if (!OidIsValid(relid))
774                 return false;
775
776         /* no such relation */
777         rel = try_relation_open(relid, NoLock);
778         if (rel == NULL)
779                 return false;
780
781         /* check by namespace name. */
782         schema_name = get_namespace_name(rel->rd_rel->relnamespace);
783         result = dbms_stats_is_system_schema_internal(schema_name);
784         relation_close(rel, NoLock);
785
786         return result;
787 }
788
789 /*
790  * dbms_stats_get_relation_info
791  *   Hook function for get_relation_info_hook, which implements post-process of
792  *   get_relation_info().
793  *
794  *   This function is designed on the basis of the fact that only expression
795  *   indexes have statistics.
796  */
797 static void
798 dbms_stats_get_relation_info(PlannerInfo *root,
799                                                          Oid relid,
800                                                          bool inhparent,
801                                                          RelOptInfo *rel)
802 {
803         ListCell   *lc;
804         double          allvisfrac; /* dummy */
805
806         /*
807          * Call previously installed hook function regardless to whether
808          * pg_dbms_stats is enabled or not.
809          */
810         if (prev_get_relation_info)
811                 prev_get_relation_info(root, relid, inhparent, rel);
812
813         /* If pg_dbms_stats is disabled, there is no more thing to do. */
814         if (!pg_dbms_stats_use_locked_stats)
815                 return;
816
817         /*
818          * Adjust stats of table itself, and stats of index
819          * relation_stats_effective as well
820          */
821
822         /*
823          * Estimate relation size --- unless it's an inheritance parent, in which
824          * case the size will be computed later in set_append_rel_pathlist, and we
825          * must leave it zero for now to avoid bollixing the total_table_pages
826          * calculation.
827          */
828         if (!inhparent)
829         {
830 #if PG_VERSION_NUM >= 90200
831                 get_merged_relation_stats(relid, &rel->pages, &rel->tuples,
832                                                                   &rel->allvisfrac, true);
833 #else
834                 get_merged_relation_stats(relid, &rel->pages, &rel->tuples,
835                                                                   &allvisfrac, true);
836 #endif
837         }
838         else
839                 return;
840
841         foreach(lc, rel->indexlist)
842         {
843                 /*
844                  * Estimate the index size.  If it's not a partial index, we lock
845                  * the number-of-tuples estimate to equal the parent table; if it
846                  * is partial then we have to use the same methods as we would for
847                  * a table, except we can be sure that the index is not larger
848                  * than the table.
849                  */
850                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(lc);
851                 bool                    estimate = info->indpred != NIL;
852
853                 get_merged_relation_stats(info->indexoid, &info->pages, &info->tuples,
854                                                                   &allvisfrac, estimate);
855
856                 if (!estimate || (estimate && info->tuples > rel->tuples))
857                         info->tuples = rel->tuples;
858         }
859 }
860
861 /*
862  * dbms_stats_get_attavgwidth
863  *   Hook function for get_attavgwidth_hook which replaces get_attavgwidth().
864  *   Returning 0 tells caller to use standard routine.
865  */
866 static int32
867 dbms_stats_get_attavgwidth(Oid relid, AttrNumber attnum)
868 {
869         if (pg_dbms_stats_use_locked_stats)
870         {
871                 int32   width = get_merged_avgwidth(relid, attnum);
872                 if (width > 0)
873                         return width;
874         }
875
876         if (prev_get_attavgwidth)
877                 return prev_get_attavgwidth(relid, attnum);
878         else
879                 return 0;
880 }
881
882 /*
883  * We do nothing here, to keep the tuple valid even after examination.
884  */
885 static void
886 FreeHeapTuple(HeapTuple tuple)
887 {
888         /* noop */
889 }
890
891 /*
892  * dbms_stats_get_relation_stats
893  *   Hook function for get_relation_stats_hook which provides custom
894  *   per-relation statistics.
895  *   Returning false tells caller to use standard (true) statistics.
896  */
897 static bool
898 dbms_stats_get_relation_stats(PlannerInfo *root,
899                                                           RangeTblEntry *rte,
900                                                           AttrNumber attnum,
901                                                           VariableStatData *vardata)
902 {
903         if (pg_dbms_stats_use_locked_stats)
904         {
905                 HeapTuple       tuple;
906
907                 tuple = get_merged_column_stats(rte->relid, attnum, rte->inh);
908                 vardata->statsTuple = tuple;
909                 if (tuple != NULL)
910                 {
911                         vardata->freefunc = FreeHeapTuple;
912                         return true;
913                 }
914         }
915
916         if (prev_get_relation_stats)
917                 return prev_get_relation_stats(root, rte, attnum, vardata);
918         else
919                 return false;
920 }
921
922 /*
923  * dbms_stats_get_index_stats
924  *   Hook function for get_index_stats_hook which provides custom per-relation
925  *   statistics.
926  *   Returning false tells caller to use standard (true) statistics.
927  */
928 static bool
929 dbms_stats_get_index_stats(PlannerInfo *root,
930                                                    Oid indexOid,
931                                                    AttrNumber indexattnum,
932                                                    VariableStatData *vardata)
933 {
934         if (pg_dbms_stats_use_locked_stats)
935         {
936                 HeapTuple       tuple;
937
938                 tuple = get_merged_column_stats(indexOid, indexattnum, false);
939                 vardata->statsTuple = tuple;
940                 if (tuple != NULL)
941                 {
942                         vardata->freefunc = FreeHeapTuple;
943                         return true;
944                 }
945         }
946
947         if (prev_get_index_stats)
948                 return prev_get_index_stats(root, indexOid, indexattnum, vardata);
949         else
950                 return false;
951 }
952
953 /*
954  * Extract binary value from given column.
955  */
956 static Datum
957 get_binary_datum(int column, bool *isnull)
958 {
959         return SPI_getbinval(SPI_tuptable->vals[0],
960                                                  SPI_tuptable->tupdesc, column, isnull);
961 }
962
963 /*
964  * get_merged_relation_stats
965  *   get the statistics of the table, # of pages and # of rows, by executing
966  *   SELECT against dbms_stats.relation_stats_locked view.
967  */
968 static void
969 get_merged_relation_stats(Oid relid, BlockNumber *pages, double *tuples,
970                                                   double *allvisfrac, bool estimate)
971 {
972         StatsRelationEntry *entry;
973         bool            found;
974         Relation        rel;
975
976         /* avoid recursive call and system objects */
977         if (nested_level > 0 || relid < FirstNormalObjectId)
978                 return;
979
980         /*
981          * pg_dbms_stats doesn't handle system catalogs and its internal relation_stats_effective
982          */
983         if (dbms_stats_is_system_catalog_internal(relid))
984                 return;
985
986         /*
987          * First, search from cache.  If we have not cached stats for given relid
988          * yet, initialize newly created entry.
989          */
990         entry = hash_search(rel_stats, &relid, HASH_ENTER, &found);
991         if (!found)
992                 init_rel_stats_entry(entry, relid);
993
994         if (entry->valid)
995         {
996                 /*
997                  * Valid entry with invalid relpage is a negative cache, which
998                  * eliminates excessive SPI calls below. Negative caches will be
999                  * invalidated again on invalidation of system relation cache, which
1000                  * occur on modification of the dummy stats tables
1001                  * dbms_stats.relation_stats_locked and column_stats_locked.
1002                  */
1003                 if (entry->relpages == InvalidBlockNumber)
1004                         return;
1005         }
1006         if (!entry->valid)
1007         {
1008                 /*
1009                  * If we don't have valid cache entry, retrieve system stats and dummy
1010                  * stats in dbms_stats.relation_stats_locked, then merge them for
1011                  * planner use.  We also cache curpages value to make plans stable.
1012                  */
1013                 bool            has_dummy;
1014
1015                 PG_TRY();
1016                 {
1017                         ++nested_level;
1018                         SPI_connect();
1019
1020                         /*
1021                          * Retrieve per-relation dummy statistics from
1022                          * relation_stats_locked table via SPI.
1023                          */
1024                         has_dummy = execute_plan(&rows_plan, rows_query, relid, NULL, true);
1025                         if (!has_dummy)
1026                         {
1027                                 /* If dummy stats is not found, store negative cache. */
1028                                 entry->relpages = InvalidBlockNumber;
1029                         }
1030                         else
1031                         {
1032                                 /*
1033                                  * Retrieve per-relation system stats from pg_class.  We use
1034                                  * syscache to support indexes
1035                                  */
1036                                 HeapTuple       tuple;
1037                                 Form_pg_class form;
1038                                 bool            isnull;
1039                                 Datum           val;
1040
1041                                 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1042                                 if (!HeapTupleIsValid(tuple))
1043                                         elog(ERROR, "cache lookup failed for relation %u", relid);
1044                                 form = (Form_pg_class) GETSTRUCT(tuple);
1045
1046                                 /* Choose dummy or authentic */
1047                                 val = get_binary_datum(1, &isnull);
1048                                 entry->relpages = isnull ? form->relpages :
1049                                         (BlockNumber) DatumGetInt32(val);
1050                                 val = get_binary_datum(2, &isnull);
1051                                 entry->reltuples = isnull ? form->reltuples :
1052                                         (double) DatumGetFloat4(val);
1053                                 val = get_binary_datum(3, &isnull);
1054                                 entry->curpages = isnull ? InvalidBlockNumber :
1055                                         (BlockNumber) DatumGetInt32(val);
1056 #if PG_VERSION_NUM >= 90200
1057                                 val = get_binary_datum(4, &isnull);
1058                                 entry->relallvisible = isnull ? form->relallvisible :
1059                                         (BlockNumber) DatumGetInt32(val);
1060 #endif
1061
1062                                 ReleaseSysCache(tuple);
1063                         }
1064                         entry->valid = true;
1065                         SPI_finish();
1066                         --nested_level;
1067                 }
1068                 PG_CATCH();
1069                 {
1070                         --nested_level;
1071                         PG_RE_THROW();
1072                 }
1073                 PG_END_TRY();
1074
1075                 /*
1076                  * If no dummy statistics available for this relation, do nothing then
1077                  * return immediately.
1078                  */
1079                 if (!has_dummy)
1080                         return;
1081         }
1082
1083         /* Tweaking statistics using merged statistics */
1084         if (!estimate)
1085         {
1086                 *pages = entry->relpages;
1087                 *tuples = entry->reltuples;
1088                 return;
1089         }
1090
1091         /*
1092          * Get current number of pages to estimate current number of tuples, based
1093          * on tuple density at the last ANALYZE and current number of pages.
1094          */
1095         rel = relation_open(relid, NoLock);
1096         rel->rd_rel->relpages = entry->relpages;
1097         rel->rd_rel->reltuples = entry->reltuples;
1098 #if PG_VERSION_NUM >= 90200
1099         rel->rd_rel->relallvisible = entry->relallvisible;
1100 #endif
1101         dbms_stats_estimate_rel_size(rel, NULL, pages, tuples, allvisfrac,
1102                                                                  entry->curpages);
1103         relation_close(rel, NoLock);
1104 }
1105
1106 /*
1107  * get_merged_avgwidth
1108  *   get average width of the given column by merging dummy and authentic
1109  *   statistics
1110  */
1111 static int32
1112 get_merged_avgwidth(Oid relid, AttrNumber attnum)
1113 {
1114         HeapTuple               tuple;
1115
1116         if (nested_level > 0 || relid < FirstNormalObjectId)
1117                 return 0;       /* avoid recursive call and system objects */
1118
1119         if ((tuple = get_merged_column_stats(relid, attnum, false)) == NULL)
1120                 return 0;
1121
1122         return get_pg_statistic(tuple)->stawidth;
1123 }
1124
1125 /*
1126  * get_merged_column_stats
1127  *   returns per-column statistics for given column
1128  *
1129  * This caches the result to avoid excessive SPI calls for repetitive
1130  * request for every columns many time.
1131  */
1132 static HeapTuple
1133 get_merged_column_stats(Oid relid, AttrNumber attnum, bool inh)
1134 {
1135         HeapTuple               tuple;
1136         HeapTuple               statsTuple;
1137         bool                    negative = false;
1138
1139         if (nested_level > 0 || relid < FirstNormalObjectId)
1140                 return NULL;    /* avoid recursive call and system objects */
1141
1142         /*
1143          * Return NULL for system catalog, directing the caller to use system
1144          * statistics.
1145          */
1146         if (dbms_stats_is_system_catalog_internal(relid))
1147                 return NULL;
1148
1149         /* Return cached statistics, if any. */
1150         if ((tuple = column_cache_search(relid, attnum, inh, &negative)) != NULL)
1151                 return tuple;
1152
1153         /* Obtain system statistics from syscache. */
1154         statsTuple = SearchSysCache3(STATRELATTINH,
1155                                                                  ObjectIdGetDatum(relid),
1156                                                                  Int16GetDatum(attnum),
1157                                                                  BoolGetDatum(inh));
1158         if (negative)
1159         {
1160                 /*
1161                  * Return system statistics whatever it is if negative cache for this
1162                  * column is returned
1163                  */
1164                 tuple = heap_copytuple(statsTuple);
1165         }
1166         else
1167         {
1168                 /*
1169                  * Search for dummy statistics and try merge with system stats.
1170                  */
1171                 PG_TRY();
1172                 {
1173                         /*
1174                          * Save current context in order to use during SPI is
1175                          * connected.
1176                          */
1177                         MemoryContext outer_cxt = CurrentMemoryContext;
1178                         bool              exec_success;
1179
1180                         ++nested_level;
1181                         SPI_connect();
1182
1183                         /* Obtain dummy statistics for the column using SPI call. */
1184                         exec_success = 
1185                                 execute_plan(&tuple_plan, tuple_query, relid, &attnum, inh);
1186
1187                         /* Reset to the outer memory context for following steps. */
1188                         MemoryContextSwitchTo(outer_cxt);
1189                         
1190                         if (exec_success)
1191                         {
1192                                 /* merge the dummy statistics with the system statistics */
1193                                 tuple = dbms_stats_merge_internal(SPI_tuptable->vals[0],
1194                                                                                                   statsTuple,
1195                                                                                                   SPI_tuptable->tupdesc);
1196                         }
1197                         else
1198                                 tuple = NULL;
1199
1200                         /* Cache merged result for subsequent calls. */
1201                         tuple = column_cache_enter(relid, attnum, inh, tuple);
1202
1203                         /* Return system stats if the merging results in failure. */
1204                         if (!HeapTupleIsValid(tuple))
1205                                 tuple = heap_copytuple(statsTuple);
1206
1207                         SPI_finish();
1208                         --nested_level;
1209                 }
1210                 PG_CATCH();
1211                 {
1212                         --nested_level;
1213                         PG_RE_THROW();
1214                 }
1215                 PG_END_TRY();
1216         }
1217
1218         if (HeapTupleIsValid(statsTuple))
1219                 ReleaseSysCache(statsTuple);
1220
1221         return tuple;
1222 }
1223
1224 /*
1225  * column_cache_search
1226  *   Search statistic of the given column from the cache.
1227  */
1228 static HeapTuple
1229 column_cache_search(Oid relid, AttrNumber attnum, bool inh, bool *negative)
1230 {
1231         StatsRelationEntry *entry;
1232         bool                    found;
1233         ListCell           *lc;
1234
1235         *negative = false;
1236         /*
1237          * First, get cached relation stats.  If we have not cached relation stats,
1238          * we don't have column stats too.
1239          */
1240         entry = hash_search(rel_stats, &relid, HASH_FIND, &found);
1241         if (!found)
1242                 return NULL;
1243
1244         /*
1245          * We assume that not so many column_stats_effective are defined on one
1246          * relation, so we use simple linear-search here.  Hash table would be an
1247          * alternative, but it seems overkill so far.
1248          */
1249         foreach(lc, entry->col_stats)
1250         {
1251                 StatsColumnEntry *ent = (StatsColumnEntry*) lfirst (lc);
1252
1253                 if (ent->attnum != attnum || ent->inh != inh) continue;
1254
1255                 if (ent->negative)
1256                 {
1257                         /* Retrun NULL for negative cache, with noticing of that.*/
1258                         *negative = true;
1259                         return NULL;
1260                 }
1261
1262                 return ent->tuple;
1263         }
1264
1265         return NULL;    /* Not yet registered. */
1266 }
1267
1268 /*
1269  * Cache a per-column statistics. Storing in CacheMemoryContext, the cached
1270  * statistics will live through the current session, unless dummy statistics or
1271  * table definition have been changed.
1272  */
1273 static HeapTuple
1274 column_cache_enter(Oid relid, int32 attnum, bool inh, HeapTuple tuple)
1275 {
1276         MemoryContext   oldcontext;
1277         StatsColumnEntry *newcolent;
1278         StatsRelationEntry *entry;
1279         bool                    found;
1280
1281         Assert(tuple == NULL || !heap_attisnull(tuple, 1));
1282
1283         entry = hash_search(rel_stats, &relid, HASH_ENTER, &found);
1284         if (!found)
1285                 init_rel_stats_entry(entry, relid);
1286
1287         /*
1288          * Adding this column stats to the column stats list of the relation stats
1289          * cache just obtained.
1290          */
1291         oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
1292         newcolent = (StatsColumnEntry*)palloc(sizeof(StatsColumnEntry));
1293         newcolent->attnum = attnum;
1294         newcolent->inh = inh;
1295
1296         if (HeapTupleIsValid(tuple))
1297         {
1298                 newcolent->negative = false;
1299                 newcolent->tuple = heap_copytuple(tuple);
1300         }
1301         else
1302         {
1303                 /* Invalid tuple makes a negative cache. */
1304                 newcolent->negative = true;
1305                 newcolent->tuple = NULL;
1306         }
1307
1308         entry->col_stats = lappend(entry->col_stats, newcolent);
1309         MemoryContextSwitchTo(oldcontext);
1310
1311         return newcolent->tuple;
1312 }
1313
1314 /*
1315  * Execute given plan.  When given plan is NULL, create new plan from given
1316  * query string, and execute it.  This function can be used only for retrieving
1317  * statistics of column_stats_effective and relation_stats_effective, because we assume #, types, and order
1318  * of parameters here.
1319  */
1320 static bool
1321 execute_plan(SPIPlanPtr *plan,
1322                          const char *query,
1323                          Oid relid,
1324                          const AttrNumber *attnum,
1325                          bool inh)
1326 {
1327         int             ret;
1328         Oid             argtypes[3] = { OIDOID, INT2OID, BOOLOID };
1329         int             nargs;
1330         Datum   values[3];
1331         bool    nulls[3] = { false, false, false };
1332         Oid                     save_userid;
1333         int                     save_sec_context;
1334
1335         /* XXXX: this works for now but should be fixed later.. */
1336         nargs = (attnum ? 3 : 1);
1337
1338         /*
1339          * The dummy statistics table allows access from no one other than its
1340          * owner or superuser.
1341          */
1342         GetUserIdAndSecContext(&save_userid, &save_sec_context);
1343         SetUserIdAndSecContext(get_stats_table_owner(),
1344                                                    save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
1345
1346         PG_TRY();
1347         {
1348                 /* Create plan from the query if not yet. */
1349                 if (*plan == NULL)
1350                 {
1351                         *plan = SPI_prepare(query, nargs, argtypes);
1352                         if (*plan == NULL)
1353                                 elog(ERROR,
1354                                          "pg_dbms_stats: SPI_prepare() failed. result = %d",
1355                                          SPI_result);
1356
1357                         SPI_keepplan(*plan);
1358                 }
1359
1360                 values[0] = ObjectIdGetDatum(relid);
1361                 values[1] = Int16GetDatum(attnum ? *attnum : 0);
1362                 values[2] = BoolGetDatum(inh);
1363
1364                 ret = SPI_execute_plan(*plan, values, nulls, true, 1);
1365         }
1366         PG_CATCH();
1367         {
1368                 SetUserIdAndSecContext(save_userid, save_sec_context);
1369                 if (geterrcode() == ERRCODE_INSUFFICIENT_PRIVILEGE)
1370                         errdetail("dbms_stats could not access the object as the role \"%s\".",
1371                                 stats_table_owner_name);
1372                 errhint("Check your settings of pg_dbms_stats.");
1373                 PG_RE_THROW();
1374         }
1375         PG_END_TRY();
1376
1377         SetUserIdAndSecContext(save_userid, save_sec_context);
1378         if (ret != SPI_OK_SELECT)
1379                 elog(ERROR, "pg_dbms_stats: SPI_execute_plan() returned %d", ret);
1380
1381         return SPI_processed > 0;
1382 }
1383
1384 /*
1385  * StatsCacheRelCallback
1386  *              Relcache inval callback function
1387  *
1388  * Invalidate cached statistic info of the given relid, or all cached statistic
1389  * info if relid == InvalidOid.  We don't complain even when we don't have such
1390  * statistics.
1391  *
1392  * Note: arg is not used.
1393  */
1394 static void
1395 StatsCacheRelCallback(Datum arg, Oid relid)
1396 {
1397         HASH_SEQ_STATUS         status;
1398         StatsRelationEntry *entry;
1399
1400         hash_seq_init(&status, rel_stats);
1401         while ((entry = hash_seq_search(&status)) != NULL)
1402         {
1403                 if (relid == InvalidOid || relid == entry->relid)
1404                 {
1405                         ListCell *lc;
1406
1407                         /* Mark the relation entry as INVALID */
1408                         entry->valid = false;
1409
1410                         /* Discard every column statistics */
1411                         foreach (lc, entry->col_stats)
1412                         {
1413                                 StatsColumnEntry *ent = (StatsColumnEntry*) lfirst(lc);
1414
1415                                 if (!ent->negative)
1416                                         pfree(ent->tuple);
1417                                 pfree(ent);
1418                         }
1419                         list_free(entry->col_stats);
1420                         entry->col_stats = NIL;
1421                 }
1422         }
1423
1424         /* We always check throughout the list, so hash_seq_term is not necessary */
1425 }
1426
1427 /*
1428  * Initialize hash table for per-relation statistics.
1429  */
1430 static void
1431 init_rel_stats(void)
1432 {
1433         HTAB       *hash;
1434         HASHCTL         ctl;
1435
1436         /* Prevent double initialization. */
1437         if (rel_stats != NULL)
1438                 return;
1439
1440         MemSet(&ctl, 0, sizeof(ctl));
1441         ctl.keysize = sizeof(Oid);
1442         ctl.entrysize = sizeof(StatsRelationEntry);
1443         ctl.hash = oid_hash;
1444         ctl.hcxt = CacheMemoryContext;
1445         hash = hash_create("dbms_stats relation statistics cache",
1446                                            MAX_REL_CACHE,
1447                                            &ctl, HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);
1448
1449         rel_stats = hash;
1450 }
1451
1452 /*
1453  * Initialize newly added cache entry so that it represents an invalid cache
1454  * entry for given relid.
1455  */
1456 static void
1457 init_rel_stats_entry(StatsRelationEntry *entry, Oid relid)
1458 {
1459         entry->relid = relid;
1460         entry->valid = false;
1461         entry->relpages = InvalidBlockNumber;
1462         entry->reltuples = 0.0;
1463         entry->relallvisible = InvalidBlockNumber;
1464         entry->curpages = InvalidBlockNumber;
1465         entry->col_stats = NIL;
1466 }
1467
1468 /*
1469  * dbms_stats_estimate_rel_size - estimate # pages and # tuples in a table or
1470  * index
1471  *
1472  * We also estimate the fraction of the pages that are marked all-visible in
1473  * the visibility map, for use in estimation of index-only scans.
1474  *
1475  * If attr_widths isn't NULL, it points to the zero-index entry of the
1476  * relation's attr_widths[] cache; we fill this in if we have need to compute
1477  * the attribute widths for estimation purposes.
1478  *
1479  * Note: This function is copied from plancat.c in core source tree of version
1480  * 9.2, and customized for pg_dbms_stats.  Changes from original one are:
1481  *   - rename by prefixing dbms_stats_
1482  *   - add 3 parameters (relpages, reltuples, curpage) to pass dummy curpage
1483  *     values.
1484  *   - Get current # of pages only when supplied curpages is InvalidBlockNumber
1485  *   - get fraction of all-visible-pages
1486  */
1487 static void
1488 dbms_stats_estimate_rel_size(Relation rel, int32 *attr_widths,
1489                                                          BlockNumber *pages, double *tuples,
1490                                                          double *allvisfrac, BlockNumber curpages)
1491 {
1492         BlockNumber relpages;
1493         double          reltuples;
1494         BlockNumber relallvisible;
1495         double          density;
1496
1497         switch (rel->rd_rel->relkind)
1498         {
1499                 case RELKIND_RELATION:
1500                 case RELKIND_INDEX:
1501 #if PG_VERSION_NUM >= 90300
1502                 case RELKIND_MATVIEW:
1503 #endif
1504                 case RELKIND_TOASTVALUE:
1505                         /* it has storage, ok to call the smgr */
1506                         if (curpages == InvalidBlockNumber)
1507                                 curpages = RelationGetNumberOfBlocks(rel);
1508
1509                         /*
1510                          * HACK: if the relation has never yet been vacuumed, use a
1511                          * minimum size estimate of 10 pages.  The idea here is to avoid
1512                          * assuming a newly-created table is really small, even if it
1513                          * currently is, because that may not be true once some data gets
1514                          * loaded into it.  Once a vacuum or analyze cycle has been done
1515                          * on it, it's more reasonable to believe the size is somewhat
1516                          * stable.
1517                          *
1518                          * (Note that this is only an issue if the plan gets cached and
1519                          * used again after the table has been filled.  What we're trying
1520                          * to avoid is using a nestloop-type plan on a table that has
1521                          * grown substantially since the plan was made.  Normally,
1522                          * autovacuum/autoanalyze will occur once enough inserts have
1523                          * happened and cause cached-plan invalidation; but that doesn't
1524                          * happen instantaneously, and it won't happen at all for cases
1525                          * such as temporary tables.)
1526                          *
1527                          * We approximate "never vacuumed" by "has relpages = 0", which
1528                          * means this will also fire on genuinely empty relation_stats_effective.       Not
1529                          * great, but fortunately that's a seldom-seen case in the real
1530                          * world, and it shouldn't degrade the quality of the plan too
1531                          * much anyway to err in this direction.
1532                          *
1533                          * There are two exceptions wherein we don't apply this heuristic.
1534                          * One is if the table has inheritance children.  Totally empty
1535                          * parent tables are quite common, so we should be willing to
1536                          * believe that they are empty.  Also, we don't apply the 10-page
1537                          * minimum to indexes.
1538                          */
1539                         if (curpages < 10 &&
1540                                 rel->rd_rel->relpages == 0 &&
1541                                 !rel->rd_rel->relhassubclass &&
1542                                 rel->rd_rel->relkind != RELKIND_INDEX)
1543                                 curpages = 10;
1544
1545                         /* report estimated # pages */
1546                         *pages = curpages;
1547                         /* quick exit if rel is clearly empty */
1548                         if (curpages == 0)
1549                         {
1550                                 *tuples = 0;
1551                                 *allvisfrac = 0;
1552                                 break;
1553                         }
1554                         /* coerce values in pg_class to more desirable types */
1555                         relpages = (BlockNumber) rel->rd_rel->relpages;
1556                         reltuples = (double) rel->rd_rel->reltuples;
1557 #if PG_VERSION_NUM >= 90200
1558                         relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
1559 #else
1560                         relallvisible = 0;
1561 #endif
1562                         /*
1563                          * If it's an index, discount the metapage while estimating the
1564                          * number of tuples.  This is a kluge because it assumes more than
1565                          * it ought to about index structure.  Currently it's OK for
1566                          * btree, hash, and GIN indexes but suspect for GiST indexes.
1567                          */
1568                         if (rel->rd_rel->relkind == RELKIND_INDEX &&
1569                                 relpages > 0)
1570                         {
1571                                 curpages--;
1572                                 relpages--;
1573                         }
1574
1575                         /* estimate number of tuples from previous tuple density */
1576                         if (relpages > 0)
1577                                 density = reltuples / (double) relpages;
1578                         else
1579                         {
1580                                 /*
1581                                  * When we have no data because the relation was truncated,
1582                                  * estimate tuple width from attribute datatypes.  We assume
1583                                  * here that the pages are completely full, which is OK for
1584                                  * tables (since they've presumably not been VACUUMed yet) but
1585                                  * is probably an overestimate for indexes.  Fortunately
1586                                  * get_relation_info() can clamp the overestimate to the
1587                                  * parent table's size.
1588                                  *
1589                                  * Note: this code intentionally disregards alignment
1590                                  * considerations, because (a) that would be gilding the lily
1591                                  * considering how crude the estimate is, and (b) it creates
1592                                  * platform dependencies in the default plans which are kind
1593                                  * of a headache for regression testing.
1594                                  */
1595                                 int32           tuple_width;
1596
1597                                 tuple_width = dbms_stats_get_rel_data_width(rel, attr_widths);
1598                                 tuple_width += sizeof(HeapTupleHeaderData);
1599                                 tuple_width += sizeof(ItemPointerData);
1600                                 /* note: integer division is intentional here */
1601                                 density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
1602                         }
1603                         *tuples = rint(density * (double) curpages);
1604
1605                         /*
1606                          * We use relallvisible as-is, rather than scaling it up like we
1607                          * do for the pages and tuples counts, on the theory that any
1608                          * pages added since the last VACUUM are most likely not marked
1609                          * all-visible.  But costsize.c wants it converted to a fraction.
1610                          */
1611                         if (relallvisible == 0 || curpages <= 0)
1612                                 *allvisfrac = 0;
1613                         else if ((double) relallvisible >= curpages)
1614                                 *allvisfrac = 1;
1615                         else
1616                                 *allvisfrac = (double) relallvisible / curpages;
1617                         break;
1618                 case RELKIND_SEQUENCE:
1619                         /* Sequences always have a known size */
1620                         *pages = 1;
1621                         *tuples = 1;
1622                         *allvisfrac = 0;
1623                         break;
1624                 case RELKIND_FOREIGN_TABLE:
1625                         /* Just use whatever's in pg_class */
1626                         *pages = rel->rd_rel->relpages;
1627                         *tuples = rel->rd_rel->reltuples;
1628                         *allvisfrac = 0;
1629                         break;
1630                 default:
1631                         /* else it has no disk storage; probably shouldn't get here? */
1632                         *pages = 0;
1633                         *tuples = 0;
1634                         *allvisfrac = 0;
1635                         break;
1636         }
1637 }
1638
1639 /*
1640  * dbms_stats_get_rel_data_width
1641  *
1642  * Estimate the average width of (the data part of) the relation's tuples.
1643  *
1644  * If attr_widths isn't NULL, it points to the zero-index entry of the
1645  * relation's attr_widths[] cache; use and update that cache as appropriate.
1646  *
1647  * Currently we ignore dropped column_stats_effective.  Ideally those should be included
1648  * in the result, but we haven't got any way to get info about them; and
1649  * since they might be mostly NULLs, treating them as zero-width is not
1650  * necessarily the wrong thing anyway.
1651  *
1652  * Note: This function is copied from plancat.c in core source tree of version
1653  * 9.2, and just renamed.
1654  */
1655 static int32
1656 dbms_stats_get_rel_data_width(Relation rel, int32 *attr_widths)
1657 {
1658         int32           tuple_width = 0;
1659         int                     i;
1660
1661         for (i = 1; i <= RelationGetNumberOfAttributes(rel); i++)
1662         {
1663                 Form_pg_attribute att = rel->rd_att->attrs[i - 1];
1664                 int32           item_width;
1665
1666                 if (att->attisdropped)
1667                         continue;
1668
1669                 /* use previously cached data, if any */
1670                 if (attr_widths != NULL && attr_widths[i] > 0)
1671                 {
1672                         tuple_width += attr_widths[i];
1673                         continue;
1674                 }
1675
1676                 /* This should match set_rel_width() in costsize.c */
1677                 item_width = get_attavgwidth(RelationGetRelid(rel), i);
1678                 if (item_width <= 0)
1679                 {
1680                         item_width = get_typavgwidth(att->atttypid, att->atttypmod);
1681                         Assert(item_width > 0);
1682                 }
1683                 if (attr_widths != NULL)
1684                         attr_widths[i] = item_width;
1685                 tuple_width += item_width;
1686         }
1687
1688         return tuple_width;
1689 }
1690
1691 #ifdef UNIT_TEST
1692 void test_pg_dbms_stats(int *passed, int *total);
1693 static void test_init_rel_stats(int *passed, int *total);
1694 static void test_init_rel_stats_entry(int *passed, int *total);
1695
1696 void
1697 test_pg_dbms_stats(int *passed, int *total)
1698 {
1699         int local_passed = 0;
1700         int local_total = 0;
1701
1702         elog(WARNING, "==========");
1703
1704         /* Do tests here */
1705         test_init_rel_stats(&local_passed, &local_total);
1706         test_init_rel_stats_entry(&local_passed, &local_total);
1707
1708         elog(WARNING, "%s %d/%d passed", __FUNCTION__, local_passed, local_total);
1709         *passed += local_passed;
1710         *total += local_total;
1711 }
1712
1713 static void
1714 test_init_rel_stats_entry(int *passed, int *total)
1715 {
1716         int             caseno = 0;
1717         StatsRelationEntry entry;
1718         
1719         /*
1720          * *-*-1
1721          */
1722         caseno++;
1723         init_rel_stats_entry(&entry, 1234);
1724         if (entry.relid == 1234 &&
1725                 entry.valid == false &&
1726                 entry.relpages == InvalidBlockNumber &&
1727                 entry.reltuples == 0 &&
1728                 entry.relallvisible == InvalidBlockNumber &&
1729                 entry.curpages == InvalidBlockNumber &&
1730                 entry.col_stats == NIL)
1731         {
1732                 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
1733                 (*passed)++;
1734         }
1735         else
1736                 elog(WARNING, "%s-%d failed: initialized", __FUNCTION__, caseno);
1737
1738         (*total) += caseno;
1739 }
1740
1741 static void
1742 test_init_rel_stats(int *passed, int *total)
1743 {
1744         int                     caseno = 0;
1745         static HTAB        *org_rel_stats;
1746
1747         /*
1748          * *-*-1
1749          *   - first call
1750          */
1751         caseno++;
1752         init_rel_stats();
1753         if (rel_stats != NULL)
1754         {
1755                 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
1756                 (*passed)++;
1757         }
1758         else
1759                 elog(WARNING, "%s-%d failed: rel_stats is NULL", __FUNCTION__, caseno);
1760
1761         /*
1762          * *-*-2
1763          *   - second call
1764          */
1765         caseno++;
1766         org_rel_stats = rel_stats;
1767         init_rel_stats();
1768         if (org_rel_stats == rel_stats)
1769         {
1770                 elog(WARNING, "%s-%d ok", __FUNCTION__, caseno);
1771                 (*passed)++;
1772         }
1773         else
1774                 elog(WARNING, "%s-%d failed: rel_stats changed from %p to %p",
1775                          __FUNCTION__, caseno, org_rel_stats, rel_stats);
1776
1777         *total += caseno;
1778 }
1779 #endif