Skip to content

Commit 4f62250

Browse files
committed
Make attstattarget nullable
This changes the pg_attribute field attstattarget into a nullable field in the variable-length part of the row. If no value is set by the user for attstattarget, it is now null instead of previously -1. This saves space in pg_attribute and tuple descriptors for most practical scenarios. (ATTRIBUTE_FIXED_PART_SIZE is reduced from 108 to 104.) Also, null is the semantically more correct value. The ANALYZE code internally continues to represent the default statistics target by -1, so that that code can avoid having to deal with null values. But that is now contained to the ANALYZE code. Only the DDL code deals with attstattarget possibly null. For system columns, the field is now always null. The ANALYZE code skips system columns anyway. To set a column's statistics target to the default value, the new command form ALTER TABLE ... SET STATISTICS DEFAULT can be used. (SET STATISTICS -1 still works.) Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org> Discussion: https://www.postgresql.org/message-id/flat/4da8d211-d54d-44b9-9847-f2a9f1184c76@eisentraut.org
1 parent 45da693 commit 4f62250

File tree

16 files changed

+113
-87
lines changed

16 files changed

+113
-87
lines changed

doc/src/sgml/ref/alter_table.sgml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
5151
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ]
5252
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET GENERATED { ALWAYS | BY DEFAULT } | SET <replaceable>sequence_option</replaceable> | RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] } [...]
5353
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP IDENTITY [ IF EXISTS ]
54-
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STATISTICS <replaceable class="parameter">integer</replaceable>
54+
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STATISTICS { <replaceable class="parameter">integer</replaceable> | DEFAULT }
5555
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
5656
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
5757
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT }
@@ -328,9 +328,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
328328
This form
329329
sets the per-column statistics-gathering target for subsequent
330330
<link linkend="sql-analyze"><command>ANALYZE</command></link> operations.
331-
The target can be set in the range 0 to 10000; alternatively, set it
332-
to -1 to revert to using the system default statistics
333-
target (<xref linkend="guc-default-statistics-target"/>).
331+
The target can be set in the range 0 to 10000. Set it
332+
to <literal>DEFAULT</literal> to revert to using the system default
333+
statistics target (<xref linkend="guc-default-statistics-target"/>).
334+
(Setting to a value of -1 is an obsolete way spelling to get the same
335+
outcome.)
334336
For more information on the use of statistics by the
335337
<productname>PostgreSQL</productname> query planner, refer to
336338
<xref linkend="planner-stats"/>.

src/backend/access/common/tupdesc.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
453453
return false;
454454
if (attr1->atttypid != attr2->atttypid)
455455
return false;
456-
if (attr1->attstattarget != attr2->attstattarget)
457-
return false;
458456
if (attr1->attlen != attr2->attlen)
459457
return false;
460458
if (attr1->attndims != attr2->attndims)
@@ -639,7 +637,6 @@ TupleDescInitEntry(TupleDesc desc,
639637
else if (attributeName != NameStr(att->attname))
640638
namestrcpy(&(att->attname), attributeName);
641639

642-
att->attstattarget = -1;
643640
att->attcacheoff = -1;
644641
att->atttypmod = typmod;
645642

@@ -702,7 +699,6 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
702699
Assert(attributeName != NULL);
703700
namestrcpy(&(att->attname), attributeName);
704701

705-
att->attstattarget = -1;
706702
att->attcacheoff = -1;
707703
att->atttypmod = typmod;
708704

src/backend/bootstrap/bootstrap.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,6 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
552552
if (OidIsValid(attrtypes[attnum]->attcollation))
553553
attrtypes[attnum]->attcollation = C_COLLATION_OID;
554554

555-
attrtypes[attnum]->attstattarget = -1;
556555
attrtypes[attnum]->attcacheoff = -1;
557556
attrtypes[attnum]->atttypmod = -1;
558557
attrtypes[attnum]->attislocal = true;

src/backend/catalog/genbki.pl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,6 @@ sub gen_pg_attribute
840840
my %row;
841841
$row{attnum} = $attnum;
842842
$row{attrelid} = $table->{relation_oid};
843-
$row{attstattarget} = '0';
844843

845844
morph_row_for_pgattr(\%row, $schema, $attr, 1);
846845
print_bki_insert(\%row, $schema);

src/backend/catalog/heap.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -749,14 +749,16 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
749749
slot[slotCount]->tts_values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(attrs->attisdropped);
750750
slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(attrs->attislocal);
751751
slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] = Int16GetDatum(attrs->attinhcount);
752-
slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(attrs->attstattarget);
753752
slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(attrs->attcollation);
754753
if (attoptions && attoptions[natts] != (Datum) 0)
755754
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = attoptions[natts];
756755
else
757756
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;
758757

759-
/* start out with empty permissions and empty options */
758+
/*
759+
* The remaining fields are not set for new columns.
760+
*/
761+
slot[slotCount]->tts_isnull[Anum_pg_attribute_attstattarget - 1] = true;
760762
slot[slotCount]->tts_isnull[Anum_pg_attribute_attacl - 1] = true;
761763
slot[slotCount]->tts_isnull[Anum_pg_attribute_attfdwoptions - 1] = true;
762764
slot[slotCount]->tts_isnull[Anum_pg_attribute_attmissingval - 1] = true;
@@ -818,9 +820,6 @@ AddNewAttributeTuples(Oid new_rel_oid,
818820

819821
indstate = CatalogOpenIndexes(rel);
820822

821-
/* set stats detail level to a sane default */
822-
for (int i = 0; i < natts; i++)
823-
tupdesc->attrs[i].attstattarget = -1;
824823
InsertPgAttributeTuples(rel, tupdesc, new_rel_oid, NULL, indstate);
825824

826825
/* add dependencies on their datatypes and collations */
@@ -1685,9 +1684,6 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
16851684
/* Remove any not-null constraint the column may have */
16861685
attStruct->attnotnull = false;
16871686

1688-
/* We don't want to keep stats for it anymore */
1689-
attStruct->attstattarget = 0;
1690-
16911687
/* Unset this so no one tries to look up the generation expression */
16921688
attStruct->attgenerated = '\0';
16931689

@@ -1704,9 +1700,11 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
17041700
replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
17051701

17061702
/*
1707-
* Clear the other variable-length fields. This saves some space in
1708-
* pg_attribute and removes no longer useful information.
1703+
* Clear the other nullable fields. This saves some space in pg_attribute
1704+
* and removes no longer useful information.
17091705
*/
1706+
nullsAtt[Anum_pg_attribute_attstattarget - 1] = true;
1707+
replacesAtt[Anum_pg_attribute_attstattarget - 1] = true;
17101708
nullsAtt[Anum_pg_attribute_attacl - 1] = true;
17111709
replacesAtt[Anum_pg_attribute_attacl - 1] = true;
17121710
nullsAtt[Anum_pg_attribute_attoptions - 1] = true;

src/backend/catalog/index.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,6 @@ ConstructTupleDescriptor(Relation heapRelation,
325325

326326
MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE);
327327
to->attnum = i + 1;
328-
to->attstattarget = -1;
329328
to->attcacheoff = -1;
330329
to->attislocal = true;
331330
to->attcollation = (i < numkeyatts) ? collationIds[i] : InvalidOid;
@@ -1780,10 +1779,12 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
17801779
while (HeapTupleIsValid((attrTuple = systable_getnext(scan))))
17811780
{
17821781
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attrTuple);
1782+
HeapTuple tp;
1783+
Datum dat;
1784+
bool isnull;
17831785
Datum repl_val[Natts_pg_attribute];
17841786
bool repl_null[Natts_pg_attribute];
17851787
bool repl_repl[Natts_pg_attribute];
1786-
int attstattarget;
17871788
HeapTuple newTuple;
17881789

17891790
/* Ignore dropped columns */
@@ -1793,18 +1794,26 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
17931794
/*
17941795
* Get attstattarget from the old index and refresh the new value.
17951796
*/
1796-
attstattarget = get_attstattarget(oldIndexId, att->attnum);
1797+
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(oldIndexId), Int16GetDatum(att->attnum));
1798+
if (!HeapTupleIsValid(tp))
1799+
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1800+
att->attnum, oldIndexId);
1801+
dat = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attstattarget, &isnull);
1802+
ReleaseSysCache(tp);
17971803

1798-
/* no need for a refresh if both match */
1799-
if (attstattarget == att->attstattarget)
1804+
/*
1805+
* No need for a refresh if old index value is null. (All new
1806+
* index values are null at this point.)
1807+
*/
1808+
if (isnull)
18001809
continue;
18011810

18021811
memset(repl_val, 0, sizeof(repl_val));
18031812
memset(repl_null, false, sizeof(repl_null));
18041813
memset(repl_repl, false, sizeof(repl_repl));
18051814

18061815
repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
1807-
repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(attstattarget);
1816+
repl_val[Anum_pg_attribute_attstattarget - 1] = dat;
18081817

18091818
newTuple = heap_modify_tuple(attrTuple,
18101819
RelationGetDescr(pg_attribute),

src/backend/commands/analyze.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,10 @@ static VacAttrStats *
10041004
examine_attribute(Relation onerel, int attnum, Node *index_expr)
10051005
{
10061006
Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1);
1007+
int attstattarget;
1008+
HeapTuple atttuple;
1009+
Datum dat;
1010+
bool isnull;
10071011
HeapTuple typtuple;
10081012
VacAttrStats *stats;
10091013
int i;
@@ -1013,15 +1017,28 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
10131017
if (attr->attisdropped)
10141018
return NULL;
10151019

1020+
/*
1021+
* Get attstattarget value. Set to -1 if null. (Analyze functions expect
1022+
* -1 to mean use default_statistics_target; see for example
1023+
* std_typanalyze.)
1024+
*/
1025+
atttuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(RelationGetRelid(onerel)), Int16GetDatum(attnum));
1026+
if (!HeapTupleIsValid(atttuple))
1027+
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1028+
attnum, RelationGetRelid(onerel));
1029+
dat = SysCacheGetAttr(ATTNUM, atttuple, Anum_pg_attribute_attstattarget, &isnull);
1030+
attstattarget = isnull ? -1 : DatumGetInt16(dat);
1031+
ReleaseSysCache(atttuple);
1032+
10161033
/* Don't analyze column if user has specified not to */
1017-
if (attr->attstattarget == 0)
1034+
if (attstattarget == 0)
10181035
return NULL;
10191036

10201037
/*
10211038
* Create the VacAttrStats struct.
10221039
*/
10231040
stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
1024-
stats->attstattarget = attr->attstattarget;
1041+
stats->attstattarget = attstattarget;
10251042

10261043
/*
10271044
* When analyzing an expression index, believe the expression tree's type

src/backend/commands/tablecmds.c

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8543,10 +8543,14 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
85438543
{
85448544
int newtarget;
85458545
Relation attrelation;
8546-
HeapTuple tuple;
8546+
HeapTuple tuple,
8547+
newtuple;
85478548
Form_pg_attribute attrtuple;
85488549
AttrNumber attnum;
85498550
ObjectAddress address;
8551+
Datum repl_val[Natts_pg_attribute];
8552+
bool repl_null[Natts_pg_attribute];
8553+
bool repl_repl[Natts_pg_attribute];
85508554

85518555
/*
85528556
* We allow referencing columns by numbers only for indexes, since table
@@ -8559,8 +8563,18 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
85598563
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
85608564
errmsg("cannot refer to non-index column by number")));
85618565

8562-
Assert(IsA(newValue, Integer));
8563-
newtarget = intVal(newValue);
8566+
if (newValue)
8567+
{
8568+
Assert(IsA(newValue, Integer));
8569+
newtarget = intVal(newValue);
8570+
}
8571+
else
8572+
{
8573+
/*
8574+
* -1 was used in previous versions to represent the default setting
8575+
*/
8576+
newtarget = -1;
8577+
}
85648578

85658579
/*
85668580
* Limit target to a sane range
@@ -8585,7 +8599,7 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
85858599

85868600
if (colName)
85878601
{
8588-
tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8602+
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
85898603

85908604
if (!HeapTupleIsValid(tuple))
85918605
ereport(ERROR,
@@ -8595,7 +8609,7 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
85958609
}
85968610
else
85978611
{
8598-
tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
8612+
tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
85998613

86008614
if (!HeapTupleIsValid(tuple))
86018615
ereport(ERROR,
@@ -8629,16 +8643,27 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
86298643
errhint("Alter statistics on table column instead.")));
86308644
}
86318645

8632-
attrtuple->attstattarget = newtarget;
8633-
8634-
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8646+
/* Build new tuple. */
8647+
memset(repl_null, false, sizeof(repl_null));
8648+
memset(repl_repl, false, sizeof(repl_repl));
8649+
if (newtarget != -1)
8650+
repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8651+
else
8652+
repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8653+
repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8654+
newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8655+
repl_val, repl_null, repl_repl);
8656+
CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
86358657

86368658
InvokeObjectPostAlterHook(RelationRelationId,
86378659
RelationGetRelid(rel),
86388660
attrtuple->attnum);
86398661
ObjectAddressSubSet(address, RelationRelationId,
86408662
RelationGetRelid(rel), attnum);
8641-
heap_freetuple(tuple);
8663+
8664+
heap_freetuple(newtuple);
8665+
8666+
ReleaseSysCache(tuple);
86428667

86438668
table_close(attrelation, RowExclusiveLock);
86448669

src/backend/parser/gram.y

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
337337
%type <list> alter_table_cmds alter_type_cmds
338338
%type <list> alter_identity_column_option_list
339339
%type <defelt> alter_identity_column_option
340+
%type <node> set_statistics_value
340341

341342
%type <list> createdb_opt_list createdb_opt_items copy_opt_list
342343
transaction_mode_list
@@ -2446,18 +2447,18 @@ alter_table_cmd:
24462447
n->missing_ok = true;
24472448
$$ = (Node *) n;
24482449
}
2449-
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS <SignedIconst> */
2450-
| ALTER opt_column ColId SET STATISTICS SignedIconst
2450+
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS */
2451+
| ALTER opt_column ColId SET STATISTICS set_statistics_value
24512452
{
24522453
AlterTableCmd *n = makeNode(AlterTableCmd);
24532454

24542455
n->subtype = AT_SetStatistics;
24552456
n->name = $3;
2456-
n->def = (Node *) makeInteger($6);
2457+
n->def = $6;
24572458
$$ = (Node *) n;
24582459
}
2459-
/* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS <SignedIconst> */
2460-
| ALTER opt_column Iconst SET STATISTICS SignedIconst
2460+
/* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS */
2461+
| ALTER opt_column Iconst SET STATISTICS set_statistics_value
24612462
{
24622463
AlterTableCmd *n = makeNode(AlterTableCmd);
24632464

@@ -2469,7 +2470,7 @@ alter_table_cmd:
24692470

24702471
n->subtype = AT_SetStatistics;
24712472
n->num = (int16) $3;
2472-
n->def = (Node *) makeInteger($6);
2473+
n->def = $6;
24732474
$$ = (Node *) n;
24742475
}
24752476
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
@@ -3070,6 +3071,11 @@ alter_identity_column_option:
30703071
}
30713072
;
30723073

3074+
set_statistics_value:
3075+
SignedIconst { $$ = (Node *) makeInteger($1); }
3076+
| DEFAULT { $$ = NULL; }
3077+
;
3078+
30733079
PartitionBoundSpec:
30743080
/* a HASH partition */
30753081
FOR VALUES WITH '(' hash_partbound ')'

src/backend/utils/cache/lsyscache.c

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -872,33 +872,6 @@ get_attnum(Oid relid, const char *attname)
872872
return InvalidAttrNumber;
873873
}
874874

875-
/*
876-
* get_attstattarget
877-
*
878-
* Given the relation id and the attribute number,
879-
* return the "attstattarget" field from the attribute relation.
880-
*
881-
* Errors if not found.
882-
*/
883-
int
884-
get_attstattarget(Oid relid, AttrNumber attnum)
885-
{
886-
HeapTuple tp;
887-
Form_pg_attribute att_tup;
888-
int result;
889-
890-
tp = SearchSysCache2(ATTNUM,
891-
ObjectIdGetDatum(relid),
892-
Int16GetDatum(attnum));
893-
if (!HeapTupleIsValid(tp))
894-
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
895-
attnum, relid);
896-
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
897-
result = att_tup->attstattarget;
898-
ReleaseSysCache(tp);
899-
return result;
900-
}
901-
902875
/*
903876
* get_attgenerated
904877
*

0 commit comments

Comments
 (0)