2020 * ===== shared segment init (postmaster startup) =====
2121 *
2222 * _PG_init
23- * -> ispell_shmem_startup (registered as a hook)
23+ * -> ispell_shmem_startup (registered as a hook)
2424 *
2525 * ===== dictionary init (backend) =====
2626 *
2727 * dispell_init
28- * -> init_shared_dict
29- * -> get_shared_dict
30- * -> NIStartBuild
31- * -> NIImportDictionary
32- * -> NIImportAffixes
33- * -> NISortDictionary
34- * -> NISortAffixes
35- * -> NIFinishBuild
36- * -> sizeIspellDict
37- * -> copyIspellDict
38- * -> copySPNode
39- * -> get_shared_stop_list
40- * -> readstoplist
41- * -> copyStopList
28+ * -> init_shared_dict
29+ * -> get_shared_dict
30+ * -> NIStartBuild
31+ * -> NIImportDictionary
32+ * -> NIImportAffixes
33+ * -> NISortDictionary
34+ * -> NISortAffixes
35+ * -> NIFinishBuild
36+ * -> sizeIspellDict
37+ * -> copyIspellDict
38+ * -> copySPNode
39+ * -> get_shared_stop_list
40+ * -> readstoplist
41+ * -> copyStopList
4242 *
4343 * ===== dictionary reinit after reset (backend) =====
4444 *
4545 * dispell_lexize
46- * -> timestamp of lookup < last reset
47- * -> init_shared_dict
48- * (see dispell_init above)
49- * -> SharedNINormalizeWord
46+ * -> timestamp of lookup < last reset
47+ * -> init_shared_dict
48+ * (see dispell_init above)
49+ * -> SharedNINormalizeWord
5050*/
5151
5252#include "postgres.h"
@@ -166,7 +166,7 @@ _PG_fini(void)
166166static void
167167ispell_shmem_startup ()
168168{
169- bool found = false ;
169+ bool found = FALSE ;
170170char * segment ;
171171
172172if (prev_shmem_startup_hook )
@@ -185,6 +185,12 @@ ispell_shmem_startup()
185185/* Was the shared memory segment already initialized? */
186186if (!found )
187187{
188+ if (segment == NULL ) {
189+ ereport (ERROR ,
190+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
191+ errmsg ("Cannot acquire %d kB of shared memory" ,
192+ max_ispell_mem_size_kb )));
193+ }
188194memset (segment ,0 ,max_ispell_mem_size ());
189195
190196#if PG_VERSION_NUM >=90600
@@ -288,13 +294,9 @@ static void
288294init_shared_dict (DictInfo * info ,char * dictFile ,char * affFile ,char * stopFile )
289295{
290296int size ;
291-
292297SharedIspellDict * shdict = NULL ;
293298SharedStopList * shstop = NULL ;
294299
295- IspellDict * dict ;
296- StopList stoplist ;
297-
298300/* DICTIONARY + AFFIXES */
299301
300302/* TODO This should probably check that the filenames are not NULL, and maybe that
@@ -313,6 +315,8 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile)
313315/* load the dictionary (word list) if not yet defined */
314316if (shdict == NULL )
315317{
318+ IspellDict * dict ;
319+
316320dict = (IspellDict * )palloc0 (sizeof (IspellDict ));
317321
318322NIStartBuild (dict );
@@ -383,6 +387,8 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile)
383387/* load the stopwords if not yet defined */
384388if (shstop == NULL )
385389{
390+ StopList stoplist ;
391+
386392readstoplist (stopFile ,& stoplist ,lowerstr );
387393
388394size = sizeStopList (& stoplist ,stopFile );
@@ -407,11 +413,14 @@ init_shared_dict(DictInfo *info, char *dictFile, char *affFile, char *stopFile)
407413info -> lookup = GetCurrentTimestamp ();
408414
409415memcpy (info -> dictFile ,dictFile ,strlen (dictFile )+ 1 );
410- memcpy (info -> affixFile ,dictFile ,strlen (affFile )+ 1 );
416+ memcpy (info -> affixFile ,affFile ,strlen (affFile )+ 1 );
411417if (stopFile != NULL )
412- memcpy (info -> stopFile ,dictFile ,strlen (stopFile )+ 1 );
418+ memcpy (info -> stopFile ,stopFile ,strlen (stopFile )+ 1 );
413419else
414420memset (info -> stopFile ,0 ,sizeof (info -> stopFile ));
421+
422+ /* save current context as long-lived */
423+ info -> saveCntx = CurrentMemoryContext ;
415424}
416425
417426Datum dispell_init (PG_FUNCTION_ARGS );
@@ -498,6 +507,9 @@ dispell_mem_used(PG_FUNCTION_ARGS)
498507 * The StopWords parameter is optional, the two other are required.
499508 *
500509 * If any of the filenames are incorrect, the call to init_shared_dict will fail.
510+ *
511+ * Do not call it directly - it saves current memory context as long-lived
512+ * context.
501513 */
502514Datum
503515dispell_init (PG_FUNCTION_ARGS )
@@ -586,7 +598,7 @@ dispell_lexize(PG_FUNCTION_ARGS)
586598char * txt ;
587599TSLexeme * res ;
588600TSLexeme * ptr ,
589- * cptr ;
601+ * cptr ;
590602
591603if (len <=0 )
592604PG_RETURN_POINTER (NULL );
@@ -599,11 +611,27 @@ dispell_lexize(PG_FUNCTION_ARGS)
599611/* do we need to reinit the dictionary? was the dict reset since the lookup */
600612if (timestamp_cmp_internal (info -> lookup ,segment_info -> lastReset )< 0 )
601613{
614+ DictInfo saveInfo = * info ;
615+ MemoryContext ctx ;
616+
602617/* relock in exclusive mode */
603618LWLockRelease (segment_info -> lock );
604619LWLockAcquire (segment_info -> lock ,LW_EXCLUSIVE );
605620
606- init_shared_dict (info ,info -> dictFile ,info -> affixFile ,info -> stopFile );
621+ /*
622+ * info is allocated in info->saveCntx, so that's why we use a copy of
623+ * info here
624+ */
625+
626+ MemoryContextResetAndDeleteChildren (saveInfo .saveCntx );
627+ ctx = MemoryContextSwitchTo (saveInfo .saveCntx );
628+
629+ info = palloc0 (sizeof (* info ));
630+
631+ init_shared_dict (info ,saveInfo .dictFile ,
632+ saveInfo .affixFile ,saveInfo .stopFile );
633+
634+ MemoryContextSwitchTo (ctx );
607635}
608636
609637res = NINormalizeWord (& (info -> dict ),txt );
@@ -697,13 +725,13 @@ copySPNode(SPNode *node)
697725SPNode * copy = NULL ;
698726
699727if (node == NULL )
700- return NULL ;
728+ return NULL ;
701729
702730copy = (SPNode * )shalloc (offsetof(SPNode ,data )+ sizeof (SPNodeData )* node -> length );
703731memcpy (copy ,node , offsetof(SPNode ,data )+ sizeof (SPNodeData )* node -> length );
704732
705733for (i = 0 ;i < node -> length ;i ++ )
706- copy -> data [i ].node = copySPNode (node -> data [i ].node );
734+ copy -> data [i ].node = copySPNode (node -> data [i ].node );
707735
708736return copy ;
709737}
@@ -715,7 +743,7 @@ sizeSPNode(SPNode *node)
715743int size = 0 ;
716744
717745if (node == NULL )
718- return 0 ;
746+ return 0 ;
719747
720748size = MAXALIGN (offsetof(SPNode ,data )+ sizeof (SPNodeData )* node -> length );
721749
@@ -815,7 +843,7 @@ sizeIspellDict(IspellDict *dict, char *dictFile, char *affixFile)
815843/* copy affix data */
816844size += MAXALIGN (sizeof (char * )* dict -> nAffixData );
817845for (i = 0 ;i < dict -> nAffixData ;i ++ )
818- size += MAXALIGN (sizeof (char )* strlen (dict -> AffixData [i ])+ 1 );
846+ size += MAXALIGN (sizeof (char )* strlen (dict -> AffixData [i ])+ 1 );
819847
820848return size ;
821849}
@@ -842,10 +870,10 @@ dispell_list_dicts(PG_FUNCTION_ARGS)
842870
843871/* Build a tuple descriptor for our result type */
844872if (get_call_result_type (fcinfo ,NULL ,& tupdesc )!= TYPEFUNC_COMPOSITE )
845- ereport (ERROR ,
846- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
847- errmsg ("function returning record called in context "
848- "that cannot accept type record" )));
873+ ereport (ERROR ,
874+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
875+ errmsg ("function returning record called in context "
876+ "that cannot accept type record" )));
849877
850878/*
851879 * generate attribute metadata needed later to produce tuples from raw