Attached is a patch that adds a namespace capability to reloptions. It works this way:
alter table foo set (namespace.option = value) Currently the only namespace that does anything is "toast". What it does is store the option in the toast table pg_class.reloptions. It works fine, i.e. I can set a toast table fillfactor with a command that references the main table. I am also able to do this: CREATE TABLE foo (a int, b text) WITH (fillfactor = 80, toast.fillfactor = 75); and it correctly sets the fillfactor for both tables. This uses a new parse node. One possible disadvantage is that a command like this works, but does nothing: alvherre=# alter table foo set (test.foo = 1); ALTER TABLE This now needs pg_dump support to be complete. -- Alvaro Herrera http://www.CommandPrompt.com/ The PostgreSQL Company - Command Prompt, Inc.
Index: src/backend/access/common/reloptions.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/common/reloptions.c,v retrieving revision 1.18 diff -c -p -r1.18 reloptions.c *** src/backend/access/common/reloptions.c 12 Jan 2009 21:02:14 -0000 1.18 --- src/backend/access/common/reloptions.c 13 Jan 2009 21:13:37 -0000 *************** add_string_reloption(int kind, char *nam *** 390,397 **** } /* ! * Transform a relation options list (list of DefElem) into the text array ! * format that is kept in pg_class.reloptions. * * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing --- 390,399 ---- } /* ! * 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 *************** add_string_reloption(int kind, char *nam *** 408,414 **** * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum ! transformRelOptions(Datum oldOptions, List *defList, bool ignoreOids, bool isReset) { Datum result; --- 410,416 ---- * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum ! transformRelOptions(Datum oldOptions, List *defList, char *namspace, bool ignoreOids, bool isReset) { Datum result; *************** transformRelOptions(Datum oldOptions, Li *** 444,454 **** /* Search for a match in defList */ foreach(cell, defList) { ! DefElem *def = lfirst(cell); ! int kw_len = strlen(def->defname); if (text_len > kw_len && text_str[kw_len] == '=' && ! pg_strncasecmp(text_str, def->defname, kw_len) == 0) break; } if (!cell) --- 446,468 ---- /* Search for a match in defList */ foreach(cell, defList) { ! 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->optname, kw_len) == 0) break; } if (!cell) *************** transformRelOptions(Datum oldOptions, Li *** 468,474 **** */ foreach(cell, defList) { ! DefElem *def = lfirst(cell); if (isReset) { --- 482,488 ---- */ foreach(cell, defList) { ! ReloptElem *def = lfirst(cell); if (isReset) { *************** transformRelOptions(Datum oldOptions, Li *** 483,504 **** const char *value; Size len; ! if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0) continue; /* ! * Flatten the DefElem into a text string like "name=arg". If we ! * have just "name", assume "name=true" is meant. */ if (def->arg != NULL) ! value = defGetString(def); else value = "true"; ! len = VARHDRSZ + strlen(def->defname) + 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); astate = accumArrayResult(astate, PointerGetDatum(t), false, TEXTOID, --- 497,530 ---- const char *value; Size len; ! 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 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 = reloptGetString(def); else value = "true"; ! 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->optname, value); astate = accumArrayResult(astate, PointerGetDatum(t), false, TEXTOID, *************** default_reloptions(Datum reloptions, boo *** 897,903 **** } /* ! * Parse options for heaps (and perhaps someday toast tables). */ bytea * heap_reloptions(char relkind, Datum reloptions, bool validate) --- 923,929 ---- } /* ! * Parse options for heaps and toast tables. */ bytea * heap_reloptions(char relkind, Datum reloptions, bool validate) Index: src/backend/catalog/toasting.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/catalog/toasting.c,v retrieving revision 1.12 diff -c -p -r1.12 toasting.c *** src/backend/catalog/toasting.c 1 Jan 2009 17:23:37 -0000 1.12 --- src/backend/catalog/toasting.c 7 Jan 2009 22:10:48 -0000 *************** *** 32,38 **** #include "utils/syscache.h" ! static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid); static bool needs_toast_table(Relation rel); --- 32,39 ---- #include "utils/syscache.h" ! static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, ! Datum reloptions); static bool needs_toast_table(Relation rel); *************** static bool needs_toast_table(Relation r *** 46,52 **** * to end with CommandCounterIncrement if it makes any changes. */ void ! AlterTableCreateToastTable(Oid relOid) { Relation rel; --- 47,53 ---- * to end with CommandCounterIncrement if it makes any changes. */ void ! AlterTableCreateToastTable(Oid relOid, Datum reloptions) { Relation rel; *************** AlterTableCreateToastTable(Oid relOid) *** 58,64 **** rel = heap_open(relOid, AccessExclusiveLock); /* create_toast_table does all the work */ ! (void) create_toast_table(rel, InvalidOid, InvalidOid); heap_close(rel, NoLock); } --- 59,65 ---- rel = heap_open(relOid, AccessExclusiveLock); /* create_toast_table does all the work */ ! (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions); heap_close(rel, NoLock); } *************** BootstrapToastTable(char *relName, Oid t *** 84,90 **** relName))); /* create_toast_table does all the work */ ! if (!create_toast_table(rel, toastOid, toastIndexOid)) elog(ERROR, "\"%s\" does not require a toast table", relName); --- 85,91 ---- relName))); /* create_toast_table does all the work */ ! if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0)) elog(ERROR, "\"%s\" does not require a toast table", relName); *************** BootstrapToastTable(char *relName, Oid t *** 100,106 **** * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool ! create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; --- 101,107 ---- * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool ! create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; *************** create_toast_table(Relation rel, Oid toa *** 183,192 **** 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, --- 184,189 ---- *************** create_toast_table(Relation rel, Oid toa *** 199,205 **** true, 0, ONCOMMIT_NOOP, ! (Datum) 0, true); /* make the toast relation visible, else index creation will fail */ --- 196,202 ---- true, 0, ONCOMMIT_NOOP, ! reloptions, true); /* make the toast relation visible, else index creation will fail */ Index: src/backend/commands/cluster.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/cluster.c,v retrieving revision 1.180 diff -c -p -r1.180 cluster.c *** src/backend/commands/cluster.c 1 Jan 2009 17:23:37 -0000 1.180 --- src/backend/commands/cluster.c 13 Jan 2009 00:55:25 -0000 *************** make_new_heap(Oid OIDOldHeap, const char *** 668,673 **** --- 668,674 ---- TupleDesc OldHeapDesc, tupdesc; Oid OIDNewHeap; + Oid toastid; Relation OldHeap; HeapTuple tuple; Datum reloptions; *************** make_new_heap(Oid OIDOldHeap, const char *** 726,732 **** * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ ! AlterTableCreateToastTable(OIDNewHeap); heap_close(OldHeap, NoLock); --- 727,750 ---- * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ ! 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); Index: src/backend/commands/define.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/define.c,v retrieving revision 1.102 diff -c -p -r1.102 define.c *** src/backend/commands/define.c 1 Jan 2009 17:23:37 -0000 1.102 --- src/backend/commands/define.c 13 Jan 2009 00:31:44 -0000 *************** case_translate_language_name(const char *** 55,78 **** } ! /* ! * Extract a string value (otherwise uninterpreted) from a DefElem. ! */ ! char * ! defGetString(DefElem *def) { ! if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("%s requires a parameter", ! def->defname))); ! switch (nodeTag(def->arg)) { case T_Integer: { char *str = palloc(32); ! snprintf(str, 32, "%ld", (long) intVal(def->arg)); return str; } case T_Float: --- 55,74 ---- } ! static char * ! nodeGetString(Node *value, char *name) { ! if (value == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("%s requires a parameter", name))); ! switch (nodeTag(value)) { case T_Integer: { char *str = palloc(32); ! snprintf(str, 32, "%ld", (long) intVal(value)); return str; } case T_Float: *************** defGetString(DefElem *def) *** 81,100 **** * T_Float values are kept in string form, so this type cheat * works (and doesn't risk losing precision) */ ! return strVal(def->arg); case T_String: ! return strVal(def->arg); case T_TypeName: ! return TypeNameToString((TypeName *) def->arg); case T_List: ! return NameListToString((List *) def->arg); default: ! elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg)); } return NULL; /* keep compiler quiet */ } /* * Extract a numeric value (actually double) from a DefElem. */ double --- 77,105 ---- * T_Float values are kept in string form, so this type cheat * works (and doesn't risk losing precision) */ ! return strVal(value); case T_String: ! return strVal(value); case T_TypeName: ! return TypeNameToString((TypeName *) value); case T_List: ! return NameListToString((List *) value); default: ! 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 *************** defGetNumeric(DefElem *def) *** 120,144 **** return 0; /* keep compiler quiet */ } ! /* ! * Extract a boolean value from a DefElem. ! */ ! bool ! defGetBoolean(DefElem *def) { /* * If no parameter given, assume "true" is meant. */ ! if (def->arg == NULL) return true; /* * Allow 0, 1, "true", "false" */ ! switch (nodeTag(def->arg)) { case T_Integer: ! switch (intVal(def->arg)) { case 0: return false; --- 125,146 ---- return 0; /* keep compiler quiet */ } ! static bool ! nodeGetBoolean(Node *value, char *name) { /* * If no parameter given, assume "true" is meant. */ ! if (value == NULL) return true; /* * Allow 0, 1, "true", "false" */ ! switch (nodeTag(value)) { case T_Integer: ! switch (intVal(value)) { case 0: return false; *************** defGetBoolean(DefElem *def) *** 151,157 **** break; default: { ! char *sval = defGetString(def); if (pg_strcasecmp(sval, "true") == 0) return true; --- 153,159 ---- break; default: { ! char *sval = nodeGetString(value, name); if (pg_strcasecmp(sval, "true") == 0) return true; *************** defGetBoolean(DefElem *def) *** 163,174 **** } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("%s requires a Boolean value", ! def->defname))); return false; /* keep compiler quiet */ } /* * Extract an int64 value from a DefElem. */ int64 --- 165,184 ---- } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! 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 *************** defGetTypeLength(DefElem *def) *** 305,319 **** return 0; /* keep compiler quiet */ } /* ! * Create a DefElem setting "oids" to the specified value. */ ! DefElem * ! defWithOids(bool value) { ! DefElem *f = makeNode(DefElem); ! f->defname = "oids"; f->arg = (Node *) makeInteger(value); return f; } --- 315,349 ---- 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 ReloptElem setting "oids" to the specified value. */ ! ReloptElem * ! reloptWithOids(bool value) { ! ReloptElem *f = makeNode(ReloptElem); ! f->optname = "oids"; ! f->nmspc = NULL; f->arg = (Node *) makeInteger(value); return f; } Index: src/backend/commands/indexcmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/indexcmds.c,v retrieving revision 1.181 diff -c -p -r1.181 indexcmds.c *** src/backend/commands/indexcmds.c 1 Jan 2009 17:23:38 -0000 1.181 --- src/backend/commands/indexcmds.c 7 Jan 2009 22:17:00 -0000 *************** DefineIndex(RangeVar *heapRelation, *** 398,404 **** /* * Parse AM-specific options, convert to text array form, validate. */ ! reloptions = transformRelOptions((Datum) 0, options, false, false); (void) index_reloptions(amoptions, reloptions, true); --- 398,404 ---- /* * Parse AM-specific options, convert to text array form, validate. */ ! reloptions = transformRelOptions((Datum) 0, options, NULL, false, false); (void) index_reloptions(amoptions, reloptions, true); Index: src/backend/commands/sequence.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/sequence.c,v retrieving revision 1.156 diff -c -p -r1.156 sequence.c *** src/backend/commands/sequence.c 1 Jan 2009 17:23:39 -0000 1.156 --- src/backend/commands/sequence.c 7 Jan 2009 23:43:29 -0000 *************** DefineSequence(CreateSeqStmt *seq) *** 198,204 **** stmt->relation = seq->sequence; stmt->inhRelations = NIL; stmt->constraints = NIL; ! stmt->options = list_make1(defWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; --- 198,204 ---- stmt->relation = seq->sequence; stmt->inhRelations = NIL; stmt->constraints = NIL; ! stmt->options = list_make1(reloptWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.277 diff -c -p -r1.277 tablecmds.c *** src/backend/commands/tablecmds.c 12 Jan 2009 08:54:26 -0000 1.277 --- src/backend/commands/tablecmds.c 13 Jan 2009 21:34:09 -0000 *************** DefineRelation(CreateStmt *stmt, char re *** 417,423 **** /* * Parse and validate reloptions, if any. */ ! reloptions = transformRelOptions((Datum) 0, stmt->options, true, false); (void) heap_reloptions(relkind, reloptions, true); --- 417,423 ---- /* * Parse and validate reloptions, if any. */ ! reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, true, false); (void) heap_reloptions(relkind, reloptions, true); *************** ATRewriteCatalogs(List **wqueue) *** 2571,2577 **** (tab->subcmds[AT_PASS_ADD_COL] || tab->subcmds[AT_PASS_ALTER_TYPE] || tab->subcmds[AT_PASS_COL_ATTRS])) ! AlterTableCreateToastTable(tab->relid); } } --- 2571,2577 ---- (tab->subcmds[AT_PASS_ADD_COL] || tab->subcmds[AT_PASS_ALTER_TYPE] || tab->subcmds[AT_PASS_COL_ATTRS])) ! AlterTableCreateToastTable(tab->relid, (Datum) 0); } } *************** ATExecSetRelOptions(Relation rel, List * *** 6459,6465 **** /* Generate new proposed reloptions (text array) */ newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, ! defList, false, isReset); /* Validate */ switch (rel->rd_rel->relkind) --- 6459,6465 ---- /* Generate new proposed reloptions (text array) */ newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, ! defList, NULL, false, isReset); /* Validate */ switch (rel->rd_rel->relkind) *************** ATExecSetRelOptions(Relation rel, List * *** 6505,6510 **** --- 6505,6557 ---- 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", 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); } Index: src/backend/commands/typecmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/typecmds.c,v retrieving revision 1.130 diff -c -p -r1.130 typecmds.c *** src/backend/commands/typecmds.c 9 Jan 2009 15:46:10 -0000 1.130 --- src/backend/commands/typecmds.c 12 Jan 2009 21:35:28 -0000 *************** DefineCompositeType(const RangeVar *type *** 1491,1497 **** createStmt->tableElts = coldeflist; createStmt->inhRelations = NIL; createStmt->constraints = NIL; ! createStmt->options = list_make1(defWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; --- 1491,1497 ---- createStmt->tableElts = coldeflist; createStmt->inhRelations = NIL; createStmt->constraints = NIL; ! createStmt->options = list_make1(reloptWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; Index: src/backend/commands/view.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/view.c,v retrieving revision 1.111 diff -c -p -r1.111 view.c *** src/backend/commands/view.c 1 Jan 2009 17:23:40 -0000 1.111 --- src/backend/commands/view.c 7 Jan 2009 23:43:43 -0000 *************** DefineVirtualRelation(const RangeVar *re *** 229,235 **** createStmt->tableElts = attrList; createStmt->inhRelations = NIL; createStmt->constraints = NIL; ! createStmt->options = list_make1(defWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; --- 229,235 ---- createStmt->tableElts = attrList; createStmt->inhRelations = NIL; createStmt->constraints = NIL; ! createStmt->options = list_make1(reloptWithOids(false)); createStmt->oncommit = ONCOMMIT_NOOP; createStmt->tablespacename = NULL; Index: src/backend/executor/execMain.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/execMain.c,v retrieving revision 1.320 diff -c -p -r1.320 execMain.c *** src/backend/executor/execMain.c 1 Jan 2009 17:23:41 -0000 1.320 --- src/backend/executor/execMain.c 7 Jan 2009 22:10:48 -0000 *************** OpenIntoRel(QueryDesc *queryDesc) *** 2787,2792 **** --- 2787,2793 ---- /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, into->options, + NULL, true, false); (void) heap_reloptions(RELKIND_RELATION, reloptions, true); *************** OpenIntoRel(QueryDesc *queryDesc) *** 2823,2829 **** * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ ! AlterTableCreateToastTable(intoRelationId); /* * And open the constructed table for writing. --- 2824,2838 ---- * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ ! reloptions = transformRelOptions((Datum) 0, ! into->options, ! "toast", ! true, ! false); ! ! (void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true); ! ! AlterTableCreateToastTable(intoRelationId, reloptions); /* * And open the constructed table for writing. Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.419 diff -c -p -r1.419 copyfuncs.c *** src/backend/nodes/copyfuncs.c 1 Jan 2009 17:23:43 -0000 1.419 --- src/backend/nodes/copyfuncs.c 7 Jan 2009 22:21:31 -0000 *************** _copyOptionDefElem(OptionDefElem *from) *** 2123,2128 **** --- 2123,2140 ---- 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) { *************** copyObject(void *from) *** 4066,4071 **** --- 4078,4086 ---- case T_OptionDefElem: retval = _copyOptionDefElem(from); break; + case T_ReloptElem: + retval = _copyReloptElem(from); + break; case T_LockingClause: retval = _copyLockingClause(from); break; Index: src/backend/nodes/makefuncs.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/makefuncs.c,v retrieving revision 1.62 diff -c -p -r1.62 makefuncs.c *** src/backend/nodes/makefuncs.c 1 Jan 2009 17:23:43 -0000 1.62 --- src/backend/nodes/makefuncs.c 7 Jan 2009 22:10:48 -0000 *************** makeOptionDefElem(int op, DefElem *def) *** 374,376 **** --- 374,387 ---- 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; + } Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.654 diff -c -p -r2.654 gram.y *** src/backend/parser/gram.y 12 Jan 2009 09:38:30 -0000 2.654 --- src/backend/parser/gram.y 12 Jan 2009 21:35:29 -0000 *************** static TypeName *TableFuncTypeName(List *** 157,162 **** --- 157,163 ---- FuncWithArgs *funwithargs; DefElem *defelt; OptionDefElem *optdef; + ReloptElem *reloptel; SortBy *sortby; WindowDef *windef; JoinExpr *jexpr; *************** static TypeName *TableFuncTypeName(List *** 263,268 **** --- 264,270 ---- %type <list> stmtblock stmtmulti OptTableElementList TableElementList OptInherit definition + 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 *************** static TypeName *TableFuncTypeName(List *** 276,282 **** 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 opt_select_limit opclass_item_list opclass_drop_list opt_opfamily transaction_mode_list_or_empty TableFuncElementList opt_type_modifiers --- 278,284 ---- 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 ! 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 *************** static TypeName *TableFuncTypeName(List *** 334,339 **** --- 336,342 ---- %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 *************** alter_table_cmd: *** 1773,1779 **** $$ = (Node *)n; } /* ALTER TABLE <name> SET (...) */ ! | SET definition { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_SetRelOptions; --- 1776,1782 ---- $$ = (Node *)n; } /* ALTER TABLE <name> SET (...) */ ! | SET reloptions { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_SetRelOptions; *************** alter_table_cmd: *** 1781,1787 **** $$ = (Node *)n; } /* ALTER TABLE <name> RESET (...) */ ! | RESET definition { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_ResetRelOptions; --- 1784,1790 ---- $$ = (Node *)n; } /* ALTER TABLE <name> RESET (...) */ ! | RESET reloptions { AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_ResetRelOptions; *************** alter_using: *** 1806,1812 **** --- 1809,1841 ---- | /* EMPTY */ { $$ = NULL; } ; + reloptions: + '(' reloption_list ')' { $$ = $2; } + ; + + 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); + } + ; /***************************************************************************** * *************** OptInherit: INHERITS '(' qualified_name_ *** 2432,2440 **** /* 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)); } | /*EMPTY*/ { $$ = NIL; } ; --- 2461,2469 ---- /* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */ OptWith: ! WITH reloptions { $$ = $2; } ! | WITH OIDS { $$ = list_make1(reloptWithOids(true)); } ! | WITHOUT OIDS { $$ = list_make1(reloptWithOids(false)); } | /*EMPTY*/ { $$ = NIL; } ; Index: src/backend/parser/parse_clause.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/parser/parse_clause.c,v retrieving revision 1.185 diff -c -p -r1.185 parse_clause.c *** src/backend/parser/parse_clause.c 1 Jan 2009 17:23:45 -0000 1.185 --- src/backend/parser/parse_clause.c 7 Jan 2009 23:04:33 -0000 *************** interpretInhOption(InhOption inhOpt) *** 233,239 **** } /* ! * Given a relation-options list (of DefElems), 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. --- 233,239 ---- } /* ! * 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. *************** interpretOidsOption(List *defList) *** 246,255 **** /* Scan list to see if OIDS was included */ foreach(cell, defList) { ! DefElem *def = (DefElem *) lfirst(cell); ! if (pg_strcasecmp(def->defname, "oids") == 0) ! return defGetBoolean(def); } /* OIDS option was not specified, so use default. */ --- 246,255 ---- /* Scan list to see if OIDS was included */ foreach(cell, defList) { ! ReloptElem *def = (ReloptElem *) lfirst(cell); ! if (pg_strcasecmp(def->optname, "oids") == 0) ! return reloptGetBoolean(def); } /* OIDS option was not specified, so use default. */ Index: src/backend/tcop/utility.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.304 diff -c -p -r1.304 utility.c *** src/backend/tcop/utility.c 1 Jan 2009 17:23:48 -0000 1.304 --- src/backend/tcop/utility.c 7 Jan 2009 22:10:48 -0000 *************** *** 16,21 **** --- 16,22 ---- */ #include "postgres.h" + #include "access/reloptions.h" #include "access/twophase.h" #include "access/xact.h" #include "catalog/catalog.h" *************** ProcessUtility(Node *parsetree, *** 422,427 **** --- 423,430 ---- if (IsA(stmt, CreateStmt)) { + Datum toast_options; + /* Create the table itself */ relOid = DefineRelation((CreateStmt *) stmt, RELKIND_RELATION); *************** ProcessUtility(Node *parsetree, *** 431,437 **** * needs a secondary relation too. */ CommandCounterIncrement(); ! AlterTableCreateToastTable(relOid); } else { --- 434,449 ---- * needs a secondary relation too. */ CommandCounterIncrement(); ! ! /* parse and validate reloptions for the toast table */ ! toast_options = transformRelOptions((Datum) 0, ! ((CreateStmt *)stmt)->options, ! "toast", ! true, false); ! (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, ! true); ! ! AlterTableCreateToastTable(relOid, toast_options); } else { Index: src/include/access/reloptions.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/access/reloptions.h,v retrieving revision 1.10 diff -c -p -r1.10 reloptions.h *** src/include/access/reloptions.h 12 Jan 2009 21:02:15 -0000 1.10 --- src/include/access/reloptions.h 12 Jan 2009 23:38:37 -0000 *************** extern void add_string_reloption(int kin *** 239,245 **** char *default_val, validate_string_relopt validator); extern Datum transformRelOptions(Datum oldOptions, List *defList, ! bool ignoreOids, bool isReset); extern List *untransformRelOptions(Datum options); extern relopt_value *parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts); --- 239,245 ---- char *default_val, validate_string_relopt validator); extern Datum transformRelOptions(Datum oldOptions, List *defList, ! char *namspace, bool ignoreOids, bool isReset); extern List *untransformRelOptions(Datum options); extern relopt_value *parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts); Index: src/include/catalog/toasting.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/catalog/toasting.h,v retrieving revision 1.5 diff -c -p -r1.5 toasting.h *** src/include/catalog/toasting.h 1 Jan 2009 17:23:58 -0000 1.5 --- src/include/catalog/toasting.h 7 Jan 2009 22:10:48 -0000 *************** *** 17,23 **** /* * toasting.c prototypes */ ! extern void AlterTableCreateToastTable(Oid relOid); extern void BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid); --- 17,23 ---- /* * toasting.c prototypes */ ! extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions); extern void BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid); Index: src/include/commands/defrem.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/commands/defrem.h,v retrieving revision 1.92 diff -c -p -r1.92 defrem.h *** src/include/commands/defrem.h 1 Jan 2009 17:23:58 -0000 1.92 --- src/include/commands/defrem.h 13 Jan 2009 00:06:05 -0000 *************** extern int64 defGetInt64(DefElem *def); *** 145,150 **** extern List *defGetQualifiedName(DefElem *def); extern TypeName *defGetTypeName(DefElem *def); extern int defGetTypeLength(DefElem *def); ! extern DefElem *defWithOids(bool value); #endif /* DEFREM_H */ --- 145,152 ---- extern List *defGetQualifiedName(DefElem *def); extern TypeName *defGetTypeName(DefElem *def); extern int defGetTypeLength(DefElem *def); ! extern char *reloptGetString(ReloptElem *relopt); ! extern bool reloptGetBoolean(ReloptElem *relopt); ! extern ReloptElem *reloptWithOids(bool value); #endif /* DEFREM_H */ Index: src/include/nodes/makefuncs.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/nodes/makefuncs.h,v retrieving revision 1.65 diff -c -p -r1.65 makefuncs.h *** src/include/nodes/makefuncs.h 1 Jan 2009 17:24:00 -0000 1.65 --- src/include/nodes/makefuncs.h 7 Jan 2009 22:10:48 -0000 *************** extern DefElem *makeDefElem(char *name, *** 69,72 **** --- 69,74 ---- extern OptionDefElem *makeOptionDefElem(int op, DefElem *def); + extern ReloptElem *makeReloptElem(char *name, char *namspc, Node *arg); + #endif /* MAKEFUNC_H */ Index: src/include/nodes/nodes.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.218 diff -c -p -r1.218 nodes.h *** src/include/nodes/nodes.h 1 Jan 2009 17:24:00 -0000 1.218 --- src/include/nodes/nodes.h 7 Jan 2009 22:10:48 -0000 *************** typedef enum NodeTag *** 363,368 **** --- 363,369 ---- T_Constraint, T_DefElem, T_OptionDefElem, + T_ReloptElem, T_RangeTblEntry, T_SortGroupClause, T_WindowClause, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.387 diff -c -p -r1.387 parsenodes.h *** src/include/nodes/parsenodes.h 1 Jan 2009 17:24:00 -0000 1.387 --- src/include/nodes/parsenodes.h 7 Jan 2009 22:10:48 -0000 *************** typedef struct OptionDefElem *** 532,537 **** --- 532,548 ---- } 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
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers