1515#include <limits.h>
1616#include <math.h>
1717
18+ #include "access/hash.h"
1819#include "access/parallel.h"
20+ #include "access/relscan.h"
21+ #include "access/skey.h"
1922#include "access/table.h"
20- #include "catalog/pg_statistic_ext_data_d.h"
23+ #include "access/tableam.h"
24+ #include "catalog/pg_statistic_ext.h"
2125#include "commands/explain.h"
2226#include "commands/defrem.h"
2327#include "executor/instrument.h"
3034#include "parser/parsetree.h"
3135#include "storage/ipc.h"
3236#include "statistics/statistics.h"
37+ #include "utils/fmgroids.h"
3338#include "utils/guc.h"
3439#include "utils/syscache.h"
3540#include "utils/lsyscache.h"
@@ -428,31 +433,12 @@ vars_list_comparator(const ListCell *a, const ListCell *b)
428433 return strcmp (va , vb );
429434}
430435
431- typedef struct
432- {
433- Oid rel_oid ;
434- int n_attrs ;
435- uint64 attrs_mask ;
436- } StatHashKey ;
437-
438- static HTAB * ext_stat_hash ;
439-
440436static void
441437AddMultiColumnStatisticsForQual (void * qual , ExplainState * es )
442438{
443439 List * vars = NULL ;
444440 ListCell * lc ;
445- StatHashKey key ;
446- key .attrs_mask = 0 ;
447- key .n_attrs = 0 ;
448441
449- if (ext_stat_hash == NULL )
450- {
451- HASHCTL ctl ;
452- MemSet (& ctl , 0 , sizeof (ctl ));
453- ctl .keysize = ctl .entrysize = sizeof (StatHashKey );
454- ext_stat_hash = hash_create ("ext_stat_hash" , 113 , & ctl , HASH_ELEM |HASH_BLOBS );
455- }
456442 foreach (lc , qual )
457443 {
458444 Node * node = (Node * )lfirst (lc );
@@ -490,8 +476,6 @@ AddMultiColumnStatisticsForQual(void* qual, ExplainState *es)
490476 col -> fields = list_make1 (makeString (colname ));
491477 cols = lappend (cols , col );
492478 colmap = bms_add_member (colmap , var -> varoattno );
493- key .attrs_mask |= 1LL << (var -> varoattno & 63 );
494- key .n_attrs += 1 ;
495479 }
496480 }
497481 }
@@ -505,65 +489,73 @@ AddMultiColumnStatisticsForQual(void* qual, ExplainState *es)
505489 if (list_length (cols ) >= 2 )
506490 {
507491 RangeTblEntry * rte = rt_fetch (varno , es -> rtable );
508- bool found ;
509- key .rel_oid = rte -> relid ;
510- hash_search (ext_stat_hash , & key , HASH_ENTER , & found );
511- if (!found )
512- {
513- CreateStatsStmt * stats = makeNode (CreateStatsStmt );
514- char * rel_namespace = get_namespace_name (get_rel_namespace (rte -> relid ));
515- char * rel_name = get_rel_name (rte -> relid );
516- RangeVar * rel = makeRangeVar (rel_namespace , rel_name , 0 );
517- char * stat_name = rel_name ;
518- char * create_stat_stmt = (char * )"" ;
519- char const * sep = "ON" ;
520-
521- list_sort (cols , vars_list_comparator );
522- /* Construct name for statistic by concatenating relation name with all columns */
523- foreach (cell , cols )
492+ CreateStatsStmt * stats = makeNode (CreateStatsStmt );
493+ char * rel_namespace = get_namespace_name (get_rel_namespace (rte -> relid ));
494+ char * rel_name = get_rel_name (rte -> relid );
495+ RangeVar * rel = makeRangeVar (rel_namespace , rel_name , 0 );
496+ char * stat_name = rel_name ;
497+ char * create_stat_stmt = (char * )"" ;
498+ char const * sep = "ON" ;
499+ ScanKeyData entry [2 ];
500+ TableScanDesc scan ;
501+ Relation stat_rel ;
502+ size_t name_len ;
503+ TupleTableSlot * slot ;
504+
505+ list_sort (cols , vars_list_comparator );
506+ /* Construct name for statistic by concatenating relation name with all columns */
507+ foreach (cell , cols )
508+ {
509+ char * col_name = strVal ((Value * ) linitial (((ColumnRef * )lfirst (cell ))-> fields ));
510+ stat_name = psprintf ("%s_%s" , stat_name , col_name );
511+ create_stat_stmt = psprintf ("%s%s %s" , create_stat_stmt , sep , col_name );
512+ sep = "," ;
513+ }
514+
515+ name_len = strlen (stat_name );
516+ if (name_len >= NAMEDATALEN )
517+ stat_name = psprintf ("%.*s_%08x" , NAMEDATALEN - 10 , stat_name , (unsigned )hash_any ((uint8 * )stat_name , name_len ));
518+
519+ ScanKeyInit (& entry [0 ],
520+ Anum_pg_statistic_ext_stxname ,
521+ BTEqualStrategyNumber , F_NAMEEQ ,
522+ CStringGetDatum (stat_name ));
523+ ScanKeyInit (& entry [1 ],
524+ Anum_pg_statistic_ext_stxnamespace ,
525+ BTEqualStrategyNumber , F_OIDEQ ,
526+ ObjectIdGetDatum (get_rel_namespace (rte -> relid )));
527+
528+ /*
529+ * Prevent concurrent access to extended statistic table
530+ */
531+ stat_rel = table_open (StatisticExtRelationId , AccessExclusiveLock );
532+ slot = table_slot_create (stat_rel , NULL );
533+ scan = table_beginscan_catalog (stat_rel , 2 , entry );
534+
535+ /*
536+ * Check if multicolumn statistic object with such name already exists.
537+ * Most likely if was already created by auto_explain, but either ANALYZE was not performed since
538+ * this time, either presence of this multicolumn statistic doesn't help to provide more precise estimation.
539+ * Despite to the fact that we create statistics with "if_not_exist" option, presence of such check
540+ * allows to eliminate notice message that statistics object already exists.
541+ */
542+ if (!table_scan_getnextslot (scan , ForwardScanDirection , slot ))
543+ {
544+ if (auto_explain_suggest_only )
524545 {
525- char * col_name = strVal ((Value * ) linitial (((ColumnRef * )lfirst (cell ))-> fields ));
526- stat_name = psprintf ("%s_%s" , stat_name , col_name );
527- create_stat_stmt = psprintf ("%s%s %s" , create_stat_stmt , sep , col_name );
528- sep = "," ;
546+ elog (NOTICE , "Auto_explain suggestion: CREATE STATISTICS %s %s FROM %s" , stat_name , create_stat_stmt , rel_name );
529547 }
530- /*
531- * Check if multicolumn statistic object with such name already exists.
532- * Most likely if was already created by auto_explain, but either ANALYZE was not performed since
533- * this time, either presence of this multicolumn statistic doesn't help to provide more precise estimation.
534- * Despite to the fact that we create statistics with "if_not_exist" option, presence of such check
535- * allows to eliminate notice message that statistics object already exists.
536- */
537- if (!SearchSysCacheExists2 (STATEXTNAMENSP ,
538- CStringGetDatum (stat_name ),
539- ObjectIdGetDatum (get_rel_namespace (rte -> relid ))))
548+ else
540549 {
541- if (auto_explain_suggest_only )
542- {
543- elog (NOTICE , "Auto_explain suggestion: CREATE STATISTICS %s %s FROM %s" , stat_name , create_stat_stmt , rel_name );
544- }
545- else
546- {
547- Relation stat = table_open (StatisticExtDataRelationId , AccessExclusiveLock );
548- if (stat == NULL )
549- elog (ERROR , "Failed to lock statistic table" );
550-
551- /* Recheck under lock */
552- if (!SearchSysCacheExists2 (STATEXTNAMENSP ,
553- CStringGetDatum (stat_name ),
554- ObjectIdGetDatum (get_rel_namespace (rte -> relid ))))
555- {
556- elog (LOG , "Add statistics %s" , stat_name );
557- stats -> defnames = list_make2 (makeString (rel_namespace ), makeString (stat_name ));
558- stats -> if_not_exists = true;
559- stats -> relations = list_make1 (rel );
560- stats -> exprs = cols ;
561- CreateStatistics (stats );
562- }
563- table_close (stat , AccessExclusiveLock );
564- }
550+ elog (LOG , "Add statistics %s" , stat_name );
551+ stats -> defnames = list_make2 (makeString (rel_namespace ), makeString (stat_name ));
552+ stats -> if_not_exists = true;
553+ stats -> relations = list_make1 (rel );
554+ stats -> exprs = cols ;
555+ CreateStatistics (stats );
565556 }
566557 }
558+ table_close (stat_rel , AccessExclusiveLock );
567559 }
568560 }
569561}
0 commit comments