diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml index 02e8e8aa90..65c2d6daa3 100644 --- a/doc/src/sgml/textsearch.sgml +++ b/doc/src/sgml/textsearch.sgml @@ -2829,6 +2829,7 @@ iconv -f ISO_8859-1 -t UTF-8 -o nn_no.dict nn_NO.dic CREATE TEXT SEARCH DICTIONARY english_hunspell ( TEMPLATE = ispell, + Shareable = false, DictFile = en_us, AffFile = en_us, Stopwords = english); @@ -2843,6 +2844,9 @@ CREATE TEXT SEARCH DICTIONARY english_hunspell ( The stop-words file has the same format explained above for the simple dictionary type. The format of the other files is not specified here but is available from the above-mentioned web sites. + Shareable controls loading into shared memory. By + default it is true (see more in + ). @@ -3040,6 +3044,8 @@ CREATE TEXT SEARCH DICTIONARY english_stem ( both CPU and time-consuming. Instead of doing this in each backend when it needs a dictionary for the first time, the compiled dictionary may be stored in shared memory so that it may be reused by other backends. + Currently only Ispell supports loading into + shared memory. diff --git a/src/backend/tsearch/dict_ispell.c b/src/backend/tsearch/dict_ispell.c index 6294a52af3..1475cdb908 100644 --- a/src/backend/tsearch/dict_ispell.c +++ b/src/backend/tsearch/dict_ispell.c @@ -38,7 +38,8 @@ typedef struct } DictISpell; static void parse_dictoptions(List *dictoptions, - char **dictfile, char **afffile, char **stopfile); + char **dictfile, char **afffile, char **stopfile, + bool *isshared); static void *dispell_build(List *dictoptions, Size *size); Datum @@ -48,15 +49,21 @@ dispell_init(PG_FUNCTION_ARGS) DictISpell *d; void *dict_location; char *stopfile; + bool isshared; d = (DictISpell *) palloc0(sizeof(DictISpell)); - parse_dictoptions(init_data->dictoptions, NULL, NULL, &stopfile); + parse_dictoptions(init_data->dictoptions, NULL, NULL, &stopfile, &isshared); + /* Make stop word list */ if (stopfile) readstoplist(stopfile, &(d->stoplist), lowerstr); - dict_location = ts_dict_shmem_location(init_data, dispell_build); + /* Make or get from shared memory dictionary itself */ + if (isshared) + dict_location = ts_dict_shmem_location(init_data, dispell_build); + else + dict_location = dispell_build(init_data->dictoptions, NULL); Assert(dict_location); d->obj.dict = (IspellDictData *) dict_location; @@ -110,9 +117,10 @@ dispell_lexize(PG_FUNCTION_ARGS) static void parse_dictoptions(List *dictoptions, char **dictfile, char **afffile, - char **stopfile) + char **stopfile, bool *isshared) { ListCell *l; + bool isshared_defined = false; if (dictfile) *dictfile = NULL; @@ -120,6 +128,8 @@ parse_dictoptions(List *dictoptions, char **dictfile, char **afffile, *afffile = NULL; if (stopfile) *stopfile = NULL; + if (isshared) + *isshared = true; foreach(l, dictoptions) { @@ -158,6 +168,19 @@ parse_dictoptions(List *dictoptions, char **dictfile, char **afffile, errmsg("multiple StopWords parameters"))); *stopfile = defGetString(defel); } + else if (pg_strcasecmp(defel->defname, "Shareable") == 0) + { + if (!isshared) + continue; + + if (isshared_defined) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("multiple Shareable parameters"))); + + *isshared = defGetBoolean(defel); + isshared_defined = true; + } else { ereport(ERROR, @@ -180,7 +203,7 @@ dispell_build(List *dictoptions, Size *size) char *dictfile, *afffile; - parse_dictoptions(dictoptions, &dictfile, &afffile, NULL); + parse_dictoptions(dictoptions, &dictfile, &afffile, NULL, NULL); if (!afffile) { @@ -212,6 +235,7 @@ dispell_build(List *dictoptions, Size *size) NIFinishBuild(&build); /* Return the buffer and its size */ - *size = build.dict_size; + if (size) + *size = build.dict_size; return build.dict; } diff --git a/src/test/regress/expected/tsdicts.out b/src/test/regress/expected/tsdicts.out index 0c1d7c7675..71a43b74e8 100644 --- a/src/test/regress/expected/tsdicts.out +++ b/src/test/regress/expected/tsdicts.out @@ -194,6 +194,7 @@ SELECT ts_lexize('hunspell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG long parameter CREATE TEXT SEARCH DICTIONARY hunspell_long ( Template=ispell, + Shareable=false, DictFile=hunspell_sample_long, AffFile=hunspell_sample_long ); @@ -290,6 +291,7 @@ SELECT ts_lexize('hunspell_long', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG num parameter CREATE TEXT SEARCH DICTIONARY hunspell_num ( Template=ispell, + Shareable=false, DictFile=hunspell_sample_num, AffFile=hunspell_sample_num ); @@ -588,3 +590,64 @@ CREATE TEXT SEARCH DICTIONARY tsdict_case "AffFile" = ispell_sample ); ERROR: unrecognized Ispell parameter: "DictFile" +-- Test shared dictionaries +CREATE TEXT SEARCH DICTIONARY shared_ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +SHOW max_shared_dictionaries_size; + max_shared_dictionaries_size +------------------------------ + 100MB +(1 row) + +-- Make sure that dictionaries in shared memory +SELECT ts_lexize('ispell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + +SELECT ts_lexize('hunspell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + +SELECT ts_lexize('shared_ispell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + +SELECT schemaname, dictname FROM pg_ts_shared_dictionaries; + schemaname | dictname +------------+--------------- + public | ispell + public | hunspell + public | shared_ispell +(3 rows) + +-- shared_ispell space should be released in shared memory +DROP TEXT SEARCH DICTIONARY shared_ispell; +-- Make sure that dictionaries in shared memory, DROP invalidates cache +SELECT ts_lexize('ispell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + +SELECT ts_lexize('hunspell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + +SELECT schemaname, dictname FROM pg_ts_shared_dictionaries; + schemaname | dictname +------------+---------- + public | ispell + public | hunspell +(2 rows) + diff --git a/src/test/regress/sql/tsdicts.sql b/src/test/regress/sql/tsdicts.sql index 1633c0d066..d6e69d5511 100644 --- a/src/test/regress/sql/tsdicts.sql +++ b/src/test/regress/sql/tsdicts.sql @@ -51,6 +51,7 @@ SELECT ts_lexize('hunspell', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG long parameter CREATE TEXT SEARCH DICTIONARY hunspell_long ( Template=ispell, + Shareable=false, DictFile=hunspell_sample_long, AffFile=hunspell_sample_long ); @@ -75,6 +76,7 @@ SELECT ts_lexize('hunspell_long', 'footballyklubber'); -- Test ISpell dictionary with hunspell affix file with FLAG num parameter CREATE TEXT SEARCH DICTIONARY hunspell_num ( Template=ispell, + Shareable=false, DictFile=hunspell_sample_num, AffFile=hunspell_sample_num ); @@ -196,3 +198,28 @@ CREATE TEXT SEARCH DICTIONARY tsdict_case "DictFile" = ispell_sample, "AffFile" = ispell_sample ); + +-- Test shared dictionaries +CREATE TEXT SEARCH DICTIONARY shared_ispell ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); + +SHOW max_shared_dictionaries_size; + +-- Make sure that dictionaries in shared memory +SELECT ts_lexize('ispell', 'skies'); +SELECT ts_lexize('hunspell', 'skies'); +SELECT ts_lexize('shared_ispell', 'skies'); + +SELECT schemaname, dictname FROM pg_ts_shared_dictionaries; + +-- shared_ispell space should be released in shared memory +DROP TEXT SEARCH DICTIONARY shared_ispell; + +-- Make sure that dictionaries in shared memory, DROP invalidates cache +SELECT ts_lexize('ispell', 'skies'); +SELECT ts_lexize('hunspell', 'skies'); + +SELECT schemaname, dictname FROM pg_ts_shared_dictionaries;