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

Generated by: LCOV version 1.14