Simplify the syntax of CREATE/ALTER TEXT SEARCH DICTIONARY by treating the

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.
This commit is contained in:
Tom Lane 2007-08-22 01:39:46 +00:00
parent fd33d90a23
commit d321421d0a
17 changed files with 618 additions and 417 deletions

View 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 PostgreSQL documentation
--> -->
@ -20,7 +20,9 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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> RENAME TO <replaceable>newname</replaceable>
ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceable>newowner</replaceable> ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceable>newowner</replaceable>
</synopsis> </synopsis>
@ -31,8 +33,8 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
<para> <para>
<command>ALTER TEXT SEARCH DICTIONARY</command> changes the definition of <command>ALTER TEXT SEARCH DICTIONARY</command> changes the definition of
a text search dictionary. You can change the dictionary's initialization a text search dictionary. You can change the dictionary's
options, or change the dictionary's name or owner. template-specific options, or change the dictionary's name or owner.
</para> </para>
<para> <para>
@ -56,11 +58,22 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">init_options</replaceable></term> <term><replaceable class="parameter">option</replaceable></term>
<listitem> <listitem>
<para> <para>
A new list of initialization options, or <literal>NULL</> to The name of a template-specific option to be set for this dictionary.
remove all options. </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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -83,18 +96,31 @@ ALTER TEXT SEARCH DICTIONARY <replaceable>name</replaceable> OWNER TO <replaceab
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<para>
Template-specific options can appear in any order.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Examples</title>
<para> <para>
The following example command sets the language and stopword list The following example command changes the stopword list
for a Snowball-based dictionary. for a Snowball-based dictionary. Other parameters remain unchanged.
</para> </para>
<programlisting> <programlisting>
ALTER TEXT SEARCH DICTIONARY my_russian ( option = 'Language=russian, StopWords=my_russian' ); 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_dict ( language = dutch, StopWords );
</programlisting> </programlisting>
</refsect1> </refsect1>

View 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 PostgreSQL documentation
--> -->
@ -22,7 +22,7 @@ PostgreSQL documentation
<synopsis> <synopsis>
CREATE TEXT SEARCH DICTIONARY <replaceable class="parameter">name</replaceable> ( CREATE TEXT SEARCH DICTIONARY <replaceable class="parameter">name</replaceable> (
TEMPLATE = <replaceable class="parameter">template</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> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
@ -78,17 +78,46 @@ CREATE TEXT SEARCH DICTIONARY <replaceable class="parameter">name</replaceable>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">init_options</replaceable></term> <term><replaceable class="parameter">option</replaceable></term>
<listitem> <listitem>
<para> <para>
A list of initialization options for the template functions. The name of a template-specific option to be set for this dictionary.
This is a string containing <replaceable>keyword</> <literal>=</> </para>
<replaceable>value</> pairs. The specific keywords allowed </listitem>
vary depending on the text search template. </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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </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>
<refsect1> <refsect1>

View File

@ -9,12 +9,13 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "miscadmin.h"
#include <ctype.h>
#include "access/heapam.h" #include "access/heapam.h"
#include "access/genam.h" #include "access/genam.h"
@ -31,6 +32,8 @@
#include "catalog/pg_ts_template.h" #include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h" #include "parser/parse_func.h"
#include "tsearch/ts_cache.h" #include "tsearch/ts_cache.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
@ -86,7 +89,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
break; break;
case Anum_pg_ts_parser_prsheadline: case Anum_pg_ts_parser_prsheadline:
nargs = 3; nargs = 3;
typeId[1] = TEXTOID; typeId[1] = INTERNALOID;
typeId[2] = TSQUERYOID; typeId[2] = TSQUERYOID;
break; break;
case Anum_pg_ts_parser_prslextype: case Anum_pg_ts_parser_prslextype:
@ -407,6 +410,53 @@ makeDictionaryDependencies(HeapTuple tuple)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); 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 * CREATE TEXT SEARCH DICTIONARY
*/ */
@ -419,7 +469,8 @@ DefineTSDictionary(List *names, List *parameters)
Datum values[Natts_pg_ts_dict]; Datum values[Natts_pg_ts_dict];
char nulls[Natts_pg_ts_dict]; char nulls[Natts_pg_ts_dict];
NameData dname; NameData dname;
int i; Oid templId = InvalidOid;
List *dictoptions = NIL;
Oid dictOid; Oid dictOid;
Oid namespaceoid; Oid namespaceoid;
AclResult aclresult; AclResult aclresult;
@ -434,18 +485,6 @@ DefineTSDictionary(List *names, List *parameters)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceoid)); 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. * 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) if (pg_strcasecmp(defel->defname, "template") == 0)
{ {
Oid templId;
templId = TSTemplateGetTmplid(defGetQualifiedName(defel), false); 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)
{
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] = ' ';
}
} }
else else
ereport(ERROR, {
(errcode(ERRCODE_SYNTAX_ERROR), /* Assume it's an option for the dictionary itself */
errmsg("text search dictionary parameter \"%s\" not recognized", dictoptions = lappend(dictoptions, defel);
defel->defname))); }
} }
/* /*
* Validation * Validation
*/ */
if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_dict_dicttemplate - 1]))) if (!OidIsValid(templId))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("text search template is required"))); errmsg("text search template is required")));
verify_dictoptions(templId, dictoptions);
/* /*
* Looks good, insert * 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); dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
@ -652,6 +690,9 @@ AlterTSDictionary(AlterTSDictionaryStmt * stmt)
Relation rel; Relation rel;
Oid dictId; Oid dictId;
ListCell *pl; ListCell *pl;
List *dictoptions;
Datum opt;
bool isnull;
Datum repl_val[Natts_pg_ts_dict]; Datum repl_val[Natts_pg_ts_dict];
char repl_null[Natts_pg_ts_dict]; char repl_null[Natts_pg_ts_dict];
char repl_repl[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, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
NameListToString(stmt->dictname)); NameListToString(stmt->dictname));
memset(repl_val, 0, sizeof(repl_val)); /* deserialize the existing set of options */
memset(repl_null, ' ', sizeof(repl_null)); opt = SysCacheGetAttr(TSDICTOID, tup,
memset(repl_repl, ' ', sizeof(repl_repl)); 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, * Modify the options list as per specified changes
* there is no need to update dependencies.
*/ */
foreach(pl, stmt->options) foreach(pl, stmt->options)
{ {
DefElem *defel = (DefElem *) lfirst(pl); 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) next = lnext(cell);
{ if (pg_strcasecmp(oldel->defname, defel->defname) == 0)
repl_null[Anum_pg_ts_dict_dictinitoption - 1] = 'n'; dictoptions = list_delete_cell(dictoptions, cell, prev);
}
else else
{ prev = cell;
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';
} }
else
ereport(ERROR, /*
(errcode(ERRCODE_SYNTAX_ERROR), * and add new value if it's got one
errmsg("text search dictionary parameter \"%s\" not recognized", */
defel->defname))); 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), newtup = heap_modifytuple(tup, RelationGetDescr(rel),
repl_val, repl_null, repl_repl); repl_val, repl_null, repl_repl);
@ -715,6 +782,12 @@ AlterTSDictionary(AlterTSDictionaryStmt * stmt)
CatalogUpdateIndexes(rel, newtup); 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); heap_freetuple(newtup);
ReleaseSysCache(tup); ReleaseSysCache(tup);
@ -1941,3 +2014,265 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup)
heap_close(relMap, RowExclusiveLock); 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;
}

View File

@ -6,12 +6,13 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 "postgres.h"
#include "commands/defrem.h"
#include "fmgr.h" #include "fmgr.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
@ -185,59 +186,44 @@ locate_stem_module(DictSnowball * d, char *lang)
Datum Datum
dsnowball_init(PG_FUNCTION_ARGS) dsnowball_init(PG_FUNCTION_ARGS)
{ {
text *in; List *dictoptions = (List *) PG_GETARG_POINTER(0);
DictSnowball *d; DictSnowball *d;
Map *cfg,
*pcfg;
bool stoploaded = false; bool stoploaded = false;
ListCell *l;
/* 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);
d = (DictSnowball *) palloc0(sizeof(DictSnowball)); d = (DictSnowball *) palloc0(sizeof(DictSnowball));
d->stoplist.wordop = recode_and_lowerstr; d->stoplist.wordop = recode_and_lowerstr;
parse_keyvalpairs(in, &cfg); foreach(l, dictoptions)
pcfg = cfg;
PG_FREE_IF_COPY(in, 0);
while (pcfg && pcfg->key)
{ {
if (pg_strcasecmp("StopWords", pcfg->key) == 0) DefElem *defel = (DefElem *) lfirst(l);
if (pg_strcasecmp("StopWords", defel->defname) == 0)
{ {
if (stoploaded) if (stoploaded)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple StopWords parameters"))); errmsg("multiple StopWords parameters")));
readstoplist(pcfg->value, &d->stoplist); readstoplist(defGetString(defel), &d->stoplist);
sortstoplist(&d->stoplist); sortstoplist(&d->stoplist);
stoploaded = true; stoploaded = true;
} }
else if (pg_strcasecmp("Language", pcfg->key) == 0) else if (pg_strcasecmp("Language", defel->defname) == 0)
{ {
if (d->stem) if (d->stem)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple Language parameters"))); errmsg("multiple Language parameters")));
locate_stem_module(d, pcfg->value); locate_stem_module(d, defGetString(defel));
} }
else else
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized Snowball parameter: \"%s\"", errmsg("unrecognized Snowball parameter: \"%s\"",
pcfg->key))); defel->defname)));
} }
pfree(pcfg->key);
pfree(pcfg->value);
pcfg++;
} }
pfree(cfg);
if (!d->stem) if (!d->stem)
ereport(ERROR, ereport(ERROR,

View 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 -- text search configuration for _CFGNAME_ language
CREATE TEXT SEARCH DICTIONARY _DICTNAME_ CREATE TEXT SEARCH DICTIONARY _DICTNAME_
(TEMPLATE = snowball, (TEMPLATE = snowball,
OPTION = 'Language=_DICTNAME__STOPWORDS_'); Language = _DICTNAME_ _STOPWORDS_);
COMMENT ON TEXT SEARCH DICTIONARY _DICTNAME_ IS 'Snowball stemmer for _DICTNAME_ language'; COMMENT ON TEXT SEARCH DICTIONARY _DICTNAME_ IS 'Snowball stemmer for _DICTNAME_ language';

View File

@ -7,12 +7,13 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "commands/defrem.h"
#include "tsearch/dicts/spell.h" #include "tsearch/dicts/spell.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
@ -30,59 +31,49 @@ typedef struct
Datum Datum
dispell_init(PG_FUNCTION_ARGS) dispell_init(PG_FUNCTION_ARGS)
{ {
List *dictoptions = (List *) PG_GETARG_POINTER(0);
DictISpell *d; DictISpell *d;
Map *cfg,
*pcfg;
bool affloaded = false, bool affloaded = false,
dictloaded = false, dictloaded = false,
stoploaded = false; stoploaded = false;
text *in; ListCell *l;
/* 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);
d = (DictISpell *) palloc0(sizeof(DictISpell)); d = (DictISpell *) palloc0(sizeof(DictISpell));
d->stoplist.wordop = recode_and_lowerstr; d->stoplist.wordop = recode_and_lowerstr;
pcfg = cfg; foreach(l, dictoptions)
while (pcfg->key)
{ {
if (pg_strcasecmp("DictFile", pcfg->key) == 0) DefElem *defel = (DefElem *) lfirst(l);
if (pg_strcasecmp(defel->defname, "DictFile") == 0)
{ {
if (dictloaded) if (dictloaded)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple DictFile parameters"))); errmsg("multiple DictFile parameters")));
NIImportDictionary(&(d->obj), NIImportDictionary(&(d->obj),
get_tsearch_config_filename(pcfg->value, get_tsearch_config_filename(defGetString(defel),
"dict")); "dict"));
dictloaded = true; dictloaded = true;
} }
else if (pg_strcasecmp("AffFile", pcfg->key) == 0) else if (pg_strcasecmp(defel->defname, "AffFile") == 0)
{ {
if (affloaded) if (affloaded)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple AffFile parameters"))); errmsg("multiple AffFile parameters")));
NIImportAffixes(&(d->obj), NIImportAffixes(&(d->obj),
get_tsearch_config_filename(pcfg->value, get_tsearch_config_filename(defGetString(defel),
"affix")); "affix"));
affloaded = true; affloaded = true;
} }
else if (pg_strcasecmp("StopWords", pcfg->key) == 0) else if (pg_strcasecmp(defel->defname, "StopWords") == 0)
{ {
if (stoploaded) if (stoploaded)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple StopWords parameters"))); errmsg("multiple StopWords parameters")));
readstoplist(pcfg->value, &(d->stoplist)); readstoplist(defGetString(defel), &(d->stoplist));
sortstoplist(&(d->stoplist)); sortstoplist(&(d->stoplist));
stoploaded = true; stoploaded = true;
} }
@ -91,13 +82,9 @@ dispell_init(PG_FUNCTION_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized ISpell parameter: \"%s\"", errmsg("unrecognized ISpell parameter: \"%s\"",
pcfg->key))); defel->defname)));
} }
pfree(pcfg->key);
pfree(pcfg->value);
pcfg++;
} }
pfree(cfg);
if (affloaded && dictloaded) if (affloaded && dictloaded)
{ {

View File

@ -7,12 +7,13 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "commands/defrem.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
#include "tsearch/ts_utils.h" #include "tsearch/ts_utils.h"
@ -28,18 +29,34 @@ typedef struct
Datum Datum
dsimple_init(PG_FUNCTION_ARGS) dsimple_init(PG_FUNCTION_ARGS)
{ {
List *dictoptions = (List *) PG_GETARG_POINTER(0);
DictExample *d = (DictExample *) palloc0(sizeof(DictExample)); DictExample *d = (DictExample *) palloc0(sizeof(DictExample));
bool stoploaded = false;
ListCell *l;
d->stoplist.wordop = recode_and_lowerstr; 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); DefElem *defel = (DefElem *) lfirst(l);
char *filename = TextPGetCString(in);
readstoplist(filename, &d->stoplist); if (pg_strcasecmp("StopWords", defel->defname) == 0)
sortstoplist(&d->stoplist); {
pfree(filename); 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); PG_RETURN_POINTER(d);

View File

@ -7,13 +7,14 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "commands/defrem.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "tsearch/ts_cache.h" #include "tsearch/ts_cache.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
@ -593,57 +594,43 @@ compileTheSubstitute(DictThesaurus * d)
Datum Datum
thesaurus_init(PG_FUNCTION_ARGS) thesaurus_init(PG_FUNCTION_ARGS)
{ {
List *dictoptions = (List *) PG_GETARG_POINTER(0);
DictThesaurus *d; DictThesaurus *d;
Map *cfg,
*pcfg;
text *in;
char *subdictname = NULL; char *subdictname = NULL;
bool fileloaded = false; bool fileloaded = false;
ListCell *l;
/* 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);
d = (DictThesaurus *) palloc0(sizeof(DictThesaurus)); d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
pcfg = cfg; foreach(l, dictoptions)
while (pcfg->key)
{ {
if (pg_strcasecmp("DictFile", pcfg->key) == 0) DefElem *defel = (DefElem *) lfirst(l);
if (pg_strcasecmp("DictFile", defel->defname) == 0)
{ {
if (fileloaded) if (fileloaded)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple DictFile parameters"))); errmsg("multiple DictFile parameters")));
thesaurusRead(pcfg->value, d); thesaurusRead(defGetString(defel), d);
fileloaded = true; fileloaded = true;
} }
else if (pg_strcasecmp("Dictionary", pcfg->key) == 0) else if (pg_strcasecmp("Dictionary", defel->defname) == 0)
{ {
if (subdictname) if (subdictname)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("multiple Dictionary parameters"))); errmsg("multiple Dictionary parameters")));
subdictname = pstrdup(pcfg->value); subdictname = pstrdup(defGetString(defel));
} }
else else
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized Thesaurus parameter: \"%s\"", errmsg("unrecognized Thesaurus parameter: \"%s\"",
pcfg->key))); defel->defname)));
} }
pfree(pcfg->key);
pfree(pcfg->value);
pcfg++;
} }
pfree(cfg);
if (!fileloaded) if (!fileloaded)
ereport(ERROR, ereport(ERROR,

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -24,169 +24,6 @@
#include "utils/builtins.h" #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 * 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, * its full path name. The base name is assumed to be user-supplied,

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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/namespace.h"
#include "catalog/pg_ts_parser.h" #include "catalog/pg_ts_parser.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "tsearch/ts_cache.h" #include "tsearch/ts_cache.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
#include "tsearch/ts_utils.h" #include "tsearch/ts_utils.h"
@ -300,6 +301,7 @@ ts_headline_byid_opt(PG_FUNCTION_ARGS)
TSQuery query = PG_GETARG_TSQUERY(2); TSQuery query = PG_GETARG_TSQUERY(2);
text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL; text *opt = (PG_NARGS() > 3 && PG_GETARG_POINTER(3)) ? PG_GETARG_TEXT_P(3) : NULL;
HeadlineText prs; HeadlineText prs;
List *prsoptions;
text *out; text *out;
TSConfigCacheEntry *cfg; TSConfigCacheEntry *cfg;
TSParserCacheEntry *prsobj; TSParserCacheEntry *prsobj;
@ -313,9 +315,14 @@ ts_headline_byid_opt(PG_FUNCTION_ARGS)
hlparsetext(cfg->cfgId, &prs, query, VARDATA(in), VARSIZE(in) - VARHDRSZ); hlparsetext(cfg->cfgId, &prs, query, VARDATA(in), VARSIZE(in) - VARHDRSZ);
if (opt)
prsoptions = deserialize_deflist(PointerGetDatum(opt));
else
prsoptions = NIL;
FunctionCall3(&(prsobj->prsheadline), FunctionCall3(&(prsobj->prsheadline),
PointerGetDatum(&prs), PointerGetDatum(&prs),
PointerGetDatum(opt), PointerGetDatum(prsoptions),
PointerGetDatum(query)); PointerGetDatum(query));
out = generatHeadline(&prs); out = generatHeadline(&prs);

View File

@ -7,13 +7,14 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "commands/defrem.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
#include "tsearch/ts_type.h" #include "tsearch/ts_type.h"
@ -1662,7 +1663,7 @@ Datum
prsd_headline(PG_FUNCTION_ARGS) prsd_headline(PG_FUNCTION_ARGS)
{ {
HeadlineParsedText *prs = (HeadlineParsedText *) PG_GETARG_POINTER(0); 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); TSQuery query = PG_GETARG_TSQUERY(2);
/* from opt + start and and tag */ /* from opt + start and and tag */
@ -1682,66 +1683,55 @@ prsd_headline(PG_FUNCTION_ARGS)
int i; int i;
int highlight = 0; int highlight = 0;
ListCell *l;
/* config */ /* config */
prs->startsel = NULL; prs->startsel = NULL;
prs->stopsel = NULL; prs->stopsel = NULL;
if (opt) foreach(l, prsoptions)
{ {
Map *map, DefElem *defel = (DefElem *) lfirst(l);
*mptr; char *val = defGetString(defel);
parse_keyvalpairs(opt, &map); if (pg_strcasecmp(defel->defname, "MaxWords") == 0)
mptr = map; max_words = pg_atoi(val, sizeof(int32), 0);
else if (pg_strcasecmp(defel->defname, "MinWords") == 0)
while (mptr && mptr->key) min_words = pg_atoi(val, sizeof(int32), 0);
{ else if (pg_strcasecmp(defel->defname, "ShortWord") == 0)
if (pg_strcasecmp(mptr->key, "MaxWords") == 0) shortword = pg_atoi(val, sizeof(int32), 0);
max_words = pg_atoi(mptr->value, 4, 1); else if (pg_strcasecmp(defel->defname, "StartSel") == 0)
else if (pg_strcasecmp(mptr->key, "MinWords") == 0) prs->startsel = pstrdup(val);
min_words = pg_atoi(mptr->value, 4, 1); else if (pg_strcasecmp(defel->defname, "StopSel") == 0)
else if (pg_strcasecmp(mptr->key, "ShortWord") == 0) prs->stopsel = pstrdup(val);
shortword = pg_atoi(mptr->value, 4, 1); else if (pg_strcasecmp(defel->defname, "HighlightAll") == 0)
else if (pg_strcasecmp(mptr->key, "StartSel") == 0) highlight = (pg_strcasecmp(val, "1") == 0 ||
prs->startsel = pstrdup(mptr->value); pg_strcasecmp(val, "on") == 0 ||
else if (pg_strcasecmp(mptr->key, "StopSel") == 0) pg_strcasecmp(val, "true") == 0 ||
prs->stopsel = pstrdup(mptr->value); pg_strcasecmp(val, "t") == 0 ||
else if (pg_strcasecmp(mptr->key, "HighlightAll") == 0) pg_strcasecmp(val, "y") == 0 ||
highlight = ( pg_strcasecmp(val, "yes") == 0);
pg_strcasecmp(mptr->value, "1") == 0 || else
pg_strcasecmp(mptr->value, "on") == 0 || ereport(ERROR,
pg_strcasecmp(mptr->value, "true") == 0 || (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
pg_strcasecmp(mptr->value, "t") == 0 || errmsg("unrecognized headline parameter: \"%s\"",
pg_strcasecmp(mptr->value, "y") == 0 || defel->defname)));
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")));
}
} }
if (highlight == 0) 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)) while (hlCover(prs, query, &p, &q))
{ {
/* find cover len in words */ /* find cover len in words */

View File

@ -20,7 +20,7 @@
* Copyright (c) 2006-2007, PostgreSQL Global Development Group * Copyright (c) 2006-2007, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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_parser.h"
#include "catalog/pg_ts_template.h" #include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "tsearch/ts_cache.h" #include "tsearch/ts_cache.h"
#include "tsearch/ts_utils.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/catcache.h" #include "utils/catcache.h"
@ -252,7 +252,7 @@ lookup_ts_dictionary_cache(Oid dictId)
tptmpl; tptmpl;
Form_pg_ts_dict dict; Form_pg_ts_dict dict;
Form_pg_ts_template template; Form_pg_ts_template template;
MemoryContext saveCtx = NULL; MemoryContext saveCtx;
tpdict = SearchSysCache(TSDICTOID, tpdict = SearchSysCache(TSDICTOID,
ObjectIdGetDatum(dictId), ObjectIdGetDatum(dictId),
@ -319,21 +319,30 @@ lookup_ts_dictionary_cache(Oid dictId)
if (OidIsValid(template->tmplinit)) if (OidIsValid(template->tmplinit))
{ {
bool isnull; List *dictoptions;
Datum opt; 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, opt = SysCacheGetAttr(TSDICTOID, tpdict,
Anum_pg_ts_dict_dictinitoption, Anum_pg_ts_dict_dictinitoption,
&isnull); &isnull);
if (isnull) if (isnull)
opt = PointerGetDatum(NULL); dictoptions = NIL;
else
dictoptions = deserialize_deflist(opt);
/* entry->dictData =
* Init method runs in dictionary's private memory context DatumGetPointer(OidFunctionCall1(template->tmplinit,
*/ PointerGetDatum(dictoptions)));
saveCtx = MemoryContextSwitchTo(entry->dictCtx);
entry->dictData = DatumGetPointer(OidFunctionCall1(template->tmplinit, opt)); MemoryContextSwitchTo(oldcontext);
MemoryContextSwitchTo(saveCtx);
} }
ReleaseSysCache(tptmpl); ReleaseSysCache(tptmpl);

View File

@ -12,7 +12,7 @@
* by PostgreSQL * by PostgreSQL
* *
* IDENTIFICATION * 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); PQclear(res);
/* the dictinitoption can be dumped straight into the command */
if (dictinfo->dictinitoption) if (dictinfo->dictinitoption)
{ appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
appendPQExpBuffer(q, ",\n OPTION = ");
appendStringLiteralConn(q, dictinfo->dictinitoption, g_conn);
}
appendPQExpBuffer(q, " );\n"); appendPQExpBuffer(q, " );\n");

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200708201 #define CATALOG_VERSION_NO 200708211
#endif #endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * 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(""); 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_ )); 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(""); 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(""); 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_ )); 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(""); 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_ )); 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"); 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(""); 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_ )); 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(""); 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(""); 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_ )); 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(""); 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(""); 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_ )); 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(""); 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(""); 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_ )); 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(""); DESCR("");

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId); 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 */ /* support routines in commands/define.c */
extern char *case_translate_language_name(const char *input); extern char *case_translate_language_name(const char *input);

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 1998-2007, PostgreSQL Global Development Group * 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 * 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, extern char *get_tsearch_config_filename(const char *basename,
const char *extension); const char *extension);