Coverage Report

Created: 2025-08-03 06:43

/src/tarantool/src/box/error.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
3
 *
4
 * Redistribution and use in source and binary forms, with or
5
 * without modification, are permitted provided that the following
6
 * conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above
9
 *    copyright notice, this list of conditions and the
10
 *    following disclaimer.
11
 *
12
 * 2. Redistributions in binary form must reproduce the above
13
 *    copyright notice, this list of conditions and the following
14
 *    disclaimer in the documentation and/or other materials
15
 *    provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
#include "error.h"
32
#include <stdio.h>
33
34
#include "event.h"
35
#include "fiber.h"
36
#include "func_adapter.h"
37
#include "rmean.h"
38
#include "trigger.h"
39
#include "vclock/vclock.h"
40
#include "schema.h"
41
#include "ssl_error.h"
42
#include "tuple.h"
43
44
/* {{{ public API */
45
46
const char *
47
box_error_type(const box_error_t *e)
48
0
{
49
0
  return e->type->name;
50
0
}
51
52
uint32_t
53
box_error_code(const box_error_t *e)
54
0
{
55
0
  return ClientError::get_errcode(e);
56
0
}
57
58
const char *
59
box_error_message(const box_error_t *error)
60
0
{
61
0
  return error->errmsg;
62
0
}
63
64
box_error_t *
65
box_error_last(void)
66
0
{
67
0
  return diag_last_error(&fiber()->diag);
68
0
}
69
70
void
71
box_error_clear(void)
72
0
{
73
0
  diag_clear(&fiber()->diag);
74
0
}
75
76
int
77
box_error_set(const char *file, unsigned line, uint32_t code,
78
    const char *fmt, ...)
79
2.28k
{
80
2.28k
  struct error *e = BuildClientError(file, line, ER_UNKNOWN);
81
2.28k
  ClientError *client_error = type_cast(ClientError, e);
82
2.28k
  client_error->code = code;
83
2.28k
  va_list ap;
84
2.28k
  va_start(ap, fmt);
85
2.28k
  error_vformat_msg(e, fmt, ap);
86
2.28k
  va_end(ap);
87
2.28k
  diag_set_error(&fiber()->diag, e);
88
2.28k
  return -1;
89
2.28k
}
90
91
static struct error *
92
box_error_new_va(const char *file, unsigned line, uint32_t code,
93
     const char *custom_type, const char *fmt, va_list ap)
94
0
{
95
0
  struct error *e;
96
0
  if (custom_type != NULL) {
97
0
    e = BuildCustomError(file, line, custom_type, code);
98
0
  } else if (code == ER_ILLEGAL_PARAMS) {
99
0
    e = BuildIllegalParams(file, line, "");
100
0
  } else {
101
0
    e = BuildClientError(file, line, ER_UNKNOWN);
102
0
    ClientError *client_error = type_cast(ClientError, e);
103
0
    client_error->code = code;
104
0
  }
105
0
  error_vformat_msg(e, fmt, ap);
106
0
  return e;
107
0
}
108
109
struct error *
110
box_error_new(const char *file, unsigned line, uint32_t code,
111
        const char *custom_type, const char *fmt, ...)
112
0
{
113
0
  va_list ap;
114
0
  va_start(ap, fmt);
115
0
  struct error *e = box_error_new_va(file, line, code, custom_type,
116
0
             fmt, ap);
117
0
  va_end(ap);
118
0
  return e;
119
0
}
120
121
int
122
box_error_add(const char *file, unsigned line, uint32_t code,
123
        const char *custom_type, const char *fmt, ...)
124
0
{
125
0
  va_list ap;
126
0
  va_start(ap, fmt);
127
0
  struct error *e = box_error_new_va(file, line, code, custom_type,
128
0
             fmt, ap);
129
0
  va_end(ap);
130
131
0
  struct diag *d = &fiber()->diag;
132
0
  if (diag_is_empty(d))
133
0
    diag_set_error(d, e);
134
0
  else
135
0
    diag_add_error(d, e);
136
0
  return -1;
137
0
}
138
139
/* }}} */
140
141
const char *
142
box_error_custom_type(const struct error *e)
143
0
{
144
0
  CustomError *custom_error = type_cast(CustomError, e);
145
0
  if (custom_error)
146
0
    return custom_error->custom_type();
147
148
0
  return NULL;
149
0
}
150
151
struct rmean *rmean_error = NULL;
152
153
const char *rmean_error_strings[RMEAN_ERROR_LAST] = {
154
  "ERROR"
155
};
156
157
/** Format client error with arguments in `ap` according to error format. */
158
static void
159
client_error_create(struct error *e, va_list ap)
160
65.4k
{
161
65.4k
  const struct errcode_record *r = tnt_errcode_record(e->code);
162
65.4k
  va_list ap_copy;
163
65.4k
  va_copy(ap_copy, ap);
164
65.4k
  error_vformat_msg(e, r->errdesc, ap_copy);
165
65.4k
  va_end(ap_copy);
166
97.4k
  for (int i = 0; i < r->errfields_count; i++) {
167
31.9k
    const char *name = r->errfields[i].name;
168
31.9k
    bool set_payload = name[0] != '\0';
169
31.9k
    switch (r->errfields[i].type) {
170
0
    case ERRCODE_FIELD_TYPE_CHAR: {
171
0
      char buf[2] = {(char)va_arg(ap, int), '\0'};
172
0
      if (set_payload)
173
0
        error_set_str(e, name, buf);
174
0
      break;
175
0
    }
176
1.81k
    case ERRCODE_FIELD_TYPE_INT: {
177
1.81k
      int v = va_arg(ap, int);
178
1.81k
      if (set_payload)
179
1.81k
        error_set_int(e, name, v);
180
1.81k
      break;
181
0
    }
182
0
    case ERRCODE_FIELD_TYPE_UINT: {
183
0
      unsigned v = va_arg(ap, unsigned);
184
0
      if (set_payload)
185
0
        error_set_uint(e, name, v);
186
0
      break;
187
0
    }
188
0
    case ERRCODE_FIELD_TYPE_LONG: {
189
0
      long v = va_arg(ap, long);
190
0
      if (set_payload)
191
0
        error_set_int(e, name, v);
192
0
      break;
193
0
    }
194
0
    case ERRCODE_FIELD_TYPE_ULONG: {
195
0
      unsigned long v = va_arg(ap, unsigned long);
196
0
      if (set_payload)
197
0
        error_set_uint(e, name, v);
198
0
      break;
199
0
    }
200
0
    case ERRCODE_FIELD_TYPE_LLONG: {
201
0
      long long v = va_arg(ap, long long);
202
0
      if (set_payload)
203
0
        error_set_int(e, name, v);
204
0
      break;
205
0
    }
206
0
    case ERRCODE_FIELD_TYPE_ULLONG: {
207
0
      unsigned long long v = va_arg(ap, unsigned long long);
208
0
      if (set_payload)
209
0
        error_set_uint(e, name, v);
210
0
      break;
211
0
    }
212
30.1k
    case ERRCODE_FIELD_TYPE_STRING: {
213
30.1k
      const char *s = va_arg(ap, const char *);
214
30.1k
      if (set_payload && s != NULL)
215
29.9k
        error_set_str(e, name, s);
216
30.1k
      break;
217
0
    }
218
0
    case ERRCODE_FIELD_TYPE_MSGPACK: {
219
0
      const char *mp = va_arg(ap, const char *);
220
0
      const char *mp_end = mp;
221
0
      assert(set_payload);
222
0
      if (mp != NULL) {
223
0
        mp_next(&mp_end);
224
0
        error_set_mp(e, name, mp, mp_end - mp);
225
0
      }
226
0
      break;
227
0
    }
228
0
    case ERRCODE_FIELD_TYPE_TUPLE: {
229
0
      struct tuple *tuple = va_arg(ap, struct tuple *);
230
0
      assert(set_payload);
231
0
      if (tuple != NULL)
232
0
        error_set_mp(e, name, tuple_data(tuple),
233
0
               tuple_bsize(tuple));
234
0
      break;
235
0
    }
236
0
    default:
237
0
      assert(false);
238
31.9k
    }
239
31.9k
  }
240
65.4k
  assert(strncmp("ER_", r->errstr, 3) == 0);
241
65.4k
  error_set_str(e, "name", r->errstr + 3);
242
65.4k
}
243
244
const struct type_info type_ClientError =
245
  make_type("ClientError", &type_Exception);
246
247
ClientError::ClientError(const type_info *type, const char *file, unsigned line,
248
       uint32_t errcode)
249
33.1k
  :Exception(type, file, line)
250
33.1k
{
251
33.1k
  code = errcode;
252
33.1k
  if (rmean_error)
253
0
    rmean_collect(rmean_error, RMEAN_ERROR, 1);
254
33.1k
}
255
256
ClientError::ClientError(const char *file, unsigned line,
257
       uint32_t errcode, ...)
258
32.7k
  :ClientError(&type_ClientError, file, line, errcode)
259
32.7k
{
260
32.7k
  va_list ap;
261
32.7k
  va_start(ap, errcode);
262
32.7k
  client_error_create(this, ap);
263
32.7k
  va_end(ap);
264
32.7k
}
265
266
struct error *
267
BuildClientError(const char *file, unsigned line, uint32_t errcode, ...)
268
32.7k
{
269
32.7k
  assert(errcode != ER_ILLEGAL_PARAMS); /* use IllegalParams */
270
32.7k
  assert(errcode != ER_MEMORY_ISSUE); /* use OutOfMemory */
271
32.7k
  assert(errcode != ER_SYSTEM); /* use SystemError */
272
32.7k
  assert(errcode != ER_SSL); /* use SSLError */
273
32.7k
  assert(errcode != ER_XLOG_GAP); /* use XlogGapError */
274
32.7k
  assert(errcode != ER_ACCESS_DENIED); /* use AccessDeniedError */
275
32.7k
  ClientError *e = new ClientError(file, line, ER_UNKNOWN);
276
32.7k
  va_list ap;
277
32.7k
  va_start(ap, errcode);
278
32.7k
  e->code = errcode;
279
32.7k
  client_error_create(e, ap);
280
32.7k
  va_end(ap);
281
32.7k
  return e;
282
32.7k
}
283
284
void
285
ClientError::log() const
286
0
{
287
0
  say_file_line(S_ERROR, file, line, errmsg, "%s", tnt_errcode_str(code));
288
0
}
289
290
291
uint32_t
292
ClientError::get_errcode(const struct error *e)
293
0
{
294
0
  ClientError *client_error = type_cast(ClientError, e);
295
0
  if (client_error)
296
0
    return client_error->errcode();
297
0
  if (type_cast(IllegalParams, e))
298
0
    return ER_ILLEGAL_PARAMS;
299
0
  if (type_cast(OutOfMemory, e))
300
0
    return ER_MEMORY_ISSUE;
301
0
  if (type_cast(SystemError, e))
302
0
    return ER_SYSTEM;
303
0
  if (type_cast(SSLError, e))
304
0
    return ER_SSL;
305
0
  if (type_cast(CollationError, e))
306
0
    return ER_CANT_CREATE_COLLATION;
307
0
  if (type_cast(XlogGapError, e))
308
0
    return ER_XLOG_GAP;
309
0
  return ER_PROC_LUA;
310
0
}
311
312
const struct type_info type_XlogError = make_type("XlogError", &type_Exception);
313
314
struct error *
315
BuildXlogError(const char *file, unsigned line, const char *format, ...)
316
0
{
317
0
  va_list ap;
318
0
  va_start(ap, format);
319
0
  XlogError *e = new XlogError(file, line, format, ap);
320
0
  va_end(ap);
321
0
  return e;
322
0
}
323
324
const struct type_info type_XlogGapError =
325
  make_type("XlogGapError", &type_XlogError);
326
327
XlogGapError::XlogGapError(const char *file, unsigned line,
328
         const struct vclock *from, const struct vclock *to)
329
0
    : XlogError(&type_XlogGapError, file, line)
330
0
{
331
0
  const char *s_from = vclock_to_string(from);
332
0
  const char *s_to = vclock_to_string(to);
333
0
  error_format_msg(this,
334
0
       "Missing .xlog file between LSN %lld %s and %lld %s",
335
0
       (long long)vclock_sum(from), s_from ? s_from : "",
336
0
       (long long)vclock_sum(to), s_to ? s_to : "");
337
0
}
338
339
struct error *
340
BuildXlogGapError(const char *file, unsigned line,
341
      const struct vclock *from, const struct vclock *to)
342
0
{
343
0
  return new XlogGapError(file, line, from, to);
344
0
}
345
346
struct rlist on_access_denied = RLIST_HEAD_INITIALIZER(on_access_denied);
347
348
const struct type_info type_AccessDeniedError =
349
  make_type("AccessDeniedError", &type_ClientError);
350
351
AccessDeniedError::AccessDeniedError(const char *file, unsigned int line,
352
             const char *access_type,
353
             const char *object_type,
354
             const char *object_name,
355
             const char *user_name,
356
             bool run_triggers)
357
0
  :ClientError(&type_AccessDeniedError, file, line, ER_ACCESS_DENIED)
358
0
{
359
0
  error_format_msg(this, tnt_errcode_desc(code),
360
0
       access_type, object_type, object_name, user_name);
361
0
  struct on_access_denied_ctx trigger_ctx =
362
0
    {access_type, object_type, object_name};
363
  /*
364
   * Don't run the triggers when create after marshaling
365
   * through network.
366
   */
367
0
  if (run_triggers) {
368
0
    if (trigger_run(&on_access_denied, &trigger_ctx) != 0)
369
0
      diag_log();
370
0
  }
371
0
  error_set_str(this, "object_type", object_type);
372
0
  error_set_str(this, "object_name", object_name);
373
0
  error_set_str(this, "access_type", access_type);
374
0
  error_set_str(this, "user", user_name);
375
0
}
376
377
struct error *
378
BuildAccessDeniedError(const char *file, unsigned int line,
379
           const char *access_type, const char *object_type,
380
           const char *object_name,
381
           const char *user_name)
382
0
{
383
0
  return new AccessDeniedError(file, line, access_type,
384
0
             object_type, object_name,
385
0
             user_name);
386
0
}
387
388
const struct type_info type_CustomError =
389
  make_type("CustomError", &type_ClientError);
390
391
CustomError::CustomError(const char *file, unsigned int line,
392
       const char *custom_type, uint32_t errcode)
393
0
  :ClientError(&type_CustomError, file, line, errcode)
394
0
{
395
0
  error_set_str(this, "custom_type", custom_type);
396
0
}
397
398
void
399
CustomError::log() const
400
0
{
401
0
  say_file_line(S_ERROR, file, line, errmsg,
402
0
          "Custom type %s", custom_type());
403
0
}
404
405
struct error *
406
BuildCustomError(const char *file, unsigned int line, const char *custom_type,
407
     uint32_t errcode)
408
0
{
409
0
  return new CustomError(file, line, custom_type, errcode);
410
0
}