diff options
author | Alvaro Herrera | 2009-02-02 19:31:40 +0000 |
---|---|---|
committer | Alvaro Herrera | 2009-02-02 19:31:40 +0000 |
commit | efa0b99af9e8182643966f925216d116df981bb8 (patch) | |
tree | b05ef486524c557f109ff29fcdaa0777491e0491 | |
parent | 61b7d880a263ed9594b3e443331b49337e4981a6 (diff) |
Allow reloption names to have qualifiers, initially supporting a TOAST
qualifier, and add support for this in pg_dump.
This allows TOAST tables to have user-defined fillfactor, and will also
enable us to move the autovacuum parameters to reloptions without taking
away the possibility of setting values for TOAST tables.
27 files changed, 425 insertions, 100 deletions
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 8b502e6a38..0fb0cd39c7 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -231,7 +231,8 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re <listitem> <para> The name of an index-method-specific storage parameter. See - below for details. + <xref linkend="sql-createindex-storage-parameters" endterm="sql-createindex-storage-parameters-title"> + for details. </para> </listitem> </varlistentry> @@ -265,7 +266,8 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re <para> The <literal>WITH</> clause can specify <firstterm>storage parameters</> for indexes. Each index method can have its own set of allowed storage - parameters. The built-in index methods all accept a single parameter: + parameters. The <literal>B-tree</literal>, <literal>hash</literal> and + <literal>GiST</literal> built-in index methods all accept a single parameter: </para> <variablelist> diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index bfbd25c4c8..44148b989e 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -690,8 +690,8 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: for tables, and for indexes associated with a <literal>UNIQUE</literal> or <literal>PRIMARY KEY</literal> constraint. Storage parameters for indexes are documented in <xref linkend="SQL-CREATEINDEX" - endterm="sql-createindex-title">. The only storage parameter currently - available for tables is: + endterm="sql-createindex-title">. The storage parameters currently + available for tables are: </para> <variablelist> @@ -714,6 +714,16 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: </listitem> </varlistentry> + <varlistentry> + <term><literal>TOAST.FILLFACTOR</literal></term> + <listitem> + <para> + Same as above, for the supplementary storage table, if any; see + <xref linkend="storage-toast">. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect2> diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index f9d36ee92b..e16f127a78 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -390,8 +390,10 @@ add_string_reloption(int kind, char *name, char *desc, char *default_val, } /* - * Transform a relation options list (list of DefElem) into the text array - * format that is kept in pg_class.reloptions. + * Transform a relation options list (list of ReloptElem) into the text array + * format that is kept in pg_class.reloptions, including only those options + * that are in the passed namespace. The output values do not include the + * namespace. * * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing @@ -402,14 +404,17 @@ add_string_reloption(int kind, char *name, char *desc, char *default_val, * in the list (it will be or has been handled by interpretOidsOption()). * * Note that this is not responsible for determining whether the options - * are valid. + * are valid, but it does check that namespaces for all the options given are + * listed in validnsps. The NULL namespace is always valid and needs not be + * explicitely listed. Passing a NULL pointer means that only the NULL + * namespace is valid. * * Both oldOptions and the result are text arrays (or NULL for "default"), * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum -transformRelOptions(Datum oldOptions, List *defList, - bool ignoreOids, bool isReset) +transformRelOptions(Datum oldOptions, List *defList, char *namspace, + char *validnsps[], bool ignoreOids, bool isReset) { Datum result; ArrayBuildState *astate; @@ -444,11 +449,23 @@ transformRelOptions(Datum oldOptions, List *defList, /* Search for a match in defList */ foreach(cell, defList) { - DefElem *def = lfirst(cell); - int kw_len = strlen(def->defname); + ReloptElem *def = lfirst(cell); + int kw_len; + /* ignore if not in the same namespace */ + if (namspace == NULL) + { + if (def->nmspc != NULL) + continue; + } + else if (def->nmspc == NULL) + continue; + else if (pg_strcasecmp(def->nmspc, namspace) != 0) + continue; + + kw_len = strlen(def->optname); if (text_len > kw_len && text_str[kw_len] == '=' && - pg_strncasecmp(text_str, def->defname, kw_len) == 0) + pg_strncasecmp(text_str, def->optname, kw_len) == 0) break; } if (!cell) @@ -468,7 +485,8 @@ transformRelOptions(Datum oldOptions, List *defList, */ foreach(cell, defList) { - DefElem *def = lfirst(cell); + ReloptElem *def = lfirst(cell); + if (isReset) { @@ -483,22 +501,62 @@ transformRelOptions(Datum oldOptions, List *defList, const char *value; Size len; - if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0) + /* + * Error out if the namespace is not valid. A NULL namespace + * is always valid. + */ + if (def->nmspc != NULL) + { + bool valid = false; + int i; + + if (validnsps) + { + for (i = 0; validnsps[i]; i++) + { + if (pg_strcasecmp(def->nmspc, validnsps[i]) == 0) + { + valid = true; + break; + } + } + } + + if (!valid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized parameter namespace \"%s\"", + def->nmspc))); + } + + if (ignoreOids && pg_strcasecmp(def->optname, "oids") == 0) + continue; + + /* ignore if not in the same namespace */ + if (namspace == NULL) + { + if (def->nmspc != NULL) + continue; + } + else if (def->nmspc == NULL) + continue; + else if (pg_strcasecmp(def->nmspc, namspace) != 0) continue; /* - * Flatten the DefElem into a text string like "name=arg". If we - * have just "name", assume "name=true" is meant. + * Flatten the ReloptElem into a text string like "name=arg". If we + * have just "name", assume "name=true" is meant. Note: the + * namespace is not output. */ if (def->arg != NULL) - value = defGetString(def); + value = reloptGetString(def); else value = "true"; - len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value); + len = VARHDRSZ + strlen(def->optname) + 1 + strlen(value); /* +1 leaves room for sprintf's trailing null */ t = (text *) palloc(len + 1); SET_VARSIZE(t, len); - sprintf(VARDATA(t), "%s=%s", def->defname, value); + sprintf(VARDATA(t), "%s=%s", def->optname, value); astate = accumArrayResult(astate, PointerGetDatum(t), false, TEXTOID, @@ -944,7 +1002,7 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) } /* - * Parse options for heaps (and perhaps someday toast tables). + * Parse options for heaps and toast tables. */ bytea * heap_reloptions(char relkind, Datum reloptions, bool validate) diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index cd267849e5..7f93523da2 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -32,7 +32,8 @@ #include "utils/syscache.h" -static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid); +static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, + Datum reloptions); static bool needs_toast_table(Relation rel); @@ -46,7 +47,7 @@ static bool needs_toast_table(Relation rel); * to end with CommandCounterIncrement if it makes any changes. */ void -AlterTableCreateToastTable(Oid relOid) +AlterTableCreateToastTable(Oid relOid, Datum reloptions) { Relation rel; @@ -58,7 +59,7 @@ AlterTableCreateToastTable(Oid relOid) rel = heap_open(relOid, AccessExclusiveLock); /* create_toast_table does all the work */ - (void) create_toast_table(rel, InvalidOid, InvalidOid); + (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions); heap_close(rel, NoLock); } @@ -84,7 +85,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) relName))); /* create_toast_table does all the work */ - if (!create_toast_table(rel, toastOid, toastIndexOid)) + if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0)) elog(ERROR, "\"%s\" does not require a toast table", relName); @@ -100,7 +101,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool -create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid) +create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; @@ -183,10 +184,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid) else namespaceid = PG_TOAST_NAMESPACE; - /* - * XXX would it make sense to apply the master's reloptions to the toast - * table? Or maybe some toast-specific reloptions? - */ toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, @@ -199,7 +196,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid) true, 0, ONCOMMIT_NOOP, - (Datum) 0, + reloptions, true); /* make the toast relation visible, else index creation will fail */ diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index c7f464900c..e08873abe1 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -668,6 +668,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) TupleDesc OldHeapDesc, tupdesc; Oid OIDNewHeap; + Oid toastid; Relation OldHeap; HeapTuple tuple; Datum reloptions; @@ -726,7 +727,24 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ - AlterTableCreateToastTable(OIDNewHeap); + toastid = OldHeap->rd_rel->reltoastrelid; + reloptions = (Datum) 0; + if (OidIsValid(toastid)) + { + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(toastid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", toastid); + reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, + &isNull); + if (isNull) + reloptions = (Datum) 0; + } + AlterTableCreateToastTable(OIDNewHeap, reloptions); + + if (OidIsValid(toastid)) + ReleaseSysCache(tuple); heap_close(OldHeap, NoLock); diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 53ddaa785c..a5d2d55bda 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -55,24 +55,20 @@ case_translate_language_name(const char *input) } -/* - * Extract a string value (otherwise uninterpreted) from a DefElem. - */ -char * -defGetString(DefElem *def) +static char * +nodeGetString(Node *value, char *name) { - if (def->arg == NULL) + if (value == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("%s requires a parameter", - def->defname))); - switch (nodeTag(def->arg)) + errmsg("%s requires a parameter", name))); + switch (nodeTag(value)) { case T_Integer: { char *str = palloc(32); - snprintf(str, 32, "%ld", (long) intVal(def->arg)); + snprintf(str, 32, "%ld", (long) intVal(value)); return str; } case T_Float: @@ -81,20 +77,29 @@ defGetString(DefElem *def) * T_Float values are kept in string form, so this type cheat * works (and doesn't risk losing precision) */ - return strVal(def->arg); + return strVal(value); case T_String: - return strVal(def->arg); + return strVal(value); case T_TypeName: - return TypeNameToString((TypeName *) def->arg); + return TypeNameToString((TypeName *) value); case T_List: - return NameListToString((List *) def->arg); + return NameListToString((List *) value); default: - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg)); + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(value)); } return NULL; /* keep compiler quiet */ } /* + * Extract a string value (otherwise uninterpreted) from a DefElem. + */ +char * +defGetString(DefElem *def) +{ + return nodeGetString(def->arg, def->defname); +} + +/* * Extract a numeric value (actually double) from a DefElem. */ double @@ -120,25 +125,22 @@ defGetNumeric(DefElem *def) return 0; /* keep compiler quiet */ } -/* - * Extract a boolean value from a DefElem. - */ -bool -defGetBoolean(DefElem *def) +static bool +nodeGetBoolean(Node *value, char *name) { /* * If no parameter given, assume "true" is meant. */ - if (def->arg == NULL) + if (value == NULL) return true; /* * Allow 0, 1, "true", "false" */ - switch (nodeTag(def->arg)) + switch (nodeTag(value)) { case T_Integer: - switch (intVal(def->arg)) + switch (intVal(value)) { case 0: return false; @@ -151,7 +153,7 @@ defGetBoolean(DefElem *def) break; default: { - char *sval = defGetString(def); + char *sval = nodeGetString(value, name); if (pg_strcasecmp(sval, "true") == 0) return true; @@ -163,12 +165,20 @@ defGetBoolean(DefElem *def) } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("%s requires a Boolean value", - def->defname))); + errmsg("%s requires a Boolean value", name))); return false; /* keep compiler quiet */ } /* + * Extract a boolean value from a DefElem. + */ +bool +defGetBoolean(DefElem *def) +{ + return nodeGetBoolean(def->arg, def->defname); +} + +/* * Extract an int64 value from a DefElem. */ int64 @@ -305,15 +315,35 @@ defGetTypeLength(DefElem *def) return 0; /* keep compiler quiet */ } + +/* + * Extract a string value (otherwise uninterpreted) from a ReloptElem. + */ +char * +reloptGetString(ReloptElem *relopt) +{ + return nodeGetString(relopt->arg, relopt->optname); +} + +/* + * Extract a boolean value from a ReloptElem. + */ +bool +reloptGetBoolean(ReloptElem *relopt) +{ + return nodeGetBoolean(relopt->arg, relopt->optname); +} + /* - * Create a DefElem setting "oids" to the specified value. + * Create a ReloptElem setting "oids" to the specified value. */ -DefElem * -defWithOids(bool value) +ReloptElem * +reloptWithOids(bool value) { - DefElem *f = makeNode(DefElem); + ReloptElem *f = makeNode(ReloptElem); - f->defname = "oids"; + f->optname = "oids"; + f->nmspc = NULL; f->arg = (Node *) makeInteger(value); return f; } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9f7cbc8dbd..8c36d7d635 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -398,7 +398,7 @@ DefineIndex(RangeVar *heapRelation, /* * Parse AM-specific options, convert to text array form, validate. */ - reloptions = transformRelOptions((Datum) 0, options, false, false); + reloptions = transformRelOptions((Datum) 0, options, NULL, NULL, false, false); (void) index_reloptions(amoptions, reloptions, true); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 46d7683377..2f178054f2 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -198,7 +198,7 @@ DefineSequence(CreateSeqStmt *seq) stmt->relation = seq->sequence; stmt->inhRelations = NIL; stmt->constraints = NIL; - stmt->options = list_make1(defWithOids(false)); + stmt->options = list_make1(reloptWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ee7f135c4d..917497dd66 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -351,6 +351,7 @@ DefineRelation(CreateStmt *stmt, char relkind) Datum reloptions; ListCell *listptr; AttrNumber attnum; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -418,7 +419,8 @@ DefineRelation(CreateStmt *stmt, char relkind) /* * Parse and validate reloptions, if any. */ - reloptions = transformRelOptions((Datum) 0, stmt->options, true, false); + reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, + true, false); (void) heap_reloptions(relkind, reloptions, true); @@ -2572,7 +2574,7 @@ ATRewriteCatalogs(List **wqueue) (tab->subcmds[AT_PASS_ADD_COL] || tab->subcmds[AT_PASS_ALTER_TYPE] || tab->subcmds[AT_PASS_COL_ATTRS])) - AlterTableCreateToastTable(tab->relid); + AlterTableCreateToastTable(tab->relid, (Datum) 0); } } @@ -6457,6 +6459,7 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) Datum repl_val[Natts_pg_class]; bool repl_null[Natts_pg_class]; bool repl_repl[Natts_pg_class]; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; if (defList == NIL) return; /* nothing to do */ @@ -6475,7 +6478,7 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) /* Generate new proposed reloptions (text array) */ newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, - defList, false, isReset); + defList, NULL, validnsps, false, isReset); /* Validate */ switch (rel->rd_rel->relkind) @@ -6521,6 +6524,53 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) ReleaseSysCache(tuple); + /* repeat the whole exercise for the toast table, if there's one */ + if (OidIsValid(rel->rd_rel->reltoastrelid)) + { + Relation toastrel; + Oid toastid = rel->rd_rel->reltoastrelid; + + toastrel = heap_open(toastid, AccessExclusiveLock); + + /* Get the old reloptions */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(toastid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", toastid); + + datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull); + + newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, + defList, "toast", validnsps, false, isReset); + + (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true); + + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + + if (newOptions != (Datum) 0) + repl_val[Anum_pg_class_reloptions - 1] = newOptions; + else + repl_null[Anum_pg_class_reloptions - 1] = true; + + repl_repl[Anum_pg_class_reloptions - 1] = true; + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass), + repl_val, repl_null, repl_repl); + + simple_heap_update(pgclass, &newtuple->t_self, newtuple); + + CatalogUpdateIndexes(pgclass, newtuple); + + heap_freetuple(newtuple); + + ReleaseSysCache(tuple); + + heap_close(toastrel, NoLock); + } + heap_close(pgclass, RowExclusiveLock); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index d3e22f8a03..bd82aa03f8 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1491,7 +1491,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist) createStmt->tableElts = coldeflist; createStmt->inhRelations = NIL; createStmt->constraints = NIL; - createStmt->options = list_make1(defWithOids(false)); + createStmt->options = list_make1(reloptWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index ac70facce7..60dcfb3c77 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -229,7 +229,7 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) createStmt->tableElts = attrList; createStmt->inhRelations = NIL; createStmt->constraints = NIL; - createStmt->options = list_make1(defWithOids(false)); + createStmt->options = list_make1(reloptWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index ebec42cabc..0dcb28544a 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2832,6 +2832,7 @@ OpenIntoRel(QueryDesc *queryDesc) Oid intoRelationId; TupleDesc tupdesc; DR_intorel *myState; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Assert(into); @@ -2890,6 +2891,8 @@ OpenIntoRel(QueryDesc *queryDesc) /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, into->options, + NULL, + validnsps, true, false); (void) heap_reloptions(RELKIND_RELATION, reloptions, true); @@ -2926,7 +2929,16 @@ OpenIntoRel(QueryDesc *queryDesc) * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ - AlterTableCreateToastTable(intoRelationId); + reloptions = transformRelOptions((Datum) 0, + into->options, + "toast", + validnsps, + true, + false); + + (void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true); + + AlterTableCreateToastTable(intoRelationId, reloptions); /* * And open the constructed table for writing. diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 977e2c85fd..a9b2e378cd 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2125,6 +2125,18 @@ _copyOptionDefElem(OptionDefElem *from) return newnode; } +static ReloptElem * +_copyReloptElem(ReloptElem *from) +{ + ReloptElem *newnode = makeNode(ReloptElem); + + COPY_STRING_FIELD(optname); + COPY_STRING_FIELD(nmspc); + COPY_NODE_FIELD(arg); + + return newnode; +} + static LockingClause * _copyLockingClause(LockingClause *from) { @@ -4079,6 +4091,9 @@ copyObject(void *from) case T_OptionDefElem: retval = _copyOptionDefElem(from); break; + case T_ReloptElem: + retval = _copyReloptElem(from); + break; case T_LockingClause: retval = _copyLockingClause(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c78089581f..4da928b795 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2099,6 +2099,16 @@ _equalOptionDefElem(OptionDefElem *a, OptionDefElem *b) } static bool +_equalReloptElem(ReloptElem *a, ReloptElem *b) +{ + COMPARE_STRING_FIELD(nmspc); + COMPARE_STRING_FIELD(optname); + COMPARE_NODE_FIELD(arg); + + return true; +} + +static bool _equalLockingClause(LockingClause *a, LockingClause *b) { COMPARE_NODE_FIELD(lockedRels); @@ -2855,6 +2865,9 @@ equal(void *a, void *b) case T_OptionDefElem: retval = _equalOptionDefElem(a, b); break; + case T_ReloptElem: + retval = _equalReloptElem(a, b); + break; case T_LockingClause: retval = _equalLockingClause(a, b); break; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 241f1a39fa..440577048b 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -374,3 +374,14 @@ makeOptionDefElem(int op, DefElem *def) res->def = def; return res; } + +ReloptElem * +makeReloptElem(char *name, char *nmspc, Node *arg) +{ + ReloptElem *res = makeNode(ReloptElem); + + res->optname = name; + res->nmspc = nmspc; + res->arg = arg; + return res; +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 27a4f519ad..8e6e78f16f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1805,6 +1805,16 @@ _outDefElem(StringInfo str, DefElem *node) } static void +_outReloptElem(StringInfo str, ReloptElem *node) +{ + WRITE_NODE_TYPE("RELOPTELEM"); + + WRITE_STRING_FIELD(nmspc); + WRITE_STRING_FIELD(optname); + WRITE_NODE_FIELD(arg); +} + +static void _outLockingClause(StringInfo str, LockingClause *node) { WRITE_NODE_TYPE("LOCKINGCLAUSE"); @@ -2770,6 +2780,9 @@ _outNode(StringInfo str, void *obj) case T_DefElem: _outDefElem(str, obj); break; + case T_ReloptElem: + _outReloptElem(str, obj); + break; case T_LockingClause: _outLockingClause(str, obj); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5c097c3265..3aa8fca83a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -164,6 +164,7 @@ static TypeName *TableFuncTypeName(List *columns); FuncWithArgs *funwithargs; DefElem *defelt; OptionDefElem *optdef; + ReloptElem *reloptel; SortBy *sortby; WindowDef *windef; JoinExpr *jexpr; @@ -271,6 +272,7 @@ static TypeName *TableFuncTypeName(List *columns); %type <list> stmtblock stmtmulti OptTableElementList TableElementList OptInherit definition + reloptions opt_reloptions OptWith opt_distinct opt_definition func_args func_args_list func_args_with_defaults func_args_with_defaults_list func_as createfunc_opt_list alterfunc_opt_list @@ -284,7 +286,7 @@ static TypeName *TableFuncTypeName(List *columns); target_list insert_column_list set_target_list set_clause_list set_clause multiple_set_clause ctext_expr_list ctext_row def_list indirection opt_indirection - group_clause TriggerFuncArgs select_limit + reloption_list group_clause TriggerFuncArgs select_limit opt_select_limit opclass_item_list opclass_drop_list opt_opfamily transaction_mode_list_or_empty TableFuncElementList opt_type_modifiers @@ -342,6 +344,7 @@ static TypeName *TableFuncTypeName(List *columns); %type <node> TableElement ConstraintElem TableFuncElement %type <node> columnDef %type <defelt> def_elem old_aggr_elem +%type <reloptel> reloption_elem %type <node> def_arg columnElem where_clause where_or_current_clause a_expr b_expr c_expr func_expr AexprConst indirection_el columnref in_expr having_clause func_table array_expr @@ -1781,7 +1784,7 @@ alter_table_cmd: $$ = (Node *)n; } /* ALTER TABLE <name> SET (...) */ - | SET definition + | SET reloptions { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_SetRelOptions; @@ -1789,7 +1792,7 @@ alter_table_cmd: $$ = (Node *)n; } /* ALTER TABLE <name> RESET (...) */ - | RESET definition + | RESET reloptions { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_ResetRelOptions; @@ -1814,6 +1817,37 @@ alter_using: | /* EMPTY */ { $$ = NULL; } ; +reloptions: + '(' reloption_list ')' { $$ = $2; } + ; + +opt_reloptions: WITH reloptions { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +reloption_list: + reloption_elem { $$ = list_make1($1); } + | reloption_list ',' reloption_elem { $$ = lappend($1, $3); } + ; + +reloption_elem: + ColLabel '=' def_arg + { + $$ = makeReloptElem($1, NULL, (Node *) $3); + } + | ColLabel + { + $$ = makeReloptElem($1, NULL, NULL); + } + | ColLabel '.' ColLabel '=' def_arg + { + $$ = makeReloptElem($3, $1, (Node *) $5); + } + | ColLabel '.' ColLabel + { + $$ = makeReloptElem($3, $1, NULL); + } + ; /***************************************************************************** @@ -2440,9 +2474,9 @@ OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } /* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */ OptWith: - WITH definition { $$ = $2; } - | WITH OIDS { $$ = list_make1(defWithOids(true)); } - | WITHOUT OIDS { $$ = list_make1(defWithOids(false)); } + WITH reloptions { $$ = $2; } + | WITH OIDS { $$ = list_make1(reloptWithOids(true)); } + | WITHOUT OIDS { $$ = list_make1(reloptWithOids(false)); } | /*EMPTY*/ { $$ = NIL; } ; @@ -4473,7 +4507,7 @@ opt_granted_by: GRANTED BY RoleId { $$ = $3; } IndexStmt: CREATE index_opt_unique INDEX index_name ON qualified_name access_method_clause '(' index_params ')' - opt_definition OptTableSpace where_clause + opt_reloptions OptTableSpace where_clause { IndexStmt *n = makeNode(IndexStmt); n->unique = $2; @@ -4489,7 +4523,7 @@ IndexStmt: CREATE index_opt_unique INDEX index_name } | CREATE index_opt_unique INDEX CONCURRENTLY index_name ON qualified_name access_method_clause '(' index_params ')' - opt_definition OptTableSpace where_clause + opt_reloptions OptTableSpace where_clause { IndexStmt *n = makeNode(IndexStmt); n->unique = $2; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index e0bd7d261e..ef548a7aff 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -233,7 +233,7 @@ interpretInhOption(InhOption inhOpt) } /* - * Given a relation-options list (of DefElems), return true iff the specified + * Given a relation-options list (of ReloptElems), return true iff the specified * table/result set should be created with OIDs. This needs to be done after * parsing the query string because the return value can depend upon the * default_with_oids GUC var. @@ -246,10 +246,10 @@ interpretOidsOption(List *defList) /* Scan list to see if OIDS was included */ foreach(cell, defList) { - DefElem *def = (DefElem *) lfirst(cell); + ReloptElem *def = (ReloptElem *) lfirst(cell); - if (pg_strcasecmp(def->defname, "oids") == 0) - return defGetBoolean(def); + if (pg_strcasecmp(def->optname, "oids") == 0) + return reloptGetBoolean(def); } /* OIDS option was not specified, so use default. */ diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 751d7deaa5..80070e39b2 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -16,6 +16,7 @@ */ #include "postgres.h" +#include "access/reloptions.h" #include "access/twophase.h" #include "access/xact.h" #include "catalog/catalog.h" @@ -422,6 +423,9 @@ ProcessUtility(Node *parsetree, if (IsA(stmt, CreateStmt)) { + Datum toast_options; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_RELATION); @@ -431,7 +435,17 @@ ProcessUtility(Node *parsetree, * needs a secondary relation too. */ CommandCounterIncrement(); - AlterTableCreateToastTable(relOid); + + /* parse and validate reloptions for the toast table */ + toast_options = transformRelOptions((Datum) 0, + ((CreateStmt *)stmt)->options, + "toast", + validnsps, + true, false); + (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, + true); + + AlterTableCreateToastTable(relOid, toast_options); } else { diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 39ef0334ad..c8f0a32058 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3100,6 +3100,7 @@ getTables(int *numTables) int i_owning_col; int i_reltablespace; int i_reloptions; + int i_toastreloptions; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); @@ -3131,22 +3132,24 @@ getTables(int *numTables) * owning column, if any (note this dependency is AUTO as of 8.2) */ appendPQExpBuffer(query, - "SELECT c.tableoid, c.oid, relname, " - "relacl, relkind, relnamespace, " - "(%s relowner) as rolname, " - "relchecks, relhastriggers, " - "relhasindex, relhasrules, relhasoids, " + "SELECT c.tableoid, c.oid, c.relname, " + "c.relacl, c.relkind, c.relnamespace, " + "(%s c.relowner) as rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " "d.refobjid as owning_tab, " "d.refobjsubid as owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') as reloptions " + "array_to_string(c.reloptions, ', ') as reloptions, " + "array_to_string(array(select 'toast.' || x from unnest(tc.reloptions) x), ', ') as toast_reloptions " "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " "d.classid = c.tableoid and d.objid = c.oid and " "d.objsubid = 0 and " "d.refclassid = c.tableoid and d.deptype = 'a') " - "where relkind in ('%c', '%c', '%c', '%c') " + "left join pg_class tc on (c.reltoastrelid = tc.oid) " + "where c.relkind in ('%c', '%c', '%c', '%c') " "order by c.oid", username_subquery, RELKIND_SEQUENCE, @@ -3168,7 +3171,8 @@ getTables(int *numTables) "d.refobjid as owning_tab, " "d.refobjsubid as owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') as reloptions " + "array_to_string(c.reloptions, ', ') as reloptions, " + "NULL as toast_reloptions " "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " @@ -3197,7 +3201,8 @@ getTables(int *numTables) "d.refobjid as owning_tab, " "d.refobjsubid as owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "NULL as reloptions " + "NULL as reloptions, " + "NULL as toast_reloptions " "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " @@ -3226,7 +3231,8 @@ getTables(int *numTables) "d.refobjid as owning_tab, " "d.refobjsubid as owning_col, " "NULL as reltablespace, " - "NULL as reloptions " + "NULL as reloptions, " + "NULL as toast_reloptions " "from pg_class c " "left join pg_depend d on " "(c.relkind = '%c' and " @@ -3251,7 +3257,8 @@ getTables(int *numTables) "NULL::oid as owning_tab, " "NULL::int4 as owning_col, " "NULL as reltablespace, " - "NULL as reloptions " + "NULL as reloptions, " + "NULL as toast_reloptions " "from pg_class " "where relkind in ('%c', '%c', '%c') " "order by oid", @@ -3271,7 +3278,8 @@ getTables(int *numTables) "NULL::oid as owning_tab, " "NULL::int4 as owning_col, " "NULL as reltablespace, " - "NULL as reloptions " + "NULL as reloptions, " + "NULL as toast_reloptions " "from pg_class " "where relkind in ('%c', '%c', '%c') " "order by oid", @@ -3301,7 +3309,8 @@ getTables(int *numTables) "NULL::oid as owning_tab, " "NULL::int4 as owning_col, " "NULL as reltablespace, " - "NULL as reloptions " + "NULL as reloptions, " + "NULL as toast_reloptions " "from pg_class c " "where relkind in ('%c', '%c') " "order by oid", @@ -3344,6 +3353,7 @@ getTables(int *numTables) i_owning_col = PQfnumber(res, "owning_col"); i_reltablespace = PQfnumber(res, "reltablespace"); i_reloptions = PQfnumber(res, "reloptions"); + i_toastreloptions = PQfnumber(res, "toast_reloptions"); if (lockWaitTimeout && g_fout->remoteVersion >= 70300) { @@ -3389,6 +3399,7 @@ getTables(int *numTables) } tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); + tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); /* other fields were zeroed above */ @@ -9700,8 +9711,24 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, ")"); } - if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) - appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions); + if ((tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) || + (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0)) + { + bool addcomma = false; + + appendPQExpBuffer(q, "\nWITH ("); + if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) + { + addcomma = true; + appendPQExpBuffer(q, "%s", tbinfo->reloptions); + } + if (tbinfo->toast_reloptions && strlen(tbinfo->toast_reloptions) > 0) + { + appendPQExpBuffer(q, "%s%s", addcomma ? ", " : "", + tbinfo->toast_reloptions); + } + appendPQExpBuffer(q, ")"); + } appendPQExpBuffer(q, ";\n"); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index bfe9962c60..3ca3764760 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -221,6 +221,7 @@ typedef struct _tableInfo char relkind; char *reltablespace; /* relation tablespace */ char *reloptions; /* options specified by WITH (...) */ + char *toast_reloptions; /* ditto, for the TOAST table */ bool hasindex; /* does it have any indexes? */ bool hasrules; /* does it have any rules? */ bool hastriggers; /* does it have any triggers? */ diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index 8fc10d636d..d05a9d311e 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -44,6 +44,9 @@ typedef enum relopt_kind RELOPT_KIND_MAX = 255 } relopt_kind; +/* reloption namespaces allowed for heaps -- currently only TOAST */ +#define HEAP_RELOPT_NAMESPACES { "toast", NULL } + /* generic struct to hold shared data */ typedef struct relopt_gen { @@ -240,6 +243,7 @@ extern void add_string_reloption(int kind, char *name, char *desc, char *default_val, validate_string_relopt validator); extern Datum transformRelOptions(Datum oldOptions, List *defList, + char *namspace, char *validnsps[], bool ignoreOids, bool isReset); extern List *untransformRelOptions(Datum options); extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index acc5f3669e..4024103a5f 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -17,7 +17,7 @@ /* * toasting.c prototypes */ -extern void AlterTableCreateToastTable(Oid relOid); +extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions); extern void BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 4356492c97..64af22cbd6 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -145,6 +145,8 @@ extern int64 defGetInt64(DefElem *def); extern List *defGetQualifiedName(DefElem *def); extern TypeName *defGetTypeName(DefElem *def); extern int defGetTypeLength(DefElem *def); -extern DefElem *defWithOids(bool value); +extern char *reloptGetString(ReloptElem *relopt); +extern bool reloptGetBoolean(ReloptElem *relopt); +extern ReloptElem *reloptWithOids(bool value); #endif /* DEFREM_H */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 4b6f056d1f..cd7bedfb42 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -69,4 +69,6 @@ extern DefElem *makeDefElem(char *name, Node *arg); extern OptionDefElem *makeOptionDefElem(int op, DefElem *def); +extern ReloptElem *makeReloptElem(char *name, char *namspc, Node *arg); + #endif /* MAKEFUNC_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 42e0117028..34f7cc1c32 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -363,6 +363,7 @@ typedef enum NodeTag T_Constraint, T_DefElem, T_OptionDefElem, + T_ReloptElem, T_RangeTblEntry, T_SortGroupClause, T_WindowClause, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 555b935aa1..dcd50d6119 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -533,6 +533,17 @@ typedef struct OptionDefElem } OptionDefElem; /* + * Reloption definition. As DefElem, with optional option namespace. + */ +typedef struct ReloptElem +{ + NodeTag type; + char *nmspc; + char *optname; + Node *arg; +} ReloptElem; + +/* * LockingClause - raw representation of FOR UPDATE/SHARE options * * Note: lockedRels == NIL means "all relations in query". Otherwise it |