1 /*****************************************************************************
2
3 Copyright (c) 1994, 2017, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2017, 2021, MariaDB Corporation.
5
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17
18 *****************************************************************************/
19
20 /***************************************************************//**
21 @file ut/ut0ut.cc
22 Various utilities for Innobase.
23
24 Created 5/11/1994 Heikki Tuuri
25 ********************************************************************/
26
27 #include "ha_prototypes.h"
28
29 #if HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32
33 #ifndef UNIV_INNOCHECKSUM
34 #include <mysql_com.h>
35 #include "os0thread.h"
36 #include "ut0ut.h"
37 #include "trx0trx.h"
38 #include <string>
39 #include "log.h"
40 #include "my_cpu.h"
41 #ifndef DBUG_OFF
42 #include "rem0rec.h"
43 #endif
44
45 /**********************************************************//**
46 Returns the number of milliseconds since some epoch. The
47 value may wrap around. It should only be used for heuristic
48 purposes.
49 @return ms since epoch */
50 ulint
ut_time_ms(void)51 ut_time_ms(void)
52 /*============*/
53 {
54 return static_cast<ulint>(my_interval_timer() / 1000000);
55 }
56 #endif /* !UNIV_INNOCHECKSUM */
57
58 /**********************************************************//**
59 Prints a timestamp to a file. */
60 void
ut_print_timestamp(FILE * file)61 ut_print_timestamp(
62 /*===============*/
63 FILE* file) /*!< in: file where to print */
64 {
65 #ifdef _WIN32
66 SYSTEMTIME cal_tm;
67 GetLocalTime(&cal_tm);
68 #else
69 time_t tm;
70 struct tm cal_tm;
71 time(&tm);
72 localtime_r(&tm, &cal_tm);
73 #endif
74 fprintf(file,
75 IF_WIN("%u-%02u-%02u %02u:%02u:%02u %#zx",
76 "%d-%02d-%02d %02d:%02d:%02d %#zx"),
77 #ifdef _WIN32
78 cal_tm.wYear,
79 cal_tm.wMonth,
80 cal_tm.wDay,
81 cal_tm.wHour,
82 cal_tm.wMinute,
83 cal_tm.wSecond,
84 #else
85 cal_tm.tm_year + 1900,
86 cal_tm.tm_mon + 1,
87 cal_tm.tm_mday,
88 cal_tm.tm_hour,
89 cal_tm.tm_min,
90 cal_tm.tm_sec,
91 #endif
92 #ifdef UNIV_INNOCHECKSUM
93 ulint{0}
94 #else
95 ulint(os_thread_get_curr_id())
96 #endif
97 );
98 }
99
100 #ifndef UNIV_INNOCHECKSUM
101
102 /**********************************************************//**
103 Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */
104 void
ut_sprintf_timestamp(char * buf)105 ut_sprintf_timestamp(
106 /*=================*/
107 char* buf) /*!< in: buffer where to sprintf */
108 {
109 #ifdef _WIN32
110 SYSTEMTIME cal_tm;
111 GetLocalTime(&cal_tm);
112
113 sprintf(buf, "%02u%02u%02u %2u:%02u:%02u",
114 cal_tm.wYear % 100,
115 cal_tm.wMonth,
116 cal_tm.wDay,
117 cal_tm.wHour,
118 cal_tm.wMinute,
119 cal_tm.wSecond);
120 #else
121 time_t tm;
122 struct tm cal_tm;
123 time(&tm);
124 localtime_r(&tm, &cal_tm);
125 sprintf(buf, "%02d%02d%02d %2d:%02d:%02d",
126 cal_tm.tm_year % 100,
127 cal_tm.tm_mon + 1,
128 cal_tm.tm_mday,
129 cal_tm.tm_hour,
130 cal_tm.tm_min,
131 cal_tm.tm_sec);
132 #endif
133 }
134
135 /*************************************************************//**
136 Prints the contents of a memory buffer in hex and ascii. */
137 void
ut_print_buf(FILE * file,const void * buf,ulint len)138 ut_print_buf(
139 /*=========*/
140 FILE* file, /*!< in: file where to print */
141 const void* buf, /*!< in: memory buffer */
142 ulint len) /*!< in: length of the buffer */
143 {
144 const byte* data;
145 ulint i;
146
147 fprintf(file, " len " ULINTPF "; hex ", len);
148
149 for (data = (const byte*) buf, i = 0; i < len; i++) {
150 fprintf(file, "%02x", *data++);
151 }
152
153 fputs("; asc ", file);
154
155 data = (const byte*) buf;
156
157 for (i = 0; i < len; i++) {
158 int c = (int) *data++;
159 putc(isprint(c) ? c : ' ', file);
160 }
161
162 putc(';', file);
163 }
164
165 /*************************************************************//**
166 Prints the contents of a memory buffer in hex. */
167 void
ut_print_buf_hex(std::ostream & o,const void * buf,ulint len)168 ut_print_buf_hex(
169 /*=============*/
170 std::ostream& o, /*!< in/out: output stream */
171 const void* buf, /*!< in: memory buffer */
172 ulint len) /*!< in: length of the buffer */
173 {
174 const byte* data;
175 ulint i;
176
177 static const char hexdigit[16] = {
178 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
179 };
180
181 o << "(0x";
182
183 for (data = static_cast<const byte*>(buf), i = 0; i < len; i++) {
184 byte b = *data++;
185 o << hexdigit[int(b) >> 4] << hexdigit[b & 15];
186 }
187
188 o << ")";
189 }
190
191 /*************************************************************//**
192 Prints the contents of a memory buffer in hex and ascii. */
193 void
ut_print_buf(std::ostream & o,const void * buf,ulint len)194 ut_print_buf(
195 /*=========*/
196 std::ostream& o, /*!< in/out: output stream */
197 const void* buf, /*!< in: memory buffer */
198 ulint len) /*!< in: length of the buffer */
199 {
200 const byte* data;
201 ulint i;
202
203 for (data = static_cast<const byte*>(buf), i = 0; i < len; i++) {
204 int c = static_cast<int>(*data++);
205 o << (isprint(c) ? static_cast<char>(c) : ' ');
206 }
207
208 ut_print_buf_hex(o, buf, len);
209 }
210
211 /** Get a fixed-length string, quoted as an SQL identifier.
212 If the string contains a slash '/', the string will be
213 output as two identifiers separated by a period (.),
214 as in SQL database_name.identifier.
215 @param [in] trx transaction (NULL=no quotes).
216 @param [in] name table name.
217 @retval String quoted as an SQL identifier.
218 */
219 std::string
ut_get_name(const trx_t * trx,const char * name)220 ut_get_name(
221 const trx_t* trx,
222 const char* name)
223 {
224 /* 2 * NAME_LEN for database and table name,
225 and some slack for the #mysql50# prefix and quotes */
226 char buf[3 * NAME_LEN];
227 const char* bufend;
228
229 bufend = innobase_convert_name(buf, sizeof buf,
230 name, strlen(name),
231 trx ? trx->mysql_thd : NULL);
232 buf[bufend - buf] = '\0';
233 return(std::string(buf, 0, size_t(bufend - buf)));
234 }
235
236 /**********************************************************************//**
237 Outputs a fixed-length string, quoted as an SQL identifier.
238 If the string contains a slash '/', the string will be
239 output as two identifiers separated by a period (.),
240 as in SQL database_name.identifier. */
241 void
ut_print_name(FILE * f,const trx_t * trx,const char * name)242 ut_print_name(
243 /*==========*/
244 FILE* f, /*!< in: output stream */
245 const trx_t* trx, /*!< in: transaction */
246 const char* name) /*!< in: name to print */
247 {
248 /* 2 * NAME_LEN for database and table name,
249 and some slack for the #mysql50# prefix and quotes */
250 char buf[3 * NAME_LEN];
251 const char* bufend;
252
253 bufend = innobase_convert_name(buf, sizeof buf,
254 name, strlen(name),
255 trx ? trx->mysql_thd : NULL);
256
257 if (fwrite(buf, 1, size_t(bufend - buf), f) != size_t(bufend - buf)) {
258 perror("fwrite");
259 }
260 }
261
262 /** Format a table name, quoted as an SQL identifier.
263 If the name contains a slash '/', the result will contain two
264 identifiers separated by a period (.), as in SQL
265 database_name.table_name.
266 @see table_name_t
267 @param[in] name table or index name
268 @param[out] formatted formatted result, will be NUL-terminated
269 @param[in] formatted_size size of the buffer in bytes
270 @return pointer to 'formatted' */
271 char*
ut_format_name(const char * name,char * formatted,ulint formatted_size)272 ut_format_name(
273 const char* name,
274 char* formatted,
275 ulint formatted_size)
276 {
277 switch (formatted_size) {
278 case 1:
279 formatted[0] = '\0';
280 /* FALL-THROUGH */
281 case 0:
282 return(formatted);
283 }
284
285 char* end;
286
287 end = innobase_convert_name(formatted, formatted_size,
288 name, strlen(name), NULL);
289
290 /* If the space in 'formatted' was completely used, then sacrifice
291 the last character in order to write '\0' at the end. */
292 if ((ulint) (end - formatted) == formatted_size) {
293 end--;
294 }
295
296 ut_a((ulint) (end - formatted) < formatted_size);
297
298 *end = '\0';
299
300 return(formatted);
301 }
302
303 /**********************************************************************//**
304 Catenate files. */
305 void
ut_copy_file(FILE * dest,FILE * src)306 ut_copy_file(
307 /*=========*/
308 FILE* dest, /*!< in: output file */
309 FILE* src) /*!< in: input file to be appended to output */
310 {
311 long len = ftell(src);
312 char buf[4096];
313
314 rewind(src);
315 do {
316 size_t maxs = len < (long) sizeof buf
317 ? (size_t) len
318 : sizeof buf;
319 size_t size = fread(buf, 1, maxs, src);
320 if (fwrite(buf, 1, size, dest) != size) {
321 perror("fwrite");
322 }
323 len -= (long) size;
324 if (size < maxs) {
325 break;
326 }
327 } while (len > 0);
328 }
329
330 /** Convert an error number to a human readable text message.
331 The returned string is static and should not be freed or modified.
332 @param[in] num InnoDB internal error number
333 @return string, describing the error */
334 const char*
ut_strerr(dberr_t num)335 ut_strerr(
336 dberr_t num)
337 {
338 switch (num) {
339 case DB_SUCCESS:
340 return("Success");
341 case DB_SUCCESS_LOCKED_REC:
342 return("Success, record lock created");
343 case DB_ERROR:
344 return("Generic error");
345 case DB_READ_ONLY:
346 return("Read only transaction");
347 case DB_INTERRUPTED:
348 return("Operation interrupted");
349 case DB_OUT_OF_MEMORY:
350 return("Cannot allocate memory");
351 case DB_OUT_OF_FILE_SPACE:
352 return("Out of disk space");
353 case DB_LOCK_WAIT:
354 return("Lock wait");
355 case DB_DEADLOCK:
356 return("Deadlock");
357 case DB_ROLLBACK:
358 return("Rollback");
359 case DB_DUPLICATE_KEY:
360 return("Duplicate key");
361 case DB_MISSING_HISTORY:
362 return("Required history data has been deleted");
363 case DB_CLUSTER_NOT_FOUND:
364 return("Cluster not found");
365 case DB_TABLE_NOT_FOUND:
366 return("Table not found");
367 case DB_MUST_GET_MORE_FILE_SPACE:
368 return("More file space needed");
369 case DB_TABLE_IS_BEING_USED:
370 return("Table is being used");
371 case DB_TOO_BIG_RECORD:
372 return("Record too big");
373 case DB_TOO_BIG_INDEX_COL:
374 return("Index columns size too big");
375 case DB_LOCK_WAIT_TIMEOUT:
376 return("Lock wait timeout");
377 case DB_NO_REFERENCED_ROW:
378 return("Referenced key value not found");
379 case DB_ROW_IS_REFERENCED:
380 return("Row is referenced");
381 case DB_CANNOT_ADD_CONSTRAINT:
382 return("Cannot add constraint");
383 case DB_CORRUPTION:
384 return("Data structure corruption");
385 case DB_CANNOT_DROP_CONSTRAINT:
386 return("Cannot drop constraint");
387 case DB_NO_SAVEPOINT:
388 return("No such savepoint");
389 case DB_TABLESPACE_EXISTS:
390 return("Tablespace already exists");
391 case DB_TABLESPACE_DELETED:
392 return("Tablespace deleted or being deleted");
393 case DB_TABLESPACE_NOT_FOUND:
394 return("Tablespace not found");
395 case DB_LOCK_TABLE_FULL:
396 return("Lock structs have exhausted the buffer pool");
397 case DB_FOREIGN_DUPLICATE_KEY:
398 return("Foreign key activated with duplicate keys");
399 case DB_FOREIGN_EXCEED_MAX_CASCADE:
400 return("Foreign key cascade delete/update exceeds max depth");
401 case DB_TOO_MANY_CONCURRENT_TRXS:
402 return("Too many concurrent transactions");
403 case DB_UNSUPPORTED:
404 return("Unsupported");
405 case DB_INVALID_NULL:
406 return("NULL value encountered in NOT NULL column");
407 case DB_STATS_DO_NOT_EXIST:
408 return("Persistent statistics do not exist");
409 case DB_FAIL:
410 return("Failed, retry may succeed");
411 case DB_OVERFLOW:
412 return("Overflow");
413 case DB_UNDERFLOW:
414 return("Underflow");
415 case DB_STRONG_FAIL:
416 return("Failed, retry will not succeed");
417 case DB_ZIP_OVERFLOW:
418 return("Zip overflow");
419 case DB_RECORD_NOT_FOUND:
420 return("Record not found");
421 case DB_CHILD_NO_INDEX:
422 return("No index on referencing keys in referencing table");
423 case DB_PARENT_NO_INDEX:
424 return("No index on referenced keys in referenced table");
425 case DB_FTS_INVALID_DOCID:
426 return("FTS Doc ID cannot be zero");
427 case DB_INDEX_CORRUPT:
428 return("Index corrupted");
429 case DB_UNDO_RECORD_TOO_BIG:
430 return("Undo record too big");
431 case DB_END_OF_INDEX:
432 return("End of index");
433 case DB_IO_ERROR:
434 return("I/O error");
435 case DB_TABLE_IN_FK_CHECK:
436 return("Table is being used in foreign key check");
437 case DB_NOT_FOUND:
438 return("not found");
439 case DB_ONLINE_LOG_TOO_BIG:
440 return("Log size exceeded during online index creation");
441 case DB_IDENTIFIER_TOO_LONG:
442 return("Identifier name is too long");
443 case DB_FTS_EXCEED_RESULT_CACHE_LIMIT:
444 return("FTS query exceeds result cache limit");
445 case DB_TEMP_FILE_WRITE_FAIL:
446 return("Temp file write failure");
447 case DB_CANT_CREATE_GEOMETRY_OBJECT:
448 return("Can't create specificed geometry data object");
449 case DB_CANNOT_OPEN_FILE:
450 return("Cannot open a file");
451 case DB_TABLE_CORRUPT:
452 return("Table is corrupted");
453 case DB_FTS_TOO_MANY_WORDS_IN_PHRASE:
454 return("Too many words in a FTS phrase or proximity search");
455 case DB_DECRYPTION_FAILED:
456 return("Table is encrypted but decrypt failed.");
457 case DB_IO_PARTIAL_FAILED:
458 return("Partial IO failed");
459 case DB_FORCED_ABORT:
460 return("Transaction aborted by another higher priority "
461 "transaction");
462 case DB_COMPUTE_VALUE_FAILED:
463 return("Compute generated column failed");
464 case DB_NO_FK_ON_S_BASE_COL:
465 return("Cannot add foreign key on the base column "
466 "of stored column");
467 case DB_IO_NO_PUNCH_HOLE:
468 return ("File system does not support punch hole (trim) operation.");
469 case DB_PAGE_CORRUPTED:
470 return("Page read from tablespace is corrupted.");
471
472 /* do not add default: in order to produce a warning if new code
473 is added to the enum but not added here */
474 }
475
476 /* we abort here because if unknown error code is given, this could
477 mean that memory corruption has happened and someone's error-code
478 variable has been overwritten with bogus data */
479 ut_error;
480
481 /* NOT REACHED */
482 return("Unknown error");
483 }
484
485 #ifdef UNIV_PFS_MEMORY
486
487 /** Extract the basename of a file without its extension.
488 For example, extract "foo0bar" out of "/path/to/foo0bar.cc".
489 @param[in] file file path, e.g. "/path/to/foo0bar.cc"
490 @param[out] base result, e.g. "foo0bar"
491 @param[in] base_size size of the output buffer 'base', if there
492 is not enough space, then the result will be truncated, but always
493 '\0'-terminated
494 @return number of characters that would have been printed if the size
495 were unlimited (not including the final ‘\0’) */
496 size_t
ut_basename_noext(const char * file,char * base,size_t base_size)497 ut_basename_noext(
498 const char* file,
499 char* base,
500 size_t base_size)
501 {
502 /* Assuming 'file' contains something like the following,
503 extract the file name without the extenstion out of it by
504 setting 'beg' and 'len'.
505 ...mysql-trunk/storage/innobase/dict/dict0dict.cc:302
506 ^-- beg, len=9
507 */
508
509 const char* beg = strrchr(file, OS_PATH_SEPARATOR);
510
511 if (beg == NULL) {
512 beg = file;
513 } else {
514 beg++;
515 }
516
517 size_t len = strlen(beg);
518
519 const char* end = strrchr(beg, '.');
520
521 if (end != NULL) {
522 len = end - beg;
523 }
524
525 const size_t copy_len = std::min(len, base_size - 1);
526
527 memcpy(base, beg, copy_len);
528
529 base[copy_len] = '\0';
530
531 return(len);
532 }
533
534 #endif /* UNIV_PFS_MEMORY */
535
536 namespace ib {
537
operator <<(dberr_t err)538 ATTRIBUTE_COLD logger& logger::operator<<(dberr_t err)
539 {
540 m_oss << ut_strerr(err);
541 return *this;
542 }
543
~info()544 info::~info()
545 {
546 sql_print_information("InnoDB: %s", m_oss.str().c_str());
547 }
548
~warn()549 warn::~warn()
550 {
551 sql_print_warning("InnoDB: %s", m_oss.str().c_str());
552 }
553
554 /** true if error::~error() was invoked, false otherwise */
555 bool error::logged;
556
~error()557 error::~error()
558 {
559 sql_print_error("InnoDB: %s", m_oss.str().c_str());
560 logged = true;
561 }
562
563 #ifdef _MSC_VER
564 /* disable warning
565 "ib::fatal::~fatal': destructor never returns, potential memory leak"
566 on Windows.
567 */
568 #pragma warning (push)
569 #pragma warning (disable : 4722)
570 #endif
571
572 ATTRIBUTE_NORETURN
~fatal()573 fatal::~fatal()
574 {
575 sql_print_error("[FATAL] InnoDB: %s", m_oss.str().c_str());
576 abort();
577 }
578
579 #ifdef _MSC_VER
580 #pragma warning (pop)
581 #endif
582
~error_or_warn()583 error_or_warn::~error_or_warn()
584 {
585 if (m_error) {
586 sql_print_error("InnoDB: %s", m_oss.str().c_str());
587 } else {
588 sql_print_warning("InnoDB: %s", m_oss.str().c_str());
589 }
590 }
591
~fatal_or_error()592 fatal_or_error::~fatal_or_error()
593 {
594 sql_print_error(m_fatal ? "[FATAL] InnoDB: %s" : "InnoDB: %s",
595 m_oss.str().c_str());
596 if (m_fatal) {
597 abort();
598 }
599 }
600
601 } // namespace ib
602
603 #ifndef DBUG_OFF
604 static char dbug_print_buf[1024];
605
dbug_print_rec(const rec_t * rec,const rec_offs * offsets)606 const char * dbug_print_rec(const rec_t* rec, const rec_offs* offsets)
607 {
608 rec_printer r(rec, offsets);
609 strmake(dbug_print_buf, r.str().c_str(), sizeof(dbug_print_buf) - 1);
610 return dbug_print_buf;
611 }
612
dbug_print_rec(const rec_t * rec,ulint info,const rec_offs * offsets)613 const char * dbug_print_rec(const rec_t* rec, ulint info, const rec_offs* offsets)
614 {
615 rec_printer r(rec, info, offsets);
616 strmake(dbug_print_buf, r.str().c_str(), sizeof(dbug_print_buf) - 1);
617 return dbug_print_buf;
618 }
619
dbug_print_rec(const dtuple_t * tuple)620 const char * dbug_print_rec(const dtuple_t* tuple)
621 {
622 rec_printer r(tuple);
623 strmake(dbug_print_buf, r.str().c_str(), sizeof(dbug_print_buf) - 1);
624 return dbug_print_buf;
625 }
626
dbug_print_rec(const dfield_t * field,ulint n)627 const char * dbug_print_rec(const dfield_t* field, ulint n)
628 {
629 rec_printer r(field, n);
630 strmake(dbug_print_buf, r.str().c_str(), sizeof(dbug_print_buf) - 1);
631 return dbug_print_buf;
632 }
633
dbug_print_rec(const rec_t * rec,dict_index_t * index)634 const char * dbug_print_rec(const rec_t* rec, dict_index_t* index)
635 {
636 rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
637 rec_offs* offsets = offsets_;
638 rec_offs_init(offsets_);
639 mem_heap_t* tmp_heap = NULL;
640 offsets = rec_get_offsets(rec, index, offsets, index->n_core_fields,
641 ULINT_UNDEFINED, &tmp_heap);
642 rec_printer r(rec, offsets);
643 strmake(dbug_print_buf, r.str().c_str(), sizeof(dbug_print_buf) - 1);
644 return dbug_print_buf;
645 }
646 #endif /* !DBUG_OFF */
647
648 #endif /* !UNIV_INNOCHECKSUM */
649