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 39554 : createExceptionInternal(enum malexception type, const char *fcn,
82 : const char *format, va_list ap)
83 : {
84 39554 : size_t msglen;
85 39554 : int len;
86 39554 : char *msg;
87 39554 : va_list ap2;
88 :
89 39554 : va_copy(ap2, ap); /* we need to use it twice */
90 39554 : msglen = strlen(exceptionNames[type]) + strlen(fcn) + 2;
91 39554 : len = vsnprintf(NULL, 0, format, ap); /* count necessary length */
92 39554 : if (len < 0) {
93 0 : TRC_CRITICAL(MAL_SERVER, "called with bad arguments");
94 0 : len = 0;
95 : }
96 39554 : msg = GDKmalloc(msglen + len + 2);
97 39549 : if (msg != NULL) {
98 : /* the calls below succeed: the arguments have already been checked */
99 39549 : (void) strconcat_len(msg, msglen + 1,
100 : exceptionNames[type], ":", fcn, ":", NULL);
101 39554 : if (len > 0)
102 39553 : (void) vsnprintf(msg + msglen, len + 1, format, ap2);
103 39554 : va_end(ap2);
104 39554 : char *q = msg + strlen(msg);
105 39554 : if (q[-1] != '\n') {
106 : /* make sure message ends with newline, we already have the space */
107 20931 : *q++ = '\n';
108 20931 : *q = '\0';
109 : }
110 39554 : q = msg;
111 79295 : for (char *p = strchr(msg, '\n'); p; q = p + 1, p = strchr(q, '\n'))
112 39741 : TRC_ERROR(MAL_SERVER, "%.*s\n", (int) (p - q), q);
113 39554 : if (*q)
114 0 : TRC_ERROR(MAL_SERVER, "%s\n", q);
115 : } else {
116 0 : msg = M5OutOfMemory;
117 : }
118 39554 : va_end(ap2);
119 :
120 39554 : assert(msg);
121 39554 : 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 39770 : createException(enum malexception type, const char *fcn, const char *format,
132 : ...)
133 : {
134 39770 : va_list ap;
135 39770 : str ret = NULL, localGDKerrbuf = GDKerrbuf;
136 :
137 39770 : if (localGDKerrbuf &&
138 39759 : (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 39759 : if (localGDKerrbuf && localGDKerrbuf[0]
156 1074 : && strcmp(format, GDK_EXCEPTION) == 0) {
157 : /* for GDK errors, report the underlying error */
158 216 : char *p = localGDKerrbuf;
159 216 : 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 216 : p += strlen(GDKERROR);
163 216 : char *q = strchr(p, ':');
164 216 : if (q && q[1] == ' ' && strlen(q) > 8 && q[7] == '!')
165 35 : ret = createException(type, fcn, "%s", q + 2);
166 : }
167 216 : if (ret == NULL)
168 181 : ret = createException(type, fcn, "GDK reported%s: %s",
169 181 : strstr(p, EXITING_MSG) ? "" : " error", p);
170 216 : GDKclrerr();
171 216 : assert(ret);
172 216 : return ret;
173 : }
174 39554 : va_start(ap, format);
175 39554 : ret = createExceptionInternal(type, fcn, format, ap);
176 39554 : va_end(ap);
177 39554 : GDKclrerr();
178 :
179 39554 : assert(ret);
180 : return ret;
181 : }
182 :
183 : void
184 52538046 : freeException(str msg)
185 : {
186 52538046 : if (msg != MAL_SUCCEED && msg != M5OutOfMemory)
187 39623 : GDKfree(msg);
188 52538046 : }
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 38597 : getExceptionMessageAndState(const char *exception)
335 : {
336 38597 : const char *s, *t;
337 38597 : enum malexception i;
338 38597 : size_t l;
339 :
340 521554 : for (i = MAL; exceptionNames[i] != NULL; i++) {
341 521367 : l = strlen(exceptionNames[i]);
342 521367 : if (strncmp(exceptionNames[i], exception, l) == 0 &&
343 38401 : exception[l] == ':') {
344 38410 : s = exception + l + 1;
345 38410 : if ((t = strpbrk(s, ":\n")) != NULL && *t == ':')
346 38410 : 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 : }
|