LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_exception.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 113 143 79.0 %
Date: 2021-09-14 22:17:06 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * (c) F. Groffen, M. Kersten
      11             :  * For documentation see website
      12             :  */
      13             : #include "monetdb_config.h"
      14             : #include "mal_exception.h"
      15             : #include "mal_private.h"
      16             : 
      17             : static char *exceptionNames[] = {
      18             : /* 0 */ "MALException",
      19             : /* 1 */ "IllegalArgumentException",
      20             : /* 2 */ "OutOfBoundsException",
      21             : /* 3 */ "IOException",
      22             : /* 4 */ "InvalidCredentialsException",
      23             : /* 5 */ "OptimizerException",
      24             : /* 6 */ "StackOverflowException",
      25             : /* 7 */ "SyntaxException",
      26             : /* 8 */ "TypeException",
      27             : /* 9 */ "LoaderException",
      28             : /*10 */ "ParseException",
      29             : /*11 */ "ArithmeticException",
      30             : /*12 */ "PermissionDeniedException",
      31             : /*13 */ "SQLException",
      32             : /*14 */ "RemoteException",
      33             : /*15 */ "Deprecated operation",
      34             : /*EOE*/ NULL
      35             : };
      36             : 
      37             : bool
      38       19123 : isExceptionVariable(const char *nme)
      39             : {
      40       19123 :         if (nme)
      41      325075 :                 for (int i = 0; exceptionNames[i]; i++)
      42      305953 :                         if (strcmp(exceptionNames[i], nme) == 0)
      43             :                                 return true;
      44             :         return false;
      45             : }
      46             : 
      47             : static char *M5OutOfMemory = MAL_MALLOC_FAIL;
      48             : 
      49             : char *
      50           0 : dupError(const char *err)
      51             : {
      52           0 :         char *msg = GDKstrdup(err);
      53             : 
      54           0 :         return msg ? msg : M5OutOfMemory;
      55             : }
      56             : 
      57             : char *
      58           0 : concatErrors(char *err1, const char *err2)
      59             : {
      60           0 :         size_t len = strlen(err1) + strlen(err2) + 1;
      61           0 :         char *new = GDKmalloc(len);
      62           0 :         if (new == NULL)
      63             :                 return err1;
      64           0 :         strconcat_len(new, len, err1, err2, NULL);
      65           0 :         freeException(err1);
      66           0 :         return new;
      67             : }
      68             : 
      69             : /**
      70             :  * Internal helper function for createException and
      71             :  * showException such that they share the same code, because reuse
      72             :  * is good.
      73             :  */
      74             : static str __attribute__((__format__(__printf__, 3, 0), __returns_nonnull__))
      75        6435 : createExceptionInternal(enum malexception type, const char *fcn, const char *format, va_list ap)
      76             : {
      77             :         size_t msglen;
      78             :         int len;
      79             :         char *msg;
      80             :         va_list ap2;
      81             : #ifndef NDEBUG
      82             :         // if there is an error we allow memory allocation once again
      83        6435 :         GDKsetmallocsuccesscount(-1);
      84             : #endif
      85        6435 :         va_copy(ap2, ap);                       /* we need to use it twice */
      86        6435 :         msglen = strlen(exceptionNames[type]) + strlen(fcn) + 2;
      87        6435 :         len = vsnprintf(NULL, 0, format, ap); /* count necessary length */
      88        6435 :         if (len < 0) {
      89           0 :                 TRC_CRITICAL(MAL_SERVER, "called with bad arguments");
      90             :                 len = 0;
      91             :         }
      92        6435 :         msg = GDKmalloc(msglen + len + 1);
      93        6435 :         if (msg != NULL) {
      94             :                 /* the calls below succeed: the arguments have already been checked */
      95        6435 :                 (void) strconcat_len(msg, msglen + 1,
      96        6435 :                                                          exceptionNames[type], ":", fcn, ":", NULL);
      97        6435 :                 if (len > 0)
      98        6435 :                         (void) vsnprintf(msg + msglen, len + 1, format, ap2);
      99        6435 :                 va_end(ap2);
     100             :                 char *q = msg;
     101        9447 :                 for (char *p = strchr(msg, '\n'); p; q = p + 1, p = strchr(q, '\n'))
     102        3012 :                         TRC_ERROR(MAL_SERVER, "%.*s\n", (int) (p - q), q);
     103        6435 :                 if (*q)
     104        3739 :                         TRC_ERROR(MAL_SERVER, "%s\n", q);
     105             :         } else {
     106           0 :                 msg = M5OutOfMemory;
     107             :         }
     108        6435 :         va_end(ap2);
     109             : 
     110        6435 :         assert(msg);
     111        6435 :         return msg;
     112             : }
     113             : 
     114             : /**
     115             :  * Returns an exception string for the given type of exception, function
     116             :  * and additional formatting parameters.  This function will crash the
     117             :  * system or return bogus when the malexception enum is not aligned with
     118             :  * the exceptionNames array.
     119             :  */
     120             : str
     121        6614 : createException(enum malexception type, const char *fcn, const char *format, ...)
     122             : {
     123             :         va_list ap;
     124        6614 :         str ret = NULL, localGDKerrbuf = GDKerrbuf;
     125             : 
     126        6615 :         if (localGDKerrbuf &&
     127        6603 :                 (ret = strstr(format, MAL_MALLOC_FAIL)) != NULL &&
     128           0 :                 ret[strlen(MAL_MALLOC_FAIL)] != ':' &&
     129           0 :                 (strncmp(localGDKerrbuf, "GDKmalloc", 9) == 0 ||
     130           0 :                  strncmp(localGDKerrbuf, "GDKrealloc", 10) == 0 ||
     131           0 :                  strncmp(localGDKerrbuf, "GDKzalloc", 9) == 0 ||
     132           0 :                  strncmp(localGDKerrbuf, "GDKstrdup", 9) == 0 ||
     133           0 :                  strncmp(localGDKerrbuf, "allocating too much virtual address space", 41) == 0)) {
     134             :                 /* override errors when the underlying error is memory
     135             :                  * exhaustion, but include whatever it is that the GDK level
     136             :                  * reported */
     137           0 :                 ret = createException(type, fcn, SQLSTATE(HY013) MAL_MALLOC_FAIL ": %s", localGDKerrbuf);
     138           0 :                 GDKclrerr();
     139             :                 assert(ret);
     140           0 :                 return ret;
     141             :         }
     142        6615 :         if (localGDKerrbuf && localGDKerrbuf[0] && strcmp(format, GDK_EXCEPTION) == 0) {
     143             :                 /* for GDK errors, report the underlying error */
     144             :                 char *p = localGDKerrbuf;
     145         180 :                 if (strncmp(p, GDKERROR, strlen(GDKERROR)) == 0) {
     146             :                         /* error is "!ERROR: function_name: STATE!error message"
     147             :                          * we need to skip everything up to the STATE */
     148         180 :                         p += strlen(GDKERROR);
     149         180 :                         char *q = strchr(p, ':');
     150         180 :                         if (q && q[1] == ' ' && strlen(q) > 8 && q[7] == '!')
     151          33 :                                 ret = createException(type, fcn, "%s", q + 2);
     152             :                 }
     153         180 :                 if (ret == NULL)
     154         147 :                         ret = createException(type, fcn, "GDK reported error: %s", p);
     155         180 :                 GDKclrerr();
     156         180 :                 assert(ret);
     157             :                 return ret;
     158             :         }
     159        6435 :         va_start(ap, format);
     160        6435 :         ret = createExceptionInternal(type, fcn, format, ap);
     161        6435 :         va_end(ap);
     162        6435 :         GDKclrerr();
     163             : 
     164             :         assert(ret);
     165             :         return ret;
     166             : }
     167             : 
     168             : void
     169    68690022 : freeException(str msg)
     170             : {
     171    68690022 :         if (msg != MAL_SUCCEED && msg != M5OutOfMemory)
     172        6539 :                 GDKfree(msg);
     173    68690023 : }
     174             : 
     175             : /**
     176             :  * Internal helper function for createMalException and
     177             :  * showScriptException such that they share the same code, because reuse
     178             :  * is good.
     179             :  */
     180             : static str __attribute__((__format__(__printf__, 5, 0), __returns_nonnull__))
     181          45 : createMalExceptionInternal(MalBlkPtr mb, int pc, enum malexception type, char *prev, const char *format, va_list ap)
     182             : {
     183             :         bool addnl = false;
     184          45 :         const char *s = mb && getInstrPtr(mb,0) ? getModName(mb) : "unknown";
     185          45 :         const char *fcn = mb && getInstrPtr(mb,0) ? getFcnName(mb) : "unknown";
     186             :         size_t msglen;
     187             : 
     188          45 :         if (prev) {
     189           6 :                 msglen = strlen(prev);
     190           6 :                 if (msglen > 0 && prev[msglen - 1] != '\n') {
     191             :                         addnl = true;
     192           6 :                         msglen++;
     193             :                 }
     194           6 :                 msglen += snprintf(NULL, 0, "!%s:%s.%s[%d]:",
     195             :                                                    exceptionNames[type], s, fcn, pc);
     196          39 :         } else if (type == SYNTAX) {
     197           0 :                 msglen = strlen(exceptionNames[type]) + 1;
     198             :         } else {
     199          39 :                 msglen = snprintf(NULL, 0, "%s:%s.%s[%d]:",
     200             :                                                   exceptionNames[type], s, fcn, pc);
     201             :         }
     202             :         va_list ap2;
     203          45 :         va_copy(ap2, ap);
     204          45 :         int len = vsnprintf(NULL, 0, format, ap);
     205             :         if (len < 0)
     206             :                 len = 0;
     207          45 :         char *msg = GDKmalloc(msglen + len + 1);
     208          45 :         if (msg != NULL) {
     209             :                 /* the calls below succeed: the arguments have already been checked */
     210          45 :                 if (prev) {
     211           6 :                         (void) snprintf(msg, msglen + 1, "%s%s!%s:%s.%s[%d]:",
     212             :                                                         prev, addnl ? "\n" : "",
     213             :                                                         exceptionNames[type], s, fcn, pc);
     214          39 :                 } else if (type == SYNTAX) {
     215           0 :                         (void) strconcat_len(msg, msglen + 1,
     216           0 :                                                                  exceptionNames[type], ":", NULL);
     217             :                 } else {
     218          39 :                         (void) snprintf(msg, msglen + 1, "%s:%s.%s[%d]:",
     219             :                                                         exceptionNames[type], s, fcn, pc);
     220             :                 }
     221          45 :                 if (len > 0)
     222          45 :                         (void) vsnprintf(msg + msglen, len + 1, format, ap2);
     223             :         } else {
     224           0 :                 msg = M5OutOfMemory;
     225             :         }
     226          45 :         va_end(ap2);
     227          45 :         freeException(prev);
     228          45 :         return msg;
     229             : }
     230             : 
     231             : /**
     232             :  * Returns an exception string for the MAL instructions.  These
     233             :  * exceptions are newline terminated, and determine module and function
     234             :  * from the given MalBlkPtr.  An old exception can be given, such that
     235             :  * this exception is chained to the previous one.  Conceptually this
     236             :  * creates a "stack" of exceptions.
     237             :  * This function will crash the system or return bogus when the
     238             :  * malexception enum is not aligned with the exceptionNames array.
     239             :  */
     240             : str
     241          45 : createMalException(MalBlkPtr mb, int pc, enum malexception type, const char *format, ...)
     242             : {
     243             :         va_list ap;
     244             :         str ret;
     245             : 
     246          45 :         va_start(ap, format);
     247          45 :         ret = createMalExceptionInternal(mb, pc, type, mb->errors, format, ap);
     248          45 :         va_end(ap);
     249             : 
     250          45 :         return(ret);
     251             : }
     252             : 
     253             : /**
     254             :  * Returns the malexception number for the given exception string.  If no
     255             :  * exception could be found in the string, MAL is returned indicating a
     256             :  * generic MALException.
     257             :  */
     258             : enum malexception
     259          10 : getExceptionType(const char *exception)
     260             : {
     261             :         enum malexception ret = MAL;
     262             :         const char *s;
     263             :         size_t len;
     264             :         enum malexception i;
     265             : 
     266          10 :         if ((s = strchr(exception, ':')) != NULL)
     267          10 :                 len = s - exception;
     268             :         else
     269           0 :                 len = strlen(exception);
     270             : 
     271          75 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     272          75 :                 if (strncmp(exceptionNames[i], exception, len) == 0 &&
     273          10 :                         exceptionNames[i][len] == '\0') {
     274             :                         ret = i;
     275             :                         break;
     276             :                 }
     277             :         }
     278             : 
     279          10 :         return(ret);
     280             : }
     281             : 
     282             : /**
     283             :  * Returns the location the exception was raised, if known.  It
     284             :  * depends on how the exception was created, what the location looks
     285             :  * like.  The returned string is mallocced with GDKmalloc, and hence
     286             :  * needs to be GDKfreed.
     287             :  */
     288             : str
     289           4 : getExceptionPlace(const char *exception)
     290             : {
     291             :         str ret;
     292             :         const char *s, *t;
     293             :         enum malexception i;
     294             :         size_t l;
     295             : 
     296           4 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     297           4 :                 l = strlen(exceptionNames[i]);
     298           4 :                 if (strncmp(exceptionNames[i], exception, l) == 0 &&
     299           4 :                         exception[l] == ':') {
     300           4 :                         s = exception + l + 1;
     301           4 :                         if ((t = strchr(s, ':')) != NULL) {
     302           4 :                                 if ((ret = GDKmalloc(t - s + 1)) == NULL)
     303             :                                         return NULL;
     304           4 :                                 strcpy_len(ret, s, t - s + 1);
     305           4 :                                 return ret;
     306             :                         }
     307             :                         break;
     308             :                 }
     309             :         }
     310           0 :         return GDKstrdup("(unknown)");
     311             : }
     312             : 
     313             : /**
     314             :  * Returns the informational message of the exception given.
     315             :  */
     316             : str
     317        5800 : getExceptionMessageAndState(const char *exception)
     318             : {
     319             :         const char *s, *t;
     320             :         enum malexception i;
     321             :         size_t l;
     322             : 
     323       64532 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     324       64197 :                 l = strlen(exceptionNames[i]);
     325       64197 :                 if (strncmp(exceptionNames[i], exception, l) == 0 &&
     326        5465 :                         exception[l] == ':') {
     327        5465 :                         s = exception + l + 1;
     328        5465 :                         if ((t = strpbrk(s, ":\n")) != NULL && *t == ':')
     329        5465 :                                 return (str) (t + 1);
     330             :                         return (str) s;
     331             :                 }
     332             :         }
     333         335 :         if (strncmp(exception, "!ERROR: ", 8) == 0)
     334           0 :                 return (str) (exception + 8);
     335             :         return (str) exception;
     336             : }
     337             : 
     338             : str
     339          40 : getExceptionMessage(const char *exception)
     340             : {
     341          40 :         char *msg = getExceptionMessageAndState(exception);
     342             : 
     343          40 :         if (strlen(msg) > 6 && msg[5] == '!' &&
     344          13 :                 (isdigit((unsigned char) msg[0]) ||
     345           1 :              (msg[0] >= 'A' && msg[0] <= 'Z')) &&
     346          13 :             (isdigit((unsigned char) msg[1]) ||
     347           1 :              (msg[1] >= 'A' && msg[1] <= 'Z')) &&
     348          13 :             (isdigit((unsigned char) msg[2]) ||
     349           2 :              (msg[2] >= 'A' && msg[2] <= 'Z')) &&
     350          13 :             (isdigit((unsigned char) msg[3]) ||
     351           0 :              (msg[3] >= 'A' && msg[3] <= 'Z')) &&
     352          13 :             (isdigit((unsigned char) msg[4]) ||
     353           0 :              (msg[4] >= 'A' && msg[4] <= 'Z')))
     354          13 :                 msg += 6;
     355          40 :         return msg;
     356             : }

Generated by: LCOV version 1.14