diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b87b714 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.so +/log/ +/results/ +/tmp_check/ diff --git a/LICENSE b/LICENSE index 417dcbb..54e49a5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,25 +1,19 @@ -Copyright 2012, Tomas Vondra (tv@fuzzy.cz). All rights reserved. +Copyright (c) 2016-2018, Postgres Professional +Portions Copyright 2012, Tomas Vondra (tv@fuzzy.cz). All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +IN NO EVENT SHALL POSTGRES PROFESSIONAL BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS +DOCUMENTATION, EVEN IF POSTGRES PROFESSIONAL HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY TOMAS VONDRA ''AS IS'' AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOMAS VONDRA OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the -authors and should not be interpreted as representing official policies, either expressed -or implied, of Tomas Vondra. \ No newline at end of file +POSTGRES PROFESSIONAL SPECIFICALLY DISCLAIMS ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND POSTGRES PROFESSIONAL HAS NO OBLIGATIONS TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/Makefile b/Makefile index cf91b65..15d3187 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ OBJS = src/shared_ispell.o EXTENSION = shared_ispell DATA = shared_ispell--1.1.0.sql -REGRESS = shared_ispell +REGRESS = security shared_ispell EXTRA_REGRESS_OPTS=--temp-config=$(top_srcdir)/$(subdir)/postgresql.conf @@ -21,4 +21,3 @@ include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif -installcheck:; diff --git a/README.md b/README.md index b6d359e..9f9b6d8 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,23 @@ dictionary, this may save you a lot of resources. Install ------- -Installing the extension is quite simple, especially if you're on 9.1. -In that case all you need to do is this: - $ make install +Before build and install `shared_ispell` you should ensure following: + +* PostgreSQL version is 9.6 or later. + +Installing the extension is quite simple. In that case all you need to do is this: + + $ git clone git@github.com:postgrespro/shared_ispell.git + $ cd shared_ispell + $ make USE_PGXS=1 + $ make USE_PGXS=1 install and then (after connecting to the database) db=# CREATE EXTENSION shared_ispell; -If you're on pre-9.1 version, you'll have to do the second part manually -by running the SQL script (shared_ispell--x.y.sql) in the database. If -needed, replace MODULE_PATHNAME by $libdir. +> **Important:** Don't forget to set the `PG_CONFIG` variable in case you want to test `shared_ispell` on a custom build of PostgreSQL. Read more [here](https://wiki.postgresql.org/wiki/Building_and_Installing_PostgreSQL_Extension_Modules). Config diff --git a/expected/security.out b/expected/security.out new file mode 100644 index 0000000..6f73aa1 --- /dev/null +++ b/expected/security.out @@ -0,0 +1,40 @@ +create type si_dicts_result as (dict_name VARCHAR, affix_name VARCHAR, words INT, affixes INT, bytes INT); +create function shared_ispell_dicts( OUT dict_name VARCHAR, OUT affix_name VARCHAR, OUT words INT, OUT affixes INT, OUT bytes INT) +returns SETOF record as $$ +declare + qString varchar(4000); + rec si_dicts_result; +begin + qString := 'select * from shared_ispell_dicts()'; + for rec in execute qString loop + return NEXT; + end loop; + return; +end +$$ language plpgsql; +create extension shared_ispell; +ERROR: function "shared_ispell_dicts" already exists with same argument types +drop extension if exists shared_ispell; +NOTICE: extension "shared_ispell" does not exist, skipping +drop type si_dicts_result; +drop function shared_ispell_dicts(); +create type si_stoplists_result as (stop_name VARCHAR, words INT, bytes INT); +create function shared_ispell_stoplists(OUT stop_name VARCHAR, OUT words INT, OUT bytes INT) +returns SETOF record as $$ +declare + rec si_stoplists_result; + qString varchar(4000); +begin + qString := 'select * from shared_ispell_stoplists()'; + for rec in execute qString loop + return NEXT; + end loop; + return; +end +$$ language plpgsql; +create extension shared_ispell; +ERROR: function "shared_ispell_stoplists" already exists with same argument types +drop extension if exists shared_ispell; +NOTICE: extension "shared_ispell" does not exist, skipping +drop type si_stoplists_result; +drop function shared_ispell_stoplists(); diff --git a/expected/shared_ispell.out b/expected/shared_ispell.out index 68e59c9..9998cb9 100644 --- a/expected/shared_ispell.out +++ b/expected/shared_ispell.out @@ -211,3 +211,9 @@ SELECT ts_lexize('shared_hunspell', 'skies'); {sky} (1 row) +SELECT ts_lexize('shared_hunspell', 'skies'); + ts_lexize +----------- + {sky} +(1 row) + diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..0f07821 --- /dev/null +++ b/meson.build @@ -0,0 +1,39 @@ +# Copyright (c) 2025, Postgres Professional + +# Does not support the PGXS infrastructure at this time. Please, compile as part +# of the contrib source tree. + +shared_ispell_sources = files( + 'src' / 'shared_ispell.c' +) + +if host_system == 'windows' + shared_ispell_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'shared_ispell', + '--FILEDESC', 'shared_ispell - provides a shared ispell dictionary, i.e. a dictionary that\'s stored in shared segment.',]) +endif + +shared_ispell = shared_module('shared_ispell', + shared_ispell_sources, + kwargs: contrib_mod_args, +) +contrib_targets += shared_ispell + +install_data( + 'shared_ispell.control', + 'shared_ispell--1.1.0.sql', + kwargs: contrib_data_args, +) + +tests += { + 'name': 'shared_ispell', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'security', + 'shared_ispell', + ], + 'regress_args': ['--temp-config', files('postgresql.conf')], + }, +} diff --git a/shared_ispell--1.1.0.sql b/shared_ispell--1.1.0.sql index 07c3ac3..7f638ab 100644 --- a/shared_ispell--1.1.0.sql +++ b/shared_ispell--1.1.0.sql @@ -1,34 +1,34 @@ -CREATE OR REPLACE FUNCTION shared_ispell_init(internal) +CREATE FUNCTION shared_ispell_init(internal) RETURNS internal AS 'MODULE_PATHNAME', 'dispell_init' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_lexize(internal,internal,internal,internal) +CREATE FUNCTION shared_ispell_lexize(internal,internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME', 'dispell_lexize' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_reset() +CREATE FUNCTION shared_ispell_reset() RETURNS void AS 'MODULE_PATHNAME', 'dispell_reset' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_mem_used() +CREATE FUNCTION shared_ispell_mem_used() RETURNS integer AS 'MODULE_PATHNAME', 'dispell_mem_used' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_mem_available() +CREATE FUNCTION shared_ispell_mem_available() RETURNS integer AS 'MODULE_PATHNAME', 'dispell_mem_available' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_dicts( OUT dict_name VARCHAR, OUT affix_name VARCHAR, OUT words INT, OUT affixes INT, OUT bytes INT) +CREATE FUNCTION shared_ispell_dicts( OUT dict_name VARCHAR, OUT affix_name VARCHAR, OUT words INT, OUT affixes INT, OUT bytes INT) RETURNS SETOF record AS 'MODULE_PATHNAME', 'dispell_list_dicts' LANGUAGE C IMMUTABLE; -CREATE OR REPLACE FUNCTION shared_ispell_stoplists( OUT stop_name VARCHAR, OUT words INT, OUT bytes INT) +CREATE FUNCTION shared_ispell_stoplists( OUT stop_name VARCHAR, OUT words INT, OUT bytes INT) RETURNS SETOF record AS 'MODULE_PATHNAME', 'dispell_list_stoplists' LANGUAGE C IMMUTABLE; diff --git a/sql/security.sql b/sql/security.sql new file mode 100644 index 0000000..33a09e1 --- /dev/null +++ b/sql/security.sql @@ -0,0 +1,43 @@ +create type si_dicts_result as (dict_name VARCHAR, affix_name VARCHAR, words INT, affixes INT, bytes INT); + +create function shared_ispell_dicts( OUT dict_name VARCHAR, OUT affix_name VARCHAR, OUT words INT, OUT affixes INT, OUT bytes INT) +returns SETOF record as $$ +declare + qString varchar(4000); + rec si_dicts_result; +begin + qString := 'select * from shared_ispell_dicts()'; + for rec in execute qString loop + return NEXT; + end loop; + return; +end +$$ language plpgsql; + +create extension shared_ispell; + +drop extension if exists shared_ispell; +drop type si_dicts_result; +drop function shared_ispell_dicts(); + +create type si_stoplists_result as (stop_name VARCHAR, words INT, bytes INT); + +create function shared_ispell_stoplists(OUT stop_name VARCHAR, OUT words INT, OUT bytes INT) +returns SETOF record as $$ +declare + rec si_stoplists_result; + qString varchar(4000); +begin + qString := 'select * from shared_ispell_stoplists()'; + for rec in execute qString loop + return NEXT; + end loop; + return; +end +$$ language plpgsql; + +create extension shared_ispell; + +drop extension if exists shared_ispell; +drop type si_stoplists_result; +drop function shared_ispell_stoplists(); diff --git a/sql/shared_ispell.sql b/sql/shared_ispell.sql index e791399..0a4af97 100644 --- a/sql/shared_ispell.sql +++ b/sql/shared_ispell.sql @@ -53,4 +53,5 @@ SELECT stop_name, words FROM shared_ispell_stoplists(); SELECT shared_ispell_reset(); SELECT ts_lexize('shared_ispell', 'skies'); -SELECT ts_lexize('shared_hunspell', 'skies'); \ No newline at end of file +SELECT ts_lexize('shared_hunspell', 'skies'); +SELECT ts_lexize('shared_hunspell', 'skies'); diff --git a/src/shared_ispell.c b/src/shared_ispell.c index 126dd72..37243e2 100644 --- a/src/shared_ispell.c +++ b/src/shared_ispell.c @@ -20,33 +20,33 @@ * ===== shared segment init (postmaster startup) ===== * * _PG_init - * -> ispell_shmem_startup (registered as a hook) + * -> ispell_shmem_startup (registered as a hook) * * ===== dictionary init (backend) ===== * * dispell_init - * -> init_shared_dict - * -> get_shared_dict - * -> NIStartBuild - * -> NIImportDictionary - * -> NIImportAffixes - * -> NISortDictionary - * -> NISortAffixes - * -> NIFinishBuild - * -> sizeIspellDict - * -> copyIspellDict - * -> copySPNode - * -> get_shared_stop_list - * -> readstoplist - * -> copyStopList + * -> init_shared_dict + * -> get_shared_dict + * -> NIStartBuild + * -> NIImportDictionary + * -> NIImportAffixes + * -> NISortDictionary + * -> NISortAffixes + * -> NIFinishBuild + * -> sizeIspellDict + * -> copyIspellDict + * -> copySPNode + * -> get_shared_stop_list + * -> readstoplist + * -> copyStopList * * ===== dictionary reinit after reset (backend) ===== * * dispell_lexize - * -> timestamp of lookup < last reset - * -> init_shared_dict - * (see dispell_init above) - * -> SharedNINormalizeWord + * -> timestamp of lookup < last reset + * -> init_shared_dict + * (see dispell_init above) + * -> SharedNINormalizeWord */ #include "postgres.h" @@ -59,6 +59,7 @@ #include "access/htup_details.h" #include "funcapi.h" #include "utils/builtins.h" +#include "utils/guc.h" #include "shared_ispell.h" #include "tsearch/dicts/spell.h" @@ -66,12 +67,11 @@ PG_MODULE_MAGIC; void _PG_init(void); -void _PG_fini(void); /* Memory for dictionaries in kbytes */ static int max_ispell_mem_size_kb; -/* Saved hook values in case of unload */ +/* Saved hook value for proper chaining */ static shmem_startup_hook_type prev_shmem_startup_hook = NULL; /* These are used to allocate data within shared segment */ @@ -94,9 +94,14 @@ static int sizeStopList(StopList *list, char *stopFile); static Size max_ispell_mem_size() { - return (Size)max_ispell_mem_size_kb * 1024L; + return (Size) max_ispell_mem_size_kb * 1024L; } +#if (PG_VERSION_NUM >= 150000) +static shmem_request_hook_type prev_shmem_request_hook = NULL; +static void shared_ispell_shmem_request(void); +#endif + /* * Module load callback */ @@ -127,34 +132,36 @@ _PG_init(void) EmitWarningsOnPlaceholders("shared_ispell"); - /* - * Request additional shared resources. (These are no-ops if we're not in - * the postmaster process.) We'll allocate or attach to the shared - * resources in ispell_shmem_startup(). - */ +#if PG_VERSION_NUM >= 150000 + prev_shmem_request_hook = shmem_request_hook; + shmem_request_hook = shared_ispell_shmem_request; +#else RequestAddinShmemSpace(max_ispell_mem_size()); - #if PG_VERSION_NUM >= 90600 +#if PG_VERSION_NUM >= 90600 RequestNamedLWLockTranche("shared_ispell", 1); - #else +#else RequestAddinLWLocks(1); - #endif +#endif +#endif /* Install hooks. */ prev_shmem_startup_hook = shmem_startup_hook; shmem_startup_hook = ispell_shmem_startup; } - -/* - * Module unload callback - */ -void -_PG_fini(void) +#if PG_VERSION_NUM >= 150000 +static void +shared_ispell_shmem_request(void) { - /* Uninstall hooks. */ - shmem_startup_hook = prev_shmem_startup_hook; + if (prev_shmem_request_hook) + prev_shmem_request_hook(); + + RequestAddinShmemSpace(max_ispell_mem_size()); + + RequestNamedLWLockTranche("shared_ispell", 1); } +#endif /* * Probably the most important part of the startup - initializes the @@ -166,8 +173,8 @@ _PG_fini(void) static void ispell_shmem_startup() { - bool found = FALSE; - char *segment; + bool found = false; + char *segment; if (prev_shmem_startup_hook) prev_shmem_startup_hook(); @@ -177,9 +184,7 @@ ispell_shmem_startup() */ LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); - segment = ShmemInitStruct(SEGMENT_NAME, - max_ispell_mem_size(), - &found); + segment = ShmemInitStruct(SEGMENT_NAME, max_ispell_mem_size(), &found); segment_info = (SegmentInfo *) segment; /* Was the shared memory segment already initialized? */ @@ -187,16 +192,16 @@ ispell_shmem_startup() { memset(segment, 0, max_ispell_mem_size()); - #if PG_VERSION_NUM >= 90600 +#if PG_VERSION_NUM >= 90600 segment_info->lock = &(GetNamedLWLockTranche("shared_ispell"))->lock; - #else +#else segment_info->lock = LWLockAssign(); - #endif +#endif segment_info->firstfree = segment + MAXALIGN(sizeof(SegmentInfo)); - segment_info->available = max_ispell_mem_size() - - (int)(segment_info->firstfree - segment); + segment_info->available = max_ispell_mem_size() - + (int) (segment_info->firstfree - segment); - segment_info->lastReset = GetCurrentTimestamp(); + INSTR_TIME_SET_CURRENT(segment_info->lastReset); } LWLockRelease(AddinShmemInitLock); @@ -285,15 +290,15 @@ clean_dict_affix(IspellDict *dict) * of the shared memory (using SegmentInfo->lock). */ static void -init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile) +init_shared_dict(DictInfo *info, MemoryContext infoCntx, + char *dictFile, char *affFile, char *stopFile) { - int size; - + int size; SharedIspellDict *shdict = NULL; - SharedStopList *shstop = NULL; + SharedStopList *shstop = NULL; + MemoryContext oldctx; - IspellDict *dict; - StopList stoplist; + oldctx = MemoryContextSwitchTo(infoCntx); /* DICTIONARY + AFFIXES */ @@ -313,11 +318,14 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile) /* load the dictionary (word list) if not yet defined */ if (shdict == NULL) { + IspellDict *dict; + dict = (IspellDict *) palloc0(sizeof(IspellDict)); NIStartBuild(dict); NIImportDictionary(dict, get_tsearch_config_filename(dictFile, "dict")); + dict->flagMode = info->dict.flagMode; dict->usecompound = info->dict.usecompound; dict->nCompoundAffixFlag = dict->mCompoundAffixFlag = @@ -333,11 +341,13 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile) */ if (info->dict.useFlagAliases) { - int i; + int i; + dict->useFlagAliases = true; dict->lenAffixData = info->dict.lenAffixData; dict->nAffixData = info->dict.nAffixData; dict->AffixData = (char **) palloc0(dict->nAffixData * sizeof(char *)); + for (i = 0; i < dict->nAffixData; i++) { dict->AffixData[i] = palloc0(strlen(info->dict.AffixData[i]) + 1); @@ -383,6 +393,8 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile) /* load the stopwords if not yet defined */ if (shstop == NULL) { + StopList stoplist; + readstoplist(stopFile, &stoplist, lowerstr); size = sizeStopList(&stoplist, stopFile); @@ -404,23 +416,19 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile) info->shdict = shdict; info->shstop = shstop; - info->lookup = GetCurrentTimestamp(); + INSTR_TIME_SET_CURRENT(info->lookup); memcpy(info->dictFile, dictFile, strlen(dictFile) + 1); - memcpy(info->affixFile, dictFile, strlen(affFile)+ 1); + memcpy(info->affixFile, affFile, strlen(affFile) + 1); if (stopFile != NULL) - memcpy(info->stopFile, dictFile, strlen(stopFile) + 1); + memcpy(info->stopFile, stopFile, strlen(stopFile) + 1); else memset(info->stopFile, 0, sizeof(info->stopFile)); -} -Datum dispell_init(PG_FUNCTION_ARGS); -Datum dispell_lexize(PG_FUNCTION_ARGS); -Datum dispell_reset(PG_FUNCTION_ARGS); -Datum dispell_mem_available(PG_FUNCTION_ARGS); -Datum dispell_mem_used(PG_FUNCTION_ARGS); -Datum dispell_list_dicts(PG_FUNCTION_ARGS); -Datum dispell_list_stoplists(PG_FUNCTION_ARGS); + MemoryContextSwitchTo(oldctx); + /* save current context as long-lived */ + info->infoCntx = infoCntx; +} PG_FUNCTION_INFO_V1(dispell_init); PG_FUNCTION_INFO_V1(dispell_lexize); @@ -443,9 +451,10 @@ dispell_reset(PG_FUNCTION_ARGS) segment_info->shdict = NULL; segment_info->shstop = NULL; - segment_info->lastReset = GetCurrentTimestamp(); + INSTR_TIME_SET_CURRENT(segment_info->lastReset); segment_info->firstfree = ((char*) segment_info) + MAXALIGN(sizeof(SegmentInfo)); - segment_info->available = max_ispell_mem_size() - (int)(segment_info->firstfree - (char*) segment_info); + segment_info->available = max_ispell_mem_size() - + (int) (segment_info->firstfree - (char*) segment_info); memset(segment_info->firstfree, 0, segment_info->available); @@ -471,12 +480,14 @@ dispell_mem_available(PG_FUNCTION_ARGS) } /* - * Returns amount of 'occupied space' in the shared segment (used by current dictionaries). + * Returns amount of 'occupied space' in the shared segment (used by current + * dictionaries). */ Datum dispell_mem_used(PG_FUNCTION_ARGS) { - int result = 0; + int result = 0; + LWLockAcquire(segment_info->lock, LW_SHARED); result = max_ispell_mem_size() - segment_info->available; @@ -498,6 +509,9 @@ dispell_mem_used(PG_FUNCTION_ARGS) * The StopWords parameter is optional, the two other are required. * * If any of the filenames are incorrect, the call to init_shared_dict will fail. + * + * Do not call it directly - it saves current memory context as long-lived + * context. */ Datum dispell_init(PG_FUNCTION_ARGS) @@ -570,7 +584,15 @@ dispell_init(PG_FUNCTION_ARGS) /* search if the dictionary is already initialized */ LWLockAcquire(segment_info->lock, LW_EXCLUSIVE); - init_shared_dict(info, dictFile, affFile, stopFile); + /* + * Current context is a long lived context. Create child context to store + * DictInfo internal data. + */ + info->infoCntx = AllocSetContextCreate(CurrentMemoryContext, + "shared_ispell context", + ALLOCSET_DEFAULT_SIZES); + + init_shared_dict(info, info->infoCntx, dictFile, affFile, stopFile); LWLockRelease(segment_info->lock); @@ -586,7 +608,7 @@ dispell_lexize(PG_FUNCTION_ARGS) char *txt; TSLexeme *res; TSLexeme *ptr, - *cptr; + *cptr; if (len <= 0) PG_RETURN_POINTER(NULL); @@ -597,13 +619,25 @@ dispell_lexize(PG_FUNCTION_ARGS) LWLockAcquire(segment_info->lock, LW_SHARED); /* do we need to reinit the dictionary? was the dict reset since the lookup */ - if (timestamp_cmp_internal(info->lookup, segment_info->lastReset) < 0) + if (INSTR_TIME_GET_MICROSEC(info->lookup) < + INSTR_TIME_GET_MICROSEC(segment_info->lastReset)) { + DictInfo saveInfo = *info; + /* relock in exclusive mode */ LWLockRelease(segment_info->lock); LWLockAcquire(segment_info->lock, LW_EXCLUSIVE); - init_shared_dict(info, info->dictFile, info->affixFile, info->stopFile); + /* + * info is allocated in info->saveCntx, so that's why we use a copy of + * info here + */ + + MemoryContextReset(saveInfo.infoCntx); + MemSet(info, 0, sizeof(*info)); + + init_shared_dict(info, saveInfo.infoCntx, saveInfo.dictFile, + saveInfo.affixFile, saveInfo.stopFile); } res = NINormalizeWord(&(info->dict), txt); @@ -649,7 +683,8 @@ dispell_lexize(PG_FUNCTION_ARGS) static char * shalloc(int bytes) { - char *result; + char *result; + bytes = MAXALIGN(bytes); /* This shouldn't really happen, as the init_shared_dict checks the size @@ -676,8 +711,10 @@ shalloc(int bytes) static char * shstrcpy(char *str) { - char *tmp = shalloc(strlen(str) + 1); + char *tmp = shalloc(strlen(str) + 1); + memcpy(tmp, str, strlen(str) + 1); + return tmp; } @@ -693,17 +730,17 @@ shstrcpy(char *str) static SPNode * copySPNode(SPNode *node) { - int i; - SPNode *copy = NULL; + int i; + SPNode *copy = NULL; if (node == NULL) - return NULL; + return NULL; copy = (SPNode *) shalloc(offsetof(SPNode, data) + sizeof(SPNodeData) * node->length); memcpy(copy, node, offsetof(SPNode, data) + sizeof(SPNodeData) * node->length); for (i = 0; i < node->length; i++) - copy->data[i].node = copySPNode(node->data[i].node); + copy->data[i].node = copySPNode(node->data[i].node); return copy; } @@ -711,11 +748,11 @@ copySPNode(SPNode *node) static int sizeSPNode(SPNode *node) { - int i; - int size = 0; + int i; + int size = 0; if (node == NULL) - return 0; + return 0; size = MAXALIGN(offsetof(SPNode, data) + sizeof(SPNodeData) * node->length); @@ -771,8 +808,7 @@ sizeStopList(StopList *list, char *stopFile) static SharedIspellDict * copyIspellDict(IspellDict *dict, char *dictFile, char *affixFile, int size, int words) { - int i; - + int i; SharedIspellDict *copy = (SharedIspellDict *) shalloc(sizeof(SharedIspellDict)); copy->dictFile = shalloc(strlen(dictFile) + 1); @@ -789,6 +825,8 @@ copyIspellDict(IspellDict *dict, char *dictFile, char *affixFile, int size, int for (i = 0; i < copy->dict.nAffixData; i++) copy->dict.AffixData[i] = shstrcpy(dict->AffixData[i]); + copy->dict.flagMode = dict->flagMode; + copy->nbytes = size; copy->nwords = words; @@ -804,8 +842,8 @@ copyIspellDict(IspellDict *dict, char *dictFile, char *affixFile, int size, int static int sizeIspellDict(IspellDict *dict, char *dictFile, char *affixFile) { - int i; - int size = MAXALIGN(sizeof(SharedIspellDict)); + int i; + int size = MAXALIGN(sizeof(SharedIspellDict)); size += MAXALIGN(strlen(dictFile) + 1); size += MAXALIGN(strlen(affixFile) + 1); @@ -815,7 +853,7 @@ sizeIspellDict(IspellDict *dict, char *dictFile, char *affixFile) /* copy affix data */ size += MAXALIGN(sizeof(char *) * dict->nAffixData); for (i = 0; i < dict->nAffixData; i++) - size += MAXALIGN(sizeof(char) * strlen(dict->AffixData[i]) + 1); + size += MAXALIGN(sizeof(char) * strlen(dict->AffixData[i]) + 1); return size; } @@ -824,9 +862,9 @@ sizeIspellDict(IspellDict *dict, char *dictFile, char *affixFile) Datum dispell_list_dicts(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - TupleDesc tupdesc; - SharedIspellDict *dict; + FuncCallContext *funcctx; + TupleDesc tupdesc; + SharedIspellDict *dict; /* init on the first call */ if (SRF_IS_FIRSTCALL()) @@ -842,10 +880,10 @@ dispell_list_dicts(PG_FUNCTION_ARGS) /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function returning record called in context " - "that cannot accept type record"))); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); /* * generate attribute metadata needed later to produce tuples from raw diff --git a/src/shared_ispell.h b/src/shared_ispell.h index 92de330..cbba198 100644 --- a/src/shared_ispell.h +++ b/src/shared_ispell.h @@ -2,6 +2,7 @@ #define __SHARED_ISPELL_H__ #include "storage/lwlock.h" +#include "utils/memutils.h" #include "utils/timestamp.h" #include "tsearch/dicts/spell.h" #include "tsearch/ts_public.h" @@ -41,9 +42,9 @@ typedef struct SharedStopList typedef struct SegmentInfo { LWLockId lock; - char *firstfree; /* first free address (always maxaligned) */ - size_t available; /* free space remaining at firstfree */ - Timestamp lastReset; /* last reset of the dictionary */ + char *firstfree; /* first free address (always maxaligned) */ + size_t available; /* free space remaining at firstfree */ + instr_time lastReset; /* last reset of the dictionary */ /* the shared segment (info and data) */ SharedIspellDict *shdict; @@ -53,7 +54,7 @@ typedef struct SegmentInfo /* used to keep track of dictionary in each backend */ typedef struct DictInfo { - Timestamp lookup; + instr_time lookup; char dictFile[MAXLEN]; char affixFile[MAXLEN]; @@ -66,6 +67,9 @@ typedef struct DictInfo SharedIspellDict *shdict; IspellDict dict; SharedStopList *shstop; + + /* MemoryContext of dict local content */ + MemoryContext infoCntx; } DictInfo; -#endif \ No newline at end of file +#endif
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: