Commit 492283fd authored by unknown's avatar unknown

Merge sanja.is.com.ua:/home/bell/mysql/bk/mysql-4.1

into sanja.is.com.ua:/home/bell/mysql/bk/work-group_concat-4.1
parents 47056bc5 423747b5
......@@ -426,7 +426,8 @@ btr_page_free_for_ibuf(
flst_add_first(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST,
page + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr);
ut_ad(flst_validate(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr));
ut_ad(flst_validate(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST,
mtr));
}
/******************************************************************
......
......@@ -776,8 +776,8 @@ btr_search_guess_on_hash(
goto failure;
}
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_ad(page_rec_is_user_rec(rec));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(page_rec_is_user_rec(rec));
btr_cur_position(index, rec, cursor);
......
......@@ -468,6 +468,11 @@ buf_block_init(
block->check_index_page_at_flush = FALSE;
block->in_free_list = FALSE;
block->in_LRU_list = FALSE;
block->n_pointers = 0;
rw_lock_create(&(block->lock));
ut_ad(rw_lock_validate(&(block->lock)));
......@@ -687,6 +692,7 @@ buf_pool_init(
}
UT_LIST_ADD_LAST(free, buf_pool->free, block);
block->in_free_list = TRUE;
}
mutex_exit(&(buf_pool->mutex));
......@@ -830,7 +836,7 @@ buf_page_make_young(
block = buf_block_align(frame);
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
buf_LRU_make_block_young(block);
......@@ -845,7 +851,7 @@ buf_block_free(
/*===========*/
buf_block_t* block) /* in, own: block to be freed */
{
ut_ad(block->state != BUF_BLOCK_FILE_PAGE);
ut_a(block->state != BUF_BLOCK_FILE_PAGE);
mutex_enter(&(buf_pool->mutex));
......@@ -1109,6 +1115,8 @@ buf_page_get_gen(
goto loop;
}
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
must_read = FALSE;
if (block->io_fix == BUF_IO_READ) {
......@@ -1407,6 +1415,8 @@ buf_page_get_known_nowait(
return(FALSE);
}
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
#ifdef UNIV_SYNC_DEBUG
buf_block_buf_fix_inc_debug(block, file, line);
#else
......@@ -1517,7 +1527,7 @@ buf_page_init(
buf_block_t* block) /* in: block to init */
{
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block->state == BUF_BLOCK_READY_FOR_USE);
ut_a(block->state != BUF_BLOCK_FILE_PAGE);
/* Set the state of the block */
block->magic_n = BUF_BLOCK_MAGIC_N;
......@@ -1533,6 +1543,18 @@ buf_page_init(
/* Insert into the hash table of file pages */
if (buf_page_hash_get(space, offset)) {
fprintf(stderr,
"InnoDB: Error: page %lu %lu already found from the hash table\n", space,
offset);
buf_print();
buf_LRU_print();
buf_validate();
buf_LRU_validate();
ut_a(0);
}
HASH_INSERT(buf_block_t, hash, buf_pool->page_hash,
buf_page_address_fold(space, offset), block);
......@@ -1605,7 +1627,7 @@ buf_page_init_for_read(
block = buf_block_alloc();
ut_ad(block);
ut_a(block);
mutex_enter(&(buf_pool->mutex));
......@@ -1769,6 +1791,8 @@ buf_page_io_complete(
ut_ad(block);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
io_type = block->io_fix;
if (io_type == BUF_IO_READ) {
......
......@@ -50,6 +50,8 @@ buf_flush_insert_into_flush_list(
{
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_ad((UT_LIST_GET_FIRST(buf_pool->flush_list) == NULL)
|| (ut_dulint_cmp(
(UT_LIST_GET_FIRST(buf_pool->flush_list))
......@@ -131,7 +133,7 @@ buf_flush_ready_for_flush(
ulint flush_type)/* in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */
{
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if ((ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) > 0)
&& (block->io_fix == 0)) {
......@@ -163,6 +165,8 @@ buf_flush_write_complete(
ut_ad(block);
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
block->oldest_modification = ut_dulint_zero;
UT_LIST_REMOVE(flush_list, buf_pool->flush_list, block);
......@@ -282,6 +286,8 @@ buf_flush_buffered_writes(void)
for (i = 0; i < trx_doublewrite->first_free; i++) {
block = trx_doublewrite->buf_block_arr[i];
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
FALSE, block->space, block->offset, 0, UNIV_PAGE_SIZE,
(void*)block->frame, (void*)block);
......@@ -321,6 +327,8 @@ buf_flush_post_to_doublewrite_buf(
try_again:
mutex_enter(&(trx_doublewrite->mutex));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (trx_doublewrite->first_free
>= 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) {
mutex_exit(&(trx_doublewrite->mutex));
......@@ -395,6 +403,8 @@ buf_flush_write_block_low(
/*======================*/
buf_block_t* block) /* in: buffer block to write */
{
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
#ifdef UNIV_IBUF_DEBUG
ut_a(ibuf_count_get(block->space, block->offset) == 0);
#endif
......@@ -443,7 +453,7 @@ buf_flush_try_page(
block = buf_page_hash_get(space, offset);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(!block || block->state == BUF_BLOCK_FILE_PAGE);
if (flush_type == BUF_FLUSH_LIST
&& block && buf_flush_ready_for_flush(block, flush_type)) {
......@@ -635,6 +645,7 @@ buf_flush_try_neighbors(
for (i = low; i < high; i++) {
block = buf_page_hash_get(space, i);
ut_a(!block || block->state == BUF_BLOCK_FILE_PAGE);
if (block && flush_type == BUF_FLUSH_LRU && i != offset
&& !block->old) {
......@@ -703,10 +714,10 @@ buf_flush_batch(
ulint offset;
ibool found;
ut_ad((flush_type == BUF_FLUSH_LRU) || (flush_type == BUF_FLUSH_LIST));
ut_ad((flush_type != BUF_FLUSH_LIST) ||
sync_thread_levels_empty_gen(TRUE));
ut_ad((flush_type == BUF_FLUSH_LRU)
|| (flush_type == BUF_FLUSH_LIST));
ut_ad((flush_type != BUF_FLUSH_LIST)
|| sync_thread_levels_empty_gen(TRUE));
mutex_enter(&(buf_pool->mutex));
if ((buf_pool->n_flush[flush_type] > 0)
......@@ -737,7 +748,6 @@ buf_flush_batch(
ut_ad(flush_type == BUF_FLUSH_LIST);
block = UT_LIST_GET_LAST(buf_pool->flush_list);
if (!block
|| (ut_dulint_cmp(block->oldest_modification,
lsn_limit) >= 0)) {
......@@ -756,6 +766,7 @@ buf_flush_batch(
function a pointer to a block in the list! */
while ((block != NULL) && !found) {
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (buf_flush_ready_for_flush(block, flush_type)) {
......@@ -781,7 +792,6 @@ buf_flush_batch(
} else if (flush_type == BUF_FLUSH_LRU) {
block = UT_LIST_GET_PREV(LRU, block);
} else {
ut_ad(flush_type == BUF_FLUSH_LIST);
......
......@@ -82,6 +82,8 @@ buf_LRU_invalidate_tablespace(
block = UT_LIST_GET_LAST(buf_pool->LRU);
while (block != NULL) {
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
if (block->space == id
&& (block->buf_fix_count > 0 || block->io_fix != 0)) {
......@@ -199,19 +201,16 @@ buf_LRU_search_and_free_block(
mutex_enter(&(buf_pool->mutex));
freed = FALSE;
block = UT_LIST_GET_LAST(buf_pool->LRU);
while (block != NULL) {
ut_a(block->in_LRU_list);
if (buf_flush_ready_for_replace(block)) {
if (buf_debug_prints) {
printf(
"Putting space %lu page %lu to free list\n",
block->space, block->offset);
}
buf_LRU_block_remove_hashed_page(block);
mutex_exit(&(buf_pool->mutex));
......@@ -223,25 +222,21 @@ buf_LRU_search_and_free_block(
if (block->frame) {
btr_search_drop_page_hash_index(block->frame);
}
mutex_enter(&(buf_pool->mutex));
ut_a(block->buf_fix_count == 0);
buf_LRU_block_free_hashed_page(block);
freed = TRUE;
break;
}
block = UT_LIST_GET_PREV(LRU, block);
distance++;
if (!freed && n_iterations <= 10
&& distance > 100 + (n_iterations * buf_pool->curr_size)
/ 10) {
buf_pool->LRU_flush_ended = 0;
mutex_exit(&(buf_pool->mutex));
......@@ -249,15 +244,12 @@ buf_LRU_search_and_free_block(
return(FALSE);
}
}
if (buf_pool->LRU_flush_ended > 0) {
buf_pool->LRU_flush_ended--;
}
if (!freed) {
buf_pool->LRU_flush_ended = 0;
}
mutex_exit(&(buf_pool->mutex));
return(freed);
......@@ -355,7 +347,11 @@ buf_LRU_get_free_block(void)
if (UT_LIST_GET_LEN(buf_pool->free) > 0) {
block = UT_LIST_GET_FIRST(buf_pool->free);
ut_a(block->in_free_list);
UT_LIST_REMOVE(free, buf_pool->free, block);
block->in_free_list = FALSE;
ut_a(block->state != BUF_BLOCK_FILE_PAGE);
ut_a(!block->in_LRU_list);
if (srv_use_awe) {
if (block->frame) {
......@@ -466,7 +462,7 @@ buf_LRU_old_adjust_len(void)
ulint old_len;
ulint new_len;
ut_ad(buf_pool->LRU_old);
ut_a(buf_pool->LRU_old);
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(3 * (BUF_LRU_OLD_MIN_LEN / 8) > BUF_LRU_OLD_TOLERANCE + 5);
......@@ -474,6 +470,8 @@ buf_LRU_old_adjust_len(void)
old_len = buf_pool->LRU_old_len;
new_len = 3 * (UT_LIST_GET_LEN(buf_pool->LRU) / 8);
ut_a(buf_pool->LRU_old->in_LRU_list);
/* Update the LRU_old pointer if necessary */
if (old_len < new_len - BUF_LRU_OLD_TOLERANCE) {
......@@ -490,7 +488,7 @@ buf_LRU_old_adjust_len(void)
buf_pool->LRU_old);
buf_pool->LRU_old_len--;
} else {
ut_ad(buf_pool->LRU_old); /* Check that we did not
ut_a(buf_pool->LRU_old); /* Check that we did not
fall out of the LRU list */
return;
}
......@@ -498,9 +496,8 @@ buf_LRU_old_adjust_len(void)
}
/***********************************************************************
Initializes the old blocks pointer in the LRU list.
This function should be called when the LRU list grows to
BUF_LRU_OLD_MIN_LEN length. */
Initializes the old blocks pointer in the LRU list. This function should be
called when the LRU list grows to BUF_LRU_OLD_MIN_LEN length. */
static
void
buf_LRU_old_init(void)
......@@ -508,7 +505,7 @@ buf_LRU_old_init(void)
{
buf_block_t* block;
ut_ad(UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN);
ut_a(UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN);
/* We first initialize all blocks in the LRU list as old and then use
the adjust function to move the LRU_old pointer to the right
......@@ -517,6 +514,8 @@ buf_LRU_old_init(void)
block = UT_LIST_GET_FIRST(buf_pool->LRU);
while (block != NULL) {
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->in_LRU_list);
block->old = TRUE;
block = UT_LIST_GET_NEXT(LRU, block);
}
......@@ -539,6 +538,9 @@ buf_LRU_remove_block(
ut_ad(block);
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->in_LRU_list);
/* If the LRU_old pointer is defined and points to just this block,
move it backward one step */
......@@ -552,11 +554,12 @@ buf_LRU_remove_block(
(buf_pool->LRU_old)->old = TRUE;
buf_pool->LRU_old_len++;
ut_ad(buf_pool->LRU_old);
ut_a(buf_pool->LRU_old);
}
/* Remove the block from the LRU list */
UT_LIST_REMOVE(LRU, buf_pool->LRU, block);
block->in_LRU_list = FALSE;
if (srv_use_awe && block->frame) {
/* Remove from the list of mapped pages */
......@@ -599,6 +602,8 @@ buf_LRU_add_block_to_end_low(
ut_ad(block);
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
block->old = TRUE;
last_block = UT_LIST_GET_LAST(buf_pool->LRU);
......@@ -609,7 +614,9 @@ buf_LRU_add_block_to_end_low(
block->LRU_position = buf_pool_clock_tic();
}
ut_a(!block->in_LRU_list);
UT_LIST_ADD_LAST(LRU, buf_pool->LRU, block);
block->in_LRU_list = TRUE;
if (srv_use_awe && block->frame) {
/* Add to the list of mapped pages */
......@@ -658,6 +665,9 @@ buf_LRU_add_block_low(
ut_ad(block);
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(!block->in_LRU_list);
block->old = old;
cl = buf_pool_clock_tic();
......@@ -687,6 +697,8 @@ buf_LRU_add_block_low(
block->LRU_position = (buf_pool->LRU_old)->LRU_position;
}
block->in_LRU_list = TRUE;
if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) {
ut_ad(buf_pool->LRU_old);
......@@ -755,9 +767,12 @@ buf_LRU_block_free_non_file_page(
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block);
ut_ad((block->state == BUF_BLOCK_MEMORY)
ut_a((block->state == BUF_BLOCK_MEMORY)
|| (block->state == BUF_BLOCK_READY_FOR_USE));
ut_a(block->n_pointers == 0);
ut_a(!block->in_free_list);
block->state = BUF_BLOCK_NOT_USED;
#ifdef UNIV_DEBUG
......@@ -765,6 +780,7 @@ buf_LRU_block_free_non_file_page(
memset(block->frame, '\0', UNIV_PAGE_SIZE);
#endif
UT_LIST_ADD_FIRST(free, buf_pool->free, block);
block->in_free_list = TRUE;
if (srv_use_awe && block->frame) {
/* Add to the list of mapped pages */
......@@ -788,8 +804,7 @@ buf_LRU_block_remove_hashed_page(
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block);
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->io_fix == 0);
ut_a(block->buf_fix_count == 0);
ut_a(ut_dulint_cmp(block->oldest_modification, ut_dulint_zero) == 0);
......@@ -802,6 +817,28 @@ buf_LRU_block_remove_hashed_page(
buf_block_modify_clock_inc(block);
if (block != buf_page_hash_get(block->space, block->offset)) {
fprintf(stderr,
"InnoDB: Error: page %lu %lu not found from the hash table\n",
block->space,
block->offset);
if (buf_page_hash_get(block->space, block->offset)) {
fprintf(stderr,
"InnoDB: From hash table we find block %lx of %lu %lu which is not %lx\n",
(ulint)buf_page_hash_get(block->space, block->offset),
buf_page_hash_get(block->space, block->offset)->space,
buf_page_hash_get(block->space, block->offset)->offset,
(ulint)block);
}
buf_print();
buf_LRU_print();
buf_validate();
buf_LRU_validate();
ut_a(0);
}
HASH_DELETE(buf_block_t, hash, buf_pool->page_hash,
buf_page_address_fold(block->space, block->offset),
block);
......@@ -819,7 +856,7 @@ buf_LRU_block_free_hashed_page(
be in a state where it can be freed */
{
ut_ad(mutex_own(&(buf_pool->mutex)));
ut_ad(block->state == BUF_BLOCK_REMOVE_HASH);
ut_a(block->state == BUF_BLOCK_REMOVE_HASH);
block->state = BUF_BLOCK_MEMORY;
......
......@@ -129,6 +129,8 @@ buf_read_page_low(
offset, sync);
}
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
*err = fil_io(OS_FILE_READ | wake_later,
sync, space,
offset, 0, UNIV_PAGE_SIZE,
......@@ -604,7 +606,6 @@ buf_read_ibuf_merge_pages(
}
for (i = 0; i < n_stored; i++) {
if ((i + 1 == n_stored) && sync) {
buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE,
space_ids[i], space_versions[i], page_nos[i]);
......
......@@ -1517,8 +1517,8 @@ fil_delete_tablespace(
if (space == NULL) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: cannot delete tablespace %lu because it is not found\n"
"InnoDB: in the tablespace memory cache.\n", id);
" InnoDB: Error: cannot delete tablespace %lu\n"
"InnoDB: because it is not found in the tablespace memory cache.\n", id);
mutex_exit(&(system->mutex));
......@@ -2426,19 +2426,24 @@ fil_space_for_table_exists_in_mem(
if (namespace == NULL) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s in InnoDB data dictionary has tablespace\n"
"InnoDB: id %lu, but tablespace with that id or name does not exist. Have\n"
"InnoDB: you deleted or moved .ibd files? We cannot open table %s now.\n",
name, id, name);
" InnoDB: Error: table %s\n"
"InnoDB: in InnoDB data dictionary has tablespace id %lu,\n"
"InnoDB: but tablespace with that id or name does not exist. Have\n"
"InnoDB: you deleted or moved .ibd files?\n",
name, id);
} else {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s in InnoDB data dictionary has tablespace\n"
"InnoDB: id %lu, but tablespace with that id does not exist. There is\n"
" InnoDB: Error: table %s\n"
"InnoDB: in InnoDB data dictionary has tablespace id %lu,\n"
"InnoDB: but tablespace with that id does not exist. There is\n"
"InnoDB: a tablespace of name %s and id %lu, though. Have\n"
"InnoDB: you deleted or moved .ibd files? We cannot open table %s now.\n",
name, id, namespace->name, namespace->id, name);
"InnoDB: you deleted or moved .ibd files?\n",
name, id, namespace->name, namespace->id);
}
fprintf(stderr,
"InnoDB: You can look from section 15.1 of http://www.innodb.com/ibman.html\n"
"InnoDB: how to resolve the issue.\n");
mutex_exit(&(system->mutex));
......@@ -2448,16 +2453,20 @@ fil_space_for_table_exists_in_mem(
if (0 != strcmp(space->name, path)) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: table %s in InnoDB data dictionary has tablespace\n"
"InnoDB: id %lu, but tablespace with that id has name %s. Have you\n"
"InnoDB: deleted or moved .ibd files? We cannot open table %s now.\n",
name, id, space->name, name);
" InnoDB: Error: table %s\n"
"InnoDB: in InnoDB data dictionary has tablespace id %lu,\n"
"InnoDB: but tablespace with that id has name %s.\n"
"InnoDB: Have you deleted or moved .ibd files?", name, id, space->name);
if (namespace != NULL) {
fprintf(stderr,
"InnoDB: There is a tablespace with the right name %s, but its id is %lu.\n",
namespace->name, namespace->id);
"InnoDB: There is a tablespace with the right name\n"
"InnoDB: %s, but its id is %lu.\n", namespace->name, namespace->id);
}
fprintf(stderr,
"InnoDB: You can look from section 15.1 of http://www.innodb.com/ibman.html\n"
"InnoDB: how to resolve the issue.\n");
mutex_exit(&(system->mutex));
return(FALSE);
......
......@@ -34,6 +34,12 @@ ha_create(
table = hash_create(n);
if (in_btr_search) {
table->adaptive = TRUE;
} else {
table->adaptive = FALSE;
}
if (n_mutexes == 0) {
if (in_btr_search) {
table->heap = mem_heap_create_in_btr_search(4096);
......@@ -106,6 +112,7 @@ ha_insert_for_fold(
hash_cell_t* cell;
ha_node_t* node;
ha_node_t* prev_node;
buf_block_t* prev_block;
ulint hash;
ut_ad(table && data);
......@@ -119,6 +126,12 @@ ha_insert_for_fold(
while (prev_node != NULL) {
if (prev_node->fold == fold) {
if (table->adaptive) {
prev_block = buf_block_align(prev_node->data);
ut_a(prev_block->n_pointers > 0);
prev_block->n_pointers--;
buf_block_align(data)->n_pointers++;
}
prev_node->data = data;
......@@ -142,6 +155,11 @@ ha_insert_for_fold(
}
ha_node_set_data(node, data);
if (table->adaptive) {
buf_block_align(data)->n_pointers++;
}
node->fold = fold;
node->next = NULL;
......@@ -174,6 +192,11 @@ ha_delete_hash_node(
hash_table_t* table, /* in: hash table */
ha_node_t* del_node) /* in: node to be deleted */
{
if (table->adaptive) {
ut_a(buf_block_align(del_node->data)->n_pointers > 0);
buf_block_align(del_node->data)->n_pointers--;
}
HASH_DELETE_AND_COMPACT(ha_node_t, next, table, del_node);
}
......@@ -199,6 +222,35 @@ ha_delete(
ha_delete_hash_node(table, node);
}
/*************************************************************
Looks for an element when we know the pointer to the data, and updates
the pointer to data, if found. */
void
ha_search_and_update_if_found(
/*==========================*/
hash_table_t* table, /* in: hash table */
ulint fold, /* in: folded value of the searched data */
void* data, /* in: pointer to the data */
void* new_data)/* in: new pointer to the data */
{
ha_node_t* node;
ut_ad(!table->mutexes || mutex_own(hash_get_mutex(table, fold)));
node = ha_search_with_data(table, fold, data);
if (node) {
if (table->adaptive) {
ut_a(buf_block_align(node->data)->n_pointers > 0);
buf_block_align(node->data)->n_pointers--;
buf_block_align(new_data)->n_pointers++;
}
node->data = new_data;
}
}
/*********************************************************************
Removes from the chain determined by fold all nodes whose data pointer
points to the page given. */
......@@ -229,10 +281,10 @@ ha_remove_all_nodes_to_page(
node = ha_chain_get_first(table, fold);
} else {
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
}
#ifdef UNIV_DEBUG
/* Check that all nodes really got deleted */
node = ha_chain_get_first(table, fold);
......@@ -240,8 +292,9 @@ ha_remove_all_nodes_to_page(
while (node) {
ut_a(buf_frame_align(ha_node_get_data(node)) != page);
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
#endif
}
/*****************************************************************
......@@ -293,12 +346,10 @@ ha_print_info(
hash_table_t* table) /* in: hash table */
{
hash_cell_t* cell;
/*
ha_node_t* node;
ulint len = 0;
ulint max_len = 0;
/* ha_node_t* node;
ulint nodes = 0;
*/
ulint len = 0;
ulint max_len = 0; */
ulint cells = 0;
ulint n_bufs;
ulint i;
......
......@@ -91,6 +91,7 @@ hash_create(
array = ut_malloc(sizeof(hash_cell_t) * prime);
table->adaptive = FALSE;
table->array = array;
table->n_cells = prime;
table->n_mutexes = 0;
......
......@@ -755,12 +755,16 @@ struct buf_block_struct{
UT_LIST_NODE_T(buf_block_t) free;
/* node of the free block list */
ibool in_free_list; /* TRUE if in the free list; used in
debugging */
UT_LIST_NODE_T(buf_block_t) LRU;
/* node of the LRU list */
UT_LIST_NODE_T(buf_block_t) awe_LRU_free_mapped;
/* in the AWE version node in the
list of free and LRU blocks which are
mapped to a frame */
ibool in_LRU_list; /* TRUE of the page is in the LRU list;
used in debugging */
ulint LRU_position; /* value which monotonically
decreases (or may stay constant if
the block is in the old blocks) toward
......@@ -821,6 +825,9 @@ struct buf_block_struct{
complete, though: there may have been
hash collisions, record deletions,
etc. */
ulint n_pointers; /* used in debugging: the number of
pointers in the adaptive hash index
pointing to this frame */
ulint curr_n_fields; /* prefix length for hash indexing:
number of full fields */
ulint curr_n_bytes; /* number of bytes in hash indexing */
......
......@@ -28,7 +28,6 @@ buf_block_peek_if_too_old(
{
if (buf_pool->freed_page_clock >= block->freed_page_clock
+ 1 + (buf_pool->curr_size / 1024)) {
return(TRUE);
}
......@@ -169,7 +168,7 @@ buf_block_get_space(
ut_ad(block);
ut_ad(block >= buf_pool->blocks);
ut_ad(block < buf_pool->blocks + buf_pool->max_size);
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_ad(block->buf_fix_count > 0);
return(block->space);
......@@ -187,7 +186,7 @@ buf_block_get_page_no(
ut_ad(block);
ut_ad(block >= buf_pool->blocks);
ut_ad(block < buf_pool->blocks + buf_pool->max_size);
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_ad(block->buf_fix_count > 0);
return(block->offset);
......@@ -550,6 +549,8 @@ buf_page_hash_get(
HASH_SEARCH(hash, buf_pool->page_hash, fold, block,
(block->space == space) && (block->offset == offset));
ut_a(block == NULL || block->state == BUF_BLOCK_FILE_PAGE);
return(block);
}
......@@ -617,8 +618,8 @@ buf_page_release(
mutex_enter_fast(&(buf_pool->mutex));
ut_ad(block->state == BUF_BLOCK_FILE_PAGE);
ut_ad(block->buf_fix_count > 0);
ut_a(block->state == BUF_BLOCK_FILE_PAGE);
ut_a(block->buf_fix_count > 0);
if (rw_latch == RW_X_LATCH && mtr->modifications) {
......
......@@ -28,7 +28,7 @@ ha_search_and_get_data(
/*************************************************************
Looks for an element when we know the pointer to the data and updates
the pointer to data if found. */
UNIV_INLINE
void
ha_search_and_update_if_found(
/*==========================*/
......
......@@ -49,7 +49,6 @@ ha_node_t*
ha_chain_get_next(
/*==============*/
/* out: next node, NULL if none */
hash_table_t* table __attribute__((unused)), /* in: hash table */
ha_node_t* node) /* in: hash chain node */
{
ut_ad(table);
......@@ -94,7 +93,7 @@ ha_search(
return(node);
}
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
return(NULL);
......@@ -124,7 +123,7 @@ ha_search_and_get_data(
return(node->data);
}
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
return(NULL);
......@@ -139,7 +138,6 @@ ha_next(
/* out: pointer to the next hash table node
in chain with the fold value, NULL if not
found */
hash_table_t* table, /* in: hash table */
ha_node_t* node) /* in: hash table node */
{
ulint fold;
......@@ -148,7 +146,7 @@ ha_next(
ut_ad(!table->mutexes || mutex_own(hash_get_mutex(table, fold)));
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
while (node) {
if (node->fold == fold) {
......@@ -156,7 +154,7 @@ ha_next(
return(node);
}
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
return(NULL);
......@@ -186,35 +184,12 @@ ha_search_with_data(
return(node);
}
node = ha_chain_get_next(table, node);
node = ha_chain_get_next(node);
}
return(NULL);
}
/*************************************************************
Looks for an element when we know the pointer to the data, and updates
the pointer to data, if found. */
UNIV_INLINE
void
ha_search_and_update_if_found(
/*==========================*/
hash_table_t* table, /* in: hash table */
ulint fold, /* in: folded value of the searched data */
void* data, /* in: pointer to the data */
void* new_data)/* in: new pointer to the data */
{
ha_node_t* node;
ut_ad(!table->mutexes || mutex_own(hash_get_mutex(table, fold)));
node = ha_search_with_data(table, fold, data);
if (node) {
node->data = new_data;
}
}
/*************************************************************
Looks for an element when we know the pointer to the data, and deletes
it from the hash table, if found. */
......
......@@ -100,7 +100,7 @@ Deletes a struct from a hash table. */
\
while (struct3333->NAME != DATA) {\
\
ut_ad(struct3333)\
ut_a(struct3333)\
struct3333 = struct3333->NAME;\
}\
\
......@@ -322,6 +322,8 @@ struct hash_cell_struct{
/* The hash table structure */
struct hash_table_struct {
ibool adaptive;/* TRUE if this is the hash table of the
adaptive hash index */
ulint n_cells;/* number of cells in the hash table */
hash_cell_t* array; /* pointer to cell array */
ulint n_mutexes;/* if mutexes != NULL, then the number of
......
......@@ -218,7 +218,7 @@ ibuf_update_free_bits_if_full(
}
if (after == 0) {
/* We move the page to front of the buffer pool LRU list:
/* We move the page to the front of the buffer pool LRU list:
the purpose of this is to prevent those pages to which we
cannot make inserts using the insert buffer from slipping
out of the buffer pool */
......
......@@ -339,6 +339,45 @@ row_drop_table_for_mysql(
char* name, /* in: table name */
trx_t* trx); /* in: transaction handle */
/*************************************************************************
Discards the tablespace of a table which stored in an .ibd file. Discarding
means that this function deletes the .ibd file and assigns a new table id for
the table. Also the flag table->ibd_file_missing is set TRUE.
How do we prevent crashes caused by ongoing operations on the table? Old
operations could try to access non-existent pages.
1) SQL queries, INSERT, SELECT, ...: we must get an exclusive MySQL table lock
on the table before we can do DISCARD TABLESPACE. Then there are no running
queries on the table.
2) Purge and rollback: we assign a new table id for the table. Since purge and
rollback look for the table based on the table id, they see the table as
'dropped' and discard their operations.
3) Insert buffer: we remove all entries for the tablespace in the insert
buffer tree; as long as the tablespace mem object does not exist, ongoing
insert buffer page merges are discarded in buf0rea.c. If we recreate the
tablespace mem object with IMPORT TABLESPACE later, then the tablespace will
have the same id, but the tablespace_version field in the mem object is
different, and ongoing old insert buffer page merges get discarded.
4) Linear readahead and random readahead: we use the same method as in 3) to
discard ongoing operations. */
int
row_discard_tablespace_for_mysql(
/*=============================*/
/* out: error code or DB_SUCCESS */
char* name, /* in: table name */
trx_t* trx); /* in: transaction handle */
/*********************************************************************
Imports a tablespace. The space id in the .ibd file must match the space id
of the table in the data dictionary. */
int
row_import_tablespace_for_mysql(
/*============================*/
/* out: error code or DB_SUCCESS */
char* name, /* in: table name */
trx_t* trx); /* in: transaction handle */
/*************************************************************************
Drops a database for MySQL. */
int
......
......@@ -582,7 +582,8 @@ byte*
page_parse_delete_rec_list(
/*=======================*/
/* out: end of log record or NULL */
byte type, /* in: MLOG_LIST_END_DELETE or MLOG_LIST_START_DELETE */
byte type, /* in: MLOG_LIST_END_DELETE or
MLOG_LIST_START_DELETE */
byte* ptr, /* in: buffer */
byte* end_ptr,/* in: buffer end */
page_t* page, /* in: page or NULL */
......
......@@ -2007,9 +2007,6 @@ row_import_tablespace_for_mysql(
success = fil_open_single_table_tablespace(table->space, table->name);
printf(
"Remember to stop purge + undo if table->ibd_file_is_missing!!!\n");
if (success) {
table->ibd_file_missing = FALSE;
table->tablespace_discarded = FALSE;
......
......@@ -529,6 +529,14 @@ row_purge_parse_undo_rec(
return(FALSE);
}
if (node->table->ibd_file_missing) {
/* We skip purge of missing .ibd files */
node->table = NULL;
return;
}
clust_index = dict_table_get_first_index(node->table);
if (clust_index == NULL) {
......
......@@ -258,6 +258,13 @@ row_undo_ins_parse_undo_rec(
return;
}
if (node->table->ibd_file_missing) {
/* We skip undo operations to missing .ibd files */
node->table = NULL;
return;
}
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
......
......@@ -620,6 +620,13 @@ row_undo_mod_parse_undo_rec(
return;
}
if (node->table->ibd_file_missing) {
/* We skip undo operations to missing .ibd files */
node->table = NULL;
return;
}
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
......
......@@ -1451,7 +1451,7 @@ ha_innobase::open(
DBUG_RETURN(1);
}
if (ib_table->ibd_file_missing) {
if (ib_table->ibd_file_missing && !current_thd->tablespace_op) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB error:\n"
"MySQL is trying to open a table handle but the .ibd file for\n"
......@@ -3628,6 +3628,42 @@ ha_innobase::create(
DBUG_RETURN(0);
}
/*********************************************************************
Discards or imports an InnoDB tablespace. */
int
ha_innobase::discard_or_import_tablespace(
/*======================================*/
/* out: 0 == success, -1 == error */
my_bool discard) /* in: TRUE if discard, else import */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
dict_table_t* table;
trx_t* trx;
int err;
DBUG_ENTER("ha_innobase::discard_or_import_tablespace");
ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N);
ut_a(prebuilt->trx ==
(trx_t*) current_thd->transaction.all.innobase_tid);
table = prebuilt->table;
trx = prebuilt->trx;
if (discard) {
err = row_discard_tablespace_for_mysql(table->name, trx);
} else {
err = row_import_tablespace_for_mysql(table->name, trx);
}
if (err == DB_SUCCESS) {
DBUG_RETURN(0);
}
DBUG_RETURN(-1);
}
/*********************************************************************
Drops a table from an InnoDB database. Before calling this function,
MySQL calls innobase_commit to commit the transaction of the current user.
......@@ -4536,7 +4572,8 @@ ha_innobase::external_lock(
update_thd(thd);
if (lock_type != F_UNLCK && prebuilt->table->ibd_file_missing) {
if (lock_type != F_UNLCK && prebuilt->table->ibd_file_missing
&& !current_thd->tablespace_op) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB error:\n"
"MySQL is trying to use a table handle but the .ibd file for\n"
......@@ -4546,6 +4583,7 @@ ha_innobase::external_lock(
"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
"how you can resolve the problem.\n",
prebuilt->table->name);
DBUG_RETURN(HA_ERR_CRASHED);
}
trx = prebuilt->trx;
......@@ -4793,11 +4831,12 @@ ha_innobase::store_lock(
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) {
/* If we are not doing a LOCK TABLE, then allow multiple
writers */
/* If we are not doing a LOCK TABLE or DISCARD/IMPORT
TABLESPACE, then allow multiple writers */
if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
lock_type <= TL_WRITE) && !thd->in_lock_tables) {
lock_type <= TL_WRITE) && !thd->in_lock_tables
&& !thd->tablespace_op) {
lock_type = TL_WRITE_ALLOW_WRITE;
}
......
......@@ -160,6 +160,7 @@ class ha_innobase: public handler
void info(uint);
int analyze(THD* thd,HA_CHECK_OPT* check_opt);
int optimize(THD* thd,HA_CHECK_OPT* check_opt);
int discard_or_import_tablespace(my_bool discard);
int extra(enum ha_extra_function operation);
int reset(void);
int external_lock(THD *thd, int lock_type);
......
......@@ -317,6 +317,7 @@ class handler :public Sql_alloc
virtual int dump(THD* thd, int fd = -1) { return ER_DUMP_NOT_IMPLEMENTED; }
virtual void deactivate_non_unique_index(ha_rows rows) {}
virtual bool activate_all_index(THD *thd) {return 0;}
virtual int discard_or_import_tablespace(my_bool discard) {return -1;}
// not implemented by default
virtual int net_read_dump(NET* net)
{ return ER_DUMP_NOT_IMPLEMENTED; }
......
......@@ -130,6 +130,7 @@ static SYMBOL symbols[] = {
{ "DESCRIBE", SYM(DESCRIBE),0,0},
{ "DIRECTORY", SYM(DIRECTORY_SYM),0,0},
{ "DISABLE", SYM(DISABLE_SYM),0,0},
{ "DISCARD", SYM(DISCARD),0,0},
{ "DISTINCT", SYM(DISTINCT),0,0},
{ "DISTINCTROW", SYM(DISTINCT),0,0}, /* Access likes this */
{ "DIV", SYM(DIV_SYM),0,0},
......@@ -200,6 +201,7 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM),0,0},
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
{ "IMPORT", SYM(IMPORT),0,0},
{ "INSERT", SYM(INSERT),0,0},
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
{ "INT", SYM(INT_SYM),0,0},
......@@ -387,6 +389,7 @@ static SYMBOL symbols[] = {
{ "SUPER", SYM(SUPER_SYM),0,0},
{ "TABLE", SYM(TABLE_SYM),0,0},
{ "TABLES", SYM(TABLES),0,0},
{ "TABLESPACE", SYM(TABLESPACE),0,0},
{ "TEMPORARY", SYM(TEMPORARY),0,0},
{ "TERMINATED", SYM(TERMINATED),0,0},
{ "TEXT", SYM(TEXT_SYM),0,0},
......
......@@ -486,6 +486,7 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
bool drop_primary,
enum enum_duplicates handle_duplicates,
enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS,
enum tablespace_op_type tablespace_op=NO_TABLESPACE_OP,
bool simple_alter=0);
int mysql_create_like_table(THD *thd, TABLE_LIST *table,
HA_CREATE_INFO *create_info,
......
......@@ -173,6 +173,7 @@ THD::THD():user_time(0), is_fatal_error(0),
protocol_simple.init(this);
protocol_prep.init(this);
tablespace_op=FALSE;
#ifdef USING_TRANSACTIONS
bzero((char*) &transaction,sizeof(transaction));
if (opt_using_transactions)
......
......@@ -502,6 +502,7 @@ class THD :public ilink
time_t connect_time,thr_create_time; // track down slow pthread_create
thr_lock_type update_lock_default;
delayed_insert *di;
my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
struct st_transactions {
IO_CACHE trans_log;
THD_TRANS all; // Trans since BEGIN WORK
......
......@@ -110,6 +110,11 @@ enum olap_type
UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE
};
enum tablespace_op_type
{
NO_TABLESPACE_OP, DISCARD_TABLESPACE, IMPORT_TABLESPACE
};
/*
The state of the lex parsing for selects
......@@ -530,6 +535,7 @@ typedef struct st_lex
enum ha_rkey_function ha_rkey_mode;
enum enum_enable_or_disable alter_keys_onoff;
enum enum_var_type option_type;
enum tablespace_op_type tablespace_op;
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
......
......@@ -2207,7 +2207,9 @@ mysql_execute_command(THD *thd)
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
lex->drop_primary, lex->duplicates,
lex->alter_keys_onoff, lex->simple_alter);
lex->alter_keys_onoff,
lex->tablespace_op,
lex->simple_alter);
}
break;
}
......
......@@ -1748,6 +1748,70 @@ int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
&handler::check));
}
/* table_list should contain just one table */
int mysql_discard_or_import_tablespace(THD *thd,
TABLE_LIST *table_list,
enum tablespace_op_type tablespace_op)
{
TABLE *table;
my_bool discard;
int error;
DBUG_ENTER("mysql_discard_or_import_tablespace");
/* Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
ALTER TABLE */
thd->proc_info="discard_or_import_tablespace";
if (tablespace_op == DISCARD_TABLESPACE)
discard = TRUE;
else
discard = FALSE;
thd->tablespace_op=TRUE; /* we set this flag so that ha_innobase::open
and ::external_lock() do not complain when we
lock the table */
mysql_ha_closeall(thd, table_list);
if (!(table=open_ltable(thd,table_list,TL_WRITE)))
{
thd->tablespace_op=FALSE;
DBUG_RETURN(-1);
}
thd->tablespace_op=FALSE;
error=table->file->discard_or_import_tablespace(discard);
thd->proc_info="end";
if (error)
goto err;
/* The 0 in the call below means 'not in a transaction', which means
immediate invalidation; that is probably what we wish here */
query_cache_invalidate3(thd, table_list, 0);
/* The ALTER TABLE is always in its own transaction */
error = ha_commit_stmt(thd);
if (ha_commit(thd))
error=1;
if (error)
goto err;
mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
mysql_bin_log.write(&qinfo);
}
err:
close_thread_tables(thd);
if (error == 0) {
send_ok(thd);
DBUG_RETURN(0);
}
DBUG_RETURN(error);
}
int mysql_alter_table(THD *thd,char *new_db, char *new_name,
HA_CREATE_INFO *create_info,
......@@ -1759,6 +1823,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
bool drop_primary,
enum enum_duplicates handle_duplicates,
enum enum_enable_or_disable keys_onoff,
enum tablespace_op_type tablespace_op,
bool simple_alter)
{
TABLE *table,*new_table;
......@@ -1771,6 +1836,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
ulonglong next_insert_id;
uint save_time_stamp,db_create_options, used_fields;
enum db_type old_db_type,new_db_type;
thr_lock_type lock_type;
DBUG_ENTER("mysql_alter_table");
thd->proc_info="init";
......@@ -1781,6 +1847,10 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
used_fields=create_info->used_fields;
mysql_ha_closeall(thd, table_list);
if (tablespace_op != NO_TABLESPACE_OP)
DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
tablespace_op));
if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
DBUG_RETURN(-1);
......@@ -1834,8 +1904,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (create_info->row_type == ROW_TYPE_NOT_USED)
create_info->row_type=table->row_type;
/* In some simple cases we need not to recreate the table */
thd->proc_info="setup";
if (simple_alter && !table->tmp_table)
{
......@@ -1860,6 +1928,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
VOID(pthread_mutex_unlock(&LOCK_open));
}
if (!error)
{
switch (keys_onoff) {
......@@ -2395,8 +2464,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
}
}
/* The ALTER TABLE is always in it's own transaction */
/* The ALTER TABLE is always in its own transaction */
error = ha_commit_stmt(thd);
if (ha_commit(thd))
error=1;
......@@ -2695,4 +2763,3 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
table->table=0;
DBUG_RETURN(-1);
}
......@@ -211,6 +211,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token DESCRIBE
%token DES_KEY_FILE
%token DISABLE_SYM
%token DISCARD
%token DISTINCT
%token DUPLICATE_SYM
%token DYNAMIC_SYM
......@@ -244,6 +245,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token HOSTS_SYM
%token IDENT
%token IGNORE_SYM
%token IMPORT
%token INDEX
%token INDEXES
%token INFILE
......@@ -360,6 +362,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SUBJECT_SYM
%token TABLES
%token TABLE_SYM
%token TABLESPACE
%token TEMPORARY
%token TERMINATED
%token TEXT_STRING
......@@ -1635,6 +1638,7 @@ alter:
lex->create_info.table_charset= thd->variables.collation_database;
lex->create_info.row_type= ROW_TYPE_NOT_USED;
lex->alter_keys_onoff=LEAVE_AS_IS;
lex->tablespace_op=NO_TABLESPACE_OP;
lex->simple_alter=1;
}
alter_list
......@@ -1648,6 +1652,8 @@ alter:
alter_list:
| DISCARD TABLESPACE { Lex->tablespace_op=DISCARD_TABLESPACE; }
| IMPORT TABLESPACE { Lex->tablespace_op=IMPORT_TABLESPACE; }
| alter_list_item
| alter_list ',' alter_list_item;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment