OSDN Git Service

Update copyright for 2021.
[ludiafuncs/ludia_funcs.git] / ludia_funcs.c
index a5de5a5..d1396e1 100644 (file)
@@ -1,6 +1,7 @@
 /*-------------------------------------------------------------------------
  *
- * 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;
@@ -57,13 +85,21 @@ 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);
@@ -71,6 +107,7 @@ static bool                  EscapeSnippetKeyword(char **s, size_t *slen);
 
 #ifdef TEXTPORTER
 #define TEXTPORTER_TMPDIR                      "/tmp"
+#define TEXTPORTER_MKSTEMP_UMASK               0177
 #define TEXTPORTER_GROUPNAME           "UTF-8"
 #define TEXTPORTER_DEFLANGNAME         "Japanese"
 #define TEXTPORTER_BBIGENDIAN          1
@@ -80,9 +117,11 @@ static bool                 EscapeSnippetKeyword(char **s, size_t *slen);
 #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
@@ -105,6 +144,7 @@ static void CleanupTextPorterTmpFiles(void);
 
 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);
@@ -117,11 +157,12 @@ _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,
@@ -168,6 +209,17 @@ _PG_init(void)
                                                           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 */
@@ -235,6 +287,7 @@ pgs2textporter1(PG_FUNCTION_ARGS)
        text    *result = NULL;
        struct stat     statbuf;
        bool    return_null = false;
+       mode_t  oumask;
 
        /* Confirm that database encoding is UTF-8 */
        GetSennaEncoding();
@@ -244,8 +297,13 @@ pgs2textporter1(PG_FUNCTION_ARGS)
                /*
                 * 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(),
@@ -256,6 +314,14 @@ pgs2textporter1(PG_FUNCTION_ARGS)
                                         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).
                 */
@@ -266,6 +332,10 @@ pgs2textporter1(PG_FUNCTION_ARGS)
                                                         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,
@@ -388,6 +458,15 @@ assign_textporter_option(const char *newval, void *extra)
        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
@@ -605,13 +684,15 @@ EscapeSnippetKeyword(char **s, size_t *slen)
        *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;
@@ -641,13 +722,15 @@ GetSennaQuery(char *str, size_t len)
                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;
        }
@@ -788,9 +871,11 @@ static inline void
 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))
@@ -886,16 +971,20 @@ pgs2norm(PG_FUNCTION_ARGS)
                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 */
@@ -964,7 +1053,7 @@ retry:
        }
 
 #ifdef PGS2_DEBUG
-       if (pgs2_enable_debug)
+       if (pgs2_enable_debug == PGS2_ENABLE_DEBUG_ON)
        {
                char    *tmp = text_to_cstring(str);
 
@@ -972,6 +1061,8 @@ retry:
                         (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);
@@ -1001,7 +1092,11 @@ pgs2seninfo(PG_FUNCTION_ARGS)
         * 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,