Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : * attribute_stats.c
3 : : *
4 : : * PostgreSQL relation attribute statistics manipulation.
5 : : *
6 : : * Code supporting the direct import of relation attribute statistics, similar
7 : : * to what is done by the ANALYZE command.
8 : : *
9 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : * IDENTIFICATION
13 : : * src/backend/statistics/attribute_stats.c
14 : : *
15 : : *-------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include "access/heapam.h"
21 : : #include "catalog/indexing.h"
22 : : #include "catalog/namespace.h"
23 : : #include "catalog/pg_collation.h"
24 : : #include "catalog/pg_operator.h"
25 : : #include "nodes/makefuncs.h"
26 : : #include "nodes/nodeFuncs.h"
27 : : #include "statistics/statistics.h"
28 : : #include "statistics/stat_utils.h"
29 : : #include "utils/array.h"
30 : : #include "utils/builtins.h"
31 : : #include "utils/fmgroids.h"
32 : : #include "utils/lsyscache.h"
33 : : #include "utils/syscache.h"
34 : :
35 : : #define DEFAULT_NULL_FRAC Float4GetDatum(0.0)
36 : : #define DEFAULT_AVG_WIDTH Int32GetDatum(0) /* unknown */
37 : : #define DEFAULT_N_DISTINCT Float4GetDatum(0.0) /* unknown */
38 : :
39 : : enum attribute_stats_argnum
40 : : {
41 : : ATTRELSCHEMA_ARG = 0,
42 : : ATTRELNAME_ARG,
43 : : ATTNAME_ARG,
44 : : ATTNUM_ARG,
45 : : INHERITED_ARG,
46 : : NULL_FRAC_ARG,
47 : : AVG_WIDTH_ARG,
48 : : N_DISTINCT_ARG,
49 : : MOST_COMMON_VALS_ARG,
50 : : MOST_COMMON_FREQS_ARG,
51 : : HISTOGRAM_BOUNDS_ARG,
52 : : CORRELATION_ARG,
53 : : MOST_COMMON_ELEMS_ARG,
54 : : MOST_COMMON_ELEM_FREQS_ARG,
55 : : ELEM_COUNT_HISTOGRAM_ARG,
56 : : RANGE_LENGTH_HISTOGRAM_ARG,
57 : : RANGE_EMPTY_FRAC_ARG,
58 : : RANGE_BOUNDS_HISTOGRAM_ARG,
59 : : NUM_ATTRIBUTE_STATS_ARGS
60 : : };
61 : :
62 : : static struct StatsArgInfo attarginfo[] =
63 : : {
64 : : [ATTRELSCHEMA_ARG] = {"schemaname", TEXTOID},
65 : : [ATTRELNAME_ARG] = {"relname", TEXTOID},
66 : : [ATTNAME_ARG] = {"attname", TEXTOID},
67 : : [ATTNUM_ARG] = {"attnum", INT2OID},
68 : : [INHERITED_ARG] = {"inherited", BOOLOID},
69 : : [NULL_FRAC_ARG] = {"null_frac", FLOAT4OID},
70 : : [AVG_WIDTH_ARG] = {"avg_width", INT4OID},
71 : : [N_DISTINCT_ARG] = {"n_distinct", FLOAT4OID},
72 : : [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTOID},
73 : : [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT4ARRAYOID},
74 : : [HISTOGRAM_BOUNDS_ARG] = {"histogram_bounds", TEXTOID},
75 : : [CORRELATION_ARG] = {"correlation", FLOAT4OID},
76 : : [MOST_COMMON_ELEMS_ARG] = {"most_common_elems", TEXTOID},
77 : : [MOST_COMMON_ELEM_FREQS_ARG] = {"most_common_elem_freqs", FLOAT4ARRAYOID},
78 : : [ELEM_COUNT_HISTOGRAM_ARG] = {"elem_count_histogram", FLOAT4ARRAYOID},
79 : : [RANGE_LENGTH_HISTOGRAM_ARG] = {"range_length_histogram", TEXTOID},
80 : : [RANGE_EMPTY_FRAC_ARG] = {"range_empty_frac", FLOAT4OID},
81 : : [RANGE_BOUNDS_HISTOGRAM_ARG] = {"range_bounds_histogram", TEXTOID},
82 : : [NUM_ATTRIBUTE_STATS_ARGS] = {0}
83 : : };
84 : :
85 : : enum clear_attribute_stats_argnum
86 : : {
87 : : C_ATTRELSCHEMA_ARG = 0,
88 : : C_ATTRELNAME_ARG,
89 : : C_ATTNAME_ARG,
90 : : C_INHERITED_ARG,
91 : : C_NUM_ATTRIBUTE_STATS_ARGS
92 : : };
93 : :
94 : : static struct StatsArgInfo cleararginfo[] =
95 : : {
96 : : [C_ATTRELSCHEMA_ARG] = {"relation", TEXTOID},
97 : : [C_ATTRELNAME_ARG] = {"relation", TEXTOID},
98 : : [C_ATTNAME_ARG] = {"attname", TEXTOID},
99 : : [C_INHERITED_ARG] = {"inherited", BOOLOID},
100 : : [C_NUM_ATTRIBUTE_STATS_ARGS] = {0}
101 : : };
102 : :
103 : : static bool attribute_statistics_update(FunctionCallInfo fcinfo);
104 : : static Node *get_attr_expr(Relation rel, int attnum);
105 : : static void get_attr_stat_type(Oid reloid, AttrNumber attnum,
106 : : Oid *atttypid, int32 *atttypmod,
107 : : char *atttyptype, Oid *atttypcoll,
108 : : Oid *eq_opr, Oid *lt_opr);
109 : : static bool get_elem_stat_type(Oid atttypid, char atttyptype,
110 : : Oid *elemtypid, Oid *elem_eq_opr);
111 : : static Datum text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d,
112 : : Oid typid, int32 typmod, bool *ok);
113 : : static void set_stats_slot(Datum *values, bool *nulls, bool *replaces,
114 : : int16 stakind, Oid staop, Oid stacoll,
115 : : Datum stanumbers, bool stanumbers_isnull,
116 : : Datum stavalues, bool stavalues_isnull);
117 : : static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
118 : : Datum *values, bool *nulls, bool *replaces);
119 : : static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
120 : : static void init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
121 : : Datum *values, bool *nulls, bool *replaces);
122 : :
123 : : /*
124 : : * Insert or Update Attribute Statistics
125 : : *
126 : : * See pg_statistic.h for an explanation of how each statistic kind is
127 : : * stored. Custom statistics kinds are not supported.
128 : : *
129 : : * Depending on the statistics kind, we need to derive information from the
130 : : * attribute for which we're storing the stats. For instance, the MCVs are
131 : : * stored as an anyarray, and the representation of the array needs to store
132 : : * the correct element type, which must be derived from the attribute.
133 : : *
134 : : * Major errors, such as the table not existing, the attribute not existing,
135 : : * or a permissions failure are always reported at ERROR. Other errors, such
136 : : * as a conversion failure on one statistic kind, are reported as a WARNING
137 : : * and other statistic kinds may still be updated.
138 : : */
139 : : static bool
244 jdavis@postgresql.or 140 :CBC 798 : attribute_statistics_update(FunctionCallInfo fcinfo)
141 : : {
142 : : char *nspname;
143 : : char *relname;
144 : : Oid reloid;
145 : : char *attname;
146 : : AttrNumber attnum;
147 : : bool inherited;
12 nathan@postgresql.or 148 : 798 : Oid locked_table = InvalidOid;
149 : :
150 : : Relation starel;
151 : : HeapTuple statup;
152 : :
370 jdavis@postgresql.or 153 : 798 : Oid atttypid = InvalidOid;
154 : : int32 atttypmod;
155 : : char atttyptype;
156 : 798 : Oid atttypcoll = InvalidOid;
157 : 798 : Oid eq_opr = InvalidOid;
158 : 798 : Oid lt_opr = InvalidOid;
159 : :
160 : 798 : Oid elemtypid = InvalidOid;
161 : 798 : Oid elem_eq_opr = InvalidOid;
162 : :
163 : : FmgrInfo array_in_fn;
164 : :
165 [ + + ]: 1177 : bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
166 [ + + ]: 379 : !PG_ARGISNULL(MOST_COMMON_VALS_ARG);
167 : 798 : bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
168 : 798 : bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
169 [ + + ]: 821 : bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
170 [ + + ]: 23 : !PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
171 : 798 : bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
172 : 798 : bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
173 [ + + ]: 814 : bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
174 [ + + ]: 16 : !PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
175 : :
176 : 798 : Datum values[Natts_pg_statistic] = {0};
177 : 798 : bool nulls[Natts_pg_statistic] = {0};
178 : 798 : bool replaces[Natts_pg_statistic] = {0};
179 : :
180 : 798 : bool result = true;
181 : :
216 182 : 798 : stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
183 : 795 : stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
184 : :
185 : 789 : nspname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELSCHEMA_ARG));
186 : 789 : relname = TextDatumGetCString(PG_GETARG_DATUM(ATTRELNAME_ARG));
187 : :
341 fujii@postgresql.org 188 [ - + ]: 789 : if (RecoveryInProgress())
341 fujii@postgresql.org 189 [ # # ]:UBC 0 : ereport(ERROR,
190 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
191 : : errmsg("recovery is in progress"),
192 : : errhint("Statistics cannot be modified during recovery.")));
193 : :
194 : : /* lock before looking up attribute */
12 nathan@postgresql.or 195 :CBC 789 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
196 : : ShareUpdateExclusiveLock, 0,
197 : : RangeVarCallbackForStats, &locked_table);
198 : :
199 : : /* user can specify either attname or attnum, but not both */
243 tgl@sss.pgh.pa.us 200 [ + + ]: 783 : if (!PG_ARGISNULL(ATTNAME_ARG))
201 : : {
202 [ + + ]: 770 : if (!PG_ARGISNULL(ATTNUM_ARG))
203 [ + - ]: 3 : ereport(ERROR,
204 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
205 : : errmsg("cannot specify both \"%s\" and \"%s\"", "attname", "attnum")));
216 jdavis@postgresql.or 206 : 767 : attname = TextDatumGetCString(PG_GETARG_DATUM(ATTNAME_ARG));
243 tgl@sss.pgh.pa.us 207 : 767 : attnum = get_attnum(reloid, attname);
208 : : /* note that this test covers attisdropped cases too: */
209 [ + + ]: 767 : if (attnum == InvalidAttrNumber)
210 [ + - ]: 3 : ereport(ERROR,
211 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
212 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
213 : : attname, relname)));
214 : : }
215 [ + + ]: 13 : else if (!PG_ARGISNULL(ATTNUM_ARG))
216 : : {
217 : 7 : attnum = PG_GETARG_INT16(ATTNUM_ARG);
218 : 7 : attname = get_attname(reloid, attnum, true);
219 : : /* annoyingly, get_attname doesn't check attisdropped */
220 [ + - ]: 7 : if (attname == NULL ||
221 [ - + ]: 7 : !SearchSysCacheExistsAttName(reloid, attname))
243 tgl@sss.pgh.pa.us 222 [ # # ]:UBC 0 : ereport(ERROR,
223 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
224 : : errmsg("column %d of relation \"%s\" does not exist",
225 : : attnum, relname)));
226 : : }
227 : : else
228 : : {
243 tgl@sss.pgh.pa.us 229 [ + - ]:CBC 6 : ereport(ERROR,
230 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
231 : : errmsg("must specify either \"%s\" or \"%s\"", "attname", "attnum")));
232 : : attname = NULL; /* keep compiler quiet */
233 : : attnum = 0;
234 : : }
235 : :
339 jdavis@postgresql.or 236 [ + + ]: 771 : if (attnum < 0)
237 [ + - ]: 3 : ereport(ERROR,
238 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
239 : : errmsg("cannot modify statistics on system column \"%s\"",
240 : : attname)));
241 : :
370 242 : 768 : stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
243 : 765 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
244 : :
245 : : /*
246 : : * Check argument sanity. If some arguments are unusable, emit a WARNING
247 : : * and set the corresponding argument to NULL in fcinfo.
248 : : */
249 : :
244 250 [ - + ]: 765 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_FREQS_ARG))
251 : : {
370 jdavis@postgresql.or 252 :UBC 0 : do_mcv = false;
253 : 0 : result = false;
254 : : }
255 : :
244 jdavis@postgresql.or 256 [ - + ]:CBC 765 : if (!stats_check_arg_array(fcinfo, attarginfo, MOST_COMMON_ELEM_FREQS_ARG))
257 : : {
370 jdavis@postgresql.or 258 :UBC 0 : do_mcelem = false;
259 : 0 : result = false;
260 : : }
244 jdavis@postgresql.or 261 [ + + ]:CBC 765 : if (!stats_check_arg_array(fcinfo, attarginfo, ELEM_COUNT_HISTOGRAM_ARG))
262 : : {
370 263 : 3 : do_dechist = false;
264 : 3 : result = false;
265 : : }
266 : :
267 [ + + ]: 765 : if (!stats_check_arg_pair(fcinfo, attarginfo,
268 : : MOST_COMMON_VALS_ARG, MOST_COMMON_FREQS_ARG))
269 : : {
270 : 9 : do_mcv = false;
271 : 9 : result = false;
272 : : }
273 : :
274 [ + + ]: 765 : if (!stats_check_arg_pair(fcinfo, attarginfo,
275 : : MOST_COMMON_ELEMS_ARG,
276 : : MOST_COMMON_ELEM_FREQS_ARG))
277 : : {
278 : 6 : do_mcelem = false;
279 : 6 : result = false;
280 : : }
281 : :
282 [ + + ]: 765 : if (!stats_check_arg_pair(fcinfo, attarginfo,
283 : : RANGE_LENGTH_HISTOGRAM_ARG,
284 : : RANGE_EMPTY_FRAC_ARG))
285 : : {
286 : 6 : do_range_length_histogram = false;
287 : 6 : result = false;
288 : : }
289 : :
290 : : /* derive information from attribute */
244 291 : 765 : get_attr_stat_type(reloid, attnum,
292 : : &atttypid, &atttypmod,
293 : : &atttyptype, &atttypcoll,
294 : : &eq_opr, <_opr);
295 : :
296 : : /* if needed, derive element type */
370 297 [ + + + + ]: 765 : if (do_mcelem || do_dechist)
298 : : {
244 299 [ + + ]: 26 : if (!get_elem_stat_type(atttypid, atttyptype,
300 : : &elemtypid, &elem_eq_opr))
301 : : {
302 [ + - ]: 9 : ereport(WARNING,
303 : : (errmsg("could not determine element type of column \"%s\"", attname),
304 : : errdetail("Cannot set %s or %s.",
305 : : "STATISTIC_KIND_MCELEM", "STATISTIC_KIND_DECHIST")));
370 306 : 9 : elemtypid = InvalidOid;
307 : 9 : elem_eq_opr = InvalidOid;
308 : :
309 : 9 : do_mcelem = false;
310 : 9 : do_dechist = false;
311 : 9 : result = false;
312 : : }
313 : : }
314 : :
315 : : /* histogram and correlation require less-than operator */
316 [ + + + + : 765 : if ((do_histogram || do_correlation) && !OidIsValid(lt_opr))
- + ]
317 : : {
244 jdavis@postgresql.or 318 [ # # ]:UBC 0 : ereport(WARNING,
319 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
320 : : errmsg("could not determine less-than operator for column \"%s\"", attname),
321 : : errdetail("Cannot set %s or %s.",
322 : : "STATISTIC_KIND_HISTOGRAM", "STATISTIC_KIND_CORRELATION")));
323 : :
370 324 : 0 : do_histogram = false;
325 : 0 : do_correlation = false;
326 : 0 : result = false;
327 : : }
328 : :
329 : : /* only range types can have range stats */
370 jdavis@postgresql.or 330 [ + + + + ]:CBC 765 : if ((do_range_length_histogram || do_bounds_histogram) &&
331 [ + + + - ]: 19 : !(atttyptype == TYPTYPE_RANGE || atttyptype == TYPTYPE_MULTIRANGE))
332 : : {
244 333 [ + - ]: 6 : ereport(WARNING,
334 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
335 : : errmsg("column \"%s\" is not a range type", attname),
336 : : errdetail("Cannot set %s or %s.",
337 : : "STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM", "STATISTIC_KIND_BOUNDS_HISTOGRAM")));
338 : :
370 339 : 6 : do_bounds_histogram = false;
340 : 6 : do_range_length_histogram = false;
341 : 6 : result = false;
342 : : }
343 : :
344 : 765 : fmgr_info(F_ARRAY_IN, &array_in_fn);
345 : :
346 : 765 : starel = table_open(StatisticRelationId, RowExclusiveLock);
347 : :
80 peter@eisentraut.org 348 :GNC 765 : statup = SearchSysCache3(STATRELATTINH, ObjectIdGetDatum(reloid), Int16GetDatum(attnum), BoolGetDatum(inherited));
349 : :
350 : : /* initialize from existing tuple if exists */
370 jdavis@postgresql.or 351 [ + + ]:CBC 765 : if (HeapTupleIsValid(statup))
352 : 63 : heap_deform_tuple(statup, RelationGetDescr(starel), values, nulls);
353 : : else
354 : 702 : init_empty_stats_tuple(reloid, attnum, inherited, values, nulls,
355 : : replaces);
356 : :
357 : : /* if specified, set to argument values */
358 [ + + ]: 765 : if (!PG_ARGISNULL(NULL_FRAC_ARG))
359 : : {
360 : 750 : values[Anum_pg_statistic_stanullfrac - 1] = PG_GETARG_DATUM(NULL_FRAC_ARG);
361 : 750 : replaces[Anum_pg_statistic_stanullfrac - 1] = true;
362 : : }
363 [ + + ]: 765 : if (!PG_ARGISNULL(AVG_WIDTH_ARG))
364 : : {
365 : 693 : values[Anum_pg_statistic_stawidth - 1] = PG_GETARG_DATUM(AVG_WIDTH_ARG);
366 : 693 : replaces[Anum_pg_statistic_stawidth - 1] = true;
367 : : }
368 [ + + ]: 765 : if (!PG_ARGISNULL(N_DISTINCT_ARG))
369 : : {
370 : 693 : values[Anum_pg_statistic_stadistinct - 1] = PG_GETARG_DATUM(N_DISTINCT_ARG);
371 : 693 : replaces[Anum_pg_statistic_stadistinct - 1] = true;
372 : : }
373 : :
374 : : /* STATISTIC_KIND_MCV */
375 [ + + ]: 765 : if (do_mcv)
376 : : {
377 : : bool converted;
378 : 376 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_FREQS_ARG);
379 : 376 : Datum stavalues = text_to_stavalues("most_common_vals",
380 : : &array_in_fn,
381 : : PG_GETARG_DATUM(MOST_COMMON_VALS_ARG),
382 : : atttypid, atttypmod,
383 : : &converted);
384 : :
385 [ + + ]: 376 : if (converted)
386 : : {
387 : 373 : set_stats_slot(values, nulls, replaces,
388 : : STATISTIC_KIND_MCV,
389 : : eq_opr, atttypcoll,
390 : : stanumbers, false, stavalues, false);
391 : : }
392 : : else
393 : 3 : result = false;
394 : : }
395 : :
396 : : /* STATISTIC_KIND_HISTOGRAM */
397 [ + + ]: 765 : if (do_histogram)
398 : : {
399 : : Datum stavalues;
400 : 371 : bool converted = false;
401 : :
402 : 371 : stavalues = text_to_stavalues("histogram_bounds",
403 : : &array_in_fn,
404 : : PG_GETARG_DATUM(HISTOGRAM_BOUNDS_ARG),
405 : : atttypid, atttypmod,
406 : : &converted);
407 : :
408 [ + + ]: 371 : if (converted)
409 : : {
410 : 368 : set_stats_slot(values, nulls, replaces,
411 : : STATISTIC_KIND_HISTOGRAM,
412 : : lt_opr, atttypcoll,
413 : : 0, true, stavalues, false);
414 : : }
415 : : else
416 : 3 : result = false;
417 : : }
418 : :
419 : : /* STATISTIC_KIND_CORRELATION */
420 [ + + ]: 765 : if (do_correlation)
421 : : {
422 : 647 : Datum elems[] = {PG_GETARG_DATUM(CORRELATION_ARG)};
423 : 647 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
424 : 647 : Datum stanumbers = PointerGetDatum(arry);
425 : :
426 : 647 : set_stats_slot(values, nulls, replaces,
427 : : STATISTIC_KIND_CORRELATION,
428 : : lt_opr, atttypcoll,
429 : : stanumbers, false, 0, true);
430 : : }
431 : :
432 : : /* STATISTIC_KIND_MCELEM */
433 [ + + ]: 765 : if (do_mcelem)
434 : : {
435 : 14 : Datum stanumbers = PG_GETARG_DATUM(MOST_COMMON_ELEM_FREQS_ARG);
436 : 14 : bool converted = false;
437 : : Datum stavalues;
438 : :
439 : 14 : stavalues = text_to_stavalues("most_common_elems",
440 : : &array_in_fn,
441 : : PG_GETARG_DATUM(MOST_COMMON_ELEMS_ARG),
442 : : elemtypid, atttypmod,
443 : : &converted);
444 : :
445 [ + - ]: 14 : if (converted)
446 : : {
447 : 14 : set_stats_slot(values, nulls, replaces,
448 : : STATISTIC_KIND_MCELEM,
449 : : elem_eq_opr, atttypcoll,
450 : : stanumbers, false, stavalues, false);
451 : : }
452 : : else
370 jdavis@postgresql.or 453 :UBC 0 : result = false;
454 : : }
455 : :
456 : : /* STATISTIC_KIND_DECHIST */
370 jdavis@postgresql.or 457 [ + + ]:CBC 765 : if (do_dechist)
458 : : {
459 : 13 : Datum stanumbers = PG_GETARG_DATUM(ELEM_COUNT_HISTOGRAM_ARG);
460 : :
461 : 13 : set_stats_slot(values, nulls, replaces,
462 : : STATISTIC_KIND_DECHIST,
463 : : elem_eq_opr, atttypcoll,
464 : : stanumbers, false, 0, true);
465 : : }
466 : :
467 : : /*
468 : : * STATISTIC_KIND_BOUNDS_HISTOGRAM
469 : : *
470 : : * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
471 : : * though it is numerically greater, and all other stakinds appear in
472 : : * numerical order. We duplicate this quirk for consistency.
473 : : */
474 [ + + ]: 765 : if (do_bounds_histogram)
475 : : {
476 : 10 : bool converted = false;
477 : : Datum stavalues;
478 : :
479 : 10 : stavalues = text_to_stavalues("range_bounds_histogram",
480 : : &array_in_fn,
481 : : PG_GETARG_DATUM(RANGE_BOUNDS_HISTOGRAM_ARG),
482 : : atttypid, atttypmod,
483 : : &converted);
484 : :
485 [ + - ]: 10 : if (converted)
486 : : {
487 : 10 : set_stats_slot(values, nulls, replaces,
488 : : STATISTIC_KIND_BOUNDS_HISTOGRAM,
489 : : InvalidOid, InvalidOid,
490 : : 0, true, stavalues, false);
491 : : }
492 : : else
370 jdavis@postgresql.or 493 :UBC 0 : result = false;
494 : : }
495 : :
496 : : /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
370 jdavis@postgresql.or 497 [ + + ]:CBC 765 : if (do_range_length_histogram)
498 : : {
499 : : /* The anyarray is always a float8[] for this stakind */
500 : 10 : Datum elems[] = {PG_GETARG_DATUM(RANGE_EMPTY_FRAC_ARG)};
501 : 10 : ArrayType *arry = construct_array_builtin(elems, 1, FLOAT4OID);
502 : 10 : Datum stanumbers = PointerGetDatum(arry);
503 : :
504 : 10 : bool converted = false;
505 : : Datum stavalues;
506 : :
507 : 10 : stavalues = text_to_stavalues("range_length_histogram",
508 : : &array_in_fn,
509 : : PG_GETARG_DATUM(RANGE_LENGTH_HISTOGRAM_ARG),
510 : : FLOAT8OID, 0, &converted);
511 : :
512 [ + - ]: 10 : if (converted)
513 : : {
514 : 10 : set_stats_slot(values, nulls, replaces,
515 : : STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
516 : : Float8LessOperator, InvalidOid,
517 : : stanumbers, false, stavalues, false);
518 : : }
519 : : else
370 jdavis@postgresql.or 520 :UBC 0 : result = false;
521 : : }
522 : :
370 jdavis@postgresql.or 523 :CBC 765 : upsert_pg_statistic(starel, statup, values, nulls, replaces);
524 : :
525 [ + + ]: 765 : if (HeapTupleIsValid(statup))
526 : 63 : ReleaseSysCache(statup);
527 : 765 : table_close(starel, RowExclusiveLock);
528 : :
529 : 765 : return result;
530 : : }
531 : :
532 : : /*
533 : : * If this relation is an index and that index has expressions in it, and
534 : : * the attnum specified is known to be an expression, then we must walk
535 : : * the list attributes up to the specified attnum to get the right
536 : : * expression.
537 : : */
538 : : static Node *
539 : 765 : get_attr_expr(Relation rel, int attnum)
540 : : {
541 : : List *index_exprs;
542 : : ListCell *indexpr_item;
543 : :
544 : : /* relation is not an index */
249 545 [ + + ]: 765 : if (rel->rd_rel->relkind != RELKIND_INDEX &&
546 [ + - ]: 758 : rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
547 : 758 : return NULL;
548 : :
549 : 7 : index_exprs = RelationGetIndexExpressions(rel);
550 : :
551 : : /* index has no expressions to give */
552 [ - + ]: 7 : if (index_exprs == NIL)
249 jdavis@postgresql.or 553 :UBC 0 : return NULL;
554 : :
555 : : /*
556 : : * The index attnum points directly to a relation attnum, then it's not an
557 : : * expression attribute.
558 : : */
249 jdavis@postgresql.or 559 [ - + ]:CBC 7 : if (rel->rd_index->indkey.values[attnum - 1] != 0)
249 jdavis@postgresql.or 560 :UBC 0 : return NULL;
561 : :
249 jdavis@postgresql.or 562 :CBC 7 : indexpr_item = list_head(rel->rd_indexprs);
563 : :
564 [ - + ]: 7 : for (int i = 0; i < attnum - 1; i++)
249 jdavis@postgresql.or 565 [ # # ]:UBC 0 : if (rel->rd_index->indkey.values[i] == 0)
566 : 0 : indexpr_item = lnext(rel->rd_indexprs, indexpr_item);
567 : :
249 jdavis@postgresql.or 568 [ - + ]:CBC 7 : if (indexpr_item == NULL) /* shouldn't happen */
249 jdavis@postgresql.or 569 [ # # ]:UBC 0 : elog(ERROR, "too few entries in indexprs list");
570 : :
249 jdavis@postgresql.or 571 :CBC 7 : return (Node *) lfirst(indexpr_item);
572 : : }
573 : :
574 : : /*
575 : : * Derive type information from the attribute.
576 : : */
577 : : static void
244 578 : 765 : get_attr_stat_type(Oid reloid, AttrNumber attnum,
579 : : Oid *atttypid, int32 *atttypmod,
580 : : char *atttyptype, Oid *atttypcoll,
581 : : Oid *eq_opr, Oid *lt_opr)
582 : : {
370 583 : 765 : Relation rel = relation_open(reloid, AccessShareLock);
584 : : Form_pg_attribute attr;
585 : : HeapTuple atup;
586 : : Node *expr;
587 : : TypeCacheEntry *typcache;
588 : :
589 : 765 : atup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(reloid),
590 : : Int16GetDatum(attnum));
591 : :
592 : : /* Attribute not found */
593 [ - + ]: 765 : if (!HeapTupleIsValid(atup))
370 jdavis@postgresql.or 594 [ # # ]:UBC 0 : ereport(ERROR,
595 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
596 : : errmsg("column %d of relation \"%s\" does not exist",
597 : : attnum, RelationGetRelationName(rel))));
598 : :
370 jdavis@postgresql.or 599 :CBC 765 : attr = (Form_pg_attribute) GETSTRUCT(atup);
600 : :
601 [ - + ]: 765 : if (attr->attisdropped)
370 jdavis@postgresql.or 602 [ # # ]:UBC 0 : ereport(ERROR,
603 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
604 : : errmsg("column %d of relation \"%s\" does not exist",
605 : : attnum, RelationGetRelationName(rel))));
606 : :
370 jdavis@postgresql.or 607 :CBC 765 : expr = get_attr_expr(rel, attr->attnum);
608 : :
609 : : /*
610 : : * When analyzing an expression index, believe the expression tree's type
611 : : * not the column datatype --- the latter might be the opckeytype storage
612 : : * type of the opclass, which is not interesting for our purposes. This
613 : : * mimics the behavior of examine_attribute().
614 : : */
615 [ + + ]: 765 : if (expr == NULL)
616 : : {
617 : 758 : *atttypid = attr->atttypid;
618 : 758 : *atttypmod = attr->atttypmod;
619 : 758 : *atttypcoll = attr->attcollation;
620 : : }
621 : : else
622 : : {
623 : 7 : *atttypid = exprType(expr);
624 : 7 : *atttypmod = exprTypmod(expr);
625 : :
626 [ - + ]: 7 : if (OidIsValid(attr->attcollation))
370 jdavis@postgresql.or 627 :UBC 0 : *atttypcoll = attr->attcollation;
628 : : else
370 jdavis@postgresql.or 629 :CBC 7 : *atttypcoll = exprCollation(expr);
630 : : }
631 : 765 : ReleaseSysCache(atup);
632 : :
633 : : /*
634 : : * If it's a multirange, step down to the range type, as is done by
635 : : * multirange_typanalyze().
636 : : */
637 [ + + ]: 765 : if (type_is_multirange(*atttypid))
638 : 1 : *atttypid = get_multirange_range(*atttypid);
639 : :
640 : : /* finds the right operators even if atttypid is a domain */
641 : 765 : typcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
642 : 765 : *atttyptype = typcache->typtype;
643 : 765 : *eq_opr = typcache->eq_opr;
644 : 765 : *lt_opr = typcache->lt_opr;
645 : :
646 : : /*
647 : : * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
648 : : * compute_tsvector_stats().
649 : : */
650 [ + + ]: 765 : if (*atttypid == TSVECTOROID)
651 : 1 : *atttypcoll = DEFAULT_COLLATION_OID;
652 : :
653 : 765 : relation_close(rel, NoLock);
654 : 765 : }
655 : :
656 : : /*
657 : : * Derive element type information from the attribute type.
658 : : */
659 : : static bool
244 660 : 26 : get_elem_stat_type(Oid atttypid, char atttyptype,
661 : : Oid *elemtypid, Oid *elem_eq_opr)
662 : : {
663 : : TypeCacheEntry *elemtypcache;
664 : :
370 665 [ + + ]: 26 : if (atttypid == TSVECTOROID)
666 : : {
667 : : /*
668 : : * Special case: element type for tsvector is text. See
669 : : * compute_tsvector_stats().
670 : : */
671 : 1 : *elemtypid = TEXTOID;
672 : : }
673 : : else
674 : : {
675 : : /* find underlying element type through any domain */
676 : 25 : *elemtypid = get_base_element_type(atttypid);
677 : : }
678 : :
679 [ + + ]: 26 : if (!OidIsValid(*elemtypid))
680 : 9 : return false;
681 : :
682 : : /* finds the right operator even if elemtypid is a domain */
683 : 17 : elemtypcache = lookup_type_cache(*elemtypid, TYPECACHE_EQ_OPR);
684 [ - + ]: 17 : if (!OidIsValid(elemtypcache->eq_opr))
370 jdavis@postgresql.or 685 :UBC 0 : return false;
686 : :
370 jdavis@postgresql.or 687 :CBC 17 : *elem_eq_opr = elemtypcache->eq_opr;
688 : :
689 : 17 : return true;
690 : : }
691 : :
692 : : /*
693 : : * Cast a text datum into an array with element type elemtypid.
694 : : *
695 : : * If an error is encountered, capture it and re-throw a WARNING, and set ok
696 : : * to false. If the resulting array contains NULLs, raise a WARNING and set ok
697 : : * to false. Otherwise, set ok to true.
698 : : */
699 : : static Datum
700 : 781 : text_to_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
701 : : int32 typmod, bool *ok)
702 : : {
703 : 781 : LOCAL_FCINFO(fcinfo, 8);
704 : : char *s;
705 : : Datum result;
706 : 781 : ErrorSaveContext escontext = {T_ErrorSaveContext};
707 : :
708 : 781 : escontext.details_wanted = true;
709 : :
710 : 781 : s = TextDatumGetCString(d);
711 : :
712 : 781 : InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid,
713 : : (Node *) &escontext, NULL);
714 : :
715 : 781 : fcinfo->args[0].value = CStringGetDatum(s);
716 : 781 : fcinfo->args[0].isnull = false;
717 : 781 : fcinfo->args[1].value = ObjectIdGetDatum(typid);
718 : 781 : fcinfo->args[1].isnull = false;
719 : 781 : fcinfo->args[2].value = Int32GetDatum(typmod);
720 : 781 : fcinfo->args[2].isnull = false;
721 : :
722 : 781 : result = FunctionCallInvoke(fcinfo);
723 : :
724 : 781 : pfree(s);
725 : :
369 726 [ + + ]: 781 : if (escontext.error_occurred)
727 : : {
244 728 : 3 : escontext.error_data->elevel = WARNING;
370 729 : 3 : ThrowErrorData(escontext.error_data);
730 : 3 : *ok = false;
731 : 3 : return (Datum) 0;
732 : : }
733 : :
734 [ + + ]: 778 : if (array_contains_nulls(DatumGetArrayTypeP(result)))
735 : : {
244 736 [ + - ]: 3 : ereport(WARNING,
737 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
738 : : errmsg("\"%s\" array must not contain null values", staname)));
370 739 : 3 : *ok = false;
740 : 3 : return (Datum) 0;
741 : : }
742 : :
743 : 775 : *ok = true;
744 : :
745 : 775 : return result;
746 : : }
747 : :
748 : : /*
749 : : * Find and update the slot with the given stakind, or use the first empty
750 : : * slot.
751 : : */
752 : : static void
753 : 1435 : set_stats_slot(Datum *values, bool *nulls, bool *replaces,
754 : : int16 stakind, Oid staop, Oid stacoll,
755 : : Datum stanumbers, bool stanumbers_isnull,
756 : : Datum stavalues, bool stavalues_isnull)
757 : : {
758 : : int slotidx;
759 : 1435 : int first_empty = -1;
760 : : AttrNumber stakind_attnum;
761 : : AttrNumber staop_attnum;
762 : : AttrNumber stacoll_attnum;
763 : :
764 : : /* find existing slot with given stakind */
765 [ + + ]: 8610 : for (slotidx = 0; slotidx < STATISTIC_NUM_SLOTS; slotidx++)
766 : : {
767 : 7175 : stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
768 : :
769 [ + + + + ]: 9499 : if (first_empty < 0 &&
770 : 2324 : DatumGetInt16(values[stakind_attnum]) == 0)
771 : 1435 : first_empty = slotidx;
772 [ - + ]: 7175 : if (DatumGetInt16(values[stakind_attnum]) == stakind)
370 jdavis@postgresql.or 773 :UBC 0 : break;
774 : : }
775 : :
370 jdavis@postgresql.or 776 [ + - + - ]:CBC 1435 : if (slotidx >= STATISTIC_NUM_SLOTS && first_empty >= 0)
777 : 1435 : slotidx = first_empty;
778 : :
779 [ - + ]: 1435 : if (slotidx >= STATISTIC_NUM_SLOTS)
370 jdavis@postgresql.or 780 [ # # ]:UBC 0 : ereport(ERROR,
781 : : (errmsg("maximum number of statistics slots exceeded: %d",
782 : : slotidx + 1)));
783 : :
370 jdavis@postgresql.or 784 :CBC 1435 : stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
785 : 1435 : staop_attnum = Anum_pg_statistic_staop1 - 1 + slotidx;
786 : 1435 : stacoll_attnum = Anum_pg_statistic_stacoll1 - 1 + slotidx;
787 : :
788 [ + - ]: 1435 : if (DatumGetInt16(values[stakind_attnum]) != stakind)
789 : : {
790 : 1435 : values[stakind_attnum] = Int16GetDatum(stakind);
791 : 1435 : replaces[stakind_attnum] = true;
792 : : }
793 [ + + ]: 1435 : if (DatumGetObjectId(values[staop_attnum]) != staop)
794 : : {
795 : 1425 : values[staop_attnum] = ObjectIdGetDatum(staop);
796 : 1425 : replaces[staop_attnum] = true;
797 : : }
798 [ + + ]: 1435 : if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
799 : : {
800 : 342 : values[stacoll_attnum] = ObjectIdGetDatum(stacoll);
801 : 342 : replaces[stacoll_attnum] = true;
802 : : }
803 [ + + ]: 1435 : if (!stanumbers_isnull)
804 : : {
805 : 1057 : values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
806 : 1057 : nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
807 : 1057 : replaces[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = true;
808 : : }
809 [ + + ]: 1435 : if (!stavalues_isnull)
810 : : {
811 : 775 : values[Anum_pg_statistic_stavalues1 - 1 + slotidx] = stavalues;
812 : 775 : nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;
813 : 775 : replaces[Anum_pg_statistic_stavalues1 - 1 + slotidx] = true;
814 : : }
815 : 1435 : }
816 : :
817 : : /*
818 : : * Upsert the pg_statistic record.
819 : : */
820 : : static void
821 : 765 : upsert_pg_statistic(Relation starel, HeapTuple oldtup,
822 : : Datum *values, bool *nulls, bool *replaces)
823 : : {
824 : : HeapTuple newtup;
825 : :
826 [ + + ]: 765 : if (HeapTupleIsValid(oldtup))
827 : : {
828 : 63 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(starel),
829 : : values, nulls, replaces);
830 : 63 : CatalogTupleUpdate(starel, &newtup->t_self, newtup);
831 : : }
832 : : else
833 : : {
834 : 702 : newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls);
835 : 702 : CatalogTupleInsert(starel, newtup);
836 : : }
837 : :
838 : 765 : heap_freetuple(newtup);
839 : :
363 840 : 765 : CommandCounterIncrement();
370 841 : 765 : }
842 : :
843 : : /*
844 : : * Delete pg_statistic record.
845 : : */
846 : : static bool
847 : 3 : delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit)
848 : : {
849 : 3 : Relation sd = table_open(StatisticRelationId, RowExclusiveLock);
850 : : HeapTuple oldtup;
363 851 : 3 : bool result = false;
852 : :
853 : : /* Is there already a pg_statistic tuple for this attribute? */
370 854 : 3 : oldtup = SearchSysCache3(STATRELATTINH,
855 : : ObjectIdGetDatum(reloid),
856 : : Int16GetDatum(attnum),
857 : : BoolGetDatum(stainherit));
858 : :
859 [ + - ]: 3 : if (HeapTupleIsValid(oldtup))
860 : : {
861 : 3 : CatalogTupleDelete(sd, &oldtup->t_self);
862 : 3 : ReleaseSysCache(oldtup);
363 863 : 3 : result = true;
864 : : }
865 : :
370 866 : 3 : table_close(sd, RowExclusiveLock);
867 : :
363 868 : 3 : CommandCounterIncrement();
869 : :
870 : 3 : return result;
871 : : }
872 : :
873 : : /*
874 : : * Initialize values and nulls for a new stats tuple.
875 : : */
876 : : static void
370 877 : 702 : init_empty_stats_tuple(Oid reloid, int16 attnum, bool inherited,
878 : : Datum *values, bool *nulls, bool *replaces)
879 : : {
880 : 702 : memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
881 : 702 : memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
882 : :
883 : : /* must initialize non-NULL attributes */
884 : :
885 : 702 : values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(reloid);
886 : 702 : nulls[Anum_pg_statistic_starelid - 1] = false;
887 : 702 : values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum);
888 : 702 : nulls[Anum_pg_statistic_staattnum - 1] = false;
889 : 702 : values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inherited);
890 : 702 : nulls[Anum_pg_statistic_stainherit - 1] = false;
891 : :
892 : 702 : values[Anum_pg_statistic_stanullfrac - 1] = DEFAULT_NULL_FRAC;
893 : 702 : nulls[Anum_pg_statistic_stanullfrac - 1] = false;
894 : 702 : values[Anum_pg_statistic_stawidth - 1] = DEFAULT_AVG_WIDTH;
895 : 702 : nulls[Anum_pg_statistic_stawidth - 1] = false;
896 : 702 : values[Anum_pg_statistic_stadistinct - 1] = DEFAULT_N_DISTINCT;
897 : 702 : nulls[Anum_pg_statistic_stadistinct - 1] = false;
898 : :
899 : : /* initialize stakind, staop, and stacoll slots */
900 [ + + ]: 4212 : for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
901 : : {
902 : 3510 : values[Anum_pg_statistic_stakind1 + slotnum - 1] = (Datum) 0;
903 : 3510 : nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
80 peter@eisentraut.org 904 :GNC 3510 : values[Anum_pg_statistic_staop1 + slotnum - 1] = ObjectIdGetDatum(InvalidOid);
370 jdavis@postgresql.or 905 :CBC 3510 : nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
80 peter@eisentraut.org 906 :GNC 3510 : values[Anum_pg_statistic_stacoll1 + slotnum - 1] = ObjectIdGetDatum(InvalidOid);
370 jdavis@postgresql.or 907 :CBC 3510 : nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;
908 : : }
909 : 702 : }
910 : :
911 : : /*
912 : : * Delete statistics for the given attribute.
913 : : */
914 : : Datum
915 : 3 : pg_clear_attribute_stats(PG_FUNCTION_ARGS)
916 : : {
917 : : char *nspname;
918 : : char *relname;
919 : : Oid reloid;
920 : : char *attname;
921 : : AttrNumber attnum;
922 : : bool inherited;
12 nathan@postgresql.or 923 : 3 : Oid locked_table = InvalidOid;
924 : :
216 jdavis@postgresql.or 925 : 3 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELSCHEMA_ARG);
926 : 3 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELNAME_ARG);
927 : 3 : stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG);
928 : 3 : stats_check_required_arg(fcinfo, cleararginfo, C_INHERITED_ARG);
929 : :
930 : 3 : nspname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELSCHEMA_ARG));
931 : 3 : relname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTRELNAME_ARG));
932 : :
341 fujii@postgresql.org 933 [ - + ]: 3 : if (RecoveryInProgress())
341 fujii@postgresql.org 934 [ # # ]:UBC 0 : ereport(ERROR,
935 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
936 : : errmsg("recovery is in progress"),
937 : : errhint("Statistics cannot be modified during recovery.")));
938 : :
12 nathan@postgresql.or 939 :CBC 3 : reloid = RangeVarGetRelidExtended(makeRangeVar(nspname, relname, -1),
940 : : ShareUpdateExclusiveLock, 0,
941 : : RangeVarCallbackForStats, &locked_table);
942 : :
216 jdavis@postgresql.or 943 : 3 : attname = TextDatumGetCString(PG_GETARG_DATUM(C_ATTNAME_ARG));
944 : 3 : attnum = get_attnum(reloid, attname);
945 : :
339 946 [ - + ]: 3 : if (attnum < 0)
339 jdavis@postgresql.or 947 [ # # ]:UBC 0 : ereport(ERROR,
948 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
949 : : errmsg("cannot clear statistics on system column \"%s\"",
950 : : attname)));
951 : :
369 jdavis@postgresql.or 952 [ - + ]:CBC 3 : if (attnum == InvalidAttrNumber)
369 jdavis@postgresql.or 953 [ # # ]:UBC 0 : ereport(ERROR,
954 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
955 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
956 : : attname, get_rel_name(reloid))));
957 : :
243 tgl@sss.pgh.pa.us 958 :CBC 3 : inherited = PG_GETARG_BOOL(C_INHERITED_ARG);
959 : :
370 jdavis@postgresql.or 960 : 3 : delete_pg_statistic(reloid, attnum, inherited);
961 : 3 : PG_RETURN_VOID();
962 : : }
963 : :
964 : : /*
965 : : * Import statistics for a given relation attribute.
966 : : *
967 : : * Inserts or replaces a row in pg_statistic for the given relation and
968 : : * attribute name or number. It takes input parameters that correspond to
969 : : * columns in the view pg_stats.
970 : : *
971 : : * Parameters are given in a pseudo named-attribute style: they must be
972 : : * pairs of parameter names (as text) and values (of appropriate types).
973 : : * We do that, rather than using regular named-parameter notation, so
974 : : * that we can add or change parameters without fear of breaking
975 : : * carelessly-written calls.
976 : : *
977 : : * Parameters null_frac, avg_width, and n_distinct all correspond to NOT NULL
978 : : * columns in pg_statistic. The remaining parameters all belong to a specific
979 : : * stakind. Some stakinds require multiple parameters, which must be specified
980 : : * together (or neither specified).
981 : : *
982 : : * Parameters are only superficially validated. Omitting a parameter or
983 : : * passing NULL leaves the statistic unchanged.
984 : : *
985 : : * Parameters corresponding to ANYARRAY columns are instead passed in as text
986 : : * values, which is a valid input string for an array of the type or element
987 : : * type of the attribute. Any error generated by the array_in() function will
988 : : * in turn fail the function.
989 : : */
990 : : Datum
368 991 : 798 : pg_restore_attribute_stats(PG_FUNCTION_ARGS)
992 : : {
993 : 798 : LOCAL_FCINFO(positional_fcinfo, NUM_ATTRIBUTE_STATS_ARGS);
994 : 798 : bool result = true;
995 : :
996 : 798 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
997 : : InvalidOid, NULL, NULL);
998 : :
999 [ + + ]: 798 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
1000 : : attarginfo))
1001 : 6 : result = false;
1002 : :
244 1003 [ + + ]: 798 : if (!attribute_statistics_update(positional_fcinfo))
368 1004 : 45 : result = false;
1005 : :
1006 : 765 : PG_RETURN_BOOL(result);
1007 : : }
|