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 : }
|