Branch data Line data Source code
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_database.h"
23 : : #include "catalog/pg_statistic_ext.h"
24 : : #include "catalog/pg_statistic_ext_data.h"
25 : : #include "miscadmin.h"
26 : : #include "nodes/makefuncs.h"
27 : : #include "statistics/extended_stats_internal.h"
28 : : #include "statistics/stat_utils.h"
29 : : #include "utils/acl.h"
30 : : #include "utils/array.h"
31 : : #include "utils/builtins.h"
32 : : #include "utils/fmgroids.h"
33 : : #include "utils/lsyscache.h"
34 : : #include "utils/syscache.h"
35 : :
36 : :
37 : : /*
38 : : * Index of the arguments for the SQL functions.
39 : : */
40 : : enum extended_stats_argnum
41 : : {
42 : : RELSCHEMA_ARG = 0,
43 : : RELNAME_ARG,
44 : : STATSCHEMA_ARG,
45 : : STATNAME_ARG,
46 : : INHERITED_ARG,
47 : : NDISTINCT_ARG,
48 : : NUM_EXTENDED_STATS_ARGS,
49 : : };
50 : :
51 : : /*
52 : : * The argument names and type OIDs of the arguments for the SQL
53 : : * functions.
54 : : */
55 : : static struct StatsArgInfo extarginfo[] =
56 : : {
57 : : [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
58 : : [RELNAME_ARG] = {"relname", TEXTOID},
59 : : [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID},
60 : : [STATNAME_ARG] = {"statistics_name", TEXTOID},
61 : : [INHERITED_ARG] = {"inherited", BOOLOID},
62 : : [NDISTINCT_ARG] = {"n_distinct", PG_NDISTINCTOID},
63 : : [NUM_EXTENDED_STATS_ARGS] = {0},
64 : : };
65 : :
66 : : static bool extended_statistics_update(FunctionCallInfo fcinfo);
67 : :
68 : : static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
69 : : const char *stxname);
70 : : static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited);
71 : :
72 : : /*
73 : : * Track the extended statistics kinds expected for a pg_statistic_ext
74 : : * tuple.
75 : : */
76 : : typedef struct
77 : : {
78 : : bool ndistinct;
79 : : bool dependencies;
80 : : bool mcv;
81 : : bool expressions;
82 : : } StakindFlags;
83 : :
84 : : static void expand_stxkind(HeapTuple tup, StakindFlags *enabled);
85 : : static void upsert_pg_statistic_ext_data(const Datum *values,
86 : : const bool *nulls,
87 : : const bool *replaces);
88 : :
89 : : /*
90 : : * Fetch a pg_statistic_ext row by name and namespace OID.
91 : : */
92 : : static HeapTuple
93 : 0 : get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
94 : : {
95 : 0 : ScanKeyData key[2];
96 : 0 : SysScanDesc scan;
97 : 0 : HeapTuple tup;
98 : 0 : Oid stxoid = InvalidOid;
99 : :
100 : 0 : ScanKeyInit(&key[0],
101 : : Anum_pg_statistic_ext_stxname,
102 : : BTEqualStrategyNumber,
103 : : F_NAMEEQ,
104 : 0 : CStringGetDatum(stxname));
105 : 0 : ScanKeyInit(&key[1],
106 : : Anum_pg_statistic_ext_stxnamespace,
107 : : BTEqualStrategyNumber,
108 : : F_OIDEQ,
109 : 0 : ObjectIdGetDatum(nspoid));
110 : :
111 : : /*
112 : : * Try to find matching pg_statistic_ext row.
113 : : */
114 : 0 : scan = systable_beginscan(pg_stext,
115 : : StatisticExtNameIndexId,
116 : : true,
117 : : NULL,
118 : : 2,
119 : 0 : key);
120 : :
121 : : /* Lookup is based on a unique index, so we get either 0 or 1 tuple. */
122 : 0 : tup = systable_getnext(scan);
123 : :
124 [ # # ]: 0 : if (HeapTupleIsValid(tup))
125 : 0 : stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
126 : :
127 : 0 : systable_endscan(scan);
128 : :
129 [ # # ]: 0 : if (!OidIsValid(stxoid))
130 : 0 : return NULL;
131 : :
132 : 0 : return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid));
133 : 0 : }
134 : :
135 : : /*
136 : : * Decode the stxkind column so that we know which stats types to expect,
137 : : * returning a StakindFlags set depending on the stats kinds expected by
138 : : * a pg_statistic_ext tuple.
139 : : */
140 : : static void
141 : 0 : expand_stxkind(HeapTuple tup, StakindFlags *enabled)
142 : : {
143 : 0 : Datum datum;
144 : 0 : ArrayType *arr;
145 : 0 : char *kinds;
146 : :
147 : 0 : datum = SysCacheGetAttrNotNull(STATEXTOID,
148 : 0 : tup,
149 : : Anum_pg_statistic_ext_stxkind);
150 : 0 : arr = DatumGetArrayTypeP(datum);
151 [ # # ]: 0 : if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID)
152 [ # # # # ]: 0 : elog(ERROR, "stxkind is not a one-dimension char array");
153 : :
154 [ # # ]: 0 : kinds = (char *) ARR_DATA_PTR(arr);
155 : :
156 [ # # ]: 0 : for (int i = 0; i < ARR_DIMS(arr)[0]; i++)
157 : : {
158 [ # # # # : 0 : switch (kinds[i])
# ]
159 : : {
160 : : case STATS_EXT_NDISTINCT:
161 : 0 : enabled->ndistinct = true;
162 : 0 : break;
163 : : case STATS_EXT_DEPENDENCIES:
164 : 0 : enabled->dependencies = true;
165 : 0 : break;
166 : : case STATS_EXT_MCV:
167 : 0 : enabled->mcv = true;
168 : 0 : break;
169 : : case STATS_EXT_EXPRESSIONS:
170 : 0 : enabled->expressions = true;
171 : 0 : break;
172 : : default:
173 [ # # # # ]: 0 : elog(ERROR, "incorrect stxkind %c found", kinds[i]);
174 : 0 : break;
175 : : }
176 : 0 : }
177 : 0 : }
178 : :
179 : : /*
180 : : * Perform the actual storage of a pg_statistic_ext_data tuple.
181 : : */
182 : : static void
183 : 0 : upsert_pg_statistic_ext_data(const Datum *values, const bool *nulls,
184 : : const bool *replaces)
185 : : {
186 : 0 : Relation pg_stextdata;
187 : 0 : HeapTuple stxdtup;
188 : 0 : HeapTuple newtup;
189 : :
190 : 0 : pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
191 : :
192 : 0 : stxdtup = SearchSysCache2(STATEXTDATASTXOID,
193 : 0 : values[Anum_pg_statistic_ext_data_stxoid - 1],
194 : 0 : values[Anum_pg_statistic_ext_data_stxdinherit - 1]);
195 : :
196 [ # # ]: 0 : if (HeapTupleIsValid(stxdtup))
197 : : {
198 : 0 : newtup = heap_modify_tuple(stxdtup,
199 : 0 : RelationGetDescr(pg_stextdata),
200 : 0 : values,
201 : 0 : nulls,
202 : 0 : replaces);
203 : 0 : CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup);
204 : 0 : ReleaseSysCache(stxdtup);
205 : 0 : }
206 : : else
207 : : {
208 : 0 : newtup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
209 : 0 : CatalogTupleInsert(pg_stextdata, newtup);
210 : : }
211 : :
212 : 0 : heap_freetuple(newtup);
213 : :
214 : 0 : CommandCounterIncrement();
215 : :
216 : 0 : table_close(pg_stextdata, RowExclusiveLock);
217 : 0 : }
218 : :
219 : : /*
220 : : * Insert or update an extended statistics object.
221 : : *
222 : : * Major errors, such as the table not existing or permission errors, are
223 : : * reported as ERRORs. There are a couple of paths that generate a WARNING,
224 : : * like when the statistics object or its schema do not exist, a conversion
225 : : * failure on one statistic kind, or when other statistic kinds may still
226 : : * be updated.
227 : : */
228 : : static bool
229 : 0 : extended_statistics_update(FunctionCallInfo fcinfo)
230 : : {
231 : 0 : char *relnspname;
232 : 0 : char *relname;
233 : 0 : Oid nspoid;
234 : 0 : char *nspname;
235 : 0 : char *stxname;
236 : 0 : bool inherited;
237 : 0 : Relation pg_stext = NULL;
238 : 0 : HeapTuple tup = NULL;
239 : :
240 : 0 : StakindFlags enabled = {false, false, false, false};
241 : 0 : StakindFlags has = {false, false, false, false};
242 : :
243 : 0 : Form_pg_statistic_ext stxform;
244 : :
245 : 0 : Datum values[Natts_pg_statistic_ext_data] = {0};
246 : 0 : bool nulls[Natts_pg_statistic_ext_data] = {0};
247 : 0 : bool replaces[Natts_pg_statistic_ext_data] = {0};
248 : 0 : bool success = true;
249 : 0 : int numexprs = 0;
250 : :
251 : : /* arrays of type info, if we need them */
252 : 0 : Oid relid;
253 : 0 : Oid locked_table = InvalidOid;
254 : :
255 : : /*
256 : : * Fill out the StakindFlags "has" structure based on which parameters
257 : : * were provided to the function.
258 : : */
259 : 0 : has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
260 : :
261 [ # # ]: 0 : if (RecoveryInProgress())
262 : : {
263 [ # # # # ]: 0 : ereport(WARNING,
264 : : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
265 : : errmsg("recovery is in progress"),
266 : : errhint("Statistics cannot be modified during recovery."));
267 : 0 : return false;
268 : : }
269 : :
270 : : /* relation arguments */
271 : 0 : stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
272 : 0 : relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
273 : 0 : stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
274 : 0 : relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
275 : :
276 : : /* extended statistics arguments */
277 : 0 : stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
278 : 0 : nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
279 : 0 : stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
280 : 0 : stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
281 : 0 : stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
282 : 0 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
283 : :
284 : : /*
285 : : * First open the relation where we expect to find the statistics. This
286 : : * is similar to relation and attribute statistics, so as ACL checks are
287 : : * done before any locks are taken, even before any attempts related to
288 : : * the extended stats object.
289 : : */
290 : 0 : relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
291 : : ShareUpdateExclusiveLock, 0,
292 : : RangeVarCallbackForStats, &locked_table);
293 : :
294 : 0 : nspoid = get_namespace_oid(nspname, true);
295 [ # # ]: 0 : if (nspoid == InvalidOid)
296 : : {
297 [ # # # # ]: 0 : ereport(WARNING,
298 : : errcode(ERRCODE_UNDEFINED_OBJECT),
299 : : errmsg("could not find schema \"%s\"", nspname));
300 : 0 : success = false;
301 : 0 : goto cleanup;
302 : : }
303 : :
304 : 0 : pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
305 : 0 : tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
306 : :
307 [ # # ]: 0 : if (!HeapTupleIsValid(tup))
308 : : {
309 [ # # # # ]: 0 : ereport(WARNING,
310 : : errcode(ERRCODE_UNDEFINED_OBJECT),
311 : : errmsg("could not find extended statistics object \"%s\".\"%s\"",
312 : : quote_identifier(nspname),
313 : : quote_identifier(stxname)));
314 : 0 : success = false;
315 : 0 : goto cleanup;
316 : : }
317 : :
318 : 0 : stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
319 : :
320 : : /*
321 : : * The relation tracked by the stats object has to match with the relation
322 : : * we have already locked.
323 : : */
324 [ # # ]: 0 : if (stxform->stxrelid != relid)
325 : : {
326 [ # # # # ]: 0 : ereport(WARNING,
327 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
328 : : errmsg("could not restore extended statistics object \"%s\".\"%s\": incorrect relation \"%s\".\"%s\" specified",
329 : : quote_identifier(nspname),
330 : : quote_identifier(stxname),
331 : : quote_identifier(relnspname),
332 : : quote_identifier(relname)));
333 : :
334 : 0 : success = false;
335 : 0 : goto cleanup;
336 : : }
337 : :
338 : : /* Find out what extended statistics kinds we should expect. */
339 : 0 : expand_stxkind(tup, &enabled);
340 : :
341 : : /*
342 : : * If the object cannot support ndistinct, we should not have data for it.
343 : : */
344 [ # # # # ]: 0 : if (has.ndistinct && !enabled.ndistinct)
345 : : {
346 [ # # # # ]: 0 : ereport(WARNING,
347 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
348 : : errmsg("cannot not specify parameter \"%s\"",
349 : : extarginfo[NDISTINCT_ARG].argname),
350 : : errhint("Extended statistics object \"%s\".\"%s\" does not support statistics of this type.",
351 : : quote_identifier(nspname),
352 : : quote_identifier(stxname)));
353 : :
354 : 0 : has.ndistinct = false;
355 : 0 : success = false;
356 : 0 : }
357 : :
358 : : /*
359 : : * Populate the pg_statistic_ext_data result tuple.
360 : : */
361 : :
362 : : /* Primary Key: cannot be NULL or replaced. */
363 : 0 : values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid);
364 : 0 : nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
365 : 0 : values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited);
366 : 0 : nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
367 : :
368 : : /* All unspecified parameters will be left unmodified */
369 : 0 : nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
370 : 0 : nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
371 : 0 : nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
372 : 0 : nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
373 : :
374 : : /*
375 : : * For each stats kind, deserialize the data at hand and perform a round
376 : : * of validation. The resulting tuple is filled with a set of updated
377 : : * values.
378 : : */
379 : :
380 [ # # ]: 0 : if (has.ndistinct)
381 : : {
382 : 0 : Datum ndistinct_datum = PG_GETARG_DATUM(NDISTINCT_ARG);
383 : 0 : bytea *data = DatumGetByteaPP(ndistinct_datum);
384 : 0 : MVNDistinct *ndistinct = statext_ndistinct_deserialize(data);
385 : :
386 [ # # # # ]: 0 : if (statext_ndistinct_validate(ndistinct, &stxform->stxkeys,
387 : 0 : numexprs, WARNING))
388 : : {
389 : 0 : values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = ndistinct_datum;
390 : 0 : nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = false;
391 : 0 : replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
392 : 0 : }
393 : : else
394 : 0 : success = false;
395 : :
396 : 0 : statext_ndistinct_free(ndistinct);
397 : 0 : }
398 : :
399 : 0 : upsert_pg_statistic_ext_data(values, nulls, replaces);
400 : :
401 : : cleanup:
402 [ # # ]: 0 : if (HeapTupleIsValid(tup))
403 : 0 : heap_freetuple(tup);
404 [ # # ]: 0 : if (pg_stext != NULL)
405 : 0 : table_close(pg_stext, RowExclusiveLock);
406 : 0 : return success;
407 : 0 : }
408 : :
409 : : /*
410 : : * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
411 : : * row and "inherited" pair.
412 : : */
413 : : static bool
414 : 0 : delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
415 : : {
416 : 0 : Relation sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
417 : 0 : HeapTuple oldtup;
418 : 0 : bool result = false;
419 : :
420 : : /* Is there already a pg_statistic tuple for this attribute? */
421 : 0 : oldtup = SearchSysCache2(STATEXTDATASTXOID,
422 : 0 : ObjectIdGetDatum(stxoid),
423 : 0 : BoolGetDatum(inherited));
424 : :
425 [ # # ]: 0 : if (HeapTupleIsValid(oldtup))
426 : : {
427 : 0 : CatalogTupleDelete(sed, &oldtup->t_self);
428 : 0 : ReleaseSysCache(oldtup);
429 : 0 : result = true;
430 : 0 : }
431 : :
432 : 0 : table_close(sed, RowExclusiveLock);
433 : :
434 : 0 : CommandCounterIncrement();
435 : :
436 : 0 : return result;
437 : 0 : }
438 : :
439 : : /*
440 : : * Restore (insert or replace) statistics for the given statistics object.
441 : : *
442 : : * This function accepts variadic arguments in key-value pairs, which are
443 : : * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
444 : : * arguments.
445 : : */
446 : : Datum
447 : 0 : pg_restore_extended_stats(PG_FUNCTION_ARGS)
448 : : {
449 : 0 : LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
450 : 0 : bool result = true;
451 : :
452 : 0 : InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
453 : : InvalidOid, NULL, NULL);
454 : :
455 [ # # ]: 0 : if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
456 : 0 : result = false;
457 : :
458 [ # # ]: 0 : if (!extended_statistics_update(positional_fcinfo))
459 : 0 : result = false;
460 : :
461 : 0 : PG_RETURN_BOOL(result);
462 : 0 : }
463 : :
464 : : /*
465 : : * Delete statistics for the given statistics object.
466 : : */
467 : : Datum
468 : 0 : pg_clear_extended_stats(PG_FUNCTION_ARGS)
469 : : {
470 : 0 : char *relnspname;
471 : 0 : char *relname;
472 : 0 : char *nspname;
473 : 0 : Oid nspoid;
474 : 0 : Oid relid;
475 : 0 : char *stxname;
476 : 0 : bool inherited;
477 : 0 : Relation pg_stext;
478 : 0 : HeapTuple tup;
479 : 0 : Form_pg_statistic_ext stxform;
480 : 0 : Oid locked_table = InvalidOid;
481 : :
482 : : /* relation arguments */
483 : 0 : stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
484 : 0 : relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
485 : 0 : stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
486 : 0 : relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
487 : :
488 : : /* extended statistics arguments */
489 : 0 : stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
490 : 0 : nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
491 : 0 : stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
492 : 0 : stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
493 : 0 : stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
494 : 0 : inherited = PG_GETARG_BOOL(INHERITED_ARG);
495 : :
496 [ # # ]: 0 : if (RecoveryInProgress())
497 : : {
498 [ # # # # ]: 0 : ereport(WARNING,
499 : : errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
500 : : errmsg("recovery is in progress"),
501 : : errhint("Statistics cannot be modified during recovery."));
502 : 0 : PG_RETURN_VOID();
503 : : }
504 : :
505 : : /*
506 : : * First open the relation where we expect to find the statistics. This
507 : : * is similar to relation and attribute statistics, so as ACL checks are
508 : : * done before any locks are taken, even before any attempts related to
509 : : * the extended stats object.
510 : : */
511 : 0 : relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
512 : : ShareUpdateExclusiveLock, 0,
513 : : RangeVarCallbackForStats, &locked_table);
514 : :
515 : : /* Now check if the namespace of the stats object exists. */
516 : 0 : nspoid = get_namespace_oid(nspname, true);
517 [ # # ]: 0 : if (nspoid == InvalidOid)
518 : : {
519 [ # # # # ]: 0 : ereport(WARNING,
520 : : errcode(ERRCODE_UNDEFINED_OBJECT),
521 : : errmsg("could not find schema \"%s\"", nspname));
522 : 0 : PG_RETURN_VOID();
523 : : }
524 : :
525 : 0 : pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
526 : 0 : tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
527 : :
528 [ # # ]: 0 : if (!HeapTupleIsValid(tup))
529 : : {
530 : 0 : table_close(pg_stext, RowExclusiveLock);
531 [ # # # # ]: 0 : ereport(WARNING,
532 : : errcode(ERRCODE_UNDEFINED_OBJECT),
533 : : errmsg("could not find extended statistics object \"%s\".\"%s\"",
534 : : nspname, stxname));
535 : 0 : PG_RETURN_VOID();
536 : : }
537 : :
538 : 0 : stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
539 : :
540 : : /*
541 : : * This should be consistent, based on the lock taken on the table when we
542 : : * started.
543 : : */
544 [ # # ]: 0 : if (stxform->stxrelid != relid)
545 : : {
546 : 0 : table_close(pg_stext, RowExclusiveLock);
547 [ # # # # ]: 0 : ereport(WARNING,
548 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
549 : : errmsg("could not clear extended statistics object \"%s\".\"%s\": incorrect relation \"%s\".\"%s\" specified",
550 : : get_namespace_name(nspoid), stxname,
551 : : relnspname, relname));
552 : 0 : PG_RETURN_VOID();
553 : : }
554 : :
555 : 0 : delete_pg_statistic_ext_data(stxform->oid, inherited);
556 : 0 : heap_freetuple(tup);
557 : :
558 : 0 : table_close(pg_stext, RowExclusiveLock);
559 : :
560 : 0 : PG_RETURN_VOID();
561 : 0 : }
|