mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-04 03:46:52 +02:00
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:
parent
fd33d90a23
commit
d321421d0a
@ -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>
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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,40 +714,66 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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] =
|
repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
|
||||||
DirectFunctionCall1(textin, CStringGetDatum(opt));
|
PointerGetDatum(serialize_deflist(dictoptions));
|
||||||
repl_null[Anum_pg_ts_dict_dictinitoption - 1] = ' ';
|
|
||||||
}
|
|
||||||
repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = 'r';
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
repl_null[Anum_pg_ts_dict_dictinitoption - 1] = 'n';
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = 'r';
|
||||||
errmsg("text search dictionary parameter \"%s\" not recognized",
|
|
||||||
defel->defname)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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';
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
{
|
||||||
|
if (stoploaded)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("multiple StopWords parameters")));
|
||||||
|
readstoplist(defGetString(defel), &d->stoplist);
|
||||||
sortstoplist(&d->stoplist);
|
sortstoplist(&d->stoplist);
|
||||||
pfree(filename);
|
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);
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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,46 +1683,39 @@ 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 (highlight == 0)
|
||||||
{
|
{
|
||||||
@ -1737,11 +1731,7 @@ prsd_headline(PG_FUNCTION_ARGS)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("ShortWord should be >= 0")));
|
errmsg("ShortWord should be >= 0")));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (highlight == 0)
|
|
||||||
{
|
|
||||||
while (hlCover(prs, query, &p, &q))
|
while (hlCover(prs, query, &p, &q))
|
||||||
{
|
{
|
||||||
/* find cover len in words */
|
/* find cover len in words */
|
||||||
|
31
src/backend/utils/cache/ts_cache.c
vendored
31
src/backend/utils/cache/ts_cache.c
vendored
@ -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);
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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("");
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user