LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_exception.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 136 177 76.8 %
Date: 2024-04-26 00:35:57 Functions: 9 12 75.0 %

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

Generated by: LCOV version 1.14