Browse Source

Rework the pg_statistic_ext catalog

Since extended statistic got introduced in PostgreSQL 10, there was a
single catalog pg_statistic_ext storing both the definitions and built
statistic.  That's however problematic when a user is supposed to have
access only to the definitions, but not to user data.

Consider for example pg_dump on a database with RLS enabled - if the
pg_statistic_ext catalog respects RLS (which it should, if it contains
user data), pg_dump would not see any records and the result would not
define any extended statistics.  That would be a surprising behavior.

Until now this was not a pressing issue, because the existing types of
extended statistic (functional dependencies and ndistinct coefficients)
do not include any user data directly.  This changed with introduction
of MCV lists, which do include most common combinations of values.

The easiest way to fix this is to split the pg_statistic_ext catalog
into two - one for definitions, one for the built statistic values.
The new catalog is called pg_statistic_ext_data, and we're maintaining
a 1:1 relationship with the old catalog - either there are matching
records in both catalogs, or neither of them.

Bumped CATVERSION due to changing system catalog definitions.

Author: Dean Rasheed, with improvements by me
Reviewed-by: Dean Rasheed, John Naylor
Discussion: https://postgr.es/m/CAEZATCUhT9rt7Ui%3DVdx4N%3D%3DVV5XOK5dsXfnGgVOz_JhAicB%3DZA%40mail.gmail.com
tags/REL_12_BETA2
Tomas Vondra 1 month ago
parent
commit
6cbfb784c3

+ 59
- 11
doc/src/sgml/catalogs.sgml View File

@@ -297,7 +297,12 @@
297 297
 
298 298
      <row>
299 299
       <entry><link linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link></entry>
300
-      <entry>extended planner statistics</entry>
300
+      <entry>extended planner statistics (definition)</entry>
301
+     </row>
302
+
303
+     <row>
304
+      <entry><link linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link></entry>
305
+      <entry>extended planner statistics (built statistics)</entry>
301 306
      </row>
302 307
 
303 308
      <row>
@@ -6506,7 +6511,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
6506 6511
 
6507 6512
   <para>
6508 6513
    The catalog <structname>pg_statistic_ext</structname>
6509
-   holds extended planner statistics.
6514
+   holds definitions of extended planner statistics.
6510 6515
    Each row in this catalog corresponds to a <firstterm>statistics object</firstterm>
6511 6516
    created with <xref linkend="sql-createstatistics"/>.
6512 6517
   </para>
@@ -6581,8 +6586,57 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
6581 6586
       </entry>
6582 6587
      </row>
6583 6588
 
6589
+    </tbody>
6590
+   </tgroup>
6591
+  </table>
6592
+
6593
+  <para>
6594
+   The <structfield>stxkind</structfield> field is filled at creation of the
6595
+   statistics object, indicating which statistic type(s) are desired. The
6596
+   statistics (once computed by <command>ANALYZE</command>) are stored in
6597
+   <link linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
6598
+   catalog.
6599
+  </para>
6600
+ </sect1>
6601
+
6602
+ <sect1 id="catalog-pg-statistic-ext-data">
6603
+  <title><structname>pg_statistic_ext_data</structname></title>
6604
+
6605
+  <indexterm zone="catalog-pg-statistic-ext">
6606
+   <primary>pg_statistic_ext_data</primary>
6607
+  </indexterm>
6608
+
6609
+  <para>
6610
+   The catalog <structname>pg_statistic_ext_data</structname>
6611
+   holds data for extended planner statistics defined in <structname>pg_statistic_ext</structname>.
6612
+   Each row in this catalog corresponds to a <firstterm>statistics object</firstterm>
6613
+   created with <xref linkend="sql-createstatistics"/>.
6614
+  </para>
6615
+
6616
+  <table>
6617
+   <title><structname>pg_statistic_ext_data</structname> Columns</title>
6618
+
6619
+   <tgroup cols="4">
6620
+    <thead>
6621
+     <row>
6622
+      <entry>Name</entry>
6623
+      <entry>Type</entry>
6624
+      <entry>References</entry>
6625
+      <entry>Description</entry>
6626
+     </row>
6627
+    </thead>
6628
+
6629
+    <tbody>
6630
+
6631
+     <row>
6632
+      <entry><structfield>stxoid</structfield></entry>
6633
+      <entry><type>oid</type></entry>
6634
+      <entry><literal><link linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link>.oid</literal></entry>
6635
+      <entry>Extended statistic containing the definition for this data.</entry>
6636
+     </row>
6637
+
6584 6638
      <row>
6585
-      <entry><structfield>stxndistinct</structfield></entry>
6639
+      <entry><structfield>stxdndistinct</structfield></entry>
6586 6640
       <entry><type>pg_ndistinct</type></entry>
6587 6641
       <entry></entry>
6588 6642
       <entry>
@@ -6591,7 +6645,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
6591 6645
      </row>
6592 6646
 
6593 6647
      <row>
6594
-      <entry><structfield>stxdependencies</structfield></entry>
6648
+      <entry><structfield>stxddependencies</structfield></entry>
6595 6649
       <entry><type>pg_dependencies</type></entry>
6596 6650
       <entry></entry>
6597 6651
       <entry>
@@ -6601,7 +6655,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
6601 6655
      </row>
6602 6656
 
6603 6657
      <row>
6604
-      <entry><structfield>stxmcv</structfield></entry>
6658
+      <entry><structfield>stxdmcv</structfield></entry>
6605 6659
       <entry><type>pg_mcv_list</type></entry>
6606 6660
       <entry></entry>
6607 6661
       <entry>
@@ -6614,12 +6668,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
6614 6668
    </tgroup>
6615 6669
   </table>
6616 6670
 
6617
-  <para>
6618
-   The <structfield>stxkind</structfield> field is filled at creation of the
6619
-   statistics object, indicating which statistic type(s) are desired.
6620
-   The fields after it are initially NULL and are filled only when the
6621
-   corresponding statistic has been computed by <command>ANALYZE</command>.
6622
-  </para>
6623 6671
  </sect1>
6624 6672
 
6625 6673
  <sect1 id="catalog-pg-subscription">

+ 3
- 3
doc/src/sgml/func.sgml View File

@@ -22427,12 +22427,12 @@ CREATE EVENT TRIGGER test_table_rewrite_oid
22427 22427
     The <function>pg_mcv_list_items</function> function can be used like this:
22428 22428
 
22429 22429
 <programlisting>
22430
-SELECT m.* FROM pg_statistic_ext,
22431
-                pg_mcv_list_items(stxmcv) m WHERE stxname = 'stts';
22430
+SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
22431
+                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts';
22432 22432
 </programlisting>
22433 22433
 
22434 22434
      Values of the <type>pg_mcv_list</type> can be obtained only from the
22435
-     <literal>pg_statistic_ext.stxmcv</literal> column.
22435
+     <literal>pg_statistic_ext_data.stxdmcv</literal> column.
22436 22436
    </para>
22437 22437
   </sect2>
22438 22438
 

+ 12
- 8
doc/src/sgml/perform.sgml View File

@@ -1076,6 +1076,10 @@ WHERE tablename = 'road';
1076 1076
     <primary>pg_statistic_ext</primary>
1077 1077
    </indexterm>
1078 1078
 
1079
+   <indexterm>
1080
+    <primary>pg_statistic_ext_data</primary>
1081
+   </indexterm>
1082
+
1079 1083
    <para>
1080 1084
     It is common to see slow queries running bad execution plans because
1081 1085
     multiple columns used in the query clauses are correlated.
@@ -1104,7 +1108,7 @@ WHERE tablename = 'road';
1104 1108
     interest in the statistics.  Actual data collection is performed
1105 1109
     by <command>ANALYZE</command> (either a manual command, or background
1106 1110
     auto-analyze).  The collected values can be examined in the
1107
-    <link linkend="catalog-pg-statistic-ext"><structname>pg_statistic_ext</structname></link>
1111
+    <link linkend="catalog-pg-statistic-ext-data"><structname>pg_statistic_ext_data</structname></link>
1108 1112
     catalog.
1109 1113
    </para>
1110 1114
 
@@ -1172,10 +1176,10 @@ CREATE STATISTICS stts (dependencies) ON zip, city FROM zipcodes;
1172 1176
 
1173 1177
 ANALYZE zipcodes;
1174 1178
 
1175
-SELECT stxname, stxkeys, stxdependencies
1176
-  FROM pg_statistic_ext
1179
+SELECT stxname, stxkeys, stxddependencies
1180
+  FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid)
1177 1181
   WHERE stxname = 'stts';
1178
- stxname | stxkeys |             stxdependencies               
1182
+ stxname | stxkeys |             stxddependencies             
1179 1183
 ---------+---------+------------------------------------------
1180 1184
  stts    | 1 5     | {"1 => 5": 1.000000, "5 => 1": 0.423130}
1181 1185
 (1 row)
@@ -1262,8 +1266,8 @@ CREATE STATISTICS stts2 (ndistinct) ON zip, state, city FROM zipcodes;
1262 1266
 
1263 1267
 ANALYZE zipcodes;
1264 1268
 
1265
-SELECT stxkeys AS k, stxndistinct AS nd
1266
-  FROM pg_statistic_ext
1269
+SELECT stxkeys AS k, stxdndistinct AS nd
1270
+  FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid)
1267 1271
   WHERE stxname = 'stts2';
1268 1272
 -[ RECORD 1 ]--------------------------------------------------------
1269 1273
 k  | 1 2 5
@@ -1317,8 +1321,8 @@ CREATE STATISTICS stts3 (mcv) ON state, city FROM zipcodes;
1317 1321
 
1318 1322
 ANALYZE zipcodes;
1319 1323
 
1320
-SELECT m.* FROM pg_statistic_ext,
1321
-                pg_mcv_list_items(stxmcv) m WHERE stxname = 'stts3';
1324
+SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
1325
+                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts3';
1322 1326
 
1323 1327
  index |         values         | nulls | frequency | base_frequency 
1324 1328
 -------+------------------------+-------+-----------+----------------

+ 2
- 2
doc/src/sgml/planstats.sgml View File

@@ -635,8 +635,8 @@ EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
635 635
     <function>pg_mcv_list_items</function> set-returning function.
636 636
 
637 637
 <programlisting>
638
-SELECT m.* FROM pg_statistic_ext,
639
-                pg_mcv_list_items(stxmcv) m WHERE stxname = 'stts2';
638
+SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
639
+                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts2';
640 640
  index |  values  | nulls | frequency | base_frequency 
641 641
 -------+----------+-------+-----------+----------------
642 642
      0 | {0, 0}   | {f,f} |      0.01 |         0.0001

+ 1
- 1
src/backend/catalog/Makefile View File

@@ -34,7 +34,7 @@ CATALOG_HEADERS := \
34 34
 	pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
35 35
 	pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
36 36
 	pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
37
-	pg_statistic_ext.h \
37
+	pg_statistic_ext.h pg_statistic_ext_data.h \
38 38
 	pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
39 39
 	pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
40 40
 	pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \

+ 56
- 17
src/backend/commands/statscmds.c View File

@@ -23,6 +23,7 @@
23 23
 #include "catalog/namespace.h"
24 24
 #include "catalog/pg_namespace.h"
25 25
 #include "catalog/pg_statistic_ext.h"
26
+#include "catalog/pg_statistic_ext_data.h"
26 27
 #include "commands/comment.h"
27 28
 #include "commands/defrem.h"
28 29
 #include "miscadmin.h"
@@ -67,8 +68,11 @@ CreateStatistics(CreateStatsStmt *stmt)
67 68
 	HeapTuple	htup;
68 69
 	Datum		values[Natts_pg_statistic_ext];
69 70
 	bool		nulls[Natts_pg_statistic_ext];
71
+	Datum		datavalues[Natts_pg_statistic_ext_data];
72
+	bool		datanulls[Natts_pg_statistic_ext_data];
70 73
 	int2vector *stxkeys;
71 74
 	Relation	statrel;
75
+	Relation	datarel;
72 76
 	Relation	rel = NULL;
73 77
 	Oid			relid;
74 78
 	ObjectAddress parentobject,
@@ -336,11 +340,6 @@ CreateStatistics(CreateStatsStmt *stmt)
336 340
 	values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
337 341
 	values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
338 342
 
339
-	/* no statistics built yet */
340
-	nulls[Anum_pg_statistic_ext_stxndistinct - 1] = true;
341
-	nulls[Anum_pg_statistic_ext_stxdependencies - 1] = true;
342
-	nulls[Anum_pg_statistic_ext_stxmcv - 1] = true;
343
-
344 343
 	/* insert it into pg_statistic_ext */
345 344
 	htup = heap_form_tuple(statrel->rd_att, values, nulls);
346 345
 	CatalogTupleInsert(statrel, htup);
@@ -348,6 +347,29 @@ CreateStatistics(CreateStatsStmt *stmt)
348 347
 
349 348
 	relation_close(statrel, RowExclusiveLock);
350 349
 
350
+	/*
351
+	 * Also build the pg_statistic_ext_data tuple, to hold the actual
352
+	 * statistics data.
353
+	 */
354
+	datarel = table_open(StatisticExtDataRelationId, RowExclusiveLock);
355
+
356
+	memset(datavalues, 0, sizeof(datavalues));
357
+	memset(datanulls, false, sizeof(datanulls));
358
+
359
+	datavalues[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statoid);
360
+
361
+	/* no statistics built yet */
362
+	datanulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
363
+	datanulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
364
+	datanulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
365
+
366
+	/* insert it into pg_statistic_ext_data */
367
+	htup = heap_form_tuple(datarel->rd_att, datavalues, datanulls);
368
+	CatalogTupleInsert(datarel, htup);
369
+	heap_freetuple(htup);
370
+
371
+	relation_close(datarel, RowExclusiveLock);
372
+
351 373
 	/*
352 374
 	 * Invalidate relcache so that others see the new statistics object.
353 375
 	 */
@@ -403,6 +425,23 @@ RemoveStatisticsById(Oid statsOid)
403 425
 	Form_pg_statistic_ext statext;
404 426
 	Oid			relid;
405 427
 
428
+	/*
429
+	 * First delete the pg_statistic_ext_data tuple holding the actual
430
+	 * statistical data.
431
+	 */
432
+	relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
433
+
434
+	tup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid));
435
+
436
+	if (!HeapTupleIsValid(tup)) /* should not happen */
437
+		elog(ERROR, "cache lookup failed for statistics data %u", statsOid);
438
+
439
+	CatalogTupleDelete(relation, &tup->t_self);
440
+
441
+	ReleaseSysCache(tup);
442
+
443
+	table_close(relation, RowExclusiveLock);
444
+
406 445
 	/*
407 446
 	 * Delete the pg_statistic_ext tuple.  Also send out a cache inval on the
408 447
 	 * associated table, so that dependent plans will be rebuilt.
@@ -431,8 +470,8 @@ RemoveStatisticsById(Oid statsOid)
431 470
  *
432 471
  * This could throw an error if the type change can't be supported.
433 472
  * If it can be supported, but the stats must be recomputed, a likely choice
434
- * would be to set the relevant column(s) of the pg_statistic_ext tuple to
435
- * null until the next ANALYZE.  (Note that the type change hasn't actually
473
+ * would be to set the relevant column(s) of the pg_statistic_ext_data tuple
474
+ * to null until the next ANALYZE.  (Note that the type change hasn't actually
436 475
  * happened yet, so one option that's *not* on the table is to recompute
437 476
  * immediately.)
438 477
  *
@@ -456,11 +495,11 @@ UpdateStatisticsForTypeChange(Oid statsOid, Oid relationOid, int attnum,
456 495
 
457 496
 	Relation	rel;
458 497
 
459
-	Datum		values[Natts_pg_statistic_ext];
460
-	bool		nulls[Natts_pg_statistic_ext];
461
-	bool		replaces[Natts_pg_statistic_ext];
498
+	Datum		values[Natts_pg_statistic_ext_data];
499
+	bool		nulls[Natts_pg_statistic_ext_data];
500
+	bool		replaces[Natts_pg_statistic_ext_data];
462 501
 
463
-	oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
502
+	oldtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid));
464 503
 	if (!HeapTupleIsValid(oldtup))
465 504
 		elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
466 505
 
@@ -479,14 +518,14 @@ UpdateStatisticsForTypeChange(Oid statsOid, Oid relationOid, int attnum,
479 518
 	 * OK, we need to reset some statistics. So let's build the new tuple,
480 519
 	 * replacing the affected statistics types with NULL.
481 520
 	 */
482
-	memset(nulls, 0, Natts_pg_statistic_ext * sizeof(bool));
483
-	memset(replaces, 0, Natts_pg_statistic_ext * sizeof(bool));
484
-	memset(values, 0, Natts_pg_statistic_ext * sizeof(Datum));
521
+	memset(nulls, 0, Natts_pg_statistic_ext_data * sizeof(bool));
522
+	memset(replaces, 0, Natts_pg_statistic_ext_data * sizeof(bool));
523
+	memset(values, 0, Natts_pg_statistic_ext_data * sizeof(Datum));
485 524
 
486
-	replaces[Anum_pg_statistic_ext_stxmcv - 1] = true;
487
-	nulls[Anum_pg_statistic_ext_stxmcv - 1] = true;
525
+	replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
526
+	nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
488 527
 
489
-	rel = heap_open(StatisticExtRelationId, RowExclusiveLock);
528
+	rel = heap_open(StatisticExtDataRelationId, RowExclusiveLock);
490 529
 
491 530
 	/* replace the old tuple */
492 531
 	stup = heap_modify_tuple(oldtup,

+ 9
- 3
src/backend/optimizer/util/plancat.c View File

@@ -1308,6 +1308,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1308 1308
 		Oid			statOid = lfirst_oid(l);
1309 1309
 		Form_pg_statistic_ext staForm;
1310 1310
 		HeapTuple	htup;
1311
+		HeapTuple	dtup;
1311 1312
 		Bitmapset  *keys = NULL;
1312 1313
 		int			i;
1313 1314
 
@@ -1316,6 +1317,10 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1316 1317
 			elog(ERROR, "cache lookup failed for statistics object %u", statOid);
1317 1318
 		staForm = (Form_pg_statistic_ext) GETSTRUCT(htup);
1318 1319
 
1320
+		dtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statOid));
1321
+		if (!HeapTupleIsValid(dtup))
1322
+			elog(ERROR, "cache lookup failed for statistics object %u", statOid);
1323
+
1319 1324
 		/*
1320 1325
 		 * First, build the array of columns covered.  This is ultimately
1321 1326
 		 * wasted if no stats within the object have actually been built, but
@@ -1325,7 +1330,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1325 1330
 			keys = bms_add_member(keys, staForm->stxkeys.values[i]);
1326 1331
 
1327 1332
 		/* add one StatisticExtInfo for each kind built */
1328
-		if (statext_is_kind_built(htup, STATS_EXT_NDISTINCT))
1333
+		if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT))
1329 1334
 		{
1330 1335
 			StatisticExtInfo *info = makeNode(StatisticExtInfo);
1331 1336
 
@@ -1337,7 +1342,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1337 1342
 			stainfos = lcons(info, stainfos);
1338 1343
 		}
1339 1344
 
1340
-		if (statext_is_kind_built(htup, STATS_EXT_DEPENDENCIES))
1345
+		if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES))
1341 1346
 		{
1342 1347
 			StatisticExtInfo *info = makeNode(StatisticExtInfo);
1343 1348
 
@@ -1349,7 +1354,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1349 1354
 			stainfos = lcons(info, stainfos);
1350 1355
 		}
1351 1356
 
1352
-		if (statext_is_kind_built(htup, STATS_EXT_MCV))
1357
+		if (statext_is_kind_built(dtup, STATS_EXT_MCV))
1353 1358
 		{
1354 1359
 			StatisticExtInfo *info = makeNode(StatisticExtInfo);
1355 1360
 
@@ -1362,6 +1367,7 @@ get_relation_statistics(RelOptInfo *rel, Relation relation)
1362 1367
 		}
1363 1368
 
1364 1369
 		ReleaseSysCache(htup);
1370
+		ReleaseSysCache(dtup);
1365 1371
 		bms_free(keys);
1366 1372
 	}
1367 1373
 

+ 6
- 3
src/backend/statistics/README.mcv View File

@@ -86,11 +86,14 @@ So instead the MCV lists are stored in a custom data type (pg_mcv_list),
86 86
 which however makes it more difficult to inspect the contents. To make that
87 87
 easier, there's a SRF returning detailed information about the MCV lists.
88 88
 
89
-    SELECT m.* FROM pg_statistic_ext,
90
-                    pg_mcv_list_items(stxmcv) m WHERE stxname = 'stts2';
89
+    SELECT m.* FROM pg_statistic_ext s,
90
+                    pg_statistic_ext_data d,
91
+                    pg_mcv_list_items(stxdmcv) m
92
+              WHERE s.stxname = 'stts2'
93
+                AND d.stxoid = s.oid;
91 94
 
92 95
 It accepts one parameter - a pg_mcv_list value (which can only be obtained
93
-from pg_statistic_ext catalog, to defend against malicious input), and
96
+from pg_statistic_ext_data catalog, to defend against malicious input), and
94 97
 returns these columns:
95 98
 
96 99
     - item index (0, ..., (nitems-1))

+ 4
- 3
src/backend/statistics/dependencies.c View File

@@ -17,6 +17,7 @@
17 17
 #include "access/sysattr.h"
18 18
 #include "catalog/pg_operator.h"
19 19
 #include "catalog/pg_statistic_ext.h"
20
+#include "catalog/pg_statistic_ext_data.h"
20 21
 #include "lib/stringinfo.h"
21 22
 #include "nodes/nodeFuncs.h"
22 23
 #include "optimizer/clauses.h"
@@ -637,12 +638,12 @@ statext_dependencies_load(Oid mvoid)
637 638
 	Datum		deps;
638 639
 	HeapTuple	htup;
639 640
 
640
-	htup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(mvoid));
641
+	htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid));
641 642
 	if (!HeapTupleIsValid(htup))
642 643
 		elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
643 644
 
644
-	deps = SysCacheGetAttr(STATEXTOID, htup,
645
-						   Anum_pg_statistic_ext_stxdependencies, &isnull);
645
+	deps = SysCacheGetAttr(STATEXTDATASTXOID, htup,
646
+						   Anum_pg_statistic_ext_data_stxddependencies, &isnull);
646 647
 	if (isnull)
647 648
 		elog(ERROR,
648 649
 			 "requested statistic kind \"%c\" is not yet built for statistics object %u",

+ 34
- 27
src/backend/statistics/extended_stats.c View File

@@ -23,6 +23,7 @@
23 23
 #include "catalog/indexing.h"
24 24
 #include "catalog/pg_collation.h"
25 25
 #include "catalog/pg_statistic_ext.h"
26
+#include "catalog/pg_statistic_ext_data.h"
26 27
 #include "nodes/nodeFuncs.h"
27 28
 #include "optimizer/clauses.h"
28 29
 #include "optimizer/optimizer.h"
@@ -65,9 +66,9 @@ typedef struct StatExtEntry
65 66
 static List *fetch_statentries_for_relation(Relation pg_statext, Oid relid);
66 67
 static VacAttrStats **lookup_var_attr_stats(Relation rel, Bitmapset *attrs,
67 68
 											int nvacatts, VacAttrStats **vacatts);
68
-static void statext_store(Relation pg_stext, Oid relid,
69
+static void statext_store(Oid relid,
69 70
 						  MVNDistinct *ndistinct, MVDependencies *dependencies,
70
-						  MCVList *mcvlist, VacAttrStats **stats);
71
+						  MCVList *mcv, VacAttrStats **stats);
71 72
 
72 73
 
73 74
 /*
@@ -145,7 +146,7 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
145 146
 		}
146 147
 
147 148
 		/* store the statistics in the catalog */
148
-		statext_store(pg_stext, stat->statOid, ndistinct, dependencies, mcv, stats);
149
+		statext_store(stat->statOid, ndistinct, dependencies, mcv, stats);
149 150
 	}
150 151
 
151 152
 	table_close(pg_stext, RowExclusiveLock);
@@ -156,7 +157,7 @@ BuildRelationExtStatistics(Relation onerel, double totalrows,
156 157
 
157 158
 /*
158 159
  * statext_is_kind_built
159
- *		Is this stat kind built in the given pg_statistic_ext tuple?
160
+ *		Is this stat kind built in the given pg_statistic_ext_data tuple?
160 161
  */
161 162
 bool
162 163
 statext_is_kind_built(HeapTuple htup, char type)
@@ -166,15 +167,15 @@ statext_is_kind_built(HeapTuple htup, char type)
166 167
 	switch (type)
167 168
 	{
168 169
 		case STATS_EXT_NDISTINCT:
169
-			attnum = Anum_pg_statistic_ext_stxndistinct;
170
+			attnum = Anum_pg_statistic_ext_data_stxdndistinct;
170 171
 			break;
171 172
 
172 173
 		case STATS_EXT_DEPENDENCIES:
173
-			attnum = Anum_pg_statistic_ext_stxdependencies;
174
+			attnum = Anum_pg_statistic_ext_data_stxddependencies;
174 175
 			break;
175 176
 
176 177
 		case STATS_EXT_MCV:
177
-			attnum = Anum_pg_statistic_ext_stxmcv;
178
+			attnum = Anum_pg_statistic_ext_data_stxdmcv;
178 179
 			break;
179 180
 
180 181
 		default:
@@ -312,70 +313,76 @@ lookup_var_attr_stats(Relation rel, Bitmapset *attrs,
312 313
 
313 314
 /*
314 315
  * statext_store
315
- *	Serializes the statistics and stores them into the pg_statistic_ext tuple.
316
+ *	Serializes the statistics and stores them into the pg_statistic_ext_data
317
+ *	tuple.
316 318
  */
317 319
 static void
318
-statext_store(Relation pg_stext, Oid statOid,
320
+statext_store(Oid statOid,
319 321
 			  MVNDistinct *ndistinct, MVDependencies *dependencies,
320 322
 			  MCVList *mcv, VacAttrStats **stats)
321 323
 {
322 324
 	HeapTuple	stup,
323 325
 				oldtup;
324
-	Datum		values[Natts_pg_statistic_ext];
325
-	bool		nulls[Natts_pg_statistic_ext];
326
-	bool		replaces[Natts_pg_statistic_ext];
326
+	Datum		values[Natts_pg_statistic_ext_data];
327
+	bool		nulls[Natts_pg_statistic_ext_data];
328
+	bool		replaces[Natts_pg_statistic_ext_data];
329
+	Relation	pg_stextdata;
327 330
 
328 331
 	memset(nulls, true, sizeof(nulls));
329 332
 	memset(replaces, false, sizeof(replaces));
330 333
 	memset(values, 0, sizeof(values));
331 334
 
332 335
 	/*
333
-	 * Construct a new pg_statistic_ext tuple, replacing the calculated stats.
336
+	 * Construct a new pg_statistic_ext_data tuple, replacing the calculated
337
+	 * stats.
334 338
 	 */
335 339
 	if (ndistinct != NULL)
336 340
 	{
337 341
 		bytea	   *data = statext_ndistinct_serialize(ndistinct);
338 342
 
339
-		nulls[Anum_pg_statistic_ext_stxndistinct - 1] = (data == NULL);
340
-		values[Anum_pg_statistic_ext_stxndistinct - 1] = PointerGetDatum(data);
343
+		nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = (data == NULL);
344
+		values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = PointerGetDatum(data);
341 345
 	}
342 346
 
343 347
 	if (dependencies != NULL)
344 348
 	{
345 349
 		bytea	   *data = statext_dependencies_serialize(dependencies);
346 350
 
347
-		nulls[Anum_pg_statistic_ext_stxdependencies - 1] = (data == NULL);
348
-		values[Anum_pg_statistic_ext_stxdependencies - 1] = PointerGetDatum(data);
351
+		nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = (data == NULL);
352
+		values[Anum_pg_statistic_ext_data_stxddependencies - 1] = PointerGetDatum(data);
349 353
 	}
350
-
351 354
 	if (mcv != NULL)
352 355
 	{
353 356
 		bytea	   *data = statext_mcv_serialize(mcv, stats);
354 357
 
355
-		nulls[Anum_pg_statistic_ext_stxmcv - 1] = (data == NULL);
356
-		values[Anum_pg_statistic_ext_stxmcv - 1] = PointerGetDatum(data);
358
+		nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = (data == NULL);
359
+		values[Anum_pg_statistic_ext_data_stxdmcv - 1] = PointerGetDatum(data);
357 360
 	}
358 361
 
359 362
 	/* always replace the value (either by bytea or NULL) */
360
-	replaces[Anum_pg_statistic_ext_stxndistinct - 1] = true;
361
-	replaces[Anum_pg_statistic_ext_stxdependencies - 1] = true;
362
-	replaces[Anum_pg_statistic_ext_stxmcv - 1] = true;
363
+	replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
364
+	replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
365
+	replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
363 366
 
364
-	/* there should already be a pg_statistic_ext tuple */
365
-	oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statOid));
367
+	/* there should already be a pg_statistic_ext_data tuple */
368
+	oldtup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(statOid));
366 369
 	if (!HeapTupleIsValid(oldtup))
367 370
 		elog(ERROR, "cache lookup failed for statistics object %u", statOid);
368 371
 
369 372
 	/* replace it */
373
+	pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
374
+
370 375
 	stup = heap_modify_tuple(oldtup,
371
-							 RelationGetDescr(pg_stext),
376
+							 RelationGetDescr(pg_stextdata),
372 377
 							 values,
373 378
 							 nulls,
374 379
 							 replaces);
375 380
 	ReleaseSysCache(oldtup);
376
-	CatalogTupleUpdate(pg_stext, &stup->t_self, stup);
381
+	CatalogTupleUpdate(pg_stextdata, &stup->t_self, stup);
377 382
 
378 383
 	heap_freetuple(stup);
384
+
385
+	table_close(pg_stextdata, RowExclusiveLock);
379 386
 }
380 387
 
381 388
 /* initialize multi-dimensional sort */

+ 4
- 3
src/backend/statistics/mcv.c View File

@@ -19,6 +19,7 @@
19 19
 #include "access/htup_details.h"
20 20
 #include "catalog/pg_collation.h"
21 21
 #include "catalog/pg_statistic_ext.h"
22
+#include "catalog/pg_statistic_ext_data.h"
22 23
 #include "fmgr.h"
23 24
 #include "funcapi.h"
24 25
 #include "nodes/nodeFuncs.h"
@@ -429,13 +430,13 @@ statext_mcv_load(Oid mvoid)
429 430
 	MCVList    *result;
430 431
 	bool		isnull;
431 432
 	Datum		mcvlist;
432
-	HeapTuple	htup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(mvoid));
433
+	HeapTuple	htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid));
433 434
 
434 435
 	if (!HeapTupleIsValid(htup))
435 436
 		elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
436 437
 
437
-	mcvlist = SysCacheGetAttr(STATEXTOID, htup,
438
-							  Anum_pg_statistic_ext_stxmcv, &isnull);
438
+	mcvlist = SysCacheGetAttr(STATEXTDATASTXOID, htup,
439
+							  Anum_pg_statistic_ext_data_stxdmcv, &isnull);
439 440
 
440 441
 	if (isnull)
441 442
 		elog(ERROR,

+ 4
- 3
src/backend/statistics/mvdistinct.c View File

@@ -27,6 +27,7 @@
27 27
 
28 28
 #include "access/htup_details.h"
29 29
 #include "catalog/pg_statistic_ext.h"
30
+#include "catalog/pg_statistic_ext_data.h"
30 31
 #include "utils/fmgrprotos.h"
31 32
 #include "utils/lsyscache.h"
32 33
 #include "lib/stringinfo.h"
@@ -145,12 +146,12 @@ statext_ndistinct_load(Oid mvoid)
145 146
 	Datum		ndist;
146 147
 	HeapTuple	htup;
147 148
 
148
-	htup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(mvoid));
149
+	htup = SearchSysCache1(STATEXTDATASTXOID, ObjectIdGetDatum(mvoid));
149 150
 	if (!HeapTupleIsValid(htup))
150 151
 		elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
151 152
 
152
-	ndist = SysCacheGetAttr(STATEXTOID, htup,
153
-							Anum_pg_statistic_ext_stxndistinct, &isnull);
153
+	ndist = SysCacheGetAttr(STATEXTDATASTXOID, htup,
154
+							Anum_pg_statistic_ext_data_stxdndistinct, &isnull);
154 155
 	if (isnull)
155 156
 		elog(ERROR,
156 157
 			 "requested statistic kind \"%c\" is not yet built for statistics object %u",

+ 12
- 0
src/backend/utils/cache/syscache.c View File

@@ -62,6 +62,7 @@
62 62
 #include "catalog/pg_replication_origin.h"
63 63
 #include "catalog/pg_statistic.h"
64 64
 #include "catalog/pg_statistic_ext.h"
65
+#include "catalog/pg_statistic_ext_data.h"
65 66
 #include "catalog/pg_subscription.h"
66 67
 #include "catalog/pg_subscription_rel.h"
67 68
 #include "catalog/pg_tablespace.h"
@@ -727,6 +728,17 @@ static const struct cachedesc cacheinfo[] = {
727 728
 		},
728 729
 		32
729 730
 	},
731
+	{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
732
+		StatisticExtDataStxoidIndexId,
733
+		1,
734
+		{
735
+			Anum_pg_statistic_ext_data_stxoid,
736
+			0,
737
+			0,
738
+			0
739
+		},
740
+		4
741
+	},
730 742
 	{StatisticExtRelationId,	/* STATEXTNAMENSP */
731 743
 		StatisticExtNameIndexId,
732 744
 		2,

+ 1
- 1
src/include/catalog/catversion.h View File

@@ -53,6 +53,6 @@
53 53
  */
54 54
 
55 55
 /*							yyyymmddN */
56
-#define CATALOG_VERSION_NO	201906041
56
+#define CATALOG_VERSION_NO	201906151
57 57
 
58 58
 #endif

+ 10
- 7
src/include/catalog/indexing.h View File

@@ -186,13 +186,6 @@ DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using
186 186
 DECLARE_UNIQUE_INDEX(pg_largeobject_metadata_oid_index, 2996, on pg_largeobject_metadata using btree(oid oid_ops));
187 187
 #define LargeObjectMetadataOidIndexId	2996
188 188
 
189
-DECLARE_UNIQUE_INDEX(pg_statistic_ext_oid_index, 3380, on pg_statistic_ext using btree(oid oid_ops));
190
-#define StatisticExtOidIndexId	3380
191
-DECLARE_UNIQUE_INDEX(pg_statistic_ext_name_index, 3997, on pg_statistic_ext using btree(stxname name_ops, stxnamespace oid_ops));
192
-#define StatisticExtNameIndexId 3997
193
-DECLARE_INDEX(pg_statistic_ext_relid_index, 3379, on pg_statistic_ext using btree(stxrelid oid_ops));
194
-#define StatisticExtRelidIndexId 3379
195
-
196 189
 DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops));
197 190
 #define NamespaceNameIndexId  2684
198 191
 DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops));
@@ -237,6 +230,16 @@ DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refc
237 230
 DECLARE_UNIQUE_INDEX(pg_statistic_relid_att_inh_index, 2696, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops, stainherit bool_ops));
238 231
 #define StatisticRelidAttnumInhIndexId	2696
239 232
 
233
+DECLARE_UNIQUE_INDEX(pg_statistic_ext_oid_index, 3380, on pg_statistic_ext using btree(oid oid_ops));
234
+#define StatisticExtOidIndexId	3380
235
+DECLARE_UNIQUE_INDEX(pg_statistic_ext_name_index, 3997, on pg_statistic_ext using btree(stxname name_ops, stxnamespace oid_ops));
236
+#define StatisticExtNameIndexId 3997
237
+DECLARE_INDEX(pg_statistic_ext_relid_index, 3379, on pg_statistic_ext using btree(stxrelid oid_ops));
238
+#define StatisticExtRelidIndexId 3379
239
+
240
+DECLARE_UNIQUE_INDEX(pg_statistic_ext_data_stxoid_index, 3433, on pg_statistic_ext_data using btree(stxoid oid_ops));
241
+#define StatisticExtDataStxoidIndexId 3433
242
+
240 243
 DECLARE_UNIQUE_INDEX(pg_tablespace_oid_index, 2697, on pg_tablespace using btree(oid oid_ops));
241 244
 #define TablespaceOidIndexId  2697
242 245
 DECLARE_UNIQUE_INDEX(pg_tablespace_spcname_index, 2698, on pg_tablespace using btree(spcname name_ops));

+ 5
- 4
src/include/catalog/pg_statistic_ext.h View File

@@ -1,8 +1,12 @@
1 1
 /*-------------------------------------------------------------------------
2 2
  *
3 3
  * pg_statistic_ext.h
4
- *	  definition of the "extended statistics" system catalog (pg_statistic_ext)
4
+ *	  definition of the "extended statistics" system catalog
5
+ *	  (pg_statistic_ext)
5 6
  *
7
+ * Note that pg_statistic_ext contains the definitions of extended statistics
8
+ * objects, created by CREATE STATISTICS, but not the actual statistical data,
9
+ * created by running ANALYZE.
6 10
  *
7 11
  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
8 12
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -47,9 +51,6 @@ CATALOG(pg_statistic_ext,3381,StatisticExtRelationId)
47 51
 #ifdef CATALOG_VARLEN
48 52
 	char		stxkind[1] BKI_FORCE_NOT_NULL;	/* statistics kinds requested
49 53
 												 * to build */
50
-	pg_ndistinct stxndistinct;	/* ndistinct coefficients (serialized) */
51
-	pg_dependencies stxdependencies;	/* dependencies (serialized) */
52
-	pg_mcv_list stxmcv;			/* MCV (serialized) */
53 54
 #endif
54 55
 
55 56
 } FormData_pg_statistic_ext;

+ 52
- 0
src/include/catalog/pg_statistic_ext_data.h View File

@@ -0,0 +1,52 @@
1
+/*-------------------------------------------------------------------------
2
+ *
3
+ * pg_statistic_ext_data.h
4
+ *	  definition of the "extended statistics data" system catalog
5
+ *	  (pg_statistic_ext_data)
6
+ *
7
+ * This catalog stores the statistical data for extended statistics objects.
8
+ *
9
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
10
+ * Portions Copyright (c) 1994, Regents of the University of California
11
+ *
12
+ * src/include/catalog/pg_statistic_ext_data.h
13
+ *
14
+ * NOTES
15
+ *	  The Catalog.pm module reads this file and derives schema
16
+ *	  information.
17
+ *
18
+ *-------------------------------------------------------------------------
19
+ */
20
+#ifndef PG_STATISTIC_EXT_DATA_H
21
+#define PG_STATISTIC_EXT_DATA_H
22
+
23
+#include "catalog/genbki.h"
24
+#include "catalog/pg_statistic_ext_data_d.h"
25
+
26
+/* ----------------
27
+ *		pg_statistic_ext_data definition.  cpp turns this into
28
+ *		typedef struct FormData_pg_statistic_ext_data
29
+ * ----------------
30
+ */
31
+CATALOG(pg_statistic_ext_data,3429,StatisticExtDataRelationId)
32
+{
33
+	Oid			stxoid;			/* statistics object this data is for */
34
+
35
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
36
+
37
+	pg_ndistinct stxdndistinct;	/* ndistinct coefficients (serialized) */
38
+	pg_dependencies stxddependencies;	/* dependencies (serialized) */
39
+	pg_mcv_list stxdmcv;			/* MCV (serialized) */
40
+
41
+#endif
42
+
43
+} FormData_pg_statistic_ext_data;
44
+
45
+/* ----------------
46
+ *		Form_pg_statistic_ext_data corresponds to a pointer to a tuple with
47
+ *		the format of pg_statistic_ext_data relation.
48
+ * ----------------
49
+ */
50
+typedef FormData_pg_statistic_ext_data *Form_pg_statistic_ext_data;
51
+
52
+#endif							/* PG_STATISTIC_EXT_DATA_H */

+ 1
- 0
src/include/catalog/toasting.h View File

@@ -70,6 +70,7 @@ DECLARE_TOAST(pg_rewrite, 2838, 2839);
70 70
 DECLARE_TOAST(pg_seclabel, 3598, 3599);
71 71
 DECLARE_TOAST(pg_statistic, 2840, 2841);
72 72
 DECLARE_TOAST(pg_statistic_ext, 3439, 3440);
73
+DECLARE_TOAST(pg_statistic_ext_data, 3430, 3431);
73 74
 DECLARE_TOAST(pg_trigger, 2336, 2337);
74 75
 DECLARE_TOAST(pg_ts_dict, 4169, 4170);
75 76
 DECLARE_TOAST(pg_type, 4171, 4172);

+ 1
- 0
src/include/utils/syscache.h View File

@@ -86,6 +86,7 @@ enum SysCacheIdentifier
86 86
 	REPLORIGNAME,
87 87
 	RULERELNAME,
88 88
 	SEQRELID,
89
+	STATEXTDATASTXOID,
89 90
 	STATEXTNAMENSP,
90 91
 	STATEXTOID,
91 92
 	STATRELATTINH,

+ 8
- 0
src/test/regress/expected/oidjoins.out View File

@@ -985,6 +985,14 @@ WHERE	stxowner != 0 AND
985 985
 ------+----------
986 986
 (0 rows)
987 987
 
988
+SELECT	ctid, stxoid
989
+FROM	pg_catalog.pg_statistic_ext_data fk
990
+WHERE	stxoid != 0 AND
991
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_statistic_ext pk WHERE pk.oid = fk.stxoid);
992
+ ctid | stxoid 
993
+------+--------
994
+(0 rows)
995
+
988 996
 SELECT	ctid, spcowner
989 997
 FROM	pg_catalog.pg_tablespace fk
990 998
 WHERE	spcowner != 0 AND

+ 1
- 0
src/test/regress/expected/sanity_check.out View File

@@ -149,6 +149,7 @@ pg_shdescription|t
149 149
 pg_shseclabel|t
150 150
 pg_statistic|t
151 151
 pg_statistic_ext|t
152
+pg_statistic_ext_data|t
152 153
 pg_subscription|t
153 154
 pg_subscription_rel|t
154 155
 pg_tablespace|t

+ 25
- 13
src/test/regress/expected/stats_ext.out View File

@@ -199,9 +199,11 @@ SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY b, c
199 199
 -- correct command
200 200
 CREATE STATISTICS s10 ON a, b, c FROM ndistinct;
201 201
 ANALYZE ndistinct;
202
-SELECT stxkind, stxndistinct
203
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
204
- stxkind |                    stxndistinct                     
202
+SELECT s.stxkind, d.stxdndistinct
203
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
204
+ WHERE s.stxrelid = 'ndistinct'::regclass
205
+   AND d.stxoid = s.oid;
206
+ stxkind |                    stxdndistinct                    
205 207
 ---------+-----------------------------------------------------
206 208
  {d,f,m} | {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
207 209
 (1 row)
@@ -246,9 +248,11 @@ INSERT INTO ndistinct (a, b, c, filler1)
246 248
             cash_words(mod(i,33)::int::money)
247 249
        FROM generate_series(1,5000) s(i);
248 250
 ANALYZE ndistinct;
249
-SELECT stxkind, stxndistinct
250
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
251
- stxkind |                        stxndistinct                        
251
+SELECT s.stxkind, d.stxdndistinct
252
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
253
+ WHERE s.stxrelid = 'ndistinct'::regclass
254
+   AND d.stxoid = s.oid;
255
+ stxkind |                       stxdndistinct                        
252 256
 ---------+------------------------------------------------------------
253 257
  {d,f,m} | {"3, 4": 2550, "3, 6": 800, "4, 6": 1632, "3, 4, 6": 5000}
254 258
 (1 row)
@@ -285,10 +289,12 @@ SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d
285 289
 (1 row)
286 290
 
287 291
 DROP STATISTICS s10;
288
-SELECT stxkind, stxndistinct
289
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
290
- stxkind | stxndistinct 
291
----------+--------------
292
+SELECT s.stxkind, d.stxdndistinct
293
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
294
+ WHERE s.stxrelid = 'ndistinct'::regclass
295
+   AND d.stxoid = s.oid;
296
+ stxkind | stxdndistinct 
297
+---------+---------------
292 298
 (0 rows)
293 299
 
294 300
 -- dropping the statistics results in under-estimates
@@ -537,7 +543,10 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <
537 543
 
538 544
 -- check change of unrelated column type does not reset the MCV statistics
539 545
 ALTER TABLE mcv_lists ALTER COLUMN d TYPE VARCHAR(64);
540
-SELECT stxmcv IS NOT NULL FROM pg_statistic_ext WHERE stxname = 'mcv_lists_stats';
546
+SELECT d.stxdmcv IS NOT NULL
547
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
548
+ WHERE s.stxname = 'mcv_lists_stats'
549
+   AND d.stxoid = s.oid;
541 550
  ?column? 
542 551
 ----------
543 552
  t
@@ -600,8 +609,11 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a IS NULL AND
600 609
 TRUNCATE mcv_lists;
601 610
 INSERT INTO mcv_lists (a, b, c) SELECT 1, 2, 3 FROM generate_series(1,1000) s(i);
602 611
 ANALYZE mcv_lists;
603
-SELECT m.* FROM pg_statistic_ext,
604
-              pg_mcv_list_items(stxmcv) m WHERE stxname = 'mcv_lists_stats';
612
+SELECT m.*
613
+  FROM pg_statistic_ext s, pg_statistic_ext_data d,
614
+       pg_mcv_list_items(d.stxdmcv) m
615
+ WHERE s.stxname = 'mcv_lists_stats'
616
+   AND d.stxoid = s.oid;
605 617
  index |  values   |  nulls  | frequency | base_frequency 
606 618
 -------+-----------+---------+-----------+----------------
607 619
      0 | {1, 2, 3} | {f,f,f} |         1 |              1

+ 4
- 0
src/test/regress/sql/oidjoins.sql View File

@@ -493,6 +493,10 @@ SELECT	ctid, stxowner
493 493
 FROM	pg_catalog.pg_statistic_ext fk
494 494
 WHERE	stxowner != 0 AND
495 495
 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.stxowner);
496
+SELECT	ctid, stxoid
497
+FROM	pg_catalog.pg_statistic_ext_data fk
498
+WHERE	stxoid != 0 AND
499
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_statistic_ext pk WHERE pk.oid = fk.stxoid);
496 500
 SELECT	ctid, spcowner
497 501
 FROM	pg_catalog.pg_tablespace fk
498 502
 WHERE	spcowner != 0 AND

+ 21
- 9
src/test/regress/sql/stats_ext.sql View File

@@ -144,8 +144,10 @@ CREATE STATISTICS s10 ON a, b, c FROM ndistinct;
144 144
 
145 145
 ANALYZE ndistinct;
146 146
 
147
-SELECT stxkind, stxndistinct
148
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
147
+SELECT s.stxkind, d.stxdndistinct
148
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
149
+ WHERE s.stxrelid = 'ndistinct'::regclass
150
+   AND d.stxoid = s.oid;
149 151
 
150 152
 -- Hash Aggregate, thanks to estimates improved by the statistic
151 153
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -170,8 +172,10 @@ INSERT INTO ndistinct (a, b, c, filler1)
170 172
 
171 173
 ANALYZE ndistinct;
172 174
 
173
-SELECT stxkind, stxndistinct
174
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
175
+SELECT s.stxkind, d.stxdndistinct
176
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
177
+ WHERE s.stxrelid = 'ndistinct'::regclass
178
+   AND d.stxoid = s.oid;
175 179
 
176 180
 -- correct esimates
177 181
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -186,8 +190,10 @@ SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, d
186 190
 
187 191
 DROP STATISTICS s10;
188 192
 
189
-SELECT stxkind, stxndistinct
190
-  FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
193
+SELECT s.stxkind, d.stxdndistinct
194
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
195
+ WHERE s.stxrelid = 'ndistinct'::regclass
196
+   AND d.stxoid = s.oid;
191 197
 
192 198
 -- dropping the statistics results in under-estimates
193 199
 SELECT * FROM check_estimated_rows('SELECT COUNT(*) FROM ndistinct GROUP BY a, b');
@@ -335,7 +341,10 @@ SELECT * FROM check_estimated_rows('SELECT * FROM mcv_lists WHERE a <= 4 AND b <
335 341
 -- check change of unrelated column type does not reset the MCV statistics
336 342
 ALTER TABLE mcv_lists ALTER COLUMN d TYPE VARCHAR(64);
337 343
 
338
-SELECT stxmcv IS NOT NULL FROM pg_statistic_ext WHERE stxname = 'mcv_lists_stats';
344
+SELECT d.stxdmcv IS NOT NULL
345
+  FROM pg_statistic_ext s, pg_statistic_ext_data d
346
+ WHERE s.stxname = 'mcv_lists_stats'
347
+   AND d.stxoid = s.oid;
339 348
 
340 349
 -- check change of column type resets the MCV statistics
341 350
 ALTER TABLE mcv_lists ALTER COLUMN c TYPE numeric;
@@ -378,8 +387,11 @@ TRUNCATE mcv_lists;
378 387
 INSERT INTO mcv_lists (a, b, c) SELECT 1, 2, 3 FROM generate_series(1,1000) s(i);
379 388
 ANALYZE mcv_lists;
380 389
 
381
-SELECT m.* FROM pg_statistic_ext,
382
-              pg_mcv_list_items(stxmcv) m WHERE stxname = 'mcv_lists_stats';
390
+SELECT m.*
391
+  FROM pg_statistic_ext s, pg_statistic_ext_data d,
392
+       pg_mcv_list_items(d.stxdmcv) m
393
+ WHERE s.stxname = 'mcv_lists_stats'
394
+   AND d.stxoid = s.oid;
383 395
 
384 396
 -- mcv with arrays
385 397
 CREATE TABLE mcv_lists_arrays (

+ 2
- 0
src/tools/pgindent/typedefs.list View File

@@ -729,6 +729,7 @@ FormData_pg_sequence_data
729 729
 FormData_pg_shdepend
730 730
 FormData_pg_statistic
731 731
 FormData_pg_statistic_ext
732
+FormData_pg_statistic_ext_data
732 733
 FormData_pg_subscription
733 734
 FormData_pg_subscription_rel
734 735
 FormData_pg_tablespace
@@ -786,6 +787,7 @@ Form_pg_sequence_data
786 787
 Form_pg_shdepend
787 788
 Form_pg_statistic
788 789
 Form_pg_statistic_ext
790
+Form_pg_statistic_ext_data
789 791
 Form_pg_subscription
790 792
 Form_pg_subscription_rel
791 793
 Form_pg_tablespace

Loading…
Cancel
Save