attoptions
PFA a revised attoptions patch. This may still be a little rough
around the edges, but I wanted to get it out there for feedback. This
would replace ALTER TABLE ... SET STATISTICS DISTINCT.
I am not very happy with ATPrepSetOptions(). I basically just
retained the logic from ATPrepSetDistinct(), but it doesn't really
make sense in this context. The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that. Even as it stands, the
n_distinct_inherited option is supported for both table columns and
index columns, but it only actually does anything for table columns.
One possibility is to just give up on supporting attoptions for system
table columns and index columns and support them only for regular
table columns. That will lose the ability to override n_distinct for
expression indexes and system tables, but how much do we really care?
Another option is to just jettison this whole concept as a bad idea
and leave it the way it is.
There might be other reasonable options, too. Thoughts?
...Robert
Attachments:
attoptions-v2.patchtext/x-patch; charset=US-ASCII; name=attoptions-v2.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef26e65..f897e05 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -902,19 +902,6 @@
</row>
<row>
- <entry><structfield>attdistinct</structfield></entry>
- <entry><type>float4</type></entry>
- <entry></entry>
- <entry>
- <structfield>attdistinct</structfield>, if nonzero, is a user-specified
- number-of-distinct-values figure to be used instead of estimating the
- number of distinct values during <command>ANALYZE</>. Nonzero values
- have the same meanings as for
- <link linkend="catalog-pg-statistic"><structname>pg_statistic</></link>.<structfield>stadistinct</>
- </entry>
- </row>
-
- <row>
<entry><structfield>attlen</structfield></entry>
<entry><type>int2</type></entry>
<entry></entry>
@@ -1061,6 +1048,15 @@
</entry>
</row>
+ <row>
+ <entry><structfield>attoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ Attribute-level options, as <quote>keyword=value</> strings
+ </entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 9fe94ed..f86a0e8 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -39,7 +39,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
- ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS DISTINCT <replaceable class="PARAMETER">number</replaceable>
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
ADD <replaceable class="PARAMETER">table_constraint</replaceable>
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
@@ -158,14 +159,21 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</varlistentry>
<varlistentry>
- <term><literal>SET STATISTICS DISTINCT</literal></term>
+ <term><literal>SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</term></literal>
+ <term><literal>RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] )</literal>
<listitem>
<para>
- This form overrides the number-of-distinct-values estimate made by
- subsequent <xref linkend="sql-analyze" endterm="sql-analyze-title">
- operations. When set to a positive value, <command>ANALYZE</> will
- assume that the column contains exactly the specified number of distinct
- nonnull values. When set to a negative value, which must be greater
+ This form sets or resets attribute-level options. Currently, the only
+ define attribute-level options are <literal>n_distinct</> and
+ <literal>n_distinct_inherited</>, which override the
+ number-of-distinct-values estimate made by subsequent
+ <xref linkend="sql-analyze" endterm="sql-analyze-title">
+ operations. <literal>n_distinct</> affects the statistics for the table
+ itself, while <literal>n_distinct_inherited</> affects the statistics
+ gathered for the table and its inheritance children. When set to a
+ positive value, <command>ANALYZE</> will assume that the column contains
+ exactly the specified number of distinct nonnull values. When set to a
+ negative value, which must be greater
than or equal to -1, <command>ANALYZE</> will assume that the number of
distinct nonnull values in the column is linear in the size of the
table; the exact count is to be computed by multiplying the estimated
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 24480e3..94fbeb3 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -24,6 +24,7 @@
#include "commands/tablespace.h"
#include "nodes/makefuncs.h"
#include "utils/array.h"
+#include "utils/attoptcache.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -196,6 +197,22 @@ static relopt_real realRelOpts[] =
},
-1, 0.0, DBL_MAX
},
+ {
+ {
+ "n_distinct",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
+ RELOPT_KIND_ATTRIBUTE
+ },
+ 0, -1.0, DBL_MAX
+ },
+ {
+ {
+ "n_distinct_inherited",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
+ RELOPT_KIND_ATTRIBUTE
+ },
+ 0, -1.0, DBL_MAX
+ },
/* list terminator */
{{NULL}}
};
@@ -1187,6 +1204,37 @@ index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
}
/*
+ * Option parser for attribute reloptions
+ */
+bytea *
+attribute_reloptions(Datum reloptions, bool validate)
+{
+ relopt_value *options;
+ AttributeOpts *aopts;
+ int numoptions;
+ static const relopt_parse_elt tab[] = {
+ {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
+ {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
+ };
+
+ options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
+ &numoptions);
+
+ /* if none set, we're done */
+ if (numoptions == 0)
+ return NULL;
+
+ aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
+
+ fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
+ validate, tab, lengthof(tab));
+
+ pfree(options);
+
+ return (bytea *) aopts;
+}
+
+/*
* Option parser for tablespace reloptions
*/
bytea *
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index b54520f..5775a22 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -338,8 +338,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attstattarget != attr2->attstattarget)
return false;
- if (attr1->attdistinct != attr2->attdistinct)
- return false;
if (attr1->attlen != attr2->attlen)
return false;
if (attr1->attndims != attr2->attndims)
@@ -362,7 +360,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
- /* attacl is ignored, since it's not even present... */
+ /* attacl and attoptions are not even present... */
}
if (tupdesc1->constr != NULL)
@@ -467,7 +465,6 @@ TupleDescInitEntry(TupleDesc desc,
MemSet(NameStr(att->attname), 0, NAMEDATALEN);
att->attstattarget = -1;
- att->attdistinct = 0;
att->attcacheoff = -1;
att->atttypmod = typmod;
@@ -479,7 +476,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
- /* attacl is not set because it's not present in tupledescs */
+ /* attacl and attoptions are not present in tupledescs */
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(oidtypeid),
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 948c82c..fd25eac 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -728,7 +728,6 @@ DefineAttr(char *name, char *type, int attnum)
}
attrtypes[attnum]->attstattarget = -1;
- attrtypes[attnum]->attdistinct = 0;
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index dc68238..1c10040 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -351,14 +351,14 @@ sub emit_pgattr_row
# Add in default values for pg_attribute
my %PGATTR_DEFAULTS = (
- attdistinct => '0',
attcacheoff => '-1',
atttypmod => '-1',
atthasdef => 'f',
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
- attacl => '_null_'
+ attacl => '_null_',
+ attoptions => '_null_'
);
return {%PGATTR_DEFAULTS, %row};
}
@@ -385,6 +385,7 @@ sub emit_schemapg_row
$row->{attstorage} = q|'| . $row->{attstorage} . q|'|;
$row->{attalign} = q|'| . $row->{attalign} . q|'|;
$row->{attacl} = q|{ 0 }|;
+ $row->{attoptions} = q|{ 0 }|;
# Expand booleans from 'f'/'t' to 'false'/'true'.
# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 81cd8c5..b542413 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -117,39 +117,39 @@ Oid binary_upgrade_next_toast_relfilenode = InvalidOid;
*/
static FormData_pg_attribute a1 = {
- 0, {"ctid"}, TIDOID, 0, 0, sizeof(ItemPointerData),
+ 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
- false, 'p', 's', true, false, false, true, 0, {0}
+ false, 'p', 's', true, false, false, true, 0, {0}, {0}
};
static FormData_pg_attribute a2 = {
- 0, {"oid"}, OIDOID, 0, 0, sizeof(Oid),
+ 0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
static FormData_pg_attribute a3 = {
- 0, {"xmin"}, XIDOID, 0, 0, sizeof(TransactionId),
+ 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
static FormData_pg_attribute a4 = {
- 0, {"cmin"}, CIDOID, 0, 0, sizeof(CommandId),
+ 0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
static FormData_pg_attribute a5 = {
- 0, {"xmax"}, XIDOID, 0, 0, sizeof(TransactionId),
+ 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
static FormData_pg_attribute a6 = {
- 0, {"cmax"}, CIDOID, 0, 0, sizeof(CommandId),
+ 0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
/*
@@ -159,7 +159,7 @@ static FormData_pg_attribute a6 = {
* used in SQL.
*/
static FormData_pg_attribute a7 = {
- 0, {"tableoid"}, OIDOID, 0, 0, sizeof(Oid),
+ 0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0, {0}
};
@@ -482,13 +482,12 @@ CheckAttributeType(const char *attname, Oid atttypid)
* Construct and insert a new tuple in pg_attribute.
*
* Caller has already opened and locked pg_attribute. new_attribute is the
- * attribute to insert (but we ignore its attacl, if indeed it has one).
+ * attribute to insert (but we ignore attacl and attoptions, which are always
+ * initialized to NULL).
*
* indstate is the index state for CatalogIndexInsert. It can be passed as
* NULL, in which case we'll fetch the necessary info. (Don't do this when
* inserting multiple attributes, because it's a tad more expensive.)
- *
- * We always initialize attacl to NULL (i.e., default permissions).
*/
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
@@ -507,7 +506,6 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attribute->attname);
values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attribute->attstattarget);
- values[Anum_pg_attribute_attdistinct - 1] = Float4GetDatum(new_attribute->attdistinct);
values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attribute->attnum);
values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attribute->attndims);
@@ -522,8 +520,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
- /* start out with empty permissions */
+ /* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
+ nulls[Anum_pg_attribute_attoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
@@ -578,7 +577,6 @@ AddNewAttributeTuples(Oid new_rel_oid,
attr->attrelid = new_rel_oid;
/* Make sure these are OK, too */
attr->attstattarget = -1;
- attr->attdistinct = 0;
attr->attcacheoff = -1;
InsertPgAttributeTuple(rel, attr, indstate);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b36702b..fb775fc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -203,7 +203,6 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attnum = i + 1;
to->attstattarget = -1;
- to->attdistinct = 0;
to->attcacheoff = -1;
to->attnotnull = false;
to->atthasdef = false;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 91f3719..35a9403 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -39,6 +39,7 @@
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
+#include "utils/attoptcache.h"
#include "utils/datum.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
@@ -493,6 +494,8 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
for (i = 0; i < attr_cnt; i++)
{
VacAttrStats *stats = vacattrstats[i];
+ AttributeOpts *aopt =
+ get_attribute_options(onerel->rd_id, stats->attr->attnum);
stats->rows = rows;
stats->tupDesc = onerel->rd_att;
@@ -501,9 +504,17 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
numrows,
totalrows);
- /* If attdistinct is set, override with that value */
- if (stats->attr->attdistinct != 0)
- stats->stadistinct = stats->attr->attdistinct;
+ /*
+ * If the appropriate flavor of the n_distinct option is
+ * specified, override with the corresponding value.
+ */
+ if (aopt != NULL)
+ {
+ float8 n_distinct =
+ inh ? aopt->n_distinct_inherited : aopt->n_distinct;
+ if (n_distinct != 0.0)
+ stats->stadistinct = n_distinct;
+ }
MemoryContextResetAndDeleteChildren(col_context);
}
@@ -751,6 +762,8 @@ compute_index_stats(Relation onerel, double totalrows,
for (i = 0; i < attr_cnt; i++)
{
VacAttrStats *stats = thisdata->vacattrstats[i];
+ AttributeOpts *aopt =
+ get_attribute_options(onerel->rd_id, stats->attr->attnum);
stats->exprvals = exprvals + i;
stats->exprnulls = exprnulls + i;
@@ -759,9 +772,15 @@ compute_index_stats(Relation onerel, double totalrows,
ind_fetch_func,
numindexrows,
totalindexrows);
- /* If attdistinct is set, override with that value */
- if (stats->attr->attdistinct != 0)
- stats->stadistinct = stats->attr->attdistinct;
+
+ /*
+ * If the n_distinct option is specified, it overrides the
+ * above computation. For indices, we always use just
+ * n_distinct, not n_distinct_inherited.
+ */
+ if (aopt != NULL && aopt->n_distinct != 0.0)
+ stats->stadistinct = aopt->n_distinct;
+
MemoryContextResetAndDeleteChildren(col_context);
}
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 30e49b1..76863c4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -284,10 +284,10 @@ static void ATPrepSetStatistics(Relation rel, const char *colName,
Node *newValue);
static void ATExecSetStatistics(Relation rel, const char *colName,
Node *newValue);
-static void ATPrepSetDistinct(Relation rel, const char *colName,
- Node *newValue);
-static void ATExecSetDistinct(Relation rel, const char *colName,
- Node *newValue);
+static void ATPrepSetOptions(Relation rel, const char *colName,
+ Node *options);
+static void ATExecSetOptions(Relation rel, const char *colName,
+ Node *options, bool isReset);
static void ATExecSetStorage(Relation rel, const char *colName,
Node *newValue);
static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
@@ -2425,10 +2425,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepSetStatistics(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
break;
- case AT_SetDistinct: /* ALTER COLUMN SET STATISTICS DISTINCT */
+ case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
+ case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
ATSimpleRecursion(wqueue, rel, cmd, recurse);
/* Performs own permission checks */
- ATPrepSetDistinct(rel, cmd->name, cmd->def);
+ ATPrepSetOptions(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
@@ -2644,8 +2645,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
ATExecSetStatistics(rel, cmd->name, cmd->def);
break;
- case AT_SetDistinct: /* ALTER COLUMN SET STATISTICS DISTINCT */
- ATExecSetDistinct(rel, cmd->name, cmd->def);
+ case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
+ ATExecSetOptions(rel, cmd->name, cmd->def, false);
+ break;
+ case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
+ ATExecSetOptions(rel, cmd->name, cmd->def, true);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATExecSetStorage(rel, cmd->name, cmd->def);
@@ -3682,7 +3686,6 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
namestrcpy(&(attribute.attname), colDef->colname);
attribute.atttypid = typeOid;
attribute.attstattarget = (newattnum > 0) ? -1 : 0;
- attribute.attdistinct = 0;
attribute.attlen = tform->typlen;
attribute.attcacheoff = -1;
attribute.atttypmod = typmod;
@@ -4152,16 +4155,16 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue)
}
/*
- * ALTER TABLE ALTER COLUMN SET STATISTICS DISTINCT
+ * ALTER TABLE ALTER COLUMN SET ( options )
+ * ALTER TABLE ALTER COLUMN RESET ( options )
*/
static void
-ATPrepSetDistinct(Relation rel, const char *colName, Node *newValue)
+ATPrepSetOptions(Relation rel, const char *colName, Node *options)
{
/*
- * We do our own permission checking because (a) we want to allow SET
- * DISTINCT on indexes (for expressional index columns), and (b) we want
- * to allow SET DISTINCT on system catalogs without requiring
- * allowSystemTableMods to be turned on.
+ * We do our own permission checking because (a) we want to allow options
+ * to be set on indexes, and (b) we want to allow options to be set on
+ * system catalogs without requiring allowSystemTableMods to be turned on.
*/
if (rel->rd_rel->relkind != RELKIND_RELATION &&
rel->rd_rel->relkind != RELKIND_INDEX)
@@ -4177,42 +4180,23 @@ ATPrepSetDistinct(Relation rel, const char *colName, Node *newValue)
}
static void
-ATExecSetDistinct(Relation rel, const char *colName, Node *newValue)
+ATExecSetOptions(Relation rel, const char *colName, Node *options,
+ bool isReset)
{
- float4 newdistinct;
Relation attrelation;
- HeapTuple tuple;
+ HeapTuple tuple,
+ newtuple;
Form_pg_attribute attrtuple;
-
- switch (nodeTag(newValue))
- {
- case T_Integer:
- newdistinct = intVal(newValue);
- break;
- case T_Float:
- newdistinct = floatVal(newValue);
- break;
- default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(newValue));
- newdistinct = 0; /* keep compiler quiet */
- break;
- }
-
- /*
- * Limit ndistinct to sane values
- */
- if (newdistinct < -1.0)
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("number of distinct values %g is too low",
- newdistinct)));
- }
+ Datum datum,
+ newOptions;
+ bool isnull;
+ Datum repl_val[Natts_pg_attribute];
+ bool repl_null[Natts_pg_attribute];
+ bool repl_repl[Natts_pg_attribute];
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
- tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
+ tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
@@ -4227,14 +4211,31 @@ ATExecSetDistinct(Relation rel, const char *colName, Node *newValue)
errmsg("cannot alter system column \"%s\"",
colName)));
- attrtuple->attdistinct = newdistinct;
-
- simple_heap_update(attrelation, &tuple->t_self, tuple);
+ /* Generate new proposed attoptions (text array) */
+ Assert(IsA(options, List));
+ datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+ newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
+ (List *) options, NULL, NULL, false,
+ isReset);
+ (void) attribute_reloptions(newOptions, true);
- /* keep system catalog indexes current */
- CatalogUpdateIndexes(attrelation, tuple);
+ /* Build new tuple. */
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+ if (newOptions != (Datum) 0)
+ repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
+ else
+ repl_null[Anum_pg_attribute_attoptions - 1] = true;
+ repl_repl[Anum_pg_attribute_attoptions - 1] = true;
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
+ repl_val, repl_null, repl_repl);
+ ReleaseSysCache(tuple);
- heap_freetuple(tuple);
+ /* Update system catalog. */
+ simple_heap_update(attrelation, &newtuple->t_self, newtuple);
+ CatalogUpdateIndexes(attrelation, newtuple);
+ heap_freetuple(newtuple);
heap_close(attrelation, RowExclusiveLock);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8201f29..3d3ae79 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1645,13 +1645,22 @@ alter_table_cmd:
n->def = (Node *) makeInteger($6);
$$ = (Node *)n;
}
- /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS DISTINCT <NumericOnly> */
- | ALTER opt_column ColId SET STATISTICS DISTINCT NumericOnly
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
+ | ALTER opt_column ColId SET reloptions
{
AlterTableCmd *n = makeNode(AlterTableCmd);
- n->subtype = AT_SetDistinct;
+ n->subtype = AT_SetOptions;
n->name = $3;
- n->def = (Node *) $7;
+ n->def = (Node *) $5;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
+ | ALTER opt_column ColId RESET reloptions
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_ResetOptions;
+ n->name = $3;
+ n->def = (Node *) $5;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile
index 83716a2..3e57964 100644
--- a/src/backend/utils/cache/Makefile
+++ b/src/backend/utils/cache/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/utils/cache
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = catcache.o inval.o plancache.o relcache.o \
+OBJS = attoptcache.o catcache.o inval.o plancache.o relcache.o \
spccache.o syscache.o lsyscache.o typcache.o ts_cache.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
new file mode 100644
index 0000000..65385b8
--- /dev/null
+++ b/src/backend/utils/cache/attoptcache.c
@@ -0,0 +1,181 @@
+/*-------------------------------------------------------------------------
+ *
+ * attoptcache.c
+ * Attribute options cache management.
+ *
+ * Attribute options are cached separately from the fixed-size portion of
+ * pg_attribute entries, which are handled by the relcache.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "catalog/pg_attribute.h"
+#include "utils/attoptcache.h"
+#include "utils/catcache.h"
+#include "utils/hsearch.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/* Hash table for informations about each attribute's options */
+static HTAB *AttoptCacheHash = NULL;
+
+/* attrelid and attnum form the lookup key, and must appear first */
+typedef struct
+{
+ Oid attrelid;
+ int attnum;
+} AttoptCacheKey;
+
+typedef struct
+{
+ AttoptCacheKey key; /* lookup key - must be first */
+ AttributeOpts *opts; /* options, or NULL if none */
+} AttoptCacheEntry;
+
+
+/*
+ * InvalidateAttoptCacheCallback
+ * Flush all cache entries when pg_attribute is updated.
+ *
+ * When pg_attribute is updated, we must flush the cache entry at least
+ * for that attribute. Currently, we just flush them all. Since attribute
+ * options are not currently used in performance-critical paths (such as
+ * query execution), this seems OK.
+ */
+static void
+InvalidateAttoptCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
+{
+ HASH_SEQ_STATUS status;
+ AttoptCacheEntry *attopt;
+
+ hash_seq_init(&status, AttoptCacheHash);
+ while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
+ {
+ if (attopt->opts)
+ pfree(attopt->opts);
+ if (hash_search(AttoptCacheHash,
+ (void *) &attopt->key,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(ERROR, "hash table corrupted");
+ }
+}
+
+/*
+ * InitializeAttoptCache
+ * Initialize the tablespace cache.
+ */
+static void
+InitializeAttoptCache(void)
+{
+ HASHCTL ctl;
+
+ /* Initialize the hash table. */
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(AttoptCacheKey);
+ ctl.entrysize = sizeof(AttoptCacheEntry);
+ ctl.hash = tag_hash;
+ AttoptCacheHash =
+ hash_create("Attopt cache", 256, &ctl,
+ HASH_ELEM | HASH_FUNCTION);
+
+ /* Make sure we've initialized CacheMemoryContext. */
+ if (!CacheMemoryContext)
+ CreateCacheMemoryContext();
+
+ /* Watch for invalidation events. */
+ CacheRegisterSyscacheCallback(ATTNUM,
+ InvalidateAttoptCacheCallback,
+ (Datum) 0);
+}
+
+/*
+ * get_attribute_options
+ * Fetch attribute options for a specified table OID.
+ */
+AttributeOpts *
+get_attribute_options(Oid attrelid, int attnum)
+{
+ AttoptCacheKey key;
+ AttoptCacheEntry *attopt;
+ AttributeOpts *result;
+ HeapTuple tp;
+
+ /* Find existing cache entry, if any. */
+ if (!AttoptCacheHash)
+ InitializeAttoptCache();
+ memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */
+ key.attrelid = attrelid;
+ key.attnum = attnum;
+ attopt =
+ (AttoptCacheEntry *) hash_search(AttoptCacheHash,
+ (void *) &key,
+ HASH_FIND,
+ NULL);
+
+ /* Not found in Attopt cache. Construct new cache entry. */
+ if (!attopt)
+ {
+ AttributeOpts *opts;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(attrelid),
+ Int16GetDatum(attnum),
+ 0, 0);
+
+ /*
+ * If we don't find a valid HeapTuple, it must mean someone has
+ * managed to request attribute details for a non-existent attribute.
+ * We treat that case as if no options were specified.
+ */
+ if (!HeapTupleIsValid(tp))
+ opts = NULL;
+ else
+ {
+ Datum datum;
+ bool isNull;
+
+ datum = SysCacheGetAttr(ATTNUM,
+ tp,
+ Anum_pg_attribute_attoptions,
+ &isNull);
+ if (isNull)
+ opts = NULL;
+ else
+ {
+ bytea *bytea_opts = attribute_reloptions(datum, false);
+ opts = MemoryContextAlloc(CacheMemoryContext,
+ VARSIZE(bytea_opts));
+ memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
+ }
+ ReleaseSysCache(tp);
+ }
+
+ /*
+ * It's important to create the actual cache entry only after
+ * reading pg_attribute, since the read could cause a cache flush.
+ */
+ attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
+ (void *) &key,
+ HASH_ENTER,
+ NULL);
+ attopt->opts = opts;
+ }
+
+ /* Return results in caller's memory context. */
+ if (attopt->opts == NULL)
+ return NULL;
+ result = palloc(VARSIZE(attopt->opts));
+ memcpy(result, attopt->opts, VARSIZE(attopt->opts));
+ return result;
+}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 99dd578..a1a5341 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5056,7 +5056,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_atttypname;
int i_atttypmod;
int i_attstattarget;
- int i_attdistinct;
int i_attstorage;
int i_typstorage;
int i_attnotnull;
@@ -5065,6 +5064,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_attlen;
int i_attalign;
int i_attislocal;
+ int i_attoptions;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -5104,13 +5104,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
if (g_fout->remoteVersion >= 80500)
{
- /* attdistinct is new in 8.5 */
+ /* attoptions is new in 8.5 */
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "a.attstattarget, a.attdistinct, "
- "a.attstorage, t.typstorage, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(attoptions, ', ') AS attoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -5122,11 +5122,11 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* need left join here to not fail on dropped columns ... */
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "a.attstattarget, 0 AS attdistinct, "
- "a.attstorage, t.typstorage, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -5142,12 +5142,12 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
* explicitly set or was just a default.
*/
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "-1 AS attstattarget, 0 AS attdistinct, "
- "a.attstorage, "
+ "-1 AS attstattarget, a.attstorage, "
"t.typstorage, a.attnotnull, a.atthasdef, "
"false AS attisdropped, a.attlen, "
"a.attalign, false AS attislocal, "
- "format_type(t.oid,a.atttypmod) AS atttypname "
+ "format_type(t.oid,a.atttypmod) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_attribute a LEFT JOIN pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::oid "
@@ -5159,12 +5159,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* format_type not available before 7.1 */
appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, "
- "-1 AS attstattarget, 0 AS attdistinct, "
+ "-1 AS attstattarget, "
"attstorage, attstorage AS typstorage, "
"attnotnull, atthasdef, false AS attisdropped, "
"attlen, attalign, "
"false AS attislocal, "
- "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname "
+ "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_attribute a "
"WHERE attrelid = '%u'::oid "
"AND attnum > 0::int2 "
@@ -5182,7 +5183,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_atttypname = PQfnumber(res, "atttypname");
i_atttypmod = PQfnumber(res, "atttypmod");
i_attstattarget = PQfnumber(res, "attstattarget");
- i_attdistinct = PQfnumber(res, "attdistinct");
i_attstorage = PQfnumber(res, "attstorage");
i_typstorage = PQfnumber(res, "typstorage");
i_attnotnull = PQfnumber(res, "attnotnull");
@@ -5191,13 +5191,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attlen = PQfnumber(res, "attlen");
i_attalign = PQfnumber(res, "attalign");
i_attislocal = PQfnumber(res, "attislocal");
+ i_attoptions = PQfnumber(res, "attoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
- tbinfo->attdistinct = (float4 *) malloc(ntups * sizeof(float4));
tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
@@ -5206,6 +5206,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
+ tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
@@ -5223,8 +5224,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
- tbinfo->attdistinct[j] = strtod(PQgetvalue(res, j, i_attdistinct),
- (char **) NULL);
tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
@@ -5232,6 +5231,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
+ tbinfo->attoptions[j] = strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -10798,22 +10798,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
/*
- * Dump per-column ndistinct information. We only issue an ALTER
- * TABLE statement if the attdistinct entry for this column is
- * non-zero (i.e. it's not the default value)
- */
- if (tbinfo->attdistinct[j] != 0 &&
- !tbinfo->attisdropped[j])
- {
- appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
- fmtId(tbinfo->dobj.name));
- appendPQExpBuffer(q, "ALTER COLUMN %s ",
- fmtId(tbinfo->attnames[j]));
- appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n",
- tbinfo->attdistinct[j]);
- }
-
- /*
* Dump per-column storage information. The statement is only
* dumped if the storage has been changed from the type's default.
*/
@@ -10850,6 +10834,19 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
storage);
}
}
+
+ /*
+ * Dump per-column attributes.
+ */
+ if (tbinfo->attoptions[j] && tbinfo->attoptions[j][0] != '\0')
+ {
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "ALTER COLUMN %s ",
+ fmtId(tbinfo->attnames[j]));
+ appendPQExpBuffer(q, "SET (%s);\n",
+ tbinfo->attoptions[j]);
+ }
}
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 77786a6..f193bdd 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -244,13 +244,13 @@ typedef struct _tableInfo
char **atttypnames; /* attribute type names */
int *atttypmod; /* type-specific type modifiers */
int *attstattarget; /* attribute statistics targets */
- float4 *attdistinct; /* override ndistinct calculation */
char *attstorage; /* attribute storage scheme */
char *typstorage; /* type storage scheme */
bool *attisdropped; /* true if attr is dropped; don't dump it */
int *attlen; /* attribute length, used by binary_upgrade */
char *attalign; /* attribute align, used by binary_upgrade */
bool *attislocal; /* true if attr has local definition */
+ char **attoptions; /* per-attribute options */
/*
* Note: we need to store per-attribute notnull, default, and constraint
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2c784aa..cb2ae9a 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -992,17 +992,6 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(list_COLUMNSET);
}
- else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
- pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
- pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
- pg_strcasecmp(prev2_wd, "SET") == 0 &&
- pg_strcasecmp(prev_wd, "STATISTICS") == 0)
- {
- static const char *const list_COLUMNSETSTATS[] =
- {"DISTINCT", NULL};
-
- COMPLETE_WITH_LIST(list_COLUMNSETSTATS);
- }
else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
(pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a0087bc..db21390 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -40,7 +40,8 @@ typedef enum relopt_kind
RELOPT_KIND_HASH = (1 << 3),
RELOPT_KIND_GIN = (1 << 4),
RELOPT_KIND_GIST = (1 << 5),
- RELOPT_KIND_TABLESPACE = (1 << 6),
+ RELOPT_KIND_ATTRIBUTE = (1 << 6),
+ RELOPT_KIND_TABLESPACE = (1 << 7),
/* if you add a new kind, make sure you update "last_default" too */
RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_TABLESPACE,
/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
@@ -266,6 +267,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
bool validate);
+extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
#endif /* RELOPTIONS_H */
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 6fdc6b8..bd7a4de 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -57,13 +57,6 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
int4 attstattarget;
/*
- * attdistinct, if nonzero, is a user-specified ndistinct value to be used
- * instead of estimating the number of distinct values during ANALYZE.
- * Nonzero values have the same meanings as for pg_statistic.stadistinct.
- */
- float4 attdistinct;
-
- /*
* attlen is a copy of the typlen field from pg_type for this attribute.
* See atttypid comments above.
*/
@@ -157,6 +150,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* Column-level access permissions */
aclitem attacl[1];
+
+ /* Column-level options */
+ aclitem attoptions[1];
} FormData_pg_attribute;
/*
@@ -185,21 +181,21 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attdistinct 5
-#define Anum_pg_attribute_attlen 6
-#define Anum_pg_attribute_attnum 7
-#define Anum_pg_attribute_attndims 8
-#define Anum_pg_attribute_attcacheoff 9
-#define Anum_pg_attribute_atttypmod 10
-#define Anum_pg_attribute_attbyval 11
-#define Anum_pg_attribute_attstorage 12
-#define Anum_pg_attribute_attalign 13
-#define Anum_pg_attribute_attnotnull 14
-#define Anum_pg_attribute_atthasdef 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attacl 19
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attisdropped 15
+#define Anum_pg_attribute_attislocal 16
+#define Anum_pg_attribute_attinhcount 17
+#define Anum_pg_attribute_attacl 18
+#define Anum_pg_attribute_attoptions 19
/* ----------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ba51833..06fc7ef 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1101,7 +1101,8 @@ typedef enum AlterTableType
AT_DropNotNull, /* alter column drop not null */
AT_SetNotNull, /* alter column set not null */
AT_SetStatistics, /* alter column set statistics */
- AT_SetDistinct, /* alter column set statistics distinct */
+ AT_SetOptions, /* alter column set ( options ) */
+ AT_ResetOptions, /* alter column reset ( options ) */
AT_SetStorage, /* alter column set storage */
AT_DropColumn, /* drop column */
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
diff --git a/src/include/utils/attoptcache.h b/src/include/utils/attoptcache.h
new file mode 100644
index 0000000..0d723fe
--- /dev/null
+++ b/src/include/utils/attoptcache.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * attoptcache.h
+ * Attribute options cache.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SPCCACHE_H
+#define SPCCACHE_H
+
+/*
+ * Attribute options.
+ */
+typedef struct AttributeOpts
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ float8 n_distinct;
+ float8 n_distinct_inherited;
+} AttributeOpts;
+
+AttributeOpts *get_attribute_options(Oid spcid, int attnum);
+
+#endif /* SPCCACHE_H */
On Sun, Jan 10, 2010 at 12:27, Robert Haas <robertmhaas@gmail.com> wrote:
I am not very happy with ATPrepSetOptions(). I basically just
retained the logic from ATPrepSetDistinct(), but it doesn't really
make sense in this context. The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that. Even as it stands, the
n_distinct_inherited option is supported for both table columns and
index columns, but it only actually does anything for table columns.
I say just do it in AT(Prep|Exec)SetOptions. We could extend struct
relopt_gen... but that seems overkill and hard to do without knowing
what else might be in attoptions. IMHO at this point its ok not to
worry about it util we have something we actually care about
restricting.
Comments on the patch below. Minus those Im happy with it.
in tablecmds.c:~3682 (ATExecAddColumn)
seems to be either missing a comment or missing the handling of
attoptions all together?
Any thoughts on how its now a float8 vs float4? Its nice how it
matches n_distinct in pg_stats now.
pg_dump.c:
You do '' AS attoptions in a few places, should that be NULL? Not
that it really matters in pg_dump...
I tested all the things you would expect (pg_dump included). The only
perhaps interesting thing is when creating or adding an inherited
table it does not pick up the parents attopts I think its debatable
if it should, but it seems kind of strange that given alter table
parent will give the child tables the appropriate attopts (of course
ONLY works as you expect)
My favorite error of the day :) :
ERROR: value -2 out of bounds for option "n_distinct_inherited"
DETAIL: Valid values are between "-1.000000" and
"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000".
See patch below on top of yours, it fixes some brainos:
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 161,167 **** static FormData_pg_attribute a6 = {
static FormData_pg_attribute a7 = {
0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
! true, 'p', 'i', true, false, false, true, 0, {0}
};
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
--- 161,167 ----
static FormData_pg_attribute a7 = {
0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
! true, 'p', 'i', true, false, false, true, 0, {0}, {0}
};
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 4218,4223 **** ATExecSetOptions(Relation rel, const char *colName,
Node *options,
--- 4218,4224 ----
newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
(List *) options, NULL, NULL, false,
isReset);
+ /* Validate new options */
(void) attribute_reloptions(newOptions, true);
/* Build new tuple. */
*** a/src/include/catalog/pg_attribute.h
--- b/src/include/catalog/pg_attribute.h
***************
*** 152,158 **** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
aclitem attacl[1];
/* Column-level options */
! aclitem attoptions[1];
} FormData_pg_attribute;
/*
--- 152,158 ----
aclitem attacl[1];
/* Column-level options */
! text attoptions[1];
} FormData_pg_attribute;
/*
First, thanks for the review. Detailed comments/questions below.
On Fri, Jan 15, 2010 at 12:52 AM, Alex Hunsaker <badalex@gmail.com> wrote:
On Sun, Jan 10, 2010 at 12:27, Robert Haas <robertmhaas@gmail.com> wrote:
I am not very happy with ATPrepSetOptions(). I basically just
retained the logic from ATPrepSetDistinct(), but it doesn't really
make sense in this context. The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that. Even as it stands, the
n_distinct_inherited option is supported for both table columns and
index columns, but it only actually does anything for table columns.I say just do it in AT(Prep|Exec)SetOptions. We could extend struct
relopt_gen... but that seems overkill and hard to do without knowing
what else might be in attoptions. IMHO at this point its ok not to
worry about it util we have something we actually care about
restricting.
I'm sorry - do what in AT(Prep|Exec)SetOptions?
Comments on the patch below. Minus those Im happy with it.
in tablecmds.c:~3682 (ATExecAddColumn)
seems to be either missing a comment or missing the handling of
attoptions all together?
Comment.
Any thoughts on how its now a float8 vs float4? Its nice how it
matches n_distinct in pg_stats now.
Well, the original reason for using float4 was to avoid bloating
TupleDescs. That doesn't matter with this design, so might as well
splurge.
pg_dump.c:
You do '' AS attoptions in a few places, should that be NULL? Not
that it really matters in pg_dump...
I like it the way it is, YMMV.
I tested all the things you would expect (pg_dump included). The only
perhaps interesting thing is when creating or adding an inherited
table it does not pick up the parents attopts I think its debatable
if it should, but it seems kind of strange that given alter table
parent will give the child tables the appropriate attopts (of course
ONLY works as you expect)
I don't think it should - it's fairly nonsensical for the current
options, at least.
My favorite error of the day :) :
ERROR: value -2 out of bounds for option "n_distinct_inherited"
DETAIL: Valid values are between "-1.000000" and
"179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000".
Yeah, I don't like that message very much, but I don't want to
reinvent the wheel just for this patch, so I think we're stuck with it
for now.
See patch below on top of yours, it fixes some brainos:
Thanks, those look like good changes.
...Robert
On Sat, Jan 16, 2010 at 05:39, Robert Haas <robertmhaas@gmail.com> wrote:
First, thanks for the review. Detailed comments/questions below.
On Fri, Jan 15, 2010 at 12:52 AM, Alex Hunsaker <badalex@gmail.com> wrote:
On Sun, Jan 10, 2010 at 12:27, Robert Haas <robertmhaas@gmail.com> wrote:
I am not very happy with ATPrepSetOptions(). I basically just
retained the logic from ATPrepSetDistinct(), but it doesn't really
make sense in this context. The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that. Even as it stands, the
n_distinct_inherited option is supported for both table columns and
index columns, but it only actually does anything for table columns.I say just do it in AT(Prep|Exec)SetOptions. We could extend struct
relopt_gen... but that seems overkill and hard to do without knowing
what else might be in attoptions. IMHO at this point its ok not to
worry about it util we have something we actually care about
restricting.I'm sorry - do what in AT(Prep|Exec)SetOptions?
Hrm lemme re-quote and try to slim it down a bit:
... The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that.
Basically I was agreeing and saying when we add something new lets
worry about it then. Clearer?
... The only
perhaps interesting thing is when creating or adding an inherited
table it does not pick up the parents attopts ...I don't think it should - it's fairly nonsensical for the current
options, at least.
No argument here. :)
On Sun, Jan 17, 2010 at 9:57 PM, Alex Hunsaker <badalex@gmail.com> wrote:
... The idea that we want to support
attdistinct for system tables and index columns was based on a very
specific understanding of what that was going to do; for attoptions,
well, it might make sense for the options that we have now, but it
might not make sense for the next thing we want to add, and there's
not going to be any easy fix for that.Basically I was agreeing and saying when we add something new lets
worry about it then. Clearer?
It's clear now, but I don't think I agree. On balance, I'm inclined
to just rip out the special case permissions checks that
AT_SetDistinct uses and just use ATSimplePermissionsRelationOrIndex()
instead. That will mean that users can't use ALTER TABLE ... ALTER
COLUMN ... SET STATISTICS DISTINCT for system tables, but I don't
think that's much of a loss, and it certainly seems cleaner than
hoping that any additional attoptions we add in the future will be
things that we don't mind having applied to system tables.
There's a further design issue here in that the reloptions code
currently contemplates at most 31 types of objects. That makes sense
if the object types are things like "table" or "GIN index", but it's
not going to work if we get too fine-grained. The "right" way to make
n_distinct apply to both table columns and index columns and
n_distinct_inherited only to table columns is probably to define two
different reloption kinds, but that's burning up our supply of
available bits a little more quickly than I feel comfortable with. So
I'm inclined to just let n_distinct_inherited be applied either place,
and if you happen to apply it to an index column it just won't affect
anything. We might want to refactor the reloptions API in the future
to allow this to be handled better, but I don't think we need or want
to do that for 8.5.
Does that make sense?
...Robert
On Tue, Jan 19, 2010 at 07:49, Robert Haas <robertmhaas@gmail.com> wrote:
That will mean that users can't use ALTER TABLE ... ALTER
COLUMN ... SET STATISTICS DISTINCT for system tables, but I don't
think that's much of a loss, and it certainly seems cleaner than
hoping that any additional attoptions we add in the future will be
things that we don't mind having applied to system tables.
I assumed there was a good reason to apply them to system tables. But
I admit I did not follow the original SET STATISTICS DISTINCT patch. [
looks ] Oh ok seeing that you were the original patch author you
probably have a good idea about the above, so ill defer :)
There's a further design issue here in that the reloptions code
currently contemplates at most 31 types of objects. That makes sense
if the object types are things like "table" or "GIN index", but it's
not going to work if we get too fine-grained. The "right" way to make
n_distinct apply to both table columns and index columns and
n_distinct_inherited only to table columns is probably to define two
different reloption kinds, but that's burning up our supply of
available bits a little more quickly than I feel comfortable with. So
I'm inclined to just let n_distinct_inherited be applied either place,
and if you happen to apply it to an index column it just won't affect
anything. We might want to refactor the reloptions API in the future
to allow this to be handled better, but I don't think we need or want
to do that for 8.5.
Agreed.
Although ISTM we might be able to extend it just using two bits:
RELOPT_KIND_SYSTEM
RELOPT_KIND_INHERIT
#define RELOPT_KIND_INDEX RELOPT_KIND_BTREE|RELOPT_KIND_GIST|...
Abuse ? Maybe. The hardest part I think would be setting those flags
appropriately.
But yes, lets keep it simple for now.
On Fri, Jan 15, 2010 at 12:52 AM, Alex Hunsaker <badalex@gmail.com> wrote:
***************
*** 152,158 **** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
aclitem attacl[1];/* Column-level options */
! aclitem attoptions[1];
} FormData_pg_attribute;/* --- 152,158 ---- aclitem attacl[1];/* Column-level options */
! text attoptions[1];
} FormData_pg_attribute;/*
Unfortunately this change (which is obviously correct and necessary)
breaks the build on src/backend/catalog/heap.c with:
heap.c:122: error: missing braces around initializer
heap.c:122: error: (near initialization for ‘a1.attoptions[0]’)
...repeated of the 7 hard-coded descriptors. Sadly I'm not quite sure
what to use instead. I can't find any examples of static initializers
for a varlena (which text is). However, I think that it doesn't
actually matter how that gets initialized, because I think only the
fixed-size portion is ever examined, so perhaps I can just leave off
the attoptions and attacl initializers altogether.
Whatever we decide about this, genbki.pl also needs the same treatment.
...Robert
On Tue, Jan 19, 2010 at 13:06, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, Jan 15, 2010 at 12:52 AM, Alex Hunsaker <badalex@gmail.com> wrote:
***************
*** 152,158 **** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
aclitem attacl[1];/* Column-level options */
! aclitem attoptions[1];
} FormData_pg_attribute;/* --- 152,158 ---- aclitem attacl[1];/* Column-level options */
! text attoptions[1];
} FormData_pg_attribute;/*
Unfortunately this change (which is obviously correct and necessary)
breaks the build on src/backend/catalog/heap.c with:heap.c:122: error: missing braces around initializer
heap.c:122: error: (near initialization for ‘a1.attoptions[0]’)
Huh. I must have not done an --enable-depend build :( Even so after
a make clean I just get warnings...
This is with gcc version 4.4.2 20091208 (prerelease) (GCC)
Well for grins I tried changing it to the obvious {{{0}, {0}}} as the
places that seem to use it mark it as "null" anyway (see heap.c
InsertPgAttributeTupe ~525).
But then relcache.c gets more warnings:
relcache.c:87: warning: missing braces around initialize
relcache.c:87: warning: (near initialization for
‘Desc_pg_class[0].attoptions[0]’)
relcache.c:88: warning: missing braces around initialize
...
Which comes from genbki.pl. Seems we are stuck with:
1) the non portable
$row->{attoptions} = q|.attoptions = { {0}, {0} }|;
2) just dont initialize as nothing seems need it (*note* I have not
looked very hard)
$row->{attoptions} = q||;
Thoughts? Id rather not have this useful patch fall on the floor
because of a stupid limitation like this :)
On Tue, Jan 19, 2010 at 23:02, Alex Hunsaker <badalex@gmail.com> wrote:
On Tue, Jan 19, 2010 at 13:06, Robert Haas <robertmhaas@gmail.com> wrote:
On Fri, Jan 15, 2010 at 12:52 AM, Alex Hunsaker <badalex@gmail.com> wrote:
! text attoptions[1];
Unfortunately this change (which is obviously correct and necessary)
breaks the build on src/backend/catalog/heap.c with:
2) just dont initialize as nothing seems need it (*note* I have not
looked very hard)
$row->{attoptions} = q||;
This should be OK because it will be zeroed anyway because its in the
.bss right?
On Tue, Jan 19, 2010 at 10:51 AM, Alex Hunsaker <badalex@gmail.com> wrote:
But yes, lets keep it simple for now.
OK. Updated patch attached. Changes:
- Incorporate your previous review patch.
- Omit attacl and attoptions from hardcoded relation descriptor
initializers so the whole thing still builds.- Use
ATSimplePermissionsRelationOrIndex instead of custom permissions
logic.
- Remove recursion, per further thought about a comment in your
original review - I agree that the recursive behavior is weird.
- Remove a stray reference to SET STATISTICS DISTINCT in the documentation.
- Bug fix: this wasn't working at all for index-expression columns.
...Robert
Attachments:
attoptions-v3.patchtext/x-patch; charset=US-ASCII; name=attoptions-v3.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 31d696e..370022e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -902,19 +902,6 @@
</row>
<row>
- <entry><structfield>attdistinct</structfield></entry>
- <entry><type>float4</type></entry>
- <entry></entry>
- <entry>
- <structfield>attdistinct</structfield>, if nonzero, is a user-specified
- number-of-distinct-values figure to be used instead of estimating the
- number of distinct values during <command>ANALYZE</>. Nonzero values
- have the same meanings as for
- <link linkend="catalog-pg-statistic"><structname>pg_statistic</></link>.<structfield>stadistinct</>
- </entry>
- </row>
-
- <row>
<entry><structfield>attlen</structfield></entry>
<entry><type>int2</type></entry>
<entry></entry>
@@ -1061,6 +1048,15 @@
</entry>
</row>
+ <row>
+ <entry><structfield>attoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ Attribute-level options, as <quote>keyword=value</> strings
+ </entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 9fe94ed..f86a0e8 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -39,7 +39,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
- ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS DISTINCT <replaceable class="PARAMETER">number</replaceable>
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
+ ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
ADD <replaceable class="PARAMETER">table_constraint</replaceable>
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
@@ -158,14 +159,21 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</varlistentry>
<varlistentry>
- <term><literal>SET STATISTICS DISTINCT</literal></term>
+ <term><literal>SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</term></literal>
+ <term><literal>RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] )</literal>
<listitem>
<para>
- This form overrides the number-of-distinct-values estimate made by
- subsequent <xref linkend="sql-analyze" endterm="sql-analyze-title">
- operations. When set to a positive value, <command>ANALYZE</> will
- assume that the column contains exactly the specified number of distinct
- nonnull values. When set to a negative value, which must be greater
+ This form sets or resets attribute-level options. Currently, the only
+ define attribute-level options are <literal>n_distinct</> and
+ <literal>n_distinct_inherited</>, which override the
+ number-of-distinct-values estimate made by subsequent
+ <xref linkend="sql-analyze" endterm="sql-analyze-title">
+ operations. <literal>n_distinct</> affects the statistics for the table
+ itself, while <literal>n_distinct_inherited</> affects the statistics
+ gathered for the table and its inheritance children. When set to a
+ positive value, <command>ANALYZE</> will assume that the column contains
+ exactly the specified number of distinct nonnull values. When set to a
+ negative value, which must be greater
than or equal to -1, <command>ANALYZE</> will assume that the number of
distinct nonnull values in the column is linear in the size of the
table; the exact count is to be computed by multiplying the estimated
diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index e884768..6ad4892 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -173,7 +173,7 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> [ ( <re
with the largest possible statistics target. If this inaccuracy leads to
bad query plans, a more accurate value can be determined manually and then
installed with
- <command>ALTER TABLE ... ALTER COLUMN ... SET STATISTICS DISTINCT</>
+ <command>ALTER TABLE ... ALTER COLUMN ... SET (n_distinct = ...)</>
(see <xref linkend="sql-altertable" endterm="sql-altertable-title">).
</para>
</refsect1>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 24480e3..94fbeb3 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -24,6 +24,7 @@
#include "commands/tablespace.h"
#include "nodes/makefuncs.h"
#include "utils/array.h"
+#include "utils/attoptcache.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -196,6 +197,22 @@ static relopt_real realRelOpts[] =
},
-1, 0.0, DBL_MAX
},
+ {
+ {
+ "n_distinct",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
+ RELOPT_KIND_ATTRIBUTE
+ },
+ 0, -1.0, DBL_MAX
+ },
+ {
+ {
+ "n_distinct_inherited",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
+ RELOPT_KIND_ATTRIBUTE
+ },
+ 0, -1.0, DBL_MAX
+ },
/* list terminator */
{{NULL}}
};
@@ -1187,6 +1204,37 @@ index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
}
/*
+ * Option parser for attribute reloptions
+ */
+bytea *
+attribute_reloptions(Datum reloptions, bool validate)
+{
+ relopt_value *options;
+ AttributeOpts *aopts;
+ int numoptions;
+ static const relopt_parse_elt tab[] = {
+ {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
+ {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
+ };
+
+ options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
+ &numoptions);
+
+ /* if none set, we're done */
+ if (numoptions == 0)
+ return NULL;
+
+ aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
+
+ fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
+ validate, tab, lengthof(tab));
+
+ pfree(options);
+
+ return (bytea *) aopts;
+}
+
+/*
* Option parser for tablespace reloptions
*/
bytea *
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index b54520f..5775a22 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -338,8 +338,6 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attstattarget != attr2->attstattarget)
return false;
- if (attr1->attdistinct != attr2->attdistinct)
- return false;
if (attr1->attlen != attr2->attlen)
return false;
if (attr1->attndims != attr2->attndims)
@@ -362,7 +360,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
- /* attacl is ignored, since it's not even present... */
+ /* attacl and attoptions are not even present... */
}
if (tupdesc1->constr != NULL)
@@ -467,7 +465,6 @@ TupleDescInitEntry(TupleDesc desc,
MemSet(NameStr(att->attname), 0, NAMEDATALEN);
att->attstattarget = -1;
- att->attdistinct = 0;
att->attcacheoff = -1;
att->atttypmod = typmod;
@@ -479,7 +476,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
- /* attacl is not set because it's not present in tupledescs */
+ /* attacl and attoptions are not present in tupledescs */
tuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(oidtypeid),
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9e7e68b..ad88460 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -737,7 +737,6 @@ DefineAttr(char *name, char *type, int attnum)
}
attrtypes[attnum]->attstattarget = -1;
- attrtypes[attnum]->attdistinct = 0;
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
attrtypes[attnum]->attislocal = true;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index dc68238..fb3ecca 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -200,7 +200,8 @@ foreach my $catname ( @{ $catalogs->{names} } )
# Store schemapg entries for later.
$row = emit_schemapg_row($row, grep { $bki_attr{$_} eq 'bool' } @attnames);
push @{ $schemapg_entries{$table_name} },
- '{ ' . join(', ', map $row->{$_}, @attnames) . ' }';
+ '{ ' . join(', ', grep { defined $_ }
+ map $row->{$_}, @attnames) . ' }';
}
# Generate entries for system attributes.
@@ -351,14 +352,14 @@ sub emit_pgattr_row
# Add in default values for pg_attribute
my %PGATTR_DEFAULTS = (
- attdistinct => '0',
attcacheoff => '-1',
atttypmod => '-1',
atthasdef => 'f',
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
- attacl => '_null_'
+ attacl => '_null_',
+ attoptions => '_null_'
);
return {%PGATTR_DEFAULTS, %row};
}
@@ -384,7 +385,8 @@ sub emit_schemapg_row
$row->{attname} = q|{"| . $row->{attname} . q|"}|;
$row->{attstorage} = q|'| . $row->{attstorage} . q|'|;
$row->{attalign} = q|'| . $row->{attalign} . q|'|;
- $row->{attacl} = q|{ 0 }|;
+ delete $row->{attacl};
+ delete $row->{attoptions};
# Expand booleans from 'f'/'t' to 'false'/'true'.
# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 81cd8c5..ae37798 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -117,39 +117,39 @@ Oid binary_upgrade_next_toast_relfilenode = InvalidOid;
*/
static FormData_pg_attribute a1 = {
- 0, {"ctid"}, TIDOID, 0, 0, sizeof(ItemPointerData),
+ 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
- false, 'p', 's', true, false, false, true, 0, {0}
+ false, 'p', 's', true, false, false, true, 0
};
static FormData_pg_attribute a2 = {
- 0, {"oid"}, OIDOID, 0, 0, sizeof(Oid),
+ 0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a3 = {
- 0, {"xmin"}, XIDOID, 0, 0, sizeof(TransactionId),
+ 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a4 = {
- 0, {"cmin"}, CIDOID, 0, 0, sizeof(CommandId),
+ 0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a5 = {
- 0, {"xmax"}, XIDOID, 0, 0, sizeof(TransactionId),
+ 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
static FormData_pg_attribute a6 = {
- 0, {"cmax"}, CIDOID, 0, 0, sizeof(CommandId),
+ 0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
/*
@@ -159,9 +159,9 @@ static FormData_pg_attribute a6 = {
* used in SQL.
*/
static FormData_pg_attribute a7 = {
- 0, {"tableoid"}, OIDOID, 0, 0, sizeof(Oid),
+ 0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
TableOidAttributeNumber, 0, -1, -1,
- true, 'p', 'i', true, false, false, true, 0, {0}
+ true, 'p', 'i', true, false, false, true, 0
};
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -482,13 +482,12 @@ CheckAttributeType(const char *attname, Oid atttypid)
* Construct and insert a new tuple in pg_attribute.
*
* Caller has already opened and locked pg_attribute. new_attribute is the
- * attribute to insert (but we ignore its attacl, if indeed it has one).
+ * attribute to insert (but we ignore attacl and attoptions, which are always
+ * initialized to NULL).
*
* indstate is the index state for CatalogIndexInsert. It can be passed as
* NULL, in which case we'll fetch the necessary info. (Don't do this when
* inserting multiple attributes, because it's a tad more expensive.)
- *
- * We always initialize attacl to NULL (i.e., default permissions).
*/
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
@@ -507,7 +506,6 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attribute->attname);
values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attribute->attstattarget);
- values[Anum_pg_attribute_attdistinct - 1] = Float4GetDatum(new_attribute->attdistinct);
values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attribute->attnum);
values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attribute->attndims);
@@ -522,8 +520,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
- /* start out with empty permissions */
+ /* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
+ nulls[Anum_pg_attribute_attoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
@@ -578,7 +577,6 @@ AddNewAttributeTuples(Oid new_rel_oid,
attr->attrelid = new_rel_oid;
/* Make sure these are OK, too */
attr->attstattarget = -1;
- attr->attdistinct = 0;
attr->attcacheoff = -1;
InsertPgAttributeTuple(rel, attr, indstate);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 1e3098e..5da720c 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -203,7 +203,6 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attnum = i + 1;
to->attstattarget = -1;
- to->attdistinct = 0;
to->attcacheoff = -1;
to->attnotnull = false;
to->atthasdef = false;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 91f3719..9062c15 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -39,6 +39,7 @@
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
+#include "utils/attoptcache.h"
#include "utils/datum.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
@@ -493,6 +494,8 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
for (i = 0; i < attr_cnt; i++)
{
VacAttrStats *stats = vacattrstats[i];
+ AttributeOpts *aopt =
+ get_attribute_options(onerel->rd_id, stats->attr->attnum);
stats->rows = rows;
stats->tupDesc = onerel->rd_att;
@@ -501,9 +504,17 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
numrows,
totalrows);
- /* If attdistinct is set, override with that value */
- if (stats->attr->attdistinct != 0)
- stats->stadistinct = stats->attr->attdistinct;
+ /*
+ * If the appropriate flavor of the n_distinct option is
+ * specified, override with the corresponding value.
+ */
+ if (aopt != NULL)
+ {
+ float8 n_distinct =
+ inh ? aopt->n_distinct_inherited : aopt->n_distinct;
+ if (n_distinct != 0.0)
+ stats->stadistinct = n_distinct;
+ }
MemoryContextResetAndDeleteChildren(col_context);
}
@@ -751,6 +762,9 @@ compute_index_stats(Relation onerel, double totalrows,
for (i = 0; i < attr_cnt; i++)
{
VacAttrStats *stats = thisdata->vacattrstats[i];
+ AttributeOpts *aopt =
+ get_attribute_options(stats->attr->attrelid,
+ stats->attr->attnum);
stats->exprvals = exprvals + i;
stats->exprnulls = exprnulls + i;
@@ -759,9 +773,15 @@ compute_index_stats(Relation onerel, double totalrows,
ind_fetch_func,
numindexrows,
totalindexrows);
- /* If attdistinct is set, override with that value */
- if (stats->attr->attdistinct != 0)
- stats->stadistinct = stats->attr->attdistinct;
+
+ /*
+ * If the n_distinct option is specified, it overrides the
+ * above computation. For indices, we always use just
+ * n_distinct, not n_distinct_inherited.
+ */
+ if (aopt != NULL && aopt->n_distinct != 0.0)
+ stats->stadistinct = aopt->n_distinct;
+
MemoryContextResetAndDeleteChildren(col_context);
}
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 10f0a94..a24d8d1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -284,10 +284,8 @@ static void ATPrepSetStatistics(Relation rel, const char *colName,
Node *newValue);
static void ATExecSetStatistics(Relation rel, const char *colName,
Node *newValue);
-static void ATPrepSetDistinct(Relation rel, const char *colName,
- Node *newValue);
-static void ATExecSetDistinct(Relation rel, const char *colName,
- Node *newValue);
+static void ATExecSetOptions(Relation rel, const char *colName,
+ Node *options, bool isReset);
static void ATExecSetStorage(Relation rel, const char *colName,
Node *newValue);
static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
@@ -2425,15 +2423,15 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATPrepSetStatistics(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
break;
- case AT_SetDistinct: /* ALTER COLUMN SET STATISTICS DISTINCT */
+ case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
+ case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
+ ATSimplePermissionsRelationOrIndex(rel);
ATSimpleRecursion(wqueue, rel, cmd, recurse);
- /* Performs own permission checks */
- ATPrepSetDistinct(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false);
- ATSimpleRecursion(wqueue, rel, cmd, recurse);
+ /* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_COL_ATTRS;
break;
@@ -2644,8 +2642,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
ATExecSetStatistics(rel, cmd->name, cmd->def);
break;
- case AT_SetDistinct: /* ALTER COLUMN SET STATISTICS DISTINCT */
- ATExecSetDistinct(rel, cmd->name, cmd->def);
+ case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
+ ATExecSetOptions(rel, cmd->name, cmd->def, false);
+ break;
+ case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
+ ATExecSetOptions(rel, cmd->name, cmd->def, true);
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATExecSetStorage(rel, cmd->name, cmd->def);
@@ -3682,7 +3683,6 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
namestrcpy(&(attribute.attname), colDef->colname);
attribute.atttypid = typeOid;
attribute.attstattarget = (newattnum > 0) ? -1 : 0;
- attribute.attdistinct = 0;
attribute.attlen = tform->typlen;
attribute.attcacheoff = -1;
attribute.atttypmod = typmod;
@@ -4151,68 +4151,24 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue)
heap_close(attrelation, RowExclusiveLock);
}
-/*
- * ALTER TABLE ALTER COLUMN SET STATISTICS DISTINCT
- */
-static void
-ATPrepSetDistinct(Relation rel, const char *colName, Node *newValue)
-{
- /*
- * We do our own permission checking because (a) we want to allow SET
- * DISTINCT on indexes (for expressional index columns), and (b) we want
- * to allow SET DISTINCT on system catalogs without requiring
- * allowSystemTableMods to be turned on.
- */
- if (rel->rd_rel->relkind != RELKIND_RELATION &&
- rel->rd_rel->relkind != RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or index",
- RelationGetRelationName(rel))));
-
- /* Permissions checks */
- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
-}
-
static void
-ATExecSetDistinct(Relation rel, const char *colName, Node *newValue)
+ATExecSetOptions(Relation rel, const char *colName, Node *options,
+ bool isReset)
{
- float4 newdistinct;
Relation attrelation;
- HeapTuple tuple;
+ HeapTuple tuple,
+ newtuple;
Form_pg_attribute attrtuple;
-
- switch (nodeTag(newValue))
- {
- case T_Integer:
- newdistinct = intVal(newValue);
- break;
- case T_Float:
- newdistinct = floatVal(newValue);
- break;
- default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(newValue));
- newdistinct = 0; /* keep compiler quiet */
- break;
- }
-
- /*
- * Limit ndistinct to sane values
- */
- if (newdistinct < -1.0)
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("number of distinct values %g is too low",
- newdistinct)));
- }
+ Datum datum,
+ newOptions;
+ bool isnull;
+ Datum repl_val[Natts_pg_attribute];
+ bool repl_null[Natts_pg_attribute];
+ bool repl_repl[Natts_pg_attribute];
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
- tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
+ tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
@@ -4227,14 +4183,32 @@ ATExecSetDistinct(Relation rel, const char *colName, Node *newValue)
errmsg("cannot alter system column \"%s\"",
colName)));
- attrtuple->attdistinct = newdistinct;
-
- simple_heap_update(attrelation, &tuple->t_self, tuple);
+ /* Generate new proposed attoptions (text array) */
+ Assert(IsA(options, List));
+ datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+ newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
+ (List *) options, NULL, NULL, false,
+ isReset);
+ /* Validate new options */
+ (void) attribute_reloptions(newOptions, true);
- /* keep system catalog indexes current */
- CatalogUpdateIndexes(attrelation, tuple);
+ /* Build new tuple. */
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
+ if (newOptions != (Datum) 0)
+ repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
+ else
+ repl_null[Anum_pg_attribute_attoptions - 1] = true;
+ repl_repl[Anum_pg_attribute_attoptions - 1] = true;
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
+ repl_val, repl_null, repl_repl);
+ ReleaseSysCache(tuple);
- heap_freetuple(tuple);
+ /* Update system catalog. */
+ simple_heap_update(attrelation, &newtuple->t_self, newtuple);
+ CatalogUpdateIndexes(attrelation, newtuple);
+ heap_freetuple(newtuple);
heap_close(attrelation, RowExclusiveLock);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8201f29..3d3ae79 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1645,13 +1645,22 @@ alter_table_cmd:
n->def = (Node *) makeInteger($6);
$$ = (Node *)n;
}
- /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STATISTICS DISTINCT <NumericOnly> */
- | ALTER opt_column ColId SET STATISTICS DISTINCT NumericOnly
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
+ | ALTER opt_column ColId SET reloptions
{
AlterTableCmd *n = makeNode(AlterTableCmd);
- n->subtype = AT_SetDistinct;
+ n->subtype = AT_SetOptions;
n->name = $3;
- n->def = (Node *) $7;
+ n->def = (Node *) $5;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET ( column_parameter = value [, ... ] ) */
+ | ALTER opt_column ColId RESET reloptions
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_ResetOptions;
+ n->name = $3;
+ n->def = (Node *) $5;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile
index 83716a2..3e57964 100644
--- a/src/backend/utils/cache/Makefile
+++ b/src/backend/utils/cache/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/utils/cache
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = catcache.o inval.o plancache.o relcache.o \
+OBJS = attoptcache.o catcache.o inval.o plancache.o relcache.o \
spccache.o syscache.o lsyscache.o typcache.o ts_cache.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
new file mode 100644
index 0000000..65385b8
--- /dev/null
+++ b/src/backend/utils/cache/attoptcache.c
@@ -0,0 +1,181 @@
+/*-------------------------------------------------------------------------
+ *
+ * attoptcache.c
+ * Attribute options cache management.
+ *
+ * Attribute options are cached separately from the fixed-size portion of
+ * pg_attribute entries, which are handled by the relcache.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "catalog/pg_attribute.h"
+#include "utils/attoptcache.h"
+#include "utils/catcache.h"
+#include "utils/hsearch.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/* Hash table for informations about each attribute's options */
+static HTAB *AttoptCacheHash = NULL;
+
+/* attrelid and attnum form the lookup key, and must appear first */
+typedef struct
+{
+ Oid attrelid;
+ int attnum;
+} AttoptCacheKey;
+
+typedef struct
+{
+ AttoptCacheKey key; /* lookup key - must be first */
+ AttributeOpts *opts; /* options, or NULL if none */
+} AttoptCacheEntry;
+
+
+/*
+ * InvalidateAttoptCacheCallback
+ * Flush all cache entries when pg_attribute is updated.
+ *
+ * When pg_attribute is updated, we must flush the cache entry at least
+ * for that attribute. Currently, we just flush them all. Since attribute
+ * options are not currently used in performance-critical paths (such as
+ * query execution), this seems OK.
+ */
+static void
+InvalidateAttoptCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
+{
+ HASH_SEQ_STATUS status;
+ AttoptCacheEntry *attopt;
+
+ hash_seq_init(&status, AttoptCacheHash);
+ while ((attopt = (AttoptCacheEntry *) hash_seq_search(&status)) != NULL)
+ {
+ if (attopt->opts)
+ pfree(attopt->opts);
+ if (hash_search(AttoptCacheHash,
+ (void *) &attopt->key,
+ HASH_REMOVE,
+ NULL) == NULL)
+ elog(ERROR, "hash table corrupted");
+ }
+}
+
+/*
+ * InitializeAttoptCache
+ * Initialize the tablespace cache.
+ */
+static void
+InitializeAttoptCache(void)
+{
+ HASHCTL ctl;
+
+ /* Initialize the hash table. */
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(AttoptCacheKey);
+ ctl.entrysize = sizeof(AttoptCacheEntry);
+ ctl.hash = tag_hash;
+ AttoptCacheHash =
+ hash_create("Attopt cache", 256, &ctl,
+ HASH_ELEM | HASH_FUNCTION);
+
+ /* Make sure we've initialized CacheMemoryContext. */
+ if (!CacheMemoryContext)
+ CreateCacheMemoryContext();
+
+ /* Watch for invalidation events. */
+ CacheRegisterSyscacheCallback(ATTNUM,
+ InvalidateAttoptCacheCallback,
+ (Datum) 0);
+}
+
+/*
+ * get_attribute_options
+ * Fetch attribute options for a specified table OID.
+ */
+AttributeOpts *
+get_attribute_options(Oid attrelid, int attnum)
+{
+ AttoptCacheKey key;
+ AttoptCacheEntry *attopt;
+ AttributeOpts *result;
+ HeapTuple tp;
+
+ /* Find existing cache entry, if any. */
+ if (!AttoptCacheHash)
+ InitializeAttoptCache();
+ memset(&key, 0, sizeof(key)); /* make sure any padding bits are unset */
+ key.attrelid = attrelid;
+ key.attnum = attnum;
+ attopt =
+ (AttoptCacheEntry *) hash_search(AttoptCacheHash,
+ (void *) &key,
+ HASH_FIND,
+ NULL);
+
+ /* Not found in Attopt cache. Construct new cache entry. */
+ if (!attopt)
+ {
+ AttributeOpts *opts;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(attrelid),
+ Int16GetDatum(attnum),
+ 0, 0);
+
+ /*
+ * If we don't find a valid HeapTuple, it must mean someone has
+ * managed to request attribute details for a non-existent attribute.
+ * We treat that case as if no options were specified.
+ */
+ if (!HeapTupleIsValid(tp))
+ opts = NULL;
+ else
+ {
+ Datum datum;
+ bool isNull;
+
+ datum = SysCacheGetAttr(ATTNUM,
+ tp,
+ Anum_pg_attribute_attoptions,
+ &isNull);
+ if (isNull)
+ opts = NULL;
+ else
+ {
+ bytea *bytea_opts = attribute_reloptions(datum, false);
+ opts = MemoryContextAlloc(CacheMemoryContext,
+ VARSIZE(bytea_opts));
+ memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
+ }
+ ReleaseSysCache(tp);
+ }
+
+ /*
+ * It's important to create the actual cache entry only after
+ * reading pg_attribute, since the read could cause a cache flush.
+ */
+ attopt = (AttoptCacheEntry *) hash_search(AttoptCacheHash,
+ (void *) &key,
+ HASH_ENTER,
+ NULL);
+ attopt->opts = opts;
+ }
+
+ /* Return results in caller's memory context. */
+ if (attopt->opts == NULL)
+ return NULL;
+ result = palloc(VARSIZE(attopt->opts));
+ memcpy(result, attopt->opts, VARSIZE(attopt->opts));
+ return result;
+}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 053e32a..b872c19 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5056,7 +5056,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_atttypname;
int i_atttypmod;
int i_attstattarget;
- int i_attdistinct;
int i_attstorage;
int i_typstorage;
int i_attnotnull;
@@ -5065,6 +5064,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_attlen;
int i_attalign;
int i_attislocal;
+ int i_attoptions;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -5104,13 +5104,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
if (g_fout->remoteVersion >= 80500)
{
- /* attdistinct is new in 8.5 */
+ /* attoptions is new in 8.5 */
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "a.attstattarget, a.attdistinct, "
- "a.attstorage, t.typstorage, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(attoptions, ', ') AS attoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -5122,11 +5122,11 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* need left join here to not fail on dropped columns ... */
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "a.attstattarget, 0 AS attdistinct, "
- "a.attstorage, t.typstorage, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -5142,12 +5142,12 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
* explicitly set or was just a default.
*/
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
- "-1 AS attstattarget, 0 AS attdistinct, "
- "a.attstorage, "
+ "-1 AS attstattarget, a.attstorage, "
"t.typstorage, a.attnotnull, a.atthasdef, "
"false AS attisdropped, a.attlen, "
"a.attalign, false AS attislocal, "
- "format_type(t.oid,a.atttypmod) AS atttypname "
+ "format_type(t.oid,a.atttypmod) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_attribute a LEFT JOIN pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::oid "
@@ -5159,12 +5159,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* format_type not available before 7.1 */
appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, "
- "-1 AS attstattarget, 0 AS attdistinct, "
+ "-1 AS attstattarget, "
"attstorage, attstorage AS typstorage, "
"attnotnull, atthasdef, false AS attisdropped, "
"attlen, attalign, "
"false AS attislocal, "
- "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname "
+ "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
+ "'' AS attoptions "
"FROM pg_attribute a "
"WHERE attrelid = '%u'::oid "
"AND attnum > 0::int2 "
@@ -5182,7 +5183,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_atttypname = PQfnumber(res, "atttypname");
i_atttypmod = PQfnumber(res, "atttypmod");
i_attstattarget = PQfnumber(res, "attstattarget");
- i_attdistinct = PQfnumber(res, "attdistinct");
i_attstorage = PQfnumber(res, "attstorage");
i_typstorage = PQfnumber(res, "typstorage");
i_attnotnull = PQfnumber(res, "attnotnull");
@@ -5191,13 +5191,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attlen = PQfnumber(res, "attlen");
i_attalign = PQfnumber(res, "attalign");
i_attislocal = PQfnumber(res, "attislocal");
+ i_attoptions = PQfnumber(res, "attoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypnames = (char **) malloc(ntups * sizeof(char *));
tbinfo->atttypmod = (int *) malloc(ntups * sizeof(int));
tbinfo->attstattarget = (int *) malloc(ntups * sizeof(int));
- tbinfo->attdistinct = (float4 *) malloc(ntups * sizeof(float4));
tbinfo->attstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
@@ -5206,6 +5206,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
+ tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
@@ -5223,8 +5224,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
- tbinfo->attdistinct[j] = strtod(PQgetvalue(res, j, i_attdistinct),
- (char **) NULL);
tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
@@ -5232,6 +5231,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
+ tbinfo->attoptions[j] = strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@@ -10798,22 +10798,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
/*
- * Dump per-column ndistinct information. We only issue an ALTER
- * TABLE statement if the attdistinct entry for this column is
- * non-zero (i.e. it's not the default value)
- */
- if (tbinfo->attdistinct[j] != 0 &&
- !tbinfo->attisdropped[j])
- {
- appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
- fmtId(tbinfo->dobj.name));
- appendPQExpBuffer(q, "ALTER COLUMN %s ",
- fmtId(tbinfo->attnames[j]));
- appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n",
- tbinfo->attdistinct[j]);
- }
-
- /*
* Dump per-column storage information. The statement is only
* dumped if the storage has been changed from the type's default.
*/
@@ -10850,6 +10834,19 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
storage);
}
}
+
+ /*
+ * Dump per-column attributes.
+ */
+ if (tbinfo->attoptions[j] && tbinfo->attoptions[j][0] != '\0')
+ {
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "ALTER COLUMN %s ",
+ fmtId(tbinfo->attnames[j]));
+ appendPQExpBuffer(q, "SET (%s);\n",
+ tbinfo->attoptions[j]);
+ }
}
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 77786a6..f193bdd 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -244,13 +244,13 @@ typedef struct _tableInfo
char **atttypnames; /* attribute type names */
int *atttypmod; /* type-specific type modifiers */
int *attstattarget; /* attribute statistics targets */
- float4 *attdistinct; /* override ndistinct calculation */
char *attstorage; /* attribute storage scheme */
char *typstorage; /* type storage scheme */
bool *attisdropped; /* true if attr is dropped; don't dump it */
int *attlen; /* attribute length, used by binary_upgrade */
char *attalign; /* attribute align, used by binary_upgrade */
bool *attislocal; /* true if attr has local definition */
+ char **attoptions; /* per-attribute options */
/*
* Note: we need to store per-attribute notnull, default, and constraint
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2c784aa..cb2ae9a 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -992,17 +992,6 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(list_COLUMNSET);
}
- else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
- pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
- pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
- pg_strcasecmp(prev2_wd, "SET") == 0 &&
- pg_strcasecmp(prev_wd, "STATISTICS") == 0)
- {
- static const char *const list_COLUMNSETSTATS[] =
- {"DISTINCT", NULL};
-
- COMPLETE_WITH_LIST(list_COLUMNSETSTATS);
- }
else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
(pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a0087bc..db21390 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -40,7 +40,8 @@ typedef enum relopt_kind
RELOPT_KIND_HASH = (1 << 3),
RELOPT_KIND_GIN = (1 << 4),
RELOPT_KIND_GIST = (1 << 5),
- RELOPT_KIND_TABLESPACE = (1 << 6),
+ RELOPT_KIND_ATTRIBUTE = (1 << 6),
+ RELOPT_KIND_TABLESPACE = (1 << 7),
/* if you add a new kind, make sure you update "last_default" too */
RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_TABLESPACE,
/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
@@ -266,6 +267,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
bool validate);
+extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
#endif /* RELOPTIONS_H */
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 6fdc6b8..078cae4 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -57,13 +57,6 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
int4 attstattarget;
/*
- * attdistinct, if nonzero, is a user-specified ndistinct value to be used
- * instead of estimating the number of distinct values during ANALYZE.
- * Nonzero values have the same meanings as for pg_statistic.stadistinct.
- */
- float4 attdistinct;
-
- /*
* attlen is a copy of the typlen field from pg_type for this attribute.
* See atttypid comments above.
*/
@@ -157,6 +150,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* Column-level access permissions */
aclitem attacl[1];
+
+ /* Column-level options */
+ text attoptions[1];
} FormData_pg_attribute;
/*
@@ -185,21 +181,21 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
#define Anum_pg_attribute_attstattarget 4
-#define Anum_pg_attribute_attdistinct 5
-#define Anum_pg_attribute_attlen 6
-#define Anum_pg_attribute_attnum 7
-#define Anum_pg_attribute_attndims 8
-#define Anum_pg_attribute_attcacheoff 9
-#define Anum_pg_attribute_atttypmod 10
-#define Anum_pg_attribute_attbyval 11
-#define Anum_pg_attribute_attstorage 12
-#define Anum_pg_attribute_attalign 13
-#define Anum_pg_attribute_attnotnull 14
-#define Anum_pg_attribute_atthasdef 15
-#define Anum_pg_attribute_attisdropped 16
-#define Anum_pg_attribute_attislocal 17
-#define Anum_pg_attribute_attinhcount 18
-#define Anum_pg_attribute_attacl 19
+#define Anum_pg_attribute_attlen 5
+#define Anum_pg_attribute_attnum 6
+#define Anum_pg_attribute_attndims 7
+#define Anum_pg_attribute_attcacheoff 8
+#define Anum_pg_attribute_atttypmod 9
+#define Anum_pg_attribute_attbyval 10
+#define Anum_pg_attribute_attstorage 11
+#define Anum_pg_attribute_attalign 12
+#define Anum_pg_attribute_attnotnull 13
+#define Anum_pg_attribute_atthasdef 14
+#define Anum_pg_attribute_attisdropped 15
+#define Anum_pg_attribute_attislocal 16
+#define Anum_pg_attribute_attinhcount 17
+#define Anum_pg_attribute_attacl 18
+#define Anum_pg_attribute_attoptions 19
/* ----------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0d3867b..0a150f7 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1101,7 +1101,8 @@ typedef enum AlterTableType
AT_DropNotNull, /* alter column drop not null */
AT_SetNotNull, /* alter column set not null */
AT_SetStatistics, /* alter column set statistics */
- AT_SetDistinct, /* alter column set statistics distinct */
+ AT_SetOptions, /* alter column set ( options ) */
+ AT_ResetOptions, /* alter column reset ( options ) */
AT_SetStorage, /* alter column set storage */
AT_DropColumn, /* drop column */
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
diff --git a/src/include/utils/attoptcache.h b/src/include/utils/attoptcache.h
new file mode 100644
index 0000000..0d723fe
--- /dev/null
+++ b/src/include/utils/attoptcache.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * attoptcache.h
+ * Attribute options cache.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SPCCACHE_H
+#define SPCCACHE_H
+
+/*
+ * Attribute options.
+ */
+typedef struct AttributeOpts
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ float8 n_distinct;
+ float8 n_distinct_inherited;
+} AttributeOpts;
+
+AttributeOpts *get_attribute_options(Oid spcid, int attnum);
+
+#endif /* SPCCACHE_H */
On Wed, Jan 20, 2010 at 19:51, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Jan 19, 2010 at 10:51 AM, Alex Hunsaker <badalex@gmail.com> wrote:
But yes, lets keep it simple for now.
OK. Updated patch attached. Changes:
- Incorporate your previous review patch.
- Omit attacl and attoptions from hardcoded relation descriptor
initializers so the whole thing still builds.
Seems to me a comment about the above might be nice. Something like
/* Things after here are should always be default null */ in
pg_attribute.h ?
Other than the below it looks good to me.
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 2426,2437 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
ATSimplePermissionsRelationOrIndex(rel);
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
pass = AT_PASS_COL_ATTRS;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false);
! /* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_COL_ATTRS;
break;
--- 2426,2437 ----
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
ATSimplePermissionsRelationOrIndex(rel);
! /* This command never recurses */
pass = AT_PASS_COL_ATTRS;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false);
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
/* No command-specific prep needed */
pass = AT_PASS_COL_ATTRS;
break;
On Thu, Jan 21, 2010 at 12:57 AM, Alex Hunsaker <badalex@gmail.com> wrote:
Seems to me a comment about the above might be nice. Something like
/* Things after here are should always be default null */ in
pg_attribute.h ?
Well... that wouldn't actually be a correct summary, so no. The point
is that variable-length fields are not used in tuple descriptors.
I'll add a comment about that.
Other than the below it looks good to me.
Oh, dear. OK, I'll fix that.
...Robert
On Thu, Jan 21, 2010 at 07:30, Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Jan 21, 2010 at 12:57 AM, Alex Hunsaker <badalex@gmail.com> wrote:
Seems to me a comment about the above might be nice. Something like
/* Things after here are should always be default null */ in
pg_attribute.h ?Well... that wouldn't actually be a correct summary, so no. The point
is that variable-length fields are not used in tuple descriptors.
I'll add a comment about that.
Yes that sounds much better, I was struggling to find the words. What
I was trying to express was something along the lines of you cant have
a non null value after these because you cant statically initialize
them in genbki.
On Thu, Jan 21, 2010 at 10:24 AM, Alex Hunsaker <badalex@gmail.com> wrote:
On Thu, Jan 21, 2010 at 07:30, Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Jan 21, 2010 at 12:57 AM, Alex Hunsaker <badalex@gmail.com> wrote:
Seems to me a comment about the above might be nice. Something like
/* Things after here are should always be default null */ in
pg_attribute.h ?Well... that wouldn't actually be a correct summary, so no. The point
is that variable-length fields are not used in tuple descriptors.
I'll add a comment about that.Yes that sounds much better, I was struggling to find the words. What
I was trying to express was something along the lines of you cant have
a non null value after these because you cant statically initialize
them in genbki.
Committed. Thanks for the very thorough review.
...Robert