Commit e8de094a authored by vasil's avatar vasil

branches/zip:

Add innodb_locks.lock_data column and some relevant tests.
For record locks this column represents the ordering fields of the
locked row in a human readable, SQL-valid, format.

Approved by:	Marko
parent c8bbe754
...@@ -478,6 +478,15 @@ static ST_FIELD_INFO innodb_locks_fields_info[] = ...@@ -478,6 +478,15 @@ static ST_FIELD_INFO innodb_locks_fields_info[] =
STRUCT_FLD(old_name, ""), STRUCT_FLD(old_name, ""),
STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
#define IDX_LOCK_DATA 9
{STRUCT_FLD(field_name, "lock_data"),
STRUCT_FLD(field_length, TRX_I_S_LOCK_DATA_MAX_LEN),
STRUCT_FLD(field_type, MYSQL_TYPE_STRING),
STRUCT_FLD(value, 0),
STRUCT_FLD(field_flags, MY_I_S_MAYBE_NULL),
STRUCT_FLD(old_name, ""),
STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
END_OF_ST_FIELD_INFO END_OF_ST_FIELD_INFO
}; };
...@@ -550,6 +559,10 @@ fill_innodb_locks_from_cache( ...@@ -550,6 +559,10 @@ fill_innodb_locks_from_cache(
OK(field_store_ulint(fields[IDX_LOCK_REC], OK(field_store_ulint(fields[IDX_LOCK_REC],
row->lock_rec)); row->lock_rec));
/* lock_data */
OK(field_store_string(fields[IDX_LOCK_DATA],
row->lock_data));
OK(schema_table_store_record(thd, table)); OK(schema_table_store_record(thd, table));
} }
......
...@@ -269,6 +269,27 @@ row_search_index_entry( ...@@ -269,6 +269,27 @@ row_search_index_entry(
No new latches may be obtained while the kernel mutex is reserved. No new latches may be obtained while the kernel mutex is reserved.
However, the kernel mutex can be reserved while latches are owned. */ However, the kernel mutex can be reserved while latches are owned. */
/***********************************************************************
Formats the raw data in "data" (in InnoDB on-disk format) using
"dict_field" and writes the result to "buf".
Not more than "buf_size" bytes are written to "buf".
The result is always '\0'-terminated (provided buf_size > 0) and the
number of bytes that were written to "buf" is returned (including the
terminating '\0'). */
ulint
row_raw_format(
/*===========*/
/* out: number of bytes
that were written */
const char* data, /* in: raw data */
ulint data_len, /* in: raw data length
in bytes */
const dict_field_t* dict_field, /* in: index field */
char* buf, /* out: output buffer */
ulint buf_size); /* in: output buffer size
in bytes */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "row0row.ic" #include "row0row.ic"
#endif #endif
......
...@@ -14,6 +14,10 @@ Created July 17, 2007 Vasil Dimov ...@@ -14,6 +14,10 @@ Created July 17, 2007 Vasil Dimov
#include "univ.i" #include "univ.i"
#include "ut0ut.h" #include "ut0ut.h"
/* the maximum length of a string that can be stored in
i_s_locks_row_t::lock_data */
#define TRX_I_S_LOCK_DATA_MAX_LEN 8192
typedef struct i_s_locks_row_struct i_s_locks_row_t; typedef struct i_s_locks_row_struct i_s_locks_row_t;
typedef struct i_s_hash_chain_struct i_s_hash_chain_t; typedef struct i_s_hash_chain_struct i_s_hash_chain_t;
...@@ -34,6 +38,8 @@ struct i_s_locks_row_struct { ...@@ -34,6 +38,8 @@ struct i_s_locks_row_struct {
ulint lock_space; ulint lock_space;
ulint lock_page; ulint lock_page;
ulint lock_rec; ulint lock_rec;
const char* lock_data;
/* The following are auxiliary and not included in the table */ /* The following are auxiliary and not included in the table */
ullint lock_table_id; ullint lock_table_id;
i_s_hash_chain_t hash_chain; /* this object is added to the hash i_s_hash_chain_t hash_chain; /* this object is added to the hash
......
...@@ -83,6 +83,16 @@ memory is read outside the allocated blocks. */ ...@@ -83,6 +83,16 @@ memory is read outside the allocated blocks. */
#define UNIV_INIT_MEM_TO_ZERO #define UNIV_INIT_MEM_TO_ZERO
*/ */
/* When this macro is defined then additional test functions will be
compiled. These functions live at the end of each relevant source file
and have "test_" prefix. These functions are not called from anywhere in
the code, they can be called from gdb after
innobase_start_or_create_for_mysql() has executed using the call
command. Not tested on Windows. */
/*
#define UNIV_COMPILE_TEST_FUNCS
*/
#if 0 #if 0
#define UNIV_DEBUG_VALGRIND /* Enable extra #define UNIV_DEBUG_VALGRIND /* Enable extra
Valgrind instrumentation */ Valgrind instrumentation */
......
...@@ -110,4 +110,35 @@ ut_dbg_stop_thread( ...@@ -110,4 +110,35 @@ ut_dbg_stop_thread(
#define UT_NOT_USED(A) A = A #define UT_NOT_USED(A) A = A
#ifdef UNIV_COMPILE_TEST_FUNCS
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
/* structure used for recording usage statistics */
typedef struct speedo_struct {
struct rusage ru;
struct timeval tv;
} speedo_t;
/***********************************************************************
Resets a speedo (records the current time in it). */
void
speedo_reset(
/*=========*/
speedo_t* speedo); /* out: speedo */
/***********************************************************************
Shows the time elapsed and usage statistics since the last reset of a
speedo. */
void
speedo_show(
/*========*/
const speedo_t* speedo); /* in: speedo */
#endif /* UNIV_COMPILE_TEST_FUNCS */
#endif #endif
...@@ -220,6 +220,23 @@ ut_raw_to_hex( ...@@ -220,6 +220,23 @@ ut_raw_to_hex(
char* hex, /* out: hex string */ char* hex, /* out: hex string */
ulint hex_size); /* in: "hex" size in bytes */ ulint hex_size); /* in: "hex" size in bytes */
/***********************************************************************
Adds single quotes to the start and end of string and escapes any quotes
by doubling them. Returns the number of bytes that were written to "buf"
(including the terminating '\0'). If buf_size is too small then the
trailing bytes from "str" are discarded. */
UNIV_INLINE
ulint
ut_str_sql_format(
/*==============*/
/* out: number of bytes
that were written */
const char* str, /* in: string */
ulint str_len, /* in: string length in bytes */
char* buf, /* out: output buffer */
ulint buf_size); /* in: output buffer size
in bytes */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "ut0mem.ic" #include "ut0mem.ic"
#endif #endif
......
...@@ -7,6 +7,7 @@ Created 5/30/1994 Heikki Tuuri ...@@ -7,6 +7,7 @@ Created 5/30/1994 Heikki Tuuri
************************************************************************/ ************************************************************************/
#include "ut0byte.h" #include "ut0byte.h"
#include "mach0data.h"
UNIV_INLINE UNIV_INLINE
void* void*
...@@ -190,3 +191,102 @@ ut_raw_to_hex( ...@@ -190,3 +191,102 @@ ut_raw_to_hex(
return(write_bytes); return(write_bytes);
} }
/***********************************************************************
Adds single quotes to the start and end of string and escapes any quotes
by doubling them. Returns the number of bytes that were written to "buf"
(including the terminating '\0'). If buf_size is too small then the
trailing bytes from "str" are discarded. */
UNIV_INLINE
ulint
ut_str_sql_format(
/*==============*/
/* out: number of bytes
that were written */
const char* str, /* in: string */
ulint str_len, /* in: string length in bytes */
char* buf, /* out: output buffer */
ulint buf_size) /* in: output buffer size
in bytes */
{
ulint str_i;
ulint buf_i;
buf_i = 0;
switch (buf_size) {
case 3:
if (str_len == 0) {
buf[buf_i] = '\'';
buf_i++;
buf[buf_i] = '\'';
buf_i++;
}
/* FALLTHROUGH */
case 2:
case 1:
buf[buf_i] = '\0';
buf_i++;
/* FALLTHROUGH */
case 0:
return(buf_i);
}
/* buf_size >= 4 */
buf[0] = '\'';
buf_i = 1;
for (str_i = 0; str_i < str_len; str_i++) {
char ch;
if (buf_size - buf_i == 2) {
break;
}
ch = str[str_i];
switch (ch) {
case '\0':
if (UNIV_UNLIKELY(buf_size - buf_i < 4)) {
goto func_exit;
}
buf[buf_i] = '\\';
buf_i++;
buf[buf_i] = '0';
buf_i++;
break;
case '\'':
case '\\':
if (UNIV_UNLIKELY(buf_size - buf_i < 4)) {
goto func_exit;
}
buf[buf_i] = ch;
buf_i++;
/* FALLTHROUGH */
default:
buf[buf_i] = ch;
buf_i++;
}
}
func_exit:
buf[buf_i] = '\'';
buf_i++;
buf[buf_i] = '\0';
buf_i++;
return(buf_i);
}
lock_data
'1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc'''''
'1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc'''''
'2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""'
'2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""'
'3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\'
'3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\'
'4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0'
'4', 'abc', '\0abc', 'abc\0', 'a\0bc', 'a\0bc\0', 'a\0bc\0\0'
-128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0
-128, 0, -32768, 0, -8388608, 0, -2147483648, 0, -9223372036854775808, 0
127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615
127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967295, 9223372036854775807, 18446744073709551615
supremum pseudo-record
supremum pseudo-record
#
# Test that user data is correctly "visualized" in
# INFORMATION_SCHEMA.innodb_locks.lock_data
#
-- source include/have_innodb.inc
-- disable_query_log
-- disable_result_log
SET storage_engine=InnoDB;
-- disable_warnings
DROP TABLE IF EXISTS t_min, t_max;
-- enable_warnings
let $table_def =
(
c01 TINYINT,
c02 TINYINT UNSIGNED,
c03 SMALLINT,
c04 SMALLINT UNSIGNED,
c05 MEDIUMINT,
c06 MEDIUMINT UNSIGNED,
c07 INT,
c08 INT UNSIGNED,
c09 BIGINT,
c10 BIGINT UNSIGNED,
PRIMARY KEY(c01, c02, c03, c04, c05, c06, c07, c08, c09, c10)
);
-- eval CREATE TABLE t_min $table_def;
INSERT INTO t_min VALUES
(-128, 0,
-32768, 0,
-8388608, 0,
-2147483648, 0,
-9223372036854775808, 0);
-- eval CREATE TABLE t_max $table_def;
INSERT INTO t_max VALUES
(127, 255,
32767, 65535,
8388607, 16777215,
2147483647, 4294967295,
9223372036854775807, 18446744073709551615);
CREATE TABLE t_str (
c1 VARCHAR(32),
c2 VARCHAR(32),
c3 VARCHAR(32),
c4 VARCHAR(32),
c5 VARCHAR(32),
c6 VARCHAR(32),
c7 VARCHAR(32),
PRIMARY KEY(c1, c2, c3, c4, c5, c6, c7)
);
INSERT INTO t_str VALUES
('1', 'abc', '''abc', 'abc''', 'a''bc', 'a''bc''', '''abc''''');
INSERT INTO t_str VALUES
('2', 'abc', '"abc', 'abc"', 'a"bc', 'a"bc"', '"abc""');
INSERT INTO t_str VALUES
('3', 'abc', '\\abc', 'abc\\', 'a\\bc', 'a\\bc\\', '\\abc\\\\');
INSERT INTO t_str VALUES
('4', 'abc', 0x00616263, 0x61626300, 0x61006263, 0x6100626300, 0x610062630000);
-- connect (con_lock,localhost,root,,)
-- connect (con_min_trylock,localhost,root,,)
-- connect (con_max_trylock,localhost,root,,)
-- connect (con_str_insert_supremum,localhost,root,,)
-- connect (con_str_lock_row1,localhost,root,,)
-- connect (con_str_lock_row2,localhost,root,,)
-- connect (con_str_lock_row3,localhost,root,,)
-- connect (con_str_lock_row4,localhost,root,,)
-- connect (con_verify_innodb_locks,localhost,root,,)
-- connection con_lock
SET autocommit=0;
SELECT * FROM t_min FOR UPDATE;
SELECT * FROM t_max FOR UPDATE;
SELECT * FROM t_str FOR UPDATE;
-- connection con_min_trylock
-- send
SELECT * FROM t_min FOR UPDATE;
-- connection con_max_trylock
-- send
SELECT * FROM t_max FOR UPDATE;
-- connection con_str_insert_supremum
-- send
INSERT INTO t_str VALUES
('z', 'z', 'z', 'z', 'z', 'z', 'z');
-- connection con_str_lock_row1
-- send
SELECT * FROM t_str WHERE c1 = '1' FOR UPDATE;
-- connection con_str_lock_row2
-- send
SELECT * FROM t_str WHERE c1 = '2' FOR UPDATE;
-- connection con_str_lock_row3
-- send
SELECT * FROM t_str WHERE c1 = '3' FOR UPDATE;
-- connection con_str_lock_row4
-- send
SELECT * FROM t_str WHERE c1 = '4' FOR UPDATE;
# Give time to the above 2 queries to execute before continuing.
# Without this sleep it sometimes happens that the SELECT from innodb_locks
# executes before some of them, resulting in less than expected number
# of rows being selected from innodb_locks.
-- sleep 0.1
-- enable_result_log
-- connection con_verify_innodb_locks
SELECT lock_data FROM INFORMATION_SCHEMA.innodb_locks ORDER BY lock_data;
-- disable_result_log
-- connection default
-- disconnect con_lock
-- disconnect con_min_trylock
-- disconnect con_max_trylock
-- disconnect con_str_insert_supremum
-- disconnect con_str_lock_row1
-- disconnect con_str_lock_row2
-- disconnect con_str_lock_row3
-- disconnect con_str_lock_row4
-- disconnect con_verify_innodb_locks
DROP TABLE t_min, t_max, t_str;
...@@ -12,8 +12,10 @@ Created 4/20/1996 Heikki Tuuri ...@@ -12,8 +12,10 @@ Created 4/20/1996 Heikki Tuuri
#include "row0row.ic" #include "row0row.ic"
#endif #endif
#include "data0type.h"
#include "dict0dict.h" #include "dict0dict.h"
#include "btr0btr.h" #include "btr0btr.h"
#include "ha_prototypes.h"
#include "mach0data.h" #include "mach0data.h"
#include "trx0rseg.h" #include "trx0rseg.h"
#include "trx0trx.h" #include "trx0trx.h"
...@@ -26,6 +28,7 @@ Created 4/20/1996 Heikki Tuuri ...@@ -26,6 +28,7 @@ Created 4/20/1996 Heikki Tuuri
#include "row0upd.h" #include "row0upd.h"
#include "rem0cmp.h" #include "rem0cmp.h"
#include "read0read.h" #include "read0read.h"
#include "ut0mem.h"
/************************************************************************* /*************************************************************************
Gets the offset of trx id field, in bytes relative to the origin of Gets the offset of trx id field, in bytes relative to the origin of
...@@ -767,3 +770,459 @@ row_search_index_entry( ...@@ -767,3 +770,459 @@ row_search_index_entry(
return(!page_rec_is_infimum(rec) && low_match == n_fields); return(!page_rec_is_infimum(rec) && low_match == n_fields);
} }
#ifndef UNIV_HOTBACKUP
#include <my_sys.h>
/***********************************************************************
Formats the raw data in "data" (in InnoDB on-disk format) that is of
type DATA_INT using "prtype" and writes the result to "buf".
If the data is in unknown format, then nothing is written to "buf",
0 is returned and "format_in_hex" is set to TRUE, otherwise
"format_in_hex" is left untouched.
Not more than "buf_size" bytes are written to "buf".
The result is always '\0'-terminated (provided buf_size > 0) and the
number of bytes that were written to "buf" is returned (including the
terminating '\0'). */
static
ulint
row_raw_format_int(
/*===============*/
/* out: number of bytes
that were written */
const char* data, /* in: raw data */
ulint data_len, /* in: raw data length
in bytes */
ulint prtype, /* in: precise type */
char* buf, /* out: output buffer */
ulint buf_size, /* in: output buffer size
in bytes */
ibool* format_in_hex) /* out: should the data be
formated in hex */
{
ulint ret;
if (data_len <= sizeof(ullint)) {
ullint value;
ibool unsigned_type = prtype & DATA_UNSIGNED;
value = mach_read_int_type((const byte*) data,
data_len, unsigned_type);
if (unsigned_type) {
ret = ut_snprintf(buf, buf_size, "%llu",
value) + 1;
} else {
ret = ut_snprintf(buf, buf_size, "%lld",
(long long) value) + 1;
}
} else {
*format_in_hex = TRUE;
ret = 0;
}
return(ut_min(ret, buf_size));
}
extern CHARSET_INFO* system_charset_info;
/***********************************************************************
Formats the raw data in "data" (in InnoDB on-disk format) that is of
type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "charset_coll" and writes
the result to "buf". The result is converted to "system_charset_info".
Not more than "buf_size" bytes are written to "buf".
The result is always '\0'-terminated (provided buf_size > 0) and the
number of bytes that were written to "buf" is returned (including the
terminating '\0'). */
static
ulint
row_raw_format_str_convert(
/*=======================*/
/* out: number of bytes
that were written */
const char* data, /* in: raw data */
ulint data_len, /* in: raw data length
in bytes */
ulint charset_coll, /* in: charset collation */
char* buf, /* out: output buffer */
ulint buf_size) /* in: output buffer size
in bytes */
{
/* XXX we use a hard limit instead of allocating
but_size bytes from the heap */
CHARSET_INFO* data_cs;
char buf_tmp[8192];
ulint buf_tmp_used;
uint num_errors;
data_cs = all_charsets[charset_coll];
buf_tmp_used = innobase_convert_string(buf_tmp, sizeof(buf_tmp),
system_charset_info,
data, data_len, data_cs,
&num_errors);
return(ut_str_sql_format(buf_tmp, buf_tmp_used, buf, buf_size));
}
/***********************************************************************
Formats the raw data in "data" (in InnoDB on-disk format) that is of
type DATA_(CHAR|VARCHAR|MYSQL|VARMYSQL) using "prtype" and writes the
result to "buf".
If the data is in binary format, then nothing is written to "buf",
0 is returned and "format_in_hex" is set to TRUE, otherwise
"format_in_hex" is left untouched.
Not more than "buf_size" bytes are written to "buf".
The result is always '\0'-terminated (provided buf_size > 0) and the
number of bytes that were written to "buf" is returned (including the
terminating '\0'). */
static
ulint
row_raw_format_str(
/*===============*/
/* out: number of bytes
that were written */
const char* data, /* in: raw data */
ulint data_len, /* in: raw data length
in bytes */
ulint prtype, /* in: precise type */
char* buf, /* out: output buffer */
ulint buf_size, /* in: output buffer size
in bytes */
ibool* format_in_hex) /* out: should the data be
formated in hex */
{
ulint charset_coll;
if (buf_size == 0) {
return(0);
}
/* we assume system_charset_info is UTF-8 */
charset_coll = dtype_get_charset_coll(prtype);
if (UNIV_LIKELY(dtype_is_utf8(prtype))) {
return(ut_str_sql_format(data, data_len, buf, buf_size));
}
/* else */
if (charset_coll == DATA_MYSQL_BINARY_CHARSET_COLL) {
*format_in_hex = TRUE;
return(0);
}
/* else */
return(row_raw_format_str_convert(data, data_len, charset_coll,
buf, buf_size));
}
/***********************************************************************
Formats the raw data in "data" (in InnoDB on-disk format) using
"dict_field" and writes the result to "buf".
Not more than "buf_size" bytes are written to "buf".
The result is always '\0'-terminated (provided buf_size > 0) and the
number of bytes that were written to "buf" is returned (including the
terminating '\0'). */
ulint
row_raw_format(
/*===========*/
/* out: number of bytes
that were written */
const char* data, /* in: raw data */
ulint data_len, /* in: raw data length
in bytes */
const dict_field_t* dict_field, /* in: index field */
char* buf, /* out: output buffer */
ulint buf_size) /* in: output buffer size
in bytes */
{
ulint mtype;
ulint prtype;
ulint ret;
ibool format_in_hex;
if (buf_size == 0) {
return(0);
}
if (data_len == UNIV_SQL_NULL) {
ret = ut_snprintf((char*) buf, buf_size, "NULL") + 1;
return(ut_min(ret, buf_size));
}
mtype = dict_field->col->mtype;
prtype = dict_field->col->prtype;
format_in_hex = FALSE;
switch (mtype) {
case DATA_INT:
ret = row_raw_format_int(data, data_len, prtype,
buf, buf_size, &format_in_hex);
break;
case DATA_CHAR:
case DATA_VARCHAR:
case DATA_MYSQL:
case DATA_VARMYSQL:
ret = row_raw_format_str(data, data_len, prtype,
buf, buf_size, &format_in_hex);
break;
/* XXX support more data types */
default:
format_in_hex = TRUE;
}
if (format_in_hex) {
if (UNIV_LIKELY(buf_size > 2)) {
memcpy(buf, "0x", 2);
buf += 2;
buf_size -= 2;
ret = ut_raw_to_hex(data, data_len, buf, buf_size);
} else {
buf[0] = '\0';
ret = 1;
}
}
return(ret);
}
#endif /* !UNIV_HOTBACKUP */
#ifdef UNIV_COMPILE_TEST_FUNCS
#include "ut0dbg.h"
void
test_row_raw_format_int()
{
ulint ret;
char buf[128];
ibool format_in_hex;
#define CALL_AND_TEST(data, data_len, prtype, buf, buf_size,\
ret_expected, buf_expected, format_in_hex_expected)\
do {\
ibool ok = TRUE;\
ulint i;\
memset(buf, 'x', 10);\
buf[10] = '\0';\
format_in_hex = FALSE;\
fprintf(stderr, "TESTING \"\\x");\
for (i = 0; i < data_len; i++) {\
fprintf(stderr, "%02hhX", data[i]);\
}\
fprintf(stderr, "\", %lu, %lu, %lu\n",\
(ulint) data_len, (ulint) prtype,\
(ulint) buf_size);\
ret = row_raw_format_int(data, data_len, prtype,\
buf, buf_size, &format_in_hex);\
if (ret != ret_expected) {\
fprintf(stderr, "expected ret %lu, got %lu\n",\
(ulint) ret_expected, ret);\
ok = FALSE;\
}\
if (strcmp((char*) buf, buf_expected) != 0) {\
fprintf(stderr, "expected buf \"%s\", got \"%s\"\n",\
buf_expected, buf);\
ok = FALSE;\
}\
if (format_in_hex != format_in_hex_expected) {\
fprintf(stderr, "expected format_in_hex %d, got %d\n",\
(int) format_in_hex_expected,\
(int) format_in_hex);\
ok = FALSE;\
}\
if (ok) {\
fprintf(stderr, "OK: %lu, \"%s\" %d\n\n",\
(ulint) ret, buf, (int) format_in_hex);\
} else {\
return;\
}\
} while (0)
#if 1
/* min values for signed 1-8 byte integers */
CALL_AND_TEST("\x00", 1, 0,
buf, sizeof(buf), 5, "-128", 0);
CALL_AND_TEST("\x00\x00", 2, 0,
buf, sizeof(buf), 7, "-32768", 0);
CALL_AND_TEST("\x00\x00\x00", 3, 0,
buf, sizeof(buf), 9, "-8388608", 0);
CALL_AND_TEST("\x00\x00\x00\x00", 4, 0,
buf, sizeof(buf), 12, "-2147483648", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, 0,
buf, sizeof(buf), 14, "-549755813888", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, 0,
buf, sizeof(buf), 17, "-140737488355328", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, 0,
buf, sizeof(buf), 19, "-36028797018963968", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, 0,
buf, sizeof(buf), 21, "-9223372036854775808", 0);
/* min values for unsigned 1-8 byte integers */
CALL_AND_TEST("\x00", 1, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00", 2, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00", 3, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00\x00", 4, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00", 5, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00", 6, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00", 7, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x00\x00\x00", 8, DATA_UNSIGNED,
buf, sizeof(buf), 2, "0", 0);
/* max values for signed 1-8 byte integers */
CALL_AND_TEST("\xFF", 1, 0,
buf, sizeof(buf), 4, "127", 0);
CALL_AND_TEST("\xFF\xFF", 2, 0,
buf, sizeof(buf), 6, "32767", 0);
CALL_AND_TEST("\xFF\xFF\xFF", 3, 0,
buf, sizeof(buf), 8, "8388607", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, 0,
buf, sizeof(buf), 11, "2147483647", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, 0,
buf, sizeof(buf), 13, "549755813887", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, 0,
buf, sizeof(buf), 16, "140737488355327", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, 0,
buf, sizeof(buf), 18, "36028797018963967", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, 0,
buf, sizeof(buf), 20, "9223372036854775807", 0);
/* max values for unsigned 1-8 byte integers */
CALL_AND_TEST("\xFF", 1, DATA_UNSIGNED,
buf, sizeof(buf), 4, "255", 0);
CALL_AND_TEST("\xFF\xFF", 2, DATA_UNSIGNED,
buf, sizeof(buf), 6, "65535", 0);
CALL_AND_TEST("\xFF\xFF\xFF", 3, DATA_UNSIGNED,
buf, sizeof(buf), 9, "16777215", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF", 4, DATA_UNSIGNED,
buf, sizeof(buf), 11, "4294967295", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF", 5, DATA_UNSIGNED,
buf, sizeof(buf), 14, "1099511627775", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF", 6, DATA_UNSIGNED,
buf, sizeof(buf), 16, "281474976710655", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7, DATA_UNSIGNED,
buf, sizeof(buf), 18, "72057594037927935", 0);
CALL_AND_TEST("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8, DATA_UNSIGNED,
buf, sizeof(buf), 21, "18446744073709551615", 0);
/* some random values */
CALL_AND_TEST("\x52", 1, 0,
buf, sizeof(buf), 4, "-46", 0);
CALL_AND_TEST("\x0E", 1, DATA_UNSIGNED,
buf, sizeof(buf), 3, "14", 0);
CALL_AND_TEST("\x62\xCE", 2, 0,
buf, sizeof(buf), 6, "-7474", 0);
CALL_AND_TEST("\x29\xD6", 2, DATA_UNSIGNED,
buf, sizeof(buf), 6, "10710", 0);
CALL_AND_TEST("\x7F\xFF\x90", 3, 0,
buf, sizeof(buf), 5, "-112", 0);
CALL_AND_TEST("\x00\xA1\x16", 3, DATA_UNSIGNED,
buf, sizeof(buf), 6, "41238", 0);
CALL_AND_TEST("\x7F\xFF\xFF\xF7", 4, 0,
buf, sizeof(buf), 3, "-9", 0);
CALL_AND_TEST("\x00\x00\x00\x5C", 4, DATA_UNSIGNED,
buf, sizeof(buf), 3, "92", 0);
CALL_AND_TEST("\x7F\xFF\xFF\xFF\xFF\xFF\xDC\x63", 8, 0,
buf, sizeof(buf), 6, "-9117", 0);
CALL_AND_TEST("\x00\x00\x00\x00\x00\x01\x64\x62", 8, DATA_UNSIGNED,
buf, sizeof(buf), 6, "91234", 0);
#endif
/* speed test */
speedo_t speedo;
ulint i;
speedo_reset(&speedo);
for (i = 0; i < 1000000; i++) {
row_raw_format_int("\x23", 1,
0, buf, sizeof(buf),
&format_in_hex);
row_raw_format_int("\x23", 1,
DATA_UNSIGNED, buf, sizeof(buf),
&format_in_hex);
row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8,
0, buf, sizeof(buf),
&format_in_hex);
row_raw_format_int("\x00\x00\x00\x00\x00\x01\x64\x62", 8,
DATA_UNSIGNED, buf, sizeof(buf),
&format_in_hex);
}
speedo_show(&speedo);
}
#endif /* UNIV_COMPILE_TEST_FUNCS */
...@@ -14,11 +14,16 @@ Created July 17, 2007 Vasil Dimov ...@@ -14,11 +14,16 @@ Created July 17, 2007 Vasil Dimov
#include <mysql/plugin.h> #include <mysql/plugin.h>
#include "univ.i" #include "univ.i"
#include "buf0buf.h"
#include "dict0dict.h"
#include "ha0storage.h" #include "ha0storage.h"
#include "hash0hash.h" #include "hash0hash.h"
#include "lock0iter.h" #include "lock0iter.h"
#include "lock0lock.h" #include "lock0lock.h"
#include "mem0mem.h" #include "mem0mem.h"
#include "page0page.h"
#include "rem0rec.h"
#include "row0row.h"
#include "srv0srv.h" #include "srv0srv.h"
#include "sync0rw.h" #include "sync0rw.h"
#include "sync0sync.h" #include "sync0sync.h"
...@@ -360,6 +365,163 @@ fill_trx_row( ...@@ -360,6 +365,163 @@ fill_trx_row(
return(row); return(row);
} }
/***********************************************************************
Format the nth field of "rec" and put it in "buf". The result is always
'\0'-terminated. Returns the number of bytes that were written to "buf"
(including the terminating '\0'). */
static
ulint
put_nth_field(
/*==========*/
/* out: end of the result */
char* buf, /* out: buffer */
ulint buf_size,/* in: buffer size in bytes */
ulint n, /* in: number of field */
const dict_index_t* index, /* in: index */
const rec_t* rec, /* in: record */
const ulint* offsets)/* in: record offsets, returned
by rec_get_offsets() */
{
const byte* data;
ulint data_len;
dict_field_t* dict_field;
ulint ret;
ut_ad(rec_offs_validate(rec, NULL, offsets));
if (buf_size == 0) {
return(0);
}
ret = 0;
if (n > 0) {
/* we must append ", " before the actual data */
if (buf_size < 3) {
buf[0] = '\0';
return(1);
}
memcpy(buf, ", ", 3);
buf += 2;
buf_size -= 2;
ret += 2;
}
/* now buf_size >= 1 */
data = rec_get_nth_field(rec, offsets, n, &data_len);
dict_field = dict_index_get_nth_field(index, n);
ret += row_raw_format((const char*) data, data_len,
dict_field, buf, buf_size);
return(ret);
}
/***********************************************************************
Fills the "lock_data" member of i_s_locks_row_t object. */
static
void
fill_lock_data(
/*===========*/
const char** lock_data,/* out: "lock_data" to fill */
const lock_t* lock, /* in: lock used to find the data */
ulint heap_no,/* in: rec num used to find the data */
trx_i_s_cache_t* cache) /* in/out: cache where to store
volatile data */
{
mtr_t mtr;
const buf_block_t* block;
const page_t* page;
const rec_t* rec;
ut_a(lock_get_type(lock) == LOCK_REC);
mtr_start(&mtr);
block = buf_page_try_get(lock_rec_get_space_id(lock),
lock_rec_get_page_no(lock),
&mtr);
if (block == NULL) {
*lock_data = NULL;
mtr_commit(&mtr);
return;
}
page = (const page_t*) buf_block_get_frame(block);
rec = page_find_rec_with_heap_no(page, heap_no);
if (page_rec_is_infimum(rec)) {
*lock_data = ha_storage_put_str(cache->storage,
"infimum pseudo-record");
} else if (page_rec_is_supremum(rec)) {
*lock_data = ha_storage_put_str(cache->storage,
"supremum pseudo-record");
} else {
const dict_index_t* index;
ulint n_fields;
mem_heap_t* heap;
ulint offsets_onstack[REC_OFFS_NORMAL_SIZE];
ulint* offsets;
char buf[TRX_I_S_LOCK_DATA_MAX_LEN];
ulint buf_used;
ulint i;
rec_offs_init(offsets_onstack);
offsets = offsets_onstack;
index = lock_rec_get_index(lock);
n_fields = dict_index_get_n_unique(index);
ut_a(n_fields > 0);
heap = NULL;
offsets = rec_get_offsets(rec, index, offsets, n_fields,
&heap);
/* format and store the data */
buf_used = 0;
for (i = 0; i < n_fields; i++) {
buf_used += put_nth_field(
buf + buf_used, sizeof(buf) - buf_used,
i, index, rec, offsets) - 1;
}
*lock_data = (const char*) ha_storage_put(cache->storage,
buf,
buf_used + 1);
if (UNIV_UNLIKELY(heap != NULL)) {
/* this means that rec_get_offsets() has created a new
heap and has stored offsets in it; check that this is
really the case and free the heap */
ut_a(offsets != offsets_onstack);
mem_heap_free(heap);
}
}
mtr_commit(&mtr);
}
/*********************************************************************** /***********************************************************************
Fills i_s_locks_row_t object. Returns its first argument. */ Fills i_s_locks_row_t object. Returns its first argument. */
static static
...@@ -391,6 +553,8 @@ fill_locks_row( ...@@ -391,6 +553,8 @@ fill_locks_row(
row->lock_page = lock_rec_get_page_no(lock); row->lock_page = lock_rec_get_page_no(lock);
row->lock_rec = heap_no; row->lock_rec = heap_no;
fill_lock_data(&row->lock_data, lock, heap_no, cache);
break; break;
case LOCK_TABLE: case LOCK_TABLE:
row->lock_index = NULL; row->lock_index = NULL;
...@@ -399,6 +563,8 @@ fill_locks_row( ...@@ -399,6 +563,8 @@ fill_locks_row(
row->lock_page = ULINT_UNDEFINED; row->lock_page = ULINT_UNDEFINED;
row->lock_rec = ULINT_UNDEFINED; row->lock_rec = ULINT_UNDEFINED;
row->lock_data = NULL;
break; break;
default: default:
ut_error; ut_error;
......
...@@ -7,6 +7,7 @@ Created 1/30/1994 Heikki Tuuri ...@@ -7,6 +7,7 @@ Created 1/30/1994 Heikki Tuuri
**********************************************************************/ **********************************************************************/
#include "univ.i" #include "univ.i"
#include "ut0dbg.h"
#if defined(__GNUC__) && (__GNUC__ > 2) #if defined(__GNUC__) && (__GNUC__ > 2)
#else #else
...@@ -96,3 +97,69 @@ ut_dbg_stop_thread( ...@@ -96,3 +97,69 @@ ut_dbg_stop_thread(
} }
# endif # endif
#endif /* __NETWARE__ */ #endif /* __NETWARE__ */
#ifdef UNIV_COMPILE_TEST_FUNCS
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#ifndef timersub
#define timersub(a, b, r) \
do { \
(r)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
(r)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
if ((r)->tv_usec < 0) { \
(r)->tv_sec--; \
(r)->tv_usec += 1000000; \
} \
} while (0)
#endif /* timersub */
/***********************************************************************
Resets a speedo (records the current time in it). */
void
speedo_reset(
/*=========*/
speedo_t* speedo) /* out: speedo */
{
gettimeofday(&speedo->tv, NULL);
getrusage(RUSAGE_SELF, &speedo->ru);
}
/***********************************************************************
Shows the time elapsed and usage statistics since the last reset of a
speedo. */
void
speedo_show(
/*========*/
const speedo_t* speedo) /* in: speedo */
{
struct rusage ru_now;
struct timeval tv_now;
struct timeval tv_diff;
getrusage(RUSAGE_SELF, &ru_now);
gettimeofday(&tv_now, NULL);
#define PRINT_TIMEVAL(prefix, tvp) \
fprintf(stderr, "%s% 5ld.%06ld sec\n", \
prefix, (tvp)->tv_sec, (tvp)->tv_usec)
timersub(&tv_now, &speedo->tv, &tv_diff);
PRINT_TIMEVAL("real", &tv_diff);
timersub(&ru_now.ru_utime, &speedo->ru.ru_utime, &tv_diff);
PRINT_TIMEVAL("user", &tv_diff);
timersub(&ru_now.ru_stime, &speedo->ru.ru_stime, &tv_diff);
PRINT_TIMEVAL("sys ", &tv_diff);
}
#endif /* UNIV_COMPILE_TEST_FUNCS */
...@@ -546,3 +546,83 @@ ut_strreplace( ...@@ -546,3 +546,83 @@ ut_strreplace(
return(new_str); return(new_str);
} }
#ifdef UNIV_COMPILE_TEST_FUNCS
void
test_ut_str_sql_format()
{
char buf[128];
ulint ret;
#define CALL_AND_TEST(str, str_len, buf, buf_size, ret_expected, buf_expected)\
do {\
ibool ok = TRUE;\
memset(buf, 'x', 10);\
buf[10] = '\0';\
fprintf(stderr, "TESTING \"%s\", %lu, %lu\n",\
str, (ulint) str_len, (ulint) buf_size);\
ret = ut_str_sql_format(str, str_len, buf, buf_size);\
if (ret != ret_expected) {\
fprintf(stderr, "expected ret %lu, got %lu\n",\
(ulint) ret_expected, ret);\
ok = FALSE;\
}\
if (strcmp((char*) buf, buf_expected) != 0) {\
fprintf(stderr, "expected buf \"%s\", got \"%s\"\n",\
buf_expected, buf);\
ok = FALSE;\
}\
if (ok) {\
fprintf(stderr, "OK: %lu, \"%s\"\n\n",\
(ulint) ret, buf);\
} else {\
return;\
}\
} while (0)
CALL_AND_TEST("abcd", 4, buf, 0, 0, "xxxxxxxxxx");
CALL_AND_TEST("abcd", 4, buf, 1, 1, "");
CALL_AND_TEST("abcd", 4, buf, 2, 1, "");
CALL_AND_TEST("abcd", 0, buf, 3, 3, "''");
CALL_AND_TEST("abcd", 1, buf, 3, 1, "");
CALL_AND_TEST("abcd", 2, buf, 3, 1, "");
CALL_AND_TEST("abcd", 3, buf, 3, 1, "");
CALL_AND_TEST("abcd", 4, buf, 3, 1, "");
CALL_AND_TEST("abcd", 0, buf, 4, 3, "''");
CALL_AND_TEST("abcd", 1, buf, 4, 4, "'a'");
CALL_AND_TEST("abcd", 2, buf, 4, 4, "'a'");
CALL_AND_TEST("abcd", 3, buf, 4, 4, "'a'");
CALL_AND_TEST("abcd", 4, buf, 4, 4, "'a'");
CALL_AND_TEST("abcde", 5, buf, 4, 4, "'a'");
CALL_AND_TEST("'", 1, buf, 4, 3, "''");
CALL_AND_TEST("''", 2, buf, 4, 3, "''");
CALL_AND_TEST("a'", 2, buf, 4, 4, "'a'");
CALL_AND_TEST("'a", 2, buf, 4, 3, "''");
CALL_AND_TEST("ab", 2, buf, 4, 4, "'a'");
CALL_AND_TEST("abcdef", 0, buf, 5, 3, "''");
CALL_AND_TEST("abcdef", 1, buf, 5, 4, "'a'");
CALL_AND_TEST("abcdef", 2, buf, 5, 5, "'ab'");
CALL_AND_TEST("abcdef", 3, buf, 5, 5, "'ab'");
CALL_AND_TEST("abcdef", 4, buf, 5, 5, "'ab'");
CALL_AND_TEST("abcdef", 5, buf, 5, 5, "'ab'");
CALL_AND_TEST("abcdef", 6, buf, 5, 5, "'ab'");
CALL_AND_TEST("'", 1, buf, 5, 5, "''''");
CALL_AND_TEST("''", 2, buf, 5, 5, "''''");
CALL_AND_TEST("a'", 2, buf, 5, 4, "'a'");
CALL_AND_TEST("'a", 2, buf, 5, 5, "''''");
CALL_AND_TEST("ab", 2, buf, 5, 5, "'ab'");
CALL_AND_TEST("abc", 3, buf, 5, 5, "'ab'");
CALL_AND_TEST("ab", 2, buf, 6, 5, "'ab'");
CALL_AND_TEST("a'b'c", 5, buf, 32, 10, "'a''b''c'");
CALL_AND_TEST("a'b'c'", 6, buf, 32, 12, "'a''b''c'''");
}
#endif /* UNIV_COMPILE_TEST_FUNCS */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment