/*-------------------------------------------------------------------------
*
- * Copyright (c) 2006-2013, NTT DATA Corporation
+ * Copyright (c) 2016-2021, ludia_funcs Development Group
+ * Copyright (c) 2006-2015, NTT DATA Corporation
* All rights reserved.
*
* Changelog:
#include "utils/guc.h"
#include "miscadmin.h"
+#if PG_VERSION_NUM >= 90300
+#include "access/htup_details.h"
+#endif
+
PG_MODULE_MAGIC;
/* Last update date of ludia_funcs */
-#define PGS2_LAST_UPDATE "2013.04.05"
+#define PGS2_LAST_UPDATE "2019.10.04"
/* GUC variables */
#ifdef PGS2_DEBUG
-static bool pgs2_enable_debug = false;
-#endif
+typedef enum pgs2_enable_debug_type
+{
+ PGS2_ENABLE_DEBUG_OFF, /* logs no debug log */
+ PGS2_ENABLE_DEBUG_TERSE, /* logs tersely, e.g., just names of
+ functions */
+ PGS2_ENABLE_DEBUG_ON /* logs detailed infomation */
+} pgs2_enable_debug_type;
+
+/* We accept all the likely variants of "on" and "off" */
+static const struct config_enum_entry pgs2_enable_debug_options[] = {
+ {"off", PGS2_ENABLE_DEBUG_OFF, false},
+ {"terse", PGS2_ENABLE_DEBUG_TERSE, false},
+ {"on", PGS2_ENABLE_DEBUG_ON, false},
+ {"true", PGS2_ENABLE_DEBUG_ON, true},
+ {"false", PGS2_ENABLE_DEBUG_OFF, true},
+ {"yes", PGS2_ENABLE_DEBUG_ON, true},
+ {"no", PGS2_ENABLE_DEBUG_OFF, true},
+ {"1", PGS2_ENABLE_DEBUG_ON, true},
+ {"0", PGS2_ENABLE_DEBUG_OFF, true},
+ {NULL, 0, false}
+};
+
+static int pgs2_enable_debug = PGS2_ENABLE_DEBUG_OFF;
+#endif /* PGS2_DEBUG */
+
static char *pgs2_last_update = NULL;
static int norm_cache_limit = -1;
static bool escape_snippet_keyword = false;
#define ISSENNAOPSCHAR(x) (*(x) == '+' || *(x) == '-' || *(x) == ' ')
PG_FUNCTION_INFO_V1(pgs2snippet1);
-Datum pgs2snippet1(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pgs2norm);
-Datum pgs2norm(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pgs2textporter1);
-Datum pgs2textporter1(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pgs2seninfo);
+
+/*
+ * The function prototypes are created as a part of PG_FUNCTION_INFO_V1
+ * macro since 9.4, and hence the declaration of the function prototypes
+ * here is necessary only for 9.3 or before.
+ */
+#if PG_VERSION_NUM < 90400
+Datum pgs2snippet1(PG_FUNCTION_ARGS);
+Datum pgs2norm(PG_FUNCTION_ARGS);
+Datum pgs2textporter1(PG_FUNCTION_ARGS);
Datum pgs2seninfo(PG_FUNCTION_ARGS);
+#endif
static sen_encoding GetSennaEncoding(void);
static sen_query *GetSennaQuery(char *str, size_t len);
#ifdef TEXTPORTER
#define TEXTPORTER_TMPDIR "/tmp"
+#define TEXTPORTER_MKSTEMP_UMASK 0177
#define TEXTPORTER_GROUPNAME "UTF-8"
#define TEXTPORTER_DEFLANGNAME "Japanese"
#define TEXTPORTER_BBIGENDIAN 1
#define TEXTPORTER_SIZE 0
#define TEXTPORTER_CSV_C 0
+
/* GUC variables for pgs2textpoter1 */
static int textporter_error = ERROR;
static unsigned int textporter_option = TEXTPORTER_OPTION;
+static bool textporter_exit_on_segv = false;
/*
* This variable is a dummy that doesn't do anything, except in some
static bool check_textporter_option(char **newval, void **extra, GucSource source);
static void assign_textporter_option(const char *newval, void *extra);
+static void textporter_exit_on_segv_handler(SIGNAL_ARGS);
#endif /* TEXTPORTER */
void _PG_init(void);
#ifdef PGS2_DEBUG
/* Define custom GUC variable for debugging */
- DefineCustomBoolVariable("ludia_funcs.enable_debug",
+ DefineCustomEnumVariable("ludia_funcs.enable_debug",
"Emit ludia_funcs debugging output.",
NULL,
&pgs2_enable_debug,
- false,
+ PGS2_ENABLE_DEBUG_OFF,
+ pgs2_enable_debug_options,
PGC_USERSET,
0,
NULL,
assign_textporter_option,
NULL);
+ DefineCustomBoolVariable("ludia_funcs.textporter_exit_on_segv",
+ "Terminate session when textporter causes segmentation fault.",
+ NULL,
+ &textporter_exit_on_segv,
+ false,
+ PGC_USERSET,
+ 0,
+ NULL,
+ NULL,
+ NULL);
+
/* Clean up remaining textporter temporary files */
CleanupTextPorterTmpFiles();
#endif /* TEXTPORTER */
text *result = NULL;
struct stat statbuf;
bool return_null = false;
+ mode_t oumask;
/* Confirm that database encoding is UTF-8 */
GetSennaEncoding();
/*
* Generate a unique temporary filename where text data gotten
* from application file by TextPorter is stored temporarily.
+ * Set the permission of a temporary file to 0600 to ensure that
+ * only the owner of PostgreSQL server can read and write the file.
*/
+ oumask = umask(TEXTPORTER_MKSTEMP_UMASK);
tmpfd = mkstemp(txtfile);
+ umask(oumask);
+
if (tmpfd < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close temporary file \"%s\": %m", txtfile)));
/*
+ * If textporter_exit_on_segv option is enabled, segmentation fault
+ * caused by textporter will terminate only this connection and
+ * not lead to the server crash.
+ */
+ if (textporter_exit_on_segv)
+ pqsignal(SIGSEGV, textporter_exit_on_segv_handler);
+
+ /*
* Run TextPorter to read text data from application file (appfile)
* to temporary file (txtfile).
*/
TEXTPORTER_BBIGENDIAN, textporter_option,
TEXTPORTER_OPTION1, TEXTPORTER_SIZE,
TEXTPORTER_CSV_C);
+
+ if (textporter_exit_on_segv)
+ pqsignal(SIGSEGV, SIG_DFL);
+
if (ret != 0)
{
ereport(textporter_error,
textporter_option = *((unsigned int *) extra);
}
+static void
+textporter_exit_on_segv_handler(SIGNAL_ARGS)
+{
+ ereport(FATAL,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("terminating PostgreSQL server process due to "
+ "segmentation fault by textporter")));
+}
+
#else /* TEXTPORTER */
Datum
*slen = ep - *s;
#ifdef PGS2_DEBUG
- if (pgs2_enable_debug)
+ if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
{
char *tmp = pnstrdup(*s, *slen);
elog(LOG, "escaped snippet keyword: %s", tmp);
pfree(tmp);
}
+ else if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_TERSE)
+ elog(LOG, "escaped snippet keyword");
#endif
return true;
escape_snippet_keyword == guc_cache)
{
#ifdef PGS2_DEBUG
- if (pgs2_enable_debug)
+ if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
{
char *tmp = pnstrdup(str, len);
elog(LOG, "GetSennaQuery(): quick exit: %s", tmp);
pfree(tmp);
}
+ else if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_TERSE)
+ elog(LOG, "GetSennaQuery(): quick exit");
#endif
return query_cache;
}
pgs2malloc(void **buf, long *buflen, long needed, long maxlen)
{
#ifdef PGS2_DEBUG
- if (pgs2_enable_debug)
+ if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
elog(LOG, "pgs2malloc(): buflen %ld, needed %ld, maxlen %ld",
*buflen, needed, maxlen);
+ else if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_TERSE)
+ elog(LOG, "pgs2malloc()");
#endif
if (*buf != NULL && *buflen >= needed && (*buflen <= maxlen || maxlen == 0))
strncmp(norm_cache, s, slen) == 0)
{
#ifdef PGS2_DEBUG
- if (pgs2_enable_debug)
+ if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
{
char *tmp = text_to_cstring(str);
elog(LOG, "pgs2norm(): quick exit: %s", tmp);
pfree(tmp);
}
+ else if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_TERSE)
+ elog(LOG, "pgs2norm(): quick exit");
#endif
- PG_RETURN_TEXT_P(pnstrdup(norm_result, norm_reslen));
+ result = (text *) palloc(norm_reslen);
+ memcpy(result, norm_result, norm_reslen);
+ PG_RETURN_TEXT_P(result);
}
/* Confirm that database encoding is UTF-8 */
}
#ifdef PGS2_DEBUG
- if (pgs2_enable_debug)
+ if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
{
char *tmp = text_to_cstring(str);
(norm_cache == NULL) ? "unset" : "set", tmp);
pfree(tmp);
}
+ else if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_TERSE)
+ elog(LOG, "pgs2norm(): complete");
#endif
PG_RETURN_TEXT_P(result);
* Construct a tuple descriptor for the result row. This must
* match this function's ludia_funcs--x.x.sql entry.
*/
+#if PG_VERSION_NUM >= 120000
+ tupdesc = CreateTemplateTupleDesc(2);
+#else
tupdesc = CreateTemplateTupleDesc(2, false);
+#endif
TupleDescInitEntry(tupdesc, (AttrNumber) 1,
"version", TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2,