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 27230 : isExceptionVariable(const char *nme)
43 : {
44 27230 : if (nme)
45 462894 : for (int i = 0; exceptionNames[i]; i++)
46 435665 : 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 39524 : createExceptionInternal(enum malexception type, const char *fcn,
82 : const char *format, va_list ap)
83 : {
84 39524 : size_t msglen;
85 39524 : int len;
86 39524 : char *msg;
87 39524 : va_list ap2;
88 : #ifndef NDEBUG
89 : // if there is an error we allow memory allocation once again
90 39524 : GDKsetmallocsuccesscount(-1);
91 : #endif
92 39515 : va_copy(ap2, ap); /* we need to use it twice */
93 39515 : msglen = strlen(exceptionNames[type]) + strlen(fcn) + 2;
94 39515 : len = vsnprintf(NULL, 0, format, ap); /* count necessary length */
95 39515 : if (len < 0) {
96 0 : TRC_CRITICAL(MAL_SERVER, "called with bad arguments");
97 0 : len = 0;
98 : }
99 39515 : msg = GDKmalloc(msglen + len + 2);
100 39520 : if (msg != NULL) {
101 : /* the calls below succeed: the arguments have already been checked */
102 39520 : (void) strconcat_len(msg, msglen + 1,
103 : exceptionNames[type], ":", fcn, ":", NULL);
104 39525 : if (len > 0)
105 39525 : (void) vsnprintf(msg + msglen, len + 1, format, ap2);
106 39525 : va_end(ap2);
107 39525 : char *q = msg + strlen(msg);
108 39525 : if (q[-1] != '\n') {
109 : /* make sure message ends with newline, we already have the space */
110 20888 : *q++ = '\n';
111 20888 : *q = '\0';
112 : }
113 39525 : q = msg;
114 79237 : for (char *p = strchr(msg, '\n'); p; q = p + 1, p = strchr(q, '\n'))
115 39712 : TRC_ERROR(MAL_SERVER, "%.*s\n", (int) (p - q), q);
116 39525 : if (*q)
117 0 : TRC_ERROR(MAL_SERVER, "%s\n", q);
118 : } else {
119 0 : msg = M5OutOfMemory;
120 : }
121 39525 : va_end(ap2);
122 :
123 39525 : assert(msg);
124 39525 : 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 39747 : createException(enum malexception type, const char *fcn, const char *format,
135 : ...)
136 : {
137 39747 : va_list ap;
138 39747 : str ret = NULL, localGDKerrbuf = GDKerrbuf;
139 :
140 39746 : if (localGDKerrbuf &&
141 39738 : (ret = strstr(format, MAL_MALLOC_FAIL)) != NULL &&
142 2 : ret[strlen(MAL_MALLOC_FAIL)] != ':' &&
143 2 : (strncmp(localGDKerrbuf, "GDKmalloc", 9) == 0 ||
144 2 : strncmp(localGDKerrbuf, "GDKrealloc", 10) == 0 ||
145 2 : strncmp(localGDKerrbuf, "GDKzalloc", 9) == 0 ||
146 2 : strncmp(localGDKerrbuf, "GDKstrdup", 9) == 0 ||
147 2 : 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 39738 : if (localGDKerrbuf && localGDKerrbuf[0]
159 1051 : && strcmp(format, GDK_EXCEPTION) == 0) {
160 : /* for GDK errors, report the underlying error */
161 222 : char *p = localGDKerrbuf;
162 222 : 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 222 : p += strlen(GDKERROR);
166 222 : char *q = strchr(p, ':');
167 222 : if (q && q[1] == ' ' && strlen(q) > 8 && q[7] == '!')
168 36 : ret = createException(type, fcn, "%s", q + 2);
169 : }
170 222 : if (ret == NULL)
171 186 : ret = createException(type, fcn, "GDK reported%s: %s",
172 186 : strstr(p,
173 : EXITING_MSG) == NULL ? " error" : "",
174 : p);
175 222 : GDKclrerr();
176 222 : assert(ret);
177 222 : return ret;
178 : }
179 39524 : va_start(ap, format);
180 39524 : ret = createExceptionInternal(type, fcn, format, ap);
181 39525 : va_end(ap);
182 39525 : GDKclrerr();
183 :
184 39525 : assert(ret);
185 : return ret;
186 : }
187 :
188 : void
189 58753095 : freeException(str msg)
190 : {
191 58753095 : if (msg != MAL_SUCCEED && msg != M5OutOfMemory)
192 39594 : GDKfree(msg);
193 58753096 : }
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 41 : createMalExceptionInternal(MalBlkPtr mb, int pc, enum malexception type,
202 : char *prev, const char *format, va_list ap)
203 : {
204 41 : bool addnl = false;
205 41 : const char *s = mb && getInstrPtr(mb, 0) ? getModName(mb) : "unknown";
206 41 : const char *fcn = mb && getInstrPtr(mb, 0) ? getFcnName(mb) : "unknown";
207 41 : size_t msglen;
208 :
209 41 : 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 37 : } else if (type == SYNTAX) {
218 0 : msglen = strlen(exceptionNames[type]) + 1;
219 : } else {
220 37 : msglen = snprintf(NULL, 0, "%s:%s.%s[%d]:",
221 : exceptionNames[type], s, fcn, pc);
222 : }
223 41 : va_list ap2;
224 41 : va_copy(ap2, ap);
225 41 : int len = vsnprintf(NULL, 0, format, ap);
226 41 : if (len < 0)
227 : len = 0;
228 41 : char *msg = GDKmalloc(msglen + len + 1);
229 41 : if (msg != NULL) {
230 : /* the calls below succeed: the arguments have already been checked */
231 41 : 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 37 : } else if (type == SYNTAX) {
236 0 : (void) strconcat_len(msg, msglen + 1,
237 : exceptionNames[type], ":", NULL);
238 : } else {
239 37 : (void) snprintf(msg, msglen + 1, "%s:%s.%s[%d]:",
240 : exceptionNames[type], s, fcn, pc);
241 : }
242 41 : if (len > 0)
243 41 : (void) vsnprintf(msg + msglen, len + 1, format, ap2);
244 : } else {
245 0 : msg = M5OutOfMemory;
246 : }
247 41 : va_end(ap2);
248 41 : freeException(prev);
249 41 : 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 41 : createMalException(MalBlkPtr mb, int pc, enum malexception type,
263 : const char *format, ...)
264 : {
265 41 : va_list ap;
266 41 : str ret;
267 :
268 41 : va_start(ap, format);
269 41 : ret = createMalExceptionInternal(mb, pc, type, mb->errors, format, ap);
270 41 : va_end(ap);
271 :
272 41 : 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 8 : getExceptionType(const char *exception)
282 : {
283 8 : enum malexception ret = MAL;
284 8 : const char *s;
285 8 : size_t len;
286 8 : enum malexception i;
287 :
288 8 : if ((s = strchr(exception, ':')) != NULL)
289 8 : len = s - exception;
290 : else
291 0 : len = strlen(exception);
292 :
293 87 : for (i = MAL; exceptionNames[i] != NULL; i++) {
294 87 : if (strncmp(exceptionNames[i], exception, len) == 0 &&
295 8 : exceptionNames[i][len] == '\0') {
296 : ret = i;
297 : break;
298 : }
299 : }
300 :
301 8 : 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 38535 : getExceptionMessageAndState(const char *exception)
340 : {
341 38535 : const char *s, *t;
342 38535 : enum malexception i;
343 38535 : size_t l;
344 :
345 520603 : for (i = MAL; exceptionNames[i] != NULL; i++) {
346 520416 : l = strlen(exceptionNames[i]);
347 520416 : if (strncmp(exceptionNames[i], exception, l) == 0 &&
348 38349 : exception[l] == ':') {
349 38348 : s = exception + l + 1;
350 38348 : if ((t = strpbrk(s, ":\n")) != NULL && *t == ':')
351 38348 : 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 41 : getExceptionMessage(const char *exception)
362 : {
363 41 : char *msg = getExceptionMessageAndState(exception);
364 :
365 41 : if (strlen(msg) > 6 && msg[5] == '!' &&
366 15 : (isdigit((unsigned char) msg[0]) ||
367 1 : (msg[0] >= 'A' && msg[0] <= 'Z')) &&
368 15 : (isdigit((unsigned char) msg[1]) ||
369 1 : (msg[1] >= 'A' && msg[1] <= 'Z')) &&
370 15 : (isdigit((unsigned char) msg[2]) ||
371 3 : (msg[2] >= 'A' && msg[2] <= 'Z')) &&
372 15 : (isdigit((unsigned char) msg[3]) ||
373 0 : (msg[3] >= 'A' && msg[3] <= 'Z')) &&
374 15 : (isdigit((unsigned char) msg[4]) || (msg[4] >= 'A' && msg[4] <= 'Z')))
375 15 : msg += 6;
376 41 : return msg;
377 : }
|