3131#include "utils/typcache.h"
3232
3333
34- /* used for sorting the attnums in CreateStatistics */
34+ /* qsort comparator for the attnums in CreateStatistics */
3535static int
3636compare_int16 (const void * a , const void * b )
3737{
38- return memcmp (a , b , sizeof (int16 ));
38+ int av = * (const int16 * ) a ;
39+ int bv = * (const int16 * ) b ;
40+
41+ /* this can't overflow if int is wider than int16 */
42+ return (av - bv );
3943}
4044
4145/*
@@ -44,8 +48,6 @@ compare_int16(const void *a, const void *b)
4448ObjectAddress
4549CreateStatistics (CreateStatsStmt * stmt )
4650{
47- int i ;
48- ListCell * l ;
4951 int16 attnums [STATS_MAX_DIMENSIONS ];
5052 int numcols = 0 ;
5153 ObjectAddress address = InvalidObjectAddress ;
@@ -68,6 +70,8 @@ CreateStatistics(CreateStatsStmt *stmt)
6870 bool build_ndistinct ;
6971 bool build_dependencies ;
7072 bool requested_type = false;
73+ int i ;
74+ ListCell * l ;
7175
7276 Assert (IsA (stmt , CreateStatsStmt ));
7377
@@ -76,10 +80,10 @@ CreateStatistics(CreateStatsStmt *stmt)
7680 namestrcpy (& stxname , namestr );
7781
7882 /*
79- * If if_not_exists was given and the statistics already exists, bail out .
83+ * Deal with the possibility that the named statistics already exist .
8084 */
8185 if (SearchSysCacheExists2 (STATEXTNAMENSP ,
82- PointerGetDatum (& stxname ),
86+ NameGetDatum (& stxname ),
8387 ObjectIdGetDatum (namespaceId )))
8488 {
8589 if (stmt -> if_not_exists )
@@ -97,10 +101,11 @@ CreateStatistics(CreateStatsStmt *stmt)
97101 }
98102
99103 /*
100- * CREATE STATISTICS will influence future execution plans but does
101- * not interfere with currently executing plans so it is safe to
104+ * CREATE STATISTICS will influence future execution plans but does not
105+ * interfere with currently executing plans. So it should be enough to
102106 * take only ShareUpdateExclusiveLock on relation, conflicting with
103- * ANALYZE and other DDL that sets statistical information.
107+ * ANALYZE and other DDL that sets statistical information, but not with
108+ * normal queries.
104109 */
105110 rel = relation_openrv (stmt -> relation , ShareUpdateExclusiveLock );
106111 relid = RelationGetRelid (rel );
@@ -137,25 +142,26 @@ CreateStatistics(CreateStatsStmt *stmt)
137142 if (attForm -> attnum < 0 )
138143 ereport (ERROR ,
139144 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
140- errmsg ("statistic creation on system columns is not supported" )));
145+ errmsg ("statistics creation on system columns is not supported" )));
141146
142147 /* Disallow data types without a less-than operator */
143148 type = lookup_type_cache (attForm -> atttypid , TYPECACHE_LT_OPR );
144149 if (type -> lt_opr == InvalidOid )
145150 ereport (ERROR ,
146151 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
147- errmsg ("only scalar types can be used in extended statistics" )));
152+ errmsg ("column \"%s\" cannot be used in statistics because its type has no default btree operator class" ,
153+ attname )));
148154
149155 /* Make sure no more than STATS_MAX_DIMENSIONS columns are used */
150156 if (numcols >= STATS_MAX_DIMENSIONS )
151157 ereport (ERROR ,
152158 (errcode (ERRCODE_TOO_MANY_COLUMNS ),
153- errmsg ("cannot have more than %d keys in statistics" ,
159+ errmsg ("cannot have more than %d columns in statistics" ,
154160 STATS_MAX_DIMENSIONS )));
155161
156162 attnums [numcols ] = ((Form_pg_attribute ) GETSTRUCT (atttuple ))-> attnum ;
157- ReleaseSysCache (atttuple );
158163 numcols ++ ;
164+ ReleaseSysCache (atttuple );
159165 }
160166
161167 /*
@@ -164,26 +170,27 @@ CreateStatistics(CreateStatsStmt *stmt)
164170 */
165171 if (numcols < 2 )
166172 ereport (ERROR ,
167- (errcode (ERRCODE_TOO_MANY_COLUMNS ),
168- errmsg ("statistics require at least 2 columns" )));
173+ (errcode (ERRCODE_INVALID_OBJECT_DEFINITION ),
174+ errmsg ("extended statistics require at least 2 columns" )));
169175
170176 /*
171- * Sort the attnums, which makes detecting duplicities somewhat easier, and
177+ * Sort the attnums, which makes detecting duplicates somewhat easier, and
172178 * it does not hurt (it does not affect the efficiency, unlike for
173179 * indexes, for example).
174180 */
175181 qsort (attnums , numcols , sizeof (int16 ), compare_int16 );
176182
177183 /*
178- * Look for duplicities in the list of columns. The attnums are sorted so
184+ * Check for duplicates in the list of columns. The attnums are sorted so
179185 * just check consecutive elements.
180186 */
181187 for (i = 1 ; i < numcols ; i ++ )
182188 if (attnums [i ] == attnums [i - 1 ])
183189 ereport (ERROR ,
184- (errcode (ERRCODE_UNDEFINED_COLUMN ),
190+ (errcode (ERRCODE_DUPLICATE_COLUMN ),
185191 errmsg ("duplicate column name in statistics definition" )));
186192
193+ /* Form an int2vector representation of the sorted column list */
187194 stxkeys = buildint2vector (attnums , numcols );
188195
189196 /*
@@ -225,7 +232,7 @@ CreateStatistics(CreateStatsStmt *stmt)
225232 types [ntypes ++ ] = CharGetDatum (STATS_EXT_NDISTINCT );
226233 if (build_dependencies )
227234 types [ntypes ++ ] = CharGetDatum (STATS_EXT_DEPENDENCIES );
228- Assert (ntypes > 0 );
235+ Assert (ntypes > 0 && ntypes <= lengthof ( types ) );
229236 stxkind = construct_array (types , ntypes , CHAROID , 1 , true, 'c' );
230237
231238 /*
@@ -240,7 +247,7 @@ CreateStatistics(CreateStatsStmt *stmt)
240247 values [Anum_pg_statistic_ext_stxkeys - 1 ] = PointerGetDatum (stxkeys );
241248 values [Anum_pg_statistic_ext_stxkind - 1 ] = PointerGetDatum (stxkind );
242249
243- /* no statistics build yet */
250+ /* no statistics built yet */
244251 nulls [Anum_pg_statistic_ext_stxndistinct - 1 ] = true;
245252 nulls [Anum_pg_statistic_ext_stxdependencies - 1 ] = true;
246253
@@ -260,7 +267,7 @@ CreateStatistics(CreateStatsStmt *stmt)
260267 relation_close (rel , NoLock );
261268
262269 /*
263- * Add a dependency on a table, so that stats get dropped on DROP TABLE.
270+ * Add a dependency on the table, so that stats get dropped on DROP TABLE.
264271 */
265272 ObjectAddressSet (parentobject , RelationRelationId , relid );
266273 ObjectAddressSet (childobject , StatisticExtRelationId , statoid );
@@ -269,12 +276,15 @@ CreateStatistics(CreateStatsStmt *stmt)
269276 /*
270277 * Also add dependency on the schema. This is required to ensure that we
271278 * drop the statistics on DROP SCHEMA. This is not handled automatically
272- * by DROP TABLE because the statistics are not an object in the table's
273- * schema.
279+ * by DROP TABLE because the statistics might be in a different schema
280+ * from the table itself. (This definition is a bit bizarre for the
281+ * single-table case, but it will make more sense if/when we support
282+ * extended stats across multiple tables.)
274283 */
275284 ObjectAddressSet (parentobject , NamespaceRelationId , namespaceId );
276285 recordDependencyOn (& childobject , & parentobject , DEPENDENCY_AUTO );
277286
287+ /* Return stats object's address */
278288 ObjectAddressSet (address , StatisticExtRelationId , statoid );
279289
280290 return address ;
@@ -287,13 +297,13 @@ void
287297RemoveStatisticsById (Oid statsOid )
288298{
289299 Relation relation ;
290- Oid relid ;
291- Relation rel ;
292300 HeapTuple tup ;
293301 Form_pg_statistic_ext statext ;
302+ Oid relid ;
294303
295304 /*
296- * Delete the pg_statistic_ext tuple.
305+ * Delete the pg_statistic_ext tuple. Also send out a cache inval on the
306+ * associated table, so that dependent plans will be rebuilt.
297307 */
298308 relation = heap_open (StatisticExtRelationId , RowExclusiveLock );
299309
@@ -305,14 +315,11 @@ RemoveStatisticsById(Oid statsOid)
305315 statext = (Form_pg_statistic_ext ) GETSTRUCT (tup );
306316 relid = statext -> stxrelid ;
307317
308- rel = heap_open (relid , AccessExclusiveLock );
318+ CacheInvalidateRelcacheByRelid (relid );
309319
310320 simple_heap_delete (relation , & tup -> t_self );
311321
312- CacheInvalidateRelcache (rel );
313-
314322 ReleaseSysCache (tup );
315323
316324 heap_close (relation , RowExclusiveLock );
317- heap_close (rel , NoLock );
318325}
0 commit comments