OSDN Git Service

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