Skip to content

Commit

Permalink
Make attstattarget nullable
Browse files Browse the repository at this point in the history
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
  • Loading branch information
petere committed Jan 13, 2024
1 parent 45da693 commit 4f62250
Show file tree
Hide file tree
Showing 16 changed files with 113 additions and 87 deletions.
10 changes: 6 additions & 4 deletions doc/src/sgml/ref/alter_table.sgml
Expand Up @@ -51,7 +51,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ]
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> ] } [...]
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP IDENTITY [ IF EXISTS ]
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STATISTICS <replaceable class="parameter">integer</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STATISTICS { <replaceable class="parameter">integer</replaceable> | DEFAULT }
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT }
Expand Down Expand Up @@ -328,9 +328,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
This form
sets the per-column statistics-gathering target for subsequent
<link linkend="sql-analyze"><command>ANALYZE</command></link> operations.
The target can be set in the range 0 to 10000; alternatively, set it
to -1 to revert to using the system default statistics
target (<xref linkend="guc-default-statistics-target"/>).
The target can be set in the range 0 to 10000. Set it
to <literal>DEFAULT</literal> to revert to using the system default
statistics target (<xref linkend="guc-default-statistics-target"/>).
(Setting to a value of -1 is an obsolete way spelling to get the same
outcome.)
For more information on the use of statistics by the
<productname>PostgreSQL</productname> query planner, refer to
<xref linkend="planner-stats"/>.
Expand Down
4 changes: 0 additions & 4 deletions src/backend/access/common/tupdesc.c
Expand Up @@ -453,8 +453,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->atttypid != attr2->atttypid)
return false;
if (attr1->attstattarget != attr2->attstattarget)
return false;
if (attr1->attlen != attr2->attlen)
return false;
if (attr1->attndims != attr2->attndims)
Expand Down Expand Up @@ -639,7 +637,6 @@ TupleDescInitEntry(TupleDesc desc,
else if (attributeName != NameStr(att->attname))
namestrcpy(&(att->attname), attributeName);

att->attstattarget = -1;
att->attcacheoff = -1;
att->atttypmod = typmod;

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

att->attstattarget = -1;
att->attcacheoff = -1;
att->atttypmod = typmod;

Expand Down
1 change: 0 additions & 1 deletion src/backend/bootstrap/bootstrap.c
Expand Up @@ -552,7 +552,6 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
if (OidIsValid(attrtypes[attnum]->attcollation))
attrtypes[attnum]->attcollation = C_COLLATION_OID;

attrtypes[attnum]->attstattarget = -1;
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
Expand Down
1 change: 0 additions & 1 deletion src/backend/catalog/genbki.pl
Expand Up @@ -840,7 +840,6 @@ sub gen_pg_attribute
my %row;
$row{attnum} = $attnum;
$row{attrelid} = $table->{relation_oid};
$row{attstattarget} = '0';

morph_row_for_pgattr(\%row, $schema, $attr, 1);
print_bki_insert(\%row, $schema);
Expand Down
18 changes: 8 additions & 10 deletions src/backend/catalog/heap.c
Expand Up @@ -749,14 +749,16 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
slot[slotCount]->tts_values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(attrs->attisdropped);
slot[slotCount]->tts_values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(attrs->attislocal);
slot[slotCount]->tts_values[Anum_pg_attribute_attinhcount - 1] = Int16GetDatum(attrs->attinhcount);
slot[slotCount]->tts_values[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(attrs->attstattarget);
slot[slotCount]->tts_values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(attrs->attcollation);
if (attoptions && attoptions[natts] != (Datum) 0)
slot[slotCount]->tts_values[Anum_pg_attribute_attoptions - 1] = attoptions[natts];
else
slot[slotCount]->tts_isnull[Anum_pg_attribute_attoptions - 1] = true;

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

indstate = CatalogOpenIndexes(rel);

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

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

/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;

/* Unset this so no one tries to look up the generation expression */
attStruct->attgenerated = '\0';

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

/*
* Clear the other variable-length fields. This saves some space in
* pg_attribute and removes no longer useful information.
* Clear the other nullable fields. This saves some space in pg_attribute
* and removes no longer useful information.
*/
nullsAtt[Anum_pg_attribute_attstattarget - 1] = true;
replacesAtt[Anum_pg_attribute_attstattarget - 1] = true;
nullsAtt[Anum_pg_attribute_attacl - 1] = true;
replacesAtt[Anum_pg_attribute_attacl - 1] = true;
nullsAtt[Anum_pg_attribute_attoptions - 1] = true;
Expand Down
21 changes: 15 additions & 6 deletions src/backend/catalog/index.c
Expand Up @@ -325,7 +325,6 @@ ConstructTupleDescriptor(Relation heapRelation,

MemSet(to, 0, ATTRIBUTE_FIXED_PART_SIZE);
to->attnum = i + 1;
to->attstattarget = -1;
to->attcacheoff = -1;
to->attislocal = true;
to->attcollation = (i < numkeyatts) ? collationIds[i] : InvalidOid;
Expand Down Expand Up @@ -1780,10 +1779,12 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
while (HeapTupleIsValid((attrTuple = systable_getnext(scan))))
{
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attrTuple);
HeapTuple tp;
Datum dat;
bool isnull;
Datum repl_val[Natts_pg_attribute];
bool repl_null[Natts_pg_attribute];
bool repl_repl[Natts_pg_attribute];
int attstattarget;
HeapTuple newTuple;

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

/* no need for a refresh if both match */
if (attstattarget == att->attstattarget)
/*
* No need for a refresh if old index value is null. (All new
* index values are null at this point.)
*/
if (isnull)
continue;

memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));

repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(attstattarget);
repl_val[Anum_pg_attribute_attstattarget - 1] = dat;

newTuple = heap_modify_tuple(attrTuple,
RelationGetDescr(pg_attribute),
Expand Down
21 changes: 19 additions & 2 deletions src/backend/commands/analyze.c
Expand Up @@ -1004,6 +1004,10 @@ static VacAttrStats *
examine_attribute(Relation onerel, int attnum, Node *index_expr)
{
Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1);
int attstattarget;
HeapTuple atttuple;
Datum dat;
bool isnull;
HeapTuple typtuple;
VacAttrStats *stats;
int i;
Expand All @@ -1013,15 +1017,28 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
if (attr->attisdropped)
return NULL;

/*
* Get attstattarget value. Set to -1 if null. (Analyze functions expect
* -1 to mean use default_statistics_target; see for example
* std_typanalyze.)
*/
atttuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(RelationGetRelid(onerel)), Int16GetDatum(attnum));
if (!HeapTupleIsValid(atttuple))
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, RelationGetRelid(onerel));
dat = SysCacheGetAttr(ATTNUM, atttuple, Anum_pg_attribute_attstattarget, &isnull);
attstattarget = isnull ? -1 : DatumGetInt16(dat);
ReleaseSysCache(atttuple);

/* Don't analyze column if user has specified not to */
if (attr->attstattarget == 0)
if (attstattarget == 0)
return NULL;

/*
* Create the VacAttrStats struct.
*/
stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
stats->attstattarget = attr->attstattarget;
stats->attstattarget = attstattarget;

/*
* When analyzing an expression index, believe the expression tree's type
Expand Down
43 changes: 34 additions & 9 deletions src/backend/commands/tablecmds.c
Expand Up @@ -8543,10 +8543,14 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
{
int newtarget;
Relation attrelation;
HeapTuple tuple;
HeapTuple tuple,
newtuple;
Form_pg_attribute attrtuple;
AttrNumber attnum;
ObjectAddress address;
Datum repl_val[Natts_pg_attribute];
bool repl_null[Natts_pg_attribute];
bool repl_repl[Natts_pg_attribute];

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

Assert(IsA(newValue, Integer));
newtarget = intVal(newValue);
if (newValue)
{
Assert(IsA(newValue, Integer));
newtarget = intVal(newValue);
}
else
{
/*
* -1 was used in previous versions to represent the default setting
*/
newtarget = -1;
}

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

if (colName)
{
tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);

if (!HeapTupleIsValid(tuple))
ereport(ERROR,
Expand All @@ -8595,7 +8609,7 @@ ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newVa
}
else
{
tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);

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

attrtuple->attstattarget = newtarget;

CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
if (newtarget != -1)
repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
else
repl_null[Anum_pg_attribute_attstattarget - 1] = true;
repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
repl_val, repl_null, repl_repl);
CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);

InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel),
attrtuple->attnum);
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
heap_freetuple(tuple);

heap_freetuple(newtuple);

ReleaseSysCache(tuple);

table_close(attrelation, RowExclusiveLock);

Expand Down
18 changes: 12 additions & 6 deletions src/backend/parser/gram.y
Expand Up @@ -337,6 +337,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> alter_table_cmds alter_type_cmds
%type <list> alter_identity_column_option_list
%type <defelt> alter_identity_column_option
%type <node> set_statistics_value

%type <list> createdb_opt_list createdb_opt_items copy_opt_list
transaction_mode_list
Expand Down Expand Up @@ -2446,18 +2447,18 @@ alter_table_cmd:
n->missing_ok = true;
$$ = (Node *) n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS <SignedIconst> */
| ALTER opt_column ColId SET STATISTICS SignedIconst
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS */
| ALTER opt_column ColId SET STATISTICS set_statistics_value
{
AlterTableCmd *n = makeNode(AlterTableCmd);

n->subtype = AT_SetStatistics;
n->name = $3;
n->def = (Node *) makeInteger($6);
n->def = $6;
$$ = (Node *) n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS <SignedIconst> */
| ALTER opt_column Iconst SET STATISTICS SignedIconst
/* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS */
| ALTER opt_column Iconst SET STATISTICS set_statistics_value
{
AlterTableCmd *n = makeNode(AlterTableCmd);

Expand All @@ -2469,7 +2470,7 @@ alter_table_cmd:

n->subtype = AT_SetStatistics;
n->num = (int16) $3;
n->def = (Node *) makeInteger($6);
n->def = $6;
$$ = (Node *) n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
Expand Down Expand Up @@ -3070,6 +3071,11 @@ alter_identity_column_option:
}
;

set_statistics_value:
SignedIconst { $$ = (Node *) makeInteger($1); }
| DEFAULT { $$ = NULL; }
;

PartitionBoundSpec:
/* a HASH partition */
FOR VALUES WITH '(' hash_partbound ')'
Expand Down
27 changes: 0 additions & 27 deletions src/backend/utils/cache/lsyscache.c
Expand Up @@ -872,33 +872,6 @@ get_attnum(Oid relid, const char *attname)
return InvalidAttrNumber;
}

/*
* get_attstattarget
*
* Given the relation id and the attribute number,
* return the "attstattarget" field from the attribute relation.
*
* Errors if not found.
*/
int
get_attstattarget(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
Form_pg_attribute att_tup;
int result;

tp = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, relid);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
result = att_tup->attstattarget;
ReleaseSysCache(tp);
return result;
}

/*
* get_attgenerated
*
Expand Down

0 comments on commit 4f62250

Please sign in to comment.