]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Simplify the syntax of CREATE/ALTER TEXT SEARCH DICTIONARY by treating the
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 22 Aug 2007 01:39:46 +0000 (01:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 22 Aug 2007 01:39:46 +0000 (01:39 +0000)
init options of the template as top-level options in the syntax.  This also
makes ALTER a bit easier to use, since options can be replaced individually.
I also made these statements verify that the tmplinit method will accept
the new settings before they get stored; in the original coding you didn't
find out about mistakes until the dictionary got invoked.

Under the hood, init methods now get options as a List of DefElem instead
of a raw text string --- that lets tsearch use existing options-pushing code
instead of duplicating functionality.

17 files changed:
doc/src/sgml/ref/alter_tsdictionary.sgml
doc/src/sgml/ref/create_tsdictionary.sgml
src/backend/commands/tsearchcmds.c
src/backend/snowball/dict_snowball.c
src/backend/snowball/snowball.sql.in
src/backend/tsearch/dict_ispell.c
src/backend/tsearch/dict_simple.c
src/backend/tsearch/dict_thesaurus.c
src/backend/tsearch/ts_utils.c
src/backend/tsearch/wparser.c
src/backend/tsearch/wparser_def.c
src/backend/utils/cache/ts_cache.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/commands/defrem.h
src/include/tsearch/ts_public.h

index 59c33666557584e0ed735a1f84ce059a50d1b095..a2929c70d12e55e514773810de54a4702dc6aa35 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_tsdictionary.sgml,v 1.1 2007/08/21 21:08:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_tsdictionary.sgml,v 1.2 2007/08/22 01:39:44 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -20,7 +20,9 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> ( OPTION = <replaceable class="parameter">init_options</replaceable> )
+ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> (
+    <replaceable class="parameter">option</replaceable> [ = <replaceable class="parameter">value</replaceable> ] [, ... ]
+)
 ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> RENAME TO <replaceable>newname</replaceable>
 ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceable>newowner</replaceable>
 </synopsis>
@@ -31,8 +33,8 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
 
   <para>
    <command>ALTER TEXT SEARCH DICTIONARY</command> changes the definition of
-   a text search dictionary.  You can change the dictionary's initialization
-   options, or change the dictionary's name or owner.
+   a text search dictionary.  You can change the dictionary's
+   template-specific options, or change the dictionary's name or owner.
   </para>
 
   <para>
@@ -56,11 +58,22 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
    </varlistentry>
 
    <varlistentry>
-    <term><replaceable class="parameter">init_options</replaceable></term>
+    <term><replaceable class="parameter">option</replaceable></term>
     <listitem>
      <para>
-      A new list of initialization options, or <literal>NULL</> to
-      remove all options.
+      The name of a template-specific option to be set for this dictionary.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">value</replaceable></term>
+    <listitem>
+     <para>
+      The new value to use for a template-specific option.
+      If the equal sign and value are omitted, then any previous
+      setting for the option is removed from the dictionary,
+      allowing the default to be used.
      </para>
     </listitem>
    </varlistentry>
@@ -83,18 +96,31 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
     </listitem>
    </varlistentry>
  </variablelist>
+
+  <para>
+   Template-specific options can appear in any order.
+  </para>
  </refsect1>
   
  <refsect1>
   <title>Examples</title>
 
   <para>
-   The following example command sets the language and stopword list
-   for a Snowball-based dictionary.
+   The following example command changes the stopword list
+   for a Snowball-based dictionary.  Other parameters remain unchanged.
+  </para>
+
+<programlisting>
+ALTER TEXT SEARCH DICTIONARY my_dict ( StopWords = newrussian );
+</programlisting>  
+
+  <para>
+   The following example command changes the language option to dutch,
+   and removes the stopword option entirely.
   </para>
 
 <programlisting>
-ALTER TEXT SEARCH DICTIONARY my_russian ( option = 'Language=russian, StopWords=my_russian' );
+ALTER TEXT SEARCH DICTIONARY my_dict ( language = dutch, StopWords );
 </programlisting>  
  </refsect1>
 
index 81c6a0c6edb2fb8885f1daec47128b61266334d0..9aa53a6a7c484d331a0662cd1a9bbe84095eaf1a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_tsdictionary.sgml,v 1.1 2007/08/21 21:08:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_tsdictionary.sgml,v 1.2 2007/08/22 01:39:44 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -22,7 +22,7 @@ PostgreSQL documentation
 <synopsis>
 CREATE TEXT SEARCH DICTIONARY <replaceable class="parameter">name</replaceable> (
     TEMPLATE = <replaceable class="parameter">template</replaceable>
-    [, OPTION = <replaceable class="parameter">init_options</replaceable> ]
+    [, <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ]]
 )
 </synopsis>
  </refsynopsisdiv>
@@ -78,17 +78,46 @@ CREATE TEXT SEARCH DICTIONARY <replaceable class="parameter">name</replaceable>
    </varlistentry>
 
    <varlistentry>
-    <term><replaceable class="parameter">init_options</replaceable></term>
+    <term><replaceable class="parameter">option</replaceable></term>
     <listitem>
      <para>
-      A list of initialization options for the template functions.
-      This is a string containing <replaceable>keyword</> <literal>=</>
-      <replaceable>value</> pairs.  The specific keywords allowed
-      vary depending on the text search template.
+      The name of a template-specific option to be set for this dictionary.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">value</replaceable></term>
+    <listitem>
+     <para>
+      The value to use for a template-specific option.  If the value
+      is not a simple identifier or number, it must be quoted (but you can
+      always quote it, if you wish).
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
+
+  <para>
+   The options can appear in any order.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   The following example command creates a Snowball-based dictionary
+   with a nonstandard list of stop words.
+  </para>
+
+<programlisting>
+CREATE TEXT SEARCH DICTIONARY my_russian (
+    template = snowball,
+    language = russian,
+    stopwords = myrussian
+);
+</programlisting>  
  </refsect1>
  
  <refsect1>
index af34c58c7c230c4d003bd871f791516793bb9b21..7c5a1c49a33040bf388599e412be9d9c711f3d75 100644 (file)
@@ -9,12 +9,13 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.2 2007/08/21 21:24:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.3 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
-#include "miscadmin.h"
+
+#include <ctype.h>
 
 #include "access/heapam.h"
 #include "access/genam.h"
@@ -31,6 +32,8 @@
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
 #include "tsearch/ts_cache.h"
 #include "tsearch/ts_public.h"
@@ -86,7 +89,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
                        break;
                case Anum_pg_ts_parser_prsheadline:
                        nargs = 3;
-                       typeId[1] = TEXTOID;
+                       typeId[1] = INTERNALOID;
                        typeId[2] = TSQUERYOID;
                        break;
                case Anum_pg_ts_parser_prslextype:
@@ -407,6 +410,53 @@ makeDictionaryDependencies(HeapTuple tuple)
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 }
 
+/*
+ * verify that a template's init method accepts a proposed option list
+ */
+static void
+verify_dictoptions(Oid tmplId, List *dictoptions)
+{
+       HeapTuple       tup;
+       Form_pg_ts_template tform;
+       Oid                     initmethod;
+
+       tup = SearchSysCache(TSTEMPLATEOID,
+                                                ObjectIdGetDatum(tmplId),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup)) /* should not happen */
+               elog(ERROR, "cache lookup failed for text search template %u",
+                        tmplId);
+       tform = (Form_pg_ts_template) GETSTRUCT(tup);
+
+       initmethod = tform->tmplinit;
+
+       if (!OidIsValid(initmethod))
+       {
+               /* If there is no init method, disallow any options */
+               if (dictoptions)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("text search template \"%s\" does not accept options",
+                                                       NameStr(tform->tmplname))));
+       }
+       else
+       {
+               /*
+                * Copy the options just in case init method thinks it can scribble
+                * on them ...
+                */
+               dictoptions = copyObject(dictoptions);
+
+               /*
+                * Call the init method and see if it complains.  We don't worry about
+                * it leaking memory, since our command will soon be over anyway.
+                */
+               (void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
+       }
+
+       ReleaseSysCache(tup);
+}
+
 /*
  * CREATE TEXT SEARCH DICTIONARY
  */
@@ -419,7 +469,8 @@ DefineTSDictionary(List *names, List *parameters)
        Datum           values[Natts_pg_ts_dict];
        char            nulls[Natts_pg_ts_dict];
        NameData        dname;
-       int                     i;
+       Oid                     templId = InvalidOid;
+       List       *dictoptions = NIL;
        Oid                     dictOid;
        Oid                     namespaceoid;
        AclResult       aclresult;
@@ -434,18 +485,6 @@ DefineTSDictionary(List *names, List *parameters)
                aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
                                           get_namespace_name(namespaceoid));
 
-       for (i = 0; i < Natts_pg_ts_dict; i++)
-       {
-               nulls[i] = ' ';
-               values[i] = ObjectIdGetDatum(InvalidOid);
-       }
-
-       namestrcpy(&dname, dictname);
-       values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
-       values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
-       values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
-       nulls[Anum_pg_ts_dict_dictinitoption - 1] = 'n';
-
        /*
         * loop over the definition list and extract the information we need.
         */
@@ -455,42 +494,41 @@ DefineTSDictionary(List *names, List *parameters)
 
                if (pg_strcasecmp(defel->defname, "template") == 0)
                {
-                       Oid                     templId;
-
                        templId = TSTemplateGetTmplid(defGetQualifiedName(defel), false);
-
-                       values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
-                       nulls[Anum_pg_ts_dict_dicttemplate - 1] = ' ';
                }
-               else if (pg_strcasecmp(defel->defname, "option") == 0)
+               else
                {
-                       char       *opt = defGetString(defel);
-
-                       if (pg_strcasecmp(opt, "null") != 0)
-                       {
-                               values[Anum_pg_ts_dict_dictinitoption - 1] =
-                                       DirectFunctionCall1(textin, CStringGetDatum(opt));
-                               nulls[Anum_pg_ts_dict_dictinitoption - 1] = ' ';
-                       }
+                       /* Assume it's an option for the dictionary itself */
+                       dictoptions = lappend(dictoptions, defel);
                }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("text search dictionary parameter \"%s\" not recognized",
-                                                       defel->defname)));
        }
 
        /*
         * Validation
         */
-       if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_dict_dicttemplate - 1])))
+       if (!OidIsValid(templId))
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                 errmsg("text search template is required")));
 
+       verify_dictoptions(templId, dictoptions);
+
        /*
         * Looks good, insert
         */
+       memset(values, 0, sizeof(values));
+       memset(nulls, ' ', sizeof(nulls));
+
+       namestrcpy(&dname, dictname);
+       values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
+       values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+       values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
+       values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
+       if (dictoptions)
+               values[Anum_pg_ts_dict_dictinitoption - 1] =
+                       PointerGetDatum(serialize_deflist(dictoptions));
+       else
+               nulls[Anum_pg_ts_dict_dictinitoption - 1] = 'n';
 
        dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
 
@@ -652,6 +690,9 @@ AlterTSDictionary(AlterTSDictionaryStmt * stmt)
        Relation        rel;
        Oid                     dictId;
        ListCell   *pl;
+       List       *dictoptions;
+       Datum           opt;
+       bool            isnull;
        Datum           repl_val[Natts_pg_ts_dict];
        char            repl_null[Natts_pg_ts_dict];
        char            repl_repl[Natts_pg_ts_dict];
@@ -673,41 +714,67 @@ AlterTSDictionary(AlterTSDictionaryStmt * stmt)
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
                                           NameListToString(stmt->dictname));
 
-       memset(repl_val, 0, sizeof(repl_val));
-       memset(repl_null, ' ', sizeof(repl_null));
-       memset(repl_repl, ' ', sizeof(repl_repl));
+       /* deserialize the existing set of options */
+       opt = SysCacheGetAttr(TSDICTOID, tup,
+                                                 Anum_pg_ts_dict_dictinitoption,
+                                                 &isnull);
+       if (isnull)
+               dictoptions = NIL;
+       else
+               dictoptions = deserialize_deflist(opt);
 
        /*
-        * NOTE: because we only support altering the option, not the template,
-        * there is no need to update dependencies.
+        * Modify the options list as per specified changes
         */
        foreach(pl, stmt->options)
        {
                DefElem    *defel = (DefElem *) lfirst(pl);
+               ListCell   *cell;
+               ListCell   *prev;
+               ListCell   *next;
 
-               if (pg_strcasecmp(defel->defname, "option") == 0)
+               /*
+                * Remove any matches ...
+                */
+               prev = NULL;
+               for (cell = list_head(dictoptions); cell; cell = next)
                {
-                       char       *opt = defGetString(defel);
+                       DefElem    *oldel = (DefElem *) lfirst(cell);
 
-                       if (pg_strcasecmp(opt, "null") == 0)
-                       {
-                               repl_null[Anum_pg_ts_dict_dictinitoption - 1] = 'n';
-                       }
+                       next = lnext(cell);
+                       if (pg_strcasecmp(oldel->defname, defel->defname) == 0)
+                               dictoptions = list_delete_cell(dictoptions, cell, prev);
                        else
-                       {
-                               repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
-                                       DirectFunctionCall1(textin, CStringGetDatum(opt));
-                               repl_null[Anum_pg_ts_dict_dictinitoption - 1] = ' ';
-                       }
-                       repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = 'r';
+                               prev = cell;
                }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("text search dictionary parameter \"%s\" not recognized",
-                                                       defel->defname)));
+
+               /*
+                * and add new value if it's got one
+                */
+               if (defel->arg)
+                       dictoptions = lappend(dictoptions, defel);
        }
 
+       /*
+        * Validate
+        */
+       verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
+                                          dictoptions);
+
+       /*
+        * Looks good, update
+        */
+       memset(repl_val, 0, sizeof(repl_val));
+       memset(repl_null, ' ', sizeof(repl_null));
+       memset(repl_repl, ' ', sizeof(repl_repl));
+
+       if (dictoptions)
+               repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
+                       PointerGetDatum(serialize_deflist(dictoptions));
+       else
+               repl_null[Anum_pg_ts_dict_dictinitoption - 1] = 'n';
+       repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = 'r';
+
        newtup = heap_modifytuple(tup, RelationGetDescr(rel),
                                                          repl_val, repl_null, repl_repl);
 
@@ -715,6 +782,12 @@ AlterTSDictionary(AlterTSDictionaryStmt * stmt)
 
        CatalogUpdateIndexes(rel, newtup);
 
+       /*
+        * NOTE: because we only support altering the options, not the template,
+        * there is no need to update dependencies.  This might have to change
+        * if the options ever reference inside-the-database objects.
+        */
+
        heap_freetuple(newtup);
        ReleaseSysCache(tup);
 
@@ -1941,3 +2014,265 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup)
 
        heap_close(relMap, RowExclusiveLock);
 }
+
+
+/*
+ * Serialize dictionary options, producing a TEXT datum from a List of DefElem
+ *
+ * This is used to form the value stored in pg_ts_dict.dictinitoption.
+ * For the convenience of pg_dump, the output is formatted exactly as it
+ * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
+ * same options.
+ *
+ * Note that we assume that only the textual representation of an option's
+ * value is interesting --- hence, non-string DefElems get forced to strings.
+ */
+text *
+serialize_deflist(List *deflist)
+{
+       text       *result;
+       StringInfoData buf;
+       ListCell   *l;
+
+       initStringInfo(&buf);
+
+       foreach(l, deflist)
+       {
+               DefElem    *defel = (DefElem *) lfirst(l);
+               char       *val = defGetString(defel);
+
+               appendStringInfo(&buf, "%s = ",
+                                        quote_identifier(defel->defname));
+               /* If backslashes appear, force E syntax to determine their handling */
+               if (strchr(val, '\\'))
+                       appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
+               appendStringInfoChar(&buf, '\'');
+               while (*val)
+               {
+                       char            ch = *val++;
+
+                       if (SQL_STR_DOUBLE(ch, true))
+                               appendStringInfoChar(&buf, ch);
+                       appendStringInfoChar(&buf, ch);
+               }
+               appendStringInfoChar(&buf, '\'');
+               if (lnext(l) != NULL)
+                       appendStringInfo(&buf, ", ");
+       }
+
+       result = CStringGetTextP(buf.data);
+       pfree(buf.data);
+       return result;
+}
+
+/*
+ * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
+ *
+ * This is also used for prsheadline options, so for backward compatibility
+ * we need to accept a few things serialize_deflist() will never emit:
+ * in particular, unquoted and double-quoted values.
+ */
+List *
+deserialize_deflist(Datum txt)
+{
+       text       *in = DatumGetTextP(txt); /* in case it's toasted */
+       List       *result = NIL;
+       int                     len = VARSIZE(in) - VARHDRSZ;
+       char       *ptr,
+                          *endptr,
+                          *workspace,
+                          *wsptr = NULL,
+                          *startvalue = NULL;
+       typedef enum {
+               CS_WAITKEY,
+               CS_INKEY,
+               CS_INQKEY,
+               CS_WAITEQ,
+               CS_WAITVALUE,
+               CS_INSQVALUE,
+               CS_INDQVALUE,
+               CS_INWVALUE
+       } ds_state;
+       ds_state        state = CS_WAITKEY;
+
+       workspace = (char *) palloc(len + 1);           /* certainly enough room */
+       ptr = VARDATA(in);
+       endptr = ptr + len;
+       for (; ptr < endptr; ptr++)
+       {
+               switch (state)
+               {
+                       case CS_WAITKEY:
+                               if (isspace((unsigned char) *ptr) || *ptr == ',')
+                                       continue;
+                               if (*ptr == '"')
+                               {
+                                       wsptr = workspace;
+                                       state = CS_INQKEY;
+                               }
+                               else
+                               {
+                                       wsptr = workspace;
+                                       *wsptr++ = *ptr;
+                                       state = CS_INKEY;
+                               }
+                               break;
+                       case CS_INKEY:
+                               if (isspace((unsigned char) *ptr))
+                               {
+                                       *wsptr++ = '\0';
+                                       state = CS_WAITEQ;
+                               }
+                               else if (*ptr == '=')
+                               {
+                                       *wsptr++ = '\0';
+                                       state = CS_WAITVALUE;
+                               }
+                               else
+                               {
+                                       *wsptr++ = *ptr;
+                               }
+                               break;
+                       case CS_INQKEY:
+                               if (*ptr == '"')
+                               {
+                                       if (ptr+1 < endptr && ptr[1] == '"')
+                                       {
+                                               /* copy only one of the two quotes */
+                                               *wsptr++ = *ptr++;
+                                       }
+                                       else
+                                       {
+                                               *wsptr++ = '\0';
+                                               state = CS_WAITEQ;
+                                       }
+                               }
+                               else
+                               {
+                                       *wsptr++ = *ptr;
+                               }
+                               break;
+                       case CS_WAITEQ:
+                               if (*ptr == '=')
+                                       state = CS_WAITVALUE;
+                               else if (!isspace((unsigned char) *ptr))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                                        errmsg("invalid parameter list format: \"%s\"",
+                                                                       TextPGetCString(in))));
+                               break;
+                       case CS_WAITVALUE:
+                               if (*ptr == '\'')
+                               {
+                                       startvalue = wsptr;
+                                       state = CS_INSQVALUE;
+                               }
+                               else if (*ptr == 'E' && ptr+1 < endptr && ptr[1] == '\'')
+                               {
+                                       ptr++;
+                                       startvalue = wsptr;
+                                       state = CS_INSQVALUE;
+                               }
+                               else if (*ptr == '"')
+                               {
+                                       startvalue = wsptr;
+                                       state = CS_INDQVALUE;
+                               }
+                               else if (!isspace((unsigned char) *ptr))
+                               {
+                                       startvalue = wsptr;
+                                       *wsptr++ = *ptr;
+                                       state = CS_INWVALUE;
+                               }
+                               break;
+                       case CS_INSQVALUE:
+                               if (*ptr == '\'')
+                               {
+                                       if (ptr+1 < endptr && ptr[1] == '\'')
+                                       {
+                                               /* copy only one of the two quotes */
+                                               *wsptr++ = *ptr++;
+                                       }
+                                       else
+                                       {
+                                               *wsptr++ = '\0';
+                                               result = lappend(result,
+                                                                                makeDefElem(pstrdup(workspace),
+                                                                                                        (Node *) makeString(pstrdup(startvalue))));
+                                               state = CS_WAITKEY;
+                                       }
+                               }
+                               else if (*ptr == '\\')
+                               {
+                                       if (ptr+1 < endptr && ptr[1] == '\\')
+                                       {
+                                               /* copy only one of the two backslashes */
+                                               *wsptr++ = *ptr++;
+                                       }
+                                       else
+                                               *wsptr++ = *ptr;
+                               }
+                               else
+                               {
+                                       *wsptr++ = *ptr;
+                               }
+                               break;
+                       case CS_INDQVALUE:
+                               if (*ptr == '"')
+                               {
+                                       if (ptr+1 < endptr && ptr[1] == '"')
+                                       {
+                                               /* copy only one of the two quotes */
+                                               *wsptr++ = *ptr++;
+                                       }
+                                       else
+                                       {
+                                               *wsptr++ = '\0';
+                                               result = lappend(result,
+                                                                                makeDefElem(pstrdup(workspace),
+                                                                                                        (Node *) makeString(pstrdup(startvalue))));
+                                               state = CS_WAITKEY;
+                                       }
+                               }
+                               else
+                               {
+                                       *wsptr++ = *ptr;
+                               }
+                               break;
+                       case CS_INWVALUE:
+                               if (*ptr == ',' || isspace((unsigned char) *ptr))
+                               {
+                                       *wsptr++ = '\0';
+                                       result = lappend(result,
+                                                                        makeDefElem(pstrdup(workspace),
+                                                                                                (Node *) makeString(pstrdup(startvalue))));
+                                       state = CS_WAITKEY;
+                               }
+                               else
+                               {
+                                       *wsptr++ = *ptr;
+                               }
+                               break;
+                       default:
+                               elog(ERROR, "unrecognized deserialize_deflist state: %d",
+                                        state);
+               }
+       }
+
+       if (state == CS_INWVALUE)
+       {
+               *wsptr++ = '\0';
+               result = lappend(result,
+                                                makeDefElem(pstrdup(workspace),
+                                                                        (Node *) makeString(pstrdup(startvalue))));
+       }
+       else if (state != CS_WAITKEY)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("invalid parameter list format: \"%s\"",
+                                               TextPGetCString(in))));
+
+       pfree(workspace);
+
+       return result;
+}
index f0bc2feedecbb02ed6a7401de6b0ea7015fdfe61..03f2dd928c2a3e77a41af2e81314ed8e57d21c0d 100644 (file)
@@ -6,12 +6,13 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/snowball/dict_snowball.c,v 1.1 2007/08/21 01:11:16 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/snowball/dict_snowball.c,v 1.2 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "commands/defrem.h"
 #include "fmgr.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
@@ -185,59 +186,44 @@ locate_stem_module(DictSnowball * d, char *lang)
 Datum
 dsnowball_init(PG_FUNCTION_ARGS)
 {
-       text       *in;
+       List       *dictoptions = (List *) PG_GETARG_POINTER(0);
        DictSnowball *d;
-       Map                *cfg,
-                          *pcfg;
        bool            stoploaded = false;
-
-       /* init functions must defend against NULLs for themselves */
-       if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("NULL config not allowed for Snowball")));
-       in = PG_GETARG_TEXT_P(0);
+       ListCell   *l;
 
        d = (DictSnowball *) palloc0(sizeof(DictSnowball));
        d->stoplist.wordop = recode_and_lowerstr;
 
-       parse_keyvalpairs(in, &cfg);
-       pcfg = cfg;
-       PG_FREE_IF_COPY(in, 0);
-
-       while (pcfg && pcfg->key)
+       foreach(l, dictoptions)
        {
-               if (pg_strcasecmp("StopWords", pcfg->key) == 0)
+               DefElem    *defel = (DefElem *) lfirst(l);
+
+               if (pg_strcasecmp("StopWords", defel->defname) == 0)
                {
                        if (stoploaded)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple StopWords parameters")));
-                       readstoplist(pcfg->value, &d->stoplist);
+                       readstoplist(defGetString(defel), &d->stoplist);
                        sortstoplist(&d->stoplist);
                        stoploaded = true;
                }
-               else if (pg_strcasecmp("Language", pcfg->key) == 0)
+               else if (pg_strcasecmp("Language", defel->defname) == 0)
                {
                        if (d->stem)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple Language parameters")));
-                       locate_stem_module(d, pcfg->value);
+                       locate_stem_module(d, defGetString(defel));
                }
                else
                {
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("unrecognized Snowball parameter: \"%s\"",
-                                                       pcfg->key)));
+                                                       defel->defname)));
                }
-
-               pfree(pcfg->key);
-               pfree(pcfg->value);
-               pcfg++;
        }
-       pfree(cfg);
 
        if (!d->stem)
                ereport(ERROR,
index 5f1f3e772e80a1d78701e67124f0ce8e4f9a8526..873a5bf5592d11497d772a90c8a3442ddac70adb 100644 (file)
@@ -1,9 +1,9 @@
--- $PostgreSQL: pgsql/src/backend/snowball/snowball.sql.in,v 1.1 2007/08/21 01:11:16 tgl Exp $$
+-- $PostgreSQL: pgsql/src/backend/snowball/snowball.sql.in,v 1.2 2007/08/22 01:39:44 tgl Exp $$
 
 -- text search configuration for _CFGNAME_ language
 CREATE TEXT SEARCH DICTIONARY _DICTNAME_
        (TEMPLATE = snowball,
-       OPTION = 'Language=_DICTNAME__STOPWORDS_');
+       Language = _DICTNAME_ _STOPWORDS_);
 
 COMMENT ON TEXT SEARCH DICTIONARY _DICTNAME_ IS 'Snowball stemmer for _DICTNAME_ language';
 
index f7cee1073005f1303c481a889437a10cf61a1acc..802a64508787460f496a724521a2f37c91222e10 100644 (file)
@@ -7,12 +7,13 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/dict_ispell.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/dict_ispell.c,v 1.2 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "commands/defrem.h"
 #include "tsearch/dicts/spell.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
@@ -30,59 +31,49 @@ typedef struct
 Datum
 dispell_init(PG_FUNCTION_ARGS)
 {
+       List       *dictoptions = (List *) PG_GETARG_POINTER(0);
        DictISpell *d;
-       Map                *cfg,
-                          *pcfg;
        bool            affloaded = false,
                                dictloaded = false,
                                stoploaded = false;
-       text       *in;
-
-       /* init functions must defend against NULLs for themselves */
-       if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("NULL config not allowed for ISpell")));
-       in = PG_GETARG_TEXT_P(0);
-
-       parse_keyvalpairs(in, &cfg);
-       PG_FREE_IF_COPY(in, 0);
+       ListCell   *l;
 
        d = (DictISpell *) palloc0(sizeof(DictISpell));
        d->stoplist.wordop = recode_and_lowerstr;
 
-       pcfg = cfg;
-       while (pcfg->key)
+       foreach(l, dictoptions)
        {
-               if (pg_strcasecmp("DictFile", pcfg->key) == 0)
+               DefElem    *defel = (DefElem *) lfirst(l);
+
+               if (pg_strcasecmp(defel->defname, "DictFile") == 0)
                {
                        if (dictloaded)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple DictFile parameters")));
                        NIImportDictionary(&(d->obj),
-                                                          get_tsearch_config_filename(pcfg->value,
+                                                          get_tsearch_config_filename(defGetString(defel),
                                                                                                                   "dict"));
                        dictloaded = true;
                }
-               else if (pg_strcasecmp("AffFile", pcfg->key) == 0)
+               else if (pg_strcasecmp(defel->defname, "AffFile") == 0)
                {
                        if (affloaded)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple AffFile parameters")));
                        NIImportAffixes(&(d->obj),
-                                                       get_tsearch_config_filename(pcfg->value,
+                                                       get_tsearch_config_filename(defGetString(defel),
                                                                                                                "affix"));
                        affloaded = true;
                }
-               else if (pg_strcasecmp("StopWords", pcfg->key) == 0)
+               else if (pg_strcasecmp(defel->defname, "StopWords") == 0)
                {
                        if (stoploaded)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple StopWords parameters")));
-                       readstoplist(pcfg->value, &(d->stoplist));
+                       readstoplist(defGetString(defel), &(d->stoplist));
                        sortstoplist(&(d->stoplist));
                        stoploaded = true;
                }
@@ -91,13 +82,9 @@ dispell_init(PG_FUNCTION_ARGS)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("unrecognized ISpell parameter: \"%s\"",
-                                                       pcfg->key)));
+                                                       defel->defname)));
                }
-               pfree(pcfg->key);
-               pfree(pcfg->value);
-               pcfg++;
        }
-       pfree(cfg);
 
        if (affloaded && dictloaded)
        {
index 2c1bc3d017ee33fdc0f11abe1a1df49fd4b0d288..fcc08ea180da5d561626e6b80a69004f8001de85 100644 (file)
@@ -7,12 +7,13 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/dict_simple.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/dict_simple.c,v 1.2 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "commands/defrem.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
 #include "tsearch/ts_utils.h"
@@ -28,18 +29,34 @@ typedef struct
 Datum
 dsimple_init(PG_FUNCTION_ARGS)
 {
+       List       *dictoptions = (List *) PG_GETARG_POINTER(0);
        DictExample *d = (DictExample *) palloc0(sizeof(DictExample));
+       bool            stoploaded = false;
+       ListCell   *l;
 
        d->stoplist.wordop = recode_and_lowerstr;
 
-       if (!PG_ARGISNULL(0) && PG_GETARG_POINTER(0) != NULL)
+       foreach(l, dictoptions)
        {
-               text   *in = PG_GETARG_TEXT_P(0);
-               char   *filename = TextPGetCString(in);
+               DefElem    *defel = (DefElem *) lfirst(l);
 
-               readstoplist(filename, &d->stoplist);
-               sortstoplist(&d->stoplist);
-               pfree(filename);
+               if (pg_strcasecmp("StopWords", defel->defname) == 0)
+               {
+                       if (stoploaded)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("multiple StopWords parameters")));
+                       readstoplist(defGetString(defel), &d->stoplist);
+                       sortstoplist(&d->stoplist);
+                       stoploaded = true;
+               }
+               else
+               {
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("unrecognized simple dictionary parameter: \"%s\"",
+                                                       defel->defname)));
+               }
        }
 
        PG_RETURN_POINTER(d);
index 8c544ad4f8aba495bfed226815181fb987edc1f5..70700db41fb8653d38188abbcd07a2579f8a12d8 100644 (file)
@@ -7,13 +7,14 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/dict_thesaurus.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/dict_thesaurus.c,v 1.2 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "catalog/namespace.h"
+#include "commands/defrem.h"
 #include "storage/fd.h"
 #include "tsearch/ts_cache.h"
 #include "tsearch/ts_locale.h"
@@ -593,57 +594,43 @@ compileTheSubstitute(DictThesaurus * d)
 Datum
 thesaurus_init(PG_FUNCTION_ARGS)
 {
+       List       *dictoptions = (List *) PG_GETARG_POINTER(0);
        DictThesaurus *d;
-       Map                *cfg,
-                          *pcfg;
-       text       *in;
        char       *subdictname = NULL;
        bool            fileloaded = false;
-
-       /* init functions must defend against NULLs for themselves */
-       if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("NULL config not allowed for Thesaurus")));
-       in = PG_GETARG_TEXT_P(0);
-
-       parse_keyvalpairs(in, &cfg);
-       PG_FREE_IF_COPY(in, 0);
+       ListCell   *l;
 
        d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
 
-       pcfg = cfg;
-       while (pcfg->key)
+       foreach(l, dictoptions)
        {
-               if (pg_strcasecmp("DictFile", pcfg->key) == 0)
+               DefElem    *defel = (DefElem *) lfirst(l);
+
+               if (pg_strcasecmp("DictFile", defel->defname) == 0)
                {
                        if (fileloaded)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple DictFile parameters")));
-                       thesaurusRead(pcfg->value, d);
+                       thesaurusRead(defGetString(defel), d);
                        fileloaded = true;
                }
-               else if (pg_strcasecmp("Dictionary", pcfg->key) == 0)
+               else if (pg_strcasecmp("Dictionary", defel->defname) == 0)
                {
                        if (subdictname)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                                 errmsg("multiple Dictionary parameters")));
-                       subdictname = pstrdup(pcfg->value);
+                       subdictname = pstrdup(defGetString(defel));
                }
                else
                {
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("unrecognized Thesaurus parameter: \"%s\"",
-                                                       pcfg->key)));
+                                                       defel->defname)));
                }
-               pfree(pcfg->key);
-               pfree(pcfg->value);
-               pcfg++;
        }
-       pfree(cfg);
 
        if (!fileloaded)
                ereport(ERROR,
index bb0a75ca85aa348f11302d5c60a1efee46ecce2f..9270c403696156cf1deaf19af793ebb9cab577f8 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/ts_utils.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/ts_utils.c,v 1.2 2007/08/22 01:39:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/builtins.h"
 
 
-#define CS_WAITKEY     0
-#define CS_INKEY       1
-#define CS_WAITEQ      2
-#define CS_WAITVALUE   3
-#define CS_INVALUE     4
-#define CS_IN2VALUE 5
-#define CS_WAITDELIM   6
-#define CS_INESC       7
-#define CS_IN2ESC      8
-
-static char *
-nstrdup(char *ptr, int len)
-{
-       char       *res = palloc(len + 1),
-                          *cptr;
-
-       memcpy(res, ptr, len);
-       res[len] = '\0';
-       cptr = ptr = res;
-       while (*ptr)
-       {
-               if (t_iseq(ptr, '\\'))
-                       ptr++;
-               COPYCHAR(cptr, ptr);
-               cptr += pg_mblen(ptr);
-               ptr += pg_mblen(ptr);
-       }
-       *cptr = '\0';
-
-       return res;
-}
-
-/*
- * Parse a parameter string consisting of key = value clauses
- */
-void
-parse_keyvalpairs(text *in, Map ** m)
-{
-       Map                *mptr;
-       char       *ptr = VARDATA(in),
-                          *begin = NULL;
-       char            num = 0;
-       int                     state = CS_WAITKEY;
-
-       while (ptr - VARDATA(in) < VARSIZE(in) - VARHDRSZ)
-       {
-               if (t_iseq(ptr, ','))
-                       num++;
-               ptr += pg_mblen(ptr);
-       }
-
-       *m = mptr = (Map *) palloc(sizeof(Map) * (num + 2));
-       memset(mptr, 0, sizeof(Map) * (num + 2));
-       ptr = VARDATA(in);
-       while (ptr - VARDATA(in) < VARSIZE(in) - VARHDRSZ)
-       {
-               if (state == CS_WAITKEY)
-               {
-                       if (t_isalpha(ptr))
-                       {
-                               begin = ptr;
-                               state = CS_INKEY;
-                       }
-                       else if (!t_isspace(ptr))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("invalid parameter list format: \"%s\"",
-                                                               TextPGetCString(in))));
-               }
-               else if (state == CS_INKEY)
-               {
-                       if (t_isspace(ptr))
-                       {
-                               mptr->key = nstrdup(begin, ptr - begin);
-                               state = CS_WAITEQ;
-                       }
-                       else if (t_iseq(ptr, '='))
-                       {
-                               mptr->key = nstrdup(begin, ptr - begin);
-                               state = CS_WAITVALUE;
-                       }
-                       else if (!t_isalpha(ptr))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("invalid parameter list format: \"%s\"",
-                                                               TextPGetCString(in))));
-               }
-               else if (state == CS_WAITEQ)
-               {
-                       if (t_iseq(ptr, '='))
-                               state = CS_WAITVALUE;
-                       else if (!t_isspace(ptr))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("invalid parameter list format: \"%s\"",
-                                                               TextPGetCString(in))));
-               }
-               else if (state == CS_WAITVALUE)
-               {
-                       if (t_iseq(ptr, '"'))
-                       {
-                               begin = ptr + 1;
-                               state = CS_INVALUE;
-                       }
-                       else if (!t_isspace(ptr))
-                       {
-                               begin = ptr;
-                               state = CS_IN2VALUE;
-                       }
-               }
-               else if (state == CS_INVALUE)
-               {
-                       if (t_iseq(ptr, '"'))
-                       {
-                               mptr->value = nstrdup(begin, ptr - begin);
-                               mptr++;
-                               state = CS_WAITDELIM;
-                       }
-                       else if (t_iseq(ptr, '\\'))
-                               state = CS_INESC;
-               }
-               else if (state == CS_IN2VALUE)
-               {
-                       if (t_isspace(ptr) || t_iseq(ptr, ','))
-                       {
-                               mptr->value = nstrdup(begin, ptr - begin);
-                               mptr++;
-                               state = (t_iseq(ptr, ',')) ? CS_WAITKEY : CS_WAITDELIM;
-                       }
-                       else if (t_iseq(ptr, '\\'))
-                               state = CS_INESC;
-               }
-               else if (state == CS_WAITDELIM)
-               {
-                       if (t_iseq(ptr, ','))
-                               state = CS_WAITKEY;
-                       else if (!t_isspace(ptr))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("invalid parameter list format: \"%s\"",
-                                                               TextPGetCString(in))));
-               }
-               else if (state == CS_INESC)
-                       state = CS_INVALUE;
-               else if (state == CS_IN2ESC)
-                       state = CS_IN2VALUE;
-               else
-                       elog(ERROR, "unrecognized parse_keyvalpairs state: %d", state);
-               ptr += pg_mblen(ptr);
-       }
-
-       if (state == CS_IN2VALUE)
-       {
-               mptr->value = nstrdup(begin, ptr - begin);
-               mptr++;
-       }
-       else if (!(state == CS_WAITDELIM || state == CS_WAITKEY))
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("invalid parameter list format: \"%s\"",
-                                               TextPGetCString(in))));
-}
-
 /*
  * Given the base name and extension of a tsearch config file, return
  * its full path name.  The base name is assumed to be user-supplied,
index 0b374e8159eb130e0cc87e076ced2c10ebff0805..e927e98aab2f147ba9188d7e0f6e0dde5605d67f 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/wparser.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/wparser.c,v 1.2 2007/08/22 01:39:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_ts_parser.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "tsearch/ts_cache.h"
 #include "tsearch/ts_public.h"
 #include "tsearch/ts_utils.h"
@@ -300,6 +301,7 @@ ts_headline_byid_opt(PG_FUNCTION_ARGS)
        TSQuery         query = PG_GETARG_TSQUERY(2);
        text       *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL;
        HeadlineText prs;
+       List       *prsoptions;
        text       *out;
        TSConfigCacheEntry *cfg;
        TSParserCacheEntry *prsobj;
@@ -313,9 +315,14 @@ ts_headline_byid_opt(PG_FUNCTION_ARGS)
 
        hlparsetext(cfg->cfgId, &prs, query, VARDATA(in), VARSIZE(in) - VARHDRSZ);
 
+       if (opt)
+               prsoptions = deserialize_deflist(PointerGetDatum(opt));
+       else
+               prsoptions = NIL;
+
        FunctionCall3(&(prsobj->prsheadline),
                                  PointerGetDatum(&prs),
-                                 PointerGetDatum(opt),
+                                 PointerGetDatum(prsoptions),
                                  PointerGetDatum(query));
 
        out = generatHeadline(&prs);
index 8d71e3e914e3f4e7359b3ade870874c8bf40d714..5b47f66d07fdf77c93e21cf4bdbed3eeb0f8d3d6 100644 (file)
@@ -7,13 +7,14 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tsearch/wparser_def.c,v 1.1 2007/08/21 01:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tsearch/wparser_def.c,v 1.2 2007/08/22 01:39:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "commands/defrem.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
 #include "tsearch/ts_type.h"
@@ -1662,7 +1663,7 @@ Datum
 prsd_headline(PG_FUNCTION_ARGS)
 {
        HeadlineParsedText *prs = (HeadlineParsedText *) PG_GETARG_POINTER(0);
-       text       *opt = (text *) PG_GETARG_POINTER(1);        /* can't be toasted */
+       List       *prsoptions = (List *) PG_GETARG_POINTER(1);
        TSQuery         query = PG_GETARG_TSQUERY(2);
 
        /* from opt + start and and tag */
@@ -1682,66 +1683,55 @@ prsd_headline(PG_FUNCTION_ARGS)
 
        int                     i;
        int                     highlight = 0;
+       ListCell   *l;
 
        /* config */
        prs->startsel = NULL;
        prs->stopsel = NULL;
-       if (opt)
+       foreach(l, prsoptions)
        {
-               Map                *map,
-                                  *mptr;
-
-               parse_keyvalpairs(opt, &map);
-               mptr = map;
-
-               while (mptr && mptr->key)
-               {
-                       if (pg_strcasecmp(mptr->key, "MaxWords") == 0)
-                               max_words = pg_atoi(mptr->value, 4, 1);
-                       else if (pg_strcasecmp(mptr->key, "MinWords") == 0)
-                               min_words = pg_atoi(mptr->value, 4, 1);
-                       else if (pg_strcasecmp(mptr->key, "ShortWord") == 0)
-                               shortword = pg_atoi(mptr->value, 4, 1);
-                       else if (pg_strcasecmp(mptr->key, "StartSel") == 0)
-                               prs->startsel = pstrdup(mptr->value);
-                       else if (pg_strcasecmp(mptr->key, "StopSel") == 0)
-                               prs->stopsel = pstrdup(mptr->value);
-                       else if (pg_strcasecmp(mptr->key, "HighlightAll") == 0)
-                               highlight = (
-                                                        pg_strcasecmp(mptr->value, "1") == 0 ||
-                                                        pg_strcasecmp(mptr->value, "on") == 0 ||
-                                                        pg_strcasecmp(mptr->value, "true") == 0 ||
-                                                        pg_strcasecmp(mptr->value, "t") == 0 ||
-                                                        pg_strcasecmp(mptr->value, "y") == 0 ||
-                                                        pg_strcasecmp(mptr->value, "yes") == 0) ?
-                                       1 : 0;
-
-                       pfree(mptr->key);
-                       pfree(mptr->value);
-
-                       mptr++;
-               }
-               pfree(map);
-
-               if (highlight == 0)
-               {
-                       if (min_words >= max_words)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("MinWords should be less than MaxWords")));
-                       if (min_words <= 0)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("MinWords should be positive")));
-                       if (shortword < 0)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("ShortWord should be >= 0")));
-               }
+               DefElem    *defel = (DefElem *) lfirst(l);
+               char       *val = defGetString(defel);
+
+               if (pg_strcasecmp(defel->defname, "MaxWords") == 0)
+                       max_words = pg_atoi(val, sizeof(int32), 0);
+               else if (pg_strcasecmp(defel->defname, "MinWords") == 0)
+                       min_words = pg_atoi(val, sizeof(int32), 0);
+               else if (pg_strcasecmp(defel->defname, "ShortWord") == 0)
+                       shortword = pg_atoi(val, sizeof(int32), 0);
+               else if (pg_strcasecmp(defel->defname, "StartSel") == 0)
+                       prs->startsel = pstrdup(val);
+               else if (pg_strcasecmp(defel->defname, "StopSel") == 0)
+                       prs->stopsel = pstrdup(val);
+               else if (pg_strcasecmp(defel->defname, "HighlightAll") == 0)
+                       highlight = (pg_strcasecmp(val, "1") == 0 ||
+                                                pg_strcasecmp(val, "on") == 0 ||
+                                                pg_strcasecmp(val, "true") == 0 ||
+                                                pg_strcasecmp(val, "t") == 0 ||
+                                                pg_strcasecmp(val, "y") == 0 ||
+                                                pg_strcasecmp(val, "yes") == 0);
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("unrecognized headline parameter: \"%s\"",
+                                                       defel->defname)));
        }
 
        if (highlight == 0)
        {
+               if (min_words >= max_words)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("MinWords should be less than MaxWords")));
+               if (min_words <= 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("MinWords should be positive")));
+               if (shortword < 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("ShortWord should be >= 0")));
+
                while (hlCover(prs, query, &p, &q))
                {
                        /* find cover len in words */
index cd3a9dad57116e7cb1f12f674433cbaf572e6b8e..94051b8c32f1233c3c9e259bb349bb617b31c951 100644 (file)
@@ -20,7 +20,7 @@
  * Copyright (c) 2006-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.1 2007/08/21 01:11:19 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.2 2007/08/22 01:39:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,9 +37,9 @@
 #include "catalog/pg_ts_parser.h"
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "miscadmin.h"
 #include "tsearch/ts_cache.h"
-#include "tsearch/ts_utils.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
@@ -252,7 +252,7 @@ lookup_ts_dictionary_cache(Oid dictId)
                                        tptmpl;
                Form_pg_ts_dict dict;
                Form_pg_ts_template template;
-               MemoryContext saveCtx = NULL;
+               MemoryContext saveCtx;
 
                tpdict = SearchSysCache(TSDICTOID,
                                                                ObjectIdGetDatum(dictId),
@@ -319,21 +319,30 @@ lookup_ts_dictionary_cache(Oid dictId)
 
                if (OidIsValid(template->tmplinit))
                {
-                       bool            isnull;
+                       List       *dictoptions;
                        Datum           opt;
+                       bool            isnull;
+                       MemoryContext oldcontext;
+
+                       /*
+                        * Init method runs in dictionary's private memory context,
+                        * and we make sure the options are stored there too
+                        */
+                       oldcontext = MemoryContextSwitchTo(entry->dictCtx);
 
                        opt = SysCacheGetAttr(TSDICTOID, tpdict,
                                                                  Anum_pg_ts_dict_dictinitoption,
                                                                  &isnull);
                        if (isnull)
-                               opt = PointerGetDatum(NULL);
+                               dictoptions = NIL;
+                       else
+                               dictoptions = deserialize_deflist(opt);
 
-                       /*
-                        * Init method runs in dictionary's private memory context
-                        */
-                       saveCtx = MemoryContextSwitchTo(entry->dictCtx);
-                       entry->dictData = DatumGetPointer(OidFunctionCall1(template->tmplinit, opt));
-                       MemoryContextSwitchTo(saveCtx);
+                       entry->dictData =
+                               DatumGetPointer(OidFunctionCall1(template->tmplinit,
+                                                                                       PointerGetDatum(dictoptions)));
+
+                       MemoryContextSwitchTo(oldcontext);
                }
 
                ReleaseSysCache(tptmpl);
index 4d4d7f7986e9ffb241f004d4d247b5fa90e689c7..7da419db6aa966733f2e6410a9fafcac223a7f3c 100644 (file)
@@ -12,7 +12,7 @@
  *     by PostgreSQL
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.470 2007/08/21 01:11:21 tgl Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.471 2007/08/22 01:39:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -8288,11 +8288,9 @@ dumpTSDictionary(Archive *fout, TSDictInfo * dictinfo)
 
        PQclear(res);
 
+       /* the dictinitoption can be dumped straight into the command */
        if (dictinfo->dictinitoption)
-       {
-               appendPQExpBuffer(q, ",\n    OPTION = ");
-               appendStringLiteralConn(q, dictinfo->dictinitoption, g_conn);
-       }
+               appendPQExpBuffer(q, ",\n    %s", dictinfo->dictinitoption);
 
        appendPQExpBuffer(q, " );\n");
 
index a050ff1d3ccef34354552d21b5a8bb738e03f4a6..be40e7cf0f3d7efc0269d8a7978a35e6d2bf7d85 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.416 2007/08/21 01:11:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.417 2007/08/22 01:39:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200708201
+#define CATALOG_VERSION_NO     200708211
 
 #endif
index 1a624c6dbe0cad293f6facf7ff40808c993caba3..a19bda2ab7e30485223f7c17320d8022397fa712 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.463 2007/08/21 01:11:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.464 2007/08/22 01:39:45 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -4311,7 +4311,7 @@ DATA(insert OID = 3718 (  prsd_nexttoken  PGNSP PGUID 12 1 0 f f t f i 3 2281 "22
 DESCR("");
 DATA(insert OID = 3719 (  prsd_end                     PGNSP PGUID 12 1 0 f f t f i 1 2278 "2281" _null_ _null_ _null_ prsd_end - _null_ ));
 DESCR("");
-DATA(insert OID = 3720 (  prsd_headline                PGNSP PGUID 12 1 0 f f t f i 3 2281 "2281 25 3615" _null_ _null_ _null_ prsd_headline - _null_ ));
+DATA(insert OID = 3720 (  prsd_headline                PGNSP PGUID 12 1 0 f f t f i 3 2281 "2281 2281 3615" _null_ _null_ _null_ prsd_headline - _null_ ));
 DESCR("");
 DATA(insert OID = 3721 (  prsd_lextype         PGNSP PGUID 12 1 0 f f t f i 1 2281 "2281" _null_ _null_ _null_ prsd_lextype - _null_ ));
 DESCR("");
@@ -4321,22 +4321,22 @@ DESCR("normalize one word by dictionary");
 DATA(insert OID = 3724 (  ts_lexize                    PGNSP PGUID 12 1 0 f f t f s 2 1009 "25 25" _null_ _null_ _null_ ts_lexize_byname - _null_ ));
 DESCR("normalize one word by dictionary");
 
-DATA(insert OID = 3725 (  dsimple_init         PGNSP PGUID 12 1 0 f f f f i 1 2281 "2281" _null_ _null_ _null_ dsimple_init - _null_ ));
+DATA(insert OID = 3725 (  dsimple_init         PGNSP PGUID 12 1 0 f f t f i 1 2281 "2281" _null_ _null_ _null_ dsimple_init - _null_ ));
 DESCR("");
 DATA(insert OID = 3726 (  dsimple_lexize       PGNSP PGUID 12 1 0 f f t f i 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ dsimple_lexize - _null_ ));
 DESCR("");
 
-DATA(insert OID = 3728 (  dsynonym_init                PGNSP PGUID 12 1 0 f f f f i 1 2281 "2281" _null_ _null_ _null_ dsynonym_init - _null_ ));
+DATA(insert OID = 3728 (  dsynonym_init                PGNSP PGUID 12 1 0 f f t f i 1 2281 "2281" _null_ _null_ _null_ dsynonym_init - _null_ ));
 DESCR("");
 DATA(insert OID = 3729 (  dsynonym_lexize      PGNSP PGUID 12 1 0 f f t f i 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ dsynonym_lexize - _null_ ));
 DESCR("");
 
-DATA(insert OID = 3731 (  dispell_init         PGNSP PGUID 12 1 0 f f f f i 1 2281 "2281" _null_ _null_ _null_ dispell_init - _null_ ));
+DATA(insert OID = 3731 (  dispell_init         PGNSP PGUID 12 1 0 f f t f i 1 2281 "2281" _null_ _null_ _null_ dispell_init - _null_ ));
 DESCR("");
 DATA(insert OID = 3732 (  dispell_lexize       PGNSP PGUID 12 1 0 f f t f i 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ dispell_lexize - _null_ ));
 DESCR("");
 
-DATA(insert OID = 3740 (  thesaurus_init       PGNSP PGUID 12 1 0 f f f f i 1 2281 "2281" _null_ _null_ _null_ thesaurus_init - _null_ ));
+DATA(insert OID = 3740 (  thesaurus_init       PGNSP PGUID 12 1 0 f f t f i 1 2281 "2281" _null_ _null_ _null_ thesaurus_init - _null_ ));
 DESCR("");
 DATA(insert OID = 3741 (  thesaurus_lexize     PGNSP PGUID 12 1 0 f f t f i 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ thesaurus_lexize - _null_ ));
 DESCR("");
index 514507d26f08ec283bc877bad425d5e05f964187..e3c0af870df9833051808da8229eebdd92163b21 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.83 2007/08/21 01:11:27 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.84 2007/08/22 01:39:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -121,6 +121,9 @@ extern void RemoveTSConfigurationById(Oid cfgId);
 extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId);
 
+extern text *serialize_deflist(List *deflist);
+extern List *deserialize_deflist(Datum txt);
+
 /* support routines in commands/define.c */
 
 extern char *case_translate_language_name(const char *input);
index 8e8fa5cc6ff0ee4109b2787a2f7145da3299db1e..718abdb61d4aee65c90bda693bae48b290f3f469 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1998-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/tsearch/ts_public.h,v 1.1 2007/08/21 01:11:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/tsearch/ts_public.h,v 1.2 2007/08/22 01:39:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,16 +59,6 @@ typedef struct
 /*
  * Common useful things for tsearch subsystem
  */
-
-/* simple parser of cfg string looking like "key=val, key='val'" */
-typedef struct
-{
-       char       *key;
-       char       *value;
-} Map;
-
-extern void parse_keyvalpairs(text *in, Map ** m);
-
 extern char *get_tsearch_config_filename(const char *basename,
                                                                                 const char *extension);