LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_exception.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 135 176 76.7 %
Date: 2024-12-19 23:10:26 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        1379 : isExceptionVariable(const char *nme)
      43             : {
      44        1379 :         if (nme)
      45       23427 :                 for (int i = 0; exceptionNames[i]; i++)
      46       22049 :                         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             : __attribute__((__format__(__printf__, 3, 0), __returns_nonnull__))
      81             : static str
      82       39459 : createExceptionInternal(enum malexception type, const char *fcn,
      83             :                                                 const char *format, va_list ap)
      84             : {
      85       39459 :         size_t msglen;
      86       39459 :         int len;
      87       39459 :         char *msg;
      88       39459 :         va_list ap2;
      89             : 
      90       39459 :         va_copy(ap2, ap);                       /* we need to use it twice */
      91       39459 :         msglen = strlen(exceptionNames[type]) + strlen(fcn) + 2;
      92       39459 :         len = vsnprintf(NULL, 0, format, ap);   /* count necessary length */
      93       39459 :         if (len < 0) {
      94           0 :                 TRC_CRITICAL(MAL_SERVER, "called with bad arguments");
      95           0 :                 len = 0;
      96             :         }
      97       39459 :         msg = GDKmalloc(msglen + len + 2);
      98       39567 :         if (msg != NULL) {
      99             :                 /* the calls below succeed: the arguments have already been checked */
     100       39567 :                 (void) strconcat_len(msg, msglen + 1,
     101             :                                                          exceptionNames[type], ":", fcn, ":", NULL);
     102       39528 :                 if (len > 0)
     103       39563 :                         (void) vsnprintf(msg + msglen, len + 1, format, ap2);
     104       39528 :                 va_end(ap2);
     105       39528 :                 char *q = msg + strlen(msg);
     106       39528 :                 if (q[-1] != '\n') {
     107             :                         /* make sure message ends with newline, we already have the space */
     108       20943 :                         *q++ = '\n';
     109       20943 :                         *q = '\0';
     110             :                 }
     111       39528 :                 q = msg;
     112       79300 :                 for (char *p = strchr(msg, '\n'); p; q = p + 1, p = strchr(q, '\n'))
     113       39716 :                         TRC_ERROR(MAL_SERVER, "%.*s\n", (int) (p - q), q);
     114       39584 :                 if (*q)
     115           0 :                         TRC_ERROR(MAL_SERVER, "%s\n", q);
     116             :         } else {
     117           0 :                 msg = M5OutOfMemory;
     118             :         }
     119       39584 :         va_end(ap2);
     120             : 
     121       39584 :         assert(msg);
     122       39584 :         return msg;
     123             : }
     124             : 
     125             : /**
     126             :  * Returns an exception string for the given type of exception, function
     127             :  * and additional formatting parameters.  This function will crash the
     128             :  * system or return bogus when the malexception enum is not aligned with
     129             :  * the exceptionNames array.
     130             :  */
     131             : str
     132       39685 : createException(enum malexception type, const char *fcn, const char *format,
     133             :                                 ...)
     134             : {
     135       39685 :         va_list ap;
     136       39685 :         str ret = NULL, localGDKerrbuf = GDKerrbuf;
     137             : 
     138       39757 :         if (localGDKerrbuf &&
     139       39746 :                 (ret = strstr(format, MAL_MALLOC_FAIL)) != NULL &&
     140           2 :                 ret[strlen(MAL_MALLOC_FAIL)] != ':' &&
     141           2 :                 (strncmp(localGDKerrbuf, "GDKmalloc", 9) == 0 ||
     142           2 :                  strncmp(localGDKerrbuf, "GDKrealloc", 10) == 0 ||
     143           2 :                  strncmp(localGDKerrbuf, "GDKzalloc", 9) == 0 ||
     144           2 :                  strncmp(localGDKerrbuf, "GDKstrdup", 9) == 0 ||
     145           2 :                  strncmp(localGDKerrbuf, "allocating too much virtual address space",
     146             :                                  41) == 0)) {
     147             :                 /* override errors when the underlying error is memory
     148             :                  * exhaustion, but include whatever it is that the GDK level
     149             :                  * reported */
     150           0 :                 ret = createException(type, fcn, SQLSTATE(HY013) MAL_MALLOC_FAIL ": %s",
     151             :                                                           localGDKerrbuf);
     152           0 :                 GDKclrerr();
     153           0 :                 assert(ret);
     154           0 :                 return ret;
     155             :         }
     156       39746 :         if (localGDKerrbuf && localGDKerrbuf[0]
     157        1098 :                 && strcmp(format, GDK_EXCEPTION) == 0) {
     158             :                 /* for GDK errors, report the underlying error */
     159         225 :                 char *p = localGDKerrbuf;
     160         225 :                 if (strncmp(p, GDKERROR, strlen(GDKERROR)) == 0) {
     161             :                         /* error is "!ERROR: function_name: STATE!error message"
     162             :                          * we need to skip everything up to the STATE */
     163         225 :                         p += strlen(GDKERROR);
     164         225 :                         char *q = strchr(p, ':');
     165         225 :                         if (q && q[1] == ' ' && strlen(q) > 8 && q[7] == '!')
     166          35 :                                 ret = createException(type, fcn, "%s", q + 2);
     167             :                 }
     168         225 :                 if (ret == NULL)
     169         190 :                         ret = createException(type, fcn, "GDK reported%s: %s",
     170         190 :                                                                   strstr(p, EXITING_MSG) ? "" : " error", p);
     171         225 :                 GDKclrerr();
     172         225 :                 assert(ret);
     173         225 :                 return ret;
     174             :         }
     175       39532 :         va_start(ap, format);
     176       39532 :         ret = createExceptionInternal(type, fcn, format, ap);
     177       39582 :         va_end(ap);
     178       39582 :         GDKclrerr();
     179             : 
     180       39582 :         assert(ret);
     181             :         return ret;
     182             : }
     183             : 
     184             : void
     185    69548286 : freeException(str msg)
     186             : {
     187    69548286 :         if (msg != MAL_SUCCEED && msg != M5OutOfMemory)
     188       39624 :                 GDKfree(msg);
     189    69548320 : }
     190             : 
     191             : /**
     192             :  * Internal helper function for createMalException and
     193             :  * showScriptException such that they share the same code, because reuse
     194             :  * is good.
     195             :  */
     196             : __attribute__((__format__(__printf__, 5, 0), __returns_nonnull__))
     197             : static str
     198          47 : createMalExceptionInternal(MalBlkPtr mb, int pc, enum malexception type,
     199             :                                                    char *prev, const char *format, va_list ap)
     200             : {
     201          47 :         bool addnl = false;
     202          47 :         const char *s = mb && getInstrPtr(mb, 0) ? getModName(mb) : "unknown";
     203          47 :         const char *fcn = mb && getInstrPtr(mb, 0) ? getFcnName(mb) : "unknown";
     204          47 :         size_t msglen;
     205             : 
     206          47 :         if (prev) {
     207           4 :                 msglen = strlen(prev);
     208           4 :                 if (msglen > 0 && prev[msglen - 1] != '\n') {
     209           4 :                         addnl = true;
     210           4 :                         msglen++;
     211             :                 }
     212           4 :                 msglen += snprintf(NULL, 0, "!%s:%s.%s[%d]:",
     213             :                                                    exceptionNames[type], s, fcn, pc);
     214          43 :         } else if (type == SYNTAX) {
     215           0 :                 msglen = strlen(exceptionNames[type]) + 1;
     216             :         } else {
     217          43 :                 msglen = snprintf(NULL, 0, "%s:%s.%s[%d]:",
     218             :                                                   exceptionNames[type], s, fcn, pc);
     219             :         }
     220          47 :         va_list ap2;
     221          47 :         va_copy(ap2, ap);
     222          47 :         int len = vsnprintf(NULL, 0, format, ap);
     223          47 :         if (len < 0)
     224             :                 len = 0;
     225          47 :         char *msg = GDKmalloc(msglen + len + 1);
     226          47 :         if (msg != NULL) {
     227             :                 /* the calls below succeed: the arguments have already been checked */
     228          47 :                 if (prev) {
     229           4 :                         (void) snprintf(msg, msglen + 1, "%s%s!%s:%s.%s[%d]:",
     230             :                                                         prev, addnl ? "\n" : "",
     231             :                                                         exceptionNames[type], s, fcn, pc);
     232          43 :                 } else if (type == SYNTAX) {
     233           0 :                         (void) strconcat_len(msg, msglen + 1,
     234             :                                                                  exceptionNames[type], ":", NULL);
     235             :                 } else {
     236          43 :                         (void) snprintf(msg, msglen + 1, "%s:%s.%s[%d]:",
     237             :                                                         exceptionNames[type], s, fcn, pc);
     238             :                 }
     239          47 :                 if (len > 0)
     240          47 :                         (void) vsnprintf(msg + msglen, len + 1, format, ap2);
     241             :         } else {
     242           0 :                 msg = M5OutOfMemory;
     243             :         }
     244          47 :         va_end(ap2);
     245          47 :         freeException(prev);
     246          47 :         return msg;
     247             : }
     248             : 
     249             : /**
     250             :  * Returns an exception string for the MAL instructions.  These
     251             :  * exceptions are newline terminated, and determine module and function
     252             :  * from the given MalBlkPtr.  An old exception can be given, such that
     253             :  * this exception is chained to the previous one.  Conceptually this
     254             :  * creates a "stack" of exceptions.
     255             :  * This function will crash the system or return bogus when the
     256             :  * malexception enum is not aligned with the exceptionNames array.
     257             :  */
     258             : str
     259          47 : createMalException(MalBlkPtr mb, int pc, enum malexception type,
     260             :                                    const char *format, ...)
     261             : {
     262          47 :         va_list ap;
     263          47 :         str ret;
     264             : 
     265          47 :         va_start(ap, format);
     266          47 :         ret = createMalExceptionInternal(mb, pc, type, mb->errors, format, ap);
     267          47 :         va_end(ap);
     268             : 
     269          47 :         return (ret);
     270             : }
     271             : 
     272             : /**
     273             :  * Returns the malexception number for the given exception string.  If no
     274             :  * exception could be found in the string, MAL is returned indicating a
     275             :  * generic MALException.
     276             :  */
     277             : enum malexception
     278           6 : getExceptionType(const char *exception)
     279             : {
     280           6 :         enum malexception ret = MAL;
     281           6 :         const char *s;
     282           6 :         size_t len;
     283           6 :         enum malexception i;
     284             : 
     285           6 :         if ((s = strchr(exception, ':')) != NULL)
     286           6 :                 len = s - exception;
     287             :         else
     288           0 :                 len = strlen(exception);
     289             : 
     290          59 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     291          59 :                 if (strncmp(exceptionNames[i], exception, len) == 0 &&
     292           6 :                         exceptionNames[i][len] == '\0') {
     293             :                         ret = i;
     294             :                         break;
     295             :                 }
     296             :         }
     297             : 
     298           6 :         return (ret);
     299             : }
     300             : 
     301             : /**
     302             :  * Returns the location the exception was raised, if known.  It
     303             :  * depends on how the exception was created, what the location looks
     304             :  * like.  The returned string is mallocced with GDKmalloc, and hence
     305             :  * needs to be GDKfreed.
     306             :  */
     307             : str
     308           0 : getExceptionPlace(const char *exception)
     309             : {
     310           0 :         str ret;
     311           0 :         const char *s, *t;
     312           0 :         enum malexception i;
     313           0 :         size_t l;
     314             : 
     315           0 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     316           0 :                 l = strlen(exceptionNames[i]);
     317           0 :                 if (strncmp(exceptionNames[i], exception, l) == 0 &&
     318           0 :                         exception[l] == ':') {
     319           0 :                         s = exception + l + 1;
     320           0 :                         if ((t = strchr(s, ':')) != NULL) {
     321           0 :                                 if ((ret = GDKmalloc(t - s + 1)) == NULL)
     322             :                                         return NULL;
     323           0 :                                 strcpy_len(ret, s, t - s + 1);
     324           0 :                                 return ret;
     325             :                         }
     326             :                         break;
     327             :                 }
     328             :         }
     329           0 :         return GDKstrdup("(unknown)");
     330             : }
     331             : 
     332             : /**
     333             :  * Returns the informational message of the exception given.
     334             :  */
     335             : str
     336       38575 : getExceptionMessageAndState(const char *exception)
     337             : {
     338       38575 :         const char *s, *t;
     339       38575 :         enum malexception i;
     340       38575 :         size_t l;
     341             : 
     342      520389 :         for (i = MAL; exceptionNames[i] != NULL; i++) {
     343      520201 :                 l = strlen(exceptionNames[i]);
     344      520201 :                 if (strncmp(exceptionNames[i], exception, l) == 0 &&
     345       38392 :                         exception[l] == ':') {
     346       38387 :                         s = exception + l + 1;
     347       38387 :                         if ((t = strpbrk(s, ":\n")) != NULL && *t == ':')
     348       38390 :                                 return (str) (t + 1);
     349             :                         return (str) s;
     350             :                 }
     351             :         }
     352         188 :         if (strncmp(exception, "!ERROR: ", 8) == 0)
     353           0 :                 return (str) (exception + 8);
     354             :         return (str) exception;
     355             : }
     356             : 
     357             : str
     358          42 : getExceptionMessage(const char *exception)
     359             : {
     360          42 :         char *msg = getExceptionMessageAndState(exception);
     361             : 
     362          42 :         if (strlen(msg) > 6 && msg[5] == '!' &&
     363          13 :                 (isdigit((unsigned char) msg[0]) ||
     364           1 :                  (msg[0] >= 'A' && msg[0] <= 'Z')) &&
     365          13 :                 (isdigit((unsigned char) msg[1]) ||
     366           1 :                  (msg[1] >= 'A' && msg[1] <= 'Z')) &&
     367          13 :                 (isdigit((unsigned char) msg[2]) ||
     368           3 :                  (msg[2] >= 'A' && msg[2] <= 'Z')) &&
     369          13 :                 (isdigit((unsigned char) msg[3]) ||
     370           0 :                  (msg[3] >= 'A' && msg[3] <= 'Z')) &&
     371          13 :                 (isdigit((unsigned char) msg[4]) || (msg[4] >= 'A' && msg[4] <= 'Z')))
     372          13 :                 msg += 6;
     373          42 :         return msg;
     374             : }

Generated by: LCOV version 1.14