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