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