From 48520622bf7f357cd5e61e732e400166a3d6d0b0 Mon Sep 17 00:00:00 2001
From: Arthur Zakirov <z-arthur@yandex.ru>
Date: Thu, 17 Jan 2019 15:38:09 +0300
Subject: [PATCH 3/4] Retrieve-shared-location-for-dict

Reviewed-by: Tomas Vondra, Ildus Kurbangaliev
---
 src/backend/postmaster/pgstat.c               |   3 +
 src/backend/tsearch/Makefile                  |   2 +-
 src/backend/tsearch/ts_shared.c               | 159 ++++++++++++++++++
 src/bin/initdb/initdb.c                       |   1 +
 src/bin/pg_rewind/filemap.c                   |   6 +
 .../pg_verify_checksums/pg_verify_checksums   | Bin 0 -> 337616 bytes
 src/include/pgstat.h                          |   1 +
 src/include/tsearch/ts_shared.h               |  27 +++
 8 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 src/backend/tsearch/ts_shared.c
 create mode 100755 src/bin/pg_verify_checksums/pg_verify_checksums
 create mode 100644 src/include/tsearch/ts_shared.h

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 0355fa65fb..fdcf15575a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3977,6 +3977,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_TIMELINE_HISTORY_WRITE:
 			event_name = "TimelineHistoryWrite";
 			break;
+		case WAIT_EVENT_TS_SHARED_DICT_WRITE:
+			event_name = "TSSharedDictWrite";
+			break;
 		case WAIT_EVENT_TWOPHASE_FILE_READ:
 			event_name = "TwophaseFileRead";
 			break;
diff --git a/src/backend/tsearch/Makefile b/src/backend/tsearch/Makefile
index 62d8bb3254..0b25c20fb0 100644
--- a/src/backend/tsearch/Makefile
+++ b/src/backend/tsearch/Makefile
@@ -26,7 +26,7 @@ DICTFILES_PATH=$(addprefix dicts/,$(DICTFILES))
 OBJS = ts_locale.o ts_parse.o wparser.o wparser_def.o dict.o \
 	dict_simple.o dict_synonym.o dict_thesaurus.o \
 	dict_ispell.o regis.o spell.o \
-	to_tsany.o ts_selfuncs.o ts_typanalyze.o ts_utils.o
+	to_tsany.o ts_selfuncs.o ts_shared.o ts_typanalyze.o ts_utils.o
 
 include $(top_srcdir)/src/backend/common.mk
 
diff --git a/src/backend/tsearch/ts_shared.c b/src/backend/tsearch/ts_shared.c
new file mode 100644
index 0000000000..c1249f9a75
--- /dev/null
+++ b/src/backend/tsearch/ts_shared.c
@@ -0,0 +1,159 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.c
+ *	  Text search shared dictionary management
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/tsearch/ts_shared.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unistd.h>
+#ifndef WIN32
+#include <sys/mman.h>
+#endif
+#include <sys/stat.h>
+
+#include "pgstat.h"
+#include "storage/fd.h"
+#include "tsearch/ts_shared.h"
+
+
+char *
+ts_dict_shared_init(DictInitData *init_data, ts_dict_build_callback allocate_cb)
+{
+	char	   *name;
+	int			flags;
+	int			fd;
+	void	   *dict;
+	Size		dict_size;
+
+	/*
+	 * Build the dictionary in backend's memory if dictid is invalid (it may
+	 * happen if the dicionary's init method was called within
+	 * verify_dictoptions()).
+	 */
+	if (!OidIsValid(init_data->dict.id))
+	{
+		dict = allocate_cb(init_data->dict_options, &dict_size);
+
+		return dict;
+	}
+
+	name = psprintf(PG_SHDICT_DIR "/%u", init_data->dict.id);
+
+	/* Try to create a new file */
+	flags = O_RDWR | O_CREAT | O_EXCL | PG_BINARY;
+	if ((fd = OpenTransientFile(name, flags)) == -1)
+	{
+		if (errno != EEXIST)
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not open shared dictionary file \"%s\": %m",
+							name)));
+		/* The file was created before */
+		return name;
+	}
+
+	/* Build the dictionary */
+	dict = allocate_cb(init_data->dict_options, &dict_size);
+
+	/* And write it to the shared file */
+	pgstat_report_wait_start(WAIT_EVENT_TS_SHARED_DICT_WRITE);
+	if (write(fd, dict, dict_size) != dict_size)
+	{
+		pgstat_report_wait_end();
+		/* if write didn't set errno, assume problem is no disk space */
+		if (errno == 0)
+			errno = ENOSPC;
+		CloseTransientFile(fd);
+
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write to shared dictionary file \"%s\": %m",
+						name)));
+	}
+	pgstat_report_wait_end();
+
+	pfree(dict);
+
+	if (CloseTransientFile(fd))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not close shared dictionary file \"%s\": %m",
+						name)));
+
+	return name;
+}
+
+void *
+ts_dict_shared_attach(const char *dict_name, Size *dict_size)
+{
+	int			flags;
+	int			fd;
+	void	   *address;
+	struct stat st;
+
+	/* Open an existing file for attach */
+	flags = O_RDONLY | PG_BINARY;
+	if ((fd = OpenTransientFile(dict_name, flags)) == -1)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open shared dictionary file \"%s\": %m",
+						dict_name)));
+
+	if (fstat(fd, &st) != 0)
+	{
+		int			save_errno;
+
+		save_errno = errno;
+		CloseTransientFile(fd);
+		errno = save_errno;
+
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not stat shared dictionary file \"%s\": %m",
+						dict_name)));
+	}
+	*dict_size = st.st_size;
+
+	/* Map the shared file. We need only read access */
+	address = mmap(NULL, *dict_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (address == MAP_FAILED)
+	{
+		int			save_errno;
+
+		save_errno = errno;
+		CloseTransientFile(fd);
+		errno = save_errno;
+
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not map shared dictionary file \"%s\": %m",
+						dict_name)));
+		return false;
+	}
+
+	if (CloseTransientFile(fd))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not close shared dictionary file \"%s\": %m",
+						dict_name)));
+
+	return address;
+}
+
+void
+ts_dict_shared_detach(const char *dict_name, void *dict_address, Size dict_size)
+{
+	if (munmap(dict_address, dict_size) != 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not unmap shared memory segment \"%s\": %m",
+						dict_name)));
+}
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 09b59c8324..29c7afbc27 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -207,6 +207,7 @@ static const char *const subdirs[] = {
 	"pg_dynshmem",
 	"pg_notify",
 	"pg_serial",
+	"pg_shdict",
 	"pg_snapshots",
 	"pg_subtrans",
 	"pg_twophase",
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index 63d0baee74..ff76086328 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -58,6 +58,12 @@ static const char *excludeDirContents[] =
 	 */
 	"pg_replslot",
 
+	/*
+	 * Skip compiled dictionaries files. A dictionary will be compiled on first
+	 * demand.
+	 */
+	"pg_shdict",
+
 	/* Contents removed on startup, see dsm_cleanup_for_mmap(). */
 	"pg_dynshmem",				/* defined as PG_DYNSHMEM_DIR */
 
diff --git a/src/bin/pg_verify_checksums/pg_verify_checksums b/src/bin/pg_verify_checksums/pg_verify_checksums
new file mode 100755
index 0000000000000000000000000000000000000000..06a474673896c8f16f239cad62250aea269ad7a0
GIT binary patch
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 5888242f75..42ad0a1889 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -927,6 +927,7 @@ typedef enum
 	WAIT_EVENT_TIMELINE_HISTORY_READ,
 	WAIT_EVENT_TIMELINE_HISTORY_SYNC,
 	WAIT_EVENT_TIMELINE_HISTORY_WRITE,
+	WAIT_EVENT_TS_SHARED_DICT_WRITE,
 	WAIT_EVENT_TWOPHASE_FILE_READ,
 	WAIT_EVENT_TWOPHASE_FILE_SYNC,
 	WAIT_EVENT_TWOPHASE_FILE_WRITE,
diff --git a/src/include/tsearch/ts_shared.h b/src/include/tsearch/ts_shared.h
new file mode 100644
index 0000000000..f8cff7d8ad
--- /dev/null
+++ b/src/include/tsearch/ts_shared.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * ts_shared.h
+ *	  Text search shared dictionary management
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ *
+ * src/include/tsearch/ts_shared.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TS_SHARED_H
+#define TS_SHARED_H
+
+#include "tsearch/ts_public.h"
+
+#define PG_SHDICT_DIR					"pg_shdict"
+
+typedef void *(*ts_dict_build_callback) (List *dictoptions, Size *size);
+
+extern char *ts_dict_shared_init(DictInitData *init_data,
+								 ts_dict_build_callback allocate_cb);
+extern void *ts_dict_shared_attach(const char *dict_name, Size *dict_size);
+extern void ts_dict_shared_detach(const char *dict_name, void *dict_address,
+								  Size dict_size);
+
+#endif							/* TS_SHARED_H */
-- 
2.21.0

