reloptions and toast tables

Started by Alvaro Herreraabout 17 years ago16 messages
#1Alvaro Herrera
alvherre@commandprompt.com

Right now we don't allow setting reloptions to toast tables:

=# alter table pg_toast.pg_toast_16395 set (fillfactor = 40);
ERROR: "pg_toast_16395" is not a table or index

However this is needed for autovacuum, per previous discussion.

I'm wondering if I should just allow all reloptions (including
fillfactor) or just the autovacuum ones.

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

In reply to: Alvaro Herrera (#1)
Re: reloptions and toast tables

Alvaro Herrera escreveu:

I'm wondering if I should just allow all reloptions (including
fillfactor) or just the autovacuum ones.

Yes, please. But i'm afraid it is too 'complicated' to expose
'pg_toast.pg_toast_xxxxx' to user (but we can solve it with good
documentation). What about xxx_toast reloptions? The con is that we need to
add 2 reloptions if the new reloption is table-related.

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

#3Alvaro Herrera
alvherre@commandprompt.com
In reply to: Euler Taveira de Oliveira (#2)
Re: reloptions and toast tables

Euler Taveira de Oliveira wrote:

Alvaro Herrera escreveu:

I'm wondering if I should just allow all reloptions (including
fillfactor) or just the autovacuum ones.

Yes, please. But i'm afraid it is too 'complicated' to expose
'pg_toast.pg_toast_xxxxx' to user (but we can solve it with good
documentation). What about xxx_toast reloptions? The con is that we need to
add 2 reloptions if the new reloption is table-related.

Hmm, now that I look at that again, it seems a very bad idea.

Your idea of having separate options for the toast table, I take you
mean having toast_autovacuum_enabled and such, and they would be
attached to the main table? If that's what you mean, I admit I don't
like it either -- we would duplicate the size of the reloptions table
for no good reason :-(

It would be better to have a separate command, that doesn't force the
user to look up the toast table name. I'm not sure what such a syntax
would actually look like though. I'm open to ideas.

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);
ALTER TABLE foo SET (toast.autovacuum_enabled = false);
ALTER TABLE foo TOAST SET (autovacuum_enabled = false);
ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

...?

--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#3)
Re: reloptions and toast tables

Alvaro Herrera <alvherre@commandprompt.com> writes:

Euler Taveira de Oliveira wrote:

Yes, please. But i'm afraid it is too 'complicated' to expose
'pg_toast.pg_toast_xxxxx' to user (but we can solve it with good
documentation).

Hmm, now that I look at that again, it seems a very bad idea.

Yeah --- whatever solution you pick should be amenable to letting
pg_dump preserve the settings. Directly referencing the toast
table seems right out on that basis.

regards, tom lane

#5Zdenek Kotala
Zdenek.Kotala@Sun.COM
In reply to: Alvaro Herrera (#3)
Re: reloptions and toast tables

Alvaro Herrera napsal(a):

ALTER TABLE foo SET (toast.autovacuum_enabled = false);

+1

Do not forget on toast index as well.

ALTER TABLE foo SET (toast_idx.fillfactor = 50);

Another potential problem with toast setting is that reloption is toastable and
it could generates loops in detoasting pg_class tuples. For example toast chunk
size cannot be implement like reloption (or pg_class should use every time
default values).

Zdenek

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Zdenek Kotala (#5)
Re: reloptions and toast tables

Zdenek Kotala <Zdenek.Kotala@Sun.COM> writes:

Another potential problem with toast setting is that reloption is toastable and
it could generates loops in detoasting pg_class tuples. For example toast chunk
size cannot be implement like reloption (or pg_class should use every time
default values).

Nonsense. Toast chunk size isn't going to become variable *at all*,
unless we go over to the proposed toast indexing method that allows
the chunks to be self-identifying; in which case there's no problem
in pg_class or anyplace else.

regards, tom lane

#7Zdenek Kotala
Zdenek.Kotala@Sun.COM
In reply to: Tom Lane (#6)
Re: reloptions and toast tables

Tom Lane napsal(a):

Zdenek Kotala <Zdenek.Kotala@Sun.COM> writes:

Another potential problem with toast setting is that reloption is toastable and
it could generates loops in detoasting pg_class tuples. For example toast chunk
size cannot be implement like reloption (or pg_class should use every time
default values).

Nonsense. Toast chunk size isn't going to become variable *at all*,
unless we go over to the proposed toast indexing method that allows
the chunks to be self-identifying; in which case there's no problem
in pg_class or anyplace else.

I know. It was only example that some reloption cannot be applied on pg_class
relation and pg_class toast table, because it could introduce chicken/egg problem.

Zdenek

#8Jaime Casanova
jcasanov@systemguards.com.ec
In reply to: Alvaro Herrera (#3)
Re: reloptions and toast tables

On 12/20/08, Alvaro Herrera <alvherre@commandprompt.com> wrote:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);

...

ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

i will be happy with any of this options (actually i prefer the second
one but don't have a strong argument against the first)

--
Atentamente,
Jaime Casanova
Soporte y capacitación de PostgreSQL
Asesoría y desarrollo de sistemas
Guayaquil - Ecuador
Cel. +59387171157

#9Peter Eisentraut
peter_e@gmx.net
In reply to: Alvaro Herrera (#3)
Re: reloptions and toast tables

On Sunday 21 December 2008 01:48:42 Alvaro Herrera wrote:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);
ALTER TABLE foo SET (toast.autovacuum_enabled = false);
ALTER TABLE foo TOAST SET (autovacuum_enabled = false);
ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

The last two don't appear to allow setting TOAST and non-TOAST options in one
go. I think it would be handy to allow that, though.

#10Alvaro Herrera
alvherre@commandprompt.com
In reply to: Peter Eisentraut (#9)
1 attachment(s)
Re: reloptions and toast tables

Peter Eisentraut wrote:

On Sunday 21 December 2008 01:48:42 Alvaro Herrera wrote:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);
ALTER TABLE foo SET (toast.autovacuum_enabled = false);
ALTER TABLE foo TOAST SET (autovacuum_enabled = false);
ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

The last two don't appear to allow setting TOAST and non-TOAST options in one
go. I think it would be handy to allow that, though.

Agreed -- so I'm now playing with this version:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);

So the grammar modifications needed to accept that are attached. The
support code is a lot messier than I'd like :-(

--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

relopt-toast-grammar.patchtext/x-diff; charset=us-asciiDownload
Index: src/backend/commands/define.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/define.c,v
retrieving revision 1.101
diff -c -p -r1.101 define.c
*** src/backend/commands/define.c	1 Jan 2008 19:45:48 -0000	1.101
--- src/backend/commands/define.c	31 Dec 2008 17:53:38 -0000
*************** defGetTypeLength(DefElem *def)
*** 306,319 ****
  }
  
  /*
!  * 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;
  }
--- 306,320 ----
  }
  
  /*
!  * Create a TDefElem setting "oids" to the specified value.
   */
! TDefElem *
  defWithOids(bool value)
  {
! 	TDefElem    *f = makeNode(TDefElem);
  
  	f->defname = "oids";
  	f->arg = (Node *) makeInteger(value);
+ 	f->toast = false;
  	return f;
  }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.418
diff -c -p -r1.418 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	31 Dec 2008 00:08:35 -0000	1.418
--- src/backend/nodes/copyfuncs.c	31 Dec 2008 17:16:59 -0000
*************** _copyDefElem(DefElem *from)
*** 2112,2117 ****
--- 2112,2130 ----
  	return newnode;
  }
  
+ static TDefElem *
+ _copyTDefElem(TDefElem *from)
+ {
+ 	TDefElem   *newnode = makeNode(TDefElem);
+ 
+ 	COPY_STRING_FIELD(defname);
+ 	COPY_NODE_FIELD(arg);
+ 	COPY_SCALAR_FIELD(toast);
+ 
+ 	return newnode;
+ }
+ 
+ 
  static OptionDefElem *
  _copyOptionDefElem(OptionDefElem *from)
  {
*************** copyObject(void *from)
*** 4063,4068 ****
--- 4076,4084 ----
  		case T_DefElem:
  			retval = _copyDefElem(from);
  			break;
+ 		case T_TDefElem:
+ 			retval = _copyTDefElem(from);
+ 			break;
  		case T_OptionDefElem:
  			retval = _copyOptionDefElem(from);
  			break;
Index: src/backend/nodes/makefuncs.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/makefuncs.c,v
retrieving revision 1.61
diff -c -p -r1.61 makefuncs.c
*** src/backend/nodes/makefuncs.c	19 Dec 2008 16:25:17 -0000	1.61
--- src/backend/nodes/makefuncs.c	31 Dec 2008 16:51:09 -0000
*************** makeDefElem(char *name, Node *arg)
*** 363,368 ****
--- 363,383 ----
  }
  
  /*
+  * makeTDefElem -
+  *  build a TDefElem node
+  */
+ TDefElem *
+ makeTDefElem(char *name, Node *arg, bool toast)
+ {
+ 	TDefElem   *res = makeNode(TDefElem);
+ 
+ 	res->defname = name;
+ 	res->arg = arg;
+ 	res->toast = toast;
+ 	return res;
+ }
+ 
+ /*
   * makeOptionDefElem -
   *	build an OptionDefElem node
   */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.348
diff -c -p -r1.348 outfuncs.c
*** src/backend/nodes/outfuncs.c	31 Dec 2008 00:08:36 -0000	1.348
--- src/backend/nodes/outfuncs.c	31 Dec 2008 17:17:19 -0000
*************** _outDefElem(StringInfo str, DefElem *nod
*** 1807,1812 ****
--- 1807,1822 ----
  }
  
  static void
+ _outTDefElem(StringInfo str, TDefElem *node)
+ {
+ 	WRITE_NODE_TYPE("TDEFELEM");
+ 
+ 	WRITE_STRING_FIELD(defname);
+ 	WRITE_NODE_FIELD(arg);
+ 	WRITE_BOOL_FIELD(toast);
+ }
+ 
+ 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_TDefElem:
+ 				_outTDefElem(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.650
diff -c -p -r2.650 gram.y
*** src/backend/parser/gram.y	31 Dec 2008 02:25:04 -0000	2.650
--- src/backend/parser/gram.y	31 Dec 2008 19:38:02 -0000
*************** static TypeName *TableFuncTypeName(List 
*** 156,161 ****
--- 156,162 ----
  	FunctionParameterMode fun_param_mode;
  	FuncWithArgs		*funwithargs;
  	DefElem				*defelt;
+ 	TDefElem			*tdefelt;
  	OptionDefElem		*optdef;
  	SortBy				*sortby;
  	WindowDef			*windef;
*************** static TypeName *TableFuncTypeName(List 
*** 263,268 ****
--- 264,270 ----
  
  %type <list>	stmtblock stmtmulti
  				OptTableElementList TableElementList OptInherit definition
+ 				t_definition
  				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 
*** 275,281 ****
  				any_operator expr_list attrs
  				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
--- 277,283 ----
  				any_operator expr_list attrs
  				target_list insert_column_list set_target_list
  				set_clause_list set_clause multiple_set_clause
! 				ctext_expr_list ctext_row def_list t_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
*************** static TypeName *TableFuncTypeName(List 
*** 333,338 ****
--- 335,341 ----
  %type <node>	TableElement ConstraintElem TableFuncElement
  %type <node>	columnDef
  %type <defelt>	def_elem old_aggr_elem
+ %type <tdefelt>	t_def_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
*************** static TypeName *TableFuncTypeName(List 
*** 486,492 ****
  	SYMMETRIC SYSID SYSTEM_P
  
  	TABLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
! 	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
  	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
--- 489,495 ----
  	SYMMETRIC SYSID SYSTEM_P
  
  	TABLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
! 	TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
  	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
*************** alter_table_cmd:
*** 1772,1778 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
--- 1775,1781 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET t_definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
*************** alter_table_cmd:
*** 1780,1786 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
--- 1783,1789 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET t_definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
*************** alter_using:
*** 1805,1811 ****
--- 1808,1837 ----
  			| /* EMPTY */				{ $$ = NULL; }
  		;
  
+ t_definition: '(' t_def_list ')'					{ $$ = $2; }
+ 		;
+ 
+ t_def_list:		t_def_elem							{ $$ = list_make1($1); }
+ 				| t_def_list ',' t_def_elem			{ $$ = lappend($1, $3); }
+ 		;
  
+ t_def_elem:	TOAST ColLabel '=' def_arg
+ 				{
+ 					$$ = makeTDefElem($2, (Node *) $4, true);
+ 				}
+ 			| TOAST ColLabel
+ 				{
+ 					$$ = makeTDefElem($2, NULL, true);
+ 				}
+ 			| ColLabel '=' def_arg
+ 				{
+ 					$$ = makeTDefElem($1, (Node *) $3, false);
+ 				}
+ 			| ColLabel
+ 				{
+ 					$$ = makeTDefElem($1, NULL, false);
+ 				}
+ 		;
  
  /*****************************************************************************
   *
*************** OptInherit: INHERITS '(' qualified_name_
*** 2431,2437 ****
  
  /* 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; }
--- 2457,2463 ----
  
  /* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */
  OptWith:
! 			WITH t_definition			{ $$ = $2; }
  			| WITH OIDS					{ $$ = list_make1(defWithOids(true)); }
  			| WITHOUT OIDS				{ $$ = list_make1(defWithOids(false)); }
  			| /*EMPTY*/					{ $$ = NIL; }
*************** unreserved_keyword:
*** 10234,10239 ****
--- 10260,10266 ----
  			| TEMPLATE
  			| TEMPORARY
  			| TEXT_P
+ 			| TOAST
  			| TRANSACTION
  			| TRIGGER
  			| TRUNCATE
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.208
diff -c -p -r1.208 keywords.c
*** src/backend/parser/keywords.c	31 Dec 2008 00:08:37 -0000	1.208
--- src/backend/parser/keywords.c	31 Dec 2008 17:00:37 -0000
*************** const ScanKeyword ScanKeywords[] = {
*** 382,387 ****
--- 382,388 ----
  	{"time", TIME, COL_NAME_KEYWORD},
  	{"timestamp", TIMESTAMP, COL_NAME_KEYWORD},
  	{"to", TO, RESERVED_KEYWORD},
+ 	{"toast", TOAST, UNRESERVED_KEYWORD},
  	{"trailing", TRAILING, RESERVED_KEYWORD},
  	{"transaction", TRANSACTION, UNRESERVED_KEYWORD},
  	{"treat", TREAT, COL_NAME_KEYWORD},
Index: src/include/commands/defrem.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/commands/defrem.h,v
retrieving revision 1.91
diff -c -p -r1.91 defrem.h
*** src/include/commands/defrem.h	19 Dec 2008 16:25:19 -0000	1.91
--- src/include/commands/defrem.h	31 Dec 2008 17:53:50 -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,150 ----
  extern List *defGetQualifiedName(DefElem *def);
  extern TypeName *defGetTypeName(DefElem *def);
  extern int	defGetTypeLength(DefElem *def);
! extern TDefElem *defWithOids(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.64
diff -c -p -r1.64 makefuncs.h
*** src/include/nodes/makefuncs.h	19 Dec 2008 16:25:19 -0000	1.64
--- src/include/nodes/makefuncs.h	31 Dec 2008 17:13:59 -0000
*************** extern FuncExpr *makeFuncExpr(Oid funcid
*** 67,72 ****
--- 67,74 ----
  
  extern DefElem *makeDefElem(char *name, Node *arg);
  
+ extern TDefElem *makeTDefElem(char *name, Node *arg, bool toast);
+ 
  extern OptionDefElem *makeOptionDefElem(int op, DefElem *def);
  
  #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.217
diff -c -p -r1.217 nodes.h
*** src/include/nodes/nodes.h	28 Dec 2008 18:54:00 -0000	1.217
--- src/include/nodes/nodes.h	31 Dec 2008 16:51:31 -0000
*************** typedef enum NodeTag
*** 362,367 ****
--- 362,368 ----
  	T_IndexElem,
  	T_Constraint,
  	T_DefElem,
+ 	T_TDefElem,
  	T_OptionDefElem,
  	T_RangeTblEntry,
  	T_SortGroupClause,
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.386
diff -c -p -r1.386 parsenodes.h
*** src/include/nodes/parsenodes.h	31 Dec 2008 00:08:38 -0000	1.386
--- src/include/nodes/parsenodes.h	31 Dec 2008 16:58:07 -0000
*************** typedef struct DefElem
*** 521,526 ****
--- 521,538 ----
  } DefElem;
  
  /*
+  * TDefElem -
+  * 		As DefElem, with a flag to distinguish whether it applies to TOAST
+  */
+ typedef struct TDefElem
+ {
+ 	NodeTag		type;
+ 	char	   *defname;
+ 	Node	   *arg;
+ 	bool		toast;
+ } TDefElem;
+ 
+ /*
   * Option definition. Used in options definition lists, with optional alter
   * operation.
   */
#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#10)
Re: reloptions and toast tables

Alvaro Herrera <alvherre@commandprompt.com> writes:

Peter Eisentraut wrote:

On Sunday 21 December 2008 01:48:42 Alvaro Herrera wrote:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);
ALTER TABLE foo SET (toast.autovacuum_enabled = false);
ALTER TABLE foo TOAST SET (autovacuum_enabled = false);
ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

The last two don't appear to allow setting TOAST and non-TOAST options in one
go. I think it would be handy to allow that, though.

Agreed -- so I'm now playing with this version:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);

So the grammar modifications needed to accept that are attached. The
support code is a lot messier than I'd like :-(

This is not only really ugly, but 100% toast-specific. The
qualified-name approach ("toast.autovacuum_enabled") has at least
a chance of being good for something else. Or just make it
toast_autovacuum_enabled and do the translation magic at some low
level in the statement execution code.

regards, tom lane

#12Dave Page
dpage@pgadmin.org
In reply to: Tom Lane (#11)
Re: reloptions and toast tables

On Wed, Dec 31, 2008 at 9:45 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@commandprompt.com> writes:

Peter Eisentraut wrote:

On Sunday 21 December 2008 01:48:42 Alvaro Herrera wrote:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);
ALTER TABLE foo SET (toast.autovacuum_enabled = false);
ALTER TABLE foo TOAST SET (autovacuum_enabled = false);
ALTER TABLE foo SET TOAST (autovacuum_enabled = false);

The last two don't appear to allow setting TOAST and non-TOAST options in one
go. I think it would be handy to allow that, though.

Agreed -- so I'm now playing with this version:

ALTER TABLE foo SET (TOAST autovacuum_enabled = false);

So the grammar modifications needed to accept that are attached. The
support code is a lot messier than I'd like :-(

This is not only really ugly, but 100% toast-specific. The
qualified-name approach ("toast.autovacuum_enabled") has at least
a chance of being good for something else. Or just make it
toast_autovacuum_enabled and do the translation magic at some low
level in the statement execution code.

Are we expecting this patch (or whatever it turns into) to go into
8.4? It was marked as WIP when feature freeze started and clearly
still is quite undefined at this stage.

The reason I raise this is that this is precisely the sort of patch
that has a major knock-on effect to the tools the many people expect
to be able to use with a new version of the server as soon as it's
released. Obviously we need our own freeze and beta periods prior to
that time which is already extremely tight as we wait for last minute
changes in the server that need support. The last thing we need is
for something like the per-table vacuum settings interface to
redefined right before beta as that is likely to require a fair amount
of re-working.

This is something I think we need to be more mindful of as our
project, it's surrounding eco-system of tools and users expectations
grow.

--
Dave Page
EnterpriseDB UK: http://www.enterprisedb.com

#13Alvaro Herrera
alvherre@commandprompt.com
In reply to: Dave Page (#12)
Re: reloptions and toast tables

Dave Page wrote:

Are we expecting this patch (or whatever it turns into) to go into
8.4? It was marked as WIP when feature freeze started and clearly
still is quite undefined at this stage.

Right. This is a fair objection. I started just by reviewing the
autovacuum-in-reloptions patch, but it turned out to be unworkable in
quite some ways, so I'm reworking it.

I need some more opinions on whether I should continue working here, or
stop and leave it for 8.5.

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

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#13)
Re: reloptions and toast tables

Alvaro Herrera <alvherre@commandprompt.com> writes:

I need some more opinions on whether I should continue working here, or
stop and leave it for 8.5.

Bruce and I were just talking yesterday about the need to start closing
down this commitfest. I'm not sure what the schedule is going to end
up being; but if you can't see a pretty short path to finishing whatever
development still needs doing, my advice is to set it aside for 8.5.

regards, tom lane

#15Magnus Hagander
magnus@hagander.net
In reply to: Tom Lane (#14)
Re: reloptions and toast tables

Tom Lane wrote:

Alvaro Herrera <alvherre@commandprompt.com> writes:

I need some more opinions on whether I should continue working here, or
stop and leave it for 8.5.

Bruce and I were just talking yesterday about the need to start closing
down this commitfest. I'm not sure what the schedule is going to end
up being; but if you can't see a pretty short path to finishing whatever
development still needs doing, my advice is to set it aside for 8.5.

I agree in principle. OTOH, the current behavior can almost be defined
as a bug, since we can't restore any changes you make to the autovacuum
configuration. I know it's by design, but it makes it pretty fragile to
use the ability to change the configuration at all...

//Magnus

#16Alvaro Herrera
alvherre@commandprompt.com
In reply to: Tom Lane (#11)
1 attachment(s)
Re: reloptions and toast tables

Tom Lane wrote:

This is not only really ugly, but 100% toast-specific. The
qualified-name approach ("toast.autovacuum_enabled") has at least
a chance of being good for something else. Or just make it
toast_autovacuum_enabled and do the translation magic at some low
level in the statement execution code.

Does this look better?

This is still WIP, as some cases are not handled correctly; but with
this patch it's possible to set a toast table fillfactor (not that
that's useful for anything AFAICS).

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

Attachments:

reloptions-toast.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.16
diff -c -p -r1.16 reloptions.c
*** src/backend/access/common/reloptions.c	6 Jan 2009 14:47:37 -0000	1.16
--- src/backend/access/common/reloptions.c	7 Jan 2009 23:07:22 -0000
*************** add_string_reloption(int kind, char *nam
*** 378,385 ****
  }
  
  /*
!  * 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
--- 378,387 ----
  }
  
  /*
!  * 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
*** 396,402 ****
   * 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;
--- 398,404 ----
   * 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
*** 432,442 ****
  			/* 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)
--- 434,444 ----
  			/* Search for a match in defList */
  			foreach(cell, defList)
  			{
! 				ReloptElem *def = lfirst(cell);
! 				int			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
*** 456,462 ****
  	 */
  	foreach(cell, defList)
  	{
! 		DefElem    *def = lfirst(cell);
  
  		if (isReset)
  		{
--- 458,464 ----
  	 */
  	foreach(cell, defList)
  	{
! 		ReloptElem    *def = lfirst(cell);
  
  		if (isReset)
  		{
*************** transformRelOptions(Datum oldOptions, Li
*** 471,492 ****
  			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,
--- 473,506 ----
  			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
*** 784,790 ****
  }
  
  /*
!  * Parse options for heaps (and perhaps someday toast tables).
   */
  bytea *
  heap_reloptions(char relkind, Datum reloptions, bool validate)
--- 798,804 ----
  }
  
  /*
!  * 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	7 Jan 2009 22:10:48 -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,748 ----
  	 * 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);
! 	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	7 Jan 2009 23:43:05 -0000
*************** defGetTypeLength(DefElem *def)
*** 306,319 ****
  }
  
  /*
!  * 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;
  }
--- 306,407 ----
  }
  
  /*
!  * Extract a string value (otherwise uninterpreted) from a ReloptElem.
   */
! char *
! reloptGetString(ReloptElem *def)
  {
! 	if (def->arg == NULL)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("%s requires a parameter",
! 						def->optname)));
! 	switch (nodeTag(def->arg))
! 	{
! 		case T_Integer:
! 			{
! 				char	   *str = palloc(32);
  
! 				snprintf(str, 32, "%ld", (long) intVal(def->arg));
! 				return str;
! 			}
! 		case T_Float:
! 
! 			/*
! 			 * 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 boolean value from a ReloptElem.
!  */
! bool
! reloptGetBoolean(ReloptElem *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;
! 				case 1:
! 					return true;
! 				default:
! 					/* otherwise, error out below */
! 					break;
! 			}
! 			break;
! 		default:
! 			{
! 				char	   *sval = reloptGetString(def);
! 
! 				if (pg_strcasecmp(sval, "true") == 0)
! 					return true;
! 				if (pg_strcasecmp(sval, "false") == 0)
! 					return false;
! 
! 			}
! 			break;
! 	}
! 	ereport(ERROR,
! 			(errcode(ERRCODE_SYNTAX_ERROR),
! 			 errmsg("%s requires a Boolean value",
! 					def->optname)));
! 	return false;				/* keep compiler quiet */
! }
! 
! /*
!  * 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.276
diff -c -p -r1.276 tablecmds.c
*** src/backend/commands/tablecmds.c	1 Jan 2009 17:23:39 -0000	1.276
--- src/backend/commands/tablecmds.c	7 Jan 2009 22:10:48 -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)
*** 2547,2553 ****
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid);
  	}
  }
  
--- 2547,2553 ----
  			(tab->subcmds[AT_PASS_ADD_COL] ||
  			 tab->subcmds[AT_PASS_ALTER_TYPE] ||
  			 tab->subcmds[AT_PASS_COL_ATTRS]))
! 			AlterTableCreateToastTable(tab->relid, (Datum) 0);	/* FIXME must preserve */
  	}
  }
  
*************** ATExecSetRelOptions(Relation rel, List *
*** 6435,6441 ****
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
--- 6435,6441 ----
  
  	/* Generate new proposed reloptions (text array) */
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
! 									 defList, NULL, false, isReset);
  
  	/* Validate */
  	switch (rel->rd_rel->relkind)
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/typecmds.c,v
retrieving revision 1.129
diff -c -p -r1.129 typecmds.c
*** src/backend/commands/typecmds.c	1 Jan 2009 17:23:40 -0000	1.129
--- src/backend/commands/typecmds.c	7 Jan 2009 23:43:37 -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.651
diff -c -p -r2.651 gram.y
*** src/backend/parser/gram.y	1 Jan 2009 17:23:45 -0000	2.651
--- src/backend/parser/gram.y	7 Jan 2009 23:43:59 -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 
*** 333,338 ****
--- 335,341 ----
  %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:
*** 1772,1778 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
--- 1775,1781 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> SET (...) */
! 			| SET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_SetRelOptions;
*************** alter_table_cmd:
*** 1780,1786 ****
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET definition
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
--- 1783,1789 ----
  					$$ = (Node *)n;
  				}
  			/* ALTER TABLE <name> RESET (...) */
! 			| RESET reloptions
  				{
  					AlterTableCmd *n = makeNode(AlterTableCmd);
  					n->subtype = AT_ResetRelOptions;
*************** alter_using:
*** 1805,1811 ****
--- 1808,1840 ----
  			| /* 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_
*** 2431,2439 ****
  
  /* 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; }
  		;
  
--- 2460,2468 ----
  
  /* 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.8
diff -c -p -r1.8 reloptions.h
*** src/include/access/reloptions.h	6 Jan 2009 14:47:37 -0000	1.8
--- src/include/access/reloptions.h	7 Jan 2009 22:10:48 -0000
*************** extern void add_string_reloption(int kin
*** 201,207 ****
  					 char *default_val);
  			
  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);
--- 201,207 ----
  					 char *default_val);
  			
  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	7 Jan 2009 23:43:21 -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 *def);
! extern bool reloptGetBoolean(ReloptElem *def);
! 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