diff --git a/doc/src/sgml/ref/alter_index.sgml b/doc/src/sgml/ref/alter_index.sgml
new file mode 100644
index ad77b57..34ab710
*** a/doc/src/sgml/ref/alter_index.sgml
--- b/doc/src/sgml/ref/alter_index.sgml
*************** ALTER INDEX [ IF EXISTS ] name DEPENDS ON EXTENSION extension_name
ALTER INDEX [ IF EXISTS ] name SET ( storage_parameter = value [, ... ] )
ALTER INDEX [ IF EXISTS ] name RESET ( storage_parameter [, ... ] )
+ ALTER INDEX [ IF EXISTS ] name ALTER [ COLUMN ] column_number
+ SET STATISTICS integer
ALTER INDEX ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
SET TABLESPACE new_tablespace [ NOWAIT ]
*************** ALTER INDEX ALL IN TABLESPACE
+
+ ALTER [ COLUMN ] column_number SET STATISTICS integer
+
+
+ This form sets statistics-gathering target for an indexed expression
+ referenced by its number. This value is used in subsequent
+ 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 ().
+ For more information on the use of statistics by the
+ PostgreSQL query planner, refer to
+ .
+
+
+
+
*************** ALTER INDEX ALL IN TABLESPACE
+ column_number
+
+
+ The number of index column starting from 1.
+
+
+
+
+
name
*************** ALTER INDEX distributors SET (fillfactor
*** 235,240 ****
--- 263,276 ----
REINDEX INDEX distributors;
+
+ To set statistics-gathering target for an indexed expression:
+
+ CREATE INDEX coord_idx ON measured (x, y, (z + t));
+ ALTER INDEX coord_idx ALTER COLUMN 3 SET STATISTICS 1000;
+
+
+
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index 0f08245..8e8b4ea
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** static ObjectAddress ATExecAddIdentity(R
*** 375,383 ****
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
Node *def, LOCKMODE lockmode);
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
! static void ATPrepSetStatistics(Relation rel, const char *colName,
Node *newValue, LOCKMODE lockmode);
! static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
Node *newValue, LOCKMODE lockmode);
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
Node *options, bool isReset, LOCKMODE lockmode);
--- 375,383 ----
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
Node *def, LOCKMODE lockmode);
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
! static void ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum,
Node *newValue, LOCKMODE lockmode);
! static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
Node *newValue, LOCKMODE lockmode);
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
Node *options, bool isReset, LOCKMODE lockmode);
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 3525,3531 ****
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* Performs own permission checks */
! ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
pass = AT_PASS_MISC;
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
--- 3525,3531 ----
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* Performs own permission checks */
! ATPrepSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
pass = AT_PASS_MISC;
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
*************** ATExecCmd(List **wqueue, AlteredTableInf
*** 3848,3854 ****
address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
break;
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
! address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
--- 3848,3854 ----
address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
break;
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
! address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
*************** ATExecDropIdentity(Relation rel, const c
*** 6120,6126 ****
* ALTER TABLE ALTER COLUMN SET STATISTICS
*/
static void
! ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
{
/*
* We do our own permission checking because (a) we want to allow SET
--- 6120,6126 ----
* ALTER TABLE ALTER COLUMN SET STATISTICS
*/
static void
! ATPrepSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
{
/*
* We do our own permission checking because (a) we want to allow SET
*************** ATPrepSetStatistics(Relation rel, const
*** 6138,6143 ****
--- 6138,6153 ----
errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
RelationGetRelationName(rel))));
+ /*
+ * We do allow referencing columns by names only for indexes, because
+ * table column numbers could contain gaps.
+ */
+ if (rel->rd_rel->relkind != RELKIND_INDEX && !colName)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a index, but only index columns could be refered by number",
+ RelationGetRelationName(rel))));
+
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
*************** ATPrepSetStatistics(Relation rel, const
*** 6148,6154 ****
* Return value is the address of the modified column
*/
static ObjectAddress
! ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
{
int newtarget;
Relation attrelation;
--- 6158,6164 ----
* Return value is the address of the modified column
*/
static ObjectAddress
! ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
{
int newtarget;
Relation attrelation;
*************** ATExecSetStatistics(Relation rel, const
*** 6181,6193 ****
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
! tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
- if (!HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("column \"%s\" of relation \"%s\" does not exist",
- colName, RelationGetRelationName(rel))));
attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attrtuple->attnum;
--- 6191,6217 ----
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
! if (colName)
! {
! tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
!
! if (!HeapTupleIsValid(tuple))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_COLUMN),
! errmsg("column \"%s\" of relation \"%s\" does not exist",
! colName, RelationGetRelationName(rel))));
! }
! else
! {
! tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
!
! if (!HeapTupleIsValid(tuple))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_COLUMN),
! errmsg("column number %d of relation \"%s\" does not exist",
! colNum, RelationGetRelationName(rel))));
! }
attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attrtuple->attnum;
*************** ATExecSetStatistics(Relation rel, const
*** 6197,6202 ****
--- 6221,6234 ----
errmsg("cannot alter system column \"%s\"",
colName)));
+ if (rel->rd_rel->relkind == RELKIND_INDEX &&
+ rel->rd_index->indkey.values[attnum - 1] != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
+ NameStr(attrtuple->attname), RelationGetRelationName(rel)),
+ errhint("Alter statistics on table column instead.")));
+
attrtuple->attstattarget = newtarget;
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index 7204169..3f6efdd
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyAlterTableCmd(const AlterTableCmd *
*** 3085,3090 ****
--- 3085,3091 ----
COPY_SCALAR_FIELD(subtype);
COPY_STRING_FIELD(name);
+ COPY_SCALAR_FIELD(num);
COPY_NODE_FIELD(newowner);
COPY_NODE_FIELD(def);
COPY_SCALAR_FIELD(behavior);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index 8d92c03..11731da
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalAlterTableCmd(const AlterTableCmd
*** 1098,1103 ****
--- 1098,1104 ----
{
COMPARE_SCALAR_FIELD(subtype);
COMPARE_STRING_FIELD(name);
+ COMPARE_SCALAR_FIELD(num);
COMPARE_NODE_FIELD(newowner);
COMPARE_NODE_FIELD(def);
COMPARE_SCALAR_FIELD(behavior);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index 7d0de99..c353a0e
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** alter_table_cmd:
*** 2078,2083 ****
--- 2078,2098 ----
n->def = (Node *) makeInteger($6);
$$ = (Node *)n;
}
+ /* ALTER TABLE ALTER [COLUMN] SET STATISTICS */
+ | ALTER opt_column Iconst SET STATISTICS SignedIconst
+ {
+ if ($3 <= 0 || $3 > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("column number must be in range from 1 to %d", PG_INT16_MAX),
+ parser_errposition(@3)));
+
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetStatistics;
+ n->num = (int16) $3;
+ n->def = (Node *) makeInteger($6);
+ $$ = (Node *)n;
+ }
/* ALTER TABLE ALTER [COLUMN] SET ( column_parameter = value [, ... ] ) */
| ALTER opt_column ColId SET reloptions
{
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
new file mode 100644
index 607fe9d..d83ffda
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
*************** SearchSysCacheExistsAttName(Oid relid, c
*** 1257,1262 ****
--- 1257,1308 ----
/*
+ * SearchSysCacheAttNum
+ *
+ * This routine is equivalent to SearchSysCache on the ATTNUM cache,
+ * except that it will return NULL if the found attribute is marked
+ * attisdropped. This is convenient for callers that want to act as
+ * though dropped attributes don't exist.
+ */
+ HeapTuple
+ SearchSysCacheAttNum(Oid relid, int16 attnum)
+ {
+ HeapTuple tuple;
+
+ tuple = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(tuple))
+ return NULL;
+ if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
+ {
+ ReleaseSysCache(tuple);
+ return NULL;
+ }
+ return tuple;
+ }
+
+ /*
+ * SearchSysCacheCopyAttNum
+ *
+ * As above, an attisdropped-aware version of SearchSysCacheCopy.
+ */
+ HeapTuple
+ SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
+ {
+ HeapTuple tuple,
+ newtuple;
+
+ tuple = SearchSysCacheAttNum(relid, attnum);
+ if (!HeapTupleIsValid(tuple))
+ return tuple;
+ newtuple = heap_copytuple(tuple);
+ ReleaseSysCache(tuple);
+ return newtuple;
+ }
+
+
+ /*
* SysCacheGetAttr
*
* Given a tuple previously fetched by SearchSysCache(),
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 5f2a4a7..5bc6243
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct AlterTableCmd /* one subc
*** 1772,1777 ****
--- 1772,1779 ----
AlterTableType subtype; /* Type of table alteration to apply */
char *name; /* column, constraint, or trigger to act on,
* or tablespace */
+ int16 num; /* attribute number for columns referenced
+ * by number */
RoleSpec *newowner;
Node *def; /* definition of new column, index,
* constraint, or parent table */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
new file mode 100644
index 8352b40..8a92ea2
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
*************** extern HeapTuple SearchSysCacheAttName(O
*** 131,136 ****
--- 131,139 ----
extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname);
extern bool SearchSysCacheExistsAttName(Oid relid, const char *attname);
+ extern HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum);
+ extern HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum);
+
extern Datum SysCacheGetAttr(int cacheId, HeapTuple tup,
AttrNumber attributeNumber, bool *isNull);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
new file mode 100644
index ed03cb9..64160a8
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
*************** SELECT * FROM tmp;
*** 94,99 ****
--- 94,114 ----
| 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}
(1 row)
+ CREATE INDEX tmp_idx ON tmp (a, (d + e), b);
+ ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
+ ERROR: column number must be in range from 1 to 32767
+ LINE 1: ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
+ ^
+ ALTER INDEX tmp_idx ALTER COLUMN 1 SET STATISTICS 1000;
+ ERROR: cannot alter statistics on non-expression column "a" of index "tmp_idx"
+ HINT: Alter statistics on table column instead.
+ ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS 1000;
+ ALTER INDEX tmp_idx ALTER COLUMN 3 SET STATISTICS 1000;
+ ERROR: cannot alter statistics on non-expression column "b" of index "tmp_idx"
+ HINT: Alter statistics on table column instead.
+ ALTER INDEX tmp_idx ALTER COLUMN 4 SET STATISTICS 1000;
+ ERROR: column number 4 of relation "tmp_idx" does not exist
+ ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS -1;
DROP TABLE tmp;
--
-- rename - check on both non-temp and temp tables
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
new file mode 100644
index 9a20dd1..57f44c4
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
*************** INSERT INTO tmp (a, b, c, d, e, f, g, h,
*** 142,147 ****
--- 142,161 ----
SELECT * FROM tmp;
+ CREATE INDEX tmp_idx ON tmp (a, (d + e), b);
+
+ ALTER INDEX tmp_idx ALTER COLUMN 0 SET STATISTICS 1000;
+
+ ALTER INDEX tmp_idx ALTER COLUMN 1 SET STATISTICS 1000;
+
+ ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS 1000;
+
+ ALTER INDEX tmp_idx ALTER COLUMN 3 SET STATISTICS 1000;
+
+ ALTER INDEX tmp_idx ALTER COLUMN 4 SET STATISTICS 1000;
+
+ ALTER INDEX tmp_idx ALTER COLUMN 2 SET STATISTICS -1;
+
DROP TABLE tmp;