OSDN Git Service

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