Below is the list of changes that have just been committed into a local
5.1 repository of alexi. When alexi does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet
1.2160 06/06/06 23:40:42 aivanov@stripped +3 -0
Merge mysql.com:/home/alexi/innodb/mysql-5.0-ss609
into mysql.com:/home/alexi/innodb/mysql-5.1-null
Null merge.
storage/innobase/row/row0sel.c
1.105 06/06/06 23:40:37 aivanov@stripped +1 -2
Null merge.
storage/innobase/row/row0mysql.c
1.126 06/06/06 23:40:37 aivanov@stripped +4 -11
Null merge.
mysql-test/t/innodb.test
1.147 06/06/06 23:40:37 aivanov@stripped +0 -13
Null merge
storage/innobase/row/row0sel.c
1.92.7.2 06/06/06 23:39:34 aivanov@stripped +0 -0
Merge rename: innobase/row/row0sel.c -> storage/innobase/row/row0sel.c
storage/innobase/row/row0mysql.c
1.103.14.3 06/06/06 23:39:34 aivanov@stripped +0 -0
Merge rename: innobase/row/row0mysql.c -> storage/innobase/row/row0mysql.c
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: aivanov
# Host: mysqld.localdomain
# Root: /home/alexi/innodb/mysql-5.1-null/RESYNC
--- 1.103.14.2/innobase/row/row0mysql.c 2006-06-06 23:37:38 +04:00
+++ 1.126/storage/innobase/row/row0mysql.c 2006-06-06 23:40:37 +04:00
@@ -45,7 +45,7 @@
};
UT_LIST_BASE_NODE_T(row_mysql_drop_t) row_mysql_drop_list;
-ibool row_mysql_drop_list_inited = FALSE;
+ibool row_mysql_drop_list_inited = FALSE;
/* Magic table names for invoking various monitor threads */
static const char S_innodb_monitor[] = "innodb_monitor";
@@ -85,12 +85,14 @@
system table name */
const char* name)
{
- if (memcmp(name, "mysql/", 6)) {
+ if (strncmp(name, "mysql/", 6) != 0) {
+
return(FALSE);
}
+
return(0 == strcmp(name + 6, "host")
- || 0 == strcmp(name + 6, "user")
- || 0 == strcmp(name + 6, "db"));
+ || 0 == strcmp(name + 6, "user")
+ || 0 == strcmp(name + 6, "db"));
}
/***********************************************************************
@@ -207,7 +209,7 @@
mach_write_to_n_little_endian(dest, col_len - 8, len);
- ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
+ ut_memcpy(dest + col_len - 8, &data, sizeof(byte*));
}
/***********************************************************************
@@ -226,7 +228,7 @@
*len = mach_read_from_n_little_endian(ref, col_len - 8);
- ut_memcpy((byte*)&data, ref + col_len - 8, sizeof(byte*));
+ ut_memcpy(&data, ref + col_len - 8, sizeof(byte*));
return(data);
}
@@ -267,7 +269,7 @@
VARCHAR then this is irrelevant */
ulint comp) /* in: nonzero=compact format */
{
- byte* ptr = mysql_data;
+ byte* ptr = mysql_data;
dtype_t* dtype;
ulint type;
ulint lenlen;
@@ -299,13 +301,13 @@
buf += col_len;
} else if ((type == DATA_VARCHAR
- || type == DATA_VARMYSQL
- || type == DATA_BINARY)) {
+ || type == DATA_VARMYSQL
+ || type == DATA_BINARY)) {
if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) {
/* The length of the actual data is stored to 1 or 2
bytes at the start of the field */
-
+
if (row_format_col) {
if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) {
lenlen = 2;
@@ -318,7 +320,7 @@
}
ptr = row_mysql_read_true_varchar(&col_len, mysql_data,
- lenlen);
+ lenlen);
} else {
/* Remove trailing spaces from old style VARCHAR
columns. */
@@ -361,12 +363,12 @@
Consider a CHAR(n) field, a field of n characters.
It will contain between n * mbminlen and n * mbmaxlen bytes.
We will try to truncate it to n bytes by stripping
- space padding. If the field contains single-byte
+ space padding. If the field contains single-byte
characters only, it will be truncated to n characters.
Consider a CHAR(5) field containing the string ".a "
where "." denotes a 3-byte character represented by
the bytes "$%&". After our stripping, the string will
- be stored as "$%&a " (5 bytes). The string ".abc "
+ be stored as "$%&a " (5 bytes). The string ".abc "
will be stored as "$%&abc" (6 bytes).
The space padding will be restored in row0sel.c, function
@@ -410,10 +412,10 @@
row is used, as row may contain
pointers to this record! */
{
- mysql_row_templ_t* templ;
+ mysql_row_templ_t* templ;
dfield_t* dfield;
ulint i;
-
+
ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
ut_ad(prebuilt->mysql_template);
@@ -426,7 +428,7 @@
/* Column may be SQL NULL */
if (mysql_rec[templ->mysql_null_byte_offset] &
- (byte) (templ->mysql_null_bit_mask)) {
+ (byte) (templ->mysql_null_bit_mask)) {
/* It is SQL NULL */
@@ -434,18 +436,18 @@
goto next_column;
}
- }
-
+ }
+
row_mysql_store_col_in_innobase_format(dfield,
- prebuilt->ins_upd_rec_buff
- + templ->mysql_col_offset,
- TRUE, /* MySQL row format data */
- mysql_rec + templ->mysql_col_offset,
- templ->mysql_col_len,
- prebuilt->table->comp);
+ prebuilt->ins_upd_rec_buff
+ + templ->mysql_col_offset,
+ TRUE, /* MySQL row format data */
+ mysql_rec + templ->mysql_col_offset,
+ templ->mysql_col_len,
+ dict_table_is_comp(prebuilt->table));
next_column:
;
- }
+ }
}
/********************************************************************
@@ -469,20 +471,21 @@
handle_new_error:
err = trx->error_state;
-
+
ut_a(err != DB_SUCCESS);
-
+
trx->error_state = DB_SUCCESS;
- if (err == DB_DUPLICATE_KEY) {
- if (savept) {
+ if ((err == DB_DUPLICATE_KEY)
+ || (err == DB_FOREIGN_DUPLICATE_KEY)) {
+ if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
} else if (err == DB_TOO_BIG_RECORD) {
- if (savept) {
+ if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
@@ -492,7 +495,7 @@
} else if (err == DB_ROW_IS_REFERENCED
|| err == DB_NO_REFERENCED_ROW
|| err == DB_CANNOT_ADD_CONSTRAINT) {
- if (savept) {
+ if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
@@ -519,10 +522,10 @@
to version 3.23.43 */
trx_general_rollback_for_mysql(trx, FALSE, NULL);
-
+
} else if (err == DB_OUT_OF_FILE_SPACE
|| err == DB_LOCK_WAIT_TIMEOUT) {
- if (savept) {
+ if (savept) {
/* Roll back the latest, possibly incomplete
insertion or update */
@@ -536,32 +539,32 @@
"InnoDB: The database cannot continue operation because of\n"
"InnoDB: lack of space. You must add a new data file to\n"
"InnoDB: my.cnf and restart the database.\n", stderr);
-
+
exit(1);
} else if (err == DB_CORRUPTION) {
- fputs(
- "InnoDB: We detected index corruption in an InnoDB type table.\n"
- "InnoDB: You have to dump + drop + reimport the table or, in\n"
- "InnoDB: a case of widespread corruption, dump all InnoDB\n"
- "InnoDB: tables and recreate the whole InnoDB tablespace.\n"
- "InnoDB: If the mysqld server crashes after the startup or when\n"
- "InnoDB: you dump the tables, look at\n"
- "InnoDB: http://dev.mysql.com/doc/mysql/en/Forcing_recovery.html"
- " for help.\n", stderr);
+ fputs(
+ "InnoDB: We detected index corruption in an InnoDB type table.\n"
+ "InnoDB: You have to dump + drop + reimport the table or, in\n"
+ "InnoDB: a case of widespread corruption, dump all InnoDB\n"
+ "InnoDB: tables and recreate the whole InnoDB tablespace.\n"
+ "InnoDB: If the mysqld server crashes after the startup or when\n"
+ "InnoDB: you dump the tables, look at\n"
+ "InnoDB: http://dev.mysql.com/doc/mysql/en/Forcing_recovery.html"
+ " for help.\n", stderr);
} else {
fprintf(stderr, "InnoDB: unknown error code %lu\n",
(ulong) err);
ut_error;
- }
+ }
if (trx->error_state != DB_SUCCESS) {
*new_err = trx->error_state;
} else {
*new_err = err;
}
-
+
trx->error_state = DB_SUCCESS;
return(FALSE);
@@ -570,6 +573,7 @@
InnoDB Hot Backup builds. Besides, this function should never
be called in InnoDB Hot Backup. */
ut_error;
+ return(FALSE);
#endif /* UNIV_HOTBACKUP */
}
@@ -588,7 +592,7 @@
dtuple_t* ref;
ulint ref_len;
ulint i;
-
+
heap = mem_heap_create(128);
prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t));
@@ -615,22 +619,24 @@
prebuilt->ins_node = NULL;
prebuilt->ins_upd_rec_buff = NULL;
-
+
prebuilt->upd_node = NULL;
prebuilt->ins_graph = NULL;
prebuilt->upd_graph = NULL;
- prebuilt->pcur = btr_pcur_create_for_mysql();
- prebuilt->clust_pcur = btr_pcur_create_for_mysql();
+ prebuilt->pcur = btr_pcur_create_for_mysql();
+ prebuilt->clust_pcur = btr_pcur_create_for_mysql();
prebuilt->select_lock_type = LOCK_NONE;
prebuilt->stored_select_lock_type = 99999999;
+ prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
+
prebuilt->sel_graph = NULL;
prebuilt->search_tuple = dtuple_create(heap,
2 * dict_table_get_n_cols(table));
-
+
clust_index = dict_table_get_first_index(table);
/* Make sure that search_tuple is long enough for clustered index */
@@ -668,16 +674,16 @@
ulint i;
if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
- || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) {
+ || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) {
fprintf(stderr,
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, magic n2 %lu, table name",
(ulong) prebuilt->magic_n,
(ulong) prebuilt->magic_n2);
- ut_print_name(stderr, NULL, prebuilt->table->name);
+ ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
putc('\n', stderr);
- mem_analyze_corruption((byte*)prebuilt);
+ mem_analyze_corruption(prebuilt);
ut_error;
}
@@ -699,11 +705,11 @@
if (prebuilt->sel_graph) {
que_graph_free_recursive(prebuilt->sel_graph);
}
-
+
if (prebuilt->upd_graph) {
que_graph_free_recursive(prebuilt->upd_graph);
}
-
+
if (prebuilt->blob_heap) {
mem_heap_free(prebuilt->blob_heap);
}
@@ -711,15 +717,15 @@
if (prebuilt->old_vers_heap) {
mem_heap_free(prebuilt->old_vers_heap);
}
-
+
for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
if (prebuilt->fetch_cache[i] != NULL) {
if ((ROW_PREBUILT_FETCH_MAGIC_N !=
- mach_read_from_4((prebuilt->fetch_cache[i]) - 4))
- || (ROW_PREBUILT_FETCH_MAGIC_N !=
- mach_read_from_4((prebuilt->fetch_cache[i])
- + prebuilt->mysql_row_len))) {
+ mach_read_from_4((prebuilt->fetch_cache[i]) - 4))
+ || (ROW_PREBUILT_FETCH_MAGIC_N !=
+ mach_read_from_4((prebuilt->fetch_cache[i])
+ + prebuilt->mysql_row_len))) {
fputs(
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: fetch buffer.\n", stderr);
@@ -750,14 +756,14 @@
row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
handle */
trx_t* trx) /* in: transaction handle */
-{
+{
if (trx->magic_n != TRX_MAGIC_N) {
fprintf(stderr,
"InnoDB: Error: trying to use a corrupt\n"
"InnoDB: trx handle. Magic n %lu\n",
(ulong) trx->magic_n);
- mem_analyze_corruption((byte*)trx);
+ mem_analyze_corruption(trx);
ut_error;
}
@@ -767,10 +773,10 @@
"InnoDB: Error: trying to use a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name",
(ulong) prebuilt->magic_n);
- ut_print_name(stderr, NULL, prebuilt->table->name);
+ ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
putc('\n', stderr);
- mem_analyze_corruption((byte*)prebuilt);
+ mem_analyze_corruption(prebuilt);
ut_error;
}
@@ -787,7 +793,7 @@
if (prebuilt->sel_graph) {
prebuilt->sel_graph->trx = trx;
- }
+ }
}
/*************************************************************************
@@ -799,7 +805,7 @@
row_get_prebuilt_insert_row(
/*========================*/
/* out: prebuilt dtuple; the column
- type information is also set in it */
+ type information is also set in it */
row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
handle */
{
@@ -809,14 +815,14 @@
ulint i;
ut_ad(prebuilt && table && prebuilt->trx);
-
+
if (prebuilt->ins_node == NULL) {
/* Not called before for this handle: create an insert node
and query graph to the prebuilt struct */
node = ins_node_create(INS_DIRECT, table, prebuilt->heap);
-
+
prebuilt->ins_node = node;
if (prebuilt->ins_upd_rec_buff == NULL) {
@@ -824,7 +830,7 @@
prebuilt->heap,
prebuilt->mysql_row_len);
}
-
+
row = dtuple_create(prebuilt->heap,
dict_table_get_n_cols(table));
@@ -834,8 +840,8 @@
a debug assertion from failing */
for (i = 0; i < dtuple_get_n_fields(row); i++) {
-
- dtuple_get_nth_field(row, i)->len = UNIV_SQL_NULL;
+
+ dtuple_get_nth_field(row, i)->len = UNIV_SQL_NULL;
}
ins_node_set_new_row(node, row);
@@ -848,7 +854,7 @@
prebuilt->ins_graph->state = QUE_FORK_ACTIVE;
}
- return(prebuilt->ins_node->row);
+ return(prebuilt->ins_node->row);
}
/*************************************************************************
@@ -861,7 +867,7 @@
dict_table_t* table) /* in: table */
{
ulint counter;
-
+
counter = table->stat_modified_counter;
table->stat_modified_counter = counter + 1;
@@ -873,16 +879,16 @@
a counter table which is very small and updated very often. */
if (counter > 2000000000
- || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
+ || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
dict_update_statistics(table);
- }
+ }
}
-
+
/*************************************************************************
Unlocks an AUTO_INC type lock possibly reserved by trx. */
-void
+void
row_unlock_table_autoinc_for_mysql(
/*===============================*/
trx_t* trx) /* in: transaction */
@@ -909,15 +915,15 @@
row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL
table handle */
{
- trx_t* trx = prebuilt->trx;
+ trx_t* trx = prebuilt->trx;
ins_node_t* node = prebuilt->ins_node;
que_thr_t* thr;
ulint err;
ibool was_lock_wait;
-
+
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
+
if (trx->auto_inc_lock) {
return(DB_SUCCESS);
@@ -987,11 +993,11 @@
ulint mode) /* in: lock mode of table
(ignored if table==NULL) */
{
- trx_t* trx = prebuilt->trx;
+ trx_t* trx = prebuilt->trx;
que_thr_t* thr;
ulint err;
ibool was_lock_wait;
-
+
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
@@ -1042,12 +1048,12 @@
}
que_thr_stop_for_mysql_no_error(thr, trx);
-
+
trx->op_info = "";
- return((int) err);
+ return((int) err);
}
-
+
/*************************************************************************
Does an insert for MySQL. */
@@ -1063,15 +1069,15 @@
que_thr_t* thr;
ulint err;
ibool was_lock_wait;
- trx_t* trx = prebuilt->trx;
+ trx_t* trx = prebuilt->trx;
ins_node_t* node = prebuilt->ins_node;
-
+
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
+ ut_print_timestamp(stderr);
+ fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
@@ -1088,10 +1094,11 @@
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name",
(ulong) prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->trx, prebuilt->table->name);
+ ut_print_name(stderr, prebuilt->trx, TRUE,
+ prebuilt->table->name);
putc('\n', stderr);
- mem_analyze_corruption((byte*)prebuilt);
+ mem_analyze_corruption(prebuilt);
ut_error;
}
@@ -1120,9 +1127,9 @@
}
row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);
-
+
savept = trx_savept_take(trx);
-
+
thr = que_fork_get_first_thr(prebuilt->ins_graph);
if (prebuilt->sql_stat_start) {
@@ -1131,7 +1138,7 @@
} else {
node->state = INS_NODE_ALLOC_ROW_ID;
}
-
+
que_thr_move_to_run_state_for_mysql(thr, trx);
run_again:
@@ -1139,7 +1146,7 @@
thr->prev_node = node;
row_ins_step(thr);
-
+
err = trx->error_state;
if (err != DB_SUCCESS) {
@@ -1161,15 +1168,15 @@
}
que_thr_stop_for_mysql_no_error(thr, trx);
-
+
prebuilt->table->stat_n_rows++;
srv_n_rows_inserted++;
-
+
if (prebuilt->table->stat_n_rows == 0) {
/* Avoid wrap-over */
prebuilt->table->stat_n_rows--;
- }
+ }
row_update_statistics_if_needed(prebuilt->table);
trx->op_info = "";
@@ -1189,11 +1196,11 @@
sel_node_t* node;
ut_ad(prebuilt && prebuilt->trx);
-
+
if (prebuilt->sel_graph == NULL) {
node = sel_node_create(prebuilt->heap);
-
+
prebuilt->sel_graph =
que_node_get_parent(
pars_complete_graph_for_exec(node,
@@ -1218,7 +1225,7 @@
upd_node_t* node;
node = upd_node_create(heap);
-
+
node->in_mysql_interface = TRUE;
node->is_delete = FALSE;
node->searched_update = FALSE;
@@ -1230,7 +1237,7 @@
node->update = upd_create(dict_table_get_n_cols(table), heap);
node->update_n_fields = dict_table_get_n_cols(table);
-
+
UT_LIST_INIT(node->columns);
node->has_clust_rec_x_lock = TRUE;
node->cmpl_info = 0;
@@ -1257,7 +1264,7 @@
upd_node_t* node;
ut_ad(prebuilt && table && prebuilt->trx);
-
+
if (prebuilt->upd_node == NULL) {
/* Not called before for this handle: create an update node
@@ -1266,7 +1273,7 @@
node = row_create_update_node_for_mysql(table, prebuilt->heap);
prebuilt->upd_node = node;
-
+
prebuilt->upd_graph =
que_node_get_parent(
pars_complete_graph_for_exec(node,
@@ -1294,7 +1301,7 @@
ulint err;
que_thr_t* thr;
ibool was_lock_wait;
- dict_index_t* clust_index;
+ dict_index_t* clust_index;
/* ulint ref_len; */
upd_node_t* node;
dict_table_t* table = prebuilt->table;
@@ -1303,10 +1310,10 @@
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec);
-
+
if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
+ ut_print_timestamp(stderr);
+ fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
@@ -1323,10 +1330,11 @@
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name",
(ulong) prebuilt->magic_n);
- ut_print_name(stderr, prebuilt->trx, prebuilt->table->name);
+ ut_print_name(stderr, prebuilt->trx, TRUE,
+ prebuilt->table->name);
putc('\n', stderr);
- mem_analyze_corruption((byte*)prebuilt);
+ mem_analyze_corruption(prebuilt);
ut_error;
}
@@ -1359,9 +1367,9 @@
btr_pcur_copy_stored_position(node->pcur,
prebuilt->clust_pcur);
}
-
+
ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
-
+
/* MySQL seems to call rnd_pos before updating each row it
has cached: we can get the correct cursor position from
prebuilt->pcur; NOTE that we cannot build the row reference
@@ -1370,7 +1378,7 @@
the row id used as the clustered index key */
savept = trx_savept_take(trx);
-
+
thr = que_fork_get_first_thr(prebuilt->upd_graph);
node->state = UPD_NODE_UPDATE_CLUSTERED;
@@ -1389,7 +1397,7 @@
if (err != DB_SUCCESS) {
que_thr_stop_for_mysql(thr);
-
+
if (err == DB_RECORD_NOT_FOUND) {
trx->error_state = DB_SUCCESS;
trx->op_info = "";
@@ -1397,10 +1405,11 @@
return((int) err);
}
- thr->lock_state= QUE_THR_LOCK_ROW;
+ thr->lock_state= QUE_THR_LOCK_ROW;
was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
- &savept);
- thr->lock_state= QUE_THR_LOCK_NOLOCK;;
+ &savept);
+ thr->lock_state= QUE_THR_LOCK_NOLOCK;
+
if (was_lock_wait) {
goto run_again;
}
@@ -1430,15 +1439,15 @@
}
/*************************************************************************
-This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before
+This can only be used when srv_locks_unsafe_for_binlog is TRUE or
+this session is using a READ COMMITTED isolation level. Before
calling this function we must use trx_reset_new_rec_lock_info() and
trx_register_new_rec_lock() to store the information which new record locks
really were set. This function removes a newly set lock under prebuilt->pcur,
and also under prebuilt->clust_pcur. Currently, this is only used and tested
in the case of an UPDATE or a DELETE statement, where the row lock is of the
-LOCK_X or LOCK_S type.
-
-Thus, this implements a 'mini-rollback' that releases the latest record
+LOCK_X type.
+Thus, this implements a 'mini-rollback' that releases the latest record
locks we set. */
int
@@ -1457,16 +1466,18 @@
btr_pcur_t* clust_pcur = prebuilt->clust_pcur;
trx_t* trx = prebuilt->trx;
rec_t* rec;
- mtr_t mtr;
-
+ mtr_t mtr;
+
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
- if (!srv_locks_unsafe_for_binlog) {
+
+ if (!(srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)) {
fprintf(stderr,
"InnoDB: Error: calling row_unlock_for_mysql though\n"
-"InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n");
+"InnoDB: srv_locks_unsafe_for_binlog is FALSE and\n"
+"InnoDB: this session is not using READ COMMITTED isolation level.\n");
return(DB_SUCCESS);
}
@@ -1475,19 +1486,12 @@
index = btr_pcur_get_btr_cur(pcur)->index;
- if (UNIV_UNLIKELY(index == NULL)) {
- fprintf(stderr,
-"InnoDB: Error: Index is not set for persistent cursor.\n");
- ut_print_buf(stderr, (const byte*)pcur, sizeof(btr_pcur_t));
- ut_error;
- }
-
- if (trx_new_rec_locks_contain(trx, index)) {
+ if (index != NULL && trx_new_rec_locks_contain(trx, index)) {
mtr_start(&mtr);
-
+
/* Restore the cursor position and find the record */
-
+
if (!has_latches_on_recs) {
btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
}
@@ -1504,7 +1508,7 @@
garbage! */
if (index->type & DICT_CLUSTERED) {
-
+
goto func_exit;
}
}
@@ -1514,7 +1518,7 @@
if (index != NULL && trx_new_rec_locks_contain(trx, index)) {
mtr_start(&mtr);
-
+
/* Restore the cursor position and find the record */
if (!has_latches_on_recs) {
@@ -1528,10 +1532,10 @@
mtr_commit(&mtr);
}
-
+
func_exit:
trx->op_info = "";
-
+
return(DB_SUCCESS);
}
@@ -1565,7 +1569,7 @@
if (err == DB_LOCK_WAIT) {
/* Handle lock wait here */
-
+
que_thr_stop_for_mysql(thr);
srv_suspend_mysql_thread(thr);
@@ -1580,7 +1584,7 @@
}
/* Retry operation after a normal lock wait */
-
+
goto run_again;
}
@@ -1618,8 +1622,8 @@
clust_index = dict_table_get_first_index(table);
if (dtype_get_mtype(dict_index_get_nth_type(clust_index, 0))
- == DATA_SYS) {
- return(TRUE);
+ == DATA_SYS) {
+ return(TRUE);
}
return(FALSE);
@@ -1707,7 +1711,7 @@
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == 0);
-
+
rw_lock_s_lock(&dict_operation_lock);
trx->dict_operation_lock_mode = RW_S_LATCH;
@@ -1738,8 +1742,8 @@
trx_t* trx) /* in: transaction */
{
ut_a(trx->dict_operation_lock_mode == 0
- || trx->dict_operation_lock_mode == RW_X_LATCH);
-
+ || trx->dict_operation_lock_mode == RW_X_LATCH);
+
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks or lock waits can occur then in these operations */
@@ -1795,7 +1799,7 @@
ut_ad(mutex_own(&(dict_sys->mutex)));
#endif /* UNIV_SYNC_DEBUG */
ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
-
+
if (srv_created_new_raw) {
fputs(
"InnoDB: A new raw disk partition was initialized or\n"
@@ -1812,7 +1816,7 @@
}
trx->op_info = "creating table";
-
+
if (row_mysql_is_system_table(table->name)) {
fprintf(stderr,
@@ -1849,7 +1853,7 @@
#sql-table in the tablespace. We have here a special
mechanism to recover such tables by renaming them to
rsql... */
-
+
return(row_mysql_recover_tmp_table(table, trx));
}
@@ -1868,7 +1872,7 @@
/* Table equals "innodb_monitor":
start monitor prints */
-
+
srv_print_innodb_monitor = TRUE;
/* The lock timeout monitor thread also takes care
@@ -1897,7 +1901,7 @@
} else if (table_name_len == sizeof S_innodb_mem_validate
&& !memcmp(table_name, S_innodb_mem_validate,
sizeof S_innodb_mem_validate)) {
- /* We define here a debugging feature intended for
+ /* We define here a debugging feature intended for
developers */
fputs("Validating InnoDB memory:\n"
@@ -1917,7 +1921,7 @@
heap = mem_heap_create(512);
trx->dict_operation = TRUE;
-
+
node = tab_create_graph_create(table, heap);
thr = pars_complete_graph_for_exec(node, trx, heap);
@@ -1929,30 +1933,30 @@
if (err != DB_SUCCESS) {
/* We have special error handling here */
-
+
trx->error_state = DB_SUCCESS;
-
+
trx_general_rollback_for_mysql(trx, FALSE, NULL);
if (err == DB_OUT_OF_FILE_SPACE) {
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
- fputs(" InnoDB: Warning: cannot create table ",
+ fputs(" InnoDB: Warning: cannot create table ",
stderr);
- ut_print_name(stderr, trx, table->name);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs(" because tablespace full\n", stderr);
if (dict_table_get_low(table->name)) {
- row_drop_table_for_mysql(table->name, trx,
+ row_drop_table_for_mysql(table->name, trx,
FALSE);
}
} else if (err == DB_DUPLICATE_KEY) {
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, table->name);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs(" already exists in InnoDB internal\n"
"InnoDB: data dictionary. Have you deleted the .frm file\n"
"InnoDB: and not used DROP TABLE? Have you used DROP DATABASE\n"
@@ -1967,7 +1971,7 @@
"InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n", stderr);
}
-
+
/* We may also get err == DB_ERROR if the .ibd file for the
table already exists */
@@ -2005,13 +2009,13 @@
ulint err;
ulint i, j;
ulint len;
-
+
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
ut_ad(mutex_own(&(dict_sys->mutex)));
#endif /* UNIV_SYNC_DEBUG */
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
+
trx->op_info = "creating index";
trx_start_if_not_started(trx);
@@ -2023,13 +2027,13 @@
for (i = 0; i < dict_index_get_n_fields(index); i++) {
for (j = 0; j < i; j++) {
if (0 == ut_strcmp(
- dict_index_get_nth_field(index, j)->name,
- dict_index_get_nth_field(index, i)->name)) {
+ dict_index_get_nth_field(index, j)->name,
+ dict_index_get_nth_field(index, i)->name)) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: column ", stderr);
- ut_print_name(stderr, trx,
+ ut_print_name(stderr, trx, FALSE,
dict_index_get_nth_field(index, i)->name);
fputs(" appears twice in ", stderr);
dict_index_name_print(stderr, trx, index);
@@ -2042,7 +2046,7 @@
goto error_handling;
}
}
-
+
/* Check also that prefix_len and actual length
< DICT_MAX_INDEX_COL_LEN */
@@ -2051,7 +2055,7 @@
if (field_lengths) {
len = ut_max(len, field_lengths[i]);
}
-
+
if (len >= DICT_MAX_INDEX_COL_LEN) {
err = DB_TOO_BIG_RECORD;
@@ -2078,14 +2082,14 @@
ut_a(thr == que_fork_start_command(que_node_get_parent(thr)));
que_run_threads(thr);
- err = trx->error_state;
+ err = trx->error_state;
que_graph_free((que_t*) que_node_get_parent(thr));
error_handling:
if (err != DB_SUCCESS) {
/* We have special error handling here */
-
+
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
@@ -2094,7 +2098,7 @@
trx->error_state = DB_SUCCESS;
}
-
+
trx->op_info = "";
return((int) err);
@@ -2133,7 +2137,7 @@
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
ut_a(sql_string);
-
+
trx->op_info = "adding foreign keys";
trx_start_if_not_started(trx);
@@ -2155,7 +2159,7 @@
if (err != DB_SUCCESS) {
/* We have special error handling here */
-
+
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
@@ -2194,22 +2198,22 @@
trx->check_foreigns = FALSE;
/* fputs("InnoDB: Error: Dropping table ", stderr);
- ut_print_name(stderr, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs(" in background drop list\n", stderr); */
- /* Try to drop the table in InnoDB */
+ /* Try to drop the table in InnoDB */
+
+ error = row_drop_table_for_mysql(name, trx, FALSE);
- error = row_drop_table_for_mysql(name, trx, FALSE);
-
/* Flush the log to reduce probability that the .frm files and
the InnoDB data dictionary get out-of-sync if the user runs
with innodb_flush_log_at_trx_commit = 0 */
-
+
log_buffer_flush_to_disk();
- trx_commit_for_mysql(trx);
+ trx_commit_for_mysql(trx);
- trx_free_for_background(trx);
+ trx_free_for_background(trx);
return((int) error);
}
@@ -2229,7 +2233,7 @@
dict_table_t* table;
ulint n_tables;
ulint n_tables_dropped = 0;
-loop:
+loop:
mutex_enter(&kernel_mutex);
if (!row_mysql_drop_list_inited) {
@@ -2239,7 +2243,7 @@
}
drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
-
+
n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
mutex_exit(&kernel_mutex);
@@ -2255,12 +2259,12 @@
mutex_exit(&(dict_sys->mutex));
if (table == NULL) {
- /* If for some reason the table has already been dropped
+ /* If for some reason the table has already been dropped
through some other mechanism, do not try to drop it */
- goto already_dropped;
+ goto already_dropped;
}
-
+
if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
drop->table_name)) {
/* If the DROP fails for some table, we return, and let the
@@ -2276,7 +2280,7 @@
UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Dropped table %s in background drop queue.\n",
drop->table_name);
@@ -2308,7 +2312,7 @@
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
-
+
return(UT_LIST_GET_LEN(row_mysql_drop_list));
}
@@ -2327,7 +2331,7 @@
dict_table_t* table) /* in: table */
{
row_mysql_drop_t* drop;
-
+
mutex_enter(&kernel_mutex);
if (!row_mysql_drop_list_inited) {
@@ -2335,14 +2339,14 @@
UT_LIST_INIT(row_mysql_drop_list);
row_mysql_drop_list_inited = TRUE;
}
-
+
/* Look if the table already is in the drop list */
drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
while (drop != NULL) {
if (strcmp(drop->table_name, table->name) == 0) {
/* Already in the list */
-
+
mutex_exit(&kernel_mutex);
return(FALSE);
@@ -2354,11 +2358,11 @@
drop = mem_alloc(sizeof(row_mysql_drop_t));
drop->table_name = mem_strdup(table->name);
-
+
UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
-
+
/* fputs("InnoDB: Adding table ", stderr);
- ut_print_name(stderr, drop->table_name);
+ ut_print_name(stderr, trx, TRUE, drop->table_name);
fputs(" to background drop list\n", stderr); */
mutex_exit(&kernel_mutex);
@@ -2382,11 +2386,9 @@
dict_foreign_t* foreign;
dulint new_id;
dict_table_t* table;
- que_thr_t* thr;
- que_t* graph = NULL;
ibool success;
ulint err;
- char* buf;
+ pars_info_t* info = NULL;
/* How do we prevent crashes caused by ongoing operations on the table? Old
operations could try to access non-existent pages.
@@ -2408,36 +2410,6 @@
5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we
do not allow the discard. We also reserve the data dictionary latch. */
- static const char discard_tablespace_proc1[] =
- "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
- "old_id CHAR;\n"
- "new_id CHAR;\n"
- "new_id_low INT;\n"
- "new_id_high INT;\n"
- "table_name CHAR;\n"
- "BEGIN\n"
- "table_name := '";
- static const char discard_tablespace_proc2[] =
- "';\n"
- "new_id_high := %lu;\n"
- "new_id_low := %lu;\n"
- "new_id := CONCAT(TO_BINARY(new_id_high, 4), TO_BINARY(new_id_low, 4));\n"
- "SELECT ID INTO old_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = table_name;\n"
- "IF (SQL %% NOTFOUND) THEN\n"
- " COMMIT WORK;\n"
- " RETURN;\n"
- "END IF;\n"
- "UPDATE SYS_TABLES SET ID = new_id\n"
- "WHERE ID = old_id;\n"
- "UPDATE SYS_COLUMNS SET TABLE_ID = new_id\n"
- "WHERE TABLE_ID = old_id;\n"
- "UPDATE SYS_INDEXES SET TABLE_ID = new_id\n"
- "WHERE TABLE_ID = old_id;\n"
- "COMMIT WORK;\n"
- "END;\n";
-
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx->op_info = "discarding tablespace";
@@ -2459,7 +2431,7 @@
if (table->space == 0) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: is in the system tablespace 0 which cannot be discarded\n", stderr);
err = DB_ERROR;
@@ -2469,9 +2441,9 @@
if (table->n_foreign_key_checks_running > 0) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: You are trying to DISCARD table ", stderr);
- ut_print_name(stderr, trx, table->name);
+ ut_print_timestamp(stderr);
+ fputs(" InnoDB: You are trying to DISCARD table ", stderr);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs("\n"
"InnoDB: though there is a foreign key check running on it.\n"
"InnoDB: Cannot discard the table.\n",
@@ -2486,7 +2458,7 @@
some other table (not the table itself) */
foreign = UT_LIST_GET_FIRST(table->referenced_list);
-
+
while (foreign && foreign->foreign_table == table) {
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
}
@@ -2505,10 +2477,10 @@
ut_print_timestamp(ef);
fputs(" Cannot DISCARD table ", ef);
- ut_print_name(ef, trx, name);
+ ut_print_name(ef, trx, TRUE, name);
fputs("\n"
"because it is referenced by ", ef);
- ut_print_name(ef, trx, foreign->foreign_table_name);
+ ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex);
@@ -2517,35 +2489,34 @@
new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
- buf = mem_alloc((sizeof discard_tablespace_proc1) +
- (sizeof discard_tablespace_proc2) +
- 20 + ut_strlenq(name, '\''));
-
- memcpy(buf, discard_tablespace_proc1, sizeof discard_tablespace_proc1);
- sprintf(ut_strcpyq(buf + (sizeof discard_tablespace_proc1 - 1),
- '\'', name),
- discard_tablespace_proc2,
- (ulong) ut_dulint_get_high(new_id),
- (ulong) ut_dulint_get_low(new_id));
-
- graph = pars_sql(buf);
-
- ut_a(graph);
-
/* Remove any locks there are on the table or its records */
-
lock_reset_all_on_table(table);
- graph->trx = trx;
- trx->graph = NULL;
-
- graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
+ info = pars_info_create();
- ut_a(thr = que_fork_start_command(graph));
+ pars_info_add_str_literal(info, "table_name", name);
+ pars_info_add_dulint_literal(info, "new_id", new_id);
- que_run_threads(thr);
-
- err = trx->error_state;
+ err = que_eval_sql(info,
+ "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
+ "old_id CHAR;\n"
+ "BEGIN\n"
+ "SELECT ID INTO old_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = :table_name;\n"
+ "IF (SQL % NOTFOUND) THEN\n"
+ " COMMIT WORK;\n"
+ " RETURN;\n"
+ "END IF;\n"
+ "UPDATE SYS_TABLES SET ID = :new_id\n"
+ " WHERE ID = old_id;\n"
+ "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
+ " WHERE TABLE_ID = old_id;\n"
+ "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
+ " WHERE TABLE_ID = old_id;\n"
+ "COMMIT WORK;\n"
+ "END;\n"
+ , trx);
if (err != DB_SUCCESS) {
trx->error_state = DB_SUCCESS;
@@ -2569,14 +2540,11 @@
table->ibd_file_missing = TRUE;
}
}
-funct_exit:
- trx_commit_for_mysql(trx);
- row_mysql_unlock_data_dictionary(trx);
+funct_exit:
+ trx_commit_for_mysql(trx);
- if (graph) {
- que_graph_free(graph);
- }
+ row_mysql_unlock_data_dictionary(trx);
trx->op_info = "";
@@ -2606,7 +2574,7 @@
trx->op_info = "importing tablespace";
current_lsn = log_get_lsn();
-
+
/* It is possible, though very improbable, that the lsn's in the
tablespace to be imported have risen above the current system lsn, if
a lengthy purge, ibuf merge, or rollback was performed on a backup
@@ -2624,7 +2592,7 @@
if (!success) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: cannot reset lsn's in table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n", stderr);
@@ -2645,7 +2613,7 @@
if (!table) {
ut_print_timestamp(stderr);
fputs(" InnoDB: table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: does not exist in the InnoDB data dictionary\n"
"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
@@ -2659,7 +2627,7 @@
if (table->space == 0) {
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: is in the system tablespace 0 which cannot be imported\n", stderr);
err = DB_ERROR;
@@ -2672,7 +2640,7 @@
fputs(
" InnoDB: Error: you are trying to IMPORT a tablespace\n"
"InnoDB: ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs(", though you have not called DISCARD on it yet\n"
"InnoDB: during the lifetime of the mysqld process!\n", stderr);
@@ -2697,7 +2665,7 @@
fputs(
" InnoDB: cannot find or open in the database directory the .ibd file of\n"
"InnoDB: table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
stderr);
@@ -2706,8 +2674,8 @@
err = DB_ERROR;
}
-funct_exit:
- trx_commit_for_mysql(trx);
+funct_exit:
+ trx_commit_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx);
@@ -2736,9 +2704,7 @@
btr_pcur_t pcur;
mtr_t mtr;
dulint new_id;
- char* sql;
- que_thr_t* thr;
- que_t* graph = NULL;
+ pars_info_t* info = NULL;
/* How do we prevent crashes caused by ongoing operations on the table? Old
operations could try to access non-existent pages.
@@ -2766,30 +2732,6 @@
5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we
do not allow the TRUNCATE. We also reserve the data dictionary latch. */
- static const char renumber_tablespace_proc[] =
- "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n"
- "old_id CHAR;\n"
- "new_id CHAR;\n"
- "old_id_low INT;\n"
- "old_id_high INT;\n"
- "new_id_low INT;\n"
- "new_id_high INT;\n"
- "BEGIN\n"
- "old_id_high := %lu;\n"
- "old_id_low := %lu;\n"
- "new_id_high := %lu;\n"
- "new_id_low := %lu;\n"
- "old_id := CONCAT(TO_BINARY(old_id_high, 4), TO_BINARY(old_id_low, 4));\n"
- "new_id := CONCAT(TO_BINARY(new_id_high, 4), TO_BINARY(new_id_low, 4));\n"
- "UPDATE SYS_TABLES SET ID = new_id\n"
- "WHERE ID = old_id;\n"
- "UPDATE SYS_COLUMNS SET TABLE_ID = new_id\n"
- "WHERE TABLE_ID = old_id;\n"
- "UPDATE SYS_INDEXES SET TABLE_ID = new_id\n"
- "WHERE TABLE_ID = old_id;\n"
- "COMMIT WORK;\n"
- "END;\n";
-
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_ad(table);
@@ -2800,7 +2742,7 @@
"InnoDB: database modifications by the user. Shut down\n"
"InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
"InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ stderr);
return(DB_ERROR);
}
@@ -2843,10 +2785,10 @@
ut_print_timestamp(ef);
fputs(" Cannot truncate table ", ef);
- ut_print_name(ef, trx, table->name);
+ ut_print_name(ef, trx, TRUE, table->name);
fputs(" by DROP+CREATE\n"
"InnoDB: because it is referenced by ", ef);
- ut_print_name(ef, trx, foreign->foreign_table_name);
+ ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex);
@@ -2862,8 +2804,8 @@
if (table->n_foreign_key_checks_running > 0) {
ut_print_timestamp(stderr);
- fputs(" InnoDB: Cannot truncate table ", stderr);
- ut_print_name(stderr, trx, table->name);
+ fputs(" InnoDB: Cannot truncate table ", stderr);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs(" by DROP+CREATE\n"
"InnoDB: because there is a foreign key check running on it.\n",
stderr);
@@ -2950,43 +2892,35 @@
btr_pcur_close(&pcur);
mtr_commit(&mtr);
- new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
-
- mem_heap_empty(heap);
- sql = mem_heap_alloc(heap, (sizeof renumber_tablespace_proc) + 40);
- sprintf(sql, renumber_tablespace_proc,
- (ulong) ut_dulint_get_high(table->id),
- (ulong) ut_dulint_get_low(table->id),
- (ulong) ut_dulint_get_high(new_id),
- (ulong) ut_dulint_get_low(new_id));
-
- graph = pars_sql(sql);
-
- ut_a(graph);
-
mem_heap_free(heap);
- graph->trx = trx;
- trx->graph = NULL;
-
- graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
-
- thr = que_fork_start_command(graph);
- ut_a(thr);
+ new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID);
- que_run_threads(thr);
+ info = pars_info_create();
- que_graph_free(graph);
+ pars_info_add_dulint_literal(info, "old_id", table->id);
+ pars_info_add_dulint_literal(info, "new_id", new_id);
- err = trx->error_state;
+ err = que_eval_sql(info,
+ "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n"
+ "BEGIN\n"
+ "UPDATE SYS_TABLES SET ID = :new_id\n"
+ " WHERE ID = :old_id;\n"
+ "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
+ " WHERE TABLE_ID = :old_id;\n"
+ "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
+ " WHERE TABLE_ID = :old_id;\n"
+ "COMMIT WORK;\n"
+ "END;\n"
+ , trx);
if (err != DB_SUCCESS) {
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
trx->error_state = DB_SUCCESS;
ut_print_timestamp(stderr);
-fputs(" InnoDB: Unable to assign a new identifier to table ", stderr);
- ut_print_name(stderr, trx, table->name);
+fputs(" InnoDB: Unable to assign a new identifier to table ", stderr);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs("\n"
"InnoDB: after truncating it. Background processes may corrupt the table!\n",
stderr);
@@ -2998,7 +2932,7 @@
dict_table_autoinc_initialize(table, 0);
dict_update_statistics(table);
- trx_commit_for_mysql(trx);
+ trx_commit_for_mysql(trx);
funct_exit:
@@ -3028,83 +2962,13 @@
dict_foreign_t* foreign;
dict_table_t* table;
ulint space_id;
- que_thr_t* thr;
- que_t* graph;
ulint err;
const char* table_name;
ulint namelen;
char* dir_path_of_temp_table = NULL;
ibool success;
ibool locked_dictionary = FALSE;
- char* quoted_name;
- char* sql;
-
- /* We use the private SQL parser of Innobase to generate the
- query graphs needed in deleting the dictionary data from system
- tables in Innobase. Deleting a row from SYS_INDEXES table also
- frees the file segments of the B-tree associated with the index. */
-
- static const char str1[] =
- "PROCEDURE DROP_TABLE_PROC () IS\n"
- "table_name CHAR;\n"
- "sys_foreign_id CHAR;\n"
- "table_id CHAR;\n"
- "index_id CHAR;\n"
- "foreign_id CHAR;\n"
- "found INT;\n"
- "BEGIN\n"
- "table_name := ";
- static const char str2[] =
- ";\n"
- "SELECT ID INTO table_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = table_name;\n"
- "IF (SQL % NOTFOUND) THEN\n"
- " COMMIT WORK;\n"
- " RETURN;\n"
- "END IF;\n"
- "found := 1;\n"
- "SELECT ID INTO sys_foreign_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME = 'SYS_FOREIGN';\n"
- "IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "IF (table_name = 'SYS_FOREIGN') THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "IF (table_name = 'SYS_FOREIGN_COLS') THEN\n"
- " found := 0;\n"
- "END IF;\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO foreign_id\n"
- " FROM SYS_FOREIGN\n"
- " WHERE FOR_NAME = table_name\n"
- " AND TO_BINARY(FOR_NAME) = TO_BINARY(table_name);\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE"
- " DELETE FROM SYS_FOREIGN_COLS WHERE ID = foreign_id;\n"
- " DELETE FROM SYS_FOREIGN WHERE ID = foreign_id;\n"
- " END IF;\n"
- "END LOOP;\n"
- "found := 1;\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO index_id\n"
- " FROM SYS_INDEXES\n"
- " WHERE TABLE_ID = table_id;\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE"
- " DELETE FROM SYS_FIELDS WHERE INDEX_ID = index_id;\n"
- " DELETE FROM SYS_INDEXES WHERE ID = index_id\n"
- " AND TABLE_ID = table_id;\n"
- " END IF;\n"
- "END LOOP;\n"
- "DELETE FROM SYS_COLUMNS WHERE TABLE_ID = table_id;\n"
- "DELETE FROM SYS_TABLES WHERE ID = table_id;\n"
- "COMMIT WORK;\n"
- "END;\n";
+ pars_info_t* info = NULL;
ut_a(name != NULL);
@@ -3115,7 +2979,7 @@
"InnoDB: database modifications by the user. Shut down\n"
"InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
"InnoDB: with raw, and innodb_force_... is removed.\n",
- stderr);
+ stderr);
return(DB_ERROR);
}
@@ -3139,7 +3003,7 @@
/* Table name equals "innodb_monitor":
stop monitor prints */
-
+
srv_print_innodb_monitor = FALSE;
srv_print_innodb_lock_monitor = FALSE;
} else if (namelen == sizeof S_innodb_lock_monitor
@@ -3159,14 +3023,6 @@
srv_print_innodb_table_monitor = FALSE;
}
- quoted_name = mem_strdupq(name, '\'');
- namelen = strlen(quoted_name);
- sql = mem_alloc((sizeof str1) + (sizeof str2) - 2 + 1 + namelen);
- memcpy(sql, str1, (sizeof str1) - 1);
- memcpy(sql + (sizeof str1) - 1, quoted_name, namelen);
- memcpy(sql + (sizeof str1) - 1 + namelen, str2, sizeof str2);
- mem_free(quoted_name);
-
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
@@ -3183,28 +3039,18 @@
ut_ad(mutex_own(&(dict_sys->mutex)));
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
-
- graph = pars_sql(sql);
-
- ut_a(graph);
- mem_free(sql);
-
- graph->trx = trx;
- trx->graph = NULL;
-
- graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
table = dict_table_get_low(name);
if (!table) {
err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs(" does not exist in the InnoDB internal\n"
- "InnoDB: data dictionary though MySQL is trying to drop it.\n"
- "InnoDB: Have you copied the .frm file of the table to the\n"
+ "InnoDB: data dictionary though MySQL is trying to drop it.\n"
+ "InnoDB: Have you copied the .frm file of the table to the\n"
"InnoDB: MySQL database directory from another database?\n"
"InnoDB: You can look for further help from\n"
"InnoDB: http://dev.mysql.com/doc/mysql/en/"
@@ -3216,7 +3062,7 @@
some other table (not the table itself) */
foreign = UT_LIST_GET_FIRST(table->referenced_list);
-
+
while (foreign && foreign->foreign_table == table) {
check_next_foreign:
foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
@@ -3237,10 +3083,10 @@
ut_print_timestamp(ef);
fputs(" Cannot drop table ", ef);
- ut_print_name(ef, trx, name);
+ ut_print_name(ef, trx, TRUE, name);
fputs("\n"
"because it is referenced by ", ef);
- ut_print_name(ef, trx, foreign->foreign_table_name);
+ ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
putc('\n', ef);
mutex_exit(&dict_foreign_err_mutex);
@@ -3256,15 +3102,15 @@
added = row_add_table_to_background_drop_list(table);
- if (added) {
+ if (added) {
ut_print_timestamp(stderr);
-fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr);
- ut_print_name(stderr, trx, table->name);
+fputs(" InnoDB: Warning: MySQL is trying to drop table ", stderr);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs("\n"
"InnoDB: though there are still open handles to it.\n"
"InnoDB: Adding the table to the background drop queue.\n",
stderr);
-
+
/* We return DB_SUCCESS to MySQL though the drop will
happen lazily later */
@@ -3290,9 +3136,9 @@
added = row_add_table_to_background_drop_list(table);
if (added) {
- ut_print_timestamp(stderr);
-fputs(" InnoDB: You are trying to drop table ", stderr);
- ut_print_name(stderr, trx, table->name);
+ ut_print_timestamp(stderr);
+fputs(" InnoDB: You are trying to drop table ", stderr);
+ ut_print_name(stderr, trx, TRUE, table->name);
fputs("\n"
"InnoDB: though there is a foreign key check running on it.\n"
"InnoDB: Adding the table to the background drop queue.\n",
@@ -3309,26 +3155,88 @@
goto funct_exit;
}
-
+
/* Remove any locks there are on the table or its records */
-
+
lock_reset_all_on_table(table);
trx->dict_operation = TRUE;
trx->table_id = table->id;
- ut_a(thr = que_fork_start_command(graph));
+ /* We use the private SQL parser of Innobase to generate the
+ query graphs needed in deleting the dictionary data from system
+ tables in Innobase. Deleting a row from SYS_INDEXES table also
+ frees the file segments of the B-tree associated with the index. */
- que_run_threads(thr);
+ info = pars_info_create();
- err = trx->error_state;
+ pars_info_add_str_literal(info, "table_name", name);
+
+ err = que_eval_sql(info,
+ "PROCEDURE DROP_TABLE_PROC () IS\n"
+ "sys_foreign_id CHAR;\n"
+ "table_id CHAR;\n"
+ "index_id CHAR;\n"
+ "foreign_id CHAR;\n"
+ "found INT;\n"
+ "BEGIN\n"
+ "SELECT ID INTO table_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = :table_name;\n"
+ "IF (SQL % NOTFOUND) THEN\n"
+ " COMMIT WORK;\n"
+ " RETURN;\n"
+ "END IF;\n"
+ "found := 1;\n"
+ "SELECT ID INTO sys_foreign_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = 'SYS_FOREIGN';\n"
+ "IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "IF (:table_name = 'SYS_FOREIGN') THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "IF (:table_name = 'SYS_FOREIGN_COLS') THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "WHILE found = 1 LOOP\n"
+ " SELECT ID INTO foreign_id\n"
+ " FROM SYS_FOREIGN\n"
+ " WHERE FOR_NAME = :table_name\n"
+ " AND TO_BINARY(FOR_NAME) = TO_BINARY(:table_name);\n"
+ " IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ " ELSE"
+ " DELETE FROM SYS_FOREIGN_COLS WHERE ID = foreign_id;\n"
+ " DELETE FROM SYS_FOREIGN WHERE ID = foreign_id;\n"
+ " END IF;\n"
+ "END LOOP;\n"
+ "found := 1;\n"
+ "WHILE found = 1 LOOP\n"
+ " SELECT ID INTO index_id\n"
+ " FROM SYS_INDEXES\n"
+ " WHERE TABLE_ID = table_id;\n"
+ " IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ " ELSE"
+ " DELETE FROM SYS_FIELDS WHERE INDEX_ID = index_id;\n"
+ " DELETE FROM SYS_INDEXES WHERE ID = index_id\n"
+ " AND TABLE_ID = table_id;\n"
+ " END IF;\n"
+ "END LOOP;\n"
+ "DELETE FROM SYS_COLUMNS WHERE TABLE_ID = table_id;\n"
+ "DELETE FROM SYS_TABLES WHERE ID = table_id;\n"
+ "COMMIT WORK;\n"
+ "END;\n"
+ , trx);
if (err != DB_SUCCESS) {
ut_a(err == DB_OUT_OF_FILE_SPACE);
err = DB_MUST_GET_MORE_FILE_SPACE;
-
- row_mysql_handle_errors(&err, trx, thr, NULL);
+
+ row_mysql_handle_errors(&err, trx, NULL, NULL);
ut_error;
} else {
@@ -3336,7 +3244,7 @@
const char* name_or_path;
space_id = table->space;
-
+
if (table->dir_path_of_temp_table != NULL) {
dir_path_of_temp_table =
mem_strdup(table->dir_path_of_temp_table);
@@ -3353,7 +3261,7 @@
ut_print_timestamp(stderr);
fputs(" InnoDB: Error: not able to remove table ",
stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs(" from the dictionary cache!\n", stderr);
err = DB_ERROR;
}
@@ -3370,8 +3278,8 @@
fprintf(stderr,
"InnoDB: We removed now the InnoDB internal data dictionary entry\n"
-"InnoDB: of table ");
- ut_print_name(stderr, trx, name);
+"InnoDB: of table ");
+ ut_print_name(stderr, trx, TRUE, name);
fprintf(stderr, ".\n");
goto funct_exit;
@@ -3382,15 +3290,15 @@
if (!success) {
fprintf(stderr,
"InnoDB: We removed now the InnoDB internal data dictionary entry\n"
-"InnoDB: of table ");
- ut_print_name(stderr, trx, name);
+"InnoDB: of table ");
+ ut_print_name(stderr, trx, TRUE, name);
fprintf(stderr, ".\n");
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: not able to delete tablespace %lu of table ",
(ulong) space_id);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("!\n", stderr);
err = DB_ERROR;
}
@@ -3398,18 +3306,16 @@
}
funct_exit:
- trx_commit_for_mysql(trx);
+ trx_commit_for_mysql(trx);
if (locked_dictionary) {
- row_mysql_unlock_data_dictionary(trx);
+ row_mysql_unlock_data_dictionary(trx);
}
if (dir_path_of_temp_table) {
mem_free(dir_path_of_temp_table);
}
- que_graph_free(graph);
-
trx->op_info = "";
#ifndef UNIV_HOTBACKUP
@@ -3429,17 +3335,17 @@
const char* name, /* in: database name which ends to '/' */
trx_t* trx) /* in: transaction handle */
{
- dict_table_t* table;
+ dict_table_t* table;
char* table_name;
int err = DB_SUCCESS;
ulint namelen = strlen(name);
-
+
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(name != NULL);
ut_a(name[namelen - 1] == '/');
-
+
trx->op_info = "dropping database";
-
+
trx_start_if_not_started(trx);
loop:
row_mysql_lock_data_dictionary(trx);
@@ -3460,17 +3366,17 @@
ut_print_timestamp(stderr);
fputs(
" InnoDB: Warning: MySQL is trying to drop database ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fputs("\n"
"InnoDB: though there are still open handles to table ", stderr);
- ut_print_name(stderr, trx, table_name);
+ ut_print_name(stderr, trx, TRUE, table_name);
fputs(".\n", stderr);
- os_thread_sleep(1000000);
+ os_thread_sleep(1000000);
- mem_free(table_name);
+ mem_free(table_name);
- goto loop;
+ goto loop;
}
err = row_drop_table_for_mysql(table_name, trx, TRUE);
@@ -3479,10 +3385,10 @@
if (err != DB_SUCCESS) {
fputs("InnoDB: DROP DATABASE ", stderr);
- ut_print_name(stderr, trx, name);
+ ut_print_name(stderr, trx, TRUE, name);
fprintf(stderr, " failed with error %lu for table ",
(ulint) err);
- ut_print_name(stderr, trx, table_name);
+ ut_print_name(stderr, trx, TRUE, table_name);
putc('\n', stderr);
break;
}
@@ -3491,7 +3397,7 @@
trx_commit_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx);
-
+
trx->op_info = "";
return(err);
@@ -3508,7 +3414,64 @@
const char* name) /* in: table name in the form
'database/tablename' */
{
- return(strstr(name, "/#sql") != NULL);
+ /* return(strstr(name, "/#sql") != NULL); */
+ return(strstr(name, "/@0023sql") != NULL);
+}
+
+/********************************************************************
+Delete a single constraint. */
+static
+int
+row_delete_constraint_low(
+/*======================*/
+ /* out: error code or DB_SUCCESS */
+ const char* id, /* in: constraint id */
+ trx_t* trx) /* in: transaction handle */
+{
+ pars_info_t* info = pars_info_create();
+
+ pars_info_add_str_literal(info, "id", id);
+
+ return(que_eval_sql(info,
+ "PROCEDURE DELETE_CONSTRAINT () IS\n"
+ "BEGIN\n"
+ "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n"
+ "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n"
+ "END;\n"
+ , trx));
+}
+
+/********************************************************************
+Delete a single constraint. */
+static
+int
+row_delete_constraint(
+/*==================*/
+ /* out: error code or DB_SUCCESS */
+ const char* id, /* in: constraint id */
+ const char* database_name, /* in: database name, with the
+ trailing '/' */
+ mem_heap_t* heap, /* in: memory heap */
+ trx_t* trx) /* in: transaction handle */
+{
+ ulint err;
+
+ /* New format constraints have ids <databasename>/<constraintname>. */
+ err = row_delete_constraint_low(
+ mem_heap_strcat(heap, database_name, id), trx);
+
+ if ((err == DB_SUCCESS) && !strchr(id, '/')) {
+ /* Old format < 4.0.18 constraints have constraint ids
+ <number>_<number>. We only try deleting them if the
+ constraint name does not contain a '/' character, otherwise
+ deleting a new format constraint named 'foo/bar' from
+ database 'baz' would remove constraint 'bar' from database
+ 'foo', if it existed. */
+
+ err = row_delete_constraint_low(id, trx);
+ }
+
+ return(err);
}
/*************************************************************************
@@ -3523,100 +3486,13 @@
trx_t* trx) /* in: transaction handle */
{
dict_table_t* table;
- que_thr_t* thr;
- que_t* graph = NULL;
ulint err;
- /* We use the private SQL parser of Innobase to generate the
- query graphs needed in deleting the dictionary data from system
- tables in Innobase. Deleting a row from SYS_INDEXES table also
- frees the file segments of the B-tree associated with the index. */
- static const char str1[] =
- "PROCEDURE RENAME_TABLE_PROC () IS\n"
- "new_table_name CHAR;\n"
- "old_table_name CHAR;\n"
- "gen_constr_prefix CHAR;\n"
- "new_db_name CHAR;\n"
- "foreign_id CHAR;\n"
- "new_foreign_id CHAR;\n"
- "old_db_name_len INT;\n"
- "old_t_name_len INT;\n"
- "new_db_name_len INT;\n"
- "id_len INT;\n"
- "found INT;\n"
- "BEGIN\n"
- "new_table_name := '";
- static const char str2[] =
- "';\nold_table_name := '";
- static const char str3[] =
- "';\n"
- "UPDATE SYS_TABLES SET NAME = new_table_name\n"
- "WHERE NAME = old_table_name;\n";
- static const char str4a1[] = /* drop some constraints of tmp tables */
- "DELETE FROM SYS_FOREIGN_COLS WHERE ID = '";
- static const char str4a2[] = "';\n"
- "DELETE FROM SYS_FOREIGN WHERE ID = '";
- static const char str4a3[] = "';\n";
- static const char str4b[] = /* rename all constraints */
- "found := 1;\n"
- "old_db_name_len := INSTR(old_table_name, '/') - 1;\n"
- "new_db_name_len := INSTR(new_table_name, '/') - 1;\n"
- "new_db_name := SUBSTR(new_table_name, 0, new_db_name_len);\n"
- "old_t_name_len := LENGTH(old_table_name);\n"
- "gen_constr_prefix := CONCAT(old_table_name, '_ibfk_');\n"
- "WHILE found = 1 LOOP\n"
- " SELECT ID INTO foreign_id\n"
- " FROM SYS_FOREIGN\n"
- " WHERE FOR_NAME = old_table_name\n"
- " AND TO_BINARY(FOR_NAME) = TO_BINARY(old_table_name);\n"
- " IF (SQL % NOTFOUND) THEN\n"
- " found := 0;\n"
- " ELSE\n"
- " UPDATE SYS_FOREIGN\n"
- " SET FOR_NAME = new_table_name\n"
- " WHERE ID = foreign_id;\n"
- " id_len := LENGTH(foreign_id);\n"
- " IF (INSTR(foreign_id, '/') > 0) THEN\n"
- " IF (INSTR(foreign_id,\n"
- " gen_constr_prefix) > 0)\n"
- " THEN\n"
- " new_foreign_id :=\n"
- " CONCAT(new_table_name,\n"
- " SUBSTR(foreign_id, old_t_name_len,\n"
- " id_len - old_t_name_len));\n"
- " ELSE\n"
- " new_foreign_id :=\n"
- " CONCAT(new_db_name,\n"
- " SUBSTR(foreign_id,\n"
- " old_db_name_len,\n"
- " id_len - old_db_name_len));\n"
- " END IF;\n"
- " UPDATE SYS_FOREIGN\n"
- " SET ID = new_foreign_id\n"
- " WHERE ID = foreign_id;\n"
- " UPDATE SYS_FOREIGN_COLS\n"
- " SET ID = new_foreign_id\n"
- " WHERE ID = foreign_id;\n"
- " END IF;\n"
- " END IF;\n"
- "END LOOP;\n"
- "UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n"
- "WHERE REF_NAME = old_table_name\n"
- " AND TO_BINARY(REF_NAME) = TO_BINARY(old_table_name);\n";
- static const char str5[] =
- "END;\n";
-
mem_heap_t* heap = NULL;
const char** constraints_to_drop = NULL;
ulint n_constraints_to_drop = 0;
- ibool recovering_temp_table = FALSE;
+ ibool recovering_temp_table = FALSE;
ibool old_is_tmp, new_is_tmp;
- ulint len;
- ulint i;
- ibool success;
- /* length of database name; 0 if not renaming to a temporary table */
- ulint db_name_len;
- char* sql;
- char* sqlend;
+ pars_info_t* info = NULL;
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(old_name != NULL);
@@ -3631,18 +3507,18 @@
"InnoDB: with raw, and innodb_force_... is removed.\n",
stderr);
- trx_commit_for_mysql(trx);
+ trx_commit_for_mysql(trx);
return(DB_ERROR);
}
-
+
if (row_mysql_is_system_table(new_name)) {
-
+
fprintf(stderr,
"InnoDB: Error: trying to create a MySQL system table %s of type InnoDB.\n"
"InnoDB: MySQL system tables must be of the MyISAM type!\n",
new_name);
- trx_commit_for_mysql(trx);
+ trx_commit_for_mysql(trx);
return(DB_ERROR);
}
@@ -3651,11 +3527,11 @@
old_is_tmp = row_is_mysql_tmp_table_name(old_name);
new_is_tmp = row_is_mysql_tmp_table_name(new_name);
-
+
if (row_mysql_is_recovered_tmp_table(new_name)) {
- recovering_temp_table = TRUE;
- } else {
+ recovering_temp_table = TRUE;
+ } else {
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
@@ -3666,41 +3542,35 @@
if (!table) {
err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, old_name);
- fputs(" does not exist in the InnoDB internal\n"
- "InnoDB: data dictionary though MySQL is trying to rename the table.\n"
- "InnoDB: Have you copied the .frm file of the table to the\n"
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, TRUE, old_name);
+ fputs(" does not exist in the InnoDB internal\n"
+ "InnoDB: data dictionary though MySQL is trying to rename the table.\n"
+ "InnoDB: Have you copied the .frm file of the table to the\n"
"InnoDB: MySQL database directory from another database?\n"
"InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/mysql/en/"
+ "InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n", stderr);
goto funct_exit;
}
if (table->ibd_file_missing) {
err = DB_TABLE_NOT_FOUND;
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_name(stderr, trx, old_name);
- fputs(
+ fputs(" InnoDB: Error: table ", stderr);
+ ut_print_name(stderr, trx, TRUE, old_name);
+ fputs(
" does not have an .ibd file in the database directory.\n"
"InnoDB: You can look for further help from\n"
- "InnoDB: http://dev.mysql.com/doc/mysql/en/"
+ "InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n", stderr);
goto funct_exit;
}
- /* calculate the length of the SQL string */
- len = (sizeof str1) + (sizeof str2) + (sizeof str3) + (sizeof str5) - 4
- + ut_strlenq(new_name, '\'') + ut_strlenq(old_name, '\'');
-
if (new_is_tmp) {
- db_name_len = dict_get_db_name_len(old_name) + 1;
-
/* MySQL is doing an ALTER TABLE command and it renames the
original table to a temporary table name. We want to preserve
the original foreign key constraint definitions despite the
@@ -3708,131 +3578,145 @@
the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
heap = mem_heap_create(100);
-
+
err = dict_foreign_parse_drop_constraints(heap, trx,
- table,
- &n_constraints_to_drop,
- &constraints_to_drop);
+ table, &n_constraints_to_drop, &constraints_to_drop);
+
if (err != DB_SUCCESS) {
goto funct_exit;
}
-
- /* reserve space for all database names */
- len += 2 * n_constraints_to_drop
- * (ut_strlenq(old_name, '\'')
- - ut_strlenq(old_name + db_name_len, '\''));
-
- for (i = 0; i < n_constraints_to_drop; i++) {
- ulint addlen
- = 2 * ut_strlenq(constraints_to_drop[i], '\'')
- + ((sizeof str4a1) + (sizeof str4a2)
- + (sizeof str4a3) - 3);
- if (!strchr(constraints_to_drop[i], '/')) {
- addlen *= 2;
- }
- len += addlen;
- }
- } else {
- db_name_len = 0;
- len += (sizeof str4b) - 1;
}
- sql = sqlend = mem_alloc(len + 1);
- memcpy(sql, str1, (sizeof str1) - 1);
- sqlend += (sizeof str1) - 1;
- sqlend = ut_strcpyq(sqlend, '\'', new_name);
- memcpy(sqlend, str2, (sizeof str2) - 1);
- sqlend += (sizeof str2) - 1;
- sqlend = ut_strcpyq(sqlend, '\'', old_name);
- memcpy(sqlend, str3, (sizeof str3) - 1);
- sqlend += (sizeof str3) - 1;
-
- if (db_name_len) {
- /* Internally, old format < 4.0.18 constraints have as the
- constraint id <number>_<number>, while new format constraints
- have <databasename>/<constraintname>. */
+ /* We use the private SQL parser of Innobase to generate the query
+ graphs needed in deleting the dictionary data from system tables in
+ Innobase. Deleting a row from SYS_INDEXES table also frees the file
+ segments of the B-tree associated with the index. */
+
+ info = pars_info_create();
+
+ pars_info_add_str_literal(info, "new_table_name", new_name);
+ pars_info_add_str_literal(info, "old_table_name", old_name);
+
+ err = que_eval_sql(info,
+ "PROCEDURE RENAME_TABLE () IS\n"
+ "BEGIN\n"
+ "UPDATE SYS_TABLES SET NAME = :new_table_name\n"
+ " WHERE NAME = :old_table_name;\n"
+ "END;\n"
+ , trx);
- for (i = 0; i < n_constraints_to_drop; i++) {
- memcpy(sqlend, str4a1, (sizeof str4a1) - 1);
- sqlend += (sizeof str4a1) - 1;
- sqlend = ut_memcpyq(sqlend, '\'',
- old_name, db_name_len);
- sqlend = ut_strcpyq(sqlend, '\'',
- constraints_to_drop[i]);
- memcpy(sqlend, str4a2, (sizeof str4a2) - 1);
- sqlend += (sizeof str4a2) - 1;
- sqlend = ut_memcpyq(sqlend, '\'',
- old_name, db_name_len);
- sqlend = ut_strcpyq(sqlend, '\'',
- constraints_to_drop[i]);
- memcpy(sqlend, str4a3, (sizeof str4a3) - 1);
- sqlend += (sizeof str4a3) - 1;
-
- if (!strchr(constraints_to_drop[i], '/')) {
- /* If this happens to be an old format
- constraint, let us delete it. Since all new
- format constraints contain '/', it does no
- harm to run these DELETEs anyway. */
-
- memcpy(sqlend, str4a1, (sizeof str4a1) - 1);
- sqlend += (sizeof str4a1) - 1;
- sqlend = ut_strcpyq(sqlend, '\'',
- constraints_to_drop[i]);
- memcpy(sqlend, str4a2, (sizeof str4a2) - 1);
- sqlend += (sizeof str4a2) - 1;
- sqlend = ut_strcpyq(sqlend, '\'',
- constraints_to_drop[i]);
- memcpy(sqlend, str4a3, (sizeof str4a3) - 1);
- sqlend += (sizeof str4a3) - 1;
- }
- }
- }
- else {
- memcpy(sqlend, str4b, (sizeof str4b) - 1);
- sqlend += (sizeof str4b) - 1;
- }
-
- memcpy(sqlend, str5, sizeof str5);
- sqlend += sizeof str5;
+ if (err != DB_SUCCESS) {
- ut_a(sqlend == sql + len + 1);
-
- graph = pars_sql(sql);
+ goto end;
+ }
- ut_a(graph);
- mem_free(sql);
+ if (!new_is_tmp) {
+ /* Rename all constraints. */
- graph->trx = trx;
- trx->graph = NULL;
+ info = pars_info_create();
- graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
+ pars_info_add_str_literal(info, "new_table_name", new_name);
+ pars_info_add_str_literal(info, "old_table_name", old_name);
- ut_a(thr = que_fork_start_command(graph));
+ err = que_eval_sql(info,
+ "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n"
+ "gen_constr_prefix CHAR;\n"
+ "new_db_name CHAR;\n"
+ "foreign_id CHAR;\n"
+ "new_foreign_id CHAR;\n"
+ "old_db_name_len INT;\n"
+ "old_t_name_len INT;\n"
+ "new_db_name_len INT;\n"
+ "id_len INT;\n"
+ "found INT;\n"
+ "BEGIN\n"
+ "found := 1;\n"
+ "old_db_name_len := INSTR(:old_table_name, '/') - 1;\n"
+ "new_db_name_len := INSTR(:new_table_name, '/') - 1;\n"
+ "new_db_name := SUBSTR(:new_table_name, 0, new_db_name_len);\n"
+ "old_t_name_len := LENGTH(:old_table_name);\n"
+ "gen_constr_prefix := CONCAT(:old_table_name, '_ibfk_');\n"
+ "WHILE found = 1 LOOP\n"
+ " SELECT ID INTO foreign_id\n"
+ " FROM SYS_FOREIGN\n"
+ " WHERE FOR_NAME = :old_table_name\n"
+ " AND TO_BINARY(FOR_NAME) = TO_BINARY(:old_table_name);\n"
+ " IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ " ELSE\n"
+ " UPDATE SYS_FOREIGN\n"
+ " SET FOR_NAME = :new_table_name\n"
+ " WHERE ID = foreign_id;\n"
+ " id_len := LENGTH(foreign_id);\n"
+ " IF (INSTR(foreign_id, '/') > 0) THEN\n"
+ " IF (INSTR(foreign_id,\n"
+ " gen_constr_prefix) > 0)\n"
+ " THEN\n"
+ " new_foreign_id :=\n"
+ " CONCAT(:new_table_name,\n"
+ " SUBSTR(foreign_id, old_t_name_len,\n"
+ " id_len - old_t_name_len));\n"
+ " ELSE\n"
+ " new_foreign_id :=\n"
+ " CONCAT(new_db_name,\n"
+ " SUBSTR(foreign_id,\n"
+ " old_db_name_len,\n"
+ " id_len - old_db_name_len));\n"
+ " END IF;\n"
+ " UPDATE SYS_FOREIGN\n"
+ " SET ID = new_foreign_id\n"
+ " WHERE ID = foreign_id;\n"
+ " UPDATE SYS_FOREIGN_COLS\n"
+ " SET ID = new_foreign_id\n"
+ " WHERE ID = foreign_id;\n"
+ " END IF;\n"
+ " END IF;\n"
+ "END LOOP;\n"
+ "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n"
+ "WHERE REF_NAME = :old_table_name\n"
+ " AND TO_BINARY(REF_NAME) = TO_BINARY(:old_table_name);\n"
+ "END;\n"
+ , trx);
+
+ } else if (n_constraints_to_drop > 0) {
+ /* Drop some constraints of tmp tables. */
+
+ ulint db_name_len = dict_get_db_name_len(old_name) + 1;
+ char* db_name = mem_heap_strdupl(heap, old_name,
+ db_name_len);
+ ulint i;
- que_run_threads(thr);
+ for (i = 0; i < n_constraints_to_drop; i++) {
+ err = row_delete_constraint(constraints_to_drop[i],
+ db_name, heap, trx);
- err = trx->error_state;
+ if (err != DB_SUCCESS) {
+ break;
+ }
+ }
+ }
+end:
if (err != DB_SUCCESS) {
if (err == DB_DUPLICATE_KEY) {
- ut_print_timestamp(stderr);
+ ut_print_timestamp(stderr);
fputs(
" InnoDB: Error; possible reasons:\n"
"InnoDB: 1) Table rename would cause two FOREIGN KEY constraints\n"
"InnoDB: to have the same internal name in case-insensitive comparison.\n"
"InnoDB: 2) table ", stderr);
- ut_print_name(stderr, trx, new_name);
- fputs(" exists in the InnoDB internal data\n"
+ ut_print_name(stderr, trx, TRUE, new_name);
+ fputs(" exists in the InnoDB internal data\n"
"InnoDB: dictionary though MySQL is trying rename table ", stderr);
- ut_print_name(stderr, trx, old_name);
+ ut_print_name(stderr, trx, TRUE, old_name);
fputs(" to it.\n"
"InnoDB: Have you deleted the .frm file and not used DROP TABLE?\n"
"InnoDB: You can look for further help from\n"
"InnoDB: http://dev.mysql.com/doc/mysql/en/"
"InnoDB_troubleshooting_datadict.html\n"
"InnoDB: If table ", stderr);
- ut_print_name(stderr, trx, new_name);
+ ut_print_name(stderr, trx, TRUE, new_name);
fputs(
" is a temporary table #sql..., then it can be that\n"
"InnoDB: there are still queries running on the table, and it will be\n"
@@ -3850,8 +3734,9 @@
/* The following call will also rename the .ibd data file if
the table is stored in a single-table tablespace */
- success = dict_table_rename_in_cache(table, new_name,
+ ibool success = dict_table_rename_in_cache(table, new_name,
!new_is_tmp);
+
if (!success) {
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, FALSE, NULL);
@@ -3859,9 +3744,9 @@
ut_print_timestamp(stderr);
fputs(" InnoDB: Error in table rename, cannot rename ",
stderr);
- ut_print_name(stderr, trx, old_name);
+ ut_print_name(stderr, trx, TRUE, old_name);
fputs(" to ", stderr);
- ut_print_name(stderr, trx, new_name);
+ ut_print_name(stderr, trx, TRUE, new_name);
putc('\n', stderr);
err = DB_ERROR;
@@ -3870,7 +3755,7 @@
/* We only want to switch off some of the type checking in
an ALTER, not in a RENAME. */
-
+
err = dict_load_foreigns(new_name,
old_is_tmp ? trx->check_foreigns : TRUE);
@@ -3880,7 +3765,7 @@
if (old_is_tmp) {
fputs(" InnoDB: Error: in ALTER TABLE ",
stderr);
- ut_print_name(stderr, trx, new_name);
+ ut_print_name(stderr, trx, TRUE, new_name);
fputs("\n"
"InnoDB: has or is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
@@ -3889,7 +3774,7 @@
fputs(
" InnoDB: Error: in RENAME TABLE table ",
stderr);
- ut_print_name(stderr, trx, new_name);
+ ut_print_name(stderr, trx, TRUE, new_name);
fputs("\n"
"InnoDB: is referenced in foreign key constraints\n"
"InnoDB: which are not compatible with the new table definition.\n",
@@ -3899,26 +3784,22 @@
ut_a(dict_table_rename_in_cache(table,
old_name, FALSE));
trx->error_state = DB_SUCCESS;
- trx_general_rollback_for_mysql(trx, FALSE,
- NULL);
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
trx->error_state = DB_SUCCESS;
}
}
-funct_exit:
- trx_commit_for_mysql(trx);
+
+funct_exit:
+ trx_commit_for_mysql(trx);
if (!recovering_temp_table) {
row_mysql_unlock_data_dictionary(trx);
}
- if (graph) {
- que_graph_free(graph);
- }
-
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
-
+
trx->op_info = "";
return((int) err);
@@ -3955,10 +3836,10 @@
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
*n_rows = 0;
-
+
buf = mem_alloc(UNIV_PAGE_SIZE);
heap = mem_heap_create(100);
-
+
/* Make a dummy template in prebuilt, which we will use
in scanning the index entries */
@@ -3968,7 +3849,7 @@
prebuilt->n_template = 0;
prebuilt->need_to_access_clustered = FALSE;
- dtuple_set_n_fields(prebuilt->search_tuple, 0);
+ dtuple_set_n_fields(prebuilt->search_tuple, 0);
prebuilt->select_lock_type = LOCK_NONE;
cnt = 1000;
@@ -3991,11 +3872,11 @@
}
*n_rows = *n_rows + 1;
-
+
/* row_search... returns the index record in buf, record origin offset
within buf stored in the first 4 bytes, because we have built a dummy
template */
-
+
rec = buf + mach_read_from_4(buf);
if (prev_entry != NULL) {
@@ -4012,15 +3893,15 @@
/* In a unique secondary index we allow equal key values if
they contain SQL NULLs */
- for (i = 0;
- i < dict_index_get_n_ordering_defined_by_user(index);
+ for (i = 0;
+ i < dict_index_get_n_ordering_defined_by_user(index);
i++) {
- if (UNIV_SQL_NULL == dfield_get_len(
- dtuple_get_nth_field(prev_entry, i))) {
+ if (UNIV_SQL_NULL == dfield_get_len(
+ dtuple_get_nth_field(prev_entry, i))) {
- contains_null = TRUE;
- }
- }
+ contains_null = TRUE;
+ }
+ }
if (cmp > 0) {
fputs("InnoDB: index records in a wrong order in ",
@@ -4048,12 +3929,12 @@
mem_heap_empty(heap);
offsets = offsets_;
-
+
prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT);
- goto loop;
+ goto loop;
}
/*************************************************************************
@@ -4070,12 +3951,12 @@
dict_index_t* index;
ulint n_rows;
ulint n_rows_in_table = ULINT_UNDEFINED;
- ulint ret = DB_SUCCESS;
+ ulint ret = DB_SUCCESS;
ulint old_isolation_level;
if (prebuilt->table->ibd_file_missing) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
+ ut_print_timestamp(stderr);
+ fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
@@ -4107,9 +3988,9 @@
while (index != NULL) {
/* fputs("Validating index ", stderr);
- ut_print_name(stderr, index->name);
+ ut_print_name(stderr, trx, FALSE, index->name);
putc('\n', stderr); */
-
+
if (!btr_validate_tree(index->tree, prebuilt->trx)) {
ret = DB_ERROR;
} else {
@@ -4130,7 +4011,7 @@
} else if (n_rows != n_rows_in_table) {
ret = DB_ERROR;
-
+
fputs("Error: ", stderr);
dict_index_name_print(stderr,
prebuilt->trx, index);
@@ -4146,7 +4027,7 @@
/* Restore the original isolation level */
prebuilt->trx->isolation_level = old_isolation_level;
-
+
/* We validate also the whole adaptive hash index for all tables
at every CHECK TABLE */
--- 1.92.7.1/innobase/row/row0sel.c 2006-06-06 23:37:38 +04:00
+++ 1.105/storage/innobase/row/row0sel.c 2006-06-06 23:40:37 +04:00
@@ -70,13 +70,13 @@
dict_index_t* clust_index) /* in: clustered index */
{
dict_field_t* ifield;
- dict_col_t* col;
- byte* sec_field;
- ulint sec_len;
- byte* clust_field;
- ulint clust_len;
- ulint n;
- ulint i;
+ dict_col_t* col;
+ byte* sec_field;
+ ulint sec_len;
+ byte* clust_field;
+ ulint clust_len;
+ ulint n;
+ ulint i;
dtype_t* cur_type;
mem_heap_t* heap = NULL;
ulint clust_offsets_[REC_OFFS_NORMAL_SIZE];
@@ -93,19 +93,19 @@
sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs,
ULINT_UNDEFINED, &heap);
- n = dict_index_get_n_ordering_defined_by_user(sec_index);
+ n = dict_index_get_n_ordering_defined_by_user(sec_index);
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n; i++) {
ifield = dict_index_get_nth_field(sec_index, i);
- col = dict_field_get_col(ifield);
-
+ col = dict_field_get_col(ifield);
+
clust_field = rec_get_nth_field(clust_rec, clust_offs,
- dict_col_get_clust_pos(col),
- &clust_len);
+ dict_col_get_clust_pos(col),
+ &clust_len);
sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len);
if (ifield->prefix_len > 0
- && clust_len != UNIV_SQL_NULL) {
+ && clust_len != UNIV_SQL_NULL) {
cur_type = dict_col_get_type(
dict_field_get_col(ifield));
@@ -116,13 +116,13 @@
clust_len, (char*) clust_field);
}
- if (0 != cmp_data_data(dict_col_get_type(col),
- clust_field, clust_len,
- sec_field, sec_len)) {
+ if (0 != cmp_data_data(dict_col_get_type(col),
+ clust_field, clust_len,
+ sec_field, sec_len)) {
is_equal = FALSE;
goto func_exit;
- }
- }
+ }
+ }
func_exit:
if (UNIV_LIKELY_NULL(heap)) {
@@ -150,7 +150,7 @@
node->latch_mode = BTR_SEARCH_LEAF;
node->plans = NULL;
-
+
return(node);
}
@@ -248,7 +248,7 @@
eval_node_set_int_val(func_node, 0);
func_node = que_node_get_next(func_node);
- }
+ }
node->aggregate_already_fetched = FALSE;
}
@@ -292,7 +292,7 @@
ulint field_no;
byte* data;
ulint len;
-
+
ut_ad(rec_offs_validate(rec, index, offsets));
if (index->type & DICT_CLUSTERED) {
@@ -302,19 +302,45 @@
}
while (column) {
+ mem_heap_t* heap = NULL;
+ ibool needs_copy;
+
field_no = column->field_nos[index_type];
if (field_no != ULINT_UNDEFINED) {
-
- data = rec_get_nth_field(rec, offsets, field_no, &len);
-
- if (column->copy_val) {
+
+ if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets,
+ field_no))) {
+
+ /* Copy an externally stored field to the
+ temporary heap */
+
+ heap = mem_heap_create(1);
+
+ data = btr_rec_copy_externally_stored_field(
+ rec, offsets, field_no, &len, heap);
+
+ ut_a(len != UNIV_SQL_NULL);
+
+ needs_copy = TRUE;
+ } else {
+ data = rec_get_nth_field(rec, offsets,
+ field_no, &len);
+
+ needs_copy = column->copy_val;
+ }
+
+ if (needs_copy) {
eval_node_copy_and_alloc_val(column, data,
len);
} else {
val = que_node_get_val(column);
dfield_set_data(val, data, len);
}
+
+ if (UNIV_LIKELY_NULL(heap)) {
+ mem_heap_free(heap);
+ }
}
column = UT_LIST_GET_NEXT(col_var_list, column);
@@ -333,7 +359,7 @@
ulint i;
ut_ad(que_node_get_type(column) == QUE_NODE_SYMBOL);
-
+
column->prefetch_buf = mem_alloc(SEL_MAX_N_PREFETCH
* sizeof(sel_buf_t));
for (i = 0; i < SEL_MAX_N_PREFETCH; i++) {
@@ -382,7 +408,7 @@
byte* data;
ulint len;
ulint val_buf_size;
-
+
ut_ad(plan->n_rows_prefetched > 0);
column = UT_LIST_GET_FIRST(plan->columns);
@@ -417,7 +443,7 @@
sel_buf->data = dfield_get_data(val);
sel_buf->len = dfield_get_len(val);
sel_buf->val_buf_size = que_node_get_val_buf_size(column);
-
+
dfield_set_data(val, data, len);
que_node_set_val_buf_size(column, val_buf_size);
next_col:
@@ -454,14 +480,14 @@
/* We have the convention that pushing new rows starts only
after the prefetch stack has been emptied: */
-
+
ut_ad(plan->first_prefetched == 0);
}
plan->n_rows_prefetched++;
-
+
ut_ad(pos < SEL_MAX_N_PREFETCH);
-
+
column = UT_LIST_GET_FIRST(plan->columns);
while (column) {
@@ -471,7 +497,7 @@
goto next_col;
}
-
+
if (!column->prefetch_buf) {
/* Allocate a new prefetch buffer */
@@ -492,11 +518,11 @@
dfield_set_data(val, sel_buf->data, sel_buf->len);
que_node_set_val_buf_size(column, sel_buf->val_buf_size);
-
+
sel_buf->data = data;
sel_buf->len = len;
sel_buf->val_buf_size = val_buf_size;
-next_col:
+next_col:
column = UT_LIST_GET_NEXT(col_var_list, column);
}
}
@@ -528,7 +554,7 @@
} else {
plan->old_vers_heap = mem_heap_create(512);
}
-
+
err = row_vers_build_for_consistent_read(rec, mtr, plan->index,
offsets, read_view, offset_heap,
plan->old_vers_heap, old_vers);
@@ -536,13 +562,48 @@
}
/*************************************************************************
+Builds the last committed version of a clustered index record for a
+semi-consistent read. */
+static
+ulint
+row_sel_build_committed_vers_for_mysql(
+/*===================================*/
+ /* out: DB_SUCCESS or error code */
+ dict_index_t* clust_index, /* in: clustered index */
+ row_prebuilt_t* prebuilt, /* in: prebuilt struct */
+ rec_t* rec, /* in: record in a clustered index */
+ ulint** offsets, /* in/out: offsets returned by
+ rec_get_offsets(rec, clust_index) */
+ mem_heap_t** offset_heap, /* in/out: memory heap from which
+ the offsets are allocated */
+ rec_t** old_vers, /* out: old version, or NULL if the
+ record does not exist in the view:
+ i.e., it was freshly inserted
+ afterwards */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint err;
+
+ if (prebuilt->old_vers_heap) {
+ mem_heap_empty(prebuilt->old_vers_heap);
+ } else {
+ prebuilt->old_vers_heap = mem_heap_create(200);
+ }
+
+ err = row_vers_build_for_semi_consistent_read(rec, mtr, clust_index,
+ offsets, offset_heap,
+ prebuilt->old_vers_heap, old_vers);
+ return(err);
+}
+
+/*************************************************************************
Tests the conditions which determine when the index segment we are searching
through has been exhausted. */
UNIV_INLINE
ibool
row_sel_test_end_conds(
/*===================*/
- /* out: TRUE if row passed the tests */
+ /* out: TRUE if row passed the tests */
plan_t* plan) /* in: plan for the table; the column values must
already have been retrieved and the right sides of
comparisons evaluated */
@@ -551,7 +612,7 @@
/* All conditions in end_conds are comparisons of a column to an
expression */
-
+
cond = UT_LIST_GET_FIRST(plan->end_conds);
while (cond) {
@@ -584,7 +645,7 @@
already have been retrieved */
{
func_node_t* cond;
-
+
cond = UT_LIST_GET_FIRST(plan->other_conds);
while (cond) {
@@ -635,11 +696,11 @@
offsets = rec_get_offsets(rec,
btr_pcur_get_btr_cur(&plan->pcur)->index,
offsets, ULINT_UNDEFINED, &heap);
-
+
row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets);
index = dict_table_get_first_index(plan->table);
-
+
btr_pcur_open_with_no_init(index, plan->clust_ref, PAGE_CUR_LE,
node->latch_mode, &(plan->clust_pcur),
0, mtr);
@@ -650,10 +711,11 @@
low_match value the real match to the search tuple */
if (!page_rec_is_user_rec(clust_rec)
- || btr_pcur_get_low_match(&(plan->clust_pcur))
- < dict_index_get_n_unique(index)) {
-
- ut_a(rec_get_deleted_flag(rec, plan->table->comp));
+ || btr_pcur_get_low_match(&(plan->clust_pcur))
+ < dict_index_get_n_unique(index)) {
+
+ ut_a(rec_get_deleted_flag(rec,
+ dict_table_is_comp(plan->table)));
ut_a(node->read_view);
/* In a rare case it is possible that no clust rec is found
@@ -673,13 +735,18 @@
if (!node->read_view) {
/* Try to place a lock on the index record */
-
- /* If innodb_locks_unsafe_for_binlog option is used,
+
+ /* If innodb_locks_unsafe_for_binlog option is used
+ or this session is using READ COMMITTED isolation level
we lock only the record, i.e., next-key locking is
not used. */
ulint lock_type;
+ trx_t* trx;
+
+ trx = thr_get_trx(thr);
- if (srv_locks_unsafe_for_binlog) {
+ if (srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED) {
lock_type = LOCK_REC_NOT_GAP;
} else {
lock_type = LOCK_ORDINARY;
@@ -729,10 +796,11 @@
a wrong result if we would not drop rows which we come to
visit through secondary index records that would not really
exist in our snapshot. */
-
- if ((old_vers || rec_get_deleted_flag(rec, plan->table->comp))
- && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index,
- clust_rec, index)) {
+
+ if ((old_vers || rec_get_deleted_flag(rec,
+ dict_table_is_comp(plan->table)))
+ && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index,
+ clust_rec, index)) {
goto func_exit;
}
}
@@ -762,17 +830,17 @@
dict_index_t* index, /* in: index */
const ulint* offsets,/* in: rec_get_offsets(rec, index) */
ulint mode, /* in: lock mode */
- ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */
- que_thr_t* thr) /* in: query thread */
+ ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */
+ que_thr_t* thr) /* in: query thread */
{
trx_t* trx;
ulint err;
- trx = thr_get_trx(thr);
+ trx = thr_get_trx(thr);
if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) {
if (buf_LRU_buf_pool_running_out()) {
-
+
return(DB_LOCK_TABLE_FULL);
}
}
@@ -806,7 +874,7 @@
func_node_t* cond;
que_node_t* exp;
ulint n_fields;
- ulint has_search_latch = 0; /* RW_S_LATCH or 0 */
+ ulint has_search_latch = 0; /* RW_S_LATCH or 0 */
ulint i;
if (search_latch_locked) {
@@ -823,29 +891,29 @@
while (cond) {
eval_exp(que_node_get_next(cond->args));
-
+
cond = UT_LIST_GET_NEXT(cond_list, cond);
}
-
+
if (plan->tuple) {
n_fields = dtuple_get_n_fields(plan->tuple);
-
+
if (plan->n_exact_match < n_fields) {
/* There is a non-exact match field which must be
evaluated separately */
-
+
eval_exp(plan->tuple_exps[n_fields - 1]);
}
-
+
for (i = 0; i < n_fields; i++) {
exp = plan->tuple_exps[i];
-
+
dfield_copy_data(dtuple_get_nth_field(plan->tuple, i),
que_node_get_val(exp));
}
-
+
/* Open pcur to the index */
-
+
btr_pcur_open_with_no_init(index, plan->tuple, plan->mode,
node->latch_mode, &(plan->pcur),
has_search_latch, mtr);
@@ -860,7 +928,7 @@
ut_ad(plan->n_rows_prefetched == 0);
ut_ad(plan->n_rows_fetched == 0);
ut_ad(plan->cursor_at_end == FALSE);
-
+
plan->pcur_is_open = TRUE;
}
@@ -883,14 +951,14 @@
ulint relative_position;
ut_ad(!plan->cursor_at_end);
-
+
relative_position = btr_pcur_get_rel_pos(&(plan->pcur));
equal_position = btr_pcur_restore_position(node->latch_mode,
&(plan->pcur), mtr);
/* If the cursor is traveling upwards, and relative_position is
-
+
(1) BTR_PCUR_BEFORE: this is not allowed, as we did not have a lock
yet on the successor of the page infimum;
(2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the
@@ -917,13 +985,13 @@
}
ut_ad(relative_position == BTR_PCUR_AFTER
- || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
return(FALSE);
}
/* If the cursor is traveling downwards, and relative_position is
-
+
(1) BTR_PCUR_BEFORE: btr_pcur_restore_position placed the cursor on
the last record LESS than the successor of a page infimum; we have not
processed the cursor record: no need to move the cursor;
@@ -940,7 +1008,7 @@
record, else there is no need to move the cursor. */
if (relative_position == BTR_PCUR_BEFORE
- || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) {
+ || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) {
return(FALSE);
}
@@ -956,7 +1024,7 @@
}
ut_ad(relative_position == BTR_PCUR_AFTER
- || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
return(TRUE);
}
@@ -968,13 +1036,13 @@
plan_reset_cursor(
/*==============*/
plan_t* plan) /* in: plan */
-{
+{
plan->pcur_is_open = FALSE;
- plan->cursor_at_end = FALSE;
+ plan->cursor_at_end = FALSE;
plan->n_rows_fetched = 0;
plan->n_rows_prefetched = 0;
}
-
+
/*************************************************************************
Tries to do a shortcut to fetch a clustered index record with a unique key,
using the hash index if possible (not always). */
@@ -1004,21 +1072,21 @@
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_SHARED));
#endif /* UNIV_SYNC_DEBUG */
-
+
row_sel_open_pcur(node, plan, TRUE, mtr);
rec = btr_pcur_get_rec(&(plan->pcur));
-
+
if (!page_rec_is_user_rec(rec)) {
return(SEL_RETRY);
}
ut_ad(plan->mode == PAGE_CUR_GE);
-
+
/* As the cursor is now placed on a user record after a search with
the mode PAGE_CUR_GE, the up_match field in the cursor tells how many
- fields in the user record matched to the search tuple */
+ fields in the user record matched to the search tuple */
if (btr_pcur_get_up_match(&(plan->pcur)) < plan->n_exact_match) {
@@ -1027,7 +1095,7 @@
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
-
+
offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap);
if (index->type & DICT_CLUSTERED) {
@@ -1047,14 +1115,14 @@
row_sel_fetch_columns(index, rec, offsets,
UT_LIST_GET_FIRST(plan->columns));
- if (rec_get_deleted_flag(rec, plan->table->comp)) {
+ if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))) {
ret = SEL_EXHAUSTED;
goto func_exit;
}
/* Test the rest of search conditions */
-
+
if (!row_sel_test_other_conds(plan)) {
ret = SEL_EXHAUSTED;
@@ -1064,12 +1132,11 @@
ut_ad(plan->pcur.latch_mode == node->latch_mode);
plan->n_rows_fetched++;
- ret = SEL_FOUND;
func_exit:
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
- return(ret);
+ return(SEL_FOUND);
}
/*************************************************************************
@@ -1091,7 +1158,7 @@
rec_t* clust_rec;
ibool search_latch_locked;
ibool consistent_read;
-
+
/* The following flag becomes TRUE when we are doing a
consistent read from a non-clustered index and we must look
at the clustered index to find out the previous delete mark
@@ -1101,14 +1168,14 @@
ulint cost_counter = 0;
ibool cursor_just_opened;
ibool must_go_to_next;
- ibool leaf_contains_updates = FALSE;
+ ibool leaf_contains_updates = FALSE;
/* TRUE if select_will_do_update is
TRUE and the current clustered index
leaf page has been updated during
the current mtr: mtr must be committed
at the same time as the leaf x-latch
is released */
- ibool mtr_has_extra_clust_latch = FALSE;
+ ibool mtr_has_extra_clust_latch = FALSE;
/* TRUE if the search was made using
a non-clustered index, and we had to
access the clustered record: now &mtr
@@ -1169,7 +1236,7 @@
}
/* Open a cursor to index, or restore an open cursor position */
-
+
mtr_start(&mtr);
if (consistent_read && plan->unique_search && !plan->pcur_is_open
@@ -1201,7 +1268,7 @@
goto table_exhausted;
}
-
+
ut_ad(found_flag == SEL_RETRY);
plan_reset_cursor(plan);
@@ -1236,11 +1303,11 @@
if (must_go_to_next) {
/* We have already processed the cursor record: move
to the next */
-
+
goto next_rec;
}
}
-
+
rec_loop:
/* RECORD LOOP
-----------
@@ -1253,11 +1320,11 @@
NOTE that if cursor_just_opened is TRUE here, it means that we came
to this point right after row_sel_open_pcur. */
-
+
ut_ad(mtr_has_extra_clust_latch == FALSE);
rec = btr_pcur_get_rec(&(plan->pcur));
-
+
/* PHASE 1: Set a lock if specified */
if (!node->asc && cursor_just_opened
@@ -1268,19 +1335,25 @@
be possible to insert new records next to the cursor position,
and it might be that these new records should appear in the
search result set, resulting in the phantom problem. */
-
+
if (!consistent_read) {
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e., next-key locking is
- not used. */
+ /* If innodb_locks_unsafe_for_binlog option is used
+ or this session is using READ COMMITTED isolation
+ level, we lock only the record, i.e., next-key
+ locking is not used. */
rec_t* next_rec = page_rec_get_next(rec);
ulint lock_type;
+ trx_t* trx;
+
+ trx = thr_get_trx(thr);
+
offsets = rec_get_offsets(next_rec, index, offsets,
ULINT_UNDEFINED, &heap);
- if (srv_locks_unsafe_for_binlog) {
+ if (srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED) {
lock_type = LOCK_REC_NOT_GAP;
} else {
lock_type = LOCK_ORDINARY;
@@ -1293,7 +1366,7 @@
/* Note that in this case we will store in pcur
the PREDECESSOR of the record we are waiting
the lock for */
-
+
goto lock_wait_or_error;
}
}
@@ -1312,17 +1385,23 @@
}
if (!consistent_read) {
- /* Try to place a lock on the index record */
+ /* Try to place a lock on the index record */
- /* If innodb_locks_unsafe_for_binlog option is used,
+ /* If innodb_locks_unsafe_for_binlog option is used
+ or this session is using READ COMMITTED isolation level,
we lock only the record, i.e., next-key locking is
not used. */
ulint lock_type;
+ trx_t* trx;
+
offsets = rec_get_offsets(rec, index, offsets,
ULINT_UNDEFINED, &heap);
- if (srv_locks_unsafe_for_binlog) {
+ trx = thr_get_trx(thr);
+
+ if (srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED) {
lock_type = LOCK_REC_NOT_GAP;
} else {
lock_type = LOCK_ORDINARY;
@@ -1348,7 +1427,7 @@
ut_ad(page_rec_is_user_rec(rec));
if (cost_counter > SEL_COST_LIMIT) {
-
+
/* Now that we have placed the necessary locks, we can stop
for a while and store the cursor position; NOTE that if we
would store the cursor position BEFORE placing a record lock,
@@ -1358,17 +1437,17 @@
goto stop_for_a_while;
}
-
+
/* PHASE 2: Check a mixed index mix id if needed */
if (plan->unique_search && cursor_just_opened) {
ut_ad(plan->mode == PAGE_CUR_GE);
-
+
/* As the cursor is now placed on a user record after a search
with the mode PAGE_CUR_GE, the up_match field in the cursor
tells how many fields in the user record matched to the search
- tuple */
+ tuple */
if (btr_pcur_get_up_match(&(plan->pcur))
< plan->n_exact_match) {
@@ -1377,14 +1456,6 @@
/* Ok, no need to test end_conds or mix id */
- } else if (plan->mixed_index) {
- /* We have to check if the record in a mixed cluster belongs
- to this table */
-
- if (!dict_is_mixed_table_rec(plan->table, rec)) {
-
- goto next_rec;
- }
}
/* We are ready to look at a possible new index entry in the result
@@ -1400,7 +1471,7 @@
a previous version of the record */
if (index->type & DICT_CLUSTERED) {
-
+
if (!lock_clust_rec_cons_read_sees(rec, index, offsets,
node->read_view)) {
@@ -1418,8 +1489,8 @@
rec, index, offsets,
ULINT_UNDEFINED, &heap);
row_sel_fetch_columns(index, rec,
- offsets,
- UT_LIST_GET_FIRST(plan->columns));
+ offsets,
+ UT_LIST_GET_FIRST(plan->columns));
if (!row_sel_test_end_conds(plan)) {
@@ -1440,7 +1511,7 @@
/* PHASE 4: Test search end conditions and deleted flag */
/* Fetch the columns needed in test conditions */
-
+
row_sel_fetch_columns(index, rec, offsets,
UT_LIST_GET_FIRST(plan->columns));
@@ -1457,7 +1528,7 @@
goto table_exhausted;
}
- if (rec_get_deleted_flag(rec, plan->table->comp)
+ if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))
&& !cons_read_requires_clust_rec) {
/* The record is delete marked: we can skip it if this is
@@ -1465,10 +1536,10 @@
of a non-clustered index record */
if (plan->unique_search) {
-
+
goto table_exhausted;
}
-
+
goto next_rec;
}
@@ -1483,7 +1554,7 @@
err = row_sel_get_clust_rec(node, plan, rec, thr, &clust_rec,
&mtr);
mtr_has_extra_clust_latch = TRUE;
-
+
if (err != DB_SUCCESS) {
goto lock_wait_or_error;
@@ -1501,7 +1572,8 @@
goto next_rec;
}
- if (rec_get_deleted_flag(clust_rec, plan->table->comp)) {
+ if (rec_get_deleted_flag(clust_rec,
+ dict_table_is_comp(plan->table))) {
/* The record is delete marked: we can skip it */
@@ -1512,14 +1584,14 @@
btr_pcur_store_position(&(plan->clust_pcur), &mtr);
}
- }
+ }
/* PHASE 6: Test the rest of search conditions */
-
+
if (!row_sel_test_other_conds(plan)) {
if (plan->unique_search) {
-
+
goto table_exhausted;
}
@@ -1528,7 +1600,7 @@
/* PHASE 7: We found a new qualifying row for the current table; push
the row if prefetch is on, or move to the next table in the join */
-
+
plan->n_rows_fetched++;
ut_ad(plan->pcur.latch_mode == node->latch_mode);
@@ -1544,22 +1616,22 @@
/* When the database is in the online backup mode, the number
of log records for a single mtr should be small: increment the
cost counter to ensure it */
-
+
cost_counter += 1 + (SEL_COST_LIMIT / 8);
if (plan->unique_search) {
- goto table_exhausted;
+ goto table_exhausted;
}
goto next_rec;
- }
+ }
if ((plan->n_rows_fetched <= SEL_PREFETCH_LIMIT)
|| plan->unique_search || plan->no_prefetch) {
/* No prefetch in operation: go to the next table */
-
+
goto next_table;
}
@@ -1568,13 +1640,13 @@
if (plan->n_rows_prefetched == SEL_MAX_N_PREFETCH) {
/* The prefetch buffer is now full */
-
+
sel_pop_prefetched_row(plan);
goto next_table;
}
-next_rec:
+next_rec:
ut_ad(!search_latch_locked);
if (mtr_has_extra_clust_latch) {
@@ -1586,7 +1658,7 @@
goto commit_mtr_for_a_while;
}
-
+
if (leaf_contains_updates
&& btr_pcur_is_after_last_on_page(&(plan->pcur), &mtr)) {
@@ -1607,7 +1679,7 @@
}
if (!moved) {
-
+
goto table_exhausted;
}
@@ -1622,7 +1694,7 @@
the next table or return a row in the result set */
ut_ad(btr_pcur_is_on_user_rec(&(plan->pcur), &mtr));
-
+
if (plan->unique_search && !node->can_get_updated) {
plan->cursor_at_end = TRUE;
@@ -1642,18 +1714,18 @@
next_table_no_mtr:
/* If we use 'goto' to this label, it means that the row was popped
from the prefetched rows stack, and &mtr is already committed */
-
+
if (node->fetch_table + 1 == node->n_tables) {
sel_eval_select_list(node);
if (node->is_aggregate) {
- goto table_loop;
+ goto table_loop;
}
sel_assign_into_var_values(node->into_list, node);
-
+
thr->run_node = que_node_get_parent(node);
if (search_latch_locked) {
@@ -1668,14 +1740,14 @@
/* When we move to the next table, we first reset the plan cursor:
we do not care about resetting it when we backtrack from a table */
-
+
plan_reset_cursor(sel_node_get_nth_plan(node, node->fetch_table));
-
+
goto table_loop;
table_exhausted:
/* The table cursor pcur reached the result set end: backtrack to the
- previous table in the join if we do not have cached prefetched rows */
+ previous table in the join if we do not have cached prefetched rows */
plan->cursor_at_end = TRUE;
@@ -1683,10 +1755,10 @@
leaf_contains_updates = FALSE;
mtr_has_extra_clust_latch = FALSE;
-
+
if (plan->n_rows_prefetched > 0) {
/* The table became exhausted during a prefetch */
-
+
sel_pop_prefetched_row(plan);
goto next_table_no_mtr;
@@ -1707,18 +1779,18 @@
if (search_latch_locked) {
rw_lock_s_unlock(&btr_search_latch);
}
-
+
goto func_exit;
}
node->state = SEL_NODE_NO_MORE_ROWS;
-
+
thr->run_node = que_node_get_parent(node);
if (search_latch_locked) {
rw_lock_s_unlock(&btr_search_latch);
}
-
+
goto func_exit;
}
@@ -1733,7 +1805,7 @@
record lock on the cursor record or its successor: when we reposition
the cursor, this record lock guarantees that nobody can meanwhile have
inserted new records which should have appeared in the result set,
- which would result in the phantom problem. */
+ which would result in the phantom problem. */
ut_ad(!search_latch_locked);
@@ -1741,7 +1813,7 @@
btr_pcur_store_position(&(plan->pcur), &mtr);
mtr_commit(&mtr);
-
+
ut_ad(sync_thread_levels_empty_gen(TRUE));
err = DB_SUCCESS;
goto func_exit;
@@ -1749,7 +1821,7 @@
commit_mtr_for_a_while:
/* Stores the cursor position and commits &mtr; this is used if
&mtr may contain latches which would break the latching order if
- &mtr would not be committed and the latches released. */
+ &mtr would not be committed and the latches released. */
plan->stored_cursor_rec_processed = TRUE;
@@ -1760,7 +1832,7 @@
leaf_contains_updates = FALSE;
mtr_has_extra_clust_latch = FALSE;
-
+
ut_ad(sync_thread_levels_empty_gen(TRUE));
goto table_loop;
@@ -1774,9 +1846,9 @@
plan->stored_cursor_rec_processed = FALSE;
btr_pcur_store_position(&(plan->pcur), &mtr);
-
+
mtr_commit(&mtr);
-
+
ut_ad(sync_thread_levels_empty_gen(TRUE));
func_exit:
@@ -1802,7 +1874,7 @@
ulint err;
ut_ad(thr);
-
+
node = thr->run_node;
ut_ad(que_node_get_type(node) == QUE_NODE_SELECT);
@@ -1835,23 +1907,23 @@
} else {
i_lock_mode = LOCK_IS;
}
-
+
table_node = node->table_list;
-
+
while (table_node) {
err = lock_table(0, table_node->table,
i_lock_mode, thr);
if (err != DB_SUCCESS) {
-
+
que_thr_handle_error(thr, DB_ERROR,
NULL, 0);
return(NULL);
}
-
+
table_node = que_node_get_next(table_node);
}
}
-
+
/* If this is an explicit cursor, copy stored procedure
variable values, so that the values cannot change between
fetches (currently, we copy them also for non-explicit
@@ -1862,7 +1934,7 @@
row_sel_copy_input_variable_vals(node);
}
-
+
node->state = SEL_NODE_FETCH;
node->fetch_table = 0;
@@ -1877,7 +1949,7 @@
/* NOTE! if queries are parallelized, the following assignment may
have problems; the assignment should be made only if thr is the
only top-level thr in the graph: */
-
+
thr->graph->last_sel_node = node;
if (err == DB_SUCCESS) {
@@ -1896,7 +1968,7 @@
}
return(thr);
-}
+}
/**************************************************************************
Performs a fetch for a cursor. */
@@ -1911,17 +1983,28 @@
fetch_node_t* node;
ut_ad(thr);
-
+
node = thr->run_node;
sel_node = node->cursor_def;
-
+
ut_ad(que_node_get_type(node) == QUE_NODE_FETCH);
if (thr->prev_node != que_node_get_parent(node)) {
if (sel_node->state != SEL_NODE_NO_MORE_ROWS) {
-
- sel_assign_into_var_values(node->into_list, sel_node);
+
+ if (node->into_list) {
+ sel_assign_into_var_values(node->into_list,
+ sel_node);
+ } else {
+ void* ret = (*node->func->func)(sel_node,
+ node->func->arg);
+
+ if (!ret) {
+ sel_node->state =
+ SEL_NODE_NO_MORE_ROWS;
+ }
+ }
}
thr->run_node = que_node_get_parent(node);
@@ -1933,12 +2016,12 @@
the time of the fetch, so that execution knows to return to this
fetch node after a row has been selected or we know that there is
no row left */
-
+
sel_node->common.parent = node;
-
+
if (sel_node->state == SEL_NODE_CLOSED) {
- /* SQL error detected */
- fprintf(stderr, "SQL error %lu\n", (ulong)DB_ERROR);
+ fprintf(stderr,
+ "InnoDB: Error: fetch called on a closed cursor\n");
que_thr_handle_error(thr, DB_ERROR, NULL, 0);
@@ -1948,7 +2031,82 @@
thr->run_node = sel_node;
return(thr);
-}
+}
+
+/********************************************************************
+Sample callback function for fetch that prints each row.*/
+
+void*
+row_fetch_print(
+/*============*/
+ /* out: always returns non-NULL */
+ void* row, /* in: sel_node_t* */
+ void* user_arg) /* in: not used */
+{
+ sel_node_t* node = row;
+ que_node_t* exp;
+ ulint i = 0;
+
+ UT_NOT_USED(user_arg);
+
+ fprintf(stderr, "row_fetch_print: row %p\n", row);
+
+ exp = node->select_list;
+
+ while (exp) {
+ dfield_t* dfield = que_node_get_val(exp);
+ dtype_t* type = dfield_get_type(dfield);
+
+ fprintf(stderr, " column %lu:\n", (ulong)i);
+
+ dtype_print(type);
+ fprintf(stderr, "\n");
+
+ if (dfield_get_len(dfield) != UNIV_SQL_NULL) {
+ ut_print_buf(stderr, dfield_get_data(dfield),
+ dfield_get_len(dfield));
+ } else {
+ fprintf(stderr, " <NULL>;");
+ }
+
+ fprintf(stderr, "\n");
+
+ exp = que_node_get_next(exp);
+ i++;
+ }
+
+ return((void*)42);
+}
+
+/********************************************************************
+Callback function for fetch that stores an unsigned 4 byte integer to the
+location pointed. The column's type must be DATA_INT, DATA_UNSIGNED, length
+= 4. */
+
+void*
+row_fetch_store_uint4(
+/*==================*/
+ /* out: always returns NULL */
+ void* row, /* in: sel_node_t* */
+ void* user_arg) /* in: data pointer */
+{
+ sel_node_t* node = row;
+ ib_uint32_t* val = user_arg;
+ ulint tmp;
+
+ dfield_t* dfield = que_node_get_val(node->select_list);
+ dtype_t* type = dfield_get_type(dfield);
+ ulint len = dfield_get_len(dfield);
+
+ ut_a(dtype_get_mtype(type) == DATA_INT);
+ ut_a(dtype_get_prtype(type) & DATA_UNSIGNED);
+ ut_a(len == 4);
+
+ tmp = mach_read_from_4(dfield_get_data(dfield));
+ *val = tmp;
+
+ return(NULL);
+}
/***************************************************************
Prints a row in a select result. */
@@ -1964,22 +2122,22 @@
que_node_t* arg;
ut_ad(thr);
-
+
node = thr->run_node;
-
+
sel_node = node->sel_node;
ut_ad(que_node_get_type(node) == QUE_NODE_ROW_PRINTF);
if (thr->prev_node == que_node_get_parent(node)) {
-
+
/* Reset the cursor */
sel_node->state = SEL_NODE_OPEN;
/* Fetch next row to print */
thr->run_node = sel_node;
-
+
return(thr);
}
@@ -1990,7 +2148,7 @@
/* No more rows to print */
thr->run_node = que_node_get_parent(node);
-
+
return(thr);
}
@@ -2011,7 +2169,7 @@
thr->run_node = sel_node;
return(thr);
-}
+}
/********************************************************************
Converts a key value stored in MySQL format to an Innobase dtuple. The last
@@ -2047,14 +2205,14 @@
byte* key_end;
ulint n_fields = 0;
ulint type;
-
+
/* For documentation of the key value storage format in MySQL, see
ha_innobase::store_key_val_for_row() in ha_innodb.cc. */
key_end = key_ptr + key_len;
/* Permit us to access any field in the tuple (ULINT_MAX): */
-
+
dtuple_set_n_fields(tuple, ULINT_MAX);
dfield = dtuple_get_nth_field(tuple, 0);
@@ -2070,7 +2228,7 @@
ut_a(key_len == DATA_ROW_ID_LEN);
dfield_set_data(dfield, key_ptr, DATA_ROW_ID_LEN);
-
+
dtuple_set_n_fields(tuple, 1);
return;
@@ -2079,28 +2237,28 @@
while (key_ptr < key_end) {
ut_a(dict_col_get_type(field->col)->mtype
- == dfield_get_type(dfield)->mtype);
+ == dfield_get_type(dfield)->mtype);
data_offset = 0;
is_null = FALSE;
- if (!(dfield_get_type(dfield)->prtype & DATA_NOT_NULL)) {
- /* The first byte in the field tells if this is
- an SQL NULL value */
-
+ if (!(dfield_get_type(dfield)->prtype & DATA_NOT_NULL)) {
+ /* The first byte in the field tells if this is
+ an SQL NULL value */
+
data_offset = 1;
- if (*key_ptr != 0) {
- dfield_set_data(dfield, NULL, UNIV_SQL_NULL);
+ if (*key_ptr != 0) {
+ dfield_set_data(dfield, NULL, UNIV_SQL_NULL);
is_null = TRUE;
- }
- }
+ }
+ }
type = dfield_get_type(dfield)->mtype;
/* Calculate data length and data field total length */
-
+
if (type == DATA_BLOB) {
/* The key field is a column prefix of a BLOB or
TEXT */
@@ -2137,16 +2295,16 @@
with the full prefix_len bytes. How do characters
0xff in UTF-8 behave? */
- data_len = field->prefix_len;
+ data_len = field->prefix_len;
data_field_len = data_offset + data_len;
} else {
data_len = dfield_get_type(dfield)->len;
data_field_len = data_offset + data_len;
}
- if (dtype_get_mysql_type(dfield_get_type(dfield))
+ if (dtype_get_mysql_type(dfield_get_type(dfield))
== DATA_MYSQL_TRUE_VARCHAR
- && dfield_get_type(dfield)->mtype != DATA_INT) {
+ && dfield_get_type(dfield)->mtype != DATA_INT) {
/* In a MySQL key value format, a true VARCHAR is
always preceded by 2 bytes of a length field.
dfield_get_type(dfield)->len returns the maximum
@@ -2161,32 +2319,32 @@
}
/* Storing may use at most data_len bytes of buf */
-
+
if (!is_null) {
- row_mysql_store_col_in_innobase_format(
+ row_mysql_store_col_in_innobase_format(
dfield,
buf,
FALSE, /* MySQL key value format col */
key_ptr + data_offset,
data_len,
- index->table->comp);
+ dict_table_is_comp(index->table));
buf += data_len;
}
- key_ptr += data_field_len;
+ key_ptr += data_field_len;
if (key_ptr > key_end) {
/* The last field in key was not a complete key field
but a prefix of it.
- Print a warning about this! HA_READ_PREFIX_LAST does
+ Print a warning about this! HA_READ_PREFIX_LAST does
not currently work in InnoDB with partial-field key
value prefixes. Since MySQL currently uses a padding
trick to calculate LIKE 'abc%' type queries there
should never be partial-field prefixes in searches. */
- ut_print_timestamp(stderr);
-
+ ut_print_timestamp(stderr);
+
fputs(
" InnoDB: Warning: using a partial-field key prefix in search.\n"
"InnoDB: ", stderr);
@@ -2201,21 +2359,21 @@
fprintf(stderr, "\n");
if (!is_null) {
- dfield->len -= (ulint)(key_ptr - key_end);
+ dfield->len -= (ulint)(key_ptr - key_end);
}
}
- n_fields++;
- field++;
+ n_fields++;
+ field++;
dfield++;
- }
+ }
ut_a(buf <= original_buf + buf_len);
- /* We set the length of tuple to n_fields: we assume that the memory
+ /* We set the length of tuple to n_fields: we assume that the memory
area allocated for it is big enough (usually bigger than n_fields). */
-
- dtuple_set_n_fields(tuple, n_fields);
+
+ dtuple_set_n_fields(tuple, n_fields);
}
/******************************************************************
@@ -2239,12 +2397,13 @@
dict_index_get_sys_col_pos(index, DATA_ROW_ID), &len);
if (len != DATA_ROW_ID_LEN) {
- fprintf(stderr,
+ fprintf(stderr,
"InnoDB: Error: Row id field is wrong length %lu in ", (ulong) len);
dict_index_name_print(stderr, prebuilt->trx, index);
fprintf(stderr, "\n"
"InnoDB: Field number %lu, record:\n",
- (ulong) dict_index_get_sys_col_pos(index, DATA_ROW_ID));
+ (ulong) dict_index_get_sys_col_pos(index,
+ DATA_ROW_ID));
rec_print_new(stderr, index_rec, offsets);
putc('\n', stderr);
ut_error;
@@ -2297,7 +2456,7 @@
ut_ad(templ->mysql_col_len == len);
} else if (templ->type == DATA_VARCHAR
- || templ->type == DATA_VARMYSQL
+ || templ->type == DATA_VARMYSQL
|| templ->type == DATA_BINARY) {
field_end = dest + templ->mysql_col_len;
@@ -2306,18 +2465,18 @@
/* This is a >= 5.0.3 type true VARCHAR. Store the
length of the data to the first byte or the first
two bytes of dest. */
-
+
dest = row_mysql_store_true_var_len(dest, len,
templ->mysql_length_bytes);
}
/* Copy the actual data */
ut_memcpy(dest, data, len);
-
+
/* Pad with trailing spaces. We pad with spaces also the
unused end of a >= 5.0.3 true VARCHAR column, just in case
MySQL expects its contents to be deterministic. */
-
+
pad_ptr = dest + len;
ut_ad(templ->mbminlen <= templ->mbmaxlen);
@@ -2329,13 +2488,13 @@
if (len & 1) {
/* A 0x20 has been stripped from the column.
Pad it back. */
-
+
if (pad_ptr < field_end) {
*pad_ptr = 0x20;
pad_ptr++;
}
}
-
+
/* Pad the rest of the string with 0x0020 */
while (pad_ptr < field_end) {
@@ -2414,10 +2573,11 @@
{
mysql_row_templ_t* templ;
mem_heap_t* extern_field_heap = NULL;
+ mem_heap_t* heap;
byte* data;
ulint len;
ulint i;
-
+
ut_ad(prebuilt->mysql_template);
ut_ad(rec_offs_validate(rec, NULL, offsets));
@@ -2430,9 +2590,6 @@
templ = prebuilt->mysql_template + i;
- data = rec_get_nth_field(rec, offsets,
- templ->rec_field_no, &len);
-
if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets,
templ->rec_field_no))) {
@@ -2441,7 +2598,19 @@
ut_a(!prebuilt->trx->has_search_latch);
- extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);
+ if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
+ if (prebuilt->blob_heap == NULL) {
+ prebuilt->blob_heap =
+ mem_heap_create(UNIV_PAGE_SIZE);
+ }
+
+ heap = prebuilt->blob_heap;
+ } else {
+ extern_field_heap =
+ mem_heap_create(UNIV_PAGE_SIZE);
+
+ heap = extern_field_heap;
+ }
/* NOTE: if we are retrieving a big BLOB, we may
already run out of memory in the next call, which
@@ -2449,64 +2618,27 @@
data = btr_rec_copy_externally_stored_field(rec,
offsets, templ->rec_field_no, &len,
- extern_field_heap);
+ heap);
ut_a(len != UNIV_SQL_NULL);
+ } else {
+ /* Field is stored in the row. */
+
+ data = rec_get_nth_field(rec, offsets,
+ templ->rec_field_no, &len);
}
if (len != UNIV_SQL_NULL) {
- if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
-
- ut_a(prebuilt->templ_contains_blob);
-
- /* A heuristic test that we can allocate the
- memory for a big BLOB. We have a safety margin
- of 1000000 bytes. Since the test takes some
- CPU time, we do not use it for small BLOBs. */
-
- if (UNIV_UNLIKELY(len > 2000000)
- && UNIV_UNLIKELY(!ut_test_malloc(
- len + 1000000))) {
-
- ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Warning: could not allocate %lu + 1000000 bytes to retrieve\n"
-"InnoDB: a big column. Table name ", (ulong) len);
- ut_print_name(stderr,
- prebuilt->trx,
- prebuilt->table->name);
- putc('\n', stderr);
-
- if (extern_field_heap) {
- mem_heap_free(
- extern_field_heap);
- }
- return(FALSE);
- }
-
- /* Copy the BLOB data to the BLOB heap of
- prebuilt */
-
- if (prebuilt->blob_heap == NULL) {
- prebuilt->blob_heap =
- mem_heap_create(len);
- }
-
- data = memcpy(mem_heap_alloc(
- prebuilt->blob_heap, len),
- data, len);
- }
-
row_sel_field_store_in_mysql_format(
mysql_rec + templ->mysql_col_offset,
templ, data, len);
/* Cleanup */
if (extern_field_heap) {
- mem_heap_free(extern_field_heap);
+ mem_heap_free(extern_field_heap);
extern_field_heap = NULL;
- }
-
+ }
+
if (templ->mysql_null_bit_mask) {
/* It is a nullable column with a non-NULL
value */
@@ -2514,11 +2646,11 @@
~(byte) (templ->mysql_null_bit_mask);
}
} else {
- /* MySQL seems to assume the field for an SQL NULL
- value is set to zero or space. Not taking this into
+ /* MySQL seems to assume the field for an SQL NULL
+ value is set to zero or space. Not taking this into
account caused seg faults with NULL BLOB fields, and
- bug number 154 in the MySQL bug database: GROUP BY
- and DISTINCT could treat NULL values inequal. */
+ bug number 154 in the MySQL bug database: GROUP BY
+ and DISTINCT could treat NULL values inequal. */
int pad_char;
mysql_rec[templ->mysql_null_byte_offset] |=
@@ -2528,7 +2660,7 @@
case DATA_BINARY:
case DATA_VARMYSQL:
if (templ->mysql_type
- == DATA_MYSQL_TRUE_VARCHAR) {
+ == DATA_MYSQL_TRUE_VARCHAR) {
/* This is a >= 5.0.3 type
true VARCHAR. Zero the field. */
pad_char = 0x00;
@@ -2538,7 +2670,7 @@
case DATA_CHAR:
case DATA_FIXBINARY:
case DATA_MYSQL:
- /* MySQL pads all string types (except
+ /* MySQL pads all string types (except
BLOB, TEXT and true VARCHAR) with space. */
if (UNIV_UNLIKELY(templ->mbminlen == 2)) {
/* Treat UCS2 as a special case. */
@@ -2567,7 +2699,7 @@
memset(mysql_rec + templ->mysql_col_offset,
pad_char, templ->mysql_col_len);
}
- }
+ }
return(TRUE);
}
@@ -2600,7 +2732,7 @@
} else {
prebuilt->old_vers_heap = mem_heap_create(200);
}
-
+
err = row_vers_build_for_consistent_read(rec, mtr, clust_index,
offsets, read_view, offset_heap,
prebuilt->old_vers_heap, old_vers);
@@ -2643,11 +2775,11 @@
*out_rec = NULL;
trx = thr_get_trx(thr);
-
+
row_build_row_ref_in_tuple(prebuilt->clust_ref, sec_index, rec, trx);
clust_index = dict_table_get_first_index(sec_index->table);
-
+
btr_pcur_open_with_no_init(clust_index, prebuilt->clust_ref,
PAGE_CUR_LE, BTR_SEARCH_LEAF,
prebuilt->clust_pcur, 0, mtr);
@@ -2660,9 +2792,9 @@
low_match value the real match to the search tuple */
if (!page_rec_is_user_rec(clust_rec)
- || btr_pcur_get_low_match(prebuilt->clust_pcur)
- < dict_index_get_n_unique(clust_index)) {
-
+ || btr_pcur_get_low_match(prebuilt->clust_pcur)
+ < dict_index_get_n_unique(clust_index)) {
+
/* In a rare case it is possible that no clust rec is found
for a delete-marked secondary index record: if in row0umod.c
in row_undo_mod_remove_clust_low() we have already removed
@@ -2672,9 +2804,10 @@
clustered index record did not exist in the read view of
trx. */
- if (!rec_get_deleted_flag(rec, sec_index->table->comp)
- || prebuilt->select_lock_type != LOCK_NONE) {
- ut_print_timestamp(stderr);
+ if (!rec_get_deleted_flag(rec,
+ dict_table_is_comp(sec_index->table))
+ || prebuilt->select_lock_type != LOCK_NONE) {
+ ut_print_timestamp(stderr);
fputs(" InnoDB: error clustered record"
" for sec rec not found\n"
"InnoDB: ", stderr);
@@ -2704,7 +2837,7 @@
/* Try to place a lock on the index record; we are searching
the clust rec with a unique condition, hence
we set a LOCK_REC_NOT_GAP type lock */
-
+
err = lock_clust_rec_read_check_and_lock(0, clust_rec,
clust_index, *offsets,
prebuilt->select_lock_type,
@@ -2723,9 +2856,9 @@
then we never look for an earlier version */
if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
- && !lock_clust_rec_cons_read_sees(clust_rec, clust_index,
- *offsets, trx->read_view)) {
-
+ && !lock_clust_rec_cons_read_sees(clust_rec,
+ clust_index, *offsets, trx->read_view)) {
+
/* The following call returns 'offsets' associated with
'old_vers' */
err = row_sel_build_prev_vers_for_mysql(
@@ -2733,7 +2866,7 @@
prebuilt, clust_rec,
offsets, offset_heap,
&old_vers, mtr);
-
+
if (err != DB_SUCCESS) {
goto err_exit;
@@ -2754,27 +2887,28 @@
a wrong result if we would not drop rows which we come to
visit through secondary index records that would not really
exist in our snapshot. */
-
+
if (clust_rec && (old_vers
- || rec_get_deleted_flag(rec, sec_index->table->comp))
- && !row_sel_sec_rec_is_for_clust_rec(rec, sec_index,
- clust_rec, clust_index)) {
+ || rec_get_deleted_flag(rec,
+ dict_table_is_comp(sec_index->table)))
+ && !row_sel_sec_rec_is_for_clust_rec(rec, sec_index,
+ clust_rec, clust_index)) {
clust_rec = NULL;
} else {
#ifdef UNIV_SEARCH_DEBUG
ut_a(clust_rec == NULL ||
- row_sel_sec_rec_is_for_clust_rec(rec, sec_index,
- clust_rec, clust_index));
-#endif
+ row_sel_sec_rec_is_for_clust_rec(rec,
+ sec_index, clust_rec, clust_index));
+#endif
}
}
func_exit:
*out_rec = clust_rec;
- if (prebuilt->select_lock_type != LOCK_NONE) {
- /* We may use the cursor in unlock: store its position */
-
+ if (prebuilt->select_lock_type == LOCK_X) {
+ /* We may use the cursor in update: store its position */
+
btr_pcur_store_position(prebuilt->clust_pcur, mtr);
}
@@ -2812,7 +2946,7 @@
ulint relative_position;
relative_position = pcur->rel_pos;
-
+
success = btr_pcur_restore_position(latch_mode, pcur, mtr);
*same_user_rec = success;
@@ -2830,12 +2964,12 @@
}
if (relative_position == BTR_PCUR_AFTER
- || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE) {
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE) {
if (moves_up) {
return(TRUE);
}
-
+
if (btr_pcur_is_on_user_rec(pcur, mtr)) {
btr_pcur_move_to_prev(pcur, mtr);
}
@@ -2844,8 +2978,8 @@
}
ut_ad(relative_position == BTR_PCUR_BEFORE
- || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE);
-
+ || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE);
+
if (moves_up && btr_pcur_is_on_user_rec(pcur, mtr)) {
btr_pcur_move_to_next(pcur, mtr);
}
@@ -2866,26 +3000,24 @@
ulint i;
mysql_row_templ_t* templ;
byte* cached_rec;
- ut_ad(prebuilt->n_fetch_cached > 0);
+ ut_ad(prebuilt->n_fetch_cached > 0);
ut_ad(prebuilt->mysql_prefix_len <= prebuilt->mysql_row_len);
-
- if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread))
- {
- /* Copy cache record field by field, don't touch fields that
+
+ if (UNIV_UNLIKELY(prebuilt->keep_other_fields_on_keyread)) {
+ /* Copy cache record field by field, don't touch fields that
are not covered by current key */
- cached_rec =
+ cached_rec =
prebuilt->fetch_cache[prebuilt->fetch_cache_first];
for (i = 0; i < prebuilt->n_template; i++) {
templ = prebuilt->mysql_template + i;
ut_memcpy(
- buf + templ->mysql_col_offset,
+ buf + templ->mysql_col_offset,
cached_rec + templ->mysql_col_offset,
templ->mysql_col_len);
- /* Copy NULL bit of the current field from cached_rec
+ /* Copy NULL bit of the current field from cached_rec
to buf */
- if (templ->mysql_null_bit_mask)
- {
+ if (templ->mysql_null_bit_mask) {
buf[templ->mysql_null_byte_offset] ^=
(buf[templ->mysql_null_byte_offset] ^
cached_rec[templ->mysql_null_byte_offset]) &
@@ -2893,8 +3025,7 @@
}
}
}
- else
- {
+ else {
ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first],
prebuilt->mysql_prefix_len);
}
@@ -2931,11 +3062,11 @@
/* A user has reported memory corruption in these
buffers in Linux. Put magic numbers there to help
to track a possible bug. */
-
+
buf = mem_alloc(prebuilt->mysql_row_len + 8);
prebuilt->fetch_cache[i] = buf + 4;
-
+
mach_write_to_4(buf, ROW_PREBUILT_FETCH_MAGIC_N);
mach_write_to_4(buf + 4 + prebuilt->mysql_row_len,
ROW_PREBUILT_FETCH_MAGIC_N);
@@ -2974,10 +3105,10 @@
btr_pcur_t* pcur = prebuilt->pcur;
trx_t* trx = prebuilt->trx;
rec_t* rec;
-
+
ut_ad(index->type & DICT_CLUSTERED);
ut_ad(!prebuilt->templ_contains_blob);
-
+
btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE,
BTR_SEARCH_LEAF, pcur,
#ifndef UNIV_SEARCH_DEBUG
@@ -2987,7 +3118,7 @@
#endif
mtr);
rec = btr_pcur_get_rec(pcur);
-
+
if (!page_rec_is_user_rec(rec)) {
return(SEL_RETRY);
@@ -2995,7 +3126,7 @@
/* As the cursor is now placed on a user record after a search with
the mode PAGE_CUR_GE, the up_match field in the cursor tells how many
- fields in the user record matched to the search tuple */
+ fields in the user record matched to the search tuple */
if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) {
@@ -3014,13 +3145,13 @@
return(SEL_RETRY);
}
- if (rec_get_deleted_flag(rec, index->table->comp)) {
+ if (rec_get_deleted_flag(rec, dict_table_is_comp(index->table))) {
return(SEL_EXHAUSTED);
}
*out_rec = rec;
-
+
return(SEL_FOUND);
}
@@ -3035,7 +3166,7 @@
row_search_for_mysql(
/*=================*/
/* out: DB_SUCCESS,
- DB_RECORD_NOT_FOUND,
+ DB_RECORD_NOT_FOUND,
DB_END_OF_INDEX, DB_DEADLOCK,
DB_LOCK_TABLE_FULL, DB_CORRUPTION,
or DB_TOO_BIG_RECORD */
@@ -3050,7 +3181,7 @@
the end of the index, depending on
'mode' */
ulint match_mode, /* in: 0 or ROW_SEL_EXACT or
- ROW_SEL_EXACT_PREFIX */
+ ROW_SEL_EXACT_PREFIX */
ulint direction) /* in: 0 or ROW_SEL_NEXT or
ROW_SEL_PREV; NOTE: if this is != 0,
then prebuilt must have a pcur
@@ -3058,7 +3189,7 @@
cursor 'direction' should be 0. */
{
dict_index_t* index = prebuilt->index;
- ibool comp = index->table->comp;
+ ibool comp = dict_table_is_comp(index->table);
dtuple_t* search_tuple = prebuilt->search_tuple;
btr_pcur_t* pcur = prebuilt->pcur;
trx_t* trx = prebuilt->trx;
@@ -3067,17 +3198,21 @@
rec_t* rec;
rec_t* result_rec;
rec_t* clust_rec;
- rec_t* old_vers;
ulint err = DB_SUCCESS;
ibool unique_search = FALSE;
ibool unique_search_from_clust_index = FALSE;
- ibool mtr_has_extra_clust_latch = FALSE;
- ibool moves_up = FALSE;
+ ibool mtr_has_extra_clust_latch = FALSE;
+ ibool moves_up = FALSE;
ibool set_also_gap_locks = TRUE;
/* if the query is a plain
locking SELECT, and the isolation
level is <= TRX_ISO_READ_COMMITTED,
then this is set to FALSE */
+ ibool did_semi_consistent_read = FALSE;
+ /* if the returned record was locked
+ and we did a semi-consistent read
+ (fetch the newest committed version),
+ then this is set to TRUE */
#ifdef UNIV_SEARCH_DEBUG
ulint cnt = 0;
#endif /* UNIV_SEARCH_DEBUG */
@@ -3094,8 +3229,8 @@
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: Error:\n"
+ ut_print_timestamp(stderr);
+ fprintf(stderr, " InnoDB: Error:\n"
"InnoDB: MySQL is trying to use a table handle but the .ibd file for\n"
"InnoDB: table %s does not exist.\n"
"InnoDB: Have you deleted the .ibd file from the database directory under\n"
@@ -3113,16 +3248,16 @@
"InnoDB: Error: trying to free a corrupt\n"
"InnoDB: table handle. Magic n %lu, table name ",
(ulong) prebuilt->magic_n);
- ut_print_name(stderr, trx, prebuilt->table->name);
+ ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
putc('\n', stderr);
- mem_analyze_corruption((byte*)prebuilt);
+ mem_analyze_corruption(prebuilt);
ut_error;
}
if (trx->n_mysql_tables_in_use == 0
- && UNIV_UNLIKELY(prebuilt->select_lock_type == LOCK_NONE)) {
+ && UNIV_UNLIKELY(prebuilt->select_lock_type == LOCK_NONE)) {
/* Note that if MySQL uses an InnoDB temp table that it
created inside LOCK TABLES, then n_mysql_tables_in_use can
be zero; in that case select_lock_type is set to LOCK_X in
@@ -3134,16 +3269,16 @@
fputs(
"InnoDB: Error: MySQL is trying to perform a SELECT\n"
"InnoDB: but it has not locked any tables in ::external_lock()!\n",
- stderr);
+stderr);
trx_print(stderr, trx, 600);
- fputc('\n', stderr);
+ fputc('\n', stderr);
*/
}
/* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode);
dtuple_print(search_tuple);
-
+
fprintf(stderr, "N tables locked %lu\n", trx->mysql_n_tables_locked);
*/
/*-------------------------------------------------------------*/
@@ -3151,7 +3286,7 @@
adaptive hash index latch if there is someone waiting behind */
if (UNIV_UNLIKELY(btr_search_latch.writer != RW_LOCK_NOT_LOCKED)
- && trx->has_search_latch) {
+ && trx->has_search_latch) {
/* There is an x-latch request on the adaptive hash index:
release the s-latch to reduce starvation and wait for
@@ -3163,13 +3298,15 @@
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
}
-
- /* Reset the new record lock info if we srv_locks_unsafe_for_binlog
- is set. Then we are able to remove the record locks set here on an
- individual row. */
- if (srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ /* Reset the new record lock info if srv_locks_unsafe_for_binlog
+ is set or session is using a READ COMMITED isolation level. Then
+ we are able to remove the record locks set here on an individual
+ row. */
+
+ if ((srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
trx_reset_new_rec_lock_info(trx);
}
@@ -3179,7 +3316,7 @@
if (UNIV_UNLIKELY(direction == 0)) {
trx->op_info = "starting index read";
-
+
prebuilt->n_rows_fetched = 0;
prebuilt->n_fetch_cached = 0;
prebuilt->fetch_cache_first = 0;
@@ -3203,7 +3340,7 @@
or better: prevent caching for a scroll
cursor! */
}
-
+
prebuilt->n_rows_fetched = 0;
prebuilt->n_fetch_cached = 0;
prebuilt->fetch_cache_first = 0;
@@ -3219,16 +3356,16 @@
}
if (prebuilt->fetch_cache_first > 0
- && prebuilt->fetch_cache_first < MYSQL_FETCH_CACHE_SIZE) {
+ && prebuilt->fetch_cache_first < MYSQL_FETCH_CACHE_SIZE) {
- /* The previous returned row was popped from the fetch
- cache, but the cache was not full at the time of the
- popping: no more rows can exist in the result set */
+ /* The previous returned row was popped from the fetch
+ cache, but the cache was not full at the time of the
+ popping: no more rows can exist in the result set */
err = DB_RECORD_NOT_FOUND;
goto func_exit;
}
-
+
prebuilt->n_rows_fetched++;
if (prebuilt->n_rows_fetched > 1000000000) {
@@ -3247,13 +3384,13 @@
marked versions of a record where only the primary key values differ:
thus in a secondary index we must use next-key locks when locking
delete-marked records. */
-
+
if (match_mode == ROW_SEL_EXACT
- && index->type & DICT_UNIQUE
- && dtuple_get_n_fields(search_tuple)
- == dict_index_get_n_unique(index)
- && (index->type & DICT_CLUSTERED
- || !dtuple_contains_null(search_tuple))) {
+ && index->type & DICT_UNIQUE
+ && dtuple_get_n_fields(search_tuple)
+ == dict_index_get_n_unique(index)
+ && (index->type & DICT_CLUSTERED
+ || !dtuple_contains_null(search_tuple))) {
/* Note above that a UNIQUE secondary index can contain many
rows with the same key value if one of the columns is the SQL
@@ -3270,7 +3407,7 @@
if (UNIV_UNLIKELY(direction != 0 &&
!prebuilt->used_in_HANDLER)) {
-
+
err = DB_RECORD_NOT_FOUND;
goto func_exit;
}
@@ -3288,20 +3425,20 @@
may be long and there may be externally stored fields */
if (UNIV_UNLIKELY(direction == 0)
- && unique_search
- && index->type & DICT_CLUSTERED
- && !prebuilt->templ_contains_blob
- && !prebuilt->used_in_HANDLER
- && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) {
+ && unique_search
+ && index->type & DICT_CLUSTERED
+ && !prebuilt->templ_contains_blob
+ && !prebuilt->used_in_HANDLER
+ && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) {
mode = PAGE_CUR_GE;
unique_search_from_clust_index = TRUE;
if (trx->mysql_n_tables_locked == 0
- && prebuilt->select_lock_type == LOCK_NONE
- && trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
- && trx->read_view) {
+ && prebuilt->select_lock_type == LOCK_NONE
+ && trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
+ && trx->read_view) {
/* This is a SELECT query done as a consistent read,
and the read view has already been allocated:
@@ -3315,7 +3452,7 @@
and if we try that, we can deadlock on the adaptive
hash index semaphore! */
-#ifndef UNIV_SEARCH_DEBUG
+#ifndef UNIV_SEARCH_DEBUG
if (!trx->has_search_latch) {
rw_lock_s_lock(&btr_search_latch);
trx->has_search_latch = TRUE;
@@ -3327,49 +3464,49 @@
#ifdef UNIV_SEARCH_DEBUG
ut_a(0 == cmp_dtuple_rec(search_tuple,
rec, offsets));
-#endif
+#endif
if (!row_sel_store_mysql_rec(buf, prebuilt,
rec, offsets)) {
- err = DB_TOO_BIG_RECORD;
+ err = DB_TOO_BIG_RECORD;
/* We let the main loop to do the
error handling */
- goto shortcut_fails_too_big_rec;
+ goto shortcut_fails_too_big_rec;
}
-
- mtr_commit(&mtr);
+
+ mtr_commit(&mtr);
/* ut_print_name(stderr, index->name);
fputs(" shortcut\n", stderr); */
srv_n_rows_read++;
-
+
if (trx->search_latch_timeout > 0
- && trx->has_search_latch) {
+ && trx->has_search_latch) {
trx->search_latch_timeout--;
- rw_lock_s_unlock(&btr_search_latch);
+ rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
- }
-
+ }
+
/* NOTE that we do NOT store the cursor
position */
err = DB_SUCCESS;
goto func_exit;
case SEL_EXHAUSTED:
- mtr_commit(&mtr);
+ mtr_commit(&mtr);
/* ut_print_name(stderr, index->name);
fputs(" record not found 2\n", stderr); */
if (trx->search_latch_timeout > 0
- && trx->has_search_latch) {
+ && trx->has_search_latch) {
trx->search_latch_timeout--;
- rw_lock_s_unlock(&btr_search_latch);
+ rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
}
@@ -3391,32 +3528,30 @@
if (trx->has_search_latch) {
rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
- }
+ }
trx_start_if_not_started(trx);
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
- && prebuilt->select_lock_type != LOCK_NONE
- && trx->mysql_query_str) {
+ && prebuilt->select_lock_type != LOCK_NONE
+ && trx->mysql_query_str && trx->mysql_thd) {
/* Scan the MySQL query string; check if SELECT is the first
- word there */
- ibool success;
-
- dict_accept(*trx->mysql_query_str, "SELECT", &success);
+ word there */
- if (success) {
+ if (dict_str_starts_with_keyword(trx->mysql_thd,
+ *trx->mysql_query_str, "SELECT")) {
/* It is a plain locking SELECT and the isolation
level is low: do not lock gaps */
set_also_gap_locks = FALSE;
}
}
-
+
/* Note that if the search mode was GE or G, then the cursor
naturally moves upward (in fetch next) in alphabetical order,
otherwise downward */
-
+
if (UNIV_UNLIKELY(direction == 0)) {
if (mode == PAGE_CUR_GE || mode == PAGE_CUR_G) {
moves_up = TRUE;
@@ -3432,9 +3567,28 @@
clust_index = dict_table_get_first_index(index->table);
if (UNIV_LIKELY(direction != 0)) {
- if (!sel_restore_position_for_mysql(&same_user_rec,
- BTR_SEARCH_LEAF,
- pcur, moves_up, &mtr)) {
+ ibool need_to_process = sel_restore_position_for_mysql(
+ &same_user_rec, BTR_SEARCH_LEAF,
+ pcur, moves_up, &mtr);
+
+ if (UNIV_UNLIKELY(need_to_process)) {
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ /* We did a semi-consistent read,
+ but the record was removed in
+ the meantime. */
+ prebuilt->row_read_type
+ = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ } else if (UNIV_LIKELY(prebuilt->row_read_type
+ != ROW_READ_DID_SEMI_CONSISTENT)) {
+
+ /* The cursor was positioned on the record
+ that we returned previously. If we need
+ to repeat a semi-consistent read as a
+ pessimistic locking read, the record
+ cannot be skipped. */
+
goto next_rec;
}
@@ -3459,24 +3613,24 @@
/* No need to set an intention lock or assign a read view */
if (trx->read_view == NULL
- && prebuilt->select_lock_type == LOCK_NONE) {
+ && prebuilt->select_lock_type == LOCK_NONE) {
fputs(
"InnoDB: Error: MySQL is trying to perform a consistent read\n"
"InnoDB: but the read view is not assigned!\n", stderr);
trx_print(stderr, trx, 600);
- fputc('\n', stderr);
+ fputc('\n', stderr);
ut_a(0);
}
} else if (prebuilt->select_lock_type == LOCK_NONE) {
- /* This is a consistent read */
+ /* This is a consistent read */
/* Assign a read view for the query */
trx_assign_read_view(trx);
prebuilt->sql_stat_start = FALSE;
} else {
ulint lock_mode;
- if (prebuilt->select_lock_type == LOCK_S) {
+ if (prebuilt->select_lock_type == LOCK_S) {
lock_mode = LOCK_IS;
} else {
lock_mode = LOCK_IX;
@@ -3493,7 +3647,7 @@
rec_loop:
/*-------------------------------------------------------------*/
/* PHASE 4: Look for matching records in a loop */
-
+
rec = btr_pcur_get_rec(pcur);
ut_ad(!!page_rec_is_comp(rec) == comp);
#ifdef UNIV_SEARCH_DEBUG
@@ -3518,15 +3672,17 @@
if (page_rec_is_supremum(rec)) {
if (set_also_gap_locks
- && !srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ && !(srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
/* Try to place a lock on the index record */
- /* If innodb_locks_unsafe_for_binlog option is used,
- we do not lock gaps. Supremum record is really
+ /* If innodb_locks_unsafe_for_binlog option is used
+ or this session is using a READ COMMITTED isolation
+ level we do not lock gaps. Supremum record is really
a gap and therefore we do not set locks there. */
-
+
offsets = rec_get_offsets(rec, index, offsets,
ULINT_UNDEFINED, &heap);
err = sel_set_rec_lock(rec, index, offsets,
@@ -3540,14 +3696,14 @@
}
/* A page supremum record cannot be in the result set: skip
it now that we have placed a possible lock on it */
-
+
goto next_rec;
}
/*-------------------------------------------------------------*/
/* Do sanity checks in case our cursor has bumped into page
corruption */
-
+
if (comp) {
next_offs = rec_get_next_offs(rec, TRUE);
if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) {
@@ -3583,8 +3739,8 @@
dict_index_name_print(stderr, trx, index);
fputs(". Run CHECK TABLE. You may need to\n"
"InnoDB: restore from a backup, or dump + drop + reimport the table.\n",
- stderr);
-
+ stderr);
+
err = DB_CORRUPTION;
goto lock_wait_or_error;
@@ -3640,16 +3796,18 @@
in prebuilt: if not, then we return with DB_RECORD_NOT_FOUND */
/* fputs("Comparing rec and search tuple\n", stderr); */
-
+
if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) {
if (set_also_gap_locks
- && !srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ && !(srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
- /* Try to place a gap lock on the index
+ /* Try to place a gap lock on the index
record only if innodb_locks_unsafe_for_binlog
- option is not set */
+ option is not set or this session is not
+ using a READ COMMITTED isolation level. */
err = sel_set_rec_lock(rec, index, offsets,
prebuilt->select_lock_type,
@@ -3666,21 +3824,23 @@
err = DB_RECORD_NOT_FOUND;
/* ut_print_name(stderr, index->name);
fputs(" record not found 3\n", stderr); */
-
+
goto normal_return;
}
} else if (match_mode == ROW_SEL_EXACT_PREFIX) {
if (!cmp_dtuple_is_prefix_of_rec(search_tuple, rec, offsets)) {
-
+
if (set_also_gap_locks
- && !srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ && !(srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
- /* Try to place a gap lock on the index
+ /* Try to place a gap lock on the index
record only if innodb_locks_unsafe_for_binlog
- option is not set */
+ option is not set or this session is not
+ using a READ COMMITTED isolation level. */
err = sel_set_rec_lock(rec, index, offsets,
prebuilt->select_lock_type,
@@ -3711,16 +3871,18 @@
is a non-delete marked record, then it is enough to lock its
existence with LOCK_REC_NOT_GAP. */
- /* If innodb_locks_unsafe_for_binlog option is used,
- we lock only the record, i.e., next-key locking is
+ /* If innodb_locks_unsafe_for_binlog option is used
+ or this session is using a READ COMMITED isolation
+ level we lock only the record, i.e., next-key locking is
not used. */
ulint lock_type;
if (!set_also_gap_locks
- || srv_locks_unsafe_for_binlog
- || (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag(
- rec, comp)))) {
+ || srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED
+ || (unique_search
+ && !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp)))) {
goto no_gap_lock;
} else {
@@ -3739,11 +3901,11 @@
need to lock the gap before that record. */
if (index == clust_index
- && mode == PAGE_CUR_GE
- && direction == 0
- && dtuple_get_n_fields_cmp(search_tuple)
- == dict_index_get_n_unique(index)
- && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) {
+ && mode == PAGE_CUR_GE
+ && direction == 0
+ && dtuple_get_n_fields_cmp(search_tuple)
+ == dict_index_get_n_unique(index)
+ && 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) {
no_gap_lock:
lock_type = LOCK_REC_NOT_GAP;
}
@@ -3752,7 +3914,64 @@
prebuilt->select_lock_type,
lock_type, thr);
- if (err != DB_SUCCESS) {
+ switch (err) {
+ rec_t* old_vers;
+ case DB_SUCCESS:
+ break;
+ case DB_LOCK_WAIT:
+ if (UNIV_LIKELY(prebuilt->row_read_type
+ != ROW_READ_TRY_SEMI_CONSISTENT)
+ || index != clust_index) {
+
+ goto lock_wait_or_error;
+ }
+
+ /* The following call returns 'offsets'
+ associated with 'old_vers' */
+ err = row_sel_build_committed_vers_for_mysql(
+ clust_index, prebuilt, rec,
+ &offsets, &heap,
+ &old_vers, &mtr);
+
+ if (err != DB_SUCCESS) {
+
+ goto lock_wait_or_error;
+ }
+
+ mutex_enter(&kernel_mutex);
+ if (trx->was_chosen_as_deadlock_victim) {
+ mutex_exit(&kernel_mutex);
+
+ goto lock_wait_or_error;
+ }
+ if (UNIV_LIKELY(trx->wait_lock != NULL)) {
+ lock_cancel_waiting_and_release(
+ trx->wait_lock);
+ trx_reset_new_rec_lock_info(trx);
+ } else {
+ mutex_exit(&kernel_mutex);
+
+ /* The lock was granted while we were
+ searching for the last committed version.
+ Do a normal locking read. */
+
+ offsets = rec_get_offsets(rec, index, offsets,
+ ULINT_UNDEFINED, &heap);
+ err = DB_SUCCESS;
+ break;
+ }
+ mutex_exit(&kernel_mutex);
+
+ if (old_vers == NULL) {
+ /* The row was not yet committed */
+
+ goto next_rec;
+ }
+
+ did_semi_consistent_read = TRUE;
+ rec = old_vers;
+ break;
+ default:
goto lock_wait_or_error;
}
@@ -3764,18 +3983,19 @@
/* Do nothing: we let a non-locking SELECT read the
latest version of the record */
-
+
} else if (index == clust_index) {
-
+
/* Fetch a previous version of the row if the current
one is not visible in the snapshot; if we have a very
high force recovery level set, we try to avoid crashes
by skipping this lookup */
if (UNIV_LIKELY(srv_force_recovery < 5)
- && !lock_clust_rec_cons_read_sees(rec, index,
- offsets, trx->read_view)) {
+ && !lock_clust_rec_cons_read_sees(rec, index,
+ offsets, trx->read_view)) {
+ rec_t* old_vers;
/* The following call returns 'offsets'
associated with 'old_vers' */
err = row_sel_build_prev_vers_for_mysql(
@@ -3783,7 +4003,7 @@
prebuilt, rec,
&offsets, &heap,
&old_vers, &mtr);
-
+
if (err != DB_SUCCESS) {
goto lock_wait_or_error;
@@ -3821,17 +4041,17 @@
/* The record is delete-marked: we can skip it */
- if (srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ if ((srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE
+ && !did_semi_consistent_read) {
/* No need to keep a lock on a delete-marked record
if we do not want to use next-key locking. */
row_unlock_for_mysql(prebuilt, TRUE);
-
- trx_reset_new_rec_lock_info(trx);
}
-
+
goto next_rec;
}
@@ -3855,7 +4075,7 @@
/* The following call returns 'offsets' associated with
'clust_rec'. Note that 'clust_rec' can be an old version
built for a consistent read. */
-
+
err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec,
thr, &clust_rec,
&offsets, &heap, &mtr);
@@ -3875,24 +4095,23 @@
/* The record is delete marked: we can skip it */
- if (srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ if ((srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
/* No need to keep a lock on a delete-marked
record if we do not want to use next-key
locking. */
row_unlock_for_mysql(prebuilt, TRUE);
-
- trx_reset_new_rec_lock_info(trx);
}
goto next_rec;
}
-
+
if (prebuilt->need_to_access_clustered) {
- result_rec = clust_rec;
+ result_rec = clust_rec;
ut_ad(rec_offs_validate(result_rec, clust_index,
offsets));
@@ -3920,8 +4139,8 @@
&& !prebuilt->templ_contains_blob
&& !prebuilt->clust_index_was_generated
&& !prebuilt->used_in_HANDLER
- && prebuilt->template_type
- != ROW_MYSQL_DUMMY_TEMPLATE) {
+ && prebuilt->template_type
+ != ROW_MYSQL_DUMMY_TEMPLATE) {
/* Inside an update, for example, we do not cache rows,
since we may use the cursor position to do the actual
@@ -3935,7 +4154,7 @@
row_sel_push_cache_row_for_mysql(prebuilt, result_rec,
offsets);
if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) {
-
+
goto got_row;
}
@@ -3973,13 +4192,13 @@
/* We have an optimization to save CPU time: if this is a consistent
read on a unique condition on the clustered index, then we do not
store the pcur position, because any fetch next or prev will anyway
- return 'end of file'. Exceptions are locking reads and the MySQL
- HANDLER command where the user can move the cursor with PREV or NEXT
+ return 'end of file'. Exceptions are locking reads and the MySQL
+ HANDLER command where the user can move the cursor with PREV or NEXT
even after a unique search. */
if (!unique_search_from_clust_index
- || prebuilt->select_lock_type != LOCK_NONE
- || prebuilt->used_in_HANDLER) {
+ || prebuilt->select_lock_type != LOCK_NONE
+ || prebuilt->used_in_HANDLER) {
/* Inside an update always store the cursor position */
@@ -3991,6 +4210,20 @@
goto normal_return;
next_rec:
+ /* Reset the old and new "did semi-consistent read" flags. */
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ did_semi_consistent_read = FALSE;
+
+ if (UNIV_UNLIKELY(srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && prebuilt->select_lock_type != LOCK_NONE) {
+
+ trx_reset_new_rec_lock_info(trx);
+ }
+
/*-------------------------------------------------------------*/
/* PHASE 5: Move the cursor to the next index record */
@@ -4004,7 +4237,7 @@
mtr_commit(&mtr);
mtr_has_extra_clust_latch = FALSE;
-
+
mtr_start(&mtr);
if (sel_restore_position_for_mysql(&same_user_rec,
BTR_SEARCH_LEAF,
@@ -4017,7 +4250,7 @@
}
}
- if (moves_up) {
+ if (moves_up) {
if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) {
not_moved:
btr_pcur_store_position(pcur, &mtr);
@@ -4043,13 +4276,20 @@
goto rec_loop;
lock_wait_or_error:
+ /* Reset the old and new "did semi-consistent read" flags. */
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ did_semi_consistent_read = FALSE;
+
/*-------------------------------------------------------------*/
btr_pcur_store_position(pcur, &mtr);
mtr_commit(&mtr);
mtr_has_extra_clust_latch = FALSE;
-
+
trx->error_state = err;
/* The following is a patch for MySQL */
@@ -4067,7 +4307,11 @@
sel_restore_position_for_mysql(&same_user_rec,
BTR_SEARCH_LEAF, pcur,
moves_up, &mtr);
- if (srv_locks_unsafe_for_binlog && !same_user_rec) {
+
+ if ((srv_locks_unsafe_for_binlog
+ || trx->isolation_level == TRX_ISO_READ_COMMITTED)
+ && !same_user_rec) {
+
/* Since we were not able to restore the cursor
on the same user record, we cannot use
row_unlock_for_mysql() to unlock any records, and
@@ -4086,7 +4330,7 @@
trx_reset_new_rec_lock_info(trx);
}
-
+
mode = pcur->search_mode;
goto rec_loop;
@@ -4127,6 +4371,20 @@
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
+
+ /* Set or reset the "did semi-consistent read" flag on return.
+ The flag did_semi_consistent_read is set if and only if
+ the record being returned was fetched with a semi-consistent read. */
+ ut_ad(prebuilt->row_read_type != ROW_READ_WITH_LOCKS
+ || !did_semi_consistent_read);
+
+ if (UNIV_UNLIKELY(prebuilt->row_read_type != ROW_READ_WITH_LOCKS)) {
+ if (UNIV_UNLIKELY(did_semi_consistent_read)) {
+ prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT;
+ } else {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ }
return(err);
}
@@ -4144,9 +4402,9 @@
'/' char, table name */
{
dict_table_t* table;
- ibool ret = FALSE;
+ ibool ret = FALSE;
- table = dict_table_get(norm_name, trx);
+ table = dict_table_get(norm_name);
if (table == NULL) {
@@ -4165,22 +4423,23 @@
IX type locks actually would require ret = FALSE. */
if (UT_LIST_GET_LEN(table->locks) == 0
- && ut_dulint_cmp(trx->id, table->query_cache_inv_trx_id) >= 0) {
+ && ut_dulint_cmp(trx->id,
+ table->query_cache_inv_trx_id) >= 0) {
ret = TRUE;
-
+
/* If the isolation level is high, assign a read view for the
transaction if it does not yet have one */
if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ
- && !trx->read_view) {
+ && !trx->read_view) {
- trx->read_view = read_view_open_now(trx,
+ trx->read_view = read_view_open_now(trx->id,
trx->global_read_view_heap);
trx->global_read_view = trx->read_view;
}
}
-
+
mutex_exit(&kernel_mutex);
return(ret);
| Thread |
|---|
| • bk commit into 5.1 tree (aivanov:1.2160) | Alex Ivanov Notebook | 6 Jun |