OSDN Git Service

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