reloptions with a "namespace"

Started by Alvaro Herreraalmost 17 years ago15 messages
#1Alvaro Herrera
alvherre@commandprompt.com
1 attachment(s)

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.

Attachments:

reloptions-namespace.patchtext/x-diff; charset=us-asciiDownload
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
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#1)
Re: reloptions with a "namespace"

Alvaro Herrera <alvherre@commandprompt.com> writes:

This uses a new parse node.

You need at least equalfuncs.c support for that, and outfuncs would be
advisable.

One possible disadvantage is that a command
like this works, but does nothing:
alvherre=# alter table foo set (test.foo = 1);

Uh, why doesn't it throw an error? We throw error for unrecognized
reloptions in general.

regards, tom lane

#3Alvaro Herrera
alvherre@commandprompt.com
In reply to: Tom Lane (#2)
Re: reloptions with a "namespace"

Tom Lane wrote:

Alvaro Herrera <alvherre@commandprompt.com> writes:

This uses a new parse node.

You need at least equalfuncs.c support for that, and outfuncs would be
advisable.

Added.

One possible disadvantage is that a command
like this works, but does nothing:
alvherre=# alter table foo set (test.foo = 1);

Uh, why doesn't it throw an error? We throw error for unrecognized
reloptions in general.

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

alvherre=# alter index f set (toast.fillfactor = 20);
ALTER INDEX

The original case now fails correctly with

alvherre=# alter table foo set (test.foo = 1);
ERROR: 22023: unrecognized parameter namespace "test"
UBICACI�N: transformRelOptions, /pgsql/source/04toastopt/src/backend/access/common/reloptions.c:490
alvherre=# alter table foo set (toast.foo = 1);
ERROR: 22023: unrecognized parameter "foo"
UBICACI�N: parseRelOptions, /pgsql/source/04toastopt/src/backend/access/common/reloptions.c:694

--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

In reply to: Alvaro Herrera (#3)
Re: reloptions with a "namespace"

Alvaro Herrera escreveu:

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

IMHO it's the appropriate place.

alvherre=# alter index f set (toast.fillfactor = 20);
ALTER INDEX

Maybe you need to add relopt_kind test in your validation code.

--
Euler Taveira de Oliveira
http://www.timbira.com/

#5Alvaro Herrera
alvherre@commandprompt.com
In reply to: Euler Taveira de Oliveira (#4)
1 attachment(s)
Re: reloptions with a "namespace"

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

IMHO it's the appropriate place.

I think the best place would be parseRelOptions. The problem is that
transformRelOptions does not apply any semantics to the values it's
parsing; it doesn't know about the relopt_kind for example. That stuff
is only known by parseRelOptions, but when the options reach that point,
they have already lost the namespace (due to transformRelOptions).

alvherre=# alter index f set (toast.fillfactor = 20);
ALTER INDEX

Maybe you need to add relopt_kind test in your validation code.

No clean way to do that :-(

--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

Attachments:

reloptions-namespace-2.patchtext/x-diff; charset=us-asciiDownload
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	14 Jan 2009 14:32:26 -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
*** 402,414 ****
   * 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.
   *
   * 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)
  {
  	Datum		result;
--- 404,416 ----
   * 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, but it does check that the namespaces given are known.
   *
   * 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, 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,493 ----
  	 */
  	foreach(cell, defList)
  	{
! 		ReloptElem    *def = lfirst(cell);
! 
! 		if (def->nmspc && pg_strcasecmp(def->nmspc, "toast") != 0)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 					 errmsg("unrecognized parameter namespace \"%s\"", def->nmspc)));
  
  		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,
--- 502,535 ----
  			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)
--- 928,934 ----
  }
  
  /*
!  * 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/equalfuncs.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.344
diff -c -p -r1.344 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	1 Jan 2009 17:23:43 -0000	1.344
--- src/backend/nodes/equalfuncs.c	14 Jan 2009 14:11:16 -0000
*************** _equalOptionDefElem(OptionDefElem *a, Op
*** 2090,2095 ****
--- 2090,2105 ----
  }
  
  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);
*************** equal(void *a, void *b)
*** 2844,2849 ****
--- 2854,2862 ----
  		case T_OptionDefElem:
  			retval = _equalOptionDefElem(a, b);
  			break;
+ 		case T_ReloptElem:
+ 			retval = _equalReloptElem(a, b);
+ 			break;
  		case T_LockingClause:
  			retval = _equalLockingClause(a, b);
  			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/nodes/outfuncs.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.349
diff -c -p -r1.349 outfuncs.c
*** src/backend/nodes/outfuncs.c	1 Jan 2009 17:23:43 -0000	1.349
--- src/backend/nodes/outfuncs.c	14 Jan 2009 14:12:38 -0000
*************** _outDefElem(StringInfo str, DefElem *nod
*** 1807,1812 ****
--- 1807,1822 ----
  }
  
  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");
*************** _outNode(StringInfo str, void *obj)
*** 2770,2775 ****
--- 2780,2788 ----
  			case T_DefElem:
  				_outDefElem(str, obj);
  				break;
+ 			case T_ReloptElem:
+ 				_outReloptElem(str, obj);
+ 				break;
  			case T_LockingClause:
  				_outLockingClause(str, obj);
  				break;
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
In reply to: Alvaro Herrera (#5)
Re: reloptions with a "namespace"

Alvaro Herrera escreveu:

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

IMHO it's the appropriate place.

I think the best place would be parseRelOptions. The problem is that
transformRelOptions does not apply any semantics to the values it's
parsing; it doesn't know about the relopt_kind for example. That stuff
is only known by parseRelOptions, but when the options reach that point,
they have already lost the namespace (due to transformRelOptions).

[Looking at your patch...] You're right. The only command that doesn't use
parseRelOptions() is ALTER INDEX. Is it the case to add a call at
index_reloptions() too? If it is not the case, I think you need to pass
'nmspc.relopt=value' instead of 'relopt=value' at transformRelOptions(). Of
course, you'll need to add some code at index_reloptions() to validate reloptions.

The following message doesn't say much. Isn't it the case to replace
'opt_definition' with 'OptWith' variant at gram.y?

euler=# create index fooi on foo(a) with (nmspc.relopt = 32);
ERROR: syntax error at or near "."
LINHA 1: create index fooi on foo(a) with (nmspc.relopt = 32);
^

--
Euler Taveira de Oliveira
http://www.timbira.com/

#7Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Alvaro Herrera (#5)
1 attachment(s)
Re: reloptions with a "namespace"

Alvaro Herrera wrote:

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

IMHO it's the appropriate place.

I think the best place would be parseRelOptions. The problem is that
transformRelOptions does not apply any semantics to the values it's
parsing; it doesn't know about the relopt_kind for example. That stuff
is only known by parseRelOptions, but when the options reach that point,
they have already lost the namespace (due to transformRelOptions).

Okay, so I've changed things so that the transformRelOptions' caller is
now in charge of passing an array of valid option namespaces. This is
working A-OK. I'm now going to figure out appropriate pg_dump support
and commit as soon as possible.

--
Alvaro Herrera http://www.advogato.org/person/alvherre
"No es bueno caminar con un hombre muerto"

Attachments:

reloptions-namespace-3.patchtext/x-diff; charset=us-asciiDownload
Index: src/backend/access/common/reloptions.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/common/reloptions.c,v
retrieving revision 1.19
diff -c -p -r1.19 reloptions.c
*** src/backend/access/common/reloptions.c	26 Jan 2009 19:41:06 -0000	1.19
--- src/backend/access/common/reloptions.c	29 Jan 2009 18:01:12 -0000
***************
*** 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
***************
*** 402,415 ****
   * 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.
   *
   * 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)
  {
  	Datum		result;
  	ArrayBuildState *astate;
--- 404,420 ----
   * 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, 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, char *namspace,
! 					char *validnsps[], bool ignoreOids, bool isReset)
  {
  	Datum		result;
  	ArrayBuildState *astate;
***************
*** 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)
--- 449,471 ----
  			/* 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)
***************
*** 468,474 ****
  	 */
  	foreach(cell, defList)
  	{
! 		DefElem    *def = lfirst(cell);
  
  		if (isReset)
  		{
--- 485,492 ----
  	 */
  	foreach(cell, defList)
  	{
! 		ReloptElem    *def = lfirst(cell);
! 
  
  		if (isReset)
  		{
***************
*** 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,
--- 501,562 ----
  			const char *value;
  			Size		len;
  
! 			/*
! 			 * 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 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,
***************
*** 944,950 ****
  }
  
  /*
!  * Parse options for heaps (and perhaps someday toast tables).
   */
  bytea *
  heap_reloptions(char relkind, Datum reloptions, bool validate)
--- 1002,1008 ----
  }
  
  /*
!  * 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/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	29 Jan 2009 16:46:45 -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);
  
  
***************
*** 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;
  
***************
*** 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);
  }
***************
*** 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);
  
***************
*** 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;
***************
*** 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 ----
***************
*** 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/cvs/pgsql/src/backend/commands/cluster.c,v
retrieving revision 1.181
diff -c -p -r1.181 cluster.c
*** src/backend/commands/cluster.c	16 Jan 2009 13:27:23 -0000	1.181
--- src/backend/commands/cluster.c	29 Jan 2009 16:46:45 -0000
***************
*** 668,673 ****
--- 668,674 ----
  	TupleDesc	OldHeapDesc,
  				tupdesc;
  	Oid			OIDNewHeap;
+ 	Oid			toastid;
  	Relation	OldHeap;
  	HeapTuple	tuple;
  	Datum		reloptions;
***************
*** 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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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:
***************
*** 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
***************
*** 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;
***************
*** 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;
***************
*** 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
***************
*** 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/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	29 Jan 2009 17:38:56 -0000
***************
*** 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, NULL, false, false);
  
  	(void) index_reloptions(amoptions, reloptions, true);
  
Index: src/backend/commands/sequence.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/sequence.c,v
retrieving revision 1.157
diff -c -p -r1.157 sequence.c
*** src/backend/commands/sequence.c	20 Jan 2009 18:59:37 -0000	1.157
--- src/backend/commands/sequence.c	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.278
diff -c -p -r1.278 tablecmds.c
*** src/backend/commands/tablecmds.c	22 Jan 2009 20:16:02 -0000	1.278
--- src/backend/commands/tablecmds.c	29 Jan 2009 17:48:02 -0000
***************
*** 351,356 ****
--- 351,357 ----
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
+ 	char	   *validnsps[] = { "toast" };
  
  	/*
  	 * Truncate relname to appropriate length (probably a waste of time, as
***************
*** 418,424 ****
  	/*
  	 * Parse and validate reloptions, if any.
  	 */
! 	reloptions = transformRelOptions((Datum) 0, stmt->options, true, false);
  
  	(void) heap_reloptions(relkind, reloptions, true);
  
--- 419,426 ----
  	/*
  	 * Parse and validate reloptions, if any.
  	 */
! 	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
! 									 true, false);
  
  	(void) heap_reloptions(relkind, reloptions, true);
  
***************
*** 2572,2578 ****
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid);
  	}
  }
  
--- 2574,2580 ----
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid, (Datum) 0);
  	}
  }
  
***************
*** 6457,6462 ****
--- 6459,6465 ----
  	Datum		repl_val[Natts_pg_class];
  	bool		repl_null[Natts_pg_class];
  	bool		repl_repl[Natts_pg_class];
+ 	char	   *validnsps[] = { "toast" };
  
  	if (defList == NIL)
  		return;					/* nothing to do */
***************
*** 6475,6481 ****
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
--- 6478,6484 ----
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, NULL, validnsps, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
***************
*** 6521,6526 ****
--- 6524,6576 ----
  
  	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);
  }
  
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/commands/view.c,v
retrieving revision 1.113
diff -c -p -r1.113 view.c
*** src/backend/commands/view.c	27 Jan 2009 12:40:15 -0000	1.113
--- src/backend/commands/view.c	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.321
diff -c -p -r1.321 execMain.c
*** src/backend/executor/execMain.c	22 Jan 2009 20:16:03 -0000	1.321
--- src/backend/executor/execMain.c	29 Jan 2009 17:40:15 -0000
***************
*** 2832,2837 ****
--- 2832,2838 ----
  	Oid			intoRelationId;
  	TupleDesc	tupdesc;
  	DR_intorel *myState;
+ 	char	   *validnsps[] = { "toast" };
  
  	Assert(into);
  
***************
*** 2890,2895 ****
--- 2891,2898 ----
  	/* Parse and validate any reloptions */
  	reloptions = transformRelOptions((Datum) 0,
  									 into->options,
+ 									 NULL,
+ 									 validnsps,
  									 true,
  									 false);
  	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
***************
*** 2926,2932 ****
  	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
  	 * the TOAST table will be visible for insertion.
  	 */
! 	AlterTableCreateToastTable(intoRelationId);
  
  	/*
  	 * And open the constructed table for writing.
--- 2929,2944 ----
  	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
  	 * the TOAST table will be visible for insertion.
  	 */
! 	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.
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.421
diff -c -p -r1.421 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	22 Jan 2009 20:16:03 -0000	1.421
--- src/backend/nodes/copyfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 2125,2130 ****
--- 2125,2142 ----
  	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,4084 ****
--- 4091,4099 ----
  		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/equalfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.346
diff -c -p -r1.346 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	22 Jan 2009 20:16:03 -0000	1.346
--- src/backend/nodes/equalfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 2099,2104 ****
--- 2099,2114 ----
  }
  
  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,2860 ****
--- 2865,2873 ----
  		case T_OptionDefElem:
  			retval = _equalOptionDefElem(a, b);
  			break;
+ 		case T_ReloptElem:
+ 			retval = _equalReloptElem(a, b);
+ 			break;
  		case T_LockingClause:
  			retval = _equalLockingClause(a, b);
  			break;
Index: src/backend/nodes/makefuncs.c
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/nodes/outfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.350
diff -c -p -r1.350 outfuncs.c
*** src/backend/nodes/outfuncs.c	22 Jan 2009 20:16:04 -0000	1.350
--- src/backend/nodes/outfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 1805,1810 ****
--- 1805,1820 ----
  }
  
  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,2775 ****
--- 2780,2788 ----
  			case T_DefElem:
  				_outDefElem(str, obj);
  				break;
+ 			case T_ReloptElem:
+ 				_outReloptElem(str, obj);
+ 				break;
  			case T_LockingClause:
  				_outLockingClause(str, obj);
  				break;
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.656
diff -c -p -r2.656 gram.y
*** src/backend/parser/gram.y	22 Jan 2009 20:16:05 -0000	2.656
--- src/backend/parser/gram.y	29 Jan 2009 18:44:23 -0000
***************
*** 164,169 ****
--- 164,170 ----
  	FuncWithArgs		*funwithargs;
  	DefElem				*defelt;
  	OptionDefElem		*optdef;
+ 	ReloptElem			*reloptel;
  	SortBy				*sortby;
  	WindowDef			*windef;
  	JoinExpr			*jexpr;
***************
*** 271,276 ****
--- 272,278 ----
  
  %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,290 ****
  				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
--- 286,292 ----
  				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
***************
*** 342,347 ****
--- 344,350 ----
  %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,1787 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
--- 1784,1790 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
***************
*** 1789,1795 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
--- 1792,1798 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
***************
*** 1814,1819 ****
--- 1817,1853 ----
  			| /* 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,2448 ****
  
  /* 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; }
  		;
  
--- 2474,2482 ----
  
  /* 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; }
  		;
  
***************
*** 4473,4479 ****
  
  IndexStmt:	CREATE index_opt_unique INDEX index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_definition OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
--- 4507,4513 ----
  
  IndexStmt:	CREATE index_opt_unique INDEX index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_reloptions OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
***************
*** 4489,4495 ****
  				}
  			| CREATE index_opt_unique INDEX CONCURRENTLY index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_definition OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
--- 4523,4529 ----
  				}
  			| CREATE index_opt_unique INDEX CONCURRENTLY index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_reloptions OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.186
diff -c -p -r1.186 parse_clause.c
*** src/backend/parser/parse_clause.c	22 Jan 2009 20:16:05 -0000	1.186
--- src/backend/parser/parse_clause.c	29 Jan 2009 16:46:45 -0000
***************
*** 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.
***************
*** 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/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.305
diff -c -p -r1.305 utility.c
*** src/backend/tcop/utility.c	22 Jan 2009 20:16:06 -0000	1.305
--- src/backend/tcop/utility.c	29 Jan 2009 17:49:52 -0000
***************
*** 16,21 ****
--- 16,22 ----
   */
  #include "postgres.h"
  
+ #include "access/reloptions.h"
  #include "access/twophase.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
***************
*** 422,427 ****
--- 423,431 ----
  
  					if (IsA(stmt, CreateStmt))
  					{
+ 						Datum	toast_options;
+ 						char   *validnsps[] = { "toast" };
+ 
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_RELATION);
***************
*** 431,437 ****
  						 * needs a secondary relation too.
  						 */
  						CommandCounterIncrement();
! 						AlterTableCreateToastTable(relOid);
  					}
  					else
  					{
--- 435,451 ----
  						 * needs a secondary relation too.
  						 */
  						CommandCounterIncrement();
! 
! 						/* 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
  					{
Index: src/include/access/reloptions.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/reloptions.h,v
retrieving revision 1.11
diff -c -p -r1.11 reloptions.h
*** src/include/access/reloptions.h	26 Jan 2009 19:41:06 -0000	1.11
--- src/include/access/reloptions.h	29 Jan 2009 17:38:30 -0000
***************
*** 240,245 ****
--- 240,246 ----
  					 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,
Index: src/include/catalog/toasting.h
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.219
diff -c -p -r1.219 nodes.h
*** src/include/nodes/nodes.h	22 Jan 2009 20:16:09 -0000	1.219
--- src/include/nodes/nodes.h	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.389
diff -c -p -r1.389 parsenodes.h
*** src/include/nodes/parsenodes.h	22 Jan 2009 20:16:09 -0000	1.389
--- src/include/nodes/parsenodes.h	29 Jan 2009 16:46:45 -0000
***************
*** 533,538 ****
--- 533,549 ----
  } 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
Index: src/interfaces/Makefile
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/interfaces/Makefile,v
retrieving revision 1.56
diff -c -p -r1.56 Makefile
In reply to: Alvaro Herrera (#7)
Re: reloptions with a "namespace"

Alvaro Herrera escreveu:

Alvaro Herrera wrote:

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

I wasn't sure of the best place to add a check. I have added it to
transformRelOptions; I am not entirely comfortable with it, because it
works, but it still allows this:

IMHO it's the appropriate place.

I think the best place would be parseRelOptions. The problem is that
transformRelOptions does not apply any semantics to the values it's
parsing; it doesn't know about the relopt_kind for example. That stuff
is only known by parseRelOptions, but when the options reach that point,
they have already lost the namespace (due to transformRelOptions).

Okay, so I've changed things so that the transformRelOptions' caller is
now in charge of passing an array of valid option namespaces. This is
working A-OK. I'm now going to figure out appropriate pg_dump support
and commit as soon as possible.

I don't like the spreading validnsps' approach. Isn't there a way to
centralize those variables in one place, i.e., reloption.h ? Also, remove an
obsolete comment about toast tables at reloption.h.

--
Euler Taveira de Oliveira
http://www.timbira.com/

#9Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Euler Taveira de Oliveira (#8)
Re: reloptions with a "namespace"

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

Okay, so I've changed things so that the transformRelOptions' caller is
now in charge of passing an array of valid option namespaces. This is
working A-OK. I'm now going to figure out appropriate pg_dump support
and commit as soon as possible.

I don't like the spreading validnsps' approach. Isn't there a way to
centralize those variables in one place, i.e., reloption.h ? Also, remove an
obsolete comment about toast tables at reloption.h.

No, that doesn't work, because we don't know centrally what's the
allowed list of namespaces. In fact that's precisely the problem: for
example, there's no point in having a "toast" namespace for index
reloptions. And for a user-defined access method, we don't know what
the valid namespaces are. Of course, the easiest way is to just state
that there are no valid namespaces other than NULL, and only allow
"toast" for heap, but I think that's not thinking far enough ahead.

The other option I considered was to have another AM entry point that
returns the list of valid namespaces, but that seems to be way overkill,
particularly considering that the current arrays are all NULL.

--
Alvaro Herrera http://www.amazon.com/gp/registry/CTMLCN8V17R4
"La gente vulgar solo piensa en pasar el tiempo;
el que tiene talento, en aprovecharlo"

#10Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Euler Taveira de Oliveira (#8)
1 attachment(s)
Re: reloptions with a "namespace"

New patch attached, with pg_dump support (thanks to Tom for the SQL
heads-up).

Euler Taveira de Oliveira wrote:

I don't like the spreading validnsps' approach. Isn't there a way to
centralize those variables in one place, i.e., reloption.h ?

Maybe one option is to create a #define with the options valid for
heaps?

Also, remove an obsolete comment about toast tables at reloption.h.

I'm not sure about that one -- maybe one day we'll want to separate the
options for toast tables and those for plain tables (for example, surely
we don't need per-row default security in toast tables, or stuff like
that).

--
Alvaro Herrera http://www.amazon.com/gp/registry/CTMLCN8V17R4
"The West won the world not by the superiority of its ideas or values
or religion but rather by its superiority in applying organized violence.
Westerners often forget this fact, non-Westerners never do."
(Samuel P. Huntington)

Attachments:

reloptions-namespace-4.patchtext/x-diff; charset=us-asciiDownload
Index: src/backend/access/common/reloptions.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/common/reloptions.c,v
retrieving revision 1.19
diff -c -p -r1.19 reloptions.c
*** src/backend/access/common/reloptions.c	26 Jan 2009 19:41:06 -0000	1.19
--- src/backend/access/common/reloptions.c	30 Jan 2009 19:42:38 -0000
***************
*** 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
***************
*** 402,415 ****
   * 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.
   *
   * 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)
  {
  	Datum		result;
  	ArrayBuildState *astate;
--- 404,420 ----
   * 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, 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, char *namspace,
! 					char *validnsps[], bool ignoreOids, bool isReset)
  {
  	Datum		result;
  	ArrayBuildState *astate;
***************
*** 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)
--- 449,471 ----
  			/* 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)
***************
*** 468,474 ****
  	 */
  	foreach(cell, defList)
  	{
! 		DefElem    *def = lfirst(cell);
  
  		if (isReset)
  		{
--- 485,492 ----
  	 */
  	foreach(cell, defList)
  	{
! 		ReloptElem    *def = lfirst(cell);
! 
  
  		if (isReset)
  		{
***************
*** 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,
--- 501,562 ----
  			const char *value;
  			Size		len;
  
! 			/*
! 			 * 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 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,
***************
*** 944,950 ****
  }
  
  /*
!  * Parse options for heaps (and perhaps someday toast tables).
   */
  bytea *
  heap_reloptions(char relkind, Datum reloptions, bool validate)
--- 1002,1008 ----
  }
  
  /*
!  * 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/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	29 Jan 2009 16:46:45 -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);
  
  
***************
*** 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;
  
***************
*** 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);
  }
***************
*** 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);
  
***************
*** 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;
***************
*** 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 ----
***************
*** 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/cvs/pgsql/src/backend/commands/cluster.c,v
retrieving revision 1.181
diff -c -p -r1.181 cluster.c
*** src/backend/commands/cluster.c	16 Jan 2009 13:27:23 -0000	1.181
--- src/backend/commands/cluster.c	29 Jan 2009 16:46:45 -0000
***************
*** 668,673 ****
--- 668,674 ----
  	TupleDesc	OldHeapDesc,
  				tupdesc;
  	Oid			OIDNewHeap;
+ 	Oid			toastid;
  	Relation	OldHeap;
  	HeapTuple	tuple;
  	Datum		reloptions;
***************
*** 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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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:
***************
*** 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
***************
*** 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;
***************
*** 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;
***************
*** 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
***************
*** 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/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	29 Jan 2009 17:38:56 -0000
***************
*** 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, NULL, false, false);
  
  	(void) index_reloptions(amoptions, reloptions, true);
  
Index: src/backend/commands/sequence.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/sequence.c,v
retrieving revision 1.157
diff -c -p -r1.157 sequence.c
*** src/backend/commands/sequence.c	20 Jan 2009 18:59:37 -0000	1.157
--- src/backend/commands/sequence.c	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.278
diff -c -p -r1.278 tablecmds.c
*** src/backend/commands/tablecmds.c	22 Jan 2009 20:16:02 -0000	1.278
--- src/backend/commands/tablecmds.c	29 Jan 2009 17:48:02 -0000
***************
*** 351,356 ****
--- 351,357 ----
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
+ 	char	   *validnsps[] = { "toast" };
  
  	/*
  	 * Truncate relname to appropriate length (probably a waste of time, as
***************
*** 418,424 ****
  	/*
  	 * Parse and validate reloptions, if any.
  	 */
! 	reloptions = transformRelOptions((Datum) 0, stmt->options, true, false);
  
  	(void) heap_reloptions(relkind, reloptions, true);
  
--- 419,426 ----
  	/*
  	 * Parse and validate reloptions, if any.
  	 */
! 	reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
! 									 true, false);
  
  	(void) heap_reloptions(relkind, reloptions, true);
  
***************
*** 2572,2578 ****
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid);
  	}
  }
  
--- 2574,2580 ----
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid, (Datum) 0);
  	}
  }
  
***************
*** 6457,6462 ****
--- 6459,6465 ----
  	Datum		repl_val[Natts_pg_class];
  	bool		repl_null[Natts_pg_class];
  	bool		repl_repl[Natts_pg_class];
+ 	char	   *validnsps[] = { "toast" };
  
  	if (defList == NIL)
  		return;					/* nothing to do */
***************
*** 6475,6481 ****
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
--- 6478,6484 ----
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, NULL, validnsps, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
***************
*** 6521,6526 ****
--- 6524,6576 ----
  
  	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);
  }
  
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/commands/view.c,v
retrieving revision 1.113
diff -c -p -r1.113 view.c
*** src/backend/commands/view.c	27 Jan 2009 12:40:15 -0000	1.113
--- src/backend/commands/view.c	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.321
diff -c -p -r1.321 execMain.c
*** src/backend/executor/execMain.c	22 Jan 2009 20:16:03 -0000	1.321
--- src/backend/executor/execMain.c	29 Jan 2009 17:40:15 -0000
***************
*** 2832,2837 ****
--- 2832,2838 ----
  	Oid			intoRelationId;
  	TupleDesc	tupdesc;
  	DR_intorel *myState;
+ 	char	   *validnsps[] = { "toast" };
  
  	Assert(into);
  
***************
*** 2890,2895 ****
--- 2891,2898 ----
  	/* Parse and validate any reloptions */
  	reloptions = transformRelOptions((Datum) 0,
  									 into->options,
+ 									 NULL,
+ 									 validnsps,
  									 true,
  									 false);
  	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
***************
*** 2926,2932 ****
  	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
  	 * the TOAST table will be visible for insertion.
  	 */
! 	AlterTableCreateToastTable(intoRelationId);
  
  	/*
  	 * And open the constructed table for writing.
--- 2929,2944 ----
  	 * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
  	 * the TOAST table will be visible for insertion.
  	 */
! 	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.
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.421
diff -c -p -r1.421 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	22 Jan 2009 20:16:03 -0000	1.421
--- src/backend/nodes/copyfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 2125,2130 ****
--- 2125,2142 ----
  	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,4084 ****
--- 4091,4099 ----
  		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/equalfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.346
diff -c -p -r1.346 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	22 Jan 2009 20:16:03 -0000	1.346
--- src/backend/nodes/equalfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 2099,2104 ****
--- 2099,2114 ----
  }
  
  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,2860 ****
--- 2865,2873 ----
  		case T_OptionDefElem:
  			retval = _equalOptionDefElem(a, b);
  			break;
+ 		case T_ReloptElem:
+ 			retval = _equalReloptElem(a, b);
+ 			break;
  		case T_LockingClause:
  			retval = _equalLockingClause(a, b);
  			break;
Index: src/backend/nodes/makefuncs.c
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/nodes/outfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.350
diff -c -p -r1.350 outfuncs.c
*** src/backend/nodes/outfuncs.c	22 Jan 2009 20:16:04 -0000	1.350
--- src/backend/nodes/outfuncs.c	29 Jan 2009 16:46:45 -0000
***************
*** 1805,1810 ****
--- 1805,1820 ----
  }
  
  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,2775 ****
--- 2780,2788 ----
  			case T_DefElem:
  				_outDefElem(str, obj);
  				break;
+ 			case T_ReloptElem:
+ 				_outReloptElem(str, obj);
+ 				break;
  			case T_LockingClause:
  				_outLockingClause(str, obj);
  				break;
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.656
diff -c -p -r2.656 gram.y
*** src/backend/parser/gram.y	22 Jan 2009 20:16:05 -0000	2.656
--- src/backend/parser/gram.y	29 Jan 2009 18:44:23 -0000
***************
*** 164,169 ****
--- 164,170 ----
  	FuncWithArgs		*funwithargs;
  	DefElem				*defelt;
  	OptionDefElem		*optdef;
+ 	ReloptElem			*reloptel;
  	SortBy				*sortby;
  	WindowDef			*windef;
  	JoinExpr			*jexpr;
***************
*** 271,276 ****
--- 272,278 ----
  
  %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,290 ****
  				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
--- 286,292 ----
  				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
***************
*** 342,347 ****
--- 344,350 ----
  %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,1787 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
--- 1784,1790 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
***************
*** 1789,1795 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
--- 1792,1798 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
***************
*** 1814,1819 ****
--- 1817,1853 ----
  			| /* 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,2448 ****
  
  /* 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; }
  		;
  
--- 2474,2482 ----
  
  /* 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; }
  		;
  
***************
*** 4473,4479 ****
  
  IndexStmt:	CREATE index_opt_unique INDEX index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_definition OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
--- 4507,4513 ----
  
  IndexStmt:	CREATE index_opt_unique INDEX index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_reloptions OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
***************
*** 4489,4495 ****
  				}
  			| CREATE index_opt_unique INDEX CONCURRENTLY index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_definition OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
--- 4523,4529 ----
  				}
  			| CREATE index_opt_unique INDEX CONCURRENTLY index_name
  			ON qualified_name access_method_clause '(' index_params ')'
! 			opt_reloptions OptTableSpace where_clause
  				{
  					IndexStmt *n = makeNode(IndexStmt);
  					n->unique = $2;
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.186
diff -c -p -r1.186 parse_clause.c
*** src/backend/parser/parse_clause.c	22 Jan 2009 20:16:05 -0000	1.186
--- src/backend/parser/parse_clause.c	29 Jan 2009 16:46:45 -0000
***************
*** 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.
***************
*** 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/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.305
diff -c -p -r1.305 utility.c
*** src/backend/tcop/utility.c	22 Jan 2009 20:16:06 -0000	1.305
--- src/backend/tcop/utility.c	29 Jan 2009 17:49:52 -0000
***************
*** 16,21 ****
--- 16,22 ----
   */
  #include "postgres.h"
  
+ #include "access/reloptions.h"
  #include "access/twophase.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
***************
*** 422,427 ****
--- 423,431 ----
  
  					if (IsA(stmt, CreateStmt))
  					{
+ 						Datum	toast_options;
+ 						char   *validnsps[] = { "toast" };
+ 
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
  												RELKIND_RELATION);
***************
*** 431,437 ****
  						 * needs a secondary relation too.
  						 */
  						CommandCounterIncrement();
! 						AlterTableCreateToastTable(relOid);
  					}
  					else
  					{
--- 435,451 ----
  						 * needs a secondary relation too.
  						 */
  						CommandCounterIncrement();
! 
! 						/* 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
  					{
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.517
diff -c -p -r1.517 pg_dump.c
*** src/bin/pg_dump/pg_dump.c	27 Jan 2009 12:40:15 -0000	1.517
--- src/bin/pg_dump/pg_dump.c	30 Jan 2009 19:31:36 -0000
***************
*** 3100,3105 ****
--- 3100,3106 ----
  	int			i_owning_col;
  	int			i_reltablespace;
  	int			i_reloptions;
+ 	int			i_toastreloptions;
  
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
***************
*** 3131,3152 ****
  		 * 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, "
  						  "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 "
  						  "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') "
  						  "order by c.oid",
  						  username_subquery,
  						  RELKIND_SEQUENCE,
--- 3132,3155 ----
  		 * owning column, if any (note this dependency is AUTO as of 8.2)
  		 */
  		appendPQExpBuffer(query,
! 						  "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(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') "
! 						  "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,
***************
*** 3197,3203 ****
  						  "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 "
  						  "from pg_class c "
  						  "left join pg_depend d on "
  						  "(c.relkind = '%c' and "
--- 3200,3207 ----
  						  "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 toast_reloptions "
  						  "from pg_class c "
  						  "left join pg_depend d on "
  						  "(c.relkind = '%c' and "
***************
*** 3226,3232 ****
  						  "d.refobjid as owning_tab, "
  						  "d.refobjsubid as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions "
  						  "from pg_class c "
  						  "left join pg_depend d on "
  						  "(c.relkind = '%c' and "
--- 3230,3237 ----
  						  "d.refobjid as owning_tab, "
  						  "d.refobjsubid as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions, "
! 						  "NULL as toast_reloptions "
  						  "from pg_class c "
  						  "left join pg_depend d on "
  						  "(c.relkind = '%c' and "
***************
*** 3251,3257 ****
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions "
  						  "from pg_class "
  						  "where relkind in ('%c', '%c', '%c') "
  						  "order by oid",
--- 3256,3263 ----
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions, "
! 						  "NULL as toast_reloptions "
  						  "from pg_class "
  						  "where relkind in ('%c', '%c', '%c') "
  						  "order by oid",
***************
*** 3271,3277 ****
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions "
  						  "from pg_class "
  						  "where relkind in ('%c', '%c', '%c') "
  						  "order by oid",
--- 3277,3284 ----
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions, "
! 						  "NULL as toast_reloptions "
  						  "from pg_class "
  						  "where relkind in ('%c', '%c', '%c') "
  						  "order by oid",
***************
*** 3301,3307 ****
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions "
  						  "from pg_class c "
  						  "where relkind in ('%c', '%c') "
  						  "order by oid",
--- 3308,3315 ----
  						  "NULL::oid as owning_tab, "
  						  "NULL::int4 as owning_col, "
  						  "NULL as reltablespace, "
! 						  "NULL as reloptions, "
! 						  "NULL as toast_reloptions "
  						  "from pg_class c "
  						  "where relkind in ('%c', '%c') "
  						  "order by oid",
***************
*** 3344,3349 ****
--- 3352,3358 ----
  	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,3394 ****
--- 3398,3404 ----
  		}
  		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,9707 ****
  			appendPQExpBuffer(q, ")");
  		}
  
! 		if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0)
! 			appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions);
  
  		appendPQExpBuffer(q, ";\n");
  
--- 9710,9733 ----
  			appendPQExpBuffer(q, ")");
  		}
  
! 		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");
  
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.149
diff -c -p -r1.149 pg_dump.h
*** src/bin/pg_dump/pg_dump.h	27 Jan 2009 12:40:15 -0000	1.149
--- src/bin/pg_dump/pg_dump.h	30 Jan 2009 19:28:37 -0000
***************
*** 221,226 ****
--- 221,227 ----
  	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? */
Index: src/include/access/reloptions.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/reloptions.h,v
retrieving revision 1.11
diff -c -p -r1.11 reloptions.h
*** src/include/access/reloptions.h	26 Jan 2009 19:41:06 -0000	1.11
--- src/include/access/reloptions.h	29 Jan 2009 17:38:30 -0000
***************
*** 240,245 ****
--- 240,246 ----
  					 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,
Index: src/include/catalog/toasting.h
===================================================================
RCS file: /home/alvherre/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	29 Jan 2009 16:46:45 -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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/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	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.219
diff -c -p -r1.219 nodes.h
*** src/include/nodes/nodes.h	22 Jan 2009 20:16:09 -0000	1.219
--- src/include/nodes/nodes.h	29 Jan 2009 16:46:45 -0000
***************
*** 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/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.389
diff -c -p -r1.389 parsenodes.h
*** src/include/nodes/parsenodes.h	22 Jan 2009 20:16:09 -0000	1.389
--- src/include/nodes/parsenodes.h	29 Jan 2009 16:46:45 -0000
***************
*** 533,538 ****
--- 533,549 ----
  } 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
Index: src/interfaces/Makefile
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/interfaces/Makefile,v
retrieving revision 1.56
diff -c -p -r1.56 Makefile
In reply to: Alvaro Herrera (#10)
2 attachment(s)
Re: reloptions with a "namespace"

Alvaro Herrera escreveu:

New patch attached, with pg_dump support (thanks to Tom for the SQL
heads-up).

Great! We're close. Just two minor gripes:

+ char *validnsps[] = { "toast" };

Surely, you forgot to add a NULL at the end. Patch is attached.

IIRC, my last patch includes a partial validation code for RESET cases. For
example, the last SQL will not be atomic (invalid reloption silently ignored).
So, why not apply the namespace validation code to RESET case too? Patch is
attached too. It does not handle the reloptions validation because the relOpts
initialization code is at parseRelOptions(); i leave it for a future refactor.

euler=# create table foo (a text) with (fillfactor=10);
CREATE TABLE
euler=# \d+ foo
Tabela "public.foo"
Coluna | Tipo | Modificadores | Storage | Descri��o
--------+------+---------------+----------+-----------
a | text | | extended |
T�m OIDs: n�o
Options: fillfactor=10

euler=# alter table foo reset (fillfactor,foo.fillfactor);
ALTER TABLE
euler=# \d+ foo
Tabela "public.foo"
Coluna | Tipo | Modificadores | Storage | Descri��o
--------+------+---------------+----------+-----------
a | text | | extended |
T�m OIDs: n�o

--
Euler Taveira de Oliveira
http://www.timbira.com/

Attachments:

relopt-nmspc-1.difftext/plain; name=relopt-nmspc-1.diffDownload
--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = off;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET escape_string_warning = off;

--
-- Name: plperl; Type: PROCEDURAL LANGUAGE; Schema: -; Owner: euler
--

CREATE PROCEDURAL LANGUAGE plperl;


ALTER PROCEDURAL LANGUAGE plperl OWNER TO euler;

--
-- Name: plpgsql; Type: PROCEDURAL LANGUAGE; Schema: -; Owner: euler
--

CREATE PROCEDURAL LANGUAGE plpgsql;


ALTER PROCEDURAL LANGUAGE plpgsql OWNER TO euler;

--
-- Name: plpythonu; Type: PROCEDURAL LANGUAGE; Schema: -; Owner: euler
--

CREATE PROCEDURAL LANGUAGE plpythonu;


ALTER PROCEDURAL LANGUAGE plpythonu OWNER TO euler;

--
-- Name: pltcl; Type: PROCEDURAL LANGUAGE; Schema: -; Owner: euler
--

CREATE PROCEDURAL LANGUAGE pltcl;


ALTER PROCEDURAL LANGUAGE pltcl OWNER TO euler;

SET search_path = public, pg_catalog;

SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: tst1; Type: TABLE; Schema: public; Owner: euler; Tablespace: 
--

CREATE TABLE tst1 (
    a text
)
WITH (fillfactor=10);


ALTER TABLE public.tst1 OWNER TO euler;

--
-- Name: tst2; Type: TABLE; Schema: public; Owner: euler; Tablespace: 
--

CREATE TABLE tst2 (
    a text
)
WITH (toast.fillfactor=20);


ALTER TABLE public.tst2 OWNER TO euler;

--
-- Name: tst3; Type: TABLE; Schema: public; Owner: euler; Tablespace: 
--

CREATE TABLE tst3 (
    a text
)
WITH (fillfactor=10, toast.fillfactor=20);


ALTER TABLE public.tst3 OWNER TO euler;

--
-- Data for Name: tst1; Type: TABLE DATA; Schema: public; Owner: euler
--

COPY tst1 (a) FROM stdin;
\.


--
-- Data for Name: tst2; Type: TABLE DATA; Schema: public; Owner: euler
--

COPY tst2 (a) FROM stdin;
\.


--
-- Data for Name: tst3; Type: TABLE DATA; Schema: public; Owner: euler
--

COPY tst3 (a) FROM stdin;
\.


--
-- Name: public; Type: ACL; Schema: -; Owner: euler
--

REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM euler;
GRANT ALL ON SCHEMA public TO euler;
GRANT ALL ON SCHEMA public TO PUBLIC;


--
-- PostgreSQL database dump complete
--

relopt-nmspc-2.difftext/plain; name=relopt-nmspc-2.diffDownload
*** pgsql.alvaro/src/backend/access/common/reloptions.c	2009-01-31 02:01:21.000000000 -0200
--- pgsql.euler/src/backend/access/common/reloptions.c	2009-01-31 02:16:29.000000000 -0200
***************
*** 487,492 ****
--- 487,519 ----
  	{
  		ReloptElem    *def = lfirst(cell);
  
+ 		/*
+ 		 * 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 (isReset)
  		{
***************
*** 501,534 ****
  			const char *value;
  			Size		len;
  
- 			/*
- 			 * 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;
  
--- 528,533 ----
In reply to: Euler Taveira de Oliveira (#11)
1 attachment(s)
Re: reloptions with a "namespace"

Euler Taveira de Oliveira escreveu:

[Forgot the first patch...]

Alvaro Herrera escreveu:

New patch attached, with pg_dump support (thanks to Tom for the SQL
heads-up).

Great! We're close. Just two minor gripes:

+ char *validnsps[] = { "toast" };

Surely, you forgot to add a NULL at the end. Patch is attached.

IIRC, my last patch includes a partial validation code for RESET cases. For
example, the last SQL will not be atomic (invalid reloption silently ignored).
So, why not apply the namespace validation code to RESET case too? Patch is
attached too. It does not handle the reloptions validation because the relOpts
initialization code is at parseRelOptions(); i leave it for a future refactor.

euler=# create table foo (a text) with (fillfactor=10);
CREATE TABLE
euler=# \d+ foo
Tabela "public.foo"
Coluna | Tipo | Modificadores | Storage | Descri��o
--------+------+---------------+----------+-----------
a | text | | extended |
T�m OIDs: n�o
Options: fillfactor=10

euler=# alter table foo reset (fillfactor,foo.fillfactor);
ALTER TABLE
euler=# \d+ foo
Tabela "public.foo"
Coluna | Tipo | Modificadores | Storage | Descri��o
--------+------+---------------+----------+-----------
a | text | | extended |
T�m OIDs: n�o

------------------------------------------------------------------------

--
Euler Taveira de Oliveira
http://www.timbira.com/

Attachments:

relopt-nmspc-3.difftext/plain; name=relopt-nmspc-3.diffDownload
diff -cr pgsql.alvaro/src/backend/commands/tablecmds.c pgsql.euler/src/backend/commands/tablecmds.c
*** pgsql.alvaro/src/backend/commands/tablecmds.c	2009-01-31 02:01:22.000000000 -0200
--- pgsql.euler/src/backend/commands/tablecmds.c	2009-01-31 01:47:32.000000000 -0200
***************
*** 351,357 ****
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
! 	char	   *validnsps[] = { "toast" };
  
  	/*
  	 * Truncate relname to appropriate length (probably a waste of time, as
--- 351,357 ----
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
! 	static char	   *validnsps[] = { "toast", NULL };
  
  	/*
  	 * Truncate relname to appropriate length (probably a waste of time, as
***************
*** 6459,6465 ****
  	Datum		repl_val[Natts_pg_class];
  	bool		repl_null[Natts_pg_class];
  	bool		repl_repl[Natts_pg_class];
! 	char	   *validnsps[] = { "toast" };
  
  	if (defList == NIL)
  		return;					/* nothing to do */
--- 6459,6465 ----
  	Datum		repl_val[Natts_pg_class];
  	bool		repl_null[Natts_pg_class];
  	bool		repl_repl[Natts_pg_class];
! 	static char	   *validnsps[] = { "toast", NULL };
  
  	if (defList == NIL)
  		return;					/* nothing to do */
diff -cr pgsql.alvaro/src/backend/executor/execMain.c pgsql.euler/src/backend/executor/execMain.c
*** pgsql.alvaro/src/backend/executor/execMain.c	2009-01-31 02:01:22.000000000 -0200
--- pgsql.euler/src/backend/executor/execMain.c	2009-01-31 01:48:19.000000000 -0200
***************
*** 2832,2838 ****
  	Oid			intoRelationId;
  	TupleDesc	tupdesc;
  	DR_intorel *myState;
! 	char	   *validnsps[] = { "toast" };
  
  	Assert(into);
  
--- 2832,2838 ----
  	Oid			intoRelationId;
  	TupleDesc	tupdesc;
  	DR_intorel *myState;
! 	static char	   *validnsps[] = { "toast", NULL };
  
  	Assert(into);
  
Somente em pgsql.euler/src/backend/parser: gram.c
Somente em pgsql.euler/src/backend/parser: gram.h
Somente em pgsql.euler/src/backend/parser: scan.c
diff -cr pgsql.alvaro/src/backend/tcop/utility.c pgsql.euler/src/backend/tcop/utility.c
*** pgsql.alvaro/src/backend/tcop/utility.c	2009-01-31 02:01:22.000000000 -0200
--- pgsql.euler/src/backend/tcop/utility.c	2009-01-31 01:47:51.000000000 -0200
***************
*** 424,430 ****
  					if (IsA(stmt, CreateStmt))
  					{
  						Datum	toast_options;
! 						char   *validnsps[] = { "toast" };
  
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
--- 424,430 ----
  					if (IsA(stmt, CreateStmt))
  					{
  						Datum	toast_options;
! 						static char   *validnsps[] = { "toast", NULL };
  
  						/* Create the table itself */
  						relOid = DefineRelation((CreateStmt *) stmt,
#13Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Euler Taveira de Oliveira (#11)
Re: reloptions with a "namespace"

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

New patch attached, with pg_dump support (thanks to Tom for the SQL
heads-up).

Great! We're close. Just two minor gripes:

+ char *validnsps[] = { "toast" };

Surely, you forgot to add a NULL at the end. Patch is attached.

Right, thanks.

IIRC, my last patch includes a partial validation code for RESET cases. For
example, the last SQL will not be atomic (invalid reloption silently ignored).
So, why not apply the namespace validation code to RESET case too? Patch is
attached too.

No, we must not validate the options passed to RESET, because we want to
be able to reset even options that we do not currently think that are
valid. Consider that we might be trying to clean up after options set
by a previous version of a module.

--
Alvaro Herrera http://www.PlanetPostgreSQL.org/
"Saca el libro que tu religi�n considere como el indicado para encontrar la
oraci�n que traiga paz a tu alma. Luego rebootea el computador
y ve si funciona" (Carlos Ducl�s)

In reply to: Alvaro Herrera (#13)
Re: reloptions with a "namespace"

Alvaro Herrera escreveu:

IIRC, my last patch includes a partial validation code for RESET cases. For
example, the last SQL will not be atomic (invalid reloption silently ignored).
So, why not apply the namespace validation code to RESET case too? Patch is
attached too.

No, we must not validate the options passed to RESET, because we want to
be able to reset even options that we do not currently think that are
valid. Consider that we might be trying to clean up after options set
by a previous version of a module.

Ah, idea withdrawn. But we should at least document this behavior.

--
Euler Taveira de Oliveira
http://www.timbira.com/

#15Alvaro Herrera
alvherre@commandprompt.com
In reply to: Euler Taveira de Oliveira (#14)
Re: reloptions with a "namespace"

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

IIRC, my last patch includes a partial validation code for RESET cases. For
example, the last SQL will not be atomic (invalid reloption silently ignored).
So, why not apply the namespace validation code to RESET case too? Patch is
attached too.

No, we must not validate the options passed to RESET, because we want to
be able to reset even options that we do not currently think that are
valid. Consider that we might be trying to clean up after options set
by a previous version of a module.

Ah, idea withdrawn. But we should at least document this behavior.

Well, it is documented -- see amoptions here
http://www.postgresql.org/docs/8.3/static/index-functions.html

The problem with this is that documentation for reloptions is scattered
all over the place. I think we should have a separate section somewhere
on which they are discussed at length.

--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.