1 /*****************************************************************************
2 
3 Copyright (c) 1994, 2020, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24 
25 *****************************************************************************/
26 
27 /***************************************************************//**
28 @file ut/ut0ut.cc
29 Various utilities for Innobase.
30 
31 Created 5/11/1994 Heikki Tuuri
32 ********************************************************************/
33 
34 #include "ut0ut.h"
35 
36 #ifndef UNIV_INNOCHECKSUM
37 
38 #include "ut0sort.h"
39 #include "os0thread.h" /* thread-ID */
40 
41 #ifdef UNIV_NONINL
42 #include "ut0ut.ic"
43 #endif
44 
45 #include <stdarg.h>
46 #include <string.h>
47 #include <ctype.h>
48 
49 #ifndef UNIV_HOTBACKUP
50 # include "trx0trx.h"
51 # include "ha_prototypes.h"
52 # include "mysql_com.h" /* NAME_LEN */
53 #endif /* UNIV_HOTBACKUP */
54 
55 /** A constant to prevent the compiler from optimizing ut_delay() away. */
56 UNIV_INTERN ibool	ut_always_false	= FALSE;
57 
58 #ifdef __WIN__
59 #include <mysql/innodb_priv.h> /* For sql_print_error */
60 typedef VOID(WINAPI *time_fn)(LPFILETIME);
61 static time_fn ut_get_system_time_as_file_time = GetSystemTimeAsFileTime;
62 
63 /*****************************************************************//**
64 NOTE: The Windows epoch starts from 1601/01/01 whereas the Unix
65 epoch starts from 1970/1/1. For selection of constant see:
66 http://support.microsoft.com/kb/167296/ */
67 #define WIN_TO_UNIX_DELTA_USEC  ((ib_int64_t) 11644473600000000ULL)
68 
69 
70 /**
71 Initialise highest available time resolution API on Windows
72 @return 0 if all OK else -1 */
73 int
ut_win_init_time()74 ut_win_init_time()
75 {
76 	HMODULE h = LoadLibrary("kernel32.dll");
77 	if (h != NULL)
78 	{
79 		time_fn pfn = (time_fn)GetProcAddress(h, "GetSystemTimePreciseAsFileTime");
80 		if (pfn != NULL)
81 		{
82 			ut_get_system_time_as_file_time = pfn;
83 		}
84 		return false;
85 	}
86 	DWORD error = GetLastError();
87   sql_print_error(
88 		"LoadLibrary(\"kernel32.dll\") failed: GetLastError returns %lu", error);
89 	return(-1);
90 }
91 
92 /*****************************************************************//**
93 This is the Windows version of gettimeofday(2).
94 @return	0 if all OK else -1 */
95 static
96 int
ut_gettimeofday(struct timeval * tv,void * tz)97 ut_gettimeofday(
98 /*============*/
99 	struct timeval*	tv,	/*!< out: Values are relative to Unix epoch */
100 	void*		tz)	/*!< in: not used */
101 {
102 	FILETIME	ft;
103 	ib_int64_t	tm;
104 
105 	if (!tv) {
106 		errno = EINVAL;
107 		return(-1);
108 	}
109 
110 	ut_get_system_time_as_file_time(&ft);
111 
112 	tm = (ib_int64_t) ft.dwHighDateTime << 32;
113 	tm |= ft.dwLowDateTime;
114 
115 	ut_a(tm >= 0);	/* If tm wraps over to negative, the quotient / 10
116 			does not work */
117 
118 	tm /= 10;	/* Convert from 100 nsec periods to usec */
119 
120 	/* If we don't convert to the Unix epoch the value for
121 	struct timeval::tv_sec will overflow.*/
122 	tm -= WIN_TO_UNIX_DELTA_USEC;
123 
124 	tv->tv_sec  = (long) (tm / 1000000L);
125 	tv->tv_usec = (long) (tm % 1000000L);
126 
127 	return(0);
128 }
129 #else
130 /** An alias for gettimeofday(2).  On Microsoft Windows, we have to
131 reimplement this function. */
132 #define	ut_gettimeofday		gettimeofday
133 #endif
134 
135 /**********************************************************//**
136 Returns system time. We do not specify the format of the time returned:
137 the only way to manipulate it is to use the function ut_difftime.
138 @return	system time */
139 UNIV_INTERN
140 ib_time_t
ut_time(void)141 ut_time(void)
142 /*=========*/
143 {
144 	return(time(NULL));
145 }
146 
147 #ifndef UNIV_HOTBACKUP
148 /**********************************************************//**
149 Returns system time.
150 Upon successful completion, the value 0 is returned; otherwise the
151 value -1 is returned and the global variable errno is set to indicate the
152 error.
153 @return	0 on success, -1 otherwise */
154 UNIV_INTERN
155 int
ut_usectime(ulint * sec,ulint * ms)156 ut_usectime(
157 /*========*/
158 	ulint*	sec,	/*!< out: seconds since the Epoch */
159 	ulint*	ms)	/*!< out: microseconds since the Epoch+*sec */
160 {
161 	struct timeval	tv;
162 	int		ret;
163 	int		errno_gettimeofday;
164 	int		i;
165 
166 	for (i = 0; i < 10; i++) {
167 
168 		ret = ut_gettimeofday(&tv, NULL);
169 
170 		if (ret == -1) {
171 			errno_gettimeofday = errno;
172 			ut_print_timestamp(stderr);
173 			fprintf(stderr, "  InnoDB: gettimeofday(): %s\n",
174 				strerror(errno_gettimeofday));
175 			os_thread_sleep(100000);  /* 0.1 sec */
176 			errno = errno_gettimeofday;
177 		} else {
178 			break;
179 		}
180 	}
181 
182 	if (ret != -1) {
183 		*sec = (ulint) tv.tv_sec;
184 		*ms  = (ulint) tv.tv_usec;
185 	}
186 
187 	return(ret);
188 }
189 
190 /**********************************************************//**
191 Returns the number of microseconds since epoch. Similar to
192 time(3), the return value is also stored in *tloc, provided
193 that tloc is non-NULL.
194 @return	us since epoch */
195 UNIV_INTERN
196 ullint
ut_time_us(ullint * tloc)197 ut_time_us(
198 /*=======*/
199 	ullint*	tloc)	/*!< out: us since epoch, if non-NULL */
200 {
201 	struct timeval	tv;
202 	ullint		us;
203 
204 	ut_gettimeofday(&tv, NULL);
205 
206 	us = (ullint) tv.tv_sec * 1000000 + tv.tv_usec;
207 
208 	if (tloc != NULL) {
209 		*tloc = us;
210 	}
211 
212 	return(us);
213 }
214 
215 /**********************************************************//**
216 Returns the number of milliseconds since some epoch.  The
217 value may wrap around.  It should only be used for heuristic
218 purposes.
219 @return	ms since epoch */
220 UNIV_INTERN
221 ulint
ut_time_ms(void)222 ut_time_ms(void)
223 /*============*/
224 {
225 	struct timeval	tv;
226 
227 	ut_gettimeofday(&tv, NULL);
228 
229 	return((ulint) tv.tv_sec * 1000 + tv.tv_usec / 1000);
230 }
231 #endif /* !UNIV_HOTBACKUP */
232 
233 /** Returns the number of milliseconds since some epoch using monotonic clock.
234 The value may wrap around.  This should not be used as an accurate Time Of Day.
235 It is only intended to be used as a means of calculating transient elapsed or
236 projected time that will not be influenced by changes to the systems real time
237 clock.  Returns a small structure that contains the result so as to poison the
238 code and reveal any changes that might later be introduced by upstream.
239 */
240 ut_monotonic_time
ut_monotonic_time_ms(void)241 ut_monotonic_time_ms(void) {
242 	timespec	  tp;
243 	ut_monotonic_time ret;
244 	clock_gettime(CLOCK_MONOTONIC, &tp);
245 
246 	ret.ms = (ulint) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
247 	return ret;
248 }
249 
250 /**********************************************************//**
251 Returns the difference of two times in seconds.
252 @return	time2 - time1 expressed in seconds */
253 UNIV_INTERN
254 double
ut_difftime(ib_time_t time2,ib_time_t time1)255 ut_difftime(
256 /*========*/
257 	ib_time_t	time2,	/*!< in: time */
258 	ib_time_t	time1)	/*!< in: time */
259 {
260 	return(difftime(time2, time1));
261 }
262 
263 #endif /* !UNIV_INNOCHECKSUM */
264 
265 /**********************************************************//**
266 Prints a timestamp to a file. */
267 UNIV_INTERN
268 void
ut_print_timestamp(FILE * file)269 ut_print_timestamp(
270 /*===============*/
271 	FILE*  file) /*!< in: file where to print */
272 {
273 	ulint thread_id = 0;
274 
275 #ifndef UNIV_INNOCHECKSUM
276 	thread_id = os_thread_pf(os_thread_get_curr_id());
277 #endif
278 
279 #ifdef __WIN__
280 	SYSTEMTIME cal_tm;
281 
282 	GetLocalTime(&cal_tm);
283 
284 	fprintf(file, "%d-%02d-%02d %02d:%02d:%02d %lx",
285 		(int) cal_tm.wYear,
286 		(int) cal_tm.wMonth,
287 		(int) cal_tm.wDay,
288 		(int) cal_tm.wHour,
289 		(int) cal_tm.wMinute,
290 		(int) cal_tm.wSecond,
291 		thread_id);
292 #else
293 	struct tm* cal_tm_ptr;
294 	time_t	   tm;
295 
296 #ifdef HAVE_LOCALTIME_R
297 	struct tm  cal_tm;
298 	time(&tm);
299 	localtime_r(&tm, &cal_tm);
300 	cal_tm_ptr = &cal_tm;
301 #else
302 	time(&tm);
303 	cal_tm_ptr = localtime(&tm);
304 #endif
305 	fprintf(file, "%d-%02d-%02d %02d:%02d:%02d %lx",
306 		cal_tm_ptr->tm_year + 1900,
307 		cal_tm_ptr->tm_mon + 1,
308 		cal_tm_ptr->tm_mday,
309 		cal_tm_ptr->tm_hour,
310 		cal_tm_ptr->tm_min,
311 		cal_tm_ptr->tm_sec,
312 		thread_id);
313 #endif
314 }
315 
316 #ifndef UNIV_INNOCHECKSUM
317 
318 /**********************************************************//**
319 Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */
320 UNIV_INTERN
321 void
ut_sprintf_timestamp(char * buf)322 ut_sprintf_timestamp(
323 /*=================*/
324 	char*	buf) /*!< in: buffer where to sprintf */
325 {
326 #ifdef __WIN__
327 	SYSTEMTIME cal_tm;
328 
329 	GetLocalTime(&cal_tm);
330 
331 	sprintf(buf, "%02d%02d%02d %2d:%02d:%02d",
332 		(int) cal_tm.wYear % 100,
333 		(int) cal_tm.wMonth,
334 		(int) cal_tm.wDay,
335 		(int) cal_tm.wHour,
336 		(int) cal_tm.wMinute,
337 		(int) cal_tm.wSecond);
338 #else
339 	struct tm* cal_tm_ptr;
340 	time_t	   tm;
341 
342 #ifdef HAVE_LOCALTIME_R
343 	struct tm  cal_tm;
344 	time(&tm);
345 	localtime_r(&tm, &cal_tm);
346 	cal_tm_ptr = &cal_tm;
347 #else
348 	time(&tm);
349 	cal_tm_ptr = localtime(&tm);
350 #endif
351 	sprintf(buf, "%02d%02d%02d %2d:%02d:%02d",
352 		cal_tm_ptr->tm_year % 100,
353 		cal_tm_ptr->tm_mon + 1,
354 		cal_tm_ptr->tm_mday,
355 		cal_tm_ptr->tm_hour,
356 		cal_tm_ptr->tm_min,
357 		cal_tm_ptr->tm_sec);
358 #endif
359 }
360 
361 #ifdef UNIV_HOTBACKUP
362 /**********************************************************//**
363 Sprintfs a timestamp to a buffer with no spaces and with ':' characters
364 replaced by '_'. */
365 UNIV_INTERN
366 void
ut_sprintf_timestamp_without_extra_chars(char * buf)367 ut_sprintf_timestamp_without_extra_chars(
368 /*=====================================*/
369 	char*	buf) /*!< in: buffer where to sprintf */
370 {
371 #ifdef __WIN__
372 	SYSTEMTIME cal_tm;
373 
374 	GetLocalTime(&cal_tm);
375 
376 	sprintf(buf, "%02d%02d%02d_%2d_%02d_%02d",
377 		(int) cal_tm.wYear % 100,
378 		(int) cal_tm.wMonth,
379 		(int) cal_tm.wDay,
380 		(int) cal_tm.wHour,
381 		(int) cal_tm.wMinute,
382 		(int) cal_tm.wSecond);
383 #else
384 	struct tm* cal_tm_ptr;
385 	time_t	   tm;
386 
387 #ifdef HAVE_LOCALTIME_R
388 	struct tm  cal_tm;
389 	time(&tm);
390 	localtime_r(&tm, &cal_tm);
391 	cal_tm_ptr = &cal_tm;
392 #else
393 	time(&tm);
394 	cal_tm_ptr = localtime(&tm);
395 #endif
396 	sprintf(buf, "%02d%02d%02d_%2d_%02d_%02d",
397 		cal_tm_ptr->tm_year % 100,
398 		cal_tm_ptr->tm_mon + 1,
399 		cal_tm_ptr->tm_mday,
400 		cal_tm_ptr->tm_hour,
401 		cal_tm_ptr->tm_min,
402 		cal_tm_ptr->tm_sec);
403 #endif
404 }
405 
406 /**********************************************************//**
407 Returns current year, month, day. */
408 UNIV_INTERN
409 void
ut_get_year_month_day(ulint * year,ulint * month,ulint * day)410 ut_get_year_month_day(
411 /*==================*/
412 	ulint*	year,	/*!< out: current year */
413 	ulint*	month,	/*!< out: month */
414 	ulint*	day)	/*!< out: day */
415 {
416 #ifdef __WIN__
417 	SYSTEMTIME cal_tm;
418 
419 	GetLocalTime(&cal_tm);
420 
421 	*year = (ulint) cal_tm.wYear;
422 	*month = (ulint) cal_tm.wMonth;
423 	*day = (ulint) cal_tm.wDay;
424 #else
425 	struct tm* cal_tm_ptr;
426 	time_t	   tm;
427 
428 #ifdef HAVE_LOCALTIME_R
429 	struct tm  cal_tm;
430 	time(&tm);
431 	localtime_r(&tm, &cal_tm);
432 	cal_tm_ptr = &cal_tm;
433 #else
434 	time(&tm);
435 	cal_tm_ptr = localtime(&tm);
436 #endif
437 	*year = (ulint) cal_tm_ptr->tm_year + 1900;
438 	*month = (ulint) cal_tm_ptr->tm_mon + 1;
439 	*day = (ulint) cal_tm_ptr->tm_mday;
440 #endif
441 }
442 #endif /* UNIV_HOTBACKUP */
443 
444 #ifndef UNIV_HOTBACKUP
445 /*************************************************************//**
446 Runs an idle loop on CPU. The argument gives the desired delay
447 in microseconds on 100 MHz Pentium + Visual C++.
448 @return	dummy value */
449 UNIV_INTERN
450 ulint
ut_delay(ulint delay)451 ut_delay(
452 /*=====*/
453 	ulint	delay)	/*!< in: delay in microseconds on 100 MHz Pentium */
454 {
455 	ulint	i, j;
456 
457 	j = 0;
458 
459 	for (i = 0; i < delay * 50; i++) {
460 		j += i;
461 		UT_RELAX_CPU();
462 	}
463 
464 	if (ut_always_false) {
465 		ut_always_false = (ibool) j;
466 	}
467 
468 	return(j);
469 }
470 #endif /* !UNIV_HOTBACKUP */
471 
472 /*************************************************************//**
473 Prints the contents of a memory buffer in hex and ascii. */
474 UNIV_INTERN
475 void
ut_print_buf(FILE * file,const void * buf,ulint len)476 ut_print_buf(
477 /*=========*/
478 	FILE*		file,	/*!< in: file where to print */
479 	const void*	buf,	/*!< in: memory buffer */
480 	ulint		len)	/*!< in: length of the buffer */
481 {
482 	const byte*	data;
483 	ulint		i;
484 
485 	UNIV_MEM_ASSERT_RW(buf, len);
486 
487 	fprintf(file, " len %lu; hex ", len);
488 
489 	for (data = (const byte*) buf, i = 0; i < len; i++) {
490 		fprintf(file, "%02lx", (ulong)*data++);
491 	}
492 
493 	fputs("; asc ", file);
494 
495 	data = (const byte*) buf;
496 
497 	for (i = 0; i < len; i++) {
498 		int	c = (int) *data++;
499 		putc(isprint(c) ? c : ' ', file);
500 	}
501 
502 	putc(';', file);
503 }
504 
505 /**********************************************************************//**
506 Sort function for ulint arrays. */
507 UNIV_INTERN
508 void
ut_ulint_sort(ulint * arr,ulint * aux_arr,ulint low,ulint high)509 ut_ulint_sort(
510 /*==========*/
511 	ulint*	arr,		/*!< in/out: array to sort */
512 	ulint*	aux_arr,	/*!< in/out: aux array to use in sort */
513 	ulint	low,		/*!< in: lower bound */
514 	ulint	high)		/*!< in: upper bound */
515 {
516 	UT_SORT_FUNCTION_BODY(ut_ulint_sort, arr, aux_arr, low, high,
517 			      ut_ulint_cmp);
518 }
519 
520 /*************************************************************//**
521 Calculates fast the number rounded up to the nearest power of 2.
522 @return	first power of 2 which is >= n */
523 UNIV_INTERN
524 ulint
ut_2_power_up(ulint n)525 ut_2_power_up(
526 /*==========*/
527 	ulint	n)	/*!< in: number != 0 */
528 {
529 	ulint	res;
530 
531 	res = 1;
532 
533 	ut_ad(n > 0);
534 
535 	while (res < n) {
536 		res = res * 2;
537 	}
538 
539 	return(res);
540 }
541 
542 /**********************************************************************//**
543 Outputs a NUL-terminated file name, quoted with apostrophes. */
544 UNIV_INTERN
545 void
ut_print_filename(FILE * f,const char * name)546 ut_print_filename(
547 /*==============*/
548 	FILE*		f,	/*!< in: output stream */
549 	const char*	name)	/*!< in: name to print */
550 {
551 	putc('\'', f);
552 	for (;;) {
553 		int	c = *name++;
554 		switch (c) {
555 		case 0:
556 			goto done;
557 		case '\'':
558 			putc(c, f);
559 			/* fall through */
560 		default:
561 			putc(c, f);
562 		}
563 	}
564 done:
565 	putc('\'', f);
566 }
567 #ifndef UNIV_HOTBACKUP
568 /**********************************************************************//**
569 Outputs a fixed-length string, quoted as an SQL identifier.
570 If the string contains a slash '/', the string will be
571 output as two identifiers separated by a period (.),
572 as in SQL database_name.identifier. */
573 UNIV_INTERN
574 void
ut_print_name(FILE * f,const trx_t * trx,ibool table_id,const char * name)575 ut_print_name(
576 /*==========*/
577 	FILE*		f,	/*!< in: output stream */
578 	const trx_t*	trx,	/*!< in: transaction */
579 	ibool		table_id,/*!< in: TRUE=print a table name,
580 				FALSE=print other identifier */
581 	const char*	name)	/*!< in: name to print */
582 {
583 	ut_print_namel(f, trx, table_id, name, strlen(name));
584 }
585 
586 /**********************************************************************//**
587 Outputs a fixed-length string, quoted as an SQL identifier.
588 If the string contains a slash '/', the string will be
589 output as two identifiers separated by a period (.),
590 as in SQL database_name.identifier. */
591 UNIV_INTERN
592 void
ut_print_namel(FILE * f,const trx_t * trx,ibool table_id,const char * name,ulint namelen)593 ut_print_namel(
594 /*===========*/
595 	FILE*		f,	/*!< in: output stream */
596 	const trx_t*	trx,	/*!< in: transaction (NULL=no quotes) */
597 	ibool		table_id,/*!< in: TRUE=print a table name,
598 				FALSE=print other identifier */
599 	const char*	name,	/*!< in: name to print */
600 	ulint		namelen)/*!< in: length of name */
601 {
602 	/* 2 * NAME_LEN for database and table name,
603 	and some slack for the #mysql50# prefix and quotes */
604 	char		buf[3 * NAME_LEN];
605 	const char*	bufend;
606 
607 	bufend = innobase_convert_name(buf, sizeof buf,
608 				       name, namelen,
609 				       trx ? trx->mysql_thd : NULL,
610 				       table_id);
611 
612 	fwrite(buf, 1, bufend - buf, f);
613 }
614 
615 /**********************************************************************//**
616 Formats a table or index name, quoted as an SQL identifier. If the name
617 contains a slash '/', the result will contain two identifiers separated by
618 a period (.), as in SQL database_name.identifier.
619 @return pointer to 'formatted' */
620 UNIV_INTERN
621 char*
ut_format_name(const char * name,ibool is_table,char * formatted,ulint formatted_size)622 ut_format_name(
623 /*===========*/
624 	const char*	name,		/*!< in: table or index name, must be
625 					'\0'-terminated */
626 	ibool		is_table,	/*!< in: if TRUE then 'name' is a table
627 					name */
628 	char*		formatted,	/*!< out: formatted result, will be
629 					'\0'-terminated */
630 	ulint		formatted_size)	/*!< out: no more than this number of
631 					bytes will be written to 'formatted' */
632 {
633 	switch (formatted_size) {
634 	case 1:
635 		formatted[0] = '\0';
636 		/* FALL-THROUGH */
637 	case 0:
638 		return(formatted);
639 	}
640 
641 	char*	end;
642 
643 	end = innobase_convert_name(formatted, formatted_size,
644 				    name, strlen(name), NULL, is_table);
645 
646 	/* If the space in 'formatted' was completely used, then sacrifice
647 	the last character in order to write '\0' at the end. */
648 	if ((ulint) (end - formatted) == formatted_size) {
649 		end--;
650 	}
651 
652 	ut_a((ulint) (end - formatted) < formatted_size);
653 
654 	*end = '\0';
655 
656 	return(formatted);
657 }
658 
659 /**********************************************************************//**
660 Catenate files. */
661 UNIV_INTERN
662 void
ut_copy_file(FILE * dest,FILE * src)663 ut_copy_file(
664 /*=========*/
665 	FILE*	dest,	/*!< in: output file */
666 	FILE*	src)	/*!< in: input file to be appended to output */
667 {
668 	long	len = ftell(src);
669 	char	buf[4096];
670 
671 	rewind(src);
672 	do {
673 		size_t	maxs = len < (long) sizeof buf
674 			? (size_t) len
675 			: sizeof buf;
676 		size_t	size = fread(buf, 1, maxs, src);
677 		fwrite(buf, 1, size, dest);
678 		len -= (long) size;
679 		if (size < maxs) {
680 			break;
681 		}
682 	} while (len > 0);
683 }
684 #endif /* !UNIV_HOTBACKUP */
685 
686 #ifdef __WIN__
687 # include <stdarg.h>
688 /**********************************************************************//**
689 A substitute for vsnprintf(3), formatted output conversion into
690 a limited buffer. Note: this function DOES NOT return the number of
691 characters that would have been printed if the buffer was unlimited because
692 VC's _vsnprintf() returns -1 in this case and we would need to call
693 _vscprintf() in addition to estimate that but we would need another copy
694 of "ap" for that and VC does not provide va_copy(). */
695 UNIV_INTERN
696 void
ut_vsnprintf(char * str,size_t size,const char * fmt,va_list ap)697 ut_vsnprintf(
698 /*=========*/
699 	char*		str,	/*!< out: string */
700 	size_t		size,	/*!< in: str size */
701 	const char*	fmt,	/*!< in: format */
702 	va_list		ap)	/*!< in: format values */
703 {
704 	_vsnprintf(str, size, fmt, ap);
705 	str[size - 1] = '\0';
706 }
707 
708 /**********************************************************************//**
709 A substitute for snprintf(3), formatted output conversion into
710 a limited buffer.
711 @return number of characters that would have been printed if the size
712 were unlimited, not including the terminating '\0'. */
713 UNIV_INTERN
714 int
ut_snprintf(char * str,size_t size,const char * fmt,...)715 ut_snprintf(
716 /*========*/
717 	char*		str,	/*!< out: string */
718 	size_t		size,	/*!< in: str size */
719 	const char*	fmt,	/*!< in: format */
720 	...)			/*!< in: format values */
721 {
722 	int	res;
723 	va_list	ap1;
724 	va_list	ap2;
725 
726 	va_start(ap1, fmt);
727 	va_start(ap2, fmt);
728 
729 	res = _vscprintf(fmt, ap1);
730 	ut_a(res != -1);
731 
732 	if (size > 0) {
733 		_vsnprintf(str, size, fmt, ap2);
734 
735 		if ((size_t) res >= size) {
736 			str[size - 1] = '\0';
737 		}
738 	}
739 
740 	va_end(ap1);
741 	va_end(ap2);
742 
743 	return(res);
744 }
745 #endif /* __WIN__ */
746 
747 /*************************************************************//**
748 Convert an error number to a human readable text message. The
749 returned string is static and should not be freed or modified.
750 @return	string, describing the error */
751 UNIV_INTERN
752 const char*
ut_strerr(dberr_t num)753 ut_strerr(
754 /*======*/
755 	dberr_t	num)	/*!< in: error number */
756 {
757 	switch (num) {
758 	case DB_SUCCESS:
759 		return("Success");
760 	case DB_SUCCESS_LOCKED_REC:
761 		return("Success, record lock created");
762 	case DB_ERROR:
763 		return("Generic error");
764 	case DB_READ_ONLY:
765 		return("Read only transaction");
766 	case DB_INTERRUPTED:
767 		return("Operation interrupted");
768 	case DB_OUT_OF_MEMORY:
769 		return("Cannot allocate memory");
770 	case DB_OUT_OF_FILE_SPACE:
771 		return("Out of disk space");
772 	case DB_LOCK_WAIT:
773 		return("Lock wait");
774 	case DB_DEADLOCK:
775 		return("Deadlock");
776 	case DB_ROLLBACK:
777 		return("Rollback");
778 	case DB_DUPLICATE_KEY:
779 		return("Duplicate key");
780 	case DB_QUE_THR_SUSPENDED:
781 		return("The queue thread has been suspended");
782 	case DB_MISSING_HISTORY:
783 		return("Required history data has been deleted");
784 	case DB_CLUSTER_NOT_FOUND:
785 		return("Cluster not found");
786 	case DB_TABLE_NOT_FOUND:
787 		return("Table not found");
788 	case DB_MUST_GET_MORE_FILE_SPACE:
789 		return("More file space needed");
790 	case DB_TABLE_IS_BEING_USED:
791 		return("Table is being used");
792 	case DB_TOO_BIG_RECORD:
793 		return("Record too big");
794 	case DB_TOO_BIG_INDEX_COL:
795 		return("Index columns size too big");
796 	case DB_LOCK_WAIT_TIMEOUT:
797 		return("Lock wait timeout");
798 	case DB_NO_REFERENCED_ROW:
799 		return("Referenced key value not found");
800 	case DB_ROW_IS_REFERENCED:
801 		return("Row is referenced");
802 	case DB_CANNOT_ADD_CONSTRAINT:
803 		return("Cannot add constraint");
804 	case DB_CORRUPTION:
805 		return("Data structure corruption");
806 	case DB_CANNOT_DROP_CONSTRAINT:
807 		return("Cannot drop constraint");
808 	case DB_NO_SAVEPOINT:
809 		return("No such savepoint");
810 	case DB_TABLESPACE_EXISTS:
811 		return("Tablespace already exists");
812 	case DB_TABLESPACE_DELETED:
813 		return("Tablespace deleted or being deleted");
814 	case DB_TABLESPACE_NOT_FOUND:
815 		return("Tablespace not found");
816 	case DB_LOCK_TABLE_FULL:
817 		return("Lock structs have exhausted the buffer pool");
818 	case DB_FOREIGN_DUPLICATE_KEY:
819 		return("Foreign key activated with duplicate keys");
820 	case DB_FOREIGN_EXCEED_MAX_CASCADE:
821 		return("Foreign key cascade delete/update exceeds max depth");
822 	case DB_TOO_MANY_CONCURRENT_TRXS:
823 		return("Too many concurrent transactions");
824 	case DB_UNSUPPORTED:
825 		return("Unsupported");
826 	case DB_INVALID_NULL:
827 		return("NULL value encountered in NOT NULL column");
828 	case DB_STATS_DO_NOT_EXIST:
829 		return("Persistent statistics do not exist");
830 	case DB_FAIL:
831 		return("Failed, retry may succeed");
832 	case DB_OVERFLOW:
833 		return("Overflow");
834 	case DB_UNDERFLOW:
835 		return("Underflow");
836 	case DB_STRONG_FAIL:
837 		return("Failed, retry will not succeed");
838 	case DB_ZIP_OVERFLOW:
839 		return("Zip overflow");
840 	case DB_RECORD_NOT_FOUND:
841 		return("Record not found");
842 	case DB_CHILD_NO_INDEX:
843 		return("No index on referencing keys in referencing table");
844 	case DB_PARENT_NO_INDEX:
845 		return("No index on referenced keys in referenced table");
846 	case DB_FTS_INVALID_DOCID:
847 		return("FTS Doc ID cannot be zero");
848 	case DB_INDEX_CORRUPT:
849 		return("Index corrupted");
850 	case DB_UNDO_RECORD_TOO_BIG:
851 		return("Undo record too big");
852 	case DB_END_OF_INDEX:
853 		return("End of index");
854 	case DB_IO_ERROR:
855 		return("I/O error");
856 	case DB_TABLE_IN_FK_CHECK:
857 		return("Table is being used in foreign key check");
858 	case DB_DATA_MISMATCH:
859 		return("data mismatch");
860 	case DB_SCHEMA_NOT_LOCKED:
861 		return("schema not locked");
862 	case DB_NOT_FOUND:
863 		return("not found");
864 	case DB_ONLINE_LOG_TOO_BIG:
865 		return("Log size exceeded during online index creation");
866 	case DB_DICT_CHANGED:
867 		return("Table dictionary has changed");
868 	case DB_IDENTIFIER_TOO_LONG:
869 		return("Identifier name is too long");
870 	case DB_FTS_EXCEED_RESULT_CACHE_LIMIT:
871 		return("FTS query exceeds result cache limit");
872 	case DB_TEMP_FILE_WRITE_FAILURE:
873 		return("Temp file write failure");
874 	case DB_FTS_TOO_MANY_WORDS_IN_PHRASE:
875 		return("Too many words in a FTS phrase or proximity search");
876 	case DB_TOO_BIG_FOR_REDO:
877 		return("BLOB record length is greater than 10%% of redo log");
878 	case DB_FTS_TOO_MANY_NESTED_EXP:
879 		return("Too many nested sub-expressions in a full-text search");
880 
881 	/* do not add default: in order to produce a warning if new code
882 	is added to the enum but not added here */
883 	}
884 
885 	/* we abort here because if unknown error code is given, this could
886 	mean that memory corruption has happened and someone's error-code
887 	variable has been overwritten with bogus data */
888 	ut_error;
889 
890 	/* NOT REACHED */
891 	return("Unknown error");
892 }
893 #endif /* !UNIV_INNOCHECKSUM */
894