1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * (c) F. Groffen, M. Kersten
11 * For documentation see website
12 */
13#include "monetdb_config.h"
14#include "mal_exception.h"
15#include "mal_private.h"
16
17static char *exceptionNames[] = {
18/* 0 */ "MALException",
19/* 1 */ "IllegalArgumentException",
20/* 2 */ "OutOfBoundsException",
21/* 3 */ "IOException",
22/* 4 */ "InvalidCredentialsException",
23/* 5 */ "OptimizerException",
24/* 6 */ "StackOverflowException",
25/* 7 */ "SyntaxException",
26/* 8 */ "TypeException",
27/* 9 */ "LoaderException",
28/*10 */ "ParseException",
29/*11 */ "ArithmeticException",
30/*12 */ "PermissionDeniedException",
31/*13 */ "SQLException",
32/*15 */ "Deprecated operation",
33/*EOE*/ NULL
34};
35
36int
37isExceptionVariable(str nme){
38 int i;
39 if( nme)
40 for(i=0; exceptionNames[i]; i++)
41 if( strcmp(exceptionNames[i],nme)==0)
42 return 1;
43 return 0;
44}
45
46static char *M5OutOfMemory = MAL_MALLOC_FAIL;
47
48/**
49 * Internal helper function for createException and
50 * showException such that they share the same code, because reuse
51 * is good.
52 */
53static str createExceptionInternal(enum malexception type, const char *fcn, const char *format, va_list ap)
54 __attribute__((__format__(__printf__, 3, 0)))
55 __attribute__((__returns_nonnull__));
56static str
57createExceptionInternal(enum malexception type, const char *fcn, const char *format, va_list ap)
58{
59 char *message, local[GDKMAXERRLEN];
60 int len;
61 // if there is an error we allow memory allocation once again
62#ifndef NDEBUG
63 GDKsetmallocsuccesscount(-1);
64#endif
65 message = GDKmalloc(GDKMAXERRLEN);
66 if (message == NULL){
67 /* Leave a message behind in the logging system */
68 len = snprintf(local, GDKMAXERRLEN, "%s:%s:", exceptionNames[type], fcn);
69 len = vsnprintf(local + len, GDKMAXERRLEN, format, ap);
70 fprintf(stderr, "%s", local);
71 return M5OutOfMemory; /* last resort */
72 }
73 len = snprintf(message, GDKMAXERRLEN, "%s:%s:", exceptionNames[type], fcn);
74 if (len >= GDKMAXERRLEN) /* shouldn't happen */
75 return message;
76 len += vsnprintf(message + len, GDKMAXERRLEN - len, format, ap);
77 /* realloc to reduce amount of allocated memory (GDKMAXERRLEN is
78 * way more than what is normally needed) */
79 if (len < GDKMAXERRLEN) {
80 /* in the extremely unlikely case that GDKrealloc fails, the
81 * original pointer is still valid, so use that and don't
82 * overwrite */
83 char *newmsg = GDKrealloc(message, len + 1);
84 if (newmsg != NULL)
85 message = newmsg;
86 }
87 char *q = message;
88 for (char *p = strchr(q, '\n'); p; q = p + 1, p = strchr(q, '\n'))
89 fprintf(stderr, "#%s:!ERROR:%.*s\n", MT_thread_getname(), (int) (p - q), q);
90 if (*q)
91 fprintf(stderr, "#%s:!ERROR:%s\n", MT_thread_getname(), q);
92 return message;
93}
94
95/**
96 * Returns an exception string for the given type of exception, function
97 * and additional formatting parameters. This function will crash the
98 * system or return bogus when the malexception enum is not aligned with
99 * the exceptionNames array.
100 */
101str
102createException(enum malexception type, const char *fcn, const char *format, ...)
103{
104 va_list ap;
105 str ret;
106
107 if (GDKerrbuf &&
108 (ret = strstr(format, MAL_MALLOC_FAIL)) != NULL &&
109 ret[strlen(MAL_MALLOC_FAIL)] != ':' &&
110 (strncmp(GDKerrbuf, "GDKmalloc", 9) == 0 ||
111 strncmp(GDKerrbuf, "GDKrealloc", 10) == 0 ||
112 strncmp(GDKerrbuf, "GDKzalloc", 9) == 0 ||
113 strncmp(GDKerrbuf, "GDKstrdup", 9) == 0 ||
114 strncmp(GDKerrbuf, "allocating too much virtual address space", 41) == 0)) {
115 /* override errors when the underlying error is memory
116 * exhaustion, but include whatever it is that the GDK level
117 * reported */
118 ret = createException(type, fcn, SQLSTATE(HY001) MAL_MALLOC_FAIL ": %s", GDKerrbuf);
119 GDKclrerr();
120 return ret;
121 }
122 if (strcmp(format, GDK_EXCEPTION) == 0 && GDKerrbuf[0]) {
123 /* for GDK errors, report the underlying error */
124 char *p = GDKerrbuf;
125 if (strncmp(p, GDKERROR, strlen(GDKERROR)) == 0)
126 p += strlen(GDKERROR);
127 if (strlen(p) > 6 && p[5] == '!')
128 ret = createException(type, fcn, "%s", p);
129 else
130 ret = createException(type, fcn, "GDK reported error: %s", p);
131 GDKclrerr();
132 return ret;
133 }
134 va_start(ap, format);
135 ret = createExceptionInternal(type, fcn, format, ap);
136 va_end(ap);
137 GDKclrerr();
138
139 return ret;
140}
141
142void
143freeException(str msg)
144{
145 if (msg != MAL_SUCCEED && msg != M5OutOfMemory)
146 GDKfree(msg);
147}
148
149
150/**
151 * Internal helper function for createMalException and
152 * showScriptException such that they share the same code, because reuse
153 * is good.
154 */
155static str
156createMalExceptionInternal(MalBlkPtr mb, int pc, enum malexception type, char *prev, const char *format, va_list ap)
157 __attribute__((__format__(__printf__, 5, 0)))
158 __attribute__((__returns_nonnull__));
159static str
160createMalExceptionInternal(MalBlkPtr mb, int pc, enum malexception type, char *prev, const char *format, va_list ap)
161{
162 char buf[GDKMAXERRLEN];
163 size_t i;
164 str s, fcn;
165
166 s = mb ? getModName(mb) : "unknown";
167 fcn = mb ? getFcnName(mb) : "unknown";
168 i = 0;
169
170 if (prev){
171 if( *prev){
172 i += snprintf(buf + i, GDKMAXERRLEN - 1 - i, "%s", prev);
173 if( buf[i-1] != '\n')
174 buf[i++]= '\n';
175 }
176 i += snprintf(buf + i, GDKMAXERRLEN - 1 - i, "!%s:%s.%s[%d]:",
177 exceptionNames[type], s, fcn, pc);
178 freeException(prev);
179 } else if( type == SYNTAX)
180 i += snprintf(buf + i, GDKMAXERRLEN - 1 - i, "%s:",
181 exceptionNames[type]);
182 else
183 i += snprintf(buf + i, GDKMAXERRLEN - 1 - i, "%s:%s.%s[%d]:",
184 exceptionNames[type], s, fcn, pc);
185 i += vsnprintf(buf + i, GDKMAXERRLEN - 1 - i, format, ap);
186 if( buf[i-1] != '\n')
187 buf[i++]= '\n';
188 buf[i] = '\0';
189
190 s = GDKstrdup(buf);
191 if (s == NULL) /* make sure we always return something */
192 s = M5OutOfMemory;
193 return s;
194}
195
196/**
197 * Returns an exception string for the MAL instructions. These
198 * exceptions are newline terminated, and determine module and function
199 * from the given MalBlkPtr. An old exception can be given, such that
200 * this exception is chained to the previous one. Conceptually this
201 * creates a "stack" of exceptions.
202 * This function will crash the system or return bogus when the
203 * malexception enum is not aligned with the exceptionNames array.
204 */
205str
206createMalException(MalBlkPtr mb, int pc, enum malexception type, const char *format, ...)
207{
208 va_list ap;
209 str ret;
210
211 va_start(ap, format);
212 ret = createMalExceptionInternal(mb, pc, type, mb->errors, format, ap);
213 va_end(ap);
214
215 return(ret);
216}
217
218/**
219 * Returns the malexception number for the given exception string. If no
220 * exception could be found in the string, MAL is returned indicating a
221 * generic MALException.
222 */
223enum malexception
224getExceptionType(const char *exception)
225{
226 enum malexception ret = MAL;
227 const char *s;
228 size_t len;
229 enum malexception i;
230
231 if ((s = strchr(exception, ':')) != NULL)
232 len = s - exception;
233 else
234 len = strlen(exception);
235
236 for (i = MAL; exceptionNames[i] != NULL; i++) {
237 if (strncmp(exceptionNames[i], exception, len) == 0 &&
238 exceptionNames[i][len] == '\0') {
239 ret = i;
240 break;
241 }
242 }
243
244 return(ret);
245}
246
247/**
248 * Returns the location the exception was raised, if known. It
249 * depends on how the exception was created, what the location looks
250 * like. The returned string is mallocced with GDKmalloc, and hence
251 * needs to be GDKfreed.
252 */
253str
254getExceptionPlace(const char *exception)
255{
256 str ret;
257 const char *s, *t;
258 enum malexception i;
259 size_t l;
260
261 for (i = MAL; exceptionNames[i] != NULL; i++) {
262 l = strlen(exceptionNames[i]);
263 if (strncmp(exceptionNames[i], exception, l) == 0 &&
264 exception[l] == ':') {
265 s = exception + l + 1;
266 if ((t = strchr(s, ':')) != NULL) {
267 if ((ret = GDKmalloc(t - s + 1)) == NULL)
268 return NULL;
269 strcpy_len(ret, s, t - s + 1);
270 return ret;
271 }
272 break;
273 }
274 }
275 return GDKstrdup("(unknown)");
276}
277
278/**
279 * Returns the informational message of the exception given.
280 */
281str
282getExceptionMessageAndState(const char *exception)
283{
284 const char *s, *t;
285 enum malexception i;
286 size_t l;
287
288 for (i = MAL; exceptionNames[i] != NULL; i++) {
289 l = strlen(exceptionNames[i]);
290 if (strncmp(exceptionNames[i], exception, l) == 0 &&
291 exception[l] == ':') {
292 s = exception + l + 1;
293 if ((t = strpbrk(s, ":\n")) != NULL && *t == ':')
294 return (str) (t + 1);
295 return (str) s;
296 }
297 }
298 if (strncmp(exception, "!ERROR: ", 8) == 0)
299 return (str) (exception + 8);
300 return (str) exception;
301}
302
303str
304getExceptionMessage(const char *exception)
305{
306 char *msg = getExceptionMessageAndState(exception);
307
308 if (strlen(msg) > 6 && msg[5] == '!' &&
309 (isdigit((unsigned char) msg[0]) ||
310 (msg[0] >= 'A' && msg[0] <= 'Z')) &&
311 (isdigit((unsigned char) msg[1]) ||
312 (msg[1] >= 'A' && msg[1] <= 'Z')) &&
313 (isdigit((unsigned char) msg[2]) ||
314 (msg[2] >= 'A' && msg[2] <= 'Z')) &&
315 (isdigit((unsigned char) msg[3]) ||
316 (msg[3] >= 'A' && msg[3] <= 'Z')) &&
317 (isdigit((unsigned char) msg[4]) ||
318 (msg[4] >= 'A' && msg[4] <= 'Z')))
319 msg += 6;
320 return msg;
321}
322