On Sun, Feb 9, 2014 at 2:22 PM, Fabrízio de Royes Mello <
fabriziome...@gmail.com> wrote:
>
>
> On Sat, Jan 11, 2014 at 2:47 AM, Peter Eisentraut <pete...@gmx.net> wrote:
> >
> > On Sat, 2014-01-11 at 00:48 -0200, Fabrízio de Royes Mello wrote:
> > > > Now, if bdr is installed but the validation doesn't happen unless
> > > bdr
> > > > is "loaded" in some sense, then that is an implementation deficiency
> > > > that I think we can insist be rectified before this feature is
> > > accepted.
> > > >
> >
> > > Check if extension is already installed is not enough for the first
> > > version of this feature?
> >
> > Elsewhere it was argued that tying this to extensions is not
> > appropriate.  I agree.
> >
> > It depends on how this feature is supposed to be used exactly.  A
> > replication plugin might very well be loaded via
> > session_preload_libraries and not appear in SQL at all.  In that case
> > you need some C-level hook.  In another case, an extension might want to
> > inspect relation options from user-space triggers.  So you'd need to
> > register some SQL-level function for option validation.
> >
> > This could end up being two separate but overlapping features.
> >
>
> Hi all,
>
>
> I taken this weekend to work on this patch and on monday or tuesday I'll
send it.
>
> But I have some doubts:
>
> 1) I'm not convinced to tying this to extensions. I think this feature
must enable us to just store a custom GUC. We can set custom GUCs in a
backend session using "SET class.variable = value", and this feature could
just enable us to store it for relations/attributes. Without the complexity
and overhead to register a function to validate them. That way we can use
this feature to extensions and other needs too.
>
> 2) If we're implement the Robert's idea to have a function to validate
the extension options then we must think about how a extension developer
will register this function. Beacuse when we install a extension must have
one way to get de pg_proc OID and store it in the pg_extension (or a
different catalog). Or we'll implement some way to register this function
at the SQL level, like "ALTER EXTENSION bdr SET VALIDATE FUNCTION
bdr_options_validate();" or another sintax of course.
>
> I don't know if you guys understood my concerns!! :-)
>
> Comments?
>

Hi all,

The attached patch implements the first option that I suggested before.

Regards,

--
Fabrízio de Royes Mello
Consultoria/Coaching PostgreSQL
>> Timbira: http://www.timbira.com.br
>> Blog sobre TI: http://fabriziomello.blogspot.com
>> Perfil Linkedin: http://br.linkedin.com/in/fabriziomello
>> Twitter: http://twitter.com/fabriziomello
diff --git a/doc/src/sgml/ref/alter_index.sgml b/doc/src/sgml/ref/alter_index.sgml
index d210077..5e9ee9d 100644
--- a/doc/src/sgml/ref/alter_index.sgml
+++ b/doc/src/sgml/ref/alter_index.sgml
@@ -82,6 +82,14 @@ ALTER INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> RESE
       <xref linkend="SQL-REINDEX">
       to get the desired effects.
      </para>
+     <note>
+       <para>
+         A custom name can be used as namespace to define a storage parameter.
+         Storage option pattern: namespace.option=value
+         (namespace=custom name, option=option name and value=option value).
+         See example bellow.
+       </para>
+     </note>
     </listitem>
    </varlistentry>
 
@@ -202,6 +210,17 @@ ALTER INDEX distributors SET (fillfactor = 75);
 REINDEX INDEX distributors;
 </programlisting></para>
 
+  <para>
+   To set a custom storage parameter:
+<programlisting>
+ALTER INDEX distributors
+  SET (bdr.do_replicate=true);
+</programlisting>
+   (bdr=custom name, do_replicate=option name and 
+   true=option value)
+</para>
+
+
  </refsect1>
 
  <refsect1>
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 89649a2..6fd9d67 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -213,6 +213,16 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
       of statistics by the <productname>PostgreSQL</productname> query
       planner, refer to <xref linkend="planner-stats">.
      </para>
+
+     <note>
+      <para>
+        A custom name can be used as namespace to define a storage parameter.
+        Storage option pattern: namespace.option=value
+        (namespace=custom name, option=option name and value=option value).
+        See example bellow.
+      </para>
+     </note>
+
     </listitem>
    </varlistentry>
 
@@ -476,6 +486,10 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
        <command>ALTER TABLE</> does not treat <literal>OIDS</> as a
        storage parameter.  Instead use the <literal>SET WITH OIDS</>
        and <literal>SET WITHOUT OIDS</> forms to change OID status.
+       A custom name can be used as namespace to define a storage parameter.
+       Storage option pattern: namespace.option=value
+       (namespace=custom name, option=option name and value=option value).
+       See example bellow.
       </para>
      </note>
     </listitem>
@@ -1112,6 +1126,26 @@ ALTER TABLE distributors DROP CONSTRAINT distributors_pkey,
     ADD CONSTRAINT distributors_pkey PRIMARY KEY USING INDEX dist_id_temp_idx;
 </programlisting></para>
 
+  <para>
+   To set a custom per-attribute option:
+<programlisting>
+ALTER TABLE distributors
+  ALTER COLUMN dist_id SET (bdr.do_replicate=true);
+</programlisting>
+   (bdr=custom name, do_replicate=option name and 
+   true=option value)
+</para>
+
+  <para>
+   To set a custom storage parameter:
+<programlisting>
+ALTER TABLE distributors
+  SET (bdr.do_replicate=true);
+</programlisting>
+   (bdr=custom name, do_replicate=option name and 
+   true=option value)
+</para>
+
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index fa08c45..7de2003 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -283,6 +283,9 @@ static void initialize_reloptions(void);
 static void parse_one_reloption(relopt_value *option, char *text_str,
 					int text_len, bool validate);
 
+static bool is_custom_namespace(char *namespace);
+static bool is_reserved_namespace(char *namespace);
+
 /*
  * initialize_reloptions
  *		initialization routine, must be called before parsing
@@ -610,13 +613,15 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
 			/* Search for a match in defList */
 			foreach(cell, defList)
 			{
-				DefElem    *def = (DefElem *) lfirst(cell);
+				DefElem	   *def = (DefElem *) lfirst(cell);
 				int			kw_len;
+				char	   *text_compare;
 
 				/* ignore if not in the same namespace */
 				if (namspace == NULL)
 				{
-					if (def->defnamespace != NULL)
+					if (def->defnamespace != NULL &&
+						!is_custom_namespace(def->defnamespace))
 						continue;
 				}
 				else if (def->defnamespace == NULL)
@@ -625,8 +630,17 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
 					continue;
 
 				kw_len = strlen(def->defname);
+				if (is_custom_namespace(def->defnamespace))
+					kw_len += strlen(def->defnamespace) + 1;
+
+				text_compare = (char *) palloc(kw_len + 1);
+				if (is_custom_namespace(def->defnamespace))
+					sprintf(text_compare, "%s.%s", def->defnamespace, def->defname);
+				else
+					sprintf(text_compare, "%s", def->defname);
+
 				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					pg_strncasecmp(text_str, def->defname, kw_len) == 0)
+					pg_strncasecmp(text_str, text_compare, kw_len) == 0)
 					break;
 			}
 			if (!cell)
@@ -668,22 +682,11 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
 			if (def->defnamespace != NULL)
 			{
 				bool		valid = false;
-				int			i;
 
 				if (validnsps)
-				{
-					for (i = 0; validnsps[i]; i++)
-					{
-						if (pg_strcasecmp(def->defnamespace,
-										  validnsps[i]) == 0)
-						{
-							valid = true;
-							break;
-						}
-					}
-				}
+					valid = is_reserved_namespace(def->defnamespace);
 
-				if (!valid)
+				if (!valid && !is_custom_namespace(def->defnamespace))
 					ereport(ERROR,
 							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 							 errmsg("unrecognized parameter namespace \"%s\"",
@@ -696,7 +699,8 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
 			/* ignore if not in the same namespace */
 			if (namspace == NULL)
 			{
-				if (def->defnamespace != NULL)
+				if (def->defnamespace != NULL &&
+					!is_custom_namespace(def->defnamespace))
 					continue;
 			}
 			else if (def->defnamespace == NULL)
@@ -714,10 +718,18 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
 			else
 				value = "true";
 			len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
+
+			if (is_custom_namespace(def->defnamespace))
+				len += strlen(def->defnamespace) + 1;
+
 			/* +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);
+
+			if (is_custom_namespace(def->defnamespace))
+				sprintf(VARDATA(t), "%s.%s=%s", def->defnamespace, def->defname, value);
+			else
+				sprintf(VARDATA(t), "%s=%s", def->defname, value);
 
 			astate = accumArrayResult(astate, PointerGetDatum(t),
 									  false, TEXTOID,
@@ -921,13 +933,24 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 			if (j >= numoptions && validate)
 			{
-				char	   *s;
-				char	   *p;
+				char	*s;
+				char	*n;
+				char	*p;
 
 				s = TextDatumGetCString(optiondatums[i]);
 				p = strchr(s, '=');
 				if (p)
+				{
 					*p = '\0';
+					n = strchr(s, '.');
+					if (n)
+					{
+						*n = '\0';
+						if (is_custom_namespace(s))
+							continue;
+					}
+				}
+
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("unrecognized parameter \"%s\"", s)));
@@ -1326,3 +1349,36 @@ tablespace_reloptions(Datum reloptions, bool validate)
 
 	return (bytea *) tsopts;
 }
+
+bool
+is_reserved_namespace(char *namespace)
+{
+	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+	bool		is_reserved = false;
+	int			i;
+
+	for (i = 0; validnsps[i]; i++)
+	{
+		if (pg_strcasecmp(namespace,
+						  validnsps[i]) == 0)
+		{
+			is_reserved = true;
+			break;
+		}
+	}
+
+	return is_reserved;
+}
+
+bool
+is_custom_namespace(char *namespace)
+{
+
+	if (namespace == NULL)
+		return false;
+
+	if (is_reserved_namespace(namespace))
+		return false;
+
+	return true;
+}
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 0f0c638..0d15403 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2370,3 +2370,101 @@ TRUNCATE old_system_table;
 ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
 ALTER TABLE old_system_table DROP COLUMN othercol;
 DROP TABLE old_system_table;
+--
+-- Custom Option Test
+--
+CREATE TABLE custom_reloption_test(id SERIAL PRIMARY KEY);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           | reloptions 
+----------------------------+------------
+ custom_reloption_test      | 
+ custom_reloption_test.id   | 
+ custom_reloption_test_pkey | 
+(3 rows)
+
+ALTER TABLE custom_reloption_test SET (fillfactor=70);
+ALTER INDEX custom_reloption_test_pkey SET (fillfactor=80);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (n_distinct=100);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           |    reloptions    
+----------------------------+------------------
+ custom_reloption_test      | {fillfactor=70}
+ custom_reloption_test.id   | {n_distinct=100}
+ custom_reloption_test_pkey | {fillfactor=80}
+(3 rows)
+
+ALTER TABLE custom_reloption_test SET (xx.bdr.do_replicate=true);
+ERROR:  syntax error at or near "."
+LINE 1: ALTER TABLE custom_reloption_test SET (xx.bdr.do_replicate=t...
+                                                     ^
+ALTER INDEX custom_reloption_test_pkey SET (xx.bdr.do_replicate=true);
+ERROR:  syntax error at or near "."
+LINE 1: ALTER INDEX custom_reloption_test_pkey SET (xx.bdr.do_replic...
+                                                          ^
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (xx.bdr.do_replicate=true);
+ERROR:  syntax error at or near "."
+LINE 1: ... custom_reloption_test ALTER COLUMN id SET (xx.bdr.do_replic...
+                                                             ^
+ALTER TABLE custom_reloption_test SET (xx.bdr=true);
+ALTER INDEX custom_reloption_test_pkey SET (xx.bdr=true);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (xx.bdr=true);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           |          reloptions          
+----------------------------+------------------------------
+ custom_reloption_test      | {fillfactor=70,xx.bdr=true}
+ custom_reloption_test.id   | {n_distinct=100,xx.bdr=true}
+ custom_reloption_test_pkey | {fillfactor=80,xx.bdr=true}
+(3 rows)
+
+-- test extension reloptions
+ALTER TABLE custom_reloption_test SET (plpgsql.option=true);
+ALTER INDEX custom_reloption_test_pkey SET (plpgsql.option=true);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (plpgsql.option=true);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           |                    reloptions                    
+----------------------------+--------------------------------------------------
+ custom_reloption_test      | {fillfactor=70,xx.bdr=true,plpgsql.option=true}
+ custom_reloption_test.id   | {n_distinct=100,xx.bdr=true,plpgsql.option=true}
+ custom_reloption_test_pkey | {fillfactor=80,xx.bdr=true,plpgsql.option=true}
+(3 rows)
+
+ALTER TABLE custom_reloption_test SET (plpgsql.foo=1234);
+ALTER INDEX custom_reloption_test_pkey SET (plpgsql.foo=1234);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (plpgsql.foo=1234);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           |                            reloptions                             
+----------------------------+-------------------------------------------------------------------
+ custom_reloption_test      | {fillfactor=70,xx.bdr=true,plpgsql.option=true,plpgsql.foo=1234}
+ custom_reloption_test.id   | {n_distinct=100,xx.bdr=true,plpgsql.option=true,plpgsql.foo=1234}
+ custom_reloption_test_pkey | {fillfactor=80,xx.bdr=true,plpgsql.option=true,plpgsql.foo=1234}
+(3 rows)
+
+ALTER TABLE custom_reloption_test RESET (plpgsql.foo);
+ALTER INDEX custom_reloption_test_pkey RESET (plpgsql.foo);
+ALTER TABLE custom_reloption_test ALTER COLUMN id RESET (plpgsql.foo);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+          relname           |                    reloptions                    
+----------------------------+--------------------------------------------------
+ custom_reloption_test      | {fillfactor=70,xx.bdr=true,plpgsql.option=true}
+ custom_reloption_test.id   | {n_distinct=100,xx.bdr=true,plpgsql.option=true}
+ custom_reloption_test_pkey | {fillfactor=80,xx.bdr=true,plpgsql.option=true}
+(3 rows)
+
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 87973c1..9b04c08 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1594,3 +1594,51 @@ TRUNCATE old_system_table;
 ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
 ALTER TABLE old_system_table DROP COLUMN othercol;
 DROP TABLE old_system_table;
+
+--
+-- Custom Option Test
+--
+CREATE TABLE custom_reloption_test(id SERIAL PRIMARY KEY);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+ALTER TABLE custom_reloption_test SET (fillfactor=70);
+ALTER INDEX custom_reloption_test_pkey SET (fillfactor=80);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (n_distinct=100);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+ALTER TABLE custom_reloption_test SET (xx.bdr.do_replicate=true);
+ALTER INDEX custom_reloption_test_pkey SET (xx.bdr.do_replicate=true);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (xx.bdr.do_replicate=true);
+ALTER TABLE custom_reloption_test SET (xx.bdr=true);
+ALTER INDEX custom_reloption_test_pkey SET (xx.bdr=true);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (xx.bdr=true);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+-- test extension reloptions
+ALTER TABLE custom_reloption_test SET (plpgsql.option=true);
+ALTER INDEX custom_reloption_test_pkey SET (plpgsql.option=true);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (plpgsql.option=true);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+ALTER TABLE custom_reloption_test SET (plpgsql.foo=1234);
+ALTER INDEX custom_reloption_test_pkey SET (plpgsql.foo=1234);
+ALTER TABLE custom_reloption_test ALTER COLUMN id SET (plpgsql.foo=1234);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
+ALTER TABLE custom_reloption_test RESET (plpgsql.foo);
+ALTER INDEX custom_reloption_test_pkey RESET (plpgsql.foo);
+ALTER TABLE custom_reloption_test ALTER COLUMN id RESET (plpgsql.foo);
+SELECT relname, reloptions FROM pg_class WHERE oid IN ('custom_reloption_test'::regclass, 'custom_reloption_test_pkey'::regclass)
+UNION ALL
+SELECT attrelid::regclass||'.'||attname, attoptions FROM pg_attribute WHERE attrelid = 'custom_reloption_test'::regclass AND attname = 'id'
+ORDER BY 1;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to