Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/statistics/extended_stats_funcs.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * extended_stats_funcs.c
4
 *    Functions for manipulating extended statistics.
5
 *
6
 * This file includes the set of facilities required to support the direct
7
 * manipulations of extended statistics objects.
8
 *
9
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10
 * Portions Copyright (c) 1994, Regents of the University of California
11
 *
12
 * IDENTIFICATION
13
 *    src/backend/statistics/extended_stats_funcs.c
14
 *
15
 *-------------------------------------------------------------------------
16
 */
17
#include "postgres.h"
18
19
#include "access/heapam.h"
20
#include "catalog/indexing.h"
21
#include "catalog/namespace.h"
22
#include "catalog/pg_collation_d.h"
23
#include "catalog/pg_database.h"
24
#include "catalog/pg_operator.h"
25
#include "catalog/pg_statistic_ext.h"
26
#include "catalog/pg_statistic_ext_data.h"
27
#include "miscadmin.h"
28
#include "nodes/makefuncs.h"
29
#include "nodes/nodeFuncs.h"
30
#include "optimizer/optimizer.h"
31
#include "statistics/extended_stats_internal.h"
32
#include "statistics/stat_utils.h"
33
#include "utils/acl.h"
34
#include "utils/array.h"
35
#include "utils/builtins.h"
36
#include "utils/fmgroids.h"
37
#include "utils/jsonb.h"
38
#include "utils/lsyscache.h"
39
#include "utils/syscache.h"
40
#include "utils/typcache.h"
41
42
43
/*
44
 * Index of the arguments for the SQL functions.
45
 */
46
enum extended_stats_argnum
47
{
48
  RELSCHEMA_ARG = 0,
49
  RELNAME_ARG,
50
  STATSCHEMA_ARG,
51
  STATNAME_ARG,
52
  INHERITED_ARG,
53
  NDISTINCT_ARG,
54
  DEPENDENCIES_ARG,
55
  MOST_COMMON_VALS_ARG,
56
  MOST_COMMON_FREQS_ARG,
57
  MOST_COMMON_BASE_FREQS_ARG,
58
  EXPRESSIONS_ARG,
59
  NUM_EXTENDED_STATS_ARGS,
60
};
61
62
/*
63
 * The argument names and type OIDs of the arguments for the SQL
64
 * functions.
65
 */
66
static struct StatsArgInfo extarginfo[] =
67
{
68
  [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
69
  [RELNAME_ARG] = {"relname", TEXTOID},
70
  [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID},
71
  [STATNAME_ARG] = {"statistics_name", TEXTOID},
72
  [INHERITED_ARG] = {"inherited", BOOLOID},
73
  [NDISTINCT_ARG] = {"n_distinct", PG_NDISTINCTOID},
74
  [DEPENDENCIES_ARG] = {"dependencies", PG_DEPENDENCIESOID},
75
  [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTARRAYOID},
76
  [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT8ARRAYOID},
77
  [MOST_COMMON_BASE_FREQS_ARG] = {"most_common_base_freqs", FLOAT8ARRAYOID},
78
  [EXPRESSIONS_ARG] = {"exprs", JSONBOID},
79
  [NUM_EXTENDED_STATS_ARGS] = {0},
80
};
81
82
/*
83
 * An index of the elements of a stxdexpr Datum, which repeat for each
84
 * expression in the extended statistics object.
85
 */
86
enum extended_stats_exprs_element
87
{
88
  NULL_FRAC_ELEM = 0,
89
  AVG_WIDTH_ELEM,
90
  N_DISTINCT_ELEM,
91
  MOST_COMMON_VALS_ELEM,
92
  MOST_COMMON_FREQS_ELEM,
93
  HISTOGRAM_BOUNDS_ELEM,
94
  CORRELATION_ELEM,
95
  MOST_COMMON_ELEMS_ELEM,
96
  MOST_COMMON_ELEM_FREQS_ELEM,
97
  ELEM_COUNT_HISTOGRAM_ELEM,
98
  RANGE_LENGTH_HISTOGRAM_ELEM,
99
  RANGE_EMPTY_FRAC_ELEM,
100
  RANGE_BOUNDS_HISTOGRAM_ELEM,
101
  NUM_ATTRIBUTE_STATS_ELEMS
102
};
103
104
/*
105
 * The argument names of the repeating arguments for stxdexpr.
106
 */
107
static const char *extexprargname[NUM_ATTRIBUTE_STATS_ELEMS] =
108
{
109
  "null_frac",
110
  "avg_width",
111
  "n_distinct",
112
  "most_common_vals",
113
  "most_common_freqs",
114
  "histogram_bounds",
115
  "correlation",
116
  "most_common_elems",
117
  "most_common_elem_freqs",
118
  "elem_count_histogram",
119
  "range_length_histogram",
120
  "range_empty_frac",
121
  "range_bounds_histogram"
122
};
123
124
static bool extended_statistics_update(FunctionCallInfo fcinfo);
125
126
static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
127
                    const char *stxname);
128
static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited);
129
130
/*
131
 * Track the extended statistics kinds expected for a pg_statistic_ext
132
 * tuple.
133
 */
134
typedef struct
135
{
136
  bool    ndistinct;
137
  bool    dependencies;
138
  bool    mcv;
139
  bool    expressions;
140
} StakindFlags;
141
142
static void expand_stxkind(HeapTuple tup, StakindFlags *enabled);
143
static void upsert_pg_statistic_ext_data(const Datum *values,
144
                     const bool *nulls,
145
                     const bool *replaces);
146
147
static bool check_mcvlist_array(const ArrayType *arr, int argindex,
148
                int required_ndims, int mcv_length);
149
static Datum import_expressions(Relation pgsd, int numexprs,
150
                Oid *atttypids, int32 *atttypmods,
151
                Oid *atttypcolls, Jsonb *exprs_jsonb,
152
                bool *exprs_is_perfect);
153
static Datum import_mcv(const ArrayType *mcv_arr,
154
            const ArrayType *freqs_arr,
155
            const ArrayType *base_freqs_arr,
156
            Oid *atttypids, int32 *atttypmods,
157
            Oid *atttypcolls, int numattrs,
158
            bool *ok);
159
160
static char *jbv_string_get_cstr(JsonbValue *jval);
161
static bool jbv_to_infunc_datum(JsonbValue *jval, PGFunction func,
162
                AttrNumber exprnum, const char *argname,
163
                Datum *datum);
164
static bool key_in_expr_argnames(JsonbValue *key);
165
static bool check_all_expr_argnames_valid(JsonbContainer *cont, AttrNumber exprnum);
166
static Datum array_in_safe(FmgrInfo *array_in, const char *s, Oid typid,
167
               int32 typmod, AttrNumber exprnum,
168
               const char *element_name, bool *ok);
169
static Datum import_pg_statistic(Relation pgsd, JsonbContainer *cont,
170
                 AttrNumber exprnum, FmgrInfo *array_in_fn,
171
                 Oid typid, int32 typmod, Oid typcoll,
172
                 bool *pg_statistic_ok);
173
174
/*
175
 * Fetch a pg_statistic_ext row by name and namespace OID.
176
 */
177
static HeapTuple
178
get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
179
0
{
180
0
  ScanKeyData key[2];
181
0
  SysScanDesc scan;
182
0
  HeapTuple tup;
183
0
  Oid     stxoid = InvalidOid;
184
185
0
  ScanKeyInit(&key[0],
186
0
        Anum_pg_statistic_ext_stxname,
187
0
        BTEqualStrategyNumber,
188
0
        F_NAMEEQ,
189
0
        CStringGetDatum(stxname));
190
0
  ScanKeyInit(&key[1],
191
0
        Anum_pg_statistic_ext_stxnamespace,
192
0
        BTEqualStrategyNumber,
193
0
        F_OIDEQ,
194
0
        ObjectIdGetDatum(nspoid));
195
196
  /*
197
   * Try to find matching pg_statistic_ext row.
198
   */
199
0
  scan = systable_beginscan(pg_stext,
200
0
                StatisticExtNameIndexId,
201
0
                true,
202
0
                NULL,
203
0
                2,
204
0
                key);
205
206
  /* Lookup is based on a unique index, so we get either 0 or 1 tuple. */
207
0
  tup = systable_getnext(scan);
208
209
0
  if (HeapTupleIsValid(tup))
210
0
    stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
211
212
0
  systable_endscan(scan);
213
214
0
  if (!OidIsValid(stxoid))
215
0
    return NULL;
216
217
0
  return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid));
218
0
}
219
220
/*
221
 * Decode the stxkind column so that we know which stats types to expect,
222
 * returning a StakindFlags set depending on the stats kinds expected by
223
 * a pg_statistic_ext tuple.
224
 */
225
static void
226
expand_stxkind(HeapTuple tup, StakindFlags *enabled)
227
0
{
228
0
  Datum   datum;
229
0
  ArrayType  *arr;
230
0
  char     *kinds;
231
232
0
  datum = SysCacheGetAttrNotNull(STATEXTOID,
233
0
                   tup,
234
0
                   Anum_pg_statistic_ext_stxkind);
235
0
  arr = DatumGetArrayTypeP(datum);
236
0
  if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID)
237
0
    elog(ERROR, "stxkind is not a one-dimension char array");
238
239
0
  kinds = (char *) ARR_DATA_PTR(arr);
240
241
0
  for (int i = 0; i < ARR_DIMS(arr)[0]; i++)
242
0
  {
243
0
    switch (kinds[i])
244
0
    {
245
0
      case STATS_EXT_NDISTINCT:
246
0
        enabled->ndistinct = true;
247
0
        break;
248
0
      case STATS_EXT_DEPENDENCIES:
249
0
        enabled->dependencies = true;
250
0
        break;
251
0
      case STATS_EXT_MCV:
252
0
        enabled->mcv = true;
253
0
        break;
254
0
      case STATS_EXT_EXPRESSIONS:
255
0
        enabled->expressions = true;
256
0
        break;
257
0
      default:
258
0
        elog(ERROR, "incorrect stxkind %c found", kinds[i]);
259
0
        break;
260
0
    }
261
0
  }
262
0
}
263
264
/*
265
 * Perform the actual storage of a pg_statistic_ext_data tuple.
266
 */
267
static void
268
upsert_pg_statistic_ext_data(const Datum *values, const bool *nulls,
269
               const bool *replaces)
270
0
{
271
0
  Relation  pg_stextdata;
272
0
  HeapTuple stxdtup;
273
0
  HeapTuple newtup;
274
275
0
  pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
276
277
0
  stxdtup = SearchSysCache2(STATEXTDATASTXOID,
278
0
                values[Anum_pg_statistic_ext_data_stxoid - 1],
279
0
                values[Anum_pg_statistic_ext_data_stxdinherit - 1]);
280
281
0
  if (HeapTupleIsValid(stxdtup))
282
0
  {
283
0
    newtup = heap_modify_tuple(stxdtup,
284
0
                   RelationGetDescr(pg_stextdata),
285
0
                   values,
286
0
                   nulls,
287
0
                   replaces);
288
0
    CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup);
289
0
    ReleaseSysCache(stxdtup);
290
0
  }
291
0
  else
292
0
  {
293
0
    newtup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
294
0
    CatalogTupleInsert(pg_stextdata, newtup);
295
0
  }
296
297
0
  heap_freetuple(newtup);
298
299
0
  CommandCounterIncrement();
300
301
0
  table_close(pg_stextdata, RowExclusiveLock);
302
0
}
303
304
/*
305
 * Insert or update an extended statistics object.
306
 *
307
 * Major errors, such as the table not existing or permission errors, are
308
 * reported as ERRORs.  There are a couple of paths that generate a WARNING,
309
 * like when the statistics object or its schema do not exist, a conversion
310
 * failure on one statistic kind, or when other statistic kinds may still
311
 * be updated.
312
 */
313
static bool
314
extended_statistics_update(FunctionCallInfo fcinfo)
315
0
{
316
0
  char     *relnspname;
317
0
  char     *relname;
318
0
  Oid     nspoid;
319
0
  char     *nspname;
320
0
  char     *stxname;
321
0
  bool    inherited;
322
0
  Relation  pg_stext = NULL;
323
0
  HeapTuple tup = NULL;
324
325
0
  StakindFlags enabled = {false, false, false, false};
326
0
  StakindFlags has = {false, false, false, false};
327
328
0
  Form_pg_statistic_ext stxform;
329
330
0
  Datum   values[Natts_pg_statistic_ext_data] = {0};
331
0
  bool    nulls[Natts_pg_statistic_ext_data] = {0};
332
0
  bool    replaces[Natts_pg_statistic_ext_data] = {0};
333
0
  bool    success = true;
334
0
  Datum   exprdatum;
335
0
  bool    isnull;
336
0
  List     *exprs = NIL;
337
0
  int     numattnums = 0;
338
0
  int     numexprs = 0;
339
0
  int     numattrs = 0;
340
341
  /* arrays of type info, if we need them */
342
0
  Oid      *atttypids = NULL;
343
0
  int32    *atttypmods = NULL;
344
0
  Oid      *atttypcolls = NULL;
345
0
  Oid     relid;
346
0
  Oid     locked_table = InvalidOid;
347
348
  /*
349
   * Fill out the StakindFlags "has" structure based on which parameters
350
   * were provided to the function.
351
   *
352
   * The MCV stats composite value is an array of record type, but this is
353
   * externally represented as three arrays that must be interleaved into
354
   * the array of records (pg_stats_ext stores four arrays,
355
   * most_common_val_nulls is built from the contents of most_common_vals).
356
   * Therefore, none of the three array values is meaningful unless the
357
   * other two are also present and in sync in terms of array length.
358
   */
359
0
  has.mcv = (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) &&
360
0
         !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
361
0
         !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG));
362
0
  has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
363
0
  has.dependencies = !PG_ARGISNULL(DEPENDENCIES_ARG);
364
0
  has.expressions = !PG_ARGISNULL(EXPRESSIONS_ARG);
365
366
0
  if (RecoveryInProgress())
367
0
  {
368
0
    ereport(WARNING,
369
0
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
370
0
        errmsg("recovery is in progress"),
371
0
        errhint("Statistics cannot be modified during recovery."));
372
0
    return false;
373
0
  }
374
375
  /* relation arguments */
376
0
  stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
377
0
  relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
378
0
  stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
379
0
  relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
380
381
  /* extended statistics arguments */
382
0
  stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
383
0
  nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
384
0
  stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
385
0
  stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
386
0
  stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
387
0
  inherited = PG_GETARG_BOOL(INHERITED_ARG);
388
389
  /*
390
   * First open the relation where we expect to find the statistics.  This
391
   * is similar to relation and attribute statistics, so as ACL checks are
392
   * done before any locks are taken, even before any attempts related to
393
   * the extended stats object.
394
   */
395
0
  relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
396
0
                   ShareUpdateExclusiveLock, 0,
397
0
                   RangeVarCallbackForStats, &locked_table);
398
399
0
  nspoid = get_namespace_oid(nspname, true);
400
0
  if (nspoid == InvalidOid)
401
0
  {
402
0
    ereport(WARNING,
403
0
        errcode(ERRCODE_UNDEFINED_OBJECT),
404
0
        errmsg("could not find schema \"%s\"", nspname));
405
0
    success = false;
406
0
    goto cleanup;
407
0
  }
408
409
0
  pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
410
0
  tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
411
412
0
  if (!HeapTupleIsValid(tup))
413
0
  {
414
0
    ereport(WARNING,
415
0
        errcode(ERRCODE_UNDEFINED_OBJECT),
416
0
        errmsg("could not find extended statistics object \"%s.%s\"",
417
0
             nspname, stxname));
418
0
    success = false;
419
0
    goto cleanup;
420
0
  }
421
422
0
  stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
423
424
  /*
425
   * The relation tracked by the stats object has to match with the relation
426
   * we have already locked.
427
   */
428
0
  if (stxform->stxrelid != relid)
429
0
  {
430
0
    ereport(WARNING,
431
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
432
0
        errmsg("could not restore extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
433
0
             nspname, stxname,
434
0
             relnspname, relname));
435
436
0
    success = false;
437
0
    goto cleanup;
438
0
  }
439
440
  /* Find out what extended statistics kinds we should expect. */
441
0
  expand_stxkind(tup, &enabled);
442
0
  numattnums = stxform->stxkeys.dim1;
443
444
  /* decode expression (if any) */
445
0
  exprdatum = SysCacheGetAttr(STATEXTOID,
446
0
                tup,
447
0
                Anum_pg_statistic_ext_stxexprs,
448
0
                &isnull);
449
0
  if (!isnull)
450
0
  {
451
0
    char     *s;
452
453
0
    s = TextDatumGetCString(exprdatum);
454
0
    exprs = (List *) stringToNode(s);
455
0
    pfree(s);
456
457
    /*
458
     * Run the expressions through eval_const_expressions().  This is not
459
     * just an optimization, but is necessary, because the planner will be
460
     * comparing them to similarly-processed qual clauses, and may fail to
461
     * detect valid matches without this.
462
     *
463
     * We must not use canonicalize_qual(), however, since these are not
464
     * qual expressions.
465
     */
466
0
    exprs = (List *) eval_const_expressions(NULL, (Node *) exprs);
467
468
    /* May as well fix opfuncids too */
469
0
    fix_opfuncids((Node *) exprs);
470
471
    /* Compute the number of expression, for input validation. */
472
0
    numexprs = list_length(exprs);
473
0
  }
474
475
0
  numattrs = numattnums + numexprs;
476
477
  /*
478
   * If the object cannot support ndistinct, we should not have data for it.
479
   */
480
0
  if (has.ndistinct && !enabled.ndistinct)
481
0
  {
482
0
    ereport(WARNING,
483
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
484
0
        errmsg("cannot specify parameter \"%s\"",
485
0
             extarginfo[NDISTINCT_ARG].argname),
486
0
        errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
487
0
            nspname, stxname));
488
489
0
    has.ndistinct = false;
490
0
    success = false;
491
0
  }
492
493
  /*
494
   * If the object cannot support dependencies, we should not have data for
495
   * it.
496
   */
497
0
  if (has.dependencies && !enabled.dependencies)
498
0
  {
499
0
    ereport(WARNING,
500
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
501
0
        errmsg("cannot specify parameter \"%s\"",
502
0
             extarginfo[DEPENDENCIES_ARG].argname),
503
0
        errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
504
0
            nspname, stxname));
505
0
    has.dependencies = false;
506
0
    success = false;
507
0
  }
508
509
  /*
510
   * If the object cannot hold an MCV value, but any of the MCV parameters
511
   * are set, then issue a WARNING and ensure that we do not try to load MCV
512
   * stats later.  In pg_stats_ext, most_common_val_nulls, most_common_freqs
513
   * and most_common_base_freqs are NULL if most_common_vals is NULL.
514
   */
515
0
  if (!enabled.mcv)
516
0
  {
517
0
    if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
518
0
      !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
519
0
      !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
520
0
    {
521
0
      ereport(WARNING,
522
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
523
0
          errmsg("cannot specify parameters \"%s\", \"%s\", or \"%s\"",
524
0
               extarginfo[MOST_COMMON_VALS_ARG].argname,
525
0
               extarginfo[MOST_COMMON_FREQS_ARG].argname,
526
0
               extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname),
527
0
          errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
528
0
              nspname, stxname));
529
530
0
      has.mcv = false;
531
0
      success = false;
532
0
    }
533
0
  }
534
0
  else if (!has.mcv)
535
0
  {
536
    /*
537
     * If we do not have all of the MCV arrays set while the extended
538
     * statistics object expects something, something is wrong.  This
539
     * issues a WARNING if a partial input has been provided.
540
     */
541
0
    if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
542
0
      !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
543
0
      !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
544
0
    {
545
0
      ereport(WARNING,
546
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
547
0
          errmsg("could not use \"%s\", \"%s\", and \"%s\": missing one or more parameters",
548
0
               extarginfo[MOST_COMMON_VALS_ARG].argname,
549
0
               extarginfo[MOST_COMMON_FREQS_ARG].argname,
550
0
               extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname));
551
0
      success = false;
552
0
    }
553
0
  }
554
555
  /*
556
   * If the object cannot support expressions, we should not have data for
557
   * them.
558
   */
559
0
  if (has.expressions && !enabled.expressions)
560
0
  {
561
0
    ereport(WARNING,
562
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
563
0
        errmsg("cannot specify parameter \"%s\"",
564
0
             extarginfo[EXPRESSIONS_ARG].argname),
565
0
        errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
566
0
            nspname, stxname));
567
568
0
    has.expressions = false;
569
0
    success = false;
570
0
  }
571
572
  /*
573
   * Either of these statistic types requires that we supply a semi-filled
574
   * VacAttrStatsP array.
575
   *
576
   * It is not possible to use the existing lookup_var_attr_stats() and
577
   * examine_attribute() because these functions will skip attributes where
578
   * attstattarget is 0, and we may have statistics data to import for those
579
   * attributes.
580
   */
581
0
  if (has.mcv || has.expressions)
582
0
  {
583
0
    atttypids = palloc0_array(Oid, numattrs);
584
0
    atttypmods = palloc0_array(int32, numattrs);
585
0
    atttypcolls = palloc0_array(Oid, numattrs);
586
587
    /*
588
     * The leading stxkeys are attribute numbers up through numattnums.
589
     * These keys must be in ascending AttrNumber order, but we do not
590
     * rely on that.
591
     */
592
0
    for (int i = 0; i < numattnums; i++)
593
0
    {
594
0
      AttrNumber  attnum = stxform->stxkeys.values[i];
595
0
      HeapTuple atup = SearchSysCache2(ATTNUM,
596
0
                         ObjectIdGetDatum(relid),
597
0
                         Int16GetDatum(attnum));
598
599
0
      Form_pg_attribute attr;
600
601
      /* Attribute not found */
602
0
      if (!HeapTupleIsValid(atup))
603
0
        elog(ERROR, "stxkeys references nonexistent attnum %d", attnum);
604
605
0
      attr = (Form_pg_attribute) GETSTRUCT(atup);
606
607
0
      if (attr->attisdropped)
608
0
        elog(ERROR, "stxkeys references dropped attnum %d", attnum);
609
610
0
      atttypids[i] = attr->atttypid;
611
0
      atttypmods[i] = attr->atttypmod;
612
0
      atttypcolls[i] = attr->attcollation;
613
0
      ReleaseSysCache(atup);
614
0
    }
615
616
    /*
617
     * After all the positive number attnums in stxkeys come the negative
618
     * numbers (if any) which represent expressions in the order that they
619
     * appear in stxdexpr.  Because the expressions are always
620
     * monotonically decreasing from -1, there is no point in looking at
621
     * the values in stxkeys, it's enough to know how many of them there
622
     * are.
623
     */
624
0
    for (int i = numattnums; i < numattrs; i++)
625
0
    {
626
0
      Node     *expr = list_nth(exprs, i - numattnums);
627
628
0
      atttypids[i] = exprType(expr);
629
0
      atttypmods[i] = exprTypmod(expr);
630
0
      atttypcolls[i] = exprCollation(expr);
631
0
    }
632
0
  }
633
634
  /*
635
   * Populate the pg_statistic_ext_data result tuple.
636
   */
637
638
  /* Primary Key: cannot be NULL or replaced. */
639
0
  values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid);
640
0
  nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
641
0
  values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited);
642
0
  nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
643
644
  /* All unspecified parameters will be left unmodified */
645
0
  nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
646
0
  nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
647
0
  nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
648
0
  nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
649
650
  /*
651
   * For each stats kind, deserialize the data at hand and perform a round
652
   * of validation.  The resulting tuple is filled with a set of updated
653
   * values.
654
   */
655
656
0
  if (has.ndistinct)
657
0
  {
658
0
    Datum   ndistinct_datum = PG_GETARG_DATUM(NDISTINCT_ARG);
659
0
    bytea    *data = DatumGetByteaPP(ndistinct_datum);
660
0
    MVNDistinct *ndistinct = statext_ndistinct_deserialize(data);
661
662
0
    if (statext_ndistinct_validate(ndistinct, &stxform->stxkeys,
663
0
                     numexprs, WARNING))
664
0
    {
665
0
      values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = ndistinct_datum;
666
0
      nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = false;
667
0
      replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
668
0
    }
669
0
    else
670
0
      success = false;
671
672
0
    statext_ndistinct_free(ndistinct);
673
0
  }
674
675
0
  if (has.dependencies)
676
0
  {
677
0
    Datum   dependencies_datum = PG_GETARG_DATUM(DEPENDENCIES_ARG);
678
0
    bytea    *data = DatumGetByteaPP(dependencies_datum);
679
0
    MVDependencies *dependencies = statext_dependencies_deserialize(data);
680
681
0
    if (statext_dependencies_validate(dependencies, &stxform->stxkeys,
682
0
                      numexprs, WARNING))
683
0
    {
684
0
      values[Anum_pg_statistic_ext_data_stxddependencies - 1] = dependencies_datum;
685
0
      nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = false;
686
0
      replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
687
0
    }
688
0
    else
689
0
      success = false;
690
691
0
    statext_dependencies_free(dependencies);
692
0
  }
693
694
0
  if (has.mcv)
695
0
  {
696
0
    Datum   datum;
697
0
    bool    val_ok = false;
698
699
0
    datum = import_mcv(PG_GETARG_ARRAYTYPE_P(MOST_COMMON_VALS_ARG),
700
0
               PG_GETARG_ARRAYTYPE_P(MOST_COMMON_FREQS_ARG),
701
0
               PG_GETARG_ARRAYTYPE_P(MOST_COMMON_BASE_FREQS_ARG),
702
0
               atttypids, atttypmods, atttypcolls, numattrs,
703
0
               &val_ok);
704
705
0
    if (val_ok)
706
0
    {
707
0
      Assert(datum != (Datum) 0);
708
0
      values[Anum_pg_statistic_ext_data_stxdmcv - 1] = datum;
709
0
      nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = false;
710
0
      replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
711
0
    }
712
0
    else
713
0
      success = false;
714
0
  }
715
716
0
  if (has.expressions)
717
0
  {
718
0
    Datum   datum;
719
0
    Relation  pgsd;
720
0
    bool    ok = false;
721
722
0
    pgsd = table_open(StatisticRelationId, RowExclusiveLock);
723
724
    /*
725
     * Generate the expressions array.
726
     *
727
     * The atttypids, atttypmods, and atttypcolls arrays have all the
728
     * regular attributes listed first, so we can pass those arrays with a
729
     * start point after the last regular attribute.  There are numexprs
730
     * elements remaining.
731
     */
732
0
    datum = import_expressions(pgsd, numexprs,
733
0
                   &atttypids[numattnums],
734
0
                   &atttypmods[numattnums],
735
0
                   &atttypcolls[numattnums],
736
0
                   PG_GETARG_JSONB_P(EXPRESSIONS_ARG),
737
0
                   &ok);
738
739
0
    table_close(pgsd, RowExclusiveLock);
740
741
0
    if (ok)
742
0
    {
743
0
      Assert(datum != (Datum) 0);
744
0
      values[Anum_pg_statistic_ext_data_stxdexpr - 1] = datum;
745
0
      replaces[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
746
0
      nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = false;
747
0
    }
748
0
    else
749
0
      success = false;
750
0
  }
751
752
0
  upsert_pg_statistic_ext_data(values, nulls, replaces);
753
754
0
cleanup:
755
0
  if (HeapTupleIsValid(tup))
756
0
    heap_freetuple(tup);
757
0
  if (pg_stext != NULL)
758
0
    table_close(pg_stext, RowExclusiveLock);
759
0
  if (atttypids != NULL)
760
0
    pfree(atttypids);
761
0
  if (atttypmods != NULL)
762
0
    pfree(atttypmods);
763
0
  if (atttypcolls != NULL)
764
0
    pfree(atttypcolls);
765
0
  return success;
766
0
}
767
768
/*
769
 * Consistency checks to ensure that other mcvlist arrays are in alignment
770
 * with the mcv array.
771
 */
772
static bool
773
check_mcvlist_array(const ArrayType *arr, int argindex, int required_ndims,
774
          int mcv_length)
775
{
776
  if (ARR_NDIM(arr) != required_ndims)
777
  {
778
    ereport(WARNING,
779
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
780
        errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
781
             extarginfo[argindex].argname, required_ndims));
782
    return false;
783
  }
784
785
  if (array_contains_nulls(arr))
786
  {
787
    ereport(WARNING,
788
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
789
        errmsg("could not parse array \"%s\": NULL value found",
790
             extarginfo[argindex].argname));
791
    return false;
792
  }
793
794
  if (ARR_DIMS(arr)[0] != mcv_length)
795
  {
796
    ereport(WARNING,
797
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
798
        errmsg("could not parse array \"%s\": incorrect number of elements (same as \"%s\" required)",
799
             extarginfo[argindex].argname,
800
             extarginfo[MOST_COMMON_VALS_ARG].argname));
801
    return false;
802
  }
803
804
  return true;
805
}
806
807
/*
808
 * Create the stxdmcv datum from the equal-sized arrays of most common values,
809
 * their null flags, and the frequency and base frequency associated with
810
 * each value.
811
 */
812
static Datum
813
import_mcv(const ArrayType *mcv_arr, const ArrayType *freqs_arr,
814
       const ArrayType *base_freqs_arr, Oid *atttypids, int32 *atttypmods,
815
       Oid *atttypcolls, int numattrs, bool *ok)
816
0
{
817
0
  int     nitems;
818
0
  Datum    *mcv_elems;
819
0
  bool     *mcv_nulls;
820
0
  int     check_nummcv;
821
0
  Datum   mcv = (Datum) 0;
822
823
0
  *ok = false;
824
825
  /*
826
   * mcv_arr is an array of arrays.  Each inner array must have the same
827
   * number of elements "numattrs".
828
   */
829
0
  if (ARR_NDIM(mcv_arr) != 2)
830
0
  {
831
0
    ereport(WARNING,
832
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
833
0
        errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
834
0
             extarginfo[MOST_COMMON_VALS_ARG].argname, 2));
835
0
    goto mcv_error;
836
0
  }
837
838
0
  if (ARR_DIMS(mcv_arr)[1] != numattrs)
839
0
  {
840
0
    ereport(WARNING,
841
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
842
0
        errmsg("could not parse array \"%s\": found %d attributes but expected %d",
843
0
             extarginfo[MOST_COMMON_VALS_ARG].argname,
844
0
             ARR_DIMS(mcv_arr)[1], numattrs));
845
0
    goto mcv_error;
846
0
  }
847
848
  /*
849
   * "most_common_freqs" and "most_common_base_freqs" arrays must be of the
850
   * same length, one-dimension and cannot contain NULLs.  We use mcv_arr as
851
   * the reference array for determining their length.
852
   */
853
0
  nitems = ARR_DIMS(mcv_arr)[0];
854
855
  /*
856
   * Reject a MCV list larger than what statext_mcv_deserialize() is able to
857
   * accept.
858
   */
859
0
  if (nitems > STATS_MCVLIST_MAX_ITEMS)
860
0
  {
861
0
    ereport(WARNING,
862
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
863
0
        errmsg("could not parse array \"%s\": number of items (%d) exceeds maximum (%d)",
864
0
             extarginfo[MOST_COMMON_VALS_ARG].argname,
865
0
             nitems, STATS_MCVLIST_MAX_ITEMS));
866
0
    goto mcv_error;
867
0
  }
868
869
0
  if (!check_mcvlist_array(freqs_arr, MOST_COMMON_FREQS_ARG, 1, nitems) ||
870
0
    !check_mcvlist_array(base_freqs_arr, MOST_COMMON_BASE_FREQS_ARG, 1, nitems))
871
0
  {
872
    /* inconsistent input arrays found */
873
0
    goto mcv_error;
874
0
  }
875
876
  /*
877
   * This part builds the contents for "most_common_val_nulls", based on the
878
   * values from "most_common_vals".
879
   */
880
0
  deconstruct_array_builtin(mcv_arr, TEXTOID, &mcv_elems,
881
0
                &mcv_nulls, &check_nummcv);
882
883
0
  mcv = statext_mcv_import(WARNING, numattrs,
884
0
               atttypids, atttypmods, atttypcolls,
885
0
               nitems, mcv_elems, mcv_nulls,
886
0
               (float8 *) ARR_DATA_PTR(freqs_arr),
887
0
               (float8 *) ARR_DATA_PTR(base_freqs_arr));
888
889
0
  *ok = (mcv != (Datum) 0);
890
891
0
mcv_error:
892
0
  return mcv;
893
0
}
894
895
/*
896
 * Check if key is found in the list of expression argnames.
897
 */
898
static bool
899
key_in_expr_argnames(JsonbValue *key)
900
0
{
901
0
  Assert(key->type == jbvString);
902
0
  for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
903
0
  {
904
0
    if (strlen(extexprargname[i]) == key->val.string.len &&
905
0
      strncmp(extexprargname[i], key->val.string.val, key->val.string.len) == 0)
906
0
      return true;
907
0
  }
908
0
  return false;
909
0
}
910
911
/*
912
 * Verify that all of the keys in the object are valid argnames.
913
 */
914
static bool
915
check_all_expr_argnames_valid(JsonbContainer *cont, AttrNumber exprnum)
916
{
917
  bool    all_keys_valid = true;
918
919
  JsonbIterator *jbit;
920
  JsonbIteratorToken jitok;
921
  JsonbValue  jkey;
922
923
  Assert(JsonContainerIsObject(cont));
924
925
  jbit = JsonbIteratorInit(cont);
926
927
  /* We always start off with a BEGIN OBJECT */
928
  jitok = JsonbIteratorNext(&jbit, &jkey, false);
929
  Assert(jitok == WJB_BEGIN_OBJECT);
930
931
  while (true)
932
  {
933
    JsonbValue  jval;
934
935
    jitok = JsonbIteratorNext(&jbit, &jkey, false);
936
937
    /*
938
     * We have run of keys. This is the only condition where it is
939
     * memory-safe to break out of the loop.
940
     */
941
    if (jitok == WJB_END_OBJECT)
942
      break;
943
944
    /* We can only find keys inside an object */
945
    Assert(jitok == WJB_KEY);
946
    Assert(jkey.type == jbvString);
947
948
    /* A value must follow the key */
949
    jitok = JsonbIteratorNext(&jbit, &jval, false);
950
    Assert(jitok == WJB_VALUE);
951
952
    /*
953
     * If we have already found an invalid key, there is no point in
954
     * looking for more, because additional WARNINGs are just clutter. We
955
     * must continue iterating over the json to ensure that we clean up
956
     * all allocated memory.
957
     */
958
    if (!all_keys_valid)
959
      continue;
960
961
    if (!key_in_expr_argnames(&jkey))
962
    {
963
      char     *bad_element_name = jbv_string_get_cstr(&jkey);
964
965
      ereport(WARNING,
966
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
967
          errmsg("could not import element in expression %d: invalid key name",
968
               exprnum));
969
970
      pfree(bad_element_name);
971
      all_keys_valid = false;
972
    }
973
  }
974
  return all_keys_valid;
975
}
976
977
/*
978
 * Simple conversion of jbvString to cstring
979
 */
980
static char *
981
jbv_string_get_cstr(JsonbValue *jval)
982
0
{
983
0
  char     *s;
984
985
0
  Assert(jval->type == jbvString);
986
987
0
  s = palloc0(jval->val.string.len + 1);
988
0
  memcpy(s, jval->val.string.val, jval->val.string.len);
989
990
0
  return s;
991
0
}
992
993
/*
994
 * Apply a jbvString value to a safe scalar input function.
995
 */
996
static bool
997
jbv_to_infunc_datum(JsonbValue *jval, PGFunction func, AttrNumber exprnum,
998
          const char *argname, Datum *datum)
999
0
{
1000
0
  ErrorSaveContext escontext = {
1001
0
    .type = T_ErrorSaveContext,
1002
0
    .details_wanted = true
1003
0
  };
1004
1005
0
  char     *s = jbv_string_get_cstr(jval);
1006
0
  bool    ok;
1007
1008
0
  ok = DirectInputFunctionCallSafe(func, s, InvalidOid, -1,
1009
0
                   (Node *) &escontext, datum);
1010
1011
  /*
1012
   * If we got a type import error, use the report generated and add an
1013
   * error hint before throwing a warning.
1014
   */
1015
0
  if (!ok)
1016
0
  {
1017
0
    StringInfoData hint_str;
1018
1019
0
    initStringInfo(&hint_str);
1020
0
    appendStringInfo(&hint_str,
1021
0
             "Element \"%s\" in expression %d could not be parsed.",
1022
0
             argname, exprnum);
1023
1024
0
    escontext.error_data->elevel = WARNING;
1025
0
    escontext.error_data->hint = hint_str.data;
1026
1027
0
    ThrowErrorData(escontext.error_data);
1028
0
    pfree(hint_str.data);
1029
0
  }
1030
1031
0
  pfree(s);
1032
0
  return ok;
1033
0
}
1034
1035
/*
1036
 * Build an array datum with element type typid from a text datum, used as
1037
 * value of an attribute in a pg_statistic tuple.
1038
 *
1039
 * If an error is encountered, capture it, and reduce the elevel to WARNING.
1040
 *
1041
 * This is an adaptation of statatt_build_stavalues().
1042
 */
1043
static Datum
1044
array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod,
1045
        AttrNumber exprnum, const char *element_name, bool *ok)
1046
{
1047
  Datum   result;
1048
1049
  ErrorSaveContext escontext = {
1050
    .type = T_ErrorSaveContext,
1051
    .details_wanted = true
1052
  };
1053
1054
  *ok = false;
1055
1056
  /*
1057
   * If the array_in function returned an error, we will want to report that
1058
   * ERROR as a WARNING, and add some location context to the error message.
1059
   * Overwriting the existing hint (if any) is not ideal, and an error
1060
   * context would only work for level >= ERROR.
1061
   */
1062
  if (!InputFunctionCallSafe(array_in, (char *) s, typid, typmod,
1063
                 (Node *) &escontext, &result))
1064
  {
1065
    StringInfoData hint_str;
1066
1067
    initStringInfo(&hint_str);
1068
    appendStringInfo(&hint_str,
1069
             "Element \"%s\" in expression %d could not be parsed.",
1070
             element_name, exprnum);
1071
    escontext.error_data->elevel = WARNING;
1072
    escontext.error_data->hint = hint_str.data;
1073
    ThrowErrorData(escontext.error_data);
1074
    pfree(hint_str.data);
1075
    return (Datum) 0;
1076
  }
1077
1078
  if (ARR_NDIM(DatumGetArrayTypeP(result)) != 1)
1079
  {
1080
    ereport(WARNING,
1081
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1082
         errmsg("could not import element \"%s\" in expression %d: must be a one-dimensional array",
1083
            element_name, exprnum)));
1084
    return (Datum) 0;
1085
  }
1086
1087
  if (array_contains_nulls(DatumGetArrayTypeP(result)))
1088
  {
1089
    ereport(WARNING,
1090
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1091
        errmsg("could not import element \"%s\" in expression %d: null value found",
1092
             element_name, exprnum));
1093
    return (Datum) 0;
1094
  }
1095
1096
  *ok = true;
1097
  return result;
1098
}
1099
1100
/*
1101
 * Create a pg_statistic tuple from an expression JSONB container.
1102
 *
1103
 * The pg_statistic tuple is pre-populated with acceptable defaults, therefore
1104
 * even if there is an issue with all of the keys in the container, we can
1105
 * still return a legit tuple datum.
1106
 *
1107
 * Set pg_statistic_ok to true if all of the values found in the container
1108
 * were imported without issue.  pg_statistic_ok is switched to "true" once
1109
 * the full pg_statistic tuple has been built and validated.
1110
 */
1111
static Datum
1112
import_pg_statistic(Relation pgsd, JsonbContainer *cont,
1113
          AttrNumber exprnum, FmgrInfo *array_in_fn,
1114
          Oid typid, int32 typmod, Oid typcoll,
1115
          bool *pg_statistic_ok)
1116
0
{
1117
0
  const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
1118
0
  TypeCacheEntry *typcache;
1119
0
  Datum   values[Natts_pg_statistic];
1120
0
  bool    nulls[Natts_pg_statistic];
1121
0
  bool    replaces[Natts_pg_statistic];
1122
0
  HeapTuple pgstup = NULL;
1123
0
  Datum   pgstdat = (Datum) 0;
1124
0
  Oid     elemtypid = InvalidOid;
1125
0
  Oid     elemeqopr = InvalidOid;
1126
0
  bool    found[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
1127
0
  JsonbValue  val[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
1128
1129
  /* Assume the worst by default. */
1130
0
  *pg_statistic_ok = false;
1131
1132
0
  if (!JsonContainerIsObject(cont))
1133
0
  {
1134
0
    ereport(WARNING,
1135
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1136
0
        errmsg("could not parse \"%s\": invalid element in expression %d",
1137
0
             argname, exprnum));
1138
0
    goto pg_statistic_error;
1139
0
  }
1140
1141
  /*
1142
   * Loop through all keys that we need to look up.  If any value found is
1143
   * neither a string nor a NULL, there is not much we can do, so just give
1144
   * on the entire tuple for this expression.
1145
   */
1146
0
  for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
1147
0
  {
1148
0
    const char *s = extexprargname[i];
1149
0
    int     len = strlen(s);
1150
1151
0
    if (getKeyJsonValueFromContainer(cont, s, len, &val[i]) == NULL)
1152
0
      continue;
1153
1154
0
    switch (val[i].type)
1155
0
    {
1156
0
      case jbvString:
1157
0
        found[i] = true;
1158
0
        break;
1159
1160
0
      case jbvNull:
1161
0
        break;
1162
1163
0
      default:
1164
0
        ereport(WARNING,
1165
0
            errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1166
0
            errmsg("could not parse \"%s\": invalid element in expression %d", argname, exprnum),
1167
0
            errhint("Value of element \"%s\" must be a null or a string.", s));
1168
0
        goto pg_statistic_error;
1169
0
    }
1170
0
  }
1171
1172
  /* Look for invalid keys */
1173
0
  if (!check_all_expr_argnames_valid(cont, exprnum))
1174
0
    goto pg_statistic_error;
1175
1176
  /*
1177
   * There are two arg pairs, MCV+MCF and MCEV+MCEF.  Both values must
1178
   * either be found or not be found.  Any disagreement is a warning.  Once
1179
   * we have ruled out disagreeing pairs, we can use either found flag as a
1180
   * proxy for the other.
1181
   */
1182
0
  if (found[MOST_COMMON_VALS_ELEM] != found[MOST_COMMON_FREQS_ELEM])
1183
0
  {
1184
0
    ereport(WARNING,
1185
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1186
0
        errmsg("could not parse \"%s\": invalid element in expression %d",
1187
0
             argname, exprnum),
1188
0
        errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
1189
0
            extexprargname[MOST_COMMON_VALS_ELEM],
1190
0
            extexprargname[MOST_COMMON_FREQS_ELEM]));
1191
0
    goto pg_statistic_error;
1192
0
  }
1193
0
  if (found[MOST_COMMON_ELEMS_ELEM] != found[MOST_COMMON_ELEM_FREQS_ELEM])
1194
0
  {
1195
0
    ereport(WARNING,
1196
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1197
0
        errmsg("could not parse \"%s\": invalid element in expression %d",
1198
0
             argname, exprnum),
1199
0
        errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
1200
0
            extexprargname[MOST_COMMON_ELEMS_ELEM],
1201
0
            extexprargname[MOST_COMMON_ELEM_FREQS_ELEM]));
1202
0
    goto pg_statistic_error;
1203
0
  }
1204
1205
  /*
1206
   * Range types may expect three values to be set.  All three of them must
1207
   * either be found or not be found.  Any disagreement is a warning.
1208
   */
1209
0
  if (found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_EMPTY_FRAC_ELEM] ||
1210
0
    found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_BOUNDS_HISTOGRAM_ELEM])
1211
0
  {
1212
0
    ereport(WARNING,
1213
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1214
0
        errmsg("could not parse \"%s\": invalid element in expression %d",
1215
0
             argname, exprnum),
1216
0
        errhint("\"%s\", \"%s\", and \"%s\" must be all either strings or all nulls.",
1217
0
            extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
1218
0
            extexprargname[RANGE_EMPTY_FRAC_ELEM],
1219
0
            extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
1220
0
    goto pg_statistic_error;
1221
0
  }
1222
1223
  /* This finds the right operators even if atttypid is a domain */
1224
0
  typcache = lookup_type_cache(typid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
1225
1226
0
  statatt_init_empty_tuple(InvalidOid, InvalidAttrNumber, false,
1227
0
               values, nulls, replaces);
1228
1229
  /*
1230
   * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
1231
   * compute_tsvector_stats().
1232
   */
1233
0
  if (typid == TSVECTOROID)
1234
0
    typcoll = DEFAULT_COLLATION_OID;
1235
1236
  /*
1237
   * We only need to fetch element type and eq operator if we have a stat of
1238
   * type MCELEM or DECHIST, otherwise the values are unnecessary and not
1239
   * meaningful.
1240
   */
1241
0
  if (found[MOST_COMMON_ELEMS_ELEM] || found[ELEM_COUNT_HISTOGRAM_ELEM])
1242
0
  {
1243
0
    if (!statatt_get_elem_type(typid, typcache->typtype,
1244
0
                   &elemtypid, &elemeqopr))
1245
0
    {
1246
0
      ereport(WARNING,
1247
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1248
0
          errmsg("could not parse \"%s\": invalid element type in expression %d",
1249
0
               argname, exprnum));
1250
0
      goto pg_statistic_error;
1251
0
    }
1252
0
  }
1253
1254
  /*
1255
   * These three fields can only be set if dealing with a range or
1256
   * multi-range type.
1257
   */
1258
0
  if (found[RANGE_LENGTH_HISTOGRAM_ELEM] ||
1259
0
    found[RANGE_EMPTY_FRAC_ELEM] ||
1260
0
    found[RANGE_BOUNDS_HISTOGRAM_ELEM])
1261
0
  {
1262
0
    if (typcache->typtype != TYPTYPE_RANGE &&
1263
0
      typcache->typtype != TYPTYPE_MULTIRANGE)
1264
0
    {
1265
0
      ereport(WARNING,
1266
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1267
0
          errmsg("could not parse \"%s\": invalid data in expression %d",
1268
0
               argname, exprnum),
1269
0
          errhint("\"%s\", \"%s\", and \"%s\" can only be set for a range type.",
1270
0
              extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
1271
0
              extexprargname[RANGE_EMPTY_FRAC_ELEM],
1272
0
              extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
1273
0
      goto pg_statistic_error;
1274
0
    }
1275
0
  }
1276
1277
  /* null_frac */
1278
0
  if (found[NULL_FRAC_ELEM])
1279
0
  {
1280
0
    Datum   datum;
1281
1282
0
    if (jbv_to_infunc_datum(&val[NULL_FRAC_ELEM], float4in, exprnum,
1283
0
                extexprargname[NULL_FRAC_ELEM], &datum))
1284
0
      values[Anum_pg_statistic_stanullfrac - 1] = datum;
1285
0
    else
1286
0
      goto pg_statistic_error;
1287
0
  }
1288
1289
  /* avg_width */
1290
0
  if (found[AVG_WIDTH_ELEM])
1291
0
  {
1292
0
    Datum   datum;
1293
1294
0
    if (jbv_to_infunc_datum(&val[AVG_WIDTH_ELEM], int4in, exprnum,
1295
0
                extexprargname[AVG_WIDTH_ELEM], &datum))
1296
0
      values[Anum_pg_statistic_stawidth - 1] = datum;
1297
0
    else
1298
0
      goto pg_statistic_error;
1299
0
  }
1300
1301
  /* n_distinct */
1302
0
  if (found[N_DISTINCT_ELEM])
1303
0
  {
1304
0
    Datum   datum;
1305
1306
0
    if (jbv_to_infunc_datum(&val[N_DISTINCT_ELEM], float4in, exprnum,
1307
0
                extexprargname[N_DISTINCT_ELEM], &datum))
1308
0
      values[Anum_pg_statistic_stadistinct - 1] = datum;
1309
0
    else
1310
0
      goto pg_statistic_error;
1311
0
  }
1312
1313
  /*
1314
   * The STAKIND statistics are the same as the ones found in attribute
1315
   * stats.  However, these are all derived from json strings, whereas the
1316
   * ones derived for attribute stats are a mix of datatypes.  This limits
1317
   * the opportunities for code sharing between the two.
1318
   *
1319
   * Some statistic kinds have both a stanumbers and a stavalues components.
1320
   * In those cases, both values must either be NOT NULL or both NULL, and
1321
   * if they aren't then we need to reject that stakind completely.
1322
   * Currently we go a step further and reject the expression array
1323
   * completely.
1324
   */
1325
1326
0
  if (found[MOST_COMMON_VALS_ELEM])
1327
0
  {
1328
0
    Datum   stavalues;
1329
0
    Datum   stanumbers;
1330
0
    bool    val_ok = false;
1331
0
    bool    num_ok = false;
1332
0
    char     *s;
1333
1334
0
    s = jbv_string_get_cstr(&val[MOST_COMMON_VALS_ELEM]);
1335
0
    stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
1336
0
                  extexprargname[MOST_COMMON_VALS_ELEM],
1337
0
                  &val_ok);
1338
1339
0
    pfree(s);
1340
1341
0
    s = jbv_string_get_cstr(&val[MOST_COMMON_FREQS_ELEM]);
1342
0
    stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
1343
0
                   extexprargname[MOST_COMMON_FREQS_ELEM],
1344
0
                   &num_ok);
1345
0
    pfree(s);
1346
1347
    /* Only set the slot if both datums have been built */
1348
0
    if (val_ok && num_ok)
1349
0
    {
1350
0
      ArrayType  *vals_arr = DatumGetArrayTypeP(stavalues);
1351
0
      ArrayType  *nums_arr = DatumGetArrayTypeP(stanumbers);
1352
0
      int     nvals = ARR_DIMS(vals_arr)[0];
1353
0
      int     nnums = ARR_DIMS(nums_arr)[0];
1354
1355
0
      if (nvals != nnums)
1356
0
      {
1357
0
        ereport(WARNING,
1358
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1359
0
             errmsg("could not parse \"%s\": incorrect number of elements (same as \"%s\" required)",
1360
0
                "most_common_vals",
1361
0
                "most_common_freqs")));
1362
0
        goto pg_statistic_error;
1363
0
      }
1364
1365
0
      statatt_set_slot(values, nulls, replaces,
1366
0
               STATISTIC_KIND_MCV,
1367
0
               typcache->eq_opr, typcoll,
1368
0
               stanumbers, false, stavalues, false);
1369
0
    }
1370
0
    else
1371
0
      goto pg_statistic_error;
1372
0
  }
1373
1374
  /* STATISTIC_KIND_HISTOGRAM */
1375
0
  if (found[HISTOGRAM_BOUNDS_ELEM])
1376
0
  {
1377
0
    Datum   stavalues;
1378
0
    bool    val_ok = false;
1379
0
    char     *s = jbv_string_get_cstr(&val[HISTOGRAM_BOUNDS_ELEM]);
1380
1381
0
    stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
1382
0
                  extexprargname[HISTOGRAM_BOUNDS_ELEM],
1383
0
                  &val_ok);
1384
0
    pfree(s);
1385
1386
0
    if (val_ok)
1387
0
      statatt_set_slot(values, nulls, replaces,
1388
0
               STATISTIC_KIND_HISTOGRAM,
1389
0
               typcache->lt_opr, typcoll,
1390
0
               0, true, stavalues, false);
1391
0
    else
1392
0
      goto pg_statistic_error;
1393
0
  }
1394
1395
  /* STATISTIC_KIND_CORRELATION */
1396
0
  if (found[CORRELATION_ELEM])
1397
0
  {
1398
0
    Datum   corr[] = {(Datum) 0};
1399
1400
0
    if (jbv_to_infunc_datum(&val[CORRELATION_ELEM], float4in, exprnum,
1401
0
                extexprargname[CORRELATION_ELEM], &corr[0]))
1402
0
    {
1403
0
      ArrayType  *arry = construct_array_builtin(corr, 1, FLOAT4OID);
1404
0
      Datum   stanumbers = PointerGetDatum(arry);
1405
1406
0
      statatt_set_slot(values, nulls, replaces,
1407
0
               STATISTIC_KIND_CORRELATION,
1408
0
               typcache->lt_opr, typcoll,
1409
0
               stanumbers, false, 0, true);
1410
0
    }
1411
0
    else
1412
0
      goto pg_statistic_error;
1413
0
  }
1414
1415
  /* STATISTIC_KIND_MCELEM */
1416
0
  if (found[MOST_COMMON_ELEMS_ELEM])
1417
0
  {
1418
0
    Datum   stavalues;
1419
0
    Datum   stanumbers;
1420
0
    bool    val_ok = false;
1421
0
    bool    num_ok = false;
1422
0
    char     *s;
1423
1424
0
    s = jbv_string_get_cstr(&val[MOST_COMMON_ELEMS_ELEM]);
1425
0
    stavalues = array_in_safe(array_in_fn, s, elemtypid, typmod, exprnum,
1426
0
                  extexprargname[MOST_COMMON_ELEMS_ELEM],
1427
0
                  &val_ok);
1428
0
    pfree(s);
1429
1430
1431
0
    s = jbv_string_get_cstr(&val[MOST_COMMON_ELEM_FREQS_ELEM]);
1432
0
    stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
1433
0
                   extexprargname[MOST_COMMON_ELEM_FREQS_ELEM],
1434
0
                   &num_ok);
1435
0
    pfree(s);
1436
1437
    /* Only set the slot if both datums have been built */
1438
0
    if (val_ok && num_ok)
1439
0
      statatt_set_slot(values, nulls, replaces,
1440
0
               STATISTIC_KIND_MCELEM,
1441
0
               elemeqopr, typcoll,
1442
0
               stanumbers, false, stavalues, false);
1443
0
    else
1444
0
      goto pg_statistic_error;
1445
0
  }
1446
1447
  /* STATISTIC_KIND_DECHIST */
1448
0
  if (found[ELEM_COUNT_HISTOGRAM_ELEM])
1449
0
  {
1450
0
    Datum   stanumbers;
1451
0
    bool    num_ok = false;
1452
0
    char     *s;
1453
1454
0
    s = jbv_string_get_cstr(&val[ELEM_COUNT_HISTOGRAM_ELEM]);
1455
0
    stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
1456
0
                   extexprargname[ELEM_COUNT_HISTOGRAM_ELEM],
1457
0
                   &num_ok);
1458
0
    pfree(s);
1459
1460
0
    if (num_ok)
1461
0
      statatt_set_slot(values, nulls, replaces, STATISTIC_KIND_DECHIST,
1462
0
               elemeqopr, typcoll, stanumbers, false, 0, true);
1463
0
    else
1464
0
      goto pg_statistic_error;
1465
0
  }
1466
1467
  /*
1468
   * STATISTIC_KIND_BOUNDS_HISTOGRAM
1469
   *
1470
   * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
1471
   * though it is numerically greater, and all other stakinds appear in
1472
   * numerical order.
1473
   */
1474
0
  if (found[RANGE_BOUNDS_HISTOGRAM_ELEM])
1475
0
  {
1476
0
    Datum   stavalues;
1477
0
    bool    val_ok = false;
1478
0
    char     *s;
1479
0
    Oid     rtypid = typid;
1480
1481
    /*
1482
     * If it's a multirange, step down to the range type, as is done by
1483
     * multirange_typanalyze().
1484
     */
1485
0
    if (type_is_multirange(typid))
1486
0
      rtypid = get_multirange_range(typid);
1487
1488
0
    s = jbv_string_get_cstr(&val[RANGE_BOUNDS_HISTOGRAM_ELEM]);
1489
1490
0
    stavalues = array_in_safe(array_in_fn, s, rtypid, typmod, exprnum,
1491
0
                  extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM],
1492
0
                  &val_ok);
1493
1494
0
    if (val_ok && statatt_check_bounds_histogram(stavalues))
1495
0
      statatt_set_slot(values, nulls, replaces,
1496
0
               STATISTIC_KIND_BOUNDS_HISTOGRAM,
1497
0
               InvalidOid, InvalidOid,
1498
0
               0, true, stavalues, false);
1499
0
    else
1500
0
      goto pg_statistic_error;
1501
0
  }
1502
1503
  /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
1504
0
  if (found[RANGE_LENGTH_HISTOGRAM_ELEM])
1505
0
  {
1506
0
    Datum   empty_frac[] = {(Datum) 0};
1507
0
    Datum   stavalues;
1508
0
    Datum   stanumbers;
1509
0
    bool    val_ok = false;
1510
0
    char     *s;
1511
1512
0
    if (jbv_to_infunc_datum(&val[RANGE_EMPTY_FRAC_ELEM], float4in, exprnum,
1513
0
                extexprargname[RANGE_EMPTY_FRAC_ELEM], &empty_frac[0]))
1514
0
    {
1515
0
      ArrayType  *arry = construct_array_builtin(empty_frac, 1, FLOAT4OID);
1516
1517
0
      stanumbers = PointerGetDatum(arry);
1518
0
    }
1519
0
    else
1520
0
      goto pg_statistic_error;
1521
1522
0
    s = jbv_string_get_cstr(&val[RANGE_LENGTH_HISTOGRAM_ELEM]);
1523
0
    stavalues = array_in_safe(array_in_fn, s, FLOAT8OID, -1, exprnum,
1524
0
                  extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
1525
0
                  &val_ok);
1526
1527
0
    if (val_ok)
1528
0
      statatt_set_slot(values, nulls, replaces,
1529
0
               STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
1530
0
               Float8LessOperator, InvalidOid,
1531
0
               stanumbers, false, stavalues, false);
1532
0
    else
1533
0
      goto pg_statistic_error;
1534
0
  }
1535
1536
0
  pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
1537
0
  pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
1538
1539
0
  heap_freetuple(pgstup);
1540
1541
0
  *pg_statistic_ok = true;
1542
1543
0
  return pgstdat;
1544
1545
0
pg_statistic_error:
1546
0
  return (Datum) 0;
1547
0
}
1548
1549
/*
1550
 * Create the stxdexpr datum, which is an array of pg_statistic rows with all
1551
 * of the object identification fields left at defaults, using the json array
1552
 * of objects/nulls referenced against the datatypes for the expressions.
1553
 *
1554
 * The exprs_is_perfect will be set to true if all pg_statistic rows were
1555
 * imported cleanly.  If any of them experienced a problem (and thus were
1556
 * set as if they were null), then the expression is kept but exprs_is_perfect
1557
 * will be marked as false.
1558
 *
1559
 * This datum is needed to fill out a complete pg_statistic_ext_data tuple.
1560
 */
1561
static Datum
1562
import_expressions(Relation pgsd, int numexprs,
1563
           Oid *atttypids, int32 *atttypmods,
1564
           Oid *atttypcolls, Jsonb *exprs_jsonb,
1565
           bool *exprs_is_perfect)
1566
0
{
1567
0
  const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
1568
0
  Oid     pgstypoid = get_rel_type_id(StatisticRelationId);
1569
0
  ArrayBuildState *astate = NULL;
1570
0
  Datum   result = (Datum) 0;
1571
0
  int     num_import_ok = 0;
1572
0
  JsonbContainer *root;
1573
0
  int     num_root_elements;
1574
1575
0
  FmgrInfo  array_in_fn;
1576
1577
0
  *exprs_is_perfect = false;
1578
1579
  /* Json schema must be [{expr},...] */
1580
0
  if (!JB_ROOT_IS_ARRAY(exprs_jsonb))
1581
0
  {
1582
0
    ereport(WARNING,
1583
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1584
0
        errmsg("could not parse \"%s\": root-level array required", argname));
1585
0
    goto exprs_error;
1586
0
  }
1587
1588
0
  root = &exprs_jsonb->root;
1589
1590
  /*
1591
   * The number of elements in the array must match the number of
1592
   * expressions in the stats object definition.
1593
   */
1594
0
  num_root_elements = JsonContainerSize(root);
1595
0
  if (numexprs != num_root_elements)
1596
0
  {
1597
0
    ereport(WARNING,
1598
0
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1599
0
        errmsg("could not parse \"%s\": incorrect number of elements (%d required)",
1600
0
             argname, numexprs));
1601
0
    goto exprs_error;
1602
0
  }
1603
1604
0
  fmgr_info(F_ARRAY_IN, &array_in_fn);
1605
1606
  /*
1607
   * Iterate over each expected expression object in the array.  Some of
1608
   * them could be null.  If the element is a completely wrong data type,
1609
   * give a WARNING and then treat the element like a NULL element in the
1610
   * result array.
1611
   *
1612
   * Each expression *MUST* have a value appended in the result pg_statistic
1613
   * array.
1614
   */
1615
0
  for (int i = 0; i < numexprs; i++)
1616
0
  {
1617
0
    Datum   pgstdat = (Datum) 0;
1618
0
    bool    isnull = false;
1619
0
    AttrNumber  exprattnum = -1 - i;
1620
1621
0
    JsonbValue *elem = getIthJsonbValueFromContainer(root, i);
1622
1623
0
    switch (elem->type)
1624
0
    {
1625
0
      case jbvBinary:
1626
0
        {
1627
0
          bool    sta_ok = false;
1628
1629
          /* a real stats object */
1630
0
          pgstdat = import_pg_statistic(pgsd, elem->val.binary.data,
1631
0
                          exprattnum, &array_in_fn,
1632
0
                          atttypids[i], atttypmods[i],
1633
0
                          atttypcolls[i], &sta_ok);
1634
1635
          /*
1636
           * If some incorrect data has been found, assign NULL for
1637
           * this expression as a mean to give up.
1638
           */
1639
0
          if (sta_ok)
1640
0
            num_import_ok++;
1641
0
          else
1642
0
          {
1643
0
            isnull = true;
1644
0
            pgstdat = (Datum) 0;
1645
0
          }
1646
0
        }
1647
0
        break;
1648
1649
0
      case jbvNull:
1650
        /* NULL placeholder for invalid data, still fine */
1651
0
        isnull = true;
1652
0
        num_import_ok++;
1653
0
        break;
1654
1655
0
      default:
1656
        /* cannot possibly be valid */
1657
0
        ereport(WARNING,
1658
0
            errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1659
0
            errmsg("could not parse \"%s\": invalid element in expression %d",
1660
0
                 argname, exprattnum));
1661
0
        goto exprs_error;
1662
0
    }
1663
1664
0
    astate = accumArrayResult(astate, pgstdat, isnull, pgstypoid,
1665
0
                  CurrentMemoryContext);
1666
0
  }
1667
1668
  /*
1669
   * The expressions datum is perfect *if and only if* all of the
1670
   * pg_statistic elements were also ok, for a number of elements equal to
1671
   * the number of expressions.  Anything else means a failure in restoring
1672
   * the data of this statistics object.
1673
   */
1674
0
  *exprs_is_perfect = (num_import_ok == numexprs);
1675
1676
0
  if (astate != NULL)
1677
0
    result = makeArrayResult(astate, CurrentMemoryContext);
1678
1679
0
  return result;
1680
1681
0
exprs_error:
1682
0
  if (astate != NULL)
1683
0
    pfree(astate);
1684
0
  return (Datum) 0;
1685
0
};
1686
1687
/*
1688
 * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
1689
 * row and "inherited" pair.
1690
 */
1691
static bool
1692
delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
1693
0
{
1694
0
  Relation  sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
1695
0
  HeapTuple oldtup;
1696
0
  bool    result = false;
1697
1698
  /* Is there already a pg_statistic_ext_data tuple for this attribute? */
1699
0
  oldtup = SearchSysCache2(STATEXTDATASTXOID,
1700
0
               ObjectIdGetDatum(stxoid),
1701
0
               BoolGetDatum(inherited));
1702
1703
0
  if (HeapTupleIsValid(oldtup))
1704
0
  {
1705
0
    CatalogTupleDelete(sed, &oldtup->t_self);
1706
0
    ReleaseSysCache(oldtup);
1707
0
    result = true;
1708
0
  }
1709
1710
0
  table_close(sed, RowExclusiveLock);
1711
1712
0
  CommandCounterIncrement();
1713
1714
0
  return result;
1715
0
}
1716
1717
/*
1718
 * Restore (insert or replace) statistics for the given statistics object.
1719
 *
1720
 * This function accepts variadic arguments in key-value pairs, which are
1721
 * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
1722
 * arguments.
1723
 */
1724
Datum
1725
pg_restore_extended_stats(PG_FUNCTION_ARGS)
1726
0
{
1727
0
  LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
1728
0
  bool    result = true;
1729
1730
0
  InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
1731
0
               InvalidOid, NULL, NULL);
1732
1733
0
  if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
1734
0
    result = false;
1735
1736
0
  if (!extended_statistics_update(positional_fcinfo))
1737
0
    result = false;
1738
1739
0
  PG_RETURN_BOOL(result);
1740
0
}
1741
1742
/*
1743
 * Delete statistics for the given statistics object.
1744
 */
1745
Datum
1746
pg_clear_extended_stats(PG_FUNCTION_ARGS)
1747
{
1748
  char     *relnspname;
1749
  char     *relname;
1750
  char     *nspname;
1751
  Oid     nspoid;
1752
  Oid     relid;
1753
  char     *stxname;
1754
  bool    inherited;
1755
  Relation  pg_stext;
1756
  HeapTuple tup;
1757
  Form_pg_statistic_ext stxform;
1758
  Oid     locked_table = InvalidOid;
1759
1760
  /* relation arguments */
1761
  stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
1762
  relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
1763
  stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
1764
  relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
1765
1766
  /* extended statistics arguments */
1767
  stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
1768
  nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
1769
  stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
1770
  stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
1771
  stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
1772
  inherited = PG_GETARG_BOOL(INHERITED_ARG);
1773
1774
  if (RecoveryInProgress())
1775
  {
1776
    ereport(WARNING,
1777
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1778
        errmsg("recovery is in progress"),
1779
        errhint("Statistics cannot be modified during recovery."));
1780
    PG_RETURN_VOID();
1781
  }
1782
1783
  /*
1784
   * First open the relation where we expect to find the statistics.  This
1785
   * is similar to relation and attribute statistics, so as ACL checks are
1786
   * done before any locks are taken, even before any attempts related to
1787
   * the extended stats object.
1788
   */
1789
  relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
1790
                   ShareUpdateExclusiveLock, 0,
1791
                   RangeVarCallbackForStats, &locked_table);
1792
1793
  /* Now check if the namespace of the stats object exists. */
1794
  nspoid = get_namespace_oid(nspname, true);
1795
  if (nspoid == InvalidOid)
1796
  {
1797
    ereport(WARNING,
1798
        errcode(ERRCODE_UNDEFINED_OBJECT),
1799
        errmsg("could not find schema \"%s\"", nspname));
1800
    PG_RETURN_VOID();
1801
  }
1802
1803
  pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
1804
  tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
1805
1806
  if (!HeapTupleIsValid(tup))
1807
  {
1808
    table_close(pg_stext, RowExclusiveLock);
1809
    ereport(WARNING,
1810
        errcode(ERRCODE_UNDEFINED_OBJECT),
1811
        errmsg("could not find extended statistics object \"%s.%s\"",
1812
             nspname, stxname));
1813
    PG_RETURN_VOID();
1814
  }
1815
1816
  stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
1817
1818
  /*
1819
   * This should be consistent, based on the lock taken on the table when we
1820
   * started.
1821
   */
1822
  if (stxform->stxrelid != relid)
1823
  {
1824
    heap_freetuple(tup);
1825
    table_close(pg_stext, RowExclusiveLock);
1826
    ereport(WARNING,
1827
        errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1828
        errmsg("could not clear extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
1829
             get_namespace_name(nspoid), stxname,
1830
             relnspname, relname));
1831
    PG_RETURN_VOID();
1832
  }
1833
1834
  delete_pg_statistic_ext_data(stxform->oid, inherited);
1835
  heap_freetuple(tup);
1836
1837
  table_close(pg_stext, RowExclusiveLock);
1838
1839
  PG_RETURN_VOID();
1840
}