1/*****************************************************************************
2
3Copyright (c) 1994, 2017, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/***************************************************************//**
21@file ut/ut0ut.cc
22Various utilities for Innobase.
23
24Created 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
42#ifdef _WIN32
43typedef VOID(WINAPI *time_fn)(LPFILETIME);
44static time_fn ut_get_system_time_as_file_time = GetSystemTimeAsFileTime;
45
46/*****************************************************************//**
47NOTE: The Windows epoch starts from 1601/01/01 whereas the Unix
48epoch starts from 1970/1/1. For selection of constant see:
49http://support.microsoft.com/kb/167296/ */
50#define WIN_TO_UNIX_DELTA_USEC 11644473600000000LL
51
52
53/*****************************************************************//**
54This is the Windows version of gettimeofday(2).
55@return 0 if all OK else -1 */
56static
57int
58ut_gettimeofday(
59/*============*/
60 struct timeval* tv, /*!< out: Values are relative to Unix epoch */
61 void* tz) /*!< in: not used */
62{
63 FILETIME ft;
64 int64_t tm;
65
66 if (!tv) {
67 errno = EINVAL;
68 return(-1);
69 }
70
71 ut_get_system_time_as_file_time(&ft);
72
73 tm = (int64_t) ft.dwHighDateTime << 32;
74 tm |= ft.dwLowDateTime;
75
76 ut_a(tm >= 0); /* If tm wraps over to negative, the quotient / 10
77 does not work */
78
79 tm /= 10; /* Convert from 100 nsec periods to usec */
80
81 /* If we don't convert to the Unix epoch the value for
82 struct timeval::tv_sec will overflow.*/
83 tm -= WIN_TO_UNIX_DELTA_USEC;
84
85 tv->tv_sec = (long) (tm / 1000000L);
86 tv->tv_usec = (long) (tm % 1000000L);
87
88 return(0);
89}
90#else
91/** An alias for gettimeofday(2). On Microsoft Windows, we have to
92reimplement this function. */
93#define ut_gettimeofday gettimeofday
94#endif
95
96/**********************************************************//**
97Returns system time. We do not specify the format of the time returned:
98the only way to manipulate it is to use the function ut_difftime.
99@return system time */
100ib_time_t
101ut_time(void)
102/*=========*/
103{
104 return(time(NULL));
105}
106
107
108/**********************************************************//**
109Returns system time.
110Upon successful completion, the value 0 is returned; otherwise the
111value -1 is returned and the global variable errno is set to indicate the
112error.
113@return 0 on success, -1 otherwise */
114int
115ut_usectime(
116/*========*/
117 ulint* sec, /*!< out: seconds since the Epoch */
118 ulint* ms) /*!< out: microseconds since the Epoch+*sec */
119{
120 struct timeval tv;
121 int ret;
122 int errno_gettimeofday;
123 int i;
124
125 for (i = 0; i < 10; i++) {
126
127 ret = ut_gettimeofday(&tv, NULL);
128
129 if (ret == -1) {
130 errno_gettimeofday = errno;
131 ib::error() << "gettimeofday(): "
132 << strerror(errno_gettimeofday);
133 os_thread_sleep(100000); /* 0.1 sec */
134 errno = errno_gettimeofday;
135 } else {
136 break;
137 }
138 }
139
140 if (ret != -1) {
141 *sec = (ulint) tv.tv_sec;
142 *ms = (ulint) tv.tv_usec;
143 }
144
145 return(ret);
146}
147
148/**********************************************************//**
149Returns the number of microseconds since epoch. Similar to
150time(3), the return value is also stored in *tloc, provided
151that tloc is non-NULL.
152@return us since epoch */
153uintmax_t
154ut_time_us(
155/*=======*/
156 uintmax_t* tloc) /*!< out: us since epoch, if non-NULL */
157{
158 struct timeval tv;
159 uintmax_t us;
160
161 ut_gettimeofday(&tv, NULL);
162
163 us = uintmax_t(tv.tv_sec) * 1000000 + uintmax_t(tv.tv_usec);
164
165 if (tloc != NULL) {
166 *tloc = us;
167 }
168
169 return(us);
170}
171
172/**********************************************************//**
173Returns the number of milliseconds since some epoch. The
174value may wrap around. It should only be used for heuristic
175purposes.
176@return ms since epoch */
177ulint
178ut_time_ms(void)
179/*============*/
180{
181 struct timeval tv;
182
183 ut_gettimeofday(&tv, NULL);
184
185 return(ulint(tv.tv_sec) * 1000 + ulint(tv.tv_usec / 1000));
186}
187
188/**********************************************************//**
189Returns the difference of two times in seconds.
190@return time2 - time1 expressed in seconds */
191double
192ut_difftime(
193/*========*/
194 ib_time_t time2, /*!< in: time */
195 ib_time_t time1) /*!< in: time */
196{
197 return(difftime(time2, time1));
198}
199
200#endif /* !UNIV_INNOCHECKSUM */
201
202/**********************************************************//**
203Prints a timestamp to a file. */
204void
205ut_print_timestamp(
206/*===============*/
207 FILE* file) /*!< in: file where to print */
208{
209 ulint thread_id = 0;
210
211#ifndef UNIV_INNOCHECKSUM
212 thread_id = os_thread_pf(os_thread_get_curr_id());
213#endif /* !UNIV_INNOCHECKSUM */
214
215#ifdef _WIN32
216 SYSTEMTIME cal_tm;
217
218 GetLocalTime(&cal_tm);
219
220 fprintf(file, "%d-%02d-%02d %02d:%02d:%02d %#zx",
221 (int) cal_tm.wYear,
222 (int) cal_tm.wMonth,
223 (int) cal_tm.wDay,
224 (int) cal_tm.wHour,
225 (int) cal_tm.wMinute,
226 (int) cal_tm.wSecond,
227 thread_id);
228#else
229 struct tm* cal_tm_ptr;
230 time_t tm;
231
232 struct tm cal_tm;
233 time(&tm);
234 localtime_r(&tm, &cal_tm);
235 cal_tm_ptr = &cal_tm;
236 fprintf(file, "%d-%02d-%02d %02d:%02d:%02d %#zx",
237 cal_tm_ptr->tm_year + 1900,
238 cal_tm_ptr->tm_mon + 1,
239 cal_tm_ptr->tm_mday,
240 cal_tm_ptr->tm_hour,
241 cal_tm_ptr->tm_min,
242 cal_tm_ptr->tm_sec,
243 thread_id);
244#endif
245}
246
247#ifndef UNIV_INNOCHECKSUM
248
249/**********************************************************//**
250Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */
251void
252ut_sprintf_timestamp(
253/*=================*/
254 char* buf) /*!< in: buffer where to sprintf */
255{
256#ifdef _WIN32
257 SYSTEMTIME cal_tm;
258
259 GetLocalTime(&cal_tm);
260
261 sprintf(buf, "%02d%02d%02d %2d:%02d:%02d",
262 (int) cal_tm.wYear % 100,
263 (int) cal_tm.wMonth,
264 (int) cal_tm.wDay,
265 (int) cal_tm.wHour,
266 (int) cal_tm.wMinute,
267 (int) cal_tm.wSecond);
268#else
269 struct tm* cal_tm_ptr;
270 time_t tm;
271
272 struct tm cal_tm;
273 time(&tm);
274 localtime_r(&tm, &cal_tm);
275 cal_tm_ptr = &cal_tm;
276 sprintf(buf, "%02d%02d%02d %2d:%02d:%02d",
277 cal_tm_ptr->tm_year % 100,
278 cal_tm_ptr->tm_mon + 1,
279 cal_tm_ptr->tm_mday,
280 cal_tm_ptr->tm_hour,
281 cal_tm_ptr->tm_min,
282 cal_tm_ptr->tm_sec);
283#endif
284}
285
286/*************************************************************//**
287Runs an idle loop on CPU. The argument gives the desired delay
288in microseconds on 100 MHz Pentium + Visual C++.
289@return dummy value */
290void
291ut_delay(
292/*=====*/
293 ulint delay) /*!< in: delay in microseconds on 100 MHz Pentium */
294{
295 ulint i;
296
297 HMT_low();
298
299 for (i = 0; i < delay * 50; i++) {
300 MY_RELAX_CPU();
301 UT_COMPILER_BARRIER();
302 }
303
304 HMT_medium();
305}
306
307/*************************************************************//**
308Prints the contents of a memory buffer in hex and ascii. */
309void
310ut_print_buf(
311/*=========*/
312 FILE* file, /*!< in: file where to print */
313 const void* buf, /*!< in: memory buffer */
314 ulint len) /*!< in: length of the buffer */
315{
316 const byte* data;
317 ulint i;
318
319 UNIV_MEM_ASSERT_RW(buf, len);
320
321 fprintf(file, " len " ULINTPF "; hex ", len);
322
323 for (data = (const byte*) buf, i = 0; i < len; i++) {
324 fprintf(file, "%02x", *data++);
325 }
326
327 fputs("; asc ", file);
328
329 data = (const byte*) buf;
330
331 for (i = 0; i < len; i++) {
332 int c = (int) *data++;
333 putc(isprint(c) ? c : ' ', file);
334 }
335
336 putc(';', file);
337}
338
339/*************************************************************//**
340Prints the contents of a memory buffer in hex. */
341void
342ut_print_buf_hex(
343/*=============*/
344 std::ostream& o, /*!< in/out: output stream */
345 const void* buf, /*!< in: memory buffer */
346 ulint len) /*!< in: length of the buffer */
347{
348 const byte* data;
349 ulint i;
350
351 static const char hexdigit[16] = {
352 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
353 };
354
355 UNIV_MEM_ASSERT_RW(buf, len);
356
357 o << "(0x";
358
359 for (data = static_cast<const byte*>(buf), i = 0; i < len; i++) {
360 byte b = *data++;
361 o << hexdigit[(int) b >> 16] << hexdigit[b & 15];
362 }
363
364 o << ")";
365}
366
367/*************************************************************//**
368Prints the contents of a memory buffer in hex and ascii. */
369void
370ut_print_buf(
371/*=========*/
372 std::ostream& o, /*!< in/out: output stream */
373 const void* buf, /*!< in: memory buffer */
374 ulint len) /*!< in: length of the buffer */
375{
376 const byte* data;
377 ulint i;
378
379 UNIV_MEM_ASSERT_RW(buf, len);
380
381 for (data = static_cast<const byte*>(buf), i = 0; i < len; i++) {
382 int c = static_cast<int>(*data++);
383 o << (isprint(c) ? static_cast<char>(c) : ' ');
384 }
385
386 ut_print_buf_hex(o, buf, len);
387}
388
389/*************************************************************//**
390Calculates fast the number rounded up to the nearest power of 2.
391@return first power of 2 which is >= n */
392ulint
393ut_2_power_up(
394/*==========*/
395 ulint n) /*!< in: number != 0 */
396{
397 ulint res;
398
399 res = 1;
400
401 ut_ad(n > 0);
402
403 while (res < n) {
404 res = res * 2;
405 }
406
407 return(res);
408}
409
410/** Get a fixed-length string, quoted as an SQL identifier.
411If the string contains a slash '/', the string will be
412output as two identifiers separated by a period (.),
413as in SQL database_name.identifier.
414 @param [in] trx transaction (NULL=no quotes).
415 @param [in] name table name.
416 @retval String quoted as an SQL identifier.
417*/
418std::string
419ut_get_name(
420 const trx_t* trx,
421 const char* name)
422{
423 /* 2 * NAME_LEN for database and table name,
424 and some slack for the #mysql50# prefix and quotes */
425 char buf[3 * NAME_LEN];
426 const char* bufend;
427
428 bufend = innobase_convert_name(buf, sizeof buf,
429 name, strlen(name),
430 trx ? trx->mysql_thd : NULL);
431 buf[bufend - buf] = '\0';
432 return(std::string(buf, 0, size_t(bufend - buf)));
433}
434
435/**********************************************************************//**
436Outputs a fixed-length string, quoted as an SQL identifier.
437If the string contains a slash '/', the string will be
438output as two identifiers separated by a period (.),
439as in SQL database_name.identifier. */
440void
441ut_print_name(
442/*==========*/
443 FILE* f, /*!< in: output stream */
444 const trx_t* trx, /*!< in: transaction */
445 const char* name) /*!< in: name to print */
446{
447 /* 2 * NAME_LEN for database and table name,
448 and some slack for the #mysql50# prefix and quotes */
449 char buf[3 * NAME_LEN];
450 const char* bufend;
451
452 bufend = innobase_convert_name(buf, sizeof buf,
453 name, strlen(name),
454 trx ? trx->mysql_thd : NULL);
455
456 if (fwrite(buf, 1, size_t(bufend - buf), f) != size_t(bufend - buf)) {
457 perror("fwrite");
458 }
459}
460
461/** Format a table name, quoted as an SQL identifier.
462If the name contains a slash '/', the result will contain two
463identifiers separated by a period (.), as in SQL
464database_name.table_name.
465@see table_name_t
466@param[in] name table or index name
467@param[out] formatted formatted result, will be NUL-terminated
468@param[in] formatted_size size of the buffer in bytes
469@return pointer to 'formatted' */
470char*
471ut_format_name(
472 const char* name,
473 char* formatted,
474 ulint formatted_size)
475{
476 switch (formatted_size) {
477 case 1:
478 formatted[0] = '\0';
479 /* FALL-THROUGH */
480 case 0:
481 return(formatted);
482 }
483
484 char* end;
485
486 end = innobase_convert_name(formatted, formatted_size,
487 name, strlen(name), NULL);
488
489 /* If the space in 'formatted' was completely used, then sacrifice
490 the last character in order to write '\0' at the end. */
491 if ((ulint) (end - formatted) == formatted_size) {
492 end--;
493 }
494
495 ut_a((ulint) (end - formatted) < formatted_size);
496
497 *end = '\0';
498
499 return(formatted);
500}
501
502/**********************************************************************//**
503Catenate files. */
504void
505ut_copy_file(
506/*=========*/
507 FILE* dest, /*!< in: output file */
508 FILE* src) /*!< in: input file to be appended to output */
509{
510 long len = ftell(src);
511 char buf[4096];
512
513 rewind(src);
514 do {
515 size_t maxs = len < (long) sizeof buf
516 ? (size_t) len
517 : sizeof buf;
518 size_t size = fread(buf, 1, maxs, src);
519 if (fwrite(buf, 1, size, dest) != size) {
520 perror("fwrite");
521 }
522 len -= (long) size;
523 if (size < maxs) {
524 break;
525 }
526 } while (len > 0);
527}
528
529/** Convert an error number to a human readable text message.
530The returned string is static and should not be freed or modified.
531@param[in] num InnoDB internal error number
532@return string, describing the error */
533const char*
534ut_strerr(
535 dberr_t num)
536{
537 switch (num) {
538 case DB_SUCCESS:
539 return("Success");
540 case DB_SUCCESS_LOCKED_REC:
541 return("Success, record lock created");
542 case DB_ERROR:
543 return("Generic error");
544 case DB_READ_ONLY:
545 return("Read only transaction");
546 case DB_INTERRUPTED:
547 return("Operation interrupted");
548 case DB_OUT_OF_MEMORY:
549 return("Cannot allocate memory");
550 case DB_OUT_OF_FILE_SPACE:
551 return("Out of disk space");
552 case DB_LOCK_WAIT:
553 return("Lock wait");
554 case DB_DEADLOCK:
555 return("Deadlock");
556 case DB_ROLLBACK:
557 return("Rollback");
558 case DB_DUPLICATE_KEY:
559 return("Duplicate key");
560 case DB_MISSING_HISTORY:
561 return("Required history data has been deleted");
562 case DB_CLUSTER_NOT_FOUND:
563 return("Cluster not found");
564 case DB_TABLE_NOT_FOUND:
565 return("Table not found");
566 case DB_MUST_GET_MORE_FILE_SPACE:
567 return("More file space needed");
568 case DB_TABLE_IS_BEING_USED:
569 return("Table is being used");
570 case DB_TOO_BIG_RECORD:
571 return("Record too big");
572 case DB_TOO_BIG_INDEX_COL:
573 return("Index columns size too big");
574 case DB_LOCK_WAIT_TIMEOUT:
575 return("Lock wait timeout");
576 case DB_NO_REFERENCED_ROW:
577 return("Referenced key value not found");
578 case DB_ROW_IS_REFERENCED:
579 return("Row is referenced");
580 case DB_CANNOT_ADD_CONSTRAINT:
581 return("Cannot add constraint");
582 case DB_CORRUPTION:
583 return("Data structure corruption");
584 case DB_CANNOT_DROP_CONSTRAINT:
585 return("Cannot drop constraint");
586 case DB_NO_SAVEPOINT:
587 return("No such savepoint");
588 case DB_TABLESPACE_EXISTS:
589 return("Tablespace already exists");
590 case DB_TABLESPACE_DELETED:
591 return("Tablespace deleted or being deleted");
592 case DB_TABLESPACE_TRUNCATED:
593 return("Tablespace was truncated");
594 case DB_TABLESPACE_NOT_FOUND:
595 return("Tablespace not found");
596 case DB_LOCK_TABLE_FULL:
597 return("Lock structs have exhausted the buffer pool");
598 case DB_FOREIGN_DUPLICATE_KEY:
599 return("Foreign key activated with duplicate keys");
600 case DB_FOREIGN_EXCEED_MAX_CASCADE:
601 return("Foreign key cascade delete/update exceeds max depth");
602 case DB_TOO_MANY_CONCURRENT_TRXS:
603 return("Too many concurrent transactions");
604 case DB_UNSUPPORTED:
605 return("Unsupported");
606 case DB_INVALID_NULL:
607 return("NULL value encountered in NOT NULL column");
608 case DB_STATS_DO_NOT_EXIST:
609 return("Persistent statistics do not exist");
610 case DB_FAIL:
611 return("Failed, retry may succeed");
612 case DB_OVERFLOW:
613 return("Overflow");
614 case DB_UNDERFLOW:
615 return("Underflow");
616 case DB_STRONG_FAIL:
617 return("Failed, retry will not succeed");
618 case DB_ZIP_OVERFLOW:
619 return("Zip overflow");
620 case DB_RECORD_NOT_FOUND:
621 return("Record not found");
622 case DB_CHILD_NO_INDEX:
623 return("No index on referencing keys in referencing table");
624 case DB_PARENT_NO_INDEX:
625 return("No index on referenced keys in referenced table");
626 case DB_FTS_INVALID_DOCID:
627 return("FTS Doc ID cannot be zero");
628 case DB_INDEX_CORRUPT:
629 return("Index corrupted");
630 case DB_UNDO_RECORD_TOO_BIG:
631 return("Undo record too big");
632 case DB_END_OF_INDEX:
633 return("End of index");
634 case DB_IO_ERROR:
635 return("I/O error");
636 case DB_TABLE_IN_FK_CHECK:
637 return("Table is being used in foreign key check");
638 case DB_NOT_FOUND:
639 return("not found");
640 case DB_ONLINE_LOG_TOO_BIG:
641 return("Log size exceeded during online index creation");
642 case DB_IDENTIFIER_TOO_LONG:
643 return("Identifier name is too long");
644 case DB_FTS_EXCEED_RESULT_CACHE_LIMIT:
645 return("FTS query exceeds result cache limit");
646 case DB_TEMP_FILE_WRITE_FAIL:
647 return("Temp file write failure");
648 case DB_CANT_CREATE_GEOMETRY_OBJECT:
649 return("Can't create specificed geometry data object");
650 case DB_CANNOT_OPEN_FILE:
651 return("Cannot open a file");
652 case DB_TABLE_CORRUPT:
653 return("Table is corrupted");
654 case DB_FTS_TOO_MANY_WORDS_IN_PHRASE:
655 return("Too many words in a FTS phrase or proximity search");
656 case DB_DECRYPTION_FAILED:
657 return("Table is encrypted but decrypt failed.");
658 case DB_IO_PARTIAL_FAILED:
659 return("Partial IO failed");
660 case DB_FORCED_ABORT:
661 return("Transaction aborted by another higher priority "
662 "transaction");
663 case DB_COMPUTE_VALUE_FAILED:
664 return("Compute generated column failed");
665 case DB_NO_FK_ON_S_BASE_COL:
666 return("Cannot add foreign key on the base column "
667 "of stored column");
668 case DB_IO_NO_PUNCH_HOLE:
669 return ("File system does not support punch hole (trim) operation.");
670 case DB_PAGE_CORRUPTED:
671 return("Page read from tablespace is corrupted.");
672
673 /* do not add default: in order to produce a warning if new code
674 is added to the enum but not added here */
675 }
676
677 /* we abort here because if unknown error code is given, this could
678 mean that memory corruption has happened and someone's error-code
679 variable has been overwritten with bogus data */
680 ut_error;
681
682 /* NOT REACHED */
683 return("Unknown error");
684}
685
686#ifdef UNIV_PFS_MEMORY
687
688/** Extract the basename of a file without its extension.
689For example, extract "foo0bar" out of "/path/to/foo0bar.cc".
690@param[in] file file path, e.g. "/path/to/foo0bar.cc"
691@param[out] base result, e.g. "foo0bar"
692@param[in] base_size size of the output buffer 'base', if there
693is not enough space, then the result will be truncated, but always
694'\0'-terminated
695@return number of characters that would have been printed if the size
696were unlimited (not including the final ‘\0’) */
697size_t
698ut_basename_noext(
699 const char* file,
700 char* base,
701 size_t base_size)
702{
703 /* Assuming 'file' contains something like the following,
704 extract the file name without the extenstion out of it by
705 setting 'beg' and 'len'.
706 ...mysql-trunk/storage/innobase/dict/dict0dict.cc:302
707 ^-- beg, len=9
708 */
709
710 const char* beg = strrchr(file, OS_PATH_SEPARATOR);
711
712 if (beg == NULL) {
713 beg = file;
714 } else {
715 beg++;
716 }
717
718 size_t len = strlen(beg);
719
720 const char* end = strrchr(beg, '.');
721
722 if (end != NULL) {
723 len = end - beg;
724 }
725
726 const size_t copy_len = std::min(len, base_size - 1);
727
728 memcpy(base, beg, copy_len);
729
730 base[copy_len] = '\0';
731
732 return(len);
733}
734
735#endif /* UNIV_PFS_MEMORY */
736
737namespace ib {
738
739info::~info()
740{
741 sql_print_information("InnoDB: %s", m_oss.str().c_str());
742}
743
744warn::~warn()
745{
746 sql_print_warning("InnoDB: %s", m_oss.str().c_str());
747}
748
749error::~error()
750{
751 sql_print_error("InnoDB: %s", m_oss.str().c_str());
752}
753
754#ifdef _MSC_VER
755/* disable warning
756 "ib::fatal::~fatal': destructor never returns, potential memory leak"
757 on Windows.
758*/
759#pragma warning (push)
760#pragma warning (disable : 4722)
761#endif
762
763ATTRIBUTE_NORETURN
764fatal::~fatal()
765{
766 sql_print_error("[FATAL] InnoDB: %s", m_oss.str().c_str());
767 abort();
768}
769
770#ifdef _MSC_VER
771#pragma warning (pop)
772#endif
773
774error_or_warn::~error_or_warn()
775{
776 if (m_error) {
777 sql_print_error("InnoDB: %s", m_oss.str().c_str());
778 } else {
779 sql_print_warning("InnoDB: %s", m_oss.str().c_str());
780 }
781}
782
783fatal_or_error::~fatal_or_error()
784{
785 sql_print_error(m_fatal ? "[FATAL] InnoDB: %s" : "InnoDB: %s",
786 m_oss.str().c_str());
787 if (m_fatal) {
788 abort();
789 }
790}
791
792} // namespace ib
793
794#endif /* !UNIV_INNOCHECKSUM */
795