Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5b6d13e

Browse files
Allow SET STATISTICS on expression indexes
Index columns are referenced by ordinal number rather than name, e.g. CREATE INDEX coord_idx ON measured (x, y, (z + t)); ALTER INDEX coord_idx ALTER COLUMN 3 SET STATISTICS 1000; Incompatibility note for release notes: \d+ for indexes now also displays Stats Target Authors: Alexander Korotkov, with contribution by Adrien NAYRAT Review: Adrien NAYRAT, Simon Riggs Wordsmith: Simon Riggs
1 parent e09db94 commit 5b6d13e

File tree

13 files changed

+201
-17
lines changed

13 files changed

+201
-17
lines changed

doc/src/sgml/ref/alter_index.sgml

+39
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> SET
2626
ALTER INDEX <replaceable class="PARAMETER">name</replaceable> DEPENDS ON EXTENSION <replaceable class="PARAMETER">extension_name</replaceable>
2727
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
2828
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
29+
ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column_number</replaceable>
30+
SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
2931
ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable> [ OWNED BY <replaceable class="PARAMETER">role_name</replaceable> [, ... ] ]
3032
SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable> [ NOWAIT ]
3133
</synopsis>
@@ -110,6 +112,25 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
110112
</listitem>
111113
</varlistentry>
112114

115+
<varlistentry>
116+
<term><literal>ALTER [ COLUMN ] <replaceable class="PARAMETER">column_number</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable></literal></term>
117+
<listitem>
118+
<para>
119+
This form sets the per-column statistics-gathering target for
120+
subsequent <xref linkend="sql-analyze"> operations, though can
121+
be used only on index columns that are defined as an expression.
122+
Since expressions lack a unique name, we refer to them using the
123+
ordinal number of the index column.
124+
The target can be set in the range 0 to 10000; alternatively, set it
125+
to -1 to revert to using the system default statistics
126+
target (<xref linkend="guc-default-statistics-target">).
127+
For more information on the use of statistics by the
128+
<productname>PostgreSQL</productname> query planner, refer to
129+
<xref linkend="planner-stats">.
130+
</para>
131+
</listitem>
132+
</varlistentry>
133+
113134
</variablelist>
114135
</para>
115136

@@ -130,6 +151,16 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
130151
</listitem>
131152
</varlistentry>
132153

154+
<varlistentry>
155+
<term><replaceable class="PARAMETER">column_number</replaceable></term>
156+
<listitem>
157+
<para>
158+
The ordinal number refers to the ordinal (left-to-right) position
159+
of the index column.
160+
</para>
161+
</listitem>
162+
</varlistentry>
163+
133164
<varlistentry>
134165
<term><replaceable class="PARAMETER">name</replaceable></term>
135166
<listitem>
@@ -235,6 +266,14 @@ ALTER INDEX distributors SET (fillfactor = 75);
235266
REINDEX INDEX distributors;
236267
</programlisting></para>
237268

269+
<para>
270+
Set the statistics-gathering target for an expression index:
271+
<programlisting>
272+
CREATE INDEX coord_idx ON measured (x, y, (z + t));
273+
ALTER INDEX coord_idx ALTER COLUMN 3 SET STATISTICS 1000;
274+
</programlisting>
275+
</para>
276+
238277
</refsect1>
239278

240279
<refsect1>

src/backend/commands/tablecmds.c

+43-12
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,9 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
375375
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
376376
Node *def, LOCKMODE lockmode);
377377
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
378-
static void ATPrepSetStatistics(Relation rel, const char *colName,
378+
static void ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum,
379379
Node *newValue, LOCKMODE lockmode);
380-
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
380+
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
381381
Node *newValue, LOCKMODE lockmode);
382382
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
383383
Node *options, bool isReset, LOCKMODE lockmode);
@@ -3525,7 +3525,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
35253525
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
35263526
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
35273527
/* Performs own permission checks */
3528-
ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
3528+
ATPrepSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
35293529
pass = AT_PASS_MISC;
35303530
break;
35313531
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
@@ -3848,7 +3848,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
38483848
address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
38493849
break;
38503850
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
3851-
address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
3851+
address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
38523852
break;
38533853
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
38543854
address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
@@ -6120,7 +6120,7 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
61206120
* ALTER TABLE ALTER COLUMN SET STATISTICS
61216121
*/
61226122
static void
6123-
ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6123+
ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
61246124
{
61256125
/*
61266126
* We do our own permission checking because (a) we want to allow SET
@@ -6138,6 +6138,15 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
61386138
errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
61396139
RelationGetRelationName(rel))));
61406140

6141+
/*
6142+
* We allow referencing columns by numbers only for indexes, since
6143+
* table column numbers could contain gaps if columns are later dropped.
6144+
*/
6145+
if (rel->rd_rel->relkind != RELKIND_INDEX && !colName)
6146+
ereport(ERROR,
6147+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6148+
errmsg("cannot refer to non-index column by number")));
6149+
61416150
/* Permissions checks */
61426151
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
61436152
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
@@ -6148,7 +6157,7 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
61486157
* Return value is the address of the modified column
61496158
*/
61506159
static ObjectAddress
6151-
ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
6160+
ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
61526161
{
61536162
int newtarget;
61546163
Relation attrelation;
@@ -6181,13 +6190,27 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
61816190

61826191
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
61836192

6184-
tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6193+
if (colName)
6194+
{
6195+
tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
6196+
6197+
if (!HeapTupleIsValid(tuple))
6198+
ereport(ERROR,
6199+
(errcode(ERRCODE_UNDEFINED_COLUMN),
6200+
errmsg("column \"%s\" of relation \"%s\" does not exist",
6201+
colName, RelationGetRelationName(rel))));
6202+
}
6203+
else
6204+
{
6205+
tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
6206+
6207+
if (!HeapTupleIsValid(tuple))
6208+
ereport(ERROR,
6209+
(errcode(ERRCODE_UNDEFINED_COLUMN),
6210+
errmsg("column number %d of relation \"%s\" does not exist",
6211+
colNum, RelationGetRelationName(rel))));
6212+
}
61856213

6186-
if (!HeapTupleIsValid(tuple))
6187-
ereport(ERROR,
6188-
(errcode(ERRCODE_UNDEFINED_COLUMN),
6189-
errmsg("column \"%s\" of relation \"%s\" does not exist",
6190-
colName, RelationGetRelationName(rel))));
61916214
attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
61926215

61936216
attnum = attrtuple->attnum;
@@ -6197,6 +6220,14 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
61976220
errmsg("cannot alter system column \"%s\"",
61986221
colName)));
61996222

6223+
if (rel->rd_rel->relkind == RELKIND_INDEX &&
6224+
rel->rd_index->indkey.values[attnum - 1] != 0)
6225+
ereport(ERROR,
6226+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6227+
errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
6228+
NameStr(attrtuple->attname), RelationGetRelationName(rel)),
6229+
errhint("Alter statistics on table column instead.")));
6230+
62006231
attrtuple->attstattarget = newtarget;
62016232

62026233
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);

src/backend/nodes/copyfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -3087,6 +3087,7 @@ _copyAlterTableCmd(const AlterTableCmd *from)
30873087

30883088
COPY_SCALAR_FIELD(subtype);
30893089
COPY_STRING_FIELD(name);
3090+
COPY_SCALAR_FIELD(num);
30903091
COPY_NODE_FIELD(newowner);
30913092
COPY_NODE_FIELD(def);
30923093
COPY_SCALAR_FIELD(behavior);

src/backend/nodes/equalfuncs.c

+1
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ _equalAlterTableCmd(const AlterTableCmd *a, const AlterTableCmd *b)
10981098
{
10991099
COMPARE_SCALAR_FIELD(subtype);
11001100
COMPARE_STRING_FIELD(name);
1101+
COMPARE_SCALAR_FIELD(num);
11011102
COMPARE_NODE_FIELD(newowner);
11021103
COMPARE_NODE_FIELD(def);
11031104
COMPARE_SCALAR_FIELD(behavior);

src/backend/parser/gram.y

+16
Original file line numberDiff line numberDiff line change
@@ -2078,6 +2078,22 @@ alter_table_cmd:
20782078
n->def = (Node *) makeInteger($6);
20792079
$$ = (Node *)n;
20802080
}
2081+
/* ALTER TABLE <name> ALTER [COLUMN] <colnum> SET STATISTICS <SignedIconst> */
2082+
| ALTER opt_column Iconst SET STATISTICS SignedIconst
2083+
{
2084+
AlterTableCmd *n = makeNode(AlterTableCmd);
2085+
2086+
if ($3 <= 0 || $3 > PG_INT16_MAX)
2087+
ereport(ERROR,
2088+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2089+
errmsg("column number must be in range from 1 to %d", PG_INT16_MAX),
2090+
parser_errposition(@3)));
2091+
2092+
n->subtype = AT_SetStatistics;
2093+
n->num = (int16) $3;
2094+
n->def = (Node *) makeInteger($6);
2095+
$$ = (Node *)n;
2096+
}
20812097
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
20822098
| ALTER opt_column ColId SET reloptions
20832099
{

src/backend/utils/cache/syscache.c

+46
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,52 @@ SearchSysCacheExistsAttName(Oid relid, const char *attname)
12561256
}
12571257

12581258

1259+
/*
1260+
* SearchSysCacheAttNum
1261+
*
1262+
* This routine is equivalent to SearchSysCache on the ATTNUM cache,
1263+
* except that it will return NULL if the found attribute is marked
1264+
* attisdropped. This is convenient for callers that want to act as
1265+
* though dropped attributes don't exist.
1266+
*/
1267+
HeapTuple
1268+
SearchSysCacheAttNum(Oid relid, int16 attnum)
1269+
{
1270+
HeapTuple tuple;
1271+
1272+
tuple = SearchSysCache2(ATTNUM,
1273+
ObjectIdGetDatum(relid),
1274+
Int16GetDatum(attnum));
1275+
if (!HeapTupleIsValid(tuple))
1276+
return NULL;
1277+
if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
1278+
{
1279+
ReleaseSysCache(tuple);
1280+
return NULL;
1281+
}
1282+
return tuple;
1283+
}
1284+
1285+
/*
1286+
* SearchSysCacheCopyAttNum
1287+
*
1288+
* As above, an attisdropped-aware version of SearchSysCacheCopy.
1289+
*/
1290+
HeapTuple
1291+
SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
1292+
{
1293+
HeapTuple tuple,
1294+
newtuple;
1295+
1296+
tuple = SearchSysCacheAttNum(relid, attnum);
1297+
if (!HeapTupleIsValid(tuple))
1298+
return NULL;
1299+
newtuple = heap_copytuple(tuple);
1300+
ReleaseSysCache(tuple);
1301+
return newtuple;
1302+
}
1303+
1304+
12591305
/*
12601306
* SysCacheGetAttr
12611307
*

src/bin/psql/describe.c

+2
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,7 @@ describeOneTableDetails(const char *schemaname,
17421742
{
17431743
headers[cols++] = gettext_noop("Storage");
17441744
if (tableinfo.relkind == RELKIND_RELATION ||
1745+
tableinfo.relkind == RELKIND_INDEX ||
17451746
tableinfo.relkind == RELKIND_MATVIEW ||
17461747
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
17471748
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
@@ -1841,6 +1842,7 @@ describeOneTableDetails(const char *schemaname,
18411842

18421843
/* Statistics target, if the relkind supports this feature */
18431844
if (tableinfo.relkind == RELKIND_RELATION ||
1845+
tableinfo.relkind == RELKIND_INDEX ||
18441846
tableinfo.relkind == RELKIND_MATVIEW ||
18451847
tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
18461848
tableinfo.relkind == RELKIND_PARTITIONED_TABLE)

src/bin/psql/tab-complete.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -1644,7 +1644,10 @@ psql_completion(const char *text, int start, int end)
16441644
"UNION SELECT 'ALL IN TABLESPACE'");
16451645
/* ALTER INDEX <name> */
16461646
else if (Matches3("ALTER", "INDEX", MatchAny))
1647-
COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET");
1647+
COMPLETE_WITH_LIST5("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET", "RESET");
1648+
/* ALTER INDEX <name> ALTER COLUMN <colnum> */
1649+
else if (Matches6("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny))
1650+
COMPLETE_WITH_CONST("SET STATISTICS");
16481651
/* ALTER INDEX <name> SET */
16491652
else if (Matches4("ALTER", "INDEX", MatchAny, "SET"))
16501653
COMPLETE_WITH_LIST2("(", "TABLESPACE");

src/include/nodes/parsenodes.h

+2
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,8 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
17771777
AlterTableType subtype; /* Type of table alteration to apply */
17781778
char *name; /* column, constraint, or trigger to act on,
17791779
* or tablespace */
1780+
int16 num; /* attribute number for columns referenced
1781+
* by number */
17801782
RoleSpec *newowner;
17811783
Node *def; /* definition of new column, index,
17821784
* constraint, or parent table */

src/include/utils/syscache.h

+3
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname);
131131
extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname);
132132
extern bool SearchSysCacheExistsAttName(Oid relid, const char *attname);
133133

134+
extern HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum);
135+
extern HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum);
136+
134137
extern Datum SysCacheGetAttr(int cacheId, HeapTuple tup,
135138
AttrNumber attributeNumber, bool *isNull);
136139

src/test/regress/expected/alter_table.out

+24
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,30 @@ SELECT * FROM tmp;
9494
| 4 | name | text | 4.1 | 4.1 | 2 | ((4.1,4.1),(3.1,3.1)) | Mon May 01 00:30:30 1995 PDT | c | {"Mon May 01 00:30:30 1995 PDT","Mon Aug 24 14:43:07 1992 PDT","Wed Dec 31 16:00:00 1969 PST"} | 314159 | (1,1) | 512 | 1 2 3 4 5 6 7 8 | magnetic disk | (1.1,1.1) | [(4.1,4.1),(3.1,3.1)] | ((0,2),(4.1,4.1),(3.1,3.1)) | (4.1,4.1),(3.1,3.1) | ["Wed Dec 31 16:00:00 1969 PST" "infinity"] | Thu Jan 01 00:00:00 1970 | @ 1 hour 10 secs | {1,2,3,4} | {1,2,3,4} | {1,2,3,4}
9595
(1 row)
9696

97+
CREATE INDEX tmp_idx ON tmp (a, (d + e), b);
98+
ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
99+
ERROR: column number must be in range from 1 to 32767
100+
LINE 1: ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
101+
^
102+
ALTER INDEX tmp_idx ALTER COLUMN 1 SET STATISTICS 1000;
103+
ERROR: cannot alter statistics on non-expression column "a" of index "tmp_idx"
104+
HINT: Alter statistics on table column instead.
105+
ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS 1000;
106+
\d+ tmp_idx
107+
Index "public.tmp_idx"
108+
Column | Type | Definition | Storage | Stats target
109+
--------+------------------+------------+---------+--------------
110+
a | integer | a | plain |
111+
expr | double precision | (d + e) | plain | 1000
112+
b | cstring | b | plain |
113+
btree, for table "public.tmp"
114+
115+
ALTER INDEX tmp_idx ALTER COLUMN 3 SET STATISTICS 1000;
116+
ERROR: cannot alter statistics on non-expression column "b" of index "tmp_idx"
117+
HINT: Alter statistics on table column instead.
118+
ALTER INDEX tmp_idx ALTER COLUMN 4 SET STATISTICS 1000;
119+
ERROR: column number 4 of relation "tmp_idx" does not exist
120+
ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS -1;
97121
DROP TABLE tmp;
98122
--
99123
-- rename - check on both non-temp and temp tables

src/test/regress/expected/create_index.out

+4-4
Original file line numberDiff line numberDiff line change
@@ -2324,10 +2324,10 @@ DROP TABLE array_gin_test;
23242324
CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i)
23252325
WITH (FASTUPDATE=on, GIN_PENDING_LIST_LIMIT=128);
23262326
\d+ gin_relopts_test
2327-
Index "public.gin_relopts_test"
2328-
Column | Type | Definition | Storage
2329-
--------+---------+------------+---------
2330-
i | integer | i | plain
2327+
Index "public.gin_relopts_test"
2328+
Column | Type | Definition | Storage | Stats target
2329+
--------+---------+------------+---------+--------------
2330+
i | integer | i | plain |
23312331
gin, for table "public.array_index_op_test"
23322332
Options: fastupdate=on, gin_pending_list_limit=128
23332333

src/test/regress/sql/alter_table.sql

+16
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,22 @@ INSERT INTO tmp (a, b, c, d, e, f, g, h, i, j, k, l, m, n, p, q, r, s, t, u,
142142

143143
SELECT * FROM tmp;
144144

145+
CREATE INDEX tmp_idx ON tmp (a, (d + e), b);
146+
147+
ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
148+
149+
ALTER INDEX tmp_idx ALTER COLUMN 1 SET STATISTICS 1000;
150+
151+
ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS 1000;
152+
153+
\d+ tmp_idx
154+
155+
ALTER INDEX tmp_idx ALTER COLUMN 3 SET STATISTICS 1000;
156+
157+
ALTER INDEX tmp_idx ALTER COLUMN 4 SET STATISTICS 1000;
158+
159+
ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS -1;
160+
145161
DROP TABLE tmp;
146162

147163

0 commit comments

Comments
 (0)