Merge bk-internal:/home/bk/mysql-5.0
into serg.mylan:/usr/home/serg/Abk/mysql-5.0 sql/ha_innodb.cc: Auto merged sql/item_cmpfunc.cc: Auto merged sql/sql_repl.cc: Auto merged
This commit is contained in:
commit
f508a6ca80
@ -1593,6 +1593,22 @@ static int reconnect(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_current_db()
|
||||||
|
{
|
||||||
|
MYSQL_RES *res;
|
||||||
|
|
||||||
|
my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
|
current_db= NULL;
|
||||||
|
/* In case of error below current_db will be NULL */
|
||||||
|
if (!mysql_query(&mysql, "SELECT DATABASE()") &&
|
||||||
|
(res= mysql_use_result(&mysql)))
|
||||||
|
{
|
||||||
|
MYSQL_ROW row= mysql_fetch_row(res);
|
||||||
|
if (row[0])
|
||||||
|
current_db= my_strdup(row[0], MYF(MY_WME));
|
||||||
|
mysql_free_result(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
The different commands
|
The different commands
|
||||||
@ -1917,6 +1933,10 @@ com_go(String *buffer,char *line __attribute__((unused)))
|
|||||||
if (err >= 1)
|
if (err >= 1)
|
||||||
error= put_error(&mysql);
|
error= put_error(&mysql);
|
||||||
|
|
||||||
|
if (!error && !status.batch &&
|
||||||
|
(mysql.server_status & SERVER_STATUS_DB_DROPPED))
|
||||||
|
get_current_db();
|
||||||
|
|
||||||
return error; /* New command follows */
|
return error; /* New command follows */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2631,24 +2651,7 @@ com_use(String *buffer __attribute__((unused)), char *line)
|
|||||||
under our feet, for example if DROP DATABASE or RENAME DATABASE
|
under our feet, for example if DROP DATABASE or RENAME DATABASE
|
||||||
(latter one not yet available by the time the comment was written)
|
(latter one not yet available by the time the comment was written)
|
||||||
*/
|
*/
|
||||||
/* Let's reset current_db, assume it's gone */
|
get_current_db();
|
||||||
my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
|
|
||||||
current_db= 0;
|
|
||||||
/*
|
|
||||||
We don't care about in case of an error below because current_db
|
|
||||||
was just set to 0.
|
|
||||||
*/
|
|
||||||
if (!mysql_query(&mysql, "SELECT DATABASE()") &&
|
|
||||||
(res= mysql_use_result(&mysql)))
|
|
||||||
{
|
|
||||||
row= mysql_fetch_row(res);
|
|
||||||
if (row[0])
|
|
||||||
{
|
|
||||||
current_db= my_strdup(row[0], MYF(MY_WME));
|
|
||||||
}
|
|
||||||
(void) mysql_fetch_row(res); // Read eof
|
|
||||||
mysql_free_result(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current_db || cmp_database(charset_info, current_db,tmp))
|
if (!current_db || cmp_database(charset_info, current_db,tmp))
|
||||||
{
|
{
|
||||||
|
@ -865,6 +865,11 @@ static int dbConnect(char *host, char *user,char *passwd)
|
|||||||
DBerror(&mysql_connection, "when trying to connect");
|
DBerror(&mysql_connection, "when trying to connect");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Don't dump SET NAMES with a pre-4.1 server (bug#7997).
|
||||||
|
*/
|
||||||
|
if (mysql_get_server_version(&mysql_connection) < 40100)
|
||||||
|
opt_set_charset= 0;
|
||||||
/*
|
/*
|
||||||
As we're going to set SQL_MODE, it would be lost on reconnect, so we
|
As we're going to set SQL_MODE, it would be lost on reconnect, so we
|
||||||
cannot reconnect.
|
cannot reconnect.
|
||||||
|
@ -531,7 +531,7 @@ typedef uint32 ha_checksum;
|
|||||||
|
|
||||||
/* Define the type of function to be passed to process_default_option_files */
|
/* Define the type of function to be passed to process_default_option_files */
|
||||||
typedef int (*Process_option_func)(void *ctx, const char *group_name,
|
typedef int (*Process_option_func)(void *ctx, const char *group_name,
|
||||||
const char *option);
|
const char *option);
|
||||||
|
|
||||||
#include <my_alloc.h>
|
#include <my_alloc.h>
|
||||||
|
|
||||||
@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv,
|
|||||||
char **defaults, char **extra_defaults);
|
char **defaults, char **extra_defaults);
|
||||||
extern int load_defaults(const char *conf_file, const char **groups,
|
extern int load_defaults(const char *conf_file, const char **groups,
|
||||||
int *argc, char ***argv);
|
int *argc, char ***argv);
|
||||||
extern int process_default_option_files(const char *conf_file,
|
extern int my_search_option_files(const char *conf_file, int *argc,
|
||||||
Process_option_func func,
|
char ***argv, uint *args_used,
|
||||||
void *func_ctx);
|
Process_option_func func, void *func_ctx);
|
||||||
extern void free_defaults(char **argv);
|
extern void free_defaults(char **argv);
|
||||||
extern void print_defaults(const char *conf_file, const char **groups);
|
extern void print_defaults(const char *conf_file, const char **groups);
|
||||||
extern my_bool my_compress(byte *, ulong *, ulong *);
|
extern my_bool my_compress(byte *, ulong *, ulong *);
|
||||||
|
@ -147,6 +147,7 @@ enum enum_server_command
|
|||||||
COM_FETCH command.
|
COM_FETCH command.
|
||||||
*/
|
*/
|
||||||
#define SERVER_STATUS_LAST_ROW_SENT 128
|
#define SERVER_STATUS_LAST_ROW_SENT 128
|
||||||
|
#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
|
||||||
|
|
||||||
#define MYSQL_ERRMSG_SIZE 512
|
#define MYSQL_ERRMSG_SIZE 512
|
||||||
#define NET_READ_TIMEOUT 30 /* Timeout on read */
|
#define NET_READ_TIMEOUT 30 /* Timeout on read */
|
||||||
|
@ -1642,7 +1642,7 @@ btr_cur_optimistic_update(
|
|||||||
|
|
||||||
btr_search_update_hash_on_delete(cursor);
|
btr_search_update_hash_on_delete(cursor);
|
||||||
|
|
||||||
page_cur_delete_rec(page_cursor, index, mtr);
|
page_cur_delete_rec(page_cursor, index, offsets, mtr);
|
||||||
|
|
||||||
page_cur_move_to_prev(page_cursor);
|
page_cur_move_to_prev(page_cursor);
|
||||||
|
|
||||||
@ -1885,7 +1885,7 @@ btr_cur_pessimistic_update(
|
|||||||
|
|
||||||
btr_search_update_hash_on_delete(cursor);
|
btr_search_update_hash_on_delete(cursor);
|
||||||
|
|
||||||
page_cur_delete_rec(page_cursor, index, mtr);
|
page_cur_delete_rec(page_cursor, index, offsets, mtr);
|
||||||
|
|
||||||
page_cur_move_to_prev(page_cursor);
|
page_cur_move_to_prev(page_cursor);
|
||||||
|
|
||||||
@ -2401,6 +2401,7 @@ btr_cur_optimistic_delete(
|
|||||||
mem_heap_t* heap = NULL;
|
mem_heap_t* heap = NULL;
|
||||||
ulint offsets_[100] = { 100, };
|
ulint offsets_[100] = { 100, };
|
||||||
ulint* offsets = offsets_;
|
ulint* offsets = offsets_;
|
||||||
|
ibool no_compress_needed;
|
||||||
|
|
||||||
ut_ad(mtr_memo_contains(mtr, buf_block_align(btr_cur_get_page(cursor)),
|
ut_ad(mtr_memo_contains(mtr, buf_block_align(btr_cur_get_page(cursor)),
|
||||||
MTR_MEMO_PAGE_X_FIX));
|
MTR_MEMO_PAGE_X_FIX));
|
||||||
@ -2414,9 +2415,11 @@ btr_cur_optimistic_delete(
|
|||||||
offsets = rec_get_offsets(rec, cursor->index, offsets,
|
offsets = rec_get_offsets(rec, cursor->index, offsets,
|
||||||
ULINT_UNDEFINED, &heap);
|
ULINT_UNDEFINED, &heap);
|
||||||
|
|
||||||
if (!rec_offs_any_extern(offsets)
|
no_compress_needed = !rec_offs_any_extern(offsets)
|
||||||
&& btr_cur_can_delete_without_compress(
|
&& btr_cur_can_delete_without_compress(
|
||||||
cursor, rec_offs_size(offsets), mtr)) {
|
cursor, rec_offs_size(offsets), mtr);
|
||||||
|
|
||||||
|
if (no_compress_needed) {
|
||||||
|
|
||||||
lock_update_delete(rec);
|
lock_update_delete(rec);
|
||||||
|
|
||||||
@ -2425,20 +2428,17 @@ btr_cur_optimistic_delete(
|
|||||||
max_ins_size = page_get_max_insert_size_after_reorganize(page,
|
max_ins_size = page_get_max_insert_size_after_reorganize(page,
|
||||||
1);
|
1);
|
||||||
page_cur_delete_rec(btr_cur_get_page_cur(cursor),
|
page_cur_delete_rec(btr_cur_get_page_cur(cursor),
|
||||||
cursor->index, mtr);
|
cursor->index, offsets, mtr);
|
||||||
|
|
||||||
ibuf_update_free_bits_low(cursor->index, page, max_ins_size,
|
ibuf_update_free_bits_low(cursor->index, page, max_ins_size,
|
||||||
mtr);
|
mtr);
|
||||||
if (heap) {
|
|
||||||
mem_heap_free(heap);
|
|
||||||
}
|
|
||||||
return(TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heap) {
|
if (heap) {
|
||||||
mem_heap_free(heap);
|
mem_heap_free(heap);
|
||||||
}
|
}
|
||||||
return(FALSE);
|
|
||||||
|
return(no_compress_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************
|
/*****************************************************************
|
||||||
@ -2478,6 +2478,7 @@ btr_cur_pessimistic_delete(
|
|||||||
ibool success;
|
ibool success;
|
||||||
ibool ret = FALSE;
|
ibool ret = FALSE;
|
||||||
mem_heap_t* heap;
|
mem_heap_t* heap;
|
||||||
|
ulint* offsets;
|
||||||
|
|
||||||
page = btr_cur_get_page(cursor);
|
page = btr_cur_get_page(cursor);
|
||||||
tree = btr_cur_get_tree(cursor);
|
tree = btr_cur_get_tree(cursor);
|
||||||
@ -2503,20 +2504,20 @@ btr_cur_pessimistic_delete(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
heap = mem_heap_create(256);
|
heap = mem_heap_create(1024);
|
||||||
rec = btr_cur_get_rec(cursor);
|
rec = btr_cur_get_rec(cursor);
|
||||||
|
|
||||||
|
offsets = rec_get_offsets(rec, cursor->index,
|
||||||
|
NULL, ULINT_UNDEFINED, &heap);
|
||||||
|
|
||||||
/* Free externally stored fields if the record is neither
|
/* Free externally stored fields if the record is neither
|
||||||
a node pointer nor in two-byte format.
|
a node pointer nor in two-byte format.
|
||||||
This avoids unnecessary calls to rec_get_offsets(). */
|
This avoids an unnecessary loop. */
|
||||||
if (cursor->index->table->comp
|
if (cursor->index->table->comp
|
||||||
? !rec_get_node_ptr_flag(rec)
|
? !rec_get_node_ptr_flag(rec)
|
||||||
: !rec_get_1byte_offs_flag(rec)) {
|
: !rec_get_1byte_offs_flag(rec)) {
|
||||||
btr_rec_free_externally_stored_fields(cursor->index,
|
btr_rec_free_externally_stored_fields(cursor->index,
|
||||||
rec, rec_get_offsets(rec, cursor->index,
|
rec, offsets, in_rollback, mtr);
|
||||||
NULL, ULINT_UNDEFINED, &heap),
|
|
||||||
in_rollback, mtr);
|
|
||||||
mem_heap_empty(heap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((page_get_n_recs(page) < 2)
|
if ((page_get_n_recs(page) < 2)
|
||||||
@ -2568,7 +2569,8 @@ btr_cur_pessimistic_delete(
|
|||||||
|
|
||||||
btr_search_update_hash_on_delete(cursor);
|
btr_search_update_hash_on_delete(cursor);
|
||||||
|
|
||||||
page_cur_delete_rec(btr_cur_get_page_cur(cursor), cursor->index, mtr);
|
page_cur_delete_rec(btr_cur_get_page_cur(cursor), cursor->index,
|
||||||
|
offsets, mtr);
|
||||||
|
|
||||||
ut_ad(btr_check_node_ptr(tree, page, mtr));
|
ut_ad(btr_check_node_ptr(tree, page, mtr));
|
||||||
|
|
||||||
|
@ -729,14 +729,17 @@ dict_drop_index_tree(
|
|||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
||||||
|
|
||||||
void
|
ulint
|
||||||
dict_truncate_index_tree(
|
dict_truncate_index_tree(
|
||||||
/*=====================*/
|
/*=====================*/
|
||||||
|
/* out: new root page number, or
|
||||||
|
FIL_NULL on failure */
|
||||||
dict_table_t* table, /* in: the table the index belongs to */
|
dict_table_t* table, /* in: the table the index belongs to */
|
||||||
rec_t* rec, /* in: record in the clustered index of
|
rec_t* rec, /* in: record in the clustered index of
|
||||||
SYS_INDEXES table */
|
SYS_INDEXES table */
|
||||||
mtr_t* mtr) /* in: mtr having the latch
|
mtr_t* mtr) /* in: mtr having the latch
|
||||||
on the record page */
|
on the record page. The mtr may be
|
||||||
|
committed and restarted in this call. */
|
||||||
{
|
{
|
||||||
ulint root_page_no;
|
ulint root_page_no;
|
||||||
ulint space;
|
ulint space;
|
||||||
@ -761,7 +764,10 @@ dict_truncate_index_tree(
|
|||||||
if (root_page_no == FIL_NULL) {
|
if (root_page_no == FIL_NULL) {
|
||||||
/* The tree has been freed. */
|
/* The tree has been freed. */
|
||||||
|
|
||||||
return;
|
ut_print_timestamp(stderr);
|
||||||
|
fprintf(stderr, " InnoDB: Trying to TRUNCATE"
|
||||||
|
" a missing index of table %s!\n", table->name);
|
||||||
|
return(FIL_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = rec_get_nth_field_old(rec,
|
ptr = rec_get_nth_field_old(rec,
|
||||||
@ -775,7 +781,10 @@ dict_truncate_index_tree(
|
|||||||
/* It is a single table tablespace and the .ibd file is
|
/* It is a single table tablespace and the .ibd file is
|
||||||
missing: do nothing */
|
missing: do nothing */
|
||||||
|
|
||||||
return;
|
ut_print_timestamp(stderr);
|
||||||
|
fprintf(stderr, " InnoDB: Trying to TRUNCATE"
|
||||||
|
" a missing .ibd file of table %s!\n", table->name);
|
||||||
|
return(FIL_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = rec_get_nth_field_old(rec,
|
ptr = rec_get_nth_field_old(rec,
|
||||||
@ -801,6 +810,20 @@ dict_truncate_index_tree(
|
|||||||
space, root_page_no, RW_X_LATCH, mtr));
|
space, root_page_no, RW_X_LATCH, mtr));
|
||||||
|
|
||||||
btr_free_root(space, root_page_no, mtr);
|
btr_free_root(space, root_page_no, mtr);
|
||||||
|
/* We will temporarily write FIL_NULL to the PAGE_NO field
|
||||||
|
in SYS_INDEXES, so that the database will not get into an
|
||||||
|
inconsistent state in case it crashes between the mtr_commit()
|
||||||
|
below and the following mtr_commit() call. */
|
||||||
|
page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD,
|
||||||
|
FIL_NULL, mtr);
|
||||||
|
|
||||||
|
/* We will need to commit the mini-transaction in order to avoid
|
||||||
|
deadlocks in the btr_create() call, because otherwise we would
|
||||||
|
be freeing and allocating pages in the same mini-transaction. */
|
||||||
|
mtr_commit(mtr);
|
||||||
|
/* mtr_commit() will invalidate rec. */
|
||||||
|
rec = NULL;
|
||||||
|
mtr_start(mtr);
|
||||||
|
|
||||||
/* Find the index corresponding to this SYS_INDEXES record. */
|
/* Find the index corresponding to this SYS_INDEXES record. */
|
||||||
for (index = UT_LIST_GET_FIRST(table->indexes);
|
for (index = UT_LIST_GET_FIRST(table->indexes);
|
||||||
@ -814,11 +837,17 @@ dict_truncate_index_tree(
|
|||||||
root_page_no = btr_create(type, space, index_id, comp, mtr);
|
root_page_no = btr_create(type, space, index_id, comp, mtr);
|
||||||
if (index) {
|
if (index) {
|
||||||
index->tree->page = root_page_no;
|
index->tree->page = root_page_no;
|
||||||
|
} else {
|
||||||
|
ut_print_timestamp(stderr);
|
||||||
|
fprintf(stderr,
|
||||||
|
" InnoDB: Index %lu %lu of table %s is missing\n"
|
||||||
|
"InnoDB: from the data dictionary during TRUNCATE!\n",
|
||||||
|
ut_dulint_get_high(index_id),
|
||||||
|
ut_dulint_get_low(index_id),
|
||||||
|
table->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
page_rec_write_index_page_no(rec,
|
return(root_page_no);
|
||||||
DICT_SYS_INDEXES_PAGE_NO_FIELD,
|
|
||||||
root_page_no, mtr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
|
@ -2719,7 +2719,8 @@ scan_more:
|
|||||||
/* Starting quote: remember the quote character. */
|
/* Starting quote: remember the quote character. */
|
||||||
quote = *sptr;
|
quote = *sptr;
|
||||||
} else if (*sptr == '#'
|
} else if (*sptr == '#'
|
||||||
|| (0 == memcmp("-- ", sptr, 3))) {
|
|| (sptr[0] == '-' && sptr[1] == '-' &&
|
||||||
|
sptr[2] == ' ')) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* In Unix a newline is 0x0A while in Windows
|
/* In Unix a newline is 0x0A while in Windows
|
||||||
it is 0x0D followed by 0x0A */
|
it is 0x0D followed by 0x0A */
|
||||||
|
@ -3817,13 +3817,6 @@ fil_io(
|
|||||||
node = UT_LIST_GET_FIRST(space->chain);
|
node = UT_LIST_GET_FIRST(space->chain);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (space->id != 0 && node->size == 0) {
|
|
||||||
/* We do not know the size of a single-table tablespace
|
|
||||||
before we open the file */
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"InnoDB: Error: trying to access page number %lu in space %lu,\n"
|
"InnoDB: Error: trying to access page number %lu in space %lu,\n"
|
||||||
@ -3837,6 +3830,13 @@ fil_io(
|
|||||||
ut_error;
|
ut_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (space->id != 0 && node->size == 0) {
|
||||||
|
/* We do not know the size of a single-table tablespace
|
||||||
|
before we open the file */
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->size > block_offset) {
|
if (node->size > block_offset) {
|
||||||
/* Found! */
|
/* Found! */
|
||||||
break;
|
break;
|
||||||
|
@ -56,14 +56,17 @@ dict_create_index_step(
|
|||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
||||||
|
|
||||||
void
|
ulint
|
||||||
dict_truncate_index_tree(
|
dict_truncate_index_tree(
|
||||||
/*=====================*/
|
/*=====================*/
|
||||||
|
/* out: new root page number, or
|
||||||
|
FIL_NULL on failure */
|
||||||
dict_table_t* table, /* in: the table the index belongs to */
|
dict_table_t* table, /* in: the table the index belongs to */
|
||||||
rec_t* rec, /* in: record in the clustered index of
|
rec_t* rec, /* in: record in the clustered index of
|
||||||
SYS_INDEXES table */
|
SYS_INDEXES table */
|
||||||
mtr_t* mtr); /* in: mtr having the latch
|
mtr_t* mtr); /* in: mtr having the latch
|
||||||
on the record page */
|
on the record page. The mtr may be
|
||||||
|
committed and restarted in this call. */
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
Drops the index tree associated with a row in SYS_INDEXES table. */
|
Drops the index tree associated with a row in SYS_INDEXES table. */
|
||||||
|
|
||||||
|
@ -182,9 +182,10 @@ next record after the deleted one. */
|
|||||||
void
|
void
|
||||||
page_cur_delete_rec(
|
page_cur_delete_rec(
|
||||||
/*================*/
|
/*================*/
|
||||||
page_cur_t* cursor, /* in: a page cursor */
|
page_cur_t* cursor, /* in: a page cursor */
|
||||||
dict_index_t* index, /* in: record descriptor */
|
dict_index_t* index, /* in: record descriptor */
|
||||||
mtr_t* mtr); /* in: mini-transaction handle */
|
const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */
|
||||||
|
mtr_t* mtr); /* in: mini-transaction handle */
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
Searches the right position for a page cursor. */
|
Searches the right position for a page cursor. */
|
||||||
UNIV_INLINE
|
UNIV_INLINE
|
||||||
|
@ -528,7 +528,7 @@ page_mem_free(
|
|||||||
/*==========*/
|
/*==========*/
|
||||||
page_t* page, /* in: index page */
|
page_t* page, /* in: index page */
|
||||||
rec_t* rec, /* in: pointer to the (origin of) record */
|
rec_t* rec, /* in: pointer to the (origin of) record */
|
||||||
dict_index_t* index); /* in: record descriptor */
|
const ulint* offsets);/* in: array returned by rec_get_offsets() */
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
The index page creation function. */
|
The index page creation function. */
|
||||||
|
|
||||||
|
@ -777,20 +777,31 @@ page_mem_free(
|
|||||||
/*==========*/
|
/*==========*/
|
||||||
page_t* page, /* in: index page */
|
page_t* page, /* in: index page */
|
||||||
rec_t* rec, /* in: pointer to the (origin of) record */
|
rec_t* rec, /* in: pointer to the (origin of) record */
|
||||||
dict_index_t* index) /* in: record descriptor */
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
||||||
{
|
{
|
||||||
rec_t* free;
|
rec_t* free;
|
||||||
ulint garbage;
|
ulint garbage;
|
||||||
|
|
||||||
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
||||||
free = page_header_get_ptr(page, PAGE_FREE);
|
free = page_header_get_ptr(page, PAGE_FREE);
|
||||||
|
|
||||||
page_rec_set_next(rec, free);
|
page_rec_set_next(rec, free);
|
||||||
page_header_set_ptr(page, PAGE_FREE, rec);
|
page_header_set_ptr(page, PAGE_FREE, rec);
|
||||||
|
|
||||||
|
#if 0 /* It's better not to destroy the user's data. */
|
||||||
|
|
||||||
|
/* Clear the data bytes of the deleted record in order to improve
|
||||||
|
the compression ratio of the page and to make it easier to read
|
||||||
|
page dumps in corruption reports. The extra bytes of the record
|
||||||
|
cannot be cleared, because page_mem_alloc() needs them in order
|
||||||
|
to determine the size of the deleted record. */
|
||||||
|
memset(rec, 0, rec_offs_data_size(offsets));
|
||||||
|
#endif
|
||||||
|
|
||||||
garbage = page_header_get_field(page, PAGE_GARBAGE);
|
garbage = page_header_get_field(page, PAGE_GARBAGE);
|
||||||
|
|
||||||
page_header_set_field(page, PAGE_GARBAGE,
|
page_header_set_field(page, PAGE_GARBAGE,
|
||||||
garbage + rec_get_size(rec, index));
|
garbage + rec_offs_size(offsets));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef UNIV_MATERIALIZE
|
#ifdef UNIV_MATERIALIZE
|
||||||
|
@ -435,15 +435,6 @@ rec_offs_size(
|
|||||||
/* out: size */
|
/* out: size */
|
||||||
const ulint* offsets);/* in: array returned by rec_get_offsets() */
|
const ulint* offsets);/* in: array returned by rec_get_offsets() */
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
Returns the total size of a physical record. */
|
|
||||||
|
|
||||||
ulint
|
|
||||||
rec_get_size(
|
|
||||||
/*=========*/
|
|
||||||
/* out: size */
|
|
||||||
rec_t* rec, /* in: physical record */
|
|
||||||
dict_index_t* index); /* in: record descriptor */
|
|
||||||
/**************************************************************
|
|
||||||
Returns a pointer to the start of the record. */
|
Returns a pointer to the start of the record. */
|
||||||
UNIV_INLINE
|
UNIV_INLINE
|
||||||
byte*
|
byte*
|
||||||
|
@ -24,6 +24,32 @@ Created 12/9/1995 Heikki Tuuri
|
|||||||
#include "trx0sys.h"
|
#include "trx0sys.h"
|
||||||
#include "trx0trx.h"
|
#include "trx0trx.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
General philosophy of InnoDB redo-logs:
|
||||||
|
|
||||||
|
1) Every change to a contents of a data page must be done
|
||||||
|
through mtr, which in mtr_commit() writes log records
|
||||||
|
to the InnoDB redo log.
|
||||||
|
|
||||||
|
2) Normally these changes are performed using a mlog_write_ulint()
|
||||||
|
or similar function.
|
||||||
|
|
||||||
|
3) In some page level operations only a code number of a
|
||||||
|
c-function and its parameters are written to the log to
|
||||||
|
reduce the size of the log.
|
||||||
|
|
||||||
|
3a) You should not add parameters to these kind of functions
|
||||||
|
(e.g. trx_undo_header_create(), trx_undo_insert_header_reuse())
|
||||||
|
|
||||||
|
3b) You should not add such functionality which either change
|
||||||
|
working when compared with the old or are dependent on data
|
||||||
|
outside of the page. These kind of functions should implement
|
||||||
|
self-contained page transformation and it should be unchanged
|
||||||
|
if you don't have very essential reasons to change log
|
||||||
|
semantics or format.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
/* Current free limit of space 0; protected by the log sys mutex; 0 means
|
/* Current free limit of space 0; protected by the log sys mutex; 0 means
|
||||||
uninitialized */
|
uninitialized */
|
||||||
ulint log_fsp_current_free_limit = 0;
|
ulint log_fsp_current_free_limit = 0;
|
||||||
|
@ -1437,7 +1437,7 @@ loop:
|
|||||||
|
|
||||||
/* This page is allocated from the buffer pool and used in the function
|
/* This page is allocated from the buffer pool and used in the function
|
||||||
below */
|
below */
|
||||||
page_t* recv_backup_application_page = NULL;
|
static page_t* recv_backup_application_page = NULL;
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
Applies log records in the hash table to a backup. */
|
Applies log records in the hash table to a backup. */
|
||||||
|
@ -1267,9 +1267,18 @@ page_cur_parse_delete_rec(
|
|||||||
ut_a(offset <= UNIV_PAGE_SIZE);
|
ut_a(offset <= UNIV_PAGE_SIZE);
|
||||||
|
|
||||||
if (page) {
|
if (page) {
|
||||||
page_cur_position(page + offset, &cursor);
|
mem_heap_t* heap = NULL;
|
||||||
|
ulint offsets_[100] = { 100, };
|
||||||
|
rec_t* rec = page + offset;
|
||||||
|
|
||||||
page_cur_delete_rec(&cursor, index, mtr);
|
page_cur_position(rec, &cursor);
|
||||||
|
|
||||||
|
page_cur_delete_rec(&cursor, index,
|
||||||
|
rec_get_offsets(rec, index, offsets_,
|
||||||
|
ULINT_UNDEFINED, &heap), mtr);
|
||||||
|
if (heap) {
|
||||||
|
mem_heap_free(heap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return(ptr);
|
return(ptr);
|
||||||
@ -1284,6 +1293,7 @@ page_cur_delete_rec(
|
|||||||
/*================*/
|
/*================*/
|
||||||
page_cur_t* cursor, /* in: a page cursor */
|
page_cur_t* cursor, /* in: a page cursor */
|
||||||
dict_index_t* index, /* in: record descriptor */
|
dict_index_t* index, /* in: record descriptor */
|
||||||
|
const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */
|
||||||
mtr_t* mtr) /* in: mini-transaction handle */
|
mtr_t* mtr) /* in: mini-transaction handle */
|
||||||
{
|
{
|
||||||
page_dir_slot_t* cur_dir_slot;
|
page_dir_slot_t* cur_dir_slot;
|
||||||
@ -1300,6 +1310,7 @@ page_cur_delete_rec(
|
|||||||
|
|
||||||
page = page_cur_get_page(cursor);
|
page = page_cur_get_page(cursor);
|
||||||
current_rec = cursor->rec;
|
current_rec = cursor->rec;
|
||||||
|
ut_ad(rec_offs_validate(current_rec, index, offsets));
|
||||||
|
|
||||||
/* The record must not be the supremum or infimum record. */
|
/* The record must not be the supremum or infimum record. */
|
||||||
ut_ad(current_rec != page_get_supremum_rec(page));
|
ut_ad(current_rec != page_get_supremum_rec(page));
|
||||||
@ -1365,7 +1376,7 @@ page_cur_delete_rec(
|
|||||||
page_dir_slot_set_n_owned(cur_dir_slot, cur_n_owned - 1);
|
page_dir_slot_set_n_owned(cur_dir_slot, cur_n_owned - 1);
|
||||||
|
|
||||||
/* 6. Free the memory occupied by the record */
|
/* 6. Free the memory occupied by the record */
|
||||||
page_mem_free(page, current_rec, index);
|
page_mem_free(page, current_rec, offsets);
|
||||||
|
|
||||||
/* 7. Now we have decremented the number of owned records of the slot.
|
/* 7. Now we have decremented the number of owned records of the slot.
|
||||||
If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the
|
If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the
|
||||||
|
@ -416,7 +416,7 @@ page_create(
|
|||||||
|
|
||||||
mem_heap_free(heap);
|
mem_heap_free(heap);
|
||||||
|
|
||||||
/* 4. INITIALIZE THE PAGE HEADER */
|
/* 4. INITIALIZE THE PAGE */
|
||||||
|
|
||||||
page_header_set_field(page, PAGE_N_DIR_SLOTS, 2);
|
page_header_set_field(page, PAGE_N_DIR_SLOTS, 2);
|
||||||
page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top);
|
page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top);
|
||||||
@ -428,6 +428,8 @@ page_create(
|
|||||||
page_header_set_field(page, PAGE_N_DIRECTION, 0);
|
page_header_set_field(page, PAGE_N_DIRECTION, 0);
|
||||||
page_header_set_field(page, PAGE_N_RECS, 0);
|
page_header_set_field(page, PAGE_N_RECS, 0);
|
||||||
page_set_max_trx_id(page, ut_dulint_zero);
|
page_set_max_trx_id(page, ut_dulint_zero);
|
||||||
|
memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START
|
||||||
|
- (heap_top - page));
|
||||||
|
|
||||||
/* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
|
/* 5. SET POINTERS IN RECORDS AND DIR SLOTS */
|
||||||
|
|
||||||
@ -829,12 +831,18 @@ page_delete_rec_list_start(
|
|||||||
{
|
{
|
||||||
page_cur_t cur1;
|
page_cur_t cur1;
|
||||||
ulint log_mode;
|
ulint log_mode;
|
||||||
|
ulint offsets_[100] = { 100, };
|
||||||
|
ulint* offsets = offsets_;
|
||||||
|
mem_heap_t* heap = NULL;
|
||||||
|
byte type;
|
||||||
|
|
||||||
page_delete_rec_list_write_log(page, rec, index,
|
if (index->table->comp) {
|
||||||
index->table->comp
|
type = MLOG_COMP_LIST_START_DELETE;
|
||||||
? MLOG_COMP_LIST_START_DELETE
|
} else {
|
||||||
: MLOG_LIST_START_DELETE,
|
type = MLOG_LIST_START_DELETE;
|
||||||
mtr);
|
}
|
||||||
|
|
||||||
|
page_delete_rec_list_write_log(page, rec, index, type, mtr);
|
||||||
|
|
||||||
page_cur_set_before_first(page, &cur1);
|
page_cur_set_before_first(page, &cur1);
|
||||||
|
|
||||||
@ -850,8 +858,13 @@ page_delete_rec_list_start(
|
|||||||
log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
|
log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
|
||||||
|
|
||||||
while (page_cur_get_rec(&cur1) != rec) {
|
while (page_cur_get_rec(&cur1) != rec) {
|
||||||
|
offsets = rec_get_offsets(page_cur_get_rec(&cur1), index,
|
||||||
|
offsets, ULINT_UNDEFINED, &heap);
|
||||||
|
page_cur_delete_rec(&cur1, index, offsets, mtr);
|
||||||
|
}
|
||||||
|
|
||||||
page_cur_delete_rec(&cur1, index, mtr);
|
if (heap) {
|
||||||
|
mem_heap_free(heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restore log mode */
|
/* Restore log mode */
|
||||||
|
@ -620,7 +620,7 @@ rec_set_nth_field_extern_bit_new(
|
|||||||
mlog_write_ulint(lens + 1, len,
|
mlog_write_ulint(lens + 1, len,
|
||||||
MLOG_1BYTE, mtr);
|
MLOG_1BYTE, mtr);
|
||||||
} else {
|
} else {
|
||||||
lens[1] = len;
|
lens[1] = (byte) len;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -658,29 +658,6 @@ rec_set_field_extern_bits(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************
|
|
||||||
Returns the total size of a physical record. */
|
|
||||||
|
|
||||||
ulint
|
|
||||||
rec_get_size(
|
|
||||||
/*=========*/
|
|
||||||
/* out: size */
|
|
||||||
rec_t* rec, /* in: physical record */
|
|
||||||
dict_index_t* index) /* in: record descriptor */
|
|
||||||
{
|
|
||||||
mem_heap_t* heap = NULL;
|
|
||||||
ulint offsets_[100 + REC_OFFS_HEADER_SIZE]
|
|
||||||
= { 100, };
|
|
||||||
ulint* offsets = rec_get_offsets(rec, index, offsets_,
|
|
||||||
ULINT_UNDEFINED, &heap);
|
|
||||||
ulint size = rec_offs_size(offsets);
|
|
||||||
|
|
||||||
if (heap) {
|
|
||||||
mem_heap_free(heap);
|
|
||||||
}
|
|
||||||
return(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
Sets an old-style record field to SQL null.
|
Sets an old-style record field to SQL null.
|
||||||
The physical size of the field is not changed. */
|
The physical size of the field is not changed. */
|
||||||
@ -935,13 +912,13 @@ init:
|
|||||||
|| dtype_get_mtype(type) == DATA_BLOB);
|
|| dtype_get_mtype(type) == DATA_BLOB);
|
||||||
if (len < 128 || (dtype_get_len(type) < 256
|
if (len < 128 || (dtype_get_len(type) < 256
|
||||||
&& dtype_get_mtype(type) != DATA_BLOB)) {
|
&& dtype_get_mtype(type) != DATA_BLOB)) {
|
||||||
*lens-- = len;
|
*lens-- = (byte) len;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* the extern bits will be set later */
|
/* the extern bits will be set later */
|
||||||
ut_ad(len < 16384);
|
ut_ad(len < 16384);
|
||||||
*lens-- = len >> 8 | 0x80;
|
*lens-- = (byte) (len >> 8) | 0x80;
|
||||||
*lens-- = len;
|
*lens-- = (byte) len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy:
|
copy:
|
||||||
|
@ -2615,6 +2615,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */
|
|||||||
rec_t* rec;
|
rec_t* rec;
|
||||||
const byte* field;
|
const byte* field;
|
||||||
ulint len;
|
ulint len;
|
||||||
|
ulint root_page_no;
|
||||||
|
|
||||||
if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) {
|
if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) {
|
||||||
/* The end of SYS_INDEXES has been reached. */
|
/* The end of SYS_INDEXES has been reached. */
|
||||||
@ -2633,11 +2634,33 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */
|
|||||||
|
|
||||||
if (rec_get_deleted_flag(rec, FALSE)) {
|
if (rec_get_deleted_flag(rec, FALSE)) {
|
||||||
/* The index has been dropped. */
|
/* The index has been dropped. */
|
||||||
continue;
|
goto next_rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_truncate_index_tree(table, rec, &mtr);
|
btr_pcur_store_position(&pcur, &mtr);
|
||||||
|
|
||||||
|
/* This call may commit and restart mtr. */
|
||||||
|
root_page_no = dict_truncate_index_tree(table, rec, &mtr);
|
||||||
|
|
||||||
|
btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr);
|
||||||
|
rec = btr_pcur_get_rec(&pcur);
|
||||||
|
|
||||||
|
if (root_page_no != FIL_NULL) {
|
||||||
|
page_rec_write_index_page_no(rec,
|
||||||
|
DICT_SYS_INDEXES_PAGE_NO_FIELD,
|
||||||
|
root_page_no, &mtr);
|
||||||
|
/* We will need to commit and restart the
|
||||||
|
mini-transaction in order to avoid deadlocks.
|
||||||
|
The dict_truncate_index_tree() call has allocated
|
||||||
|
a page in this mini-transaction, and the rest of
|
||||||
|
this loop could latch another index page. */
|
||||||
|
mtr_commit(&mtr);
|
||||||
|
mtr_start(&mtr);
|
||||||
|
btr_pcur_restore_position(BTR_MODIFY_LEAF,
|
||||||
|
&pcur, &mtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
next_rec:
|
||||||
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
|
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3058,14 +3058,19 @@ row_search_for_mysql(
|
|||||||
ut_error;
|
ut_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trx->n_mysql_tables_in_use == 0) {
|
if (trx->n_mysql_tables_in_use == 0
|
||||||
|
&& 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
|
||||||
|
::start_stmt. */
|
||||||
|
|
||||||
fputs(
|
fputs(
|
||||||
"InnoDB: Error: MySQL is trying to perform a SELECT\n"
|
"InnoDB: Error: MySQL is trying to perform a SELECT\n"
|
||||||
"InnoDB: but it has not locked any tables in ::external_lock()!\n",
|
"InnoDB: but it has not locked any tables in ::external_lock()!\n",
|
||||||
stderr);
|
stderr);
|
||||||
trx_print(stderr, trx);
|
trx_print(stderr, trx);
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
ut_a(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode);
|
/* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode);
|
||||||
|
@ -1501,7 +1501,7 @@ srv_suspend_mysql_thread(
|
|||||||
ut_usectime(&sec, &ms);
|
ut_usectime(&sec, &ms);
|
||||||
finish_time = (ib_longlong)sec * 1000000 + ms;
|
finish_time = (ib_longlong)sec * 1000000 + ms;
|
||||||
|
|
||||||
diff_time = finish_time - start_time;
|
diff_time = (ulint) (finish_time - start_time);
|
||||||
|
|
||||||
srv_n_lock_wait_current_count--;
|
srv_n_lock_wait_current_count--;
|
||||||
srv_n_lock_wait_time = srv_n_lock_wait_time + diff_time;
|
srv_n_lock_wait_time = srv_n_lock_wait_time + diff_time;
|
||||||
@ -1799,9 +1799,12 @@ srv_export_innodb_status(void)
|
|||||||
export_vars.innodb_row_lock_waits= srv_n_lock_wait_count;
|
export_vars.innodb_row_lock_waits= srv_n_lock_wait_count;
|
||||||
export_vars.innodb_row_lock_current_waits= srv_n_lock_wait_current_count;
|
export_vars.innodb_row_lock_current_waits= srv_n_lock_wait_current_count;
|
||||||
export_vars.innodb_row_lock_time= srv_n_lock_wait_time / 10000;
|
export_vars.innodb_row_lock_time= srv_n_lock_wait_time / 10000;
|
||||||
export_vars.innodb_row_lock_time_avg=
|
if (srv_n_lock_wait_count > 0) {
|
||||||
(srv_n_lock_wait_count > 0) ?
|
export_vars.innodb_row_lock_time_avg = (ulint)
|
||||||
(srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count) : 0;
|
(srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count);
|
||||||
|
} else {
|
||||||
|
export_vars.innodb_row_lock_time_avg = 0;
|
||||||
|
}
|
||||||
export_vars.innodb_row_lock_time_max= srv_n_lock_max_wait_time / 10000;
|
export_vars.innodb_row_lock_time_max= srv_n_lock_max_wait_time / 10000;
|
||||||
export_vars.innodb_rows_read= srv_n_rows_read;
|
export_vars.innodb_rows_read= srv_n_rows_read;
|
||||||
export_vars.innodb_rows_inserted= srv_n_rows_inserted;
|
export_vars.innodb_rows_inserted= srv_n_rows_inserted;
|
||||||
|
@ -369,11 +369,11 @@ mutex_spin_wait(
|
|||||||
{
|
{
|
||||||
ulint index; /* index of the reserved wait cell */
|
ulint index; /* index of the reserved wait cell */
|
||||||
ulint i; /* spin round count */
|
ulint i; /* spin round count */
|
||||||
|
#ifndef UNIV_HOTBACKUP
|
||||||
ib_longlong lstart_time = 0, lfinish_time; /* for timing os_wait */
|
ib_longlong lstart_time = 0, lfinish_time; /* for timing os_wait */
|
||||||
ulint ltime_diff;
|
ulint ltime_diff;
|
||||||
ulint sec;
|
ulint sec;
|
||||||
ulint ms;
|
ulint ms;
|
||||||
#ifndef UNIV_HOTBACKUP
|
|
||||||
uint timer_started = 0;
|
uint timer_started = 0;
|
||||||
#endif /* !UNIV_HOTBACKUP */
|
#endif /* !UNIV_HOTBACKUP */
|
||||||
ut_ad(mutex);
|
ut_ad(mutex);
|
||||||
@ -535,7 +535,7 @@ finish_timing:
|
|||||||
ut_usectime(&sec, &ms);
|
ut_usectime(&sec, &ms);
|
||||||
lfinish_time= (ib_longlong)sec * 1000000 + ms;
|
lfinish_time= (ib_longlong)sec * 1000000 + ms;
|
||||||
|
|
||||||
ltime_diff= lfinish_time - lstart_time;
|
ltime_diff= (ulint) (lfinish_time - lstart_time);
|
||||||
mutex->lspent_time += ltime_diff;
|
mutex->lspent_time += ltime_diff;
|
||||||
if (mutex->lmax_spent_time < ltime_diff)
|
if (mutex->lmax_spent_time < ltime_diff)
|
||||||
{
|
{
|
||||||
|
@ -440,7 +440,17 @@ loop:
|
|||||||
if ((trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
|
if ((trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
|
||||||
trx = UT_LIST_GET_NEXT(trx_list, trx);
|
trx = UT_LIST_GET_NEXT(trx_list, trx);
|
||||||
} else if (trx->conc_state == TRX_PREPARED) {
|
} else if (trx->conc_state == TRX_PREPARED) {
|
||||||
trx->sess = trx_dummy_sess;
|
|
||||||
|
/* Roll back all prepared transactions if
|
||||||
|
innobase_force_recovery > 0 in my.cnf */
|
||||||
|
|
||||||
|
if (srv_force_recovery > 0) {
|
||||||
|
trx->conc_state = TRX_ACTIVE;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
trx->sess = trx_dummy_sess;
|
||||||
|
trx = UT_LIST_GET_NEXT(trx_list, trx);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -435,14 +435,14 @@ trx_lists_init_at_db_start(void)
|
|||||||
|
|
||||||
if (undo->state == TRX_UNDO_PREPARED) {
|
if (undo->state == TRX_UNDO_PREPARED) {
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
||||||
ut_dulint_get_high(trx->id),
|
ut_dulint_get_high(trx->id),
|
||||||
ut_dulint_get_low(trx->id));
|
ut_dulint_get_low(trx->id));
|
||||||
|
|
||||||
/* trx->conc_state = TRX_PREPARED; */
|
trx->conc_state = TRX_ACTIVE;
|
||||||
trx->conc_state =
|
|
||||||
TRX_ACTIVE;
|
/* trx->conc_state = TRX_PREPARED;*/
|
||||||
} else {
|
} else {
|
||||||
trx->conc_state =
|
trx->conc_state =
|
||||||
TRX_COMMITTED_IN_MEMORY;
|
TRX_COMMITTED_IN_MEMORY;
|
||||||
@ -498,16 +498,15 @@ trx_lists_init_at_db_start(void)
|
|||||||
commit or abort decision from MySQL */
|
commit or abort decision from MySQL */
|
||||||
|
|
||||||
if (undo->state == TRX_UNDO_PREPARED) {
|
if (undo->state == TRX_UNDO_PREPARED) {
|
||||||
|
fprintf(stderr,
|
||||||
fprintf(stderr,
|
|
||||||
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
||||||
ut_dulint_get_high(trx->id),
|
ut_dulint_get_high(trx->id),
|
||||||
ut_dulint_get_low(trx->id));
|
ut_dulint_get_low(trx->id));
|
||||||
|
|
||||||
/* trx->conc_state = TRX_PREPARED; */
|
trx->conc_state = TRX_ACTIVE;
|
||||||
trx->conc_state =
|
|
||||||
TRX_ACTIVE;
|
|
||||||
|
|
||||||
|
/* trx->conc_state =
|
||||||
|
TRX_PREPARED; */
|
||||||
} else {
|
} else {
|
||||||
trx->conc_state =
|
trx->conc_state =
|
||||||
TRX_COMMITTED_IN_MEMORY;
|
TRX_COMMITTED_IN_MEMORY;
|
||||||
@ -1638,10 +1637,13 @@ trx_print(
|
|||||||
fputs(", not started", f);
|
fputs(", not started", f);
|
||||||
break;
|
break;
|
||||||
case TRX_ACTIVE:
|
case TRX_ACTIVE:
|
||||||
case TRX_PREPARED:
|
|
||||||
fprintf(f, ", ACTIVE %lu sec",
|
fprintf(f, ", ACTIVE %lu sec",
|
||||||
(ulong)difftime(time(NULL), trx->start_time));
|
(ulong)difftime(time(NULL), trx->start_time));
|
||||||
break;
|
break;
|
||||||
|
case TRX_PREPARED:
|
||||||
|
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
|
||||||
|
(ulong)difftime(time(NULL), trx->start_time));
|
||||||
|
break;
|
||||||
case TRX_COMMITTED_IN_MEMORY:
|
case TRX_COMMITTED_IN_MEMORY:
|
||||||
fputs(", COMMITTED IN MEMORY", f);
|
fputs(", COMMITTED IN MEMORY", f);
|
||||||
break;
|
break;
|
||||||
@ -1938,7 +1940,7 @@ trx_get_trx_by_xid(
|
|||||||
|
|
||||||
if (xid->gtrid_length == trx->xid.gtrid_length &&
|
if (xid->gtrid_length == trx->xid.gtrid_length &&
|
||||||
xid->bqual_length == trx->xid.bqual_length &&
|
xid->bqual_length == trx->xid.bqual_length &&
|
||||||
memcmp(xid, &trx->xid,
|
memcmp(xid->data, trx->xid.data,
|
||||||
xid->gtrid_length +
|
xid->gtrid_length +
|
||||||
xid->bqual_length) == 0) {
|
xid->bqual_length) == 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -599,11 +599,10 @@ trx_undo_read_xid(
|
|||||||
Adds the XA XID after an undo log old-style header. */
|
Adds the XA XID after an undo log old-style header. */
|
||||||
static
|
static
|
||||||
void
|
void
|
||||||
trx_undo_header_add_xid(
|
trx_undo_header_add_space_for_xid(
|
||||||
/*====================*/
|
/*==============================*/
|
||||||
page_t* undo_page,/* in: undo log segment header page */
|
page_t* undo_page,/* in: undo log segment header page */
|
||||||
trx_ulogf_t* log_hdr,/* in: undo log header */
|
trx_ulogf_t* log_hdr,/* in: undo log header */
|
||||||
XID* xid, /* in: X/Open XA transaction identification */
|
|
||||||
mtr_t* mtr) /* in: mtr */
|
mtr_t* mtr) /* in: mtr */
|
||||||
{
|
{
|
||||||
trx_upagef_t* page_hdr;
|
trx_upagef_t* page_hdr;
|
||||||
@ -620,9 +619,8 @@ trx_undo_header_add_xid(
|
|||||||
|
|
||||||
new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE
|
new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE
|
||||||
- TRX_UNDO_LOG_OLD_HDR_SIZE);
|
- TRX_UNDO_LOG_OLD_HDR_SIZE);
|
||||||
trx_undo_write_xid(log_hdr, xid, mtr);
|
|
||||||
|
|
||||||
/* Now that we added the XID after the header, update the free offset
|
/* Add space for a XID after the header, update the free offset
|
||||||
fields on the undo log page and in the undo log header */
|
fields on the undo log page and in the undo log header */
|
||||||
|
|
||||||
mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free,
|
mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free,
|
||||||
@ -1532,7 +1530,7 @@ trx_undo_create(
|
|||||||
|
|
||||||
offset = trx_undo_header_create(undo_page, trx_id, mtr);
|
offset = trx_undo_header_create(undo_page, trx_id, mtr);
|
||||||
|
|
||||||
trx_undo_header_add_xid(undo_page, undo_page + offset, xid, mtr);
|
trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr);
|
||||||
|
|
||||||
undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
|
undo = trx_undo_mem_create(rseg, id, type, trx_id, xid,
|
||||||
page_no, offset);
|
page_no, offset);
|
||||||
@ -1599,7 +1597,7 @@ trx_undo_reuse_cached(
|
|||||||
|
|
||||||
if (type == TRX_UNDO_INSERT) {
|
if (type == TRX_UNDO_INSERT) {
|
||||||
offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
|
offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr);
|
||||||
trx_undo_header_add_xid(undo_page, undo_page + offset, xid,
|
trx_undo_header_add_space_for_xid(undo_page, undo_page + offset,
|
||||||
mtr);
|
mtr);
|
||||||
} else {
|
} else {
|
||||||
ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
|
ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
|
||||||
@ -1607,7 +1605,7 @@ trx_undo_reuse_cached(
|
|||||||
== TRX_UNDO_UPDATE);
|
== TRX_UNDO_UPDATE);
|
||||||
|
|
||||||
offset = trx_undo_header_create(undo_page, trx_id, mtr);
|
offset = trx_undo_header_create(undo_page, trx_id, mtr);
|
||||||
trx_undo_header_add_xid(undo_page, undo_page + offset, xid,
|
trx_undo_header_add_space_for_xid(undo_page, undo_page + offset,
|
||||||
mtr);
|
mtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,3 +54,19 @@ select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1;
|
|||||||
collation(a) collation(b) collation(binary 'ccc')
|
collation(a) collation(b) collation(binary 'ccc')
|
||||||
cp1251_bin binary binary
|
cp1251_bin binary binary
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
create table t1 (
|
||||||
|
a varchar(16) character set cp1251 collate cp1251_bin not null,
|
||||||
|
b int(10) default null,
|
||||||
|
primary key(a)
|
||||||
|
) charset=cp1251;
|
||||||
|
insert into t1 (a) values ('air'),
|
||||||
|
('we'),('g'),('we_toshko'), ('s0urce'),('we_ivo'),('we_iliyan'),
|
||||||
|
('we_martin'),('vw_grado'),('vw_vasko'),('tn_vili'),('tn_kalina'),
|
||||||
|
('tn_fakira'),('vw_silvia'),('vw_starshi'),('vw_geo'),('vw_b0x1');
|
||||||
|
select * from t1 where a like 'we_%';
|
||||||
|
a b
|
||||||
|
we_iliyan NULL
|
||||||
|
we_ivo NULL
|
||||||
|
we_martin NULL
|
||||||
|
we_toshko NULL
|
||||||
|
drop table t1;
|
||||||
|
@ -669,12 +669,24 @@ select charset(max(a)), coercibility(max(a)),
|
|||||||
charset(min(a)), coercibility(min(a)) from t1;
|
charset(min(a)), coercibility(min(a)) from t1;
|
||||||
charset(max(a)) coercibility(max(a)) charset(min(a)) coercibility(min(a))
|
charset(max(a)) coercibility(max(a)) charset(min(a)) coercibility(min(a))
|
||||||
latin2 2 latin2 2
|
latin2 2 latin2 2
|
||||||
|
show create table t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`a` char(1) character set latin2 default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
create table t2 select max(a),min(a) from t1;
|
create table t2 select max(a),min(a) from t1;
|
||||||
show create table t2;
|
show create table t2;
|
||||||
Table Create Table
|
Table Create Table
|
||||||
t2 CREATE TABLE `t2` (
|
t2 CREATE TABLE `t2` (
|
||||||
`max(a)` varchar(1) character set latin2 default NULL,
|
`max(a)` char(1) character set latin2 default NULL,
|
||||||
`min(a)` varchar(1) character set latin2 default NULL
|
`min(a)` char(1) character set latin2 default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
drop table t2;
|
||||||
|
create table t2 select concat(a) from t1;
|
||||||
|
show create table t2;
|
||||||
|
Table Create Table
|
||||||
|
t2 CREATE TABLE `t2` (
|
||||||
|
`concat(a)` varchar(1) character set latin2 default NULL
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
drop table t2,t1;
|
drop table t2,t1;
|
||||||
create table t1 (a int);
|
create table t1 (a int);
|
||||||
@ -757,6 +769,15 @@ one 2
|
|||||||
two 2
|
two 2
|
||||||
three 1
|
three 1
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
create table t1(a int, b datetime);
|
||||||
|
insert into t1 values (1, NOW()), (2, NOW());
|
||||||
|
create table t2 select MAX(b) from t1 group by a;
|
||||||
|
show create table t2;
|
||||||
|
Table Create Table
|
||||||
|
t2 CREATE TABLE `t2` (
|
||||||
|
`MAX(b)` datetime default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
drop table t1, t2;
|
||||||
create table t1(f1 datetime);
|
create table t1(f1 datetime);
|
||||||
insert into t1 values (now());
|
insert into t1 values (now());
|
||||||
create table t2 select f2 from (select max(now()) f2 from t1) a;
|
create table t2 select f2 from (select max(now()) f2 from t1) a;
|
||||||
|
@ -1657,6 +1657,44 @@ a
|
|||||||
b
|
b
|
||||||
c
|
c
|
||||||
d
|
d
|
||||||
|
select distinct a1,a1 from t1;
|
||||||
|
a1 a1
|
||||||
|
a a
|
||||||
|
b b
|
||||||
|
c c
|
||||||
|
d d
|
||||||
|
select distinct a2,a1,a2,a1 from t1;
|
||||||
|
a2 a1 a2 a1
|
||||||
|
a a a a
|
||||||
|
b a b a
|
||||||
|
a b a b
|
||||||
|
b b b b
|
||||||
|
a c a c
|
||||||
|
b c b c
|
||||||
|
a d a d
|
||||||
|
b d b d
|
||||||
|
select distinct t1.a1,t2.a1 from t1,t2;
|
||||||
|
a1 a1
|
||||||
|
a a
|
||||||
|
b a
|
||||||
|
c a
|
||||||
|
d a
|
||||||
|
a b
|
||||||
|
b b
|
||||||
|
c b
|
||||||
|
d b
|
||||||
|
a c
|
||||||
|
b c
|
||||||
|
c c
|
||||||
|
d c
|
||||||
|
a d
|
||||||
|
b d
|
||||||
|
c d
|
||||||
|
d d
|
||||||
|
a e
|
||||||
|
b e
|
||||||
|
c e
|
||||||
|
d e
|
||||||
explain select distinct a1,a2,b from t1;
|
explain select distinct a1,a2,b from t1;
|
||||||
id select_type table type possible_keys key key_len ref rows Extra
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using index for group-by
|
1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using index for group-by
|
||||||
|
@ -129,7 +129,7 @@ FOUND_ROWS()
|
|||||||
1
|
1
|
||||||
execute stmt1;
|
execute stmt1;
|
||||||
FOUND_ROWS()
|
FOUND_ROWS()
|
||||||
0
|
1
|
||||||
deallocate prepare stmt1;
|
deallocate prepare stmt1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1
|
create table t1
|
||||||
|
@ -246,6 +246,31 @@ SELECT FOUND_ROWS();
|
|||||||
FOUND_ROWS()
|
FOUND_ROWS()
|
||||||
0
|
0
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
SELECT 'foo';
|
||||||
|
foo
|
||||||
|
foo
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
FOUND_ROWS()
|
||||||
|
1
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo';
|
||||||
|
foo
|
||||||
|
foo
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
FOUND_ROWS()
|
||||||
|
1
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo' limit 0;
|
||||||
|
foo
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
FOUND_ROWS()
|
||||||
|
1
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
FOUND_ROWS()
|
||||||
|
1
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo' UNION SELECT 'bar' LIMIT 0;
|
||||||
|
foo
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
FOUND_ROWS()
|
||||||
|
2
|
||||||
CREATE TABLE t1 (a int, b int);
|
CREATE TABLE t1 (a int, b int);
|
||||||
INSERT INTO t1 VALUES (1,2), (1,3), (1,4), (1,5);
|
INSERT INTO t1 VALUES (1,2), (1,3), (1,4), (1,5);
|
||||||
SELECT SQL_CALC_FOUND_ROWS DISTINCT 'a' FROM t1 GROUP BY b LIMIT 2;
|
SELECT SQL_CALC_FOUND_ROWS DISTINCT 'a' FROM t1 GROUP BY b LIMIT 2;
|
||||||
@ -254,3 +279,4 @@ a
|
|||||||
SELECT FOUND_ROWS();
|
SELECT FOUND_ROWS();
|
||||||
FOUND_ROWS()
|
FOUND_ROWS()
|
||||||
1
|
1
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -78,4 +78,19 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||||||
1 SIMPLE t2 ref b b 21 test.t1.b 6 Using where
|
1 SIMPLE t2 ref b b 21 test.t1.b 6 Using where
|
||||||
SET MAX_SEEKS_FOR_KEY=DEFAULT;
|
SET MAX_SEEKS_FOR_KEY=DEFAULT;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
create table t1 (a int);
|
||||||
|
insert into t1 values (1),(2),(3),(4),(5);
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
set local max_join_size=8;
|
||||||
|
select * from (select * from t1) x;
|
||||||
|
ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay
|
||||||
|
set local max_join_size=1;
|
||||||
|
select * from (select * from t1 a, t1 b) x;
|
||||||
|
ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay
|
||||||
|
set local max_join_size=1;
|
||||||
|
select * from (select 1 union select 2 union select 3) x;
|
||||||
|
ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay
|
||||||
|
drop table t1;
|
||||||
SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT;
|
SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT;
|
||||||
|
@ -254,7 +254,7 @@ create table t1 (a int not null);
|
|||||||
create table t2 select max(a) from t1;
|
create table t2 select max(a) from t1;
|
||||||
show columns from t2;
|
show columns from t2;
|
||||||
Field Type Null Key Default Extra
|
Field Type Null Key Default Extra
|
||||||
max(a) bigint(20) YES NULL
|
max(a) int(11) YES NULL
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
create table t1 (c decimal, d double, f float, r real);
|
create table t1 (c decimal, d double, f float, r real);
|
||||||
show columns from t1;
|
show columns from t1;
|
||||||
|
@ -466,4 +466,32 @@ ERROR 70100: Query execution was interrupted
|
|||||||
call bug6807()|
|
call bug6807()|
|
||||||
ERROR 70100: Query execution was interrupted
|
ERROR 70100: Query execution was interrupted
|
||||||
drop procedure bug6807|
|
drop procedure bug6807|
|
||||||
|
drop procedure if exists bug8776_1|
|
||||||
|
drop procedure if exists bug8776_2|
|
||||||
|
drop procedure if exists bug8776_3|
|
||||||
|
drop procedure if exists bug8776_4|
|
||||||
|
create procedure bug8776_1()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '42S0200test' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
ERROR 42000: Bad SQLSTATE: '42S0200test'
|
||||||
|
create procedure bug8776_2()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '4200' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
ERROR 42000: Bad SQLSTATE: '4200'
|
||||||
|
create procedure bug8776_3()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '420000' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
ERROR 42000: Bad SQLSTATE: '420000'
|
||||||
|
create procedure bug8776_4()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '42x00' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
ERROR 42000: Bad SQLSTATE: '42x00'
|
||||||
drop table t1|
|
drop table t1|
|
||||||
|
@ -29,6 +29,12 @@ a
|
|||||||
A
|
A
|
||||||
a,A
|
a,A
|
||||||
a,A
|
a,A
|
||||||
|
select s from t1 order by concat(s);
|
||||||
|
s
|
||||||
|
A
|
||||||
|
a
|
||||||
|
a,A
|
||||||
|
a,A
|
||||||
drop table t1;
|
drop table t1;
|
||||||
CREATE TABLE t1 (c set('ae','oe','ue','ss') collate latin1_german2_ci);
|
CREATE TABLE t1 (c set('ae','oe','ue','ss') collate latin1_german2_ci);
|
||||||
INSERT INTO t1 VALUES ('ä'),('ö'),('ü'),('ß');
|
INSERT INTO t1 VALUES ('ä'),('ö'),('ü'),('ß');
|
||||||
@ -47,4 +53,16 @@ ss
|
|||||||
ss
|
ss
|
||||||
ae,oe,ue,ss
|
ae,oe,ue,ss
|
||||||
ae,oe,ue,ss
|
ae,oe,ue,ss
|
||||||
|
SELECT c FROM t1 ORDER BY concat(c);
|
||||||
|
c
|
||||||
|
ae
|
||||||
|
ae
|
||||||
|
ae,oe,ue,ss
|
||||||
|
ae,oe,ue,ss
|
||||||
|
oe
|
||||||
|
oe
|
||||||
|
ss
|
||||||
|
ss
|
||||||
|
ue
|
||||||
|
ue
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
@ -32,3 +32,17 @@ select * from t1 where lower(b)='bbb';
|
|||||||
select charset(a), charset(b), charset(binary 'ccc') from t1 limit 1;
|
select charset(a), charset(b), charset(binary 'ccc') from t1 limit 1;
|
||||||
select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1;
|
select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
# Test for BUG#8560
|
||||||
|
create table t1 (
|
||||||
|
a varchar(16) character set cp1251 collate cp1251_bin not null,
|
||||||
|
b int(10) default null,
|
||||||
|
primary key(a)
|
||||||
|
) charset=cp1251;
|
||||||
|
insert into t1 (a) values ('air'),
|
||||||
|
('we'),('g'),('we_toshko'), ('s0urce'),('we_ivo'),('we_iliyan'),
|
||||||
|
('we_martin'),('vw_grado'),('vw_vasko'),('tn_vili'),('tn_kalina'),
|
||||||
|
('tn_fakira'),('vw_silvia'),('vw_starshi'),('vw_geo'),('vw_b0x1');
|
||||||
|
|
||||||
|
select * from t1 where a like 'we_%';
|
||||||
|
drop table t1;
|
||||||
|
@ -395,8 +395,12 @@ create table t1 (a char character set latin2);
|
|||||||
insert into t1 values ('a'),('b');
|
insert into t1 values ('a'),('b');
|
||||||
select charset(max(a)), coercibility(max(a)),
|
select charset(max(a)), coercibility(max(a)),
|
||||||
charset(min(a)), coercibility(min(a)) from t1;
|
charset(min(a)), coercibility(min(a)) from t1;
|
||||||
|
show create table t1;
|
||||||
create table t2 select max(a),min(a) from t1;
|
create table t2 select max(a),min(a) from t1;
|
||||||
show create table t2;
|
show create table t2;
|
||||||
|
drop table t2;
|
||||||
|
create table t2 select concat(a) from t1;
|
||||||
|
show create table t2;
|
||||||
drop table t2,t1;
|
drop table t2,t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -479,6 +483,15 @@ INSERT INTO t1 VALUES
|
|||||||
select val, count(*) from t1 group by val;
|
select val, count(*) from t1 group by val;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug #5615: type of aggregate function column wrong when using group by
|
||||||
|
#
|
||||||
|
|
||||||
|
create table t1(a int, b datetime);
|
||||||
|
insert into t1 values (1, NOW()), (2, NOW());
|
||||||
|
create table t2 select MAX(b) from t1 group by a;
|
||||||
|
show create table t2;
|
||||||
|
drop table t1, t2;
|
||||||
|
|
||||||
#
|
#
|
||||||
# Bug 7833: Wrong datatype of aggregate column is returned
|
# Bug 7833: Wrong datatype of aggregate column is returned
|
||||||
|
@ -475,11 +475,15 @@ select distinct a1,a2,b,c from t2 where (a2 >= 'b') and (b = 'a') and (c = 'i121
|
|||||||
select distinct a1,a2,b from t2 where (a1 > 'a') and (a2 > 'a') and (b = 'c');
|
select distinct a1,a2,b from t2 where (a1 > 'a') and (a2 > 'a') and (b = 'c');
|
||||||
select distinct b from t2 where (a2 >= 'b') and (b = 'a');
|
select distinct b from t2 where (a2 >= 'b') and (b = 'a');
|
||||||
|
|
||||||
-- BUG 6303
|
-- BUG #6303
|
||||||
select distinct t_00.a1
|
select distinct t_00.a1
|
||||||
from t1 t_00
|
from t1 t_00
|
||||||
where exists ( select * from t2 where a1 = t_00.a1 );
|
where exists ( select * from t2 where a1 = t_00.a1 );
|
||||||
|
|
||||||
|
-- BUG #8532 - SELECT DISTINCT a, a causes server to crash
|
||||||
|
select distinct a1,a1 from t1;
|
||||||
|
select distinct a2,a1,a2,a1 from t1;
|
||||||
|
select distinct t1.a1,t2.a1 from t1,t2;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- DISTINCT queries with GROUP-BY
|
-- DISTINCT queries with GROUP-BY
|
||||||
|
@ -167,6 +167,21 @@ SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a = 0 GROUP BY a HAVING a > 10;
|
|||||||
SELECT FOUND_ROWS();
|
SELECT FOUND_ROWS();
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug #6089: queries which don't use any tables
|
||||||
|
#
|
||||||
|
|
||||||
|
SELECT 'foo';
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo';
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo' limit 0;
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
|
||||||
|
SELECT SQL_CALC_FOUND_ROWS 'foo' UNION SELECT 'bar' LIMIT 0;
|
||||||
|
SELECT FOUND_ROWS();
|
||||||
|
|
||||||
#
|
#
|
||||||
# Bug #7945: group by + distinct with constant expression + limit
|
# Bug #7945: group by + distinct with constant expression + limit
|
||||||
#
|
#
|
||||||
@ -175,3 +190,4 @@ CREATE TABLE t1 (a int, b int);
|
|||||||
INSERT INTO t1 VALUES (1,2), (1,3), (1,4), (1,5);
|
INSERT INTO t1 VALUES (1,2), (1,3), (1,4), (1,5);
|
||||||
SELECT SQL_CALC_FOUND_ROWS DISTINCT 'a' FROM t1 GROUP BY b LIMIT 2;
|
SELECT SQL_CALC_FOUND_ROWS DISTINCT 'a' FROM t1 GROUP BY b LIMIT 2;
|
||||||
SELECT FOUND_ROWS();
|
SELECT FOUND_ROWS();
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -66,4 +66,24 @@ SET MAX_SEEKS_FOR_KEY=DEFAULT;
|
|||||||
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
# BUG#8726
|
||||||
|
create table t1 (a int);
|
||||||
|
insert into t1 values (1),(2),(3),(4),(5);
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
insert into t1 select * from t1;
|
||||||
|
|
||||||
|
set local max_join_size=8;
|
||||||
|
--error 1104
|
||||||
|
select * from (select * from t1) x;
|
||||||
|
|
||||||
|
set local max_join_size=1;
|
||||||
|
--error 1104
|
||||||
|
select * from (select * from t1 a, t1 b) x;
|
||||||
|
|
||||||
|
set local max_join_size=1;
|
||||||
|
--error 1104
|
||||||
|
select * from (select 1 union select 2 union select 3) x;
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT;
|
SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT;
|
||||||
|
@ -641,6 +641,44 @@ call bug6807()|
|
|||||||
|
|
||||||
drop procedure bug6807|
|
drop procedure bug6807|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#876: Stored Procedures: Invalid SQLSTATE is allowed in
|
||||||
|
# a DECLARE ? HANDLER FOR stmt.
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
drop procedure if exists bug8776_1|
|
||||||
|
drop procedure if exists bug8776_2|
|
||||||
|
drop procedure if exists bug8776_3|
|
||||||
|
drop procedure if exists bug8776_4|
|
||||||
|
--enable_warnings
|
||||||
|
--error ER_SP_BAD_SQLSTATE
|
||||||
|
create procedure bug8776_1()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '42S0200test' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
|
||||||
|
--error ER_SP_BAD_SQLSTATE
|
||||||
|
create procedure bug8776_2()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '4200' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
|
||||||
|
--error ER_SP_BAD_SQLSTATE
|
||||||
|
create procedure bug8776_3()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '420000' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
|
||||||
|
--error ER_SP_BAD_SQLSTATE
|
||||||
|
create procedure bug8776_4()
|
||||||
|
begin
|
||||||
|
declare continue handler for sqlstate '42x00' begin end;
|
||||||
|
begin end;
|
||||||
|
end|
|
||||||
|
|
||||||
|
|
||||||
drop table t1|
|
drop table t1|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ create table t1 (s set ('a','A') character set latin1 collate latin1_bin);
|
|||||||
show create table t1;
|
show create table t1;
|
||||||
insert into t1 values ('a'),('a,A'),('A,a'),('A');
|
insert into t1 values ('a'),('a,A'),('A,a'),('A');
|
||||||
select s from t1 order by s;
|
select s from t1 order by s;
|
||||||
|
select s from t1 order by concat(s);
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -34,4 +35,5 @@ INSERT INTO t1 VALUES ('ae'),('oe'),('ue'),('ss');
|
|||||||
INSERT INTO t1 VALUES ('ä,ö,ü,ß');
|
INSERT INTO t1 VALUES ('ä,ö,ü,ß');
|
||||||
INSERT INTO t1 VALUES ('ae,oe,ue,ss');
|
INSERT INTO t1 VALUES ('ae,oe,ue,ss');
|
||||||
SELECT c FROM t1 ORDER BY c;
|
SELECT c FROM t1 ORDER BY c;
|
||||||
|
SELECT c FROM t1 ORDER BY concat(c);
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
@ -83,7 +83,7 @@ static char *remove_end_comment(char *ptr);
|
|||||||
Process config files in default directories.
|
Process config files in default directories.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
search_files()
|
my_search_option_files()
|
||||||
conf_file Basename for configuration file to search for.
|
conf_file Basename for configuration file to search for.
|
||||||
If this is a path, then only this file is read.
|
If this is a path, then only this file is read.
|
||||||
argc Pointer to argc of original program
|
argc Pointer to argc of original program
|
||||||
@ -103,13 +103,13 @@ static char *remove_end_comment(char *ptr);
|
|||||||
1 given cinf_file doesn't exist
|
1 given cinf_file doesn't exist
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int search_files(const char *conf_file, int *argc, char ***argv,
|
int my_search_option_files(const char *conf_file, int *argc, char ***argv,
|
||||||
uint *args_used, Process_option_func func,
|
uint *args_used, Process_option_func func,
|
||||||
void *func_ctx)
|
void *func_ctx)
|
||||||
{
|
{
|
||||||
const char **dirs, *forced_default_file;
|
const char **dirs, *forced_default_file;
|
||||||
int error= 0;
|
int error= 0;
|
||||||
DBUG_ENTER("search_files");
|
DBUG_ENTER("my_search_option_files");
|
||||||
|
|
||||||
/* Check if we want to force the use a specific default file */
|
/* Check if we want to force the use a specific default file */
|
||||||
get_defaults_files(*argc, *argv,
|
get_defaults_files(*argc, *argv,
|
||||||
@ -180,40 +180,6 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Simplified version of search_files (no argv, argc to process).
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
process_default_option_files()
|
|
||||||
conf_file Basename for configuration file to search for.
|
|
||||||
If this is a path, then only this file is read.
|
|
||||||
func Pointer to the function to process options
|
|
||||||
func_ctx It's context. Usually it is the structure to
|
|
||||||
store additional options.
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
|
|
||||||
Often we want only to get options from default config files. In this case we
|
|
||||||
don't want to provide any argc and argv parameters. This function is a
|
|
||||||
simplified variant of search_files which allows us to forget about
|
|
||||||
argc, argv.
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
0 ok
|
|
||||||
1 given cinf_file doesn't exist
|
|
||||||
*/
|
|
||||||
|
|
||||||
int process_default_option_files(const char *conf_file,
|
|
||||||
Process_option_func func, void *func_ctx)
|
|
||||||
{
|
|
||||||
int argc= 1;
|
|
||||||
/* this is a dummy variable for search_files() */
|
|
||||||
uint args_used;
|
|
||||||
|
|
||||||
return search_files(conf_file, &argc, NULL, &args_used, func, func_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The option handler for load_defaults.
|
The option handler for load_defaults.
|
||||||
|
|
||||||
@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups,
|
|||||||
ctx.args= &args;
|
ctx.args= &args;
|
||||||
ctx.group= &group;
|
ctx.group= &group;
|
||||||
|
|
||||||
error= search_files(conf_file, argc, argv, &args_used,
|
error= my_search_option_files(conf_file, argc, argv, &args_used,
|
||||||
handle_default_option, (void *) &ctx);
|
handle_default_option, (void *) &ctx);
|
||||||
/*
|
/*
|
||||||
Here error contains <> 0 only if we have a fully specified conf_file
|
Here error contains <> 0 only if we have a fully specified conf_file
|
||||||
|
@ -50,7 +50,6 @@ const char * NEAR globerrs[GLOBERRS]=
|
|||||||
"Can't sync file '%s' to disk (Errcode: %d)",
|
"Can't sync file '%s' to disk (Errcode: %d)",
|
||||||
"Collation '%s' is not a compiled collation and is not specified in the '%s' file",
|
"Collation '%s' is not a compiled collation and is not specified in the '%s' file",
|
||||||
"File '%s' not found (Errcode: %d)",
|
"File '%s' not found (Errcode: %d)",
|
||||||
""
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_glob_errs(void)
|
void init_glob_errs(void)
|
||||||
|
@ -145,6 +145,10 @@ void my_end(int infoflag)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_GETRUSAGE
|
#ifdef HAVE_GETRUSAGE
|
||||||
struct rusage rus;
|
struct rusage rus;
|
||||||
|
#ifdef HAVE_purify
|
||||||
|
/* Purify assumes that rus is uninitialized after getrusage call */
|
||||||
|
bzero((char*) &rus, sizeof(rus));
|
||||||
|
#endif
|
||||||
if (!getrusage(RUSAGE_SELF, &rus))
|
if (!getrusage(RUSAGE_SELF, &rus))
|
||||||
fprintf(info_file,"\n\
|
fprintf(info_file,"\n\
|
||||||
User time %.2f, System time %.2f\n\
|
User time %.2f, System time %.2f\n\
|
||||||
|
@ -85,9 +85,7 @@ int my_msync(int fd, void *addr, size_t len, int flags)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WINDOWS
|
#ifndef __WIN__
|
||||||
#pragma message "no mmap!"
|
|
||||||
#else
|
|
||||||
#warning "no mmap!"
|
#warning "no mmap!"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ testReadPerf_SOURCES = testReadPerf.cpp
|
|||||||
testLcp_SOURCES = testLcp.cpp
|
testLcp_SOURCES = testLcp.cpp
|
||||||
testPartitioning_SOURCES = testPartitioning.cpp
|
testPartitioning_SOURCES = testPartitioning.cpp
|
||||||
testBitfield_SOURCES = testBitfield.cpp
|
testBitfield_SOURCES = testBitfield.cpp
|
||||||
DbCreate_SOURCES= bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp
|
DbCreate_SOURCES = bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp bench/dbPopulate.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp
|
||||||
DbAsyncGenerator_SOURCES= bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp
|
DbAsyncGenerator_SOURCES = bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp bench/dbGenerator.h bench/macros.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp
|
||||||
|
|
||||||
INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel
|
INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel
|
||||||
|
|
||||||
|
@ -30,14 +30,13 @@ liboptions_a_CXXFLAGS= $(CXXFLAGS) \
|
|||||||
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
||||||
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
||||||
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
||||||
-DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \
|
-DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \
|
||||||
-DDEFAULT_USER="root" \
|
-DDEFAULT_MONITORING_INTERVAL="20" \
|
||||||
-DDEFAULT_PASSWORD="" \
|
|
||||||
-DDEFAULT_MONITORING_INTERVAL="5" \
|
|
||||||
-DDEFAULT_PORT="2273" \
|
-DDEFAULT_PORT="2273" \
|
||||||
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
||||||
|
|
||||||
liboptions_a_SOURCES= options.h options.cc priv.h priv.cc
|
liboptions_a_SOURCES= options.h options.cc priv.h priv.cc
|
||||||
|
liboptions_a_LIBADD= $(top_builddir)/libmysql/get_password.$(OBJEXT)
|
||||||
|
|
||||||
# MySQL sometimes uses symlinks to reuse code
|
# MySQL sometimes uses symlinks to reuse code
|
||||||
# All symlinked files are grouped in libnet.a
|
# All symlinked files are grouped in libnet.a
|
||||||
@ -59,7 +58,7 @@ client_settings.h: Makefile
|
|||||||
rm -f $(srcdir)/client_settings.h
|
rm -f $(srcdir)/client_settings.h
|
||||||
@LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h
|
@LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h
|
||||||
|
|
||||||
bin_PROGRAMS= mysqlmanager
|
libexec_PROGRAMS= mysqlmanager
|
||||||
|
|
||||||
mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
||||||
manager.h manager.cc log.h log.cc \
|
manager.h manager.cc log.h log.cc \
|
||||||
@ -75,7 +74,8 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \
|
|||||||
instance_options.h instance_options.cc \
|
instance_options.h instance_options.cc \
|
||||||
buffer.h buffer.cc parse.cc parse.h \
|
buffer.h buffer.cc parse.cc parse.h \
|
||||||
guardian.cc guardian.h \
|
guardian.cc guardian.h \
|
||||||
mysql_manager_error.h client_func.c
|
parse_output.cc parse_output.h \
|
||||||
|
mysql_manager_error.h
|
||||||
|
|
||||||
mysqlmanager_LDADD= liboptions.a \
|
mysqlmanager_LDADD= liboptions.a \
|
||||||
libnet.a \
|
libnet.a \
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - The buffer came to 16Mb barrier
|
1 - got an error in reserve()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int Buffer::append(uint position, const char *string, uint len_arg)
|
int Buffer::append(uint position, const char *string, uint len_arg)
|
||||||
@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg)
|
|||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - The buffer came to 16Mb barrier
|
1 - realloc error or we have come to the 16Mb barrier
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int Buffer::reserve(uint position, uint len_arg)
|
int Buffer::reserve(uint position, uint len_arg)
|
||||||
@ -79,19 +79,31 @@ int Buffer::reserve(uint position, uint len_arg)
|
|||||||
if (position + len_arg >= MAX_BUFFER_SIZE)
|
if (position + len_arg >= MAX_BUFFER_SIZE)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (position + len_arg>= buffer_size)
|
if (position + len_arg >= buffer_size)
|
||||||
{
|
{
|
||||||
buffer= (char *) realloc(buffer,
|
buffer= (char *) my_realloc(buffer,
|
||||||
min(MAX_BUFFER_SIZE,
|
min(MAX_BUFFER_SIZE,
|
||||||
max((uint) (buffer_size*1.5),
|
max((uint) (buffer_size*1.5),
|
||||||
position + len_arg)));
|
position + len_arg)), MYF(0));
|
||||||
if (buffer == NULL)
|
if (!(buffer))
|
||||||
goto err;
|
goto err;
|
||||||
buffer_size= (uint) (buffer_size*1.5);
|
buffer_size= (uint) (buffer_size*1.5);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
error= 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Buffer::get_size()
|
||||||
|
{
|
||||||
|
return buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Buffer::is_error()
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma interface
|
#pragma interface
|
||||||
@ -36,11 +37,17 @@ private:
|
|||||||
/* maximum buffer size is 16Mb */
|
/* maximum buffer size is 16Mb */
|
||||||
enum { MAX_BUFFER_SIZE= 16777216 };
|
enum { MAX_BUFFER_SIZE= 16777216 };
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
|
/* Error flag. Triggered if we get an error of some kind */
|
||||||
|
int error;
|
||||||
public:
|
public:
|
||||||
Buffer()
|
Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE)
|
||||||
|
:buffer_size(buffer_size_arg), error(0)
|
||||||
{
|
{
|
||||||
buffer=(char *) malloc(BUFFER_INITIAL_SIZE);
|
/*
|
||||||
buffer_size= BUFFER_INITIAL_SIZE;
|
As append() will invokes realloc() anyway, it's ok if malloc returns 0
|
||||||
|
*/
|
||||||
|
if (!(buffer= (char*) my_malloc(buffer_size, MYF(0))))
|
||||||
|
buffer_size= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
~Buffer()
|
~Buffer()
|
||||||
@ -50,6 +57,8 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
int get_size();
|
||||||
|
int is_error();
|
||||||
int append(uint position, const char *string, uint len_arg);
|
int append(uint position, const char *string, uint len_arg);
|
||||||
int reserve(uint position, uint len_arg);
|
int reserve(uint position, uint len_arg);
|
||||||
};
|
};
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#include <my_global.h>
|
|
||||||
#include <my_sys.h>
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
Currently we cannot use libmysqlclient directly becouse of the linking
|
|
||||||
issues. Here we provide needed libmysqlclient functions.
|
|
||||||
TODO: to think how to use libmysqlclient code instead of copy&paste.
|
|
||||||
The other possible solution is to use simple_command directly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const char * STDCALL
|
|
||||||
mysql_get_server_info(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
return((char*) mysql->server_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
int STDCALL
|
|
||||||
mysql_ping(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
DBUG_ENTER("mysql_ping");
|
|
||||||
DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0));
|
|
||||||
}
|
|
||||||
|
|
||||||
int STDCALL
|
|
||||||
mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
|
|
||||||
{
|
|
||||||
uchar level[1];
|
|
||||||
DBUG_ENTER("mysql_shutdown");
|
|
||||||
level[0]= (uchar) shutdown_level;
|
|
||||||
DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0));
|
|
||||||
}
|
|
@ -170,12 +170,12 @@ int Show_instance_status::do_command(struct st_net *net,
|
|||||||
Instance *instance;
|
Instance *instance;
|
||||||
|
|
||||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||||
if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL)
|
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||||
goto err;
|
goto err;
|
||||||
if (instance->is_running())
|
if (instance->is_running())
|
||||||
{
|
{
|
||||||
store_to_string(&send_buff, (char *) "online", &position);
|
store_to_string(&send_buff, (char *) "online", &position);
|
||||||
store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position);
|
store_to_string(&send_buff, "unknown", &position);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -184,7 +184,8 @@ int Show_instance_status::do_command(struct st_net *net,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +201,7 @@ err:
|
|||||||
|
|
||||||
int Show_instance_status::execute(struct st_net *net, ulong connection_id)
|
int Show_instance_status::execute(struct st_net *net, ulong connection_id)
|
||||||
{
|
{
|
||||||
if (instance_name != NULL)
|
if ((instance_name))
|
||||||
{
|
{
|
||||||
if (do_command(net, instance_name))
|
if (do_command(net, instance_name))
|
||||||
return ER_OUT_OF_RESOURCES;
|
return ER_OUT_OF_RESOURCES;
|
||||||
@ -256,52 +257,31 @@ int Show_instance_options::do_command(struct st_net *net,
|
|||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
|
|
||||||
if ((instance= instance_map->
|
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||||
find(instance_name, strlen(instance_name))) == NULL)
|
|
||||||
goto err;
|
goto err;
|
||||||
store_to_string(&send_buff, (char *) "instance_name", &position);
|
store_to_string(&send_buff, (char *) "instance_name", &position);
|
||||||
store_to_string(&send_buff, (char *) instance_name, &position);
|
store_to_string(&send_buff, (char *) instance_name, &position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
if (instance->options.mysqld_path != NULL)
|
if ((instance->options.mysqld_path))
|
||||||
{
|
{
|
||||||
position= 0;
|
position= 0;
|
||||||
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
||||||
store_to_string(&send_buff,
|
store_to_string(&send_buff,
|
||||||
(char *) instance->options.mysqld_path,
|
(char *) instance->options.mysqld_path,
|
||||||
&position);
|
&position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance->options.is_guarded != NULL)
|
if ((instance->options.nonguarded))
|
||||||
{
|
{
|
||||||
position= 0;
|
position= 0;
|
||||||
store_to_string(&send_buff, (char *) "guarded", &position);
|
store_to_string(&send_buff, (char *) "nonguarded", &position);
|
||||||
store_to_string(&send_buff, "", &position);
|
store_to_string(&send_buff, "", &position);
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
goto err;
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
}
|
|
||||||
|
|
||||||
if (instance->options.mysqld_user != NULL)
|
|
||||||
{
|
|
||||||
position= 0;
|
|
||||||
store_to_string(&send_buff, (char *) "admin-user", &position);
|
|
||||||
store_to_string(&send_buff,
|
|
||||||
(char *) instance->options.mysqld_user,
|
|
||||||
&position);
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance->options.mysqld_password != NULL)
|
|
||||||
{
|
|
||||||
position= 0;
|
|
||||||
store_to_string(&send_buff, (char *) "admin-password", &position);
|
|
||||||
store_to_string(&send_buff,
|
|
||||||
(char *) instance->options.mysqld_password,
|
|
||||||
&position);
|
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +298,8 @@ int Show_instance_options::do_command(struct st_net *net,
|
|||||||
store_to_string(&send_buff, option_value + 1, &position);
|
store_to_string(&send_buff, option_value + 1, &position);
|
||||||
/* join name and the value into the same option again */
|
/* join name and the value into the same option again */
|
||||||
*option_value= '=';
|
*option_value= '=';
|
||||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
if (send_buff.is_error() ||
|
||||||
|
my_net_write(net, send_buff.buffer, (uint) position))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +316,7 @@ err:
|
|||||||
|
|
||||||
int Show_instance_options::execute(struct st_net *net, ulong connection_id)
|
int Show_instance_options::execute(struct st_net *net, ulong connection_id)
|
||||||
{
|
{
|
||||||
if (instance_name != NULL)
|
if ((instance_name))
|
||||||
{
|
{
|
||||||
if (do_command(net, instance_name))
|
if (do_command(net, instance_name))
|
||||||
return ER_OUT_OF_RESOURCES;
|
return ER_OUT_OF_RESOURCES;
|
||||||
@ -369,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (err_code= instance->start())
|
if ((err_code= instance->start()))
|
||||||
return err_code;
|
return err_code;
|
||||||
|
|
||||||
if (instance->options.is_guarded != NULL)
|
if (!(instance->options.nonguarded))
|
||||||
instance_map->guardian->guard(instance);
|
instance_map->guardian->guard(instance);
|
||||||
|
|
||||||
net_send_ok(net, connection_id);
|
net_send_ok(net, connection_id);
|
||||||
@ -403,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (instance->options.is_guarded != NULL)
|
if (!(instance->options.nonguarded))
|
||||||
instance_map->guardian->
|
instance_map->guardian->
|
||||||
stop_guard(instance);
|
stop_guard(instance);
|
||||||
if ((err_code= instance->stop()))
|
if ((err_code= instance->stop()))
|
||||||
|
@ -21,9 +21,15 @@
|
|||||||
|
|
||||||
#include "guardian.h"
|
#include "guardian.h"
|
||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
|
#include "instance.h"
|
||||||
#include "mysql_manager_error.h"
|
#include "mysql_manager_error.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
@ -42,13 +48,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
|
|||||||
uint monitoring_interval_arg) :
|
uint monitoring_interval_arg) :
|
||||||
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
||||||
monitoring_interval_arg),
|
monitoring_interval_arg),
|
||||||
thread_info(pthread_self())
|
thread_info(pthread_self()), guarded_instances(0)
|
||||||
{
|
{
|
||||||
pthread_mutex_init(&LOCK_guardian, 0);
|
pthread_mutex_init(&LOCK_guardian, 0);
|
||||||
thread_registry.register_thread(&thread_info);
|
pthread_cond_init(&COND_guardian, 0);
|
||||||
|
shutdown_requested= FALSE;
|
||||||
|
stopped= FALSE;
|
||||||
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
guarded_instances= NULL;
|
|
||||||
starting_instances= NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -57,9 +63,114 @@ Guardian_thread::~Guardian_thread()
|
|||||||
/* delay guardian destruction to the moment when no one needs it */
|
/* delay guardian destruction to the moment when no one needs it */
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
free_root(&alloc, MYF(0));
|
free_root(&alloc, MYF(0));
|
||||||
thread_registry.unregister_thread(&thread_info);
|
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
pthread_mutex_destroy(&LOCK_guardian);
|
pthread_mutex_destroy(&LOCK_guardian);
|
||||||
|
pthread_cond_destroy(&COND_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Guardian_thread::request_shutdown(bool stop_instances_arg)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
/* stop instances or just clean up Guardian repository */
|
||||||
|
stop_instances(stop_instances_arg);
|
||||||
|
shutdown_requested= TRUE;
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Guardian_thread::process_instance(Instance *instance,
|
||||||
|
GUARD_NODE *current_node,
|
||||||
|
LIST **guarded_instances,
|
||||||
|
LIST *node)
|
||||||
|
{
|
||||||
|
uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY;
|
||||||
|
/* The amount of times, Guardian attempts to restart an instance */
|
||||||
|
int restart_retry= 100;
|
||||||
|
time_t current_time= time(NULL);
|
||||||
|
|
||||||
|
if (current_node->state == STOPPING)
|
||||||
|
{
|
||||||
|
/* this brach is executed during shutdown */
|
||||||
|
if (instance->options.shutdown_delay_val)
|
||||||
|
waitchild= instance->options.shutdown_delay_val;
|
||||||
|
|
||||||
|
/* this returns true if and only if an instance was stopped for sure */
|
||||||
|
if (instance->is_crashed())
|
||||||
|
*guarded_instances= list_delete(*guarded_instances, node);
|
||||||
|
else if ( (uint) (current_time - current_node->last_checked) > waitchild)
|
||||||
|
{
|
||||||
|
instance->kill_instance(SIGKILL);
|
||||||
|
/*
|
||||||
|
Later we do node= node->next. This is ok, as we are only removing
|
||||||
|
the node from the list. The pointer to the next one is still valid.
|
||||||
|
*/
|
||||||
|
*guarded_instances= list_delete(*guarded_instances, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance->is_running())
|
||||||
|
{
|
||||||
|
/* clear status fields */
|
||||||
|
current_node->restart_counter= 0;
|
||||||
|
current_node->crash_moment= 0;
|
||||||
|
current_node->state= STARTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (current_node->state)
|
||||||
|
{
|
||||||
|
case NOT_STARTED:
|
||||||
|
instance->start();
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
log_info("guardian: starting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
current_node->state= STARTING;
|
||||||
|
break;
|
||||||
|
case STARTED: /* fallthrough */
|
||||||
|
case STARTING: /* let the instance start or crash */
|
||||||
|
if (instance->is_crashed())
|
||||||
|
{
|
||||||
|
current_node->crash_moment= current_time;
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
current_node->state= JUST_CRASHED;
|
||||||
|
/* fallthrough -- restart an instance immediately */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
case JUST_CRASHED:
|
||||||
|
if (current_time - current_node->crash_moment <= 2)
|
||||||
|
{
|
||||||
|
instance->start();
|
||||||
|
log_info("guardian: starting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
}
|
||||||
|
else current_node->state= CRASHED;
|
||||||
|
break;
|
||||||
|
case CRASHED: /* just regular restarts */
|
||||||
|
if (current_time - current_node->last_checked >
|
||||||
|
monitoring_interval)
|
||||||
|
{
|
||||||
|
if ((current_node->restart_counter < restart_retry))
|
||||||
|
{
|
||||||
|
instance->start();
|
||||||
|
current_node->last_checked= current_time;
|
||||||
|
current_node->restart_counter++;
|
||||||
|
log_info("guardian: restarting instance %s",
|
||||||
|
instance->options.instance_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current_node->state= CRASHED_AND_ABANDONED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CRASHED_AND_ABANDONED:
|
||||||
|
break; /* do nothing */
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,109 +189,148 @@ Guardian_thread::~Guardian_thread()
|
|||||||
void Guardian_thread::run()
|
void Guardian_thread::run()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
LIST *loop;
|
LIST *node;
|
||||||
|
struct timespec timeout;
|
||||||
|
|
||||||
|
thread_registry.register_thread(&thread_info);
|
||||||
|
|
||||||
my_thread_init();
|
my_thread_init();
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
|
||||||
while (!thread_registry.is_shutdown())
|
/* loop, until all instances were shut down at the end */
|
||||||
|
while (!(shutdown_requested && (guarded_instances == NULL)))
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
node= guarded_instances;
|
||||||
loop= guarded_instances;
|
|
||||||
while (loop != NULL)
|
while (node != NULL)
|
||||||
{
|
{
|
||||||
instance= (Instance *) loop->data;
|
struct timespec timeout;
|
||||||
/* instance-> start already checks whether instance is running */
|
|
||||||
if (instance->start() != ER_INSTANCE_ALREADY_STARTED)
|
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||||
log_info("guardian attempted to restart instance %s",
|
instance= ((GUARD_NODE *) node->data)->instance;
|
||||||
instance->options.instance_name);
|
process_instance(instance, current_node, &guarded_instances, node);
|
||||||
loop= loop->next;
|
|
||||||
|
node= node->next;
|
||||||
}
|
}
|
||||||
move_to_list(&starting_instances, &guarded_instances);
|
timeout.tv_sec= time(NULL) + monitoring_interval;
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
timeout.tv_nsec= 0;
|
||||||
sleep(monitoring_interval);
|
|
||||||
|
/* check the loop predicate before sleeping */
|
||||||
|
if (!(shutdown_requested && (!(guarded_instances))))
|
||||||
|
pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopped= TRUE;
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
/* now, when the Guardian is stopped we can stop the IM */
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
thread_registry.request_shutdown();
|
||||||
my_thread_end();
|
my_thread_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Guardian_thread::start()
|
int Guardian_thread::is_stopped()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
int var;
|
||||||
Instance_map::Iterator iterator(instance_map);
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
var= stopped;
|
||||||
instance_map->lock();
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
while ((instance= iterator.next()))
|
return var;
|
||||||
{
|
|
||||||
if ((instance->options.is_guarded != NULL) && (instance->is_running()))
|
|
||||||
if (guard(instance))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
instance_map->unlock();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start instance guarding
|
Initialize the list of guarded instances: loop through the Instance_map and
|
||||||
|
add all of the instances, which don't have 'nonguarded' option specified.
|
||||||
|
|
||||||
SYNOPSYS
|
SYNOPSYS
|
||||||
guard()
|
Guardian_thread::init()
|
||||||
instance the instance to be guarded
|
|
||||||
|
|
||||||
DESCRIPTION
|
NOTE: One should always lock guardian before calling this routine.
|
||||||
|
|
||||||
The instance is added to the list of starting instances. Then after one guardian
|
|
||||||
loop it is moved to the guarded instances list. Usually guard() is called after we
|
|
||||||
start an instance, so we need to give some time to the instance to start.
|
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
0 - ok
|
0 - ok
|
||||||
1 - error occured
|
1 - error occured
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::init()
|
||||||
int Guardian_thread::guard(Instance *instance)
|
|
||||||
{
|
{
|
||||||
return add_instance_to_list(instance, &starting_instances);
|
Instance *instance;
|
||||||
}
|
Instance_map::Iterator iterator(instance_map);
|
||||||
|
|
||||||
|
instance_map->lock();
|
||||||
|
/* clear the list of guarded instances */
|
||||||
|
free_root(&alloc, MYF(0));
|
||||||
|
init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
|
guarded_instances= NULL;
|
||||||
|
|
||||||
void Guardian_thread::move_to_list(LIST **from, LIST **to)
|
while ((instance= iterator.next()))
|
||||||
{
|
|
||||||
LIST *tmp;
|
|
||||||
|
|
||||||
while (*from)
|
|
||||||
{
|
{
|
||||||
tmp= rest(*from);
|
if (!(instance->options.nonguarded))
|
||||||
*to= list_add(*to, *from);
|
if (guard(instance, TRUE)) /* do not lock guardian */
|
||||||
*from= tmp;
|
{
|
||||||
|
instance_map->unlock();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance_map->unlock();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list)
|
/*
|
||||||
|
Add instance to the Guardian list
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
guard()
|
||||||
|
instance the instance to be guarded
|
||||||
|
nolock whether we prefer do not lock Guardian here,
|
||||||
|
but use external locking instead
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
The instance is added to the guarded instances list. Usually guard() is
|
||||||
|
called after we start an instance.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::guard(Instance *instance, bool nolock)
|
||||||
{
|
{
|
||||||
LIST *node;
|
LIST *node;
|
||||||
|
GUARD_NODE *content;
|
||||||
|
|
||||||
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
||||||
if (node == NULL)
|
content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE));
|
||||||
|
|
||||||
|
if ((!(node)) || (!(content)))
|
||||||
return 1;
|
return 1;
|
||||||
/* we store the pointers to instances from the instance_map's MEM_ROOT */
|
/* we store the pointers to instances from the instance_map's MEM_ROOT */
|
||||||
node->data= (void *) instance;
|
content->instance= instance;
|
||||||
|
content->restart_counter= 0;
|
||||||
|
content->crash_moment= 0;
|
||||||
|
content->state= NOT_STARTED;
|
||||||
|
node->data= (void *) content;
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_guardian);
|
if (nolock)
|
||||||
*list= list_add(*list, node);
|
guarded_instances= list_add(guarded_instances, node);
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
else
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
guarded_instances= list_add(guarded_instances, node);
|
||||||
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: perhaps it would make sense to create a pool of the LIST elements
|
TODO: perhaps it would make sense to create a pool of the LIST nodeents
|
||||||
elements and give them upon request. Now we are loosing a bit of memory when
|
and give them upon request. Now we are loosing a bit of memory when
|
||||||
guarded instance was stopped and then restarted (since we cannot free just
|
guarded instance was stopped and then restarted (since we cannot free just
|
||||||
a piece of the MEM_ROOT).
|
a piece of the MEM_ROOT).
|
||||||
*/
|
*/
|
||||||
@ -198,7 +348,7 @@ int Guardian_thread::stop_guard(Instance *instance)
|
|||||||
We compare only pointers, as we always use pointers from the
|
We compare only pointers, as we always use pointers from the
|
||||||
instance_map's MEM_ROOT.
|
instance_map's MEM_ROOT.
|
||||||
*/
|
*/
|
||||||
if ((Instance *) node->data == instance)
|
if (((GUARD_NODE *) node->data)->instance == instance)
|
||||||
{
|
{
|
||||||
guarded_instances= list_delete(guarded_instances, node);
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
pthread_mutex_unlock(&LOCK_guardian);
|
pthread_mutex_unlock(&LOCK_guardian);
|
||||||
@ -212,3 +362,73 @@ int Guardian_thread::stop_guard(Instance *instance)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start Guardian shutdown. Attempt to start instances if requested.
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
stop_instances()
|
||||||
|
stop_instances_arg whether we should stop instances at shutdown
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Loops through the guarded_instances list and prepares them for shutdown.
|
||||||
|
If stop_instances was requested, we need to issue a stop command and change
|
||||||
|
the state accordingly. Otherwise we could simply delete an entry.
|
||||||
|
NOTE: Guardian should be locked by the calling function
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Guardian_thread::stop_instances(bool stop_instances_arg)
|
||||||
|
{
|
||||||
|
LIST *node;
|
||||||
|
node= guarded_instances;
|
||||||
|
while (node != NULL)
|
||||||
|
{
|
||||||
|
if (!stop_instances_arg)
|
||||||
|
{
|
||||||
|
/* just forget about an instance */
|
||||||
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
|
/*
|
||||||
|
This should still work fine, as we have only removed the
|
||||||
|
node from the list. The pointer to the next one is still valid
|
||||||
|
*/
|
||||||
|
node= node->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||||
|
/*
|
||||||
|
If instance is running or was running (and now probably hanging),
|
||||||
|
request stop.
|
||||||
|
*/
|
||||||
|
if (current_node->instance->is_running() ||
|
||||||
|
(current_node->state == STARTED))
|
||||||
|
{
|
||||||
|
current_node->state= STOPPING;
|
||||||
|
current_node->last_checked= time(NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* otherwise remove it from the list */
|
||||||
|
guarded_instances= list_delete(guarded_instances, node);
|
||||||
|
/* But try to kill it anyway. Just in case */
|
||||||
|
current_node->instance->kill_instance(SIGTERM);
|
||||||
|
node= node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Guardian_thread::lock()
|
||||||
|
{
|
||||||
|
return pthread_mutex_lock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Guardian_thread::unlock()
|
||||||
|
{
|
||||||
|
return pthread_mutex_unlock(&LOCK_guardian);
|
||||||
|
}
|
||||||
|
@ -19,15 +19,16 @@
|
|||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <my_list.h>
|
#include <my_list.h>
|
||||||
|
#include "thread_registry.h"
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Instance;
|
||||||
class Instance_map;
|
class Instance_map;
|
||||||
|
class Thread_registry;
|
||||||
#include "thread_registry.h"
|
struct GUARD_NODE;
|
||||||
#include "instance.h"
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
@ -35,12 +36,11 @@ pthread_handler_decl(guardian, arg);
|
|||||||
|
|
||||||
C_MODE_END
|
C_MODE_END
|
||||||
|
|
||||||
|
|
||||||
struct Guardian_thread_args
|
struct Guardian_thread_args
|
||||||
{
|
{
|
||||||
Thread_registry &thread_registry;
|
Thread_registry &thread_registry;
|
||||||
Instance_map *instance_map;
|
Instance_map *instance_map;
|
||||||
uint monitoring_interval;
|
int monitoring_interval;
|
||||||
|
|
||||||
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
||||||
Instance_map *instance_map_arg,
|
Instance_map *instance_map_arg,
|
||||||
@ -60,27 +60,67 @@ struct Guardian_thread_args
|
|||||||
class Guardian_thread: public Guardian_thread_args
|
class Guardian_thread: public Guardian_thread_args
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* states of an instance */
|
||||||
|
enum INSTANCE_STATE { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED,
|
||||||
|
CRASHED, CRASHED_AND_ABANDONED, STOPPING };
|
||||||
|
|
||||||
|
/*
|
||||||
|
The Guardian list node structure. Guardian utilizes it to store
|
||||||
|
guarded instances plus some additional info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct GUARD_NODE
|
||||||
|
{
|
||||||
|
Instance *instance;
|
||||||
|
/* state of an instance (i.e. STARTED, CRASHED, etc.) */
|
||||||
|
INSTANCE_STATE state;
|
||||||
|
/* the amount of attemts to restart instance (cleaned up at success) */
|
||||||
|
int restart_counter;
|
||||||
|
/* triggered at a crash */
|
||||||
|
time_t crash_moment;
|
||||||
|
/* General time field. Used to provide timeouts (at shutdown and restart) */
|
||||||
|
time_t last_checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Guardian_thread(Thread_registry &thread_registry_arg,
|
Guardian_thread(Thread_registry &thread_registry_arg,
|
||||||
Instance_map *instance_map_arg,
|
Instance_map *instance_map_arg,
|
||||||
uint monitoring_interval_arg);
|
uint monitoring_interval_arg);
|
||||||
~Guardian_thread();
|
~Guardian_thread();
|
||||||
|
/* Main funtion of the thread */
|
||||||
void run();
|
void run();
|
||||||
|
/* Initialize or refresh the list of guarded instances */
|
||||||
int init();
|
int init();
|
||||||
int start();
|
/* Request guardian shutdown. Stop instances if needed */
|
||||||
int guard(Instance *instance);
|
void request_shutdown(bool stop_instances);
|
||||||
|
/* Start instance protection */
|
||||||
|
int guard(Instance *instance, bool nolock= FALSE);
|
||||||
|
/* Stop instance protection */
|
||||||
int stop_guard(Instance *instance);
|
int stop_guard(Instance *instance);
|
||||||
|
/* Returns true if guardian thread is stopped */
|
||||||
|
int is_stopped();
|
||||||
|
int lock();
|
||||||
|
int unlock();
|
||||||
|
|
||||||
|
public:
|
||||||
|
pthread_cond_t COND_guardian;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int add_instance_to_list(Instance *instance, LIST **list);
|
/* Prepares Guardian shutdown. Stops instances is needed */
|
||||||
void move_to_list(LIST **from, LIST **to);
|
int stop_instances(bool stop_instances_arg);
|
||||||
|
/* check instance state and act accordingly */
|
||||||
|
void process_instance(Instance *instance, GUARD_NODE *current_node,
|
||||||
|
LIST **guarded_instances, LIST *elem);
|
||||||
|
int stopped;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t LOCK_guardian;
|
pthread_mutex_t LOCK_guardian;
|
||||||
Thread_info thread_info;
|
Thread_info thread_info;
|
||||||
LIST *guarded_instances;
|
LIST *guarded_instances;
|
||||||
LIST *starting_instances;
|
|
||||||
MEM_ROOT alloc;
|
MEM_ROOT alloc;
|
||||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||||
|
/* this variable is set to TRUE when we want to stop Guardian thread */
|
||||||
|
bool shutdown_requested;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */
|
||||||
|
@ -21,11 +21,34 @@
|
|||||||
#include "instance.h"
|
#include "instance.h"
|
||||||
#include "mysql_manager_error.h"
|
#include "mysql_manager_error.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "instance_map.h"
|
||||||
|
#include "priv.h"
|
||||||
|
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <m_string.h>
|
#include <m_string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
|
C_MODE_START
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proxy thread is a simple way to avoid all pitfalls of the threads
|
||||||
|
implementation in the OS (e.g. LinuxThreads). With such a thread we
|
||||||
|
don't have to process SIGCHLD, which is a tricky business if we want
|
||||||
|
to do it in a portable way.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pthread_handler_decl(proxy, arg)
|
||||||
|
{
|
||||||
|
Instance *instance= (Instance *) arg;
|
||||||
|
instance->fork_and_monitor();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
C_MODE_END
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The method starts an instance.
|
The method starts an instance.
|
||||||
|
|
||||||
@ -43,85 +66,175 @@ int Instance::start()
|
|||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
|
/* clear crash flag */
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
crashed= 0;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
|
||||||
if (!is_running())
|
if (!is_running())
|
||||||
{
|
{
|
||||||
log_info("trying to start instance %s", options.instance_name);
|
if ((pid= options.get_pid()) != 0) /* check the pidfile */
|
||||||
switch (pid= fork()) {
|
if (options.unlink_pidfile()) /* remove stalled pidfile */
|
||||||
case 0:
|
log_error("cannot remove pidfile for instance %i, this might be \
|
||||||
if (fork()) /* zombie protection */
|
since IM lacks permmissions or hasn't found the pidifle",
|
||||||
exit(0); /* parent goes bye-bye */
|
options.instance_name);
|
||||||
else
|
|
||||||
{
|
/*
|
||||||
execv(options.mysqld_path, options.argv);
|
No need to monitor this thread in the Thread_registry, as all
|
||||||
exit(1);
|
instances are to be stopped during shutdown.
|
||||||
}
|
*/
|
||||||
case -1:
|
pthread_t proxy_thd_id;
|
||||||
|
pthread_attr_t proxy_thd_attr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
pthread_attr_init(&proxy_thd_attr);
|
||||||
|
pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy,
|
||||||
|
this);
|
||||||
|
pthread_attr_destroy(&proxy_thd_attr);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
log_error("Instance::start(): pthread_create(proxy) failed");
|
||||||
return ER_CANNOT_START_INSTANCE;
|
return ER_CANNOT_START_INSTANCE;
|
||||||
default:
|
|
||||||
waitpid(pid, NULL, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the instance is started already */
|
/* the instance is started already */
|
||||||
return ER_INSTANCE_ALREADY_STARTED;
|
return ER_INSTANCE_ALREADY_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Instance::cleanup()
|
|
||||||
|
void Instance::fork_and_monitor()
|
||||||
{
|
{
|
||||||
/*
|
pid_t pid;
|
||||||
We cannot close connection in destructor, as mysql_close needs alarm
|
log_info("starting instance %s", options.instance_name);
|
||||||
services which are definitely unavailaible at the time of destructor
|
switch (pid= fork()) {
|
||||||
call.
|
case 0:
|
||||||
*/
|
execv(options.mysqld_path, options.argv);
|
||||||
if (is_connected)
|
/* exec never returns */
|
||||||
mysql_close(&mysql);
|
exit(1);
|
||||||
return 0;
|
case -1:
|
||||||
|
log_info("cannot fork() to start instance %s", options.instance_name);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
Here we wait for the child created. This process differs for systems
|
||||||
|
running LinuxThreads and POSIX Threads compliant systems. This is because
|
||||||
|
according to POSIX we could wait() for a child in any thread of the
|
||||||
|
process. While LinuxThreads require that wait() is called by the thread,
|
||||||
|
which created the child.
|
||||||
|
On the other hand we could not expect mysqld to return the pid, we
|
||||||
|
got in from fork(), to wait4() fucntion when running on LinuxThreads.
|
||||||
|
This is because MySQL shutdown thread is not the one, which was created
|
||||||
|
by our fork() call.
|
||||||
|
So basically we have two options: whether the wait() call returns only in
|
||||||
|
the creator thread, but we cannot use waitpid() since we have no idea
|
||||||
|
which pid we should wait for (in fact it should be the pid of shutdown
|
||||||
|
thread, but we don't know this one). Or we could use waitpid(), but
|
||||||
|
couldn't use wait(), because it could return in any wait() in the program.
|
||||||
|
*/
|
||||||
|
if (linuxthreads)
|
||||||
|
wait(NULL); /* LinuxThreads were detected */
|
||||||
|
else
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
/* set instance state to crashed */
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
crashed= 1;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Wake connection threads waiting for an instance to stop. This
|
||||||
|
is needed if a user issued command to stop an instance via
|
||||||
|
mysql connection. This is not the case if Guardian stop the thread.
|
||||||
|
*/
|
||||||
|
pthread_cond_signal(&COND_instance_stopped);
|
||||||
|
/* wake guardian */
|
||||||
|
pthread_cond_signal(&instance_map->guardian->COND_guardian);
|
||||||
|
/* thread exits */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* we should never end up here */
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Instance::Instance(): crashed(0)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&LOCK_instance, 0);
|
||||||
|
pthread_cond_init(&COND_instance_stopped, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Instance::~Instance()
|
Instance::~Instance()
|
||||||
{
|
{
|
||||||
|
pthread_cond_destroy(&COND_instance_stopped);
|
||||||
pthread_mutex_destroy(&LOCK_instance);
|
pthread_mutex_destroy(&LOCK_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance::is_crashed()
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
|
val= crashed;
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Instance::is_running()
|
bool Instance::is_running()
|
||||||
{
|
{
|
||||||
|
MYSQL mysql;
|
||||||
uint port= 0;
|
uint port= 0;
|
||||||
const char *socket= NULL;
|
const char *socket= NULL;
|
||||||
|
const char *password= "321rarepassword213";
|
||||||
|
const char *username= "645rareusername945";
|
||||||
|
const char *access_denied_message= "Access denied for user";
|
||||||
|
bool return_val;
|
||||||
|
|
||||||
if (options.mysqld_port)
|
if (options.mysqld_port)
|
||||||
port= atoi(strchr(options.mysqld_port, '=') + 1);
|
port= options.mysqld_port_val;
|
||||||
|
|
||||||
if (options.mysqld_socket)
|
if (options.mysqld_socket)
|
||||||
socket= strchr(options.mysqld_socket, '=') + 1;
|
socket= strchr(options.mysqld_socket, '=') + 1;
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_instance);
|
pthread_mutex_lock(&LOCK_instance);
|
||||||
if (!is_connected)
|
|
||||||
|
mysql_init(&mysql);
|
||||||
|
/* try to connect to a server with a fake username/password pair */
|
||||||
|
if (mysql_real_connect(&mysql, LOCAL_HOST, username,
|
||||||
|
password,
|
||||||
|
NullS, port,
|
||||||
|
socket, 0))
|
||||||
{
|
{
|
||||||
mysql_init(&mysql);
|
/*
|
||||||
if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user,
|
We have successfully connected to the server using fake
|
||||||
options.mysqld_password,
|
username/password. Write a warning to the logfile.
|
||||||
NullS, port,
|
*/
|
||||||
socket, 0))
|
log_info("The Instance Manager was able to log into you server \
|
||||||
|
with faked compiled-in password while checking server status. \
|
||||||
|
Looks like something is wrong.");
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
return_val= TRUE; /* server is alive */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!strncmp(access_denied_message, mysql_error(&mysql),
|
||||||
|
sizeof(access_denied_message)-1))
|
||||||
{
|
{
|
||||||
mysql.reconnect= 1;
|
return_val= TRUE;
|
||||||
is_connected= TRUE;
|
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
mysql_close(&mysql);
|
else
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
return_val= FALSE;
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
else if (!mysql_ping(&mysql))
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mysql_close(&mysql);
|
||||||
pthread_mutex_unlock(&LOCK_instance);
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
return FALSE;
|
|
||||||
|
return return_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,22 +252,67 @@ bool Instance::is_running()
|
|||||||
|
|
||||||
int Instance::stop()
|
int Instance::stop()
|
||||||
{
|
{
|
||||||
if (is_running())
|
pid_t pid;
|
||||||
{
|
struct timespec timeout;
|
||||||
if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
|
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
|
||||||
goto err;
|
|
||||||
|
|
||||||
mysql_close(&mysql);
|
if (options.shutdown_delay_val)
|
||||||
is_connected= FALSE;
|
waitchild= options.shutdown_delay_val;
|
||||||
return 0;
|
|
||||||
|
kill_instance(SIGTERM);
|
||||||
|
/* sleep on condition to wait for SIGCHLD */
|
||||||
|
|
||||||
|
timeout.tv_sec= time(NULL) + waitchild;
|
||||||
|
timeout.tv_nsec= 0;
|
||||||
|
if (pthread_mutex_lock(&LOCK_instance))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
while (options.get_pid() != 0) /* while server isn't stopped */
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status= pthread_cond_timedwait(&COND_instance_stopped,
|
||||||
|
&LOCK_instance,
|
||||||
|
&timeout);
|
||||||
|
if (status == ETIMEDOUT)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&LOCK_instance);
|
||||||
|
|
||||||
|
kill_instance(SIGKILL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
return ER_INSTANCE_IS_NOT_STARTED;
|
return ER_INSTANCE_IS_NOT_STARTED;
|
||||||
err:
|
err:
|
||||||
return ER_STOP_INSTANCE;
|
return ER_STOP_INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Instance::kill_instance(int signum)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
/* if there are no pid, everything seems to be fine */
|
||||||
|
if ((pid= options.get_pid()) != 0) /* get pid from pidfile */
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If we cannot kill mysqld, then it has propably crashed.
|
||||||
|
Let us try to remove staled pidfile and return successfully
|
||||||
|
as mysqld is probably stopped.
|
||||||
|
*/
|
||||||
|
if (!kill(pid, signum))
|
||||||
|
options.unlink_pidfile();
|
||||||
|
else
|
||||||
|
if (signum == SIGKILL) /* really killed instance with SIGKILL */
|
||||||
|
log_error("The instance %s is being stopped forsibly. Normally \
|
||||||
|
it should not happed. Probably the instance has been \
|
||||||
|
hanging. You should also check your IM setup",
|
||||||
|
options.instance_name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We execute this function to initialize instance parameters.
|
We execute this function to initialize instance parameters.
|
||||||
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
||||||
@ -162,7 +320,14 @@ err:
|
|||||||
|
|
||||||
int Instance::init(const char *name_arg)
|
int Instance::init(const char *name_arg)
|
||||||
{
|
{
|
||||||
pthread_mutex_init(&LOCK_instance, 0);
|
|
||||||
|
|
||||||
return options.init(name_arg);
|
return options.init(name_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance::complete_initialization(Instance_map *instance_map_arg,
|
||||||
|
const char *mysqld_path,
|
||||||
|
int only_instance)
|
||||||
|
{
|
||||||
|
instance_map= instance_map_arg;
|
||||||
|
return options.complete_initialization(mysqld_path, only_instance);
|
||||||
|
}
|
||||||
|
@ -25,36 +25,44 @@
|
|||||||
#pragma interface
|
#pragma interface
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Instance_map;
|
||||||
|
|
||||||
class Instance
|
class Instance
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Instance(): is_connected(FALSE)
|
Instance();
|
||||||
{}
|
|
||||||
~Instance();
|
~Instance();
|
||||||
|
|
||||||
int init(const char *name);
|
int init(const char *name);
|
||||||
|
int complete_initialization(Instance_map *instance_map_arg,
|
||||||
|
const char *mysqld_path, int only_instance= 0);
|
||||||
|
|
||||||
/* check if the instance is running and set up mysql connection if yes */
|
|
||||||
bool is_running();
|
bool is_running();
|
||||||
int start();
|
int start();
|
||||||
int stop();
|
int stop();
|
||||||
int cleanup();
|
/* send a signal to the instance */
|
||||||
|
void kill_instance(int signo);
|
||||||
|
int is_crashed();
|
||||||
|
void fork_and_monitor();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum { DEFAULT_SHUTDOWN_DELAY= 35 };
|
||||||
Instance_options options;
|
Instance_options options;
|
||||||
|
|
||||||
/* connection to the instance */
|
|
||||||
MYSQL mysql;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
Mutex protecting the instance. Currently we use it to avoid the
|
Mutex protecting the instance. Currently we use it to avoid the
|
||||||
double start of the instance. This happens when the instance is starting
|
double start of the instance. This happens when the instance is starting
|
||||||
and we issue the start command once more.
|
and we issue the start command once more.
|
||||||
*/
|
*/
|
||||||
|
int crashed;
|
||||||
pthread_mutex_t LOCK_instance;
|
pthread_mutex_t LOCK_instance;
|
||||||
/* Here we store the state of the following connection */
|
/*
|
||||||
bool is_connected;
|
This condition variable is used to wake threads waiting for instance to
|
||||||
|
stop in Instance::stop()
|
||||||
|
*/
|
||||||
|
pthread_cond_t COND_instance_stopped;
|
||||||
|
Instance_map *instance_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */
|
||||||
|
@ -74,7 +74,7 @@ static void delete_instance(void *u)
|
|||||||
1 - error occured
|
1 - error occured
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int process_option(void * ctx, const char *group, const char *option)
|
static int process_option(void *ctx, const char *group, const char *option)
|
||||||
{
|
{
|
||||||
Instance_map *map= NULL;
|
Instance_map *map= NULL;
|
||||||
Instance *instance= NULL;
|
Instance *instance= NULL;
|
||||||
@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option)
|
|||||||
|
|
||||||
map = (Instance_map*) ctx;
|
map = (Instance_map*) ctx;
|
||||||
if (strncmp(group, prefix, sizeof prefix) == 0 &&
|
if (strncmp(group, prefix, sizeof prefix) == 0 &&
|
||||||
(my_isdigit(default_charset_info, group[sizeof prefix])))
|
((my_isdigit(default_charset_info, group[sizeof prefix]))
|
||||||
|
|| group[sizeof(prefix)] == '\0'))
|
||||||
{
|
{
|
||||||
if ((instance= map->find(group, strlen(group))) == NULL)
|
if ((instance= map->find(group, strlen(group))) == NULL)
|
||||||
{
|
{
|
||||||
@ -110,13 +111,9 @@ C_MODE_END
|
|||||||
|
|
||||||
|
|
||||||
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
||||||
const char *default_admin_user_arg,
|
const char *first_option_arg):
|
||||||
const char *default_admin_password_arg)
|
mysqld_path(default_mysqld_path_arg), first_option(first_option_arg)
|
||||||
{
|
{
|
||||||
mysqld_path= default_mysqld_path_arg;
|
|
||||||
user= default_admin_user_arg;
|
|
||||||
password= default_admin_password_arg;
|
|
||||||
|
|
||||||
pthread_mutex_init(&LOCK_instance_map, 0);
|
pthread_mutex_init(&LOCK_instance_map, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,12 +151,15 @@ int Instance_map::flush_instances()
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
guardian->lock();
|
||||||
pthread_mutex_lock(&LOCK_instance_map);
|
pthread_mutex_lock(&LOCK_instance_map);
|
||||||
hash_free(&hash);
|
hash_free(&hash);
|
||||||
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||||
get_instance_key, delete_instance, 0);
|
get_instance_key, delete_instance, 0);
|
||||||
pthread_mutex_unlock(&LOCK_instance_map);
|
pthread_mutex_unlock(&LOCK_instance_map);
|
||||||
rc= load();
|
rc= load();
|
||||||
|
guardian->init();
|
||||||
|
guardian->unlock();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,48 +181,44 @@ Instance_map::find(const char *name, uint name_len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Instance_map::complete_initialization()
|
int Instance_map::complete_initialization()
|
||||||
{
|
{
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
uint i= 0;
|
uint i= 0;
|
||||||
|
|
||||||
while (i < hash.records)
|
|
||||||
|
if (hash.records == 0) /* no instances found */
|
||||||
{
|
{
|
||||||
instance= (Instance *) hash_element(&hash, i);
|
if ((instance= new Instance) == 0)
|
||||||
instance->options.complete_initialization(mysqld_path, user, password);
|
goto err;
|
||||||
i++;
|
|
||||||
|
if (instance->init("mysqld") || add_instance(instance))
|
||||||
|
goto err_instance;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
After an instance have been added to the instance_map,
|
||||||
|
hash_free should handle it's deletion => goto err, not
|
||||||
|
err_instance.
|
||||||
|
*/
|
||||||
|
if (instance->complete_initialization(this, mysqld_path, 1))
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
while (i < hash.records)
|
||||||
|
{
|
||||||
|
instance= (Instance *) hash_element(&hash, i);
|
||||||
|
if (instance->complete_initialization(this, mysqld_path))
|
||||||
|
goto err;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
int Instance_map::cleanup()
|
err:
|
||||||
{
|
return 1;
|
||||||
Instance *instance;
|
err_instance:
|
||||||
uint i= 0;
|
delete instance;
|
||||||
|
return 1;
|
||||||
while (i < hash.records)
|
|
||||||
{
|
|
||||||
instance= (Instance *) hash_element(&hash, i);
|
|
||||||
if (instance->cleanup())
|
|
||||||
return 1;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Instance *
|
|
||||||
Instance_map::find(uint instance_number)
|
|
||||||
{
|
|
||||||
Instance *instance;
|
|
||||||
char name[80];
|
|
||||||
|
|
||||||
sprintf(name, "mysqld%i", instance_number);
|
|
||||||
pthread_mutex_lock(&LOCK_instance_map);
|
|
||||||
instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name));
|
|
||||||
pthread_mutex_unlock(&LOCK_instance_map);
|
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -230,13 +226,30 @@ Instance_map::find(uint instance_number)
|
|||||||
|
|
||||||
int Instance_map::load()
|
int Instance_map::load()
|
||||||
{
|
{
|
||||||
int error;
|
int argc= 1;
|
||||||
|
/* this is a dummy variable for search_option_files() */
|
||||||
|
uint args_used= 0;
|
||||||
|
const char *argv_options[3];
|
||||||
|
char **argv= (char **) &argv_options;
|
||||||
|
|
||||||
error= process_default_option_files("my", process_option, (void *) this);
|
|
||||||
|
|
||||||
complete_initialization();
|
/* the name of the program may be orbitrary here in fact */
|
||||||
|
argv_options[0]= "mysqlmanager";
|
||||||
|
if (first_option != NULL)
|
||||||
|
{
|
||||||
|
argc= 2;
|
||||||
|
argv_options[1]= first_option;
|
||||||
|
argv_options[2]= '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
argv_options[1]= '\0';
|
||||||
|
|
||||||
return error;
|
if (my_search_option_files("my", &argc, (char ***) &argv, &args_used,
|
||||||
|
process_option, (void *) this) ||
|
||||||
|
complete_initialization())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,17 +57,14 @@ public:
|
|||||||
public:
|
public:
|
||||||
/* returns a pointer to the instance or NULL, if there is no such instance */
|
/* returns a pointer to the instance or NULL, if there is no such instance */
|
||||||
Instance *find(const char *name, uint name_len);
|
Instance *find(const char *name, uint name_len);
|
||||||
Instance *find(uint instance_number);
|
|
||||||
|
|
||||||
int flush_instances();
|
int flush_instances();
|
||||||
int cleanup();
|
|
||||||
int lock();
|
int lock();
|
||||||
int unlock();
|
int unlock();
|
||||||
int init();
|
int init();
|
||||||
|
|
||||||
Instance_map(const char *default_mysqld_path_arg,
|
Instance_map(const char *default_mysqld_path_arg,
|
||||||
const char *default_admin_user_arg,
|
const char *first_option_arg);
|
||||||
const char *default_admin_password_arg);
|
|
||||||
~Instance_map();
|
~Instance_map();
|
||||||
|
|
||||||
/* loads options from config files */
|
/* loads options from config files */
|
||||||
@ -75,16 +72,14 @@ public:
|
|||||||
/* adds instance to internal hash */
|
/* adds instance to internal hash */
|
||||||
int add_instance(Instance *instance);
|
int add_instance(Instance *instance);
|
||||||
/* inits instances argv's after all options have been loaded */
|
/* inits instances argv's after all options have been loaded */
|
||||||
void complete_initialization();
|
int complete_initialization();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char *mysqld_path;
|
const char *mysqld_path;
|
||||||
/* user an password to shutdown MySQL */
|
|
||||||
const char *user;
|
|
||||||
const char *password;
|
|
||||||
Guardian_thread *guardian;
|
Guardian_thread *guardian;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const char *first_option;
|
||||||
enum { START_HASH_SIZE = 16 };
|
enum { START_HASH_SIZE = 16 };
|
||||||
pthread_mutex_t LOCK_instance_map;
|
pthread_mutex_t LOCK_instance_map;
|
||||||
HASH hash;
|
HASH hash;
|
||||||
|
@ -19,43 +19,170 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "instance_options.h"
|
#include "instance_options.h"
|
||||||
|
#include "parse_output.h"
|
||||||
|
#include "buffer.h"
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <m_string.h>
|
#include <m_string.h>
|
||||||
|
|
||||||
int Instance_options::complete_initialization(const char *default_path,
|
|
||||||
const char *default_user,
|
/*
|
||||||
const char *default_password)
|
Get compiled-in value of default_option
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
get_default_option()
|
||||||
|
result buffer to put found value
|
||||||
|
result_len buffer size
|
||||||
|
oprion_name the name of the option, prefixed with "--"
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Get compile-in value of requested option from server
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int Instance_options::get_default_option(char *result, size_t result_len,
|
||||||
|
const char *option_name)
|
||||||
{
|
{
|
||||||
/* we need to reserve space for the final zero + possible default options */
|
int position= 0;
|
||||||
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
int rc= 1;
|
||||||
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
char verbose_option[]= " --no-defaults --verbose --help";
|
||||||
goto err;
|
|
||||||
|
Buffer cmd(strlen(mysqld_path)+sizeof(verbose_option)+1);
|
||||||
|
if (cmd.get_size()) /* malloc succeeded */
|
||||||
|
{
|
||||||
|
cmd.append(position, mysqld_path, strlen(mysqld_path));
|
||||||
|
position+= strlen(mysqld_path);
|
||||||
|
cmd.append(position, verbose_option, sizeof(verbose_option) - 1);
|
||||||
|
position+= sizeof(verbose_option) - 1;
|
||||||
|
cmd.append(position, "\0", 1);
|
||||||
|
|
||||||
|
if (cmd.is_error())
|
||||||
|
goto err;
|
||||||
|
/* get the value from "mysqld --help --verbose" */
|
||||||
|
rc= parse_output_and_get_value(cmd.buffer, option_name + 2,
|
||||||
|
result, result_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
err:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (mysqld_path == NULL)
|
int Instance_options::get_pid_filename(char *result)
|
||||||
|
{
|
||||||
|
const char *pid_file= mysqld_pid_file;
|
||||||
|
char datadir[MAX_PATH_LEN];
|
||||||
|
|
||||||
|
if (!(mysqld_datadir))
|
||||||
|
{
|
||||||
|
/* we might get an error here if we have wrong path to the mysqld binary */
|
||||||
|
if (get_default_option(datadir, sizeof(datadir), "--datadir"))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1,
|
||||||
|
"/", NullS);
|
||||||
|
|
||||||
|
DBUG_ASSERT(mysqld_pid_file);
|
||||||
|
pid_file= strchr(pid_file, '=') + 1;
|
||||||
|
|
||||||
|
/* get the full path to the pidfile */
|
||||||
|
my_load_path(result, pid_file, datadir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance_options::unlink_pidfile()
|
||||||
|
{
|
||||||
|
return unlink(pid_file_with_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pid_t Instance_options::get_pid()
|
||||||
|
{
|
||||||
|
FILE *pid_file_stream;
|
||||||
|
|
||||||
|
/* get the pid */
|
||||||
|
if ((pid_file_stream= my_fopen(pid_file_with_path,
|
||||||
|
O_RDONLY | O_BINARY, MYF(0))) != NULL)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
fscanf(pid_file_stream, "%i", &pid);
|
||||||
|
my_fclose(pid_file_stream, MYF(0));
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Instance_options::complete_initialization(const char *default_path,
|
||||||
|
int only_instance)
|
||||||
|
{
|
||||||
|
const char *tmp;
|
||||||
|
|
||||||
|
if (!(mysqld_path))
|
||||||
{
|
{
|
||||||
if (!(mysqld_path= strdup_root(&alloc, default_path)))
|
if (!(mysqld_path= strdup_root(&alloc, default_path)))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this option must be first in the argv */
|
if (mysqld_port)
|
||||||
|
mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1);
|
||||||
|
|
||||||
|
if (shutdown_delay)
|
||||||
|
shutdown_delay_val= atoi(shutdown_delay);
|
||||||
|
|
||||||
|
if (!(tmp= strdup_root(&alloc, "--no-defaults")))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!(mysqld_pid_file))
|
||||||
|
{
|
||||||
|
char pidfilename[MAX_PATH_LEN];
|
||||||
|
char hostname[MAX_PATH_LEN];
|
||||||
|
|
||||||
|
/*
|
||||||
|
If we created only one istance [mysqld], because no config. files were
|
||||||
|
found, we would like to model mysqld pid file values.
|
||||||
|
*/
|
||||||
|
if (!gethostname(hostname, sizeof(hostname) - 1))
|
||||||
|
(only_instance == 0) ?
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, "-",
|
||||||
|
hostname, ".pid", NullS):
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname,
|
||||||
|
".pid", NullS);
|
||||||
|
|
||||||
|
else
|
||||||
|
(only_instance == 0) ?
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name,
|
||||||
|
".pid", NullS):
|
||||||
|
strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql",
|
||||||
|
".pid", NullS);
|
||||||
|
|
||||||
|
add_option(pidfilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_pid_filename(pid_file_with_path))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* we need to reserve space for the final zero + possible default options */
|
||||||
|
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
||||||
|
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* the path must be first in the argv */
|
||||||
if (add_to_argv(mysqld_path))
|
if (add_to_argv(mysqld_path))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* the following options are not for argv */
|
if (add_to_argv(tmp))
|
||||||
if (mysqld_user == NULL)
|
goto err;
|
||||||
{
|
|
||||||
if (!(mysqld_user= strdup_root(&alloc, default_user)))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mysqld_password == NULL)
|
|
||||||
{
|
|
||||||
if (!(mysqld_password= strdup_root(&alloc, default_password)))
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
|
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
|
||||||
options_array.elements*sizeof(char*));
|
options_array.elements*sizeof(char*));
|
||||||
@ -102,9 +229,8 @@ int Instance_options::add_option(const char* option)
|
|||||||
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
|
{"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD},
|
||||||
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
||||||
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
||||||
{"--admin-user=", 13, &mysqld_user, SAVE_VALUE},
|
{"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
|
||||||
{"--admin-password=", 17, &mysqld_password, SAVE_VALUE},
|
{"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE},
|
||||||
{"--guarded", 9, &is_guarded, SAVE_WHOLE},
|
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
struct selected_options_st *selected_options;
|
struct selected_options_st *selected_options;
|
||||||
@ -131,6 +257,9 @@ int Instance_options::add_option(const char* option)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we haven't returned earlier we should just save the option */
|
||||||
|
insert_dynamic(&options_array,(gptr) &tmp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -142,12 +271,24 @@ int Instance_options::add_to_argv(const char* option)
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
||||||
|
|
||||||
if (option != NULL)
|
if ((option))
|
||||||
argv[filled_default_options++]= (char *) option;
|
argv[filled_default_options++]= (char *) option;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* function for debug purposes */
|
||||||
|
void Instance_options::print_argv()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printf("printing out an instance %s argv:\n", instance_name);
|
||||||
|
for (i=0; argv[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
printf("argv: %s\n", argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We execute this function to initialize some options.
|
We execute this function to initialize some options.
|
||||||
Return value: 0 - ok. 1 - unable to allocate memory.
|
Return value: 0 - ok. 1 - unable to allocate memory.
|
||||||
|
@ -37,22 +37,31 @@ class Instance_options
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Instance_options() :
|
Instance_options() :
|
||||||
mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0),
|
mysqld_socket(0), mysqld_datadir(0),
|
||||||
mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0),
|
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
|
||||||
mysqld_password(0), is_guarded(0), filled_default_options(0)
|
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0),
|
||||||
|
shutdown_delay_val(0), filled_default_options(0)
|
||||||
{}
|
{}
|
||||||
~Instance_options();
|
~Instance_options();
|
||||||
/* fills in argv */
|
/* fills in argv */
|
||||||
int complete_initialization(const char *default_path,
|
int complete_initialization(const char *default_path, int only_instance);
|
||||||
const char *default_user,
|
|
||||||
const char *default_password);
|
|
||||||
|
|
||||||
int add_option(const char* option);
|
int add_option(const char* option);
|
||||||
int init(const char *instance_name_arg);
|
int init(const char *instance_name_arg);
|
||||||
|
pid_t get_pid();
|
||||||
|
int get_pid_filename(char *result);
|
||||||
|
int unlink_pidfile();
|
||||||
|
void print_argv();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 };
|
/*
|
||||||
|
We need this value to be greater or equal then FN_REFLEN found in
|
||||||
|
my_global.h to use my_load_path()
|
||||||
|
*/
|
||||||
|
enum { MAX_PATH_LEN= 512 };
|
||||||
|
enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 };
|
||||||
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
enum { MEM_ROOT_BLOCK_SIZE= 512 };
|
||||||
|
char pid_file_with_path[MAX_PATH_LEN];
|
||||||
char **argv;
|
char **argv;
|
||||||
/* We need the some options, so we store them as a separate pointers */
|
/* We need the some options, so we store them as a separate pointers */
|
||||||
const char *mysqld_socket;
|
const char *mysqld_socket;
|
||||||
@ -60,15 +69,19 @@ public:
|
|||||||
const char *mysqld_bind_address;
|
const char *mysqld_bind_address;
|
||||||
const char *mysqld_pid_file;
|
const char *mysqld_pid_file;
|
||||||
const char *mysqld_port;
|
const char *mysqld_port;
|
||||||
uint instance_name_len;
|
uint mysqld_port_val;
|
||||||
const char *instance_name;
|
const char *instance_name;
|
||||||
|
uint instance_name_len;
|
||||||
const char *mysqld_path;
|
const char *mysqld_path;
|
||||||
const char *mysqld_user;
|
const char *nonguarded;
|
||||||
const char *mysqld_password;
|
const char *shutdown_delay;
|
||||||
const char *is_guarded;
|
uint shutdown_delay_val;
|
||||||
|
/* this value is computed and cashed here */
|
||||||
DYNAMIC_ARRAY options_array;
|
DYNAMIC_ARRAY options_array;
|
||||||
private:
|
private:
|
||||||
int add_to_argv(const char *option);
|
int add_to_argv(const char *option);
|
||||||
|
int get_default_option(char *result, size_t result_len,
|
||||||
|
const char *option_name);
|
||||||
private:
|
private:
|
||||||
uint filled_default_options;
|
uint filled_default_options;
|
||||||
MEM_ROOT alloc;
|
MEM_ROOT alloc;
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mysql_connection.h"
|
#include "mysql_connection.h"
|
||||||
|
#include "priv.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -57,13 +58,11 @@ Listener_thread::Listener_thread(const Listener_thread_args &args) :
|
|||||||
,total_connection_count(0)
|
,total_connection_count(0)
|
||||||
,thread_info(pthread_self())
|
,thread_info(pthread_self())
|
||||||
{
|
{
|
||||||
thread_registry.register_thread(&thread_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Listener_thread::~Listener_thread()
|
Listener_thread::~Listener_thread()
|
||||||
{
|
{
|
||||||
thread_registry.unregister_thread(&thread_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +81,19 @@ void Listener_thread::run()
|
|||||||
enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
|
enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size
|
||||||
int flags;
|
int flags;
|
||||||
int arg= 1; /* value to be set by setsockopt */
|
int arg= 1; /* value to be set by setsockopt */
|
||||||
|
int unix_socket;
|
||||||
|
uint im_port;
|
||||||
|
/* we use this var to check whether we are running on LinuxThreads */
|
||||||
|
pid_t thread_pid;
|
||||||
|
|
||||||
|
thread_pid= getpid();
|
||||||
|
/* set global variable */
|
||||||
|
linuxthreads= (thread_pid != manager_pid);
|
||||||
|
|
||||||
|
thread_registry.register_thread(&thread_info);
|
||||||
|
|
||||||
|
my_thread_init();
|
||||||
|
|
||||||
/* I. prepare 'listen' sockets */
|
/* I. prepare 'listen' sockets */
|
||||||
|
|
||||||
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
|
int ip_socket= socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@ -89,8 +101,7 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
|
log_error("Listener_thead::run(): socket(AF_INET) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in ip_socket_address;
|
struct sockaddr_in ip_socket_address;
|
||||||
@ -104,7 +115,7 @@ void Listener_thread::run()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
im_bind_addr= htonl(INADDR_ANY);
|
im_bind_addr= htonl(INADDR_ANY);
|
||||||
uint im_port= options.port_number;
|
im_port= options.port_number;
|
||||||
|
|
||||||
ip_socket_address.sin_family= AF_INET;
|
ip_socket_address.sin_family= AF_INET;
|
||||||
ip_socket_address.sin_addr.s_addr = im_bind_addr;
|
ip_socket_address.sin_addr.s_addr = im_bind_addr;
|
||||||
@ -119,16 +130,14 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
/* set the socket nonblocking */
|
/* set the socket nonblocking */
|
||||||
flags= fcntl(ip_socket, F_GETFL, 0);
|
flags= fcntl(ip_socket, F_GETFL, 0);
|
||||||
@ -140,13 +149,12 @@ void Listener_thread::run()
|
|||||||
log_info("accepting connections on ip socket");
|
log_info("accepting connections on ip socket");
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
/*--------------------------------------------------------------*/
|
||||||
int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
|
unix_socket= socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (unix_socket == INVALID_SOCKET)
|
if (unix_socket == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_un unix_socket_address;
|
struct sockaddr_un unix_socket_address;
|
||||||
@ -169,8 +177,7 @@ void Listener_thread::run()
|
|||||||
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
||||||
"socket file name is '%s', error '%s'",
|
"socket file name is '%s', error '%s'",
|
||||||
unix_socket_address.sun_path, strerror(errno));
|
unix_socket_address.sun_path, strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
umask(old_mask);
|
umask(old_mask);
|
||||||
|
|
||||||
@ -178,8 +185,7 @@ void Listener_thread::run()
|
|||||||
{
|
{
|
||||||
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
thread_registry.request_shutdown();
|
goto err;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the socket nonblocking */
|
/* set the socket nonblocking */
|
||||||
@ -205,7 +211,15 @@ void Listener_thread::run()
|
|||||||
while (thread_registry.is_shutdown() == false)
|
while (thread_registry.is_shutdown() == false)
|
||||||
{
|
{
|
||||||
fd_set read_fds_arg= read_fds;
|
fd_set read_fds_arg= read_fds;
|
||||||
|
|
||||||
|
/*
|
||||||
|
When using valgrind 2.0 this syscall doesn't get kicked off by a
|
||||||
|
signal during shutdown. This results in failing assert
|
||||||
|
(Thread_registry::~Thread_registry). Valgrind 2.2 works fine.
|
||||||
|
*/
|
||||||
int rc= select(n, &read_fds_arg, 0, 0, 0);
|
int rc= select(n, &read_fds_arg, 0, 0, 0);
|
||||||
|
|
||||||
|
|
||||||
if (rc == -1 && errno != EINTR)
|
if (rc == -1 && errno != EINTR)
|
||||||
log_error("Listener_thread::run(): select() failed, %s",
|
log_error("Listener_thread::run(): select() failed, %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
@ -256,6 +270,16 @@ void Listener_thread::run()
|
|||||||
close(unix_socket);
|
close(unix_socket);
|
||||||
close(ip_socket);
|
close(ip_socket);
|
||||||
unlink(unix_socket_address.sun_path);
|
unlink(unix_socket_address.sun_path);
|
||||||
|
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
my_thread_end();
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
thread_registry.unregister_thread(&thread_info);
|
||||||
|
thread_registry.request_shutdown();
|
||||||
|
my_thread_end();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,12 +16,7 @@
|
|||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
#include <my_global.h>
|
#include "priv.h"
|
||||||
#include <my_sys.h>
|
|
||||||
#include <m_string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <thr_alarm.h>
|
|
||||||
|
|
||||||
#include "thread_registry.h"
|
#include "thread_registry.h"
|
||||||
#include "listener.h"
|
#include "listener.h"
|
||||||
#include "instance_map.h"
|
#include "instance_map.h"
|
||||||
@ -30,6 +25,14 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "guardian.h"
|
#include "guardian.h"
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <thr_alarm.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
|
||||||
static int create_pid_file(const char *pid_file_name)
|
static int create_pid_file(const char *pid_file_name)
|
||||||
{
|
{
|
||||||
if (FILE *pid_file= my_fopen(pid_file_name,
|
if (FILE *pid_file= my_fopen(pid_file_name,
|
||||||
@ -65,9 +68,7 @@ void manager(const Options &options)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
User_map user_map;
|
User_map user_map;
|
||||||
Instance_map instance_map(options.default_mysqld_path,
|
Instance_map instance_map(options.default_mysqld_path, options.first_option);
|
||||||
options.default_admin_user,
|
|
||||||
options.default_admin_password);
|
|
||||||
Guardian_thread guardian_thread(thread_registry,
|
Guardian_thread guardian_thread(thread_registry,
|
||||||
&instance_map,
|
&instance_map,
|
||||||
options.monitoring_interval);
|
options.monitoring_interval);
|
||||||
@ -75,10 +76,22 @@ void manager(const Options &options)
|
|||||||
Listener_thread_args listener_args(thread_registry, options, user_map,
|
Listener_thread_args listener_args(thread_registry, options, user_map,
|
||||||
instance_map);
|
instance_map);
|
||||||
|
|
||||||
|
manager_pid= getpid();
|
||||||
instance_map.guardian= &guardian_thread;
|
instance_map.guardian= &guardian_thread;
|
||||||
|
|
||||||
if (instance_map.init() || user_map.init() || instance_map.load() ||
|
if (instance_map.init() || user_map.init())
|
||||||
user_map.load(options.password_file_name))
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if (instance_map.load())
|
||||||
|
{
|
||||||
|
log_error("Cannot init instances repository. This might be caused by "
|
||||||
|
"the wrong config file options. For instance, missing mysqld "
|
||||||
|
"binary. Aborting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_map.load(options.password_file_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* write pid file */
|
/* write pid file */
|
||||||
@ -126,6 +139,12 @@ void manager(const Options &options)
|
|||||||
pthread_attr_t guardian_thd_attr;
|
pthread_attr_t guardian_thd_attr;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: Guardian should be shutdown first. Only then all other threads
|
||||||
|
need to be stopped. This should be done, as guardian is responsible for
|
||||||
|
shutting down the instances, and this is a long operation.
|
||||||
|
*/
|
||||||
|
|
||||||
pthread_attr_init(&guardian_thd_attr);
|
pthread_attr_init(&guardian_thd_attr);
|
||||||
pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||||
rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian,
|
rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian,
|
||||||
@ -153,26 +172,50 @@ void manager(const Options &options)
|
|||||||
more then 10 alarms at the same time.
|
more then 10 alarms at the same time.
|
||||||
*/
|
*/
|
||||||
init_thr_alarm(10);
|
init_thr_alarm(10);
|
||||||
|
/* init list of guarded instances */
|
||||||
|
guardian_thread.lock();
|
||||||
|
|
||||||
|
guardian_thread.init();
|
||||||
|
|
||||||
|
guardian_thread.unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now we can init the list of guarded instances. We have to do it after
|
After the list of guarded instances have been initialized,
|
||||||
alarm structures initialization as we have to use net_* functions while
|
Guardian should start them.
|
||||||
making the list. And they in their turn need alarms for timeout suppport.
|
|
||||||
*/
|
*/
|
||||||
guardian_thread.start();
|
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
while (!shutdown_complete)
|
while (!shutdown_complete)
|
||||||
{
|
{
|
||||||
sigwait(&mask, &signo);
|
int status= 0;
|
||||||
|
|
||||||
|
if ((status= my_sigwait(&mask, &signo)) != 0)
|
||||||
|
{
|
||||||
|
log_error("sigwait() failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
switch (signo)
|
switch (signo)
|
||||||
{
|
{
|
||||||
case THR_SERVER_ALARM:
|
case THR_SERVER_ALARM:
|
||||||
process_alarm(signo);
|
process_alarm(signo);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
thread_registry.deliver_shutdown();
|
{
|
||||||
shutdown_complete= TRUE;
|
if (!guardian_thread.is_stopped())
|
||||||
|
{
|
||||||
|
bool stop_instances= true;
|
||||||
|
guardian_thread.request_shutdown(stop_instances);
|
||||||
|
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thread_registry.deliver_shutdown();
|
||||||
|
shutdown_complete= TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,9 +224,6 @@ err:
|
|||||||
/* delete the pid file */
|
/* delete the pid file */
|
||||||
my_delete(options.pid_file_name, MYF(0));
|
my_delete(options.pid_file_name, MYF(0));
|
||||||
|
|
||||||
/* close permanent connections to the running instances */
|
|
||||||
instance_map.cleanup();
|
|
||||||
|
|
||||||
/* free alarm structures */
|
/* free alarm structures */
|
||||||
end_thr_alarm(1);
|
end_thr_alarm(1);
|
||||||
/* don't pthread_exit to kill all threads who did not shut down in time */
|
/* don't pthread_exit to kill all threads who did not shut down in time */
|
||||||
|
@ -45,8 +45,8 @@ static const char *mysqld_error_message(unsigned sql_errno)
|
|||||||
case ER_BAD_INSTANCE_NAME:
|
case ER_BAD_INSTANCE_NAME:
|
||||||
return "Bad instance name. Check that the instance with such a name exists";
|
return "Bad instance name. Check that the instance with such a name exists";
|
||||||
case ER_INSTANCE_IS_NOT_STARTED:
|
case ER_INSTANCE_IS_NOT_STARTED:
|
||||||
return "Cannot stop instance. Perhaps the instance is not started or you"
|
return "Cannot stop instance. Perhaps the instance is not started, or was started"
|
||||||
" have specified wrong username/password in the config file";
|
"manually, so IM cannot find the pidfile.";
|
||||||
case ER_INSTANCE_ALREADY_STARTED:
|
case ER_INSTANCE_ALREADY_STARTED:
|
||||||
return "The instance is already started";
|
return "The instance is already started";
|
||||||
case ER_CANNOT_START_INSTANCE:
|
case ER_CANNOT_START_INSTANCE:
|
||||||
|
@ -82,7 +82,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
/* Names are conventionally the same as in mysqld */
|
/* Names are conventionally the same as in mysqld */
|
||||||
int check_connection();
|
int check_connection();
|
||||||
int check_user(const char *user, const char *password);
|
|
||||||
int do_command();
|
int do_command();
|
||||||
int dispatch_command(enum enum_server_command command,
|
int dispatch_command(enum enum_server_command command,
|
||||||
const char *text, uint len);
|
const char *text, uint len);
|
||||||
@ -287,12 +286,6 @@ int Mysql_connection_thread::check_connection()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Mysql_connection_thread::check_user(const char *user, const char *password)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Mysql_connection_thread::do_command()
|
int Mysql_connection_thread::do_command()
|
||||||
{
|
{
|
||||||
char *packet;
|
char *packet;
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -54,6 +56,8 @@
|
|||||||
static void init_environment(char *progname);
|
static void init_environment(char *progname);
|
||||||
static void daemonize(const char *log_file_name);
|
static void daemonize(const char *log_file_name);
|
||||||
static void angel(const Options &options);
|
static void angel(const Options &options);
|
||||||
|
static struct passwd *check_user(const char *user);
|
||||||
|
static int set_user(const char *user, struct passwd *user_info);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -68,7 +72,20 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
init_environment(argv[0]);
|
init_environment(argv[0]);
|
||||||
Options options;
|
Options options;
|
||||||
options.load(argc, argv);
|
struct passwd *user_info;
|
||||||
|
|
||||||
|
if (options.load(argc, argv))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if ((user_info= check_user(options.user)))
|
||||||
|
{
|
||||||
|
if (set_user(options.user, user_info))
|
||||||
|
{
|
||||||
|
options.cleanup();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.run_as_service)
|
if (options.run_as_service)
|
||||||
{
|
{
|
||||||
/* forks, and returns only in child */
|
/* forks, and returns only in child */
|
||||||
@ -77,11 +94,84 @@ int main(int argc, char *argv[])
|
|||||||
angel(options);
|
angel(options);
|
||||||
}
|
}
|
||||||
manager(options);
|
manager(options);
|
||||||
|
options.cleanup();
|
||||||
|
my_end(0);
|
||||||
return 0;
|
return 0;
|
||||||
|
err:
|
||||||
|
my_end(0);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************* Auxilary functions implementation **********************/
|
/******************* Auxilary functions implementation **********************/
|
||||||
|
|
||||||
|
/* Change to run as another user if started with --user */
|
||||||
|
|
||||||
|
static struct passwd *check_user(const char *user)
|
||||||
|
{
|
||||||
|
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
|
||||||
|
struct passwd *user_info;
|
||||||
|
uid_t user_id= geteuid();
|
||||||
|
|
||||||
|
/* Don't bother if we aren't superuser */
|
||||||
|
if (user_id)
|
||||||
|
{
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
/* Don't give a warning, if real user is same as given with --user */
|
||||||
|
user_info= getpwnam(user);
|
||||||
|
if ((!user_info || user_id != user_info->pw_uid))
|
||||||
|
log_info("One can only use the --user switch if running as root\n");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
log_info("You are running mysqlmanager as root! This might introduce security problems. It is safer to use --user option istead.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!strcmp(user, "root"))
|
||||||
|
return NULL; /* Avoid problem with dynamic libraries */
|
||||||
|
if (!(user_info= getpwnam(user)))
|
||||||
|
{
|
||||||
|
/* Allow a numeric uid to be used */
|
||||||
|
const char *pos;
|
||||||
|
for (pos= user; my_isdigit(default_charset_info, *pos); pos++) ;
|
||||||
|
if (*pos) /* Not numeric id */
|
||||||
|
goto err;
|
||||||
|
if (!(user_info= getpwuid(atoi(user))))
|
||||||
|
goto err;
|
||||||
|
else
|
||||||
|
return user_info;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return user_info;
|
||||||
|
|
||||||
|
err:
|
||||||
|
log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user);
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_user(const char *user, struct passwd *user_info)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(user_info);
|
||||||
|
#ifdef HAVE_INITGROUPS
|
||||||
|
initgroups((char*) user,user_info->pw_gid);
|
||||||
|
#endif
|
||||||
|
if (setgid(user_info->pw_gid) == -1)
|
||||||
|
{
|
||||||
|
log_error("setgid() failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (setuid(user_info->pw_uid) == -1)
|
||||||
|
{
|
||||||
|
log_error("setuid() failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Init environment, common for daemon and non-daemon
|
Init environment, common for daemon and non-daemon
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <my_getopt.h>
|
#include <my_getopt.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
#include <mysql_com.h>
|
||||||
|
|
||||||
#include "priv.h"
|
#include "priv.h"
|
||||||
|
|
||||||
@ -35,11 +37,13 @@ const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
|
|||||||
const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
|
const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME);
|
||||||
const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
||||||
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
||||||
const char *Options::default_admin_user= QUOTE(DEFAULT_USER);
|
const char *Options::first_option= 0; /* No default value */
|
||||||
const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD);
|
const char *Options::bind_address= 0; /* No default value */
|
||||||
const char *Options::bind_address= 0; /* No default value */
|
const char *Options::user= 0; /* No default value */
|
||||||
uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
|
uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL;
|
||||||
uint Options::port_number= DEFAULT_PORT;
|
uint Options::port_number= DEFAULT_PORT;
|
||||||
|
/* just to declare */
|
||||||
|
char **Options::saved_argv;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
List of options, accepted by the instance manager.
|
List of options, accepted by the instance manager.
|
||||||
@ -54,9 +58,6 @@ enum options {
|
|||||||
OPT_MYSQLD_PATH,
|
OPT_MYSQLD_PATH,
|
||||||
OPT_RUN_AS_SERVICE,
|
OPT_RUN_AS_SERVICE,
|
||||||
OPT_USER,
|
OPT_USER,
|
||||||
OPT_PASSWORD,
|
|
||||||
OPT_DEFAULT_ADMIN_USER,
|
|
||||||
OPT_DEFAULT_ADMIN_PASSWORD,
|
|
||||||
OPT_MONITORING_INTERVAL,
|
OPT_MONITORING_INTERVAL,
|
||||||
OPT_PORT,
|
OPT_PORT,
|
||||||
OPT_BIND_ADDRESS
|
OPT_BIND_ADDRESS
|
||||||
@ -79,13 +80,16 @@ static struct my_option my_long_options[] =
|
|||||||
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
|
{ "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0,
|
||||||
|
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
|
{ "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.",
|
||||||
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "port", OPT_PORT, "Port number to use for connections",
|
{ "port", OPT_PORT, "Port number to use for connections",
|
||||||
(gptr *) &Options::port_number, (gptr *) &Options::port_number,
|
(gptr *) &Options::port_number, (gptr *) &Options::port_number,
|
||||||
0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users"
|
{ "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users"
|
||||||
" and passwords here.",
|
" and passwords here.",
|
||||||
@ -98,28 +102,22 @@ static struct my_option my_long_options[] =
|
|||||||
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "default-admin-user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL"
|
|
||||||
" instances.",
|
|
||||||
(gptr *) &Options::default_admin_user,
|
|
||||||
(gptr *) &Options::default_admin_user,
|
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
||||||
|
|
||||||
{ "default-admin-password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to"
|
|
||||||
"shutdown MySQL instances.",
|
|
||||||
(gptr *) &Options::default_admin_password,
|
|
||||||
(gptr *) &Options::default_admin_password,
|
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
|
||||||
|
|
||||||
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances"
|
{ "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances"
|
||||||
" in seconds.",
|
" in seconds.",
|
||||||
(gptr *) &Options::monitoring_interval,
|
(gptr *) &Options::monitoring_interval,
|
||||||
(gptr *) &Options::monitoring_interval,
|
(gptr *) &Options::monitoring_interval,
|
||||||
0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL,
|
||||||
|
0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "run-as-service", OPT_RUN_AS_SERVICE,
|
{ "run-as-service", OPT_RUN_AS_SERVICE,
|
||||||
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
||||||
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
|
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
|
||||||
|
|
||||||
|
{ "user", OPT_USER, "Username to start mysqlmanager",
|
||||||
|
(gptr *) &Options::user,
|
||||||
|
(gptr *) &Options::user,
|
||||||
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
|
{ "version", 'V', "Output version information and exit.", 0, 0, 0,
|
||||||
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
@ -150,6 +148,38 @@ static void usage()
|
|||||||
my_print_variables(my_long_options);
|
my_print_variables(my_long_options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void passwd()
|
||||||
|
{
|
||||||
|
char user[1024], *p;
|
||||||
|
const char *pw1, *pw2;
|
||||||
|
char pw1msg[]= "Enter password: ";
|
||||||
|
char pw2msg[]= "Re-type password: ";
|
||||||
|
char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
|
||||||
|
|
||||||
|
fprintf(stderr, "Creating record for new user.\n");
|
||||||
|
fprintf(stderr, "Enter user name: ");
|
||||||
|
if (! fgets(user, sizeof(user), stdin))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unable to read user.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((p= strchr(user, '\n'))) *p= 0;
|
||||||
|
|
||||||
|
pw1= get_tty_password(pw1msg);
|
||||||
|
pw2= get_tty_password(pw2msg);
|
||||||
|
|
||||||
|
if (strcmp(pw1, pw2))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Sorry, passwords do not match.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_scrambled_password(crypted_pw, pw1);
|
||||||
|
printf("%s:%s\n", user, crypted_pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
C_MODE_START
|
C_MODE_START
|
||||||
|
|
||||||
static my_bool
|
static my_bool
|
||||||
@ -161,7 +191,9 @@ get_one_option(int optid,
|
|||||||
case 'V':
|
case 'V':
|
||||||
version();
|
version();
|
||||||
exit(0);
|
exit(0);
|
||||||
case 'I':
|
case 'P':
|
||||||
|
passwd();
|
||||||
|
exit(0);
|
||||||
case '?':
|
case '?':
|
||||||
usage();
|
usage();
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -180,12 +212,28 @@ C_MODE_END
|
|||||||
May not return.
|
May not return.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void Options::load(int argc, char **argv)
|
int Options::load(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
{
|
||||||
|
if (is_prefix(argv[1],"--defaults-file=") ||
|
||||||
|
is_prefix(argv[1],"--defaults-extra-file="))
|
||||||
|
Options::first_option= argv[1];
|
||||||
|
}
|
||||||
|
|
||||||
/* config-file options are prepended to command-line ones */
|
/* config-file options are prepended to command-line ones */
|
||||||
load_defaults("my", default_groups, &argc, &argv);
|
load_defaults("my", default_groups, &argc, &argv);
|
||||||
|
|
||||||
if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option))
|
if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0)
|
||||||
exit(rc);
|
return rc;
|
||||||
|
Options::saved_argv= argv;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Options::cleanup()
|
||||||
|
{
|
||||||
|
/* free_defaults returns nothing */
|
||||||
|
free_defaults(Options::saved_argv);
|
||||||
|
}
|
||||||
|
@ -34,13 +34,17 @@ struct Options
|
|||||||
static const char *socket_file_name;
|
static const char *socket_file_name;
|
||||||
static const char *password_file_name;
|
static const char *password_file_name;
|
||||||
static const char *default_mysqld_path;
|
static const char *default_mysqld_path;
|
||||||
static const char *default_admin_user;
|
static const char *user;
|
||||||
static const char *default_admin_password;
|
/* the option which should be passed to process_default_option_files */
|
||||||
|
static const char *first_option;
|
||||||
static uint monitoring_interval;
|
static uint monitoring_interval;
|
||||||
static uint port_number;
|
static uint port_number;
|
||||||
static const char *bind_address;
|
static const char *bind_address;
|
||||||
|
|
||||||
static void load(int argc, char **argv);
|
static char **saved_argv;
|
||||||
|
|
||||||
|
static int load(int argc, char **argv);
|
||||||
|
void cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
|
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H
|
||||||
|
@ -49,29 +49,6 @@ static struct tokens_st tokens[]= {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
tries to find next word in the text
|
|
||||||
if found, returns the beginning and puts word length to word_len argument.
|
|
||||||
if not found returns pointer to first non-space or to '\0', word_len == 0
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline void get_word(const char **text, uint *word_len)
|
|
||||||
{
|
|
||||||
const char *word_end;
|
|
||||||
|
|
||||||
/* skip space */
|
|
||||||
while (my_isspace(default_charset_info, **text))
|
|
||||||
++(*text);
|
|
||||||
|
|
||||||
word_end= *text;
|
|
||||||
|
|
||||||
while (my_isalnum(default_charset_info, *word_end))
|
|
||||||
++word_end;
|
|
||||||
|
|
||||||
*word_len= word_end - *text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Returns token no if word corresponds to some token, otherwise returns
|
Returns token no if word corresponds to some token, otherwise returns
|
||||||
TOK_NOT_FOUND
|
TOK_NOT_FOUND
|
||||||
|
@ -20,4 +20,34 @@
|
|||||||
|
|
||||||
Command *parse_command(Command_factory *factory, const char *text);
|
Command *parse_command(Command_factory *factory, const char *text);
|
||||||
|
|
||||||
|
/* define kinds of the word seek method */
|
||||||
|
enum { ALPHANUM= 1, NONSPACE };
|
||||||
|
|
||||||
|
/*
|
||||||
|
tries to find next word in the text
|
||||||
|
if found, returns the beginning and puts word length to word_len argument.
|
||||||
|
if not found returns pointer to first non-space or to '\0', word_len == 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline void get_word(const char **text, uint *word_len,
|
||||||
|
int seek_method= ALPHANUM)
|
||||||
|
{
|
||||||
|
const char *word_end;
|
||||||
|
|
||||||
|
/* skip space */
|
||||||
|
while (my_isspace(default_charset_info, **text))
|
||||||
|
++(*text);
|
||||||
|
|
||||||
|
word_end= *text;
|
||||||
|
|
||||||
|
if (seek_method == ALPHANUM)
|
||||||
|
while (my_isalnum(default_charset_info, *word_end))
|
||||||
|
++word_end;
|
||||||
|
else
|
||||||
|
while (!my_isspace(default_charset_info, *word_end))
|
||||||
|
++word_end;
|
||||||
|
|
||||||
|
*word_len= word_end - *text;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
|
#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */
|
||||||
|
101
server-tools/instance-manager/parse_output.cc
Normal file
101
server-tools/instance-manager/parse_output.cc
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/* Copyright (C) 2004 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse output of the given command
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
parse_output_and_get_value()
|
||||||
|
|
||||||
|
command the command to execue with popen.
|
||||||
|
word the word to look for (usually an option name)
|
||||||
|
result the buffer to store the next word (option value)
|
||||||
|
result_len self-explanatory
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
Parse output of the "command". Find the "word" and return the next one
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 - ok
|
||||||
|
1 - error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
int parse_output_and_get_value(const char *command, const char *word,
|
||||||
|
char *result, size_t result_len)
|
||||||
|
{
|
||||||
|
FILE *output;
|
||||||
|
uint wordlen;
|
||||||
|
/* should be enought to store the string from the output */
|
||||||
|
enum { MAX_LINE_LEN= 512 };
|
||||||
|
char linebuf[MAX_LINE_LEN];
|
||||||
|
|
||||||
|
wordlen= strlen(word);
|
||||||
|
|
||||||
|
if (!(output= popen(command, "r")))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We want fully buffered stream. We also want system to
|
||||||
|
allocate appropriate buffer.
|
||||||
|
*/
|
||||||
|
setvbuf(output, NULL, _IOFBF, 0);
|
||||||
|
|
||||||
|
while (fgets(linebuf, sizeof(linebuf) - 1, output))
|
||||||
|
{
|
||||||
|
uint lineword_len= 0;
|
||||||
|
char *linep= linebuf;
|
||||||
|
|
||||||
|
linebuf[sizeof(linebuf) - 1]= '\0'; /* safety */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the word, which might contain non-alphanumeric characters. (Usually
|
||||||
|
these are '/', '-' and '.' in the path expressions and filenames)
|
||||||
|
*/
|
||||||
|
get_word((const char **) &linep, &lineword_len, NONSPACE);
|
||||||
|
if (!strncmp(word, linep, wordlen))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If we have found the word, return the next one. This is usually
|
||||||
|
an option value.
|
||||||
|
*/
|
||||||
|
linep+= lineword_len; /* swallow the previous one */
|
||||||
|
get_word((const char **) &linep, &lineword_len, NONSPACE);
|
||||||
|
if (result_len <= lineword_len)
|
||||||
|
goto err;
|
||||||
|
strncpy(result, linep, lineword_len);
|
||||||
|
result[lineword_len]= '\0';
|
||||||
|
goto pclose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose:
|
||||||
|
/* we are not interested in the termination status */
|
||||||
|
pclose(output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return 1;
|
||||||
|
}
|
19
server-tools/instance-manager/parse_output.h
Normal file
19
server-tools/instance-manager/parse_output.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* Copyright (C) 2004 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
int parse_output_and_get_value(const char *command, const char *word,
|
||||||
|
char *result, size_t result_len);
|
||||||
|
|
@ -16,6 +16,15 @@
|
|||||||
|
|
||||||
#include "priv.h"
|
#include "priv.h"
|
||||||
|
|
||||||
|
/* the pid of the manager process (of the signal thread on the LinuxThreads) */
|
||||||
|
pid_t manager_pid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This flag is set if mysqlmanager has detected that it is running on the
|
||||||
|
system using LinuxThreads
|
||||||
|
*/
|
||||||
|
bool linuxthreads;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The following string must be less then 80 characters, as
|
The following string must be less then 80 characters, as
|
||||||
mysql_connection.cc relies on it
|
mysql_connection.cc relies on it
|
||||||
|
@ -16,6 +16,19 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* the pid of the manager process (of the signal thread on the LinuxThreads) */
|
||||||
|
extern pid_t manager_pid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This flag is set if mysqlmanager has detected that it is running on the
|
||||||
|
system using LinuxThreads
|
||||||
|
*/
|
||||||
|
extern bool linuxthreads;
|
||||||
|
|
||||||
extern const char mysqlmanager_version[];
|
extern const char mysqlmanager_version[];
|
||||||
extern const int mysqlmanager_version_length;
|
extern const int mysqlmanager_version_length;
|
||||||
|
|
||||||
|
@ -154,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields)
|
|||||||
store_to_string(&send_buff, (char *) "", &position); /* table name alias */
|
store_to_string(&send_buff, (char *) "", &position); /* table name alias */
|
||||||
store_to_string(&send_buff, field->name, &position); /* column name */
|
store_to_string(&send_buff, field->name, &position); /* column name */
|
||||||
store_to_string(&send_buff, field->name, &position); /* column name alias */
|
store_to_string(&send_buff, field->name, &position); /* column name alias */
|
||||||
if (send_buff.reserve(position, 12))
|
send_buff.reserve(position, 12);
|
||||||
|
if (send_buff.is_error())
|
||||||
goto err;
|
goto err;
|
||||||
send_buff.buffer[position++]= 12;
|
send_buff.buffer[position++]= 12;
|
||||||
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
||||||
|
@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown()
|
|||||||
if (info->current_cond)
|
if (info->current_cond)
|
||||||
pthread_cond_signal(info->current_cond);
|
pthread_cond_signal(info->current_cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_registry);
|
pthread_mutex_unlock(&LOCK_thread_registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,185 +0,0 @@
|
|||||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma implementation
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "thread_repository.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Kick-off signal handler */
|
|
||||||
|
|
||||||
enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 };
|
|
||||||
|
|
||||||
static void handle_signal(int __attribute__((unused)) sig_no)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: think about moving signal information (now it's shutdown_in_progress)
|
|
||||||
to Thread_info. It will reduce contention and allow signal deliverence to
|
|
||||||
a particular thread, not to the whole worker crew
|
|
||||||
*/
|
|
||||||
|
|
||||||
Thread_repository::Thread_repository() :
|
|
||||||
shutdown_in_progress(false)
|
|
||||||
{
|
|
||||||
pthread_mutex_init(&LOCK_thread_repository, 0);
|
|
||||||
pthread_cond_init(&COND_thread_repository_is_empty, 0);
|
|
||||||
|
|
||||||
/* head is used by-value to simplify nodes inserting */
|
|
||||||
head.next= head.prev= &head;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Thread_repository::~Thread_repository()
|
|
||||||
{
|
|
||||||
/* Check that no one uses the repository. */
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
|
|
||||||
/* All threads must unregister */
|
|
||||||
DBUG_ASSERT(head.next == &head);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
pthread_cond_destroy(&COND_thread_repository_is_empty);
|
|
||||||
pthread_mutex_destroy(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Set signal handler for kick-off thread, and insert a thread info to the
|
|
||||||
repository. New node is appended to the end of the list; head.prev always
|
|
||||||
points to the last node.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::register_thread(Thread_info *info)
|
|
||||||
{
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_handler= handle_signal;
|
|
||||||
sa.sa_flags= 0;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0);
|
|
||||||
|
|
||||||
info->current_cond= 0;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->next= &head;
|
|
||||||
info->prev= head.prev;
|
|
||||||
head.prev->next= info;
|
|
||||||
head.prev= info;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Unregister a thread from the repository and free Thread_info structure.
|
|
||||||
Every registered thread must unregister. Unregistering should be the last
|
|
||||||
thing a thread is doing, otherwise it could have no time to finalize.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::unregister_thread(Thread_info *info)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->prev->next= info->next;
|
|
||||||
info->next->prev= info->prev;
|
|
||||||
if (head.next == &head)
|
|
||||||
pthread_cond_signal(&COND_thread_repository_is_empty);
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check whether shutdown is in progress, and if yes, return immidiately.
|
|
||||||
Else set info->current_cond and call pthread_cond_wait. When
|
|
||||||
pthread_cond_wait returns, unregister current cond and check the shutdown
|
|
||||||
status again.
|
|
||||||
RETURN VALUE
|
|
||||||
return value from pthread_cond_wait
|
|
||||||
*/
|
|
||||||
|
|
||||||
int Thread_repository::cond_wait(Thread_info *info, pthread_cond_t *cond,
|
|
||||||
pthread_mutex_t *mutex, bool *is_shutdown)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
*is_shutdown= shutdown_in_progress;
|
|
||||||
if (*is_shutdown)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
info->current_cond= cond;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
/* sic: race condition here, cond can be signaled in deliver_shutdown */
|
|
||||||
int rc= pthread_cond_wait(cond, mutex);
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
info->current_cond= 0;
|
|
||||||
*is_shutdown= shutdown_in_progress;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Deliver shutdown message to the workers crew.
|
|
||||||
As it's impossible to avoid all race conditions, we signal latecomers
|
|
||||||
again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void Thread_repository::deliver_shutdown()
|
|
||||||
{
|
|
||||||
struct timespec shutdown_time;
|
|
||||||
set_timespec(shutdown_time, 1);
|
|
||||||
Thread_info *info;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
shutdown_in_progress= true;
|
|
||||||
for (info= head.next; info != &head; info= info->next)
|
|
||||||
{
|
|
||||||
pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
|
|
||||||
/*
|
|
||||||
sic: race condition here, the thread may not yet fall into
|
|
||||||
pthread_cond_wait.
|
|
||||||
*/
|
|
||||||
if (info->current_cond)
|
|
||||||
pthread_cond_signal(info->current_cond);
|
|
||||||
}
|
|
||||||
while (pthread_cond_timedwait(&COND_thread_repository_is_empty,
|
|
||||||
&LOCK_thread_repository,
|
|
||||||
&shutdown_time) != ETIMEDOUT &&
|
|
||||||
head.next != &head)
|
|
||||||
;
|
|
||||||
/*
|
|
||||||
If previous signals did not reach some threads, they must be sleeping
|
|
||||||
in pthread_cond_wait or a blocking syscall. Wake them up:
|
|
||||||
every thread shall check signal variables after each syscall/cond_wait,
|
|
||||||
so this time everybody should be informed (presumably each worker can
|
|
||||||
get CPU during shutdown_time.)
|
|
||||||
*/
|
|
||||||
for (info= head.next; info != &head; info= info->next)
|
|
||||||
{
|
|
||||||
pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL);
|
|
||||||
if (info->current_cond)
|
|
||||||
pthread_cond_signal(info->current_cond);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
|||||||
#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
||||||
#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
||||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
||||||
|
|
||||||
/*
|
|
||||||
A multi-threaded application shall nicely work with signals.
|
|
||||||
|
|
||||||
This means it shall, first of all, shut down nicely on ``quit'' signals:
|
|
||||||
stop all running threads, cleanup and exit.
|
|
||||||
|
|
||||||
Note, that a thread can't be shut down nicely if it doesn't want to be.
|
|
||||||
That's why to perform clean shutdown, all threads consituting a process
|
|
||||||
must observe certain rules. Here we use the rules, described in Butenhof
|
|
||||||
book 'Programming with POSIX threads', namely:
|
|
||||||
- all user signals are handled in 'signal thread' in synchronous manner
|
|
||||||
(by means of sigwait). To guarantee that the signal thread is the only who
|
|
||||||
can receive user signals, all threads block them, and signal thread is
|
|
||||||
the only who calls sigwait() with an apporpriate sigmask.
|
|
||||||
To propogate a signal to the workers the signal thread sets
|
|
||||||
a variable, corresponding to the signal. Additionally the signal thread
|
|
||||||
sends each worker an internal signal (by means of pthread_kill) to kick it
|
|
||||||
out from possible blocking syscall, and possibly pthread_cond_signal if
|
|
||||||
some thread is blocked in pthread_cond_[timed]wait.
|
|
||||||
- a worker handles only internal 'kick' signal (the handler does nothing).
|
|
||||||
In case when a syscall returns 'EINTR' the worker checks all
|
|
||||||
signal-related variables and behaves accordingly.
|
|
||||||
Also these variables shall be checked from time to time in long
|
|
||||||
CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed
|
|
||||||
that a worker thread either waits in a syscall/conditional variable, or
|
|
||||||
computes something.)
|
|
||||||
- to guarantee signal deliverence, there should be some kind of feedback,
|
|
||||||
e. g. all workers shall account in the signal thread Thread Repository and
|
|
||||||
unregister from it on exit.
|
|
||||||
|
|
||||||
Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled
|
|
||||||
in manner, similar to ``quit'' signals.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma interface
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <my_global.h>
|
|
||||||
#include <my_pthread.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Thread_info - repository entry for each worker thread
|
|
||||||
All entries comprise double-linked list like:
|
|
||||||
0 -- entry -- entry -- entry - 0
|
|
||||||
Double-linked list is used to unregister threads easy.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Thread_info
|
|
||||||
{
|
|
||||||
pthread_cond_t *current_cond;
|
|
||||||
Thread_info *prev, *next;
|
|
||||||
pthread_t thread_id;
|
|
||||||
Thread_info() {}
|
|
||||||
friend class Thread_repository;
|
|
||||||
public:
|
|
||||||
Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Thread_repository - contains handles for each worker thread to deliver
|
|
||||||
signal information to workers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Thread_repository
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Thread_repository();
|
|
||||||
~Thread_repository();
|
|
||||||
|
|
||||||
void register_thread(Thread_info *info);
|
|
||||||
void unregister_thread(Thread_info *info);
|
|
||||||
void deliver_shutdown();
|
|
||||||
inline bool is_shutdown();
|
|
||||||
int cond_wait(Thread_info *info, pthread_cond_t *cond,
|
|
||||||
pthread_mutex_t *mutex, bool *is_shutdown);
|
|
||||||
private:
|
|
||||||
Thread_info head;
|
|
||||||
bool shutdown_in_progress;
|
|
||||||
pthread_mutex_t LOCK_thread_repository;
|
|
||||||
pthread_cond_t COND_thread_repository_is_empty;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline bool Thread_repository::is_shutdown()
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&LOCK_thread_repository);
|
|
||||||
bool res= shutdown_in_progress;
|
|
||||||
pthread_mutex_unlock(&LOCK_thread_repository);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH
|
|
@ -69,7 +69,7 @@ int User::init(const char *line)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
log_error("error parsing user and password at line %d", line);
|
log_error("error parsing user and password at line %s", line);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,9 +128,10 @@ int User_map::load(const char *password_file_name)
|
|||||||
|
|
||||||
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
|
if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0)
|
||||||
{
|
{
|
||||||
log_error("can't open password file %s: errno=%d, %s", password_file_name,
|
/* Probably the password file wasn't specified. Try to leave without it */
|
||||||
|
log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name,
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), file))
|
while (fgets(line, sizeof(line), file))
|
||||||
|
@ -276,6 +276,7 @@ public:
|
|||||||
virtual bool get_date(TIME *ltime,uint fuzzydate);
|
virtual bool get_date(TIME *ltime,uint fuzzydate);
|
||||||
virtual bool get_time(TIME *ltime);
|
virtual bool get_time(TIME *ltime);
|
||||||
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
|
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
|
||||||
|
virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
|
||||||
virtual bool has_charset(void) const { return FALSE; }
|
virtual bool has_charset(void) const { return FALSE; }
|
||||||
virtual void set_charset(CHARSET_INFO *charset) { }
|
virtual void set_charset(CHARSET_INFO *charset) { }
|
||||||
bool set_warning(const unsigned int level, const unsigned int code,
|
bool set_warning(const unsigned int level, const unsigned int code,
|
||||||
@ -1255,6 +1256,8 @@ public:
|
|||||||
bool optimize_range(uint idx, uint part) { return 0; }
|
bool optimize_range(uint idx, uint part) { return 0; }
|
||||||
bool eq_def(Field *field);
|
bool eq_def(Field *field);
|
||||||
bool has_charset(void) const { return TRUE; }
|
bool has_charset(void) const { return TRUE; }
|
||||||
|
/* enum and set are sorted as integers */
|
||||||
|
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
|
||||||
field_cast_enum field_cast_type() { return FIELD_CAST_ENUM; }
|
field_cast_enum field_cast_type() { return FIELD_CAST_ENUM; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1198,12 +1198,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
sortorder->length=sortorder->field->pack_length();
|
sortorder->length=sortorder->field->pack_length();
|
||||||
/*
|
if (use_strnxfrm((cs=sortorder->field->sort_charset())))
|
||||||
We must test cmp_type() to ensure that ENUM and SET are sorted
|
|
||||||
as numbers
|
|
||||||
*/
|
|
||||||
if (use_strnxfrm((cs=sortorder->field->charset())) &&
|
|
||||||
sortorder->field->cmp_type() == STRING_RESULT)
|
|
||||||
{
|
{
|
||||||
sortorder->need_strxnfrm= 1;
|
sortorder->need_strxnfrm= 1;
|
||||||
*multi_byte_charset= 1;
|
*multi_byte_charset= 1;
|
||||||
|
226
sql/ha_innodb.cc
226
sql/ha_innodb.cc
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2000 MySQL AB & Innobase Oy
|
/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -336,14 +336,18 @@ innobase_release_temporary_latches(
|
|||||||
/*===============================*/
|
/*===============================*/
|
||||||
THD *thd)
|
THD *thd)
|
||||||
{
|
{
|
||||||
|
trx_t* trx;
|
||||||
|
|
||||||
if (!innodb_inited) {
|
if (!innodb_inited) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
trx_t *trx= (trx_t*) thd->ha_data[innobase_hton.slot];
|
trx = (trx_t*) thd->ha_data[innobase_hton.slot];
|
||||||
if (trx)
|
|
||||||
innobase_release_stat_resources(trx);
|
if (trx) {
|
||||||
|
innobase_release_stat_resources(trx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
@ -743,14 +747,15 @@ transaction internally. */
|
|||||||
static
|
static
|
||||||
void
|
void
|
||||||
register_trans(
|
register_trans(
|
||||||
/*============*/
|
/*===========*/
|
||||||
THD* thd) /* in: thd to use the handle */
|
THD* thd) /* in: thd to use the handle */
|
||||||
{
|
{
|
||||||
/* register the start of the statement */
|
/* Register the start of the statement */
|
||||||
trans_register_ha(thd, FALSE, &innobase_hton);
|
trans_register_ha(thd, FALSE, &innobase_hton);
|
||||||
|
|
||||||
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
|
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
|
||||||
|
|
||||||
/* no autocommit mode, register for a transaction */
|
/* No autocommit mode, register for a transaction */
|
||||||
trans_register_ha(thd, TRUE, &innobase_hton);
|
trans_register_ha(thd, TRUE, &innobase_hton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1051,7 +1056,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
|||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
Opens an InnoDB database. */
|
Opens an InnoDB database. */
|
||||||
|
|
||||||
handlerton *
|
handlerton*
|
||||||
innobase_init(void)
|
innobase_init(void)
|
||||||
/*===============*/
|
/*===============*/
|
||||||
/* out: TRUE if error */
|
/* out: TRUE if error */
|
||||||
@ -1220,7 +1225,7 @@ innobase_init(void)
|
|||||||
|
|
||||||
srv_print_verbose_log = mysqld_embedded ? 0 : 1;
|
srv_print_verbose_log = mysqld_embedded ? 0 : 1;
|
||||||
|
|
||||||
/* Store the default charset-collation number of this MySQL
|
/* Store the default charset-collation number of this MySQL
|
||||||
installation */
|
installation */
|
||||||
|
|
||||||
data_mysql_default_charset_coll = (ulint)default_charset_info->number;
|
data_mysql_default_charset_coll = (ulint)default_charset_info->number;
|
||||||
@ -1346,14 +1351,16 @@ innobase_commit_low(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The following will be enabled later when we put the 4.1 functionality back
|
||||||
|
to 5.0. */
|
||||||
#ifdef DISABLE_HAVE_REPLICATION
|
#ifdef DISABLE_HAVE_REPLICATION
|
||||||
if (current_thd->slave_thread) {
|
if (current_thd->slave_thread) {
|
||||||
/* Update the replication position info inside InnoDB */
|
/* Update the replication position info inside InnoDB */
|
||||||
|
|
||||||
trx->mysql_master_log_file_name
|
trx->mysql_master_log_file_name
|
||||||
= active_mi->rli.group_master_log_name;
|
= active_mi->rli.group_master_log_name;
|
||||||
trx->mysql_master_log_pos= ((ib_longlong)
|
trx->mysql_master_log_pos = ((ib_longlong)
|
||||||
active_mi->rli.future_group_master_log_pos);
|
active_mi->rli.future_group_master_log_pos);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
|
|
||||||
@ -1456,7 +1463,8 @@ innobase_commit(
|
|||||||
"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
|
"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
if (all
|
||||||
|
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
||||||
|
|
||||||
/* We were instructed to commit the whole transaction, or
|
/* We were instructed to commit the whole transaction, or
|
||||||
this is an SQL statement end and autocommit is on */
|
this is an SQL statement end and autocommit is on */
|
||||||
@ -1489,10 +1497,9 @@ innobase_commit(
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* The following defined-out code will be enabled later when we put the
|
||||||
don't delete it - it may be re-enabled later
|
MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
|
||||||
as an optimization for the most common case InnoDB+binlog
|
to work. */
|
||||||
*/
|
|
||||||
#if 0
|
#if 0
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
This is called when MySQL writes the binlog entry for the current
|
This is called when MySQL writes the binlog entry for the current
|
||||||
@ -1627,7 +1634,8 @@ innobase_rollback(
|
|||||||
row_unlock_table_autoinc_for_mysql(trx);
|
row_unlock_table_autoinc_for_mysql(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
if (all
|
||||||
|
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
||||||
|
|
||||||
error = trx_rollback_for_mysql(trx);
|
error = trx_rollback_for_mysql(trx);
|
||||||
trx->active_trans = 0;
|
trx->active_trans = 0;
|
||||||
@ -1686,6 +1694,7 @@ innobase_rollback_to_savepoint(
|
|||||||
ib_longlong mysql_binlog_cache_pos;
|
ib_longlong mysql_binlog_cache_pos;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
|
char name[64];
|
||||||
|
|
||||||
DBUG_ENTER("innobase_rollback_to_savepoint");
|
DBUG_ENTER("innobase_rollback_to_savepoint");
|
||||||
|
|
||||||
@ -1698,8 +1707,8 @@ innobase_rollback_to_savepoint(
|
|||||||
innobase_release_stat_resources(trx);
|
innobase_release_stat_resources(trx);
|
||||||
|
|
||||||
/* TODO: use provided savepoint data area to store savepoint data */
|
/* TODO: use provided savepoint data area to store savepoint data */
|
||||||
char name[64];
|
|
||||||
longlong2str((ulonglong)savepoint,name,36);
|
longlong2str((ulonglong)savepoint, name, 36);
|
||||||
|
|
||||||
error = trx_rollback_to_savepoint_for_mysql(trx, name,
|
error = trx_rollback_to_savepoint_for_mysql(trx, name,
|
||||||
&mysql_binlog_cache_pos);
|
&mysql_binlog_cache_pos);
|
||||||
@ -1708,26 +1717,27 @@ innobase_rollback_to_savepoint(
|
|||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
Release transaction savepoint name. */
|
Release transaction savepoint name. */
|
||||||
|
static
|
||||||
static int
|
int
|
||||||
innobase_release_savepoint(
|
innobase_release_savepoint(
|
||||||
/*===========================*/
|
/*=======================*/
|
||||||
/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
|
/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
|
||||||
no savepoint with the given name */
|
no savepoint with the given name */
|
||||||
THD* thd, /* in: handle to the MySQL thread of the user
|
THD* thd, /* in: handle to the MySQL thread of the user
|
||||||
whose transaction should be rolled back */
|
whose transaction should be rolled back */
|
||||||
void *savepoint) /* in: savepoint data */
|
void* savepoint) /* in: savepoint data */
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
|
char name[64];
|
||||||
|
|
||||||
DBUG_ENTER("innobase_release_savepoint");
|
DBUG_ENTER("innobase_release_savepoint");
|
||||||
|
|
||||||
trx = check_trx_exists(thd);
|
trx = check_trx_exists(thd);
|
||||||
|
|
||||||
/* TODO: use provided savepoint data area to store savepoint data */
|
/* TODO: use provided savepoint data area to store savepoint data */
|
||||||
char name[64];
|
|
||||||
longlong2str((ulonglong)savepoint,name,36);
|
longlong2str((ulonglong)savepoint, name, 36);
|
||||||
|
|
||||||
error = trx_release_savepoint_for_mysql(trx, name);
|
error = trx_release_savepoint_for_mysql(trx, name);
|
||||||
|
|
||||||
@ -1736,13 +1746,13 @@ innobase_release_savepoint(
|
|||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
Sets a transaction savepoint. */
|
Sets a transaction savepoint. */
|
||||||
|
static
|
||||||
static int
|
int
|
||||||
innobase_savepoint(
|
innobase_savepoint(
|
||||||
/*===============*/
|
/*===============*/
|
||||||
/* out: always 0, that is, always succeeds */
|
/* out: always 0, that is, always succeeds */
|
||||||
THD* thd, /* in: handle to the MySQL thread */
|
THD* thd, /* in: handle to the MySQL thread */
|
||||||
void *savepoint) /* in: savepoint data */
|
void* savepoint) /* in: savepoint data */
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
@ -1911,7 +1921,8 @@ ha_innobase::open(
|
|||||||
fields when packed actually became 1 byte longer, when we also
|
fields when packed actually became 1 byte longer, when we also
|
||||||
stored the string length as the first byte. */
|
stored the string length as the first byte. */
|
||||||
|
|
||||||
upd_and_key_val_buff_len = table->s->reclength + table->s->max_key_length
|
upd_and_key_val_buff_len =
|
||||||
|
table->s->reclength + table->s->max_key_length
|
||||||
+ MAX_REF_PARTS * 3;
|
+ MAX_REF_PARTS * 3;
|
||||||
if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
|
if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
|
||||||
&upd_buff, upd_and_key_val_buff_len,
|
&upd_buff, upd_and_key_val_buff_len,
|
||||||
@ -1963,7 +1974,8 @@ ha_innobase::open(
|
|||||||
|
|
||||||
innobase_prebuilt = row_create_prebuilt(ib_table);
|
innobase_prebuilt = row_create_prebuilt(ib_table);
|
||||||
|
|
||||||
((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len = table->s->reclength;
|
((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len =
|
||||||
|
table->s->reclength;
|
||||||
|
|
||||||
/* Looks like MySQL-3.23 sometimes has primary key number != 0 */
|
/* Looks like MySQL-3.23 sometimes has primary key number != 0 */
|
||||||
|
|
||||||
@ -1985,13 +1997,11 @@ ha_innobase::open(
|
|||||||
|
|
||||||
((row_prebuilt_t*)innobase_prebuilt)
|
((row_prebuilt_t*)innobase_prebuilt)
|
||||||
->clust_index_was_generated = FALSE;
|
->clust_index_was_generated = FALSE;
|
||||||
/*
|
/* MySQL allocates the buffer for ref. key_info->key_length
|
||||||
MySQL allocates the buffer for ref. key_info->key_length
|
includes space for all key columns + one byte for each column
|
||||||
includes space for all key columns + one byte for each column
|
that may be NULL. ref_length must be as exact as possible to
|
||||||
that may be NULL. ref_length must be as exact as possible to
|
save space, because all row reference buffers are allocated
|
||||||
save space, because all row reference buffers are allocated
|
based on ref_length. */
|
||||||
based on ref_length.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ref_length = table->key_info[primary_key].key_length;
|
ref_length = table->key_info[primary_key].key_length;
|
||||||
} else {
|
} else {
|
||||||
@ -2013,15 +2023,13 @@ ha_innobase::open(
|
|||||||
|
|
||||||
ref_length = DATA_ROW_ID_LEN;
|
ref_length = DATA_ROW_ID_LEN;
|
||||||
|
|
||||||
/*
|
/* If we automatically created the clustered index, then
|
||||||
If we automatically created the clustered index, then
|
MySQL does not know about it, and MySQL must NOT be aware
|
||||||
MySQL does not know about it, and MySQL must NOT be aware
|
of the index used on scan, to make it avoid checking if we
|
||||||
of the index used on scan, to make it avoid checking if we
|
update the column of the index. That is why we assert below
|
||||||
update the column of the index. That is why we assert below
|
that key_used_on_scan is the undefined value MAX_KEY.
|
||||||
that key_used_on_scan is the undefined value MAX_KEY.
|
The column is the row id in the automatical generation case,
|
||||||
The column is the row id in the automatical generation case,
|
and it will never be updated anyway. */
|
||||||
and it will never be updated anyway.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (key_used_on_scan != MAX_KEY) {
|
if (key_used_on_scan != MAX_KEY) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@ -2611,7 +2619,8 @@ ha_innobase::write_row(
|
|||||||
"InnoDB: Dump of 200 bytes around transaction.all: ",
|
"InnoDB: Dump of 200 bytes around transaction.all: ",
|
||||||
stderr);
|
stderr);
|
||||||
ut_print_buf(stderr,
|
ut_print_buf(stderr,
|
||||||
((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100, 200);
|
((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100,
|
||||||
|
200);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
ut_error;
|
ut_error;
|
||||||
}
|
}
|
||||||
@ -2646,7 +2655,7 @@ ha_innobase::write_row(
|
|||||||
src_table = lock_get_src_table(
|
src_table = lock_get_src_table(
|
||||||
prebuilt->trx, prebuilt->table, &mode);
|
prebuilt->trx, prebuilt->table, &mode);
|
||||||
if (!src_table) {
|
if (!src_table) {
|
||||||
no_commit:
|
no_commit:
|
||||||
/* Unknown situation: do not commit */
|
/* Unknown situation: do not commit */
|
||||||
/*
|
/*
|
||||||
ut_print_timestamp(stderr);
|
ut_print_timestamp(stderr);
|
||||||
@ -2669,6 +2678,7 @@ ha_innobase::write_row(
|
|||||||
} else {
|
} else {
|
||||||
/* Ensure that there are no other table locks than
|
/* Ensure that there are no other table locks than
|
||||||
LOCK_IX and LOCK_AUTO_INC on the destination table. */
|
LOCK_IX and LOCK_AUTO_INC on the destination table. */
|
||||||
|
|
||||||
if (!lock_is_table_exclusive(prebuilt->table,
|
if (!lock_is_table_exclusive(prebuilt->table,
|
||||||
prebuilt->trx)) {
|
prebuilt->trx)) {
|
||||||
goto no_commit;
|
goto no_commit;
|
||||||
@ -2746,11 +2756,11 @@ ha_innobase::write_row(
|
|||||||
|
|
||||||
if (error == DB_SUCCESS && auto_inc_used) {
|
if (error == DB_SUCCESS && auto_inc_used) {
|
||||||
|
|
||||||
/* Fetch the value that was set in the autoincrement field */
|
/* Fetch the value that was set in the autoincrement field */
|
||||||
|
|
||||||
auto_inc = table->next_number_field->val_int();
|
auto_inc = table->next_number_field->val_int();
|
||||||
|
|
||||||
if (auto_inc != 0) {
|
if (auto_inc != 0) {
|
||||||
/* This call will calculate the max of the current
|
/* This call will calculate the max of the current
|
||||||
value and the value supplied by the user and
|
value and the value supplied by the user and
|
||||||
update the counter accordingly */
|
update the counter accordingly */
|
||||||
@ -2762,15 +2772,15 @@ ha_innobase::write_row(
|
|||||||
The lock is released at each SQL statement's
|
The lock is released at each SQL statement's
|
||||||
end. */
|
end. */
|
||||||
|
|
||||||
error = row_lock_table_autoinc_for_mysql(prebuilt);
|
error = row_lock_table_autoinc_for_mysql(prebuilt);
|
||||||
|
|
||||||
if (error != DB_SUCCESS) {
|
if (error != DB_SUCCESS) {
|
||||||
|
error = convert_error_code_to_mysql(error,
|
||||||
error = convert_error_code_to_mysql(error, user_thd);
|
user_thd);
|
||||||
goto func_exit;
|
goto func_exit;
|
||||||
}
|
}
|
||||||
dict_table_autoinc_update(prebuilt->table, auto_inc);
|
dict_table_autoinc_update(prebuilt->table, auto_inc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
innodb_srv_conc_exit_innodb(prebuilt->trx);
|
innodb_srv_conc_exit_innodb(prebuilt->trx);
|
||||||
@ -2785,7 +2795,6 @@ func_exit:
|
|||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************
|
/******************************************************************
|
||||||
Converts field data for storage in an InnoDB update vector. */
|
Converts field data for storage in an InnoDB update vector. */
|
||||||
inline
|
inline
|
||||||
@ -3071,9 +3080,6 @@ ha_innobase::unlock_row(void)
|
|||||||
|
|
||||||
DBUG_ENTER("ha_innobase::unlock_row");
|
DBUG_ENTER("ha_innobase::unlock_row");
|
||||||
|
|
||||||
ut_ad(prebuilt->trx ==
|
|
||||||
(trx_t*) current_thd->transaction.all.innobase_tid);
|
|
||||||
|
|
||||||
if (last_query_id != user_thd->query_id) {
|
if (last_query_id != user_thd->query_id) {
|
||||||
ut_print_timestamp(stderr);
|
ut_print_timestamp(stderr);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@ -4512,10 +4518,10 @@ ha_innobase::records_in_range(
|
|||||||
dict_index_t* index;
|
dict_index_t* index;
|
||||||
mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
|
mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
|
||||||
table->s->reclength
|
table->s->reclength
|
||||||
+ table->s->max_key_length + 100,
|
+ table->s->max_key_length + 100,
|
||||||
MYF(MY_WME));
|
MYF(MY_WME));
|
||||||
ulint buff2_len = table->s->reclength
|
ulint buff2_len = table->s->reclength
|
||||||
+ table->s->max_key_length + 100;
|
+ table->s->max_key_length + 100;
|
||||||
dtuple_t* range_start;
|
dtuple_t* range_start;
|
||||||
dtuple_t* range_end;
|
dtuple_t* range_end;
|
||||||
ib_longlong n_rows;
|
ib_longlong n_rows;
|
||||||
@ -4672,21 +4678,27 @@ ha_innobase::read_time(
|
|||||||
ha_rows total_rows;
|
ha_rows total_rows;
|
||||||
double time_for_scan;
|
double time_for_scan;
|
||||||
|
|
||||||
if (index != table->s->primary_key)
|
if (index != table->s->primary_key) {
|
||||||
return handler::read_time(index, ranges, rows); // Not clustered
|
/* Not clustered */
|
||||||
|
return(handler::read_time(index, ranges, rows));
|
||||||
|
}
|
||||||
|
|
||||||
if (rows <= 2)
|
if (rows <= 2) {
|
||||||
return (double) rows;
|
|
||||||
|
return((double) rows);
|
||||||
|
}
|
||||||
|
|
||||||
/* Assume that the read time is proportional to the scan time for all
|
/* Assume that the read time is proportional to the scan time for all
|
||||||
rows + at most one seek per range. */
|
rows + at most one seek per range. */
|
||||||
|
|
||||||
time_for_scan = scan_time();
|
time_for_scan = scan_time();
|
||||||
|
|
||||||
if ((total_rows = estimate_rows_upper_bound()) < rows)
|
if ((total_rows = estimate_rows_upper_bound()) < rows) {
|
||||||
return time_for_scan;
|
|
||||||
|
|
||||||
return (ranges + (double) rows / (double) total_rows * time_for_scan);
|
return(time_for_scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(ranges + (double) rows / (double) total_rows * time_for_scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@ -5104,7 +5116,7 @@ ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
|
|||||||
tmp_buff, i, 1);
|
tmp_buff, i, 1);
|
||||||
tmp_buff+= i + 1;
|
tmp_buff+= i + 1;
|
||||||
f_key_info.referenced_table= make_lex_string(thd, 0,
|
f_key_info.referenced_table= make_lex_string(thd, 0,
|
||||||
tmp_buff, strlen(tmp_buff), 1);
|
tmp_buff, strlen(tmp_buff), 1);
|
||||||
|
|
||||||
for (i= 0;;)
|
for (i= 0;;)
|
||||||
{
|
{
|
||||||
@ -5622,7 +5634,6 @@ innodb_export_status(void)
|
|||||||
srv_export_innodb_status();
|
srv_export_innodb_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
|
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
|
||||||
Monitor to the client. */
|
Monitor to the client. */
|
||||||
@ -5634,6 +5645,8 @@ innodb_show_status(
|
|||||||
{
|
{
|
||||||
Protocol *protocol= thd->protocol;
|
Protocol *protocol= thd->protocol;
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
|
long flen;
|
||||||
|
char* str;
|
||||||
|
|
||||||
DBUG_ENTER("innodb_show_status");
|
DBUG_ENTER("innodb_show_status");
|
||||||
|
|
||||||
@ -5650,14 +5663,13 @@ innodb_show_status(
|
|||||||
|
|
||||||
/* We let the InnoDB Monitor to output at most 64000 bytes of text. */
|
/* We let the InnoDB Monitor to output at most 64000 bytes of text. */
|
||||||
|
|
||||||
long flen;
|
|
||||||
char* str;
|
|
||||||
|
|
||||||
mutex_enter_noninline(&srv_monitor_file_mutex);
|
mutex_enter_noninline(&srv_monitor_file_mutex);
|
||||||
rewind(srv_monitor_file);
|
rewind(srv_monitor_file);
|
||||||
|
|
||||||
srv_printf_innodb_monitor(srv_monitor_file);
|
srv_printf_innodb_monitor(srv_monitor_file);
|
||||||
flen = ftell(srv_monitor_file);
|
flen = ftell(srv_monitor_file);
|
||||||
os_file_set_eof(srv_monitor_file);
|
os_file_set_eof(srv_monitor_file);
|
||||||
|
|
||||||
if (flen < 0) {
|
if (flen < 0) {
|
||||||
flen = 0;
|
flen = 0;
|
||||||
} else if (flen > 64000 - 1) {
|
} else if (flen > 64000 - 1) {
|
||||||
@ -5667,10 +5679,10 @@ innodb_show_status(
|
|||||||
/* allocate buffer for the string, and
|
/* allocate buffer for the string, and
|
||||||
read the contents of the temporary file */
|
read the contents of the temporary file */
|
||||||
|
|
||||||
if (!(str = my_malloc(flen + 1, MYF(0))))
|
if (!(str = my_malloc(flen + 1, MYF(0)))) {
|
||||||
{
|
mutex_exit_noninline(&srv_monitor_file_mutex);
|
||||||
mutex_exit_noninline(&srv_monitor_file_mutex);
|
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
rewind(srv_monitor_file);
|
rewind(srv_monitor_file);
|
||||||
@ -5684,7 +5696,6 @@ innodb_show_status(
|
|||||||
|
|
||||||
if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
|
if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
|
||||||
Protocol::SEND_EOF)) {
|
Protocol::SEND_EOF)) {
|
||||||
|
|
||||||
my_free(str, MYF(0));
|
my_free(str, MYF(0));
|
||||||
|
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
@ -5694,10 +5705,12 @@ innodb_show_status(
|
|||||||
protocol->store(str, flen, system_charset_info);
|
protocol->store(str, flen, system_charset_info);
|
||||||
my_free(str, MYF(0));
|
my_free(str, MYF(0));
|
||||||
|
|
||||||
if (protocol->write())
|
if (protocol->write()) {
|
||||||
DBUG_RETURN(TRUE);
|
|
||||||
|
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
send_eof(thd);
|
send_eof(thd);
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6091,7 +6104,7 @@ ha_innobase::get_auto_increment()
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
||||||
return(~(ulonglong) 0);
|
return(~(ulonglong) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return((ulonglong) nr);
|
return((ulonglong) nr);
|
||||||
@ -6113,7 +6126,8 @@ ha_innobase::cmp_ref(
|
|||||||
|
|
||||||
/* Do type-aware comparison of Primary Key members. PK members
|
/* Do type-aware comparison of Primary Key members. PK members
|
||||||
are always NOT NULL, so no checks for NULL are performed */
|
are always NOT NULL, so no checks for NULL are performed */
|
||||||
KEY_PART_INFO *key_part= table->key_info[table->s->primary_key].key_part;
|
KEY_PART_INFO *key_part=
|
||||||
|
table->key_info[table->s->primary_key].key_part;
|
||||||
KEY_PART_INFO *key_part_end=
|
KEY_PART_INFO *key_part_end=
|
||||||
key_part + table->key_info[table->s->primary_key].key_parts;
|
key_part + table->key_info[table->s->primary_key].key_parts;
|
||||||
for (; key_part != key_part_end; ++key_part) {
|
for (; key_part != key_part_end; ++key_part) {
|
||||||
@ -6258,19 +6272,21 @@ innobase_query_is_update(void)
|
|||||||
|
|
||||||
thd = (THD *)innobase_current_thd();
|
thd = (THD *)innobase_current_thd();
|
||||||
|
|
||||||
if ( thd->lex->sql_command == SQLCOM_REPLACE ||
|
if (thd->lex->sql_command == SQLCOM_REPLACE ||
|
||||||
thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
|
thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
|
||||||
( thd->lex->sql_command == SQLCOM_LOAD &&
|
(thd->lex->sql_command == SQLCOM_LOAD &&
|
||||||
thd->lex->duplicates == DUP_REPLACE )) {
|
thd->lex->duplicates == DUP_REPLACE)) {
|
||||||
return true;
|
|
||||||
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( thd->lex->sql_command == SQLCOM_INSERT &&
|
if (thd->lex->sql_command == SQLCOM_INSERT &&
|
||||||
thd->lex->duplicates == DUP_UPDATE ) {
|
thd->lex->duplicates == DUP_UPDATE) {
|
||||||
return true;
|
|
||||||
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6306,11 +6322,20 @@ innobase_xa_prepare(
|
|||||||
"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
|
"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
if (all
|
||||||
|
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
||||||
|
|
||||||
/* We were instructed to prepare the whole transaction, or
|
/* We were instructed to prepare the whole transaction, or
|
||||||
this is an SQL statement end and autocommit is on */
|
this is an SQL statement end and autocommit is on */
|
||||||
|
|
||||||
|
/* If there is no active InnoDB transaction,
|
||||||
|
trx_prepare_for_mysql() will (temporarily) start one */
|
||||||
|
|
||||||
|
if (trx->active_trans == 0) {
|
||||||
|
|
||||||
|
trx->active_trans = 1;
|
||||||
|
}
|
||||||
|
|
||||||
error = trx_prepare_for_mysql(trx);
|
error = trx_prepare_for_mysql(trx);
|
||||||
} else {
|
} else {
|
||||||
/* We just mark the SQL statement ended and do not do a
|
/* We just mark the SQL statement ended and do not do a
|
||||||
@ -6349,10 +6374,11 @@ innobase_xa_recover(
|
|||||||
uint len) /* in: number of slots in xid_list */
|
uint len) /* in: number of slots in xid_list */
|
||||||
{
|
{
|
||||||
if (len == 0 || xid_list == NULL) {
|
if (len == 0 || xid_list == NULL) {
|
||||||
return 0;
|
|
||||||
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (trx_recover_for_mysql(xid_list, len));
|
return(trx_recover_for_mysql(xid_list, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
@ -6363,7 +6389,7 @@ int
|
|||||||
innobase_commit_by_xid(
|
innobase_commit_by_xid(
|
||||||
/*===================*/
|
/*===================*/
|
||||||
/* out: 0 or error number */
|
/* out: 0 or error number */
|
||||||
XID* xid) /* in: X/Open XA Transaction Identification */
|
XID* xid) /* in: X/Open XA transaction identification */
|
||||||
{
|
{
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
|
|
||||||
@ -6386,7 +6412,7 @@ int
|
|||||||
innobase_rollback_by_xid(
|
innobase_rollback_by_xid(
|
||||||
/*=====================*/
|
/*=====================*/
|
||||||
/* out: 0 or error number */
|
/* out: 0 or error number */
|
||||||
XID *xid) /* in : X/Open XA Transaction Idenfification */
|
XID *xid) /* in: X/Open XA transaction idenfification */
|
||||||
{
|
{
|
||||||
trx_t* trx;
|
trx_t* trx;
|
||||||
|
|
||||||
|
255
sql/item.cc
255
sql/item.cc
@ -3513,6 +3513,9 @@ Item_ref::Item_ref(Item **item, const char *table_name_par,
|
|||||||
Item_field::fix_fields, here we first search the SELECT and GROUP BY
|
Item_field::fix_fields, here we first search the SELECT and GROUP BY
|
||||||
clauses, and then we search the FROM clause.
|
clauses, and then we search the FROM clause.
|
||||||
|
|
||||||
|
POSTCONDITION
|
||||||
|
Item_ref::ref is 0 or points to a valid item
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
TRUE if error
|
TRUE if error
|
||||||
FALSE on success
|
FALSE on success
|
||||||
@ -3534,6 +3537,19 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
|
|||||||
|
|
||||||
if (ref == not_found_item) /* This reference was not resolved. */
|
if (ref == not_found_item) /* This reference was not resolved. */
|
||||||
{
|
{
|
||||||
|
TABLE_LIST *table_list;
|
||||||
|
Field *from_field;
|
||||||
|
SELECT_LEX *last;
|
||||||
|
ref= 0;
|
||||||
|
|
||||||
|
if (!outer_sel || (current_sel->master_unit()->first_select()->linkage ==
|
||||||
|
DERIVED_TABLE_TYPE))
|
||||||
|
{
|
||||||
|
/* The current reference cannot be resolved in this query. */
|
||||||
|
my_error(ER_BAD_FIELD_ERROR,MYF(0),
|
||||||
|
this->full_name(), current_thd->where);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
If there is an outer select, and it is not a derived table (which do
|
If there is an outer select, and it is not a derived table (which do
|
||||||
not support the use of outer fields for now), try to resolve this
|
not support the use of outer fields for now), try to resolve this
|
||||||
@ -3543,159 +3559,133 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
|
|||||||
subselects may contain columns with the same names. The subselects are
|
subselects may contain columns with the same names. The subselects are
|
||||||
searched starting from the innermost.
|
searched starting from the innermost.
|
||||||
*/
|
*/
|
||||||
if (outer_sel && (current_sel->master_unit()->first_select()->linkage !=
|
from_field= (Field*) not_found_field;
|
||||||
DERIVED_TABLE_TYPE))
|
last= 0;
|
||||||
|
|
||||||
|
/* The following loop will always be excuted at least once */
|
||||||
|
for ( ; outer_sel ;
|
||||||
|
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
|
||||||
{
|
{
|
||||||
TABLE_LIST *table_list;
|
last= outer_sel;
|
||||||
Field *from_field= (Field*) not_found_field;
|
Item_subselect *prev_subselect_item= prev_unit->item;
|
||||||
SELECT_LEX *last= 0;
|
|
||||||
|
|
||||||
for ( ; outer_sel ;
|
/* Search in the SELECT and GROUP lists of the outer select. */
|
||||||
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
|
if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE)
|
||||||
{
|
{
|
||||||
last= outer_sel;
|
if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel)))
|
||||||
Item_subselect *prev_subselect_item= prev_unit->item;
|
return TRUE; /* Some error occurred (e.g. ambiguous names). */
|
||||||
|
if (ref != not_found_item)
|
||||||
/* Search in the SELECT and GROUP lists of the outer select. */
|
|
||||||
if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE)
|
|
||||||
{
|
{
|
||||||
if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel)))
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
return TRUE; /* Some error occurred (e.g. ambiguous names). */
|
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
||||||
if (ref != not_found_item)
|
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
||||||
{
|
break;
|
||||||
DBUG_ASSERT(*ref && (*ref)->fixed);
|
|
||||||
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
|
||||||
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search in the tables of the FROM clause of the outer select. */
|
|
||||||
table_list= outer_sel->get_table_list();
|
|
||||||
if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
|
|
||||||
/*
|
|
||||||
It is a primary INSERT st_select_lex => do not resolve against the
|
|
||||||
first table.
|
|
||||||
*/
|
|
||||||
table_list= table_list->next_local;
|
|
||||||
|
|
||||||
place= prev_subselect_item->parsing_place;
|
|
||||||
/*
|
/*
|
||||||
Check table fields only if the subquery is used somewhere out of
|
Set ref to 0 to ensure that we get an error in case we replaced
|
||||||
HAVING or the outer SELECT does not use grouping (i.e. tables are
|
this item with another item and still use this item in some
|
||||||
accessible).
|
other place of the parse tree.
|
||||||
TODO:
|
|
||||||
Here we could first find the field anyway, and then test this
|
|
||||||
condition, so that we can give a better error message -
|
|
||||||
ER_WRONG_FIELD_WITH_GROUP, instead of the less informative
|
|
||||||
ER_BAD_FIELD_ERROR which we produce now.
|
|
||||||
*/
|
*/
|
||||||
if ((place != IN_HAVING ||
|
ref= 0;
|
||||||
(!outer_sel->with_sum_func &&
|
|
||||||
outer_sel->group_list.elements == 0)))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
In case of view, find_field_in_tables() write pointer to view
|
|
||||||
field expression to 'reference', i.e. it substitute that
|
|
||||||
expression instead of this Item_ref
|
|
||||||
*/
|
|
||||||
if ((from_field= find_field_in_tables(thd, this, table_list,
|
|
||||||
reference,
|
|
||||||
IGNORE_EXCEPT_NON_UNIQUE,
|
|
||||||
TRUE)) !=
|
|
||||||
not_found_field)
|
|
||||||
{
|
|
||||||
if (from_field != view_ref_found)
|
|
||||||
{
|
|
||||||
prev_subselect_item->used_tables_cache|= from_field->table->map;
|
|
||||||
prev_subselect_item->const_item_cache= 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Item::Type type= (*reference)->type();
|
|
||||||
prev_subselect_item->used_tables_cache|=
|
|
||||||
(*reference)->used_tables();
|
|
||||||
prev_subselect_item->const_item_cache&=
|
|
||||||
(*reference)->const_item();
|
|
||||||
DBUG_ASSERT((*reference)->type() == REF_ITEM);
|
|
||||||
mark_as_dependent(thd, last, current_sel, this,
|
|
||||||
((type == REF_ITEM || type == FIELD_ITEM) ?
|
|
||||||
(Item_ident*) (*reference) :
|
|
||||||
0));
|
|
||||||
/*
|
|
||||||
view reference found, we substituted it instead of this
|
|
||||||
Item, so can quit
|
|
||||||
*/
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reference is not found => depend on outer (or just error). */
|
|
||||||
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
|
|
||||||
prev_subselect_item->const_item_cache= 0;
|
|
||||||
|
|
||||||
if (outer_sel->master_unit()->first_select()->linkage ==
|
|
||||||
DERIVED_TABLE_TYPE)
|
|
||||||
break; /* Do not consider derived tables. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_ASSERT(ref != 0);
|
/* Search in the tables of the FROM clause of the outer select. */
|
||||||
if (!from_field)
|
table_list= outer_sel->get_table_list();
|
||||||
return TRUE;
|
if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
|
||||||
if (ref == not_found_item && from_field == not_found_field)
|
|
||||||
{
|
|
||||||
my_error(ER_BAD_FIELD_ERROR, MYF(0),
|
|
||||||
this->full_name(), current_thd->where);
|
|
||||||
ref= 0; // Safety
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (from_field != not_found_field)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Set ref to 0 as we are replacing this item with the found item and
|
It is a primary INSERT st_select_lex => do not resolve against
|
||||||
this will ensure we get an error if this item would be used
|
the first table.
|
||||||
elsewhere
|
|
||||||
*/
|
*/
|
||||||
ref= 0; // Safety
|
table_list= table_list->next_local;
|
||||||
if (from_field != view_ref_found)
|
}
|
||||||
|
|
||||||
|
place= prev_subselect_item->parsing_place;
|
||||||
|
/*
|
||||||
|
Check table fields only if the subquery is used somewhere out of
|
||||||
|
HAVING or the outer SELECT does not use grouping (i.e. tables are
|
||||||
|
accessible).
|
||||||
|
TODO:
|
||||||
|
Here we could first find the field anyway, and then test this
|
||||||
|
condition, so that we can give a better error message -
|
||||||
|
ER_WRONG_FIELD_WITH_GROUP, instead of the less informative
|
||||||
|
ER_BAD_FIELD_ERROR which we produce now.
|
||||||
|
*/
|
||||||
|
if ((place != IN_HAVING ||
|
||||||
|
(!outer_sel->with_sum_func &&
|
||||||
|
outer_sel->group_list.elements == 0)))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
In case of view, find_field_in_tables() write pointer to view
|
||||||
|
field expression to 'reference', i.e. it substitute that
|
||||||
|
expression instead of this Item_ref
|
||||||
|
*/
|
||||||
|
from_field= find_field_in_tables(thd, this, table_list,
|
||||||
|
reference,
|
||||||
|
IGNORE_EXCEPT_NON_UNIQUE,
|
||||||
|
TRUE);
|
||||||
|
if (! from_field)
|
||||||
|
return TRUE;
|
||||||
|
if (from_field == view_ref_found)
|
||||||
{
|
{
|
||||||
Item_field* fld;
|
Item::Type type= (*reference)->type();
|
||||||
if (!(fld= new Item_field(from_field)))
|
prev_subselect_item->used_tables_cache|=
|
||||||
return TRUE;
|
(*reference)->used_tables();
|
||||||
thd->change_item_tree(reference, fld);
|
prev_subselect_item->const_item_cache&=
|
||||||
mark_as_dependent(thd, last, thd->lex->current_select, this, fld);
|
(*reference)->const_item();
|
||||||
|
DBUG_ASSERT((*reference)->type() == REF_ITEM);
|
||||||
|
mark_as_dependent(thd, last, current_sel, this,
|
||||||
|
((type == REF_ITEM || type == FIELD_ITEM) ?
|
||||||
|
(Item_ident*) (*reference) :
|
||||||
|
0));
|
||||||
|
/*
|
||||||
|
view reference found, we substituted it instead of this
|
||||||
|
Item, so can quit
|
||||||
|
*/
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
/*
|
if (from_field != not_found_field)
|
||||||
We can leave expression substituted from view for next PS/SP
|
{
|
||||||
re-execution (i.e. do not register this substitution for reverting
|
prev_subselect_item->used_tables_cache|= from_field->table->map;
|
||||||
on cleanup() (register_item_tree_changing())), because this subtree
|
prev_subselect_item->const_item_cache= 0;
|
||||||
will be fix_field'ed during setup_tables()->setup_ancestor()
|
break;
|
||||||
(i.e. before all other expressions of query, and references on
|
}
|
||||||
tables which do not present in query will not make problems.
|
}
|
||||||
|
DBUG_ASSERT(from_field == not_found_field);
|
||||||
|
|
||||||
Also we suppose that view can't be changed during PS/SP life.
|
/* Reference is not found => depend on outer (or just error). */
|
||||||
*/
|
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
|
||||||
}
|
prev_subselect_item->const_item_cache= 0;
|
||||||
else
|
|
||||||
{
|
if (outer_sel->master_unit()->first_select()->linkage ==
|
||||||
/* Should be checked in resolve_ref_in_select_and_group(). */
|
DERIVED_TABLE_TYPE)
|
||||||
DBUG_ASSERT(*ref && (*ref)->fixed);
|
break; /* Do not consider derived tables. */
|
||||||
mark_as_dependent(thd, last, current_sel, this, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
DBUG_ASSERT(from_field != 0 && from_field != view_ref_found);
|
||||||
|
if (from_field != not_found_field)
|
||||||
{
|
{
|
||||||
/* The current reference cannot be resolved in this query. */
|
Item_field* fld;
|
||||||
my_error(ER_BAD_FIELD_ERROR,MYF(0),
|
if (!(fld= new Item_field(from_field)))
|
||||||
|
return TRUE;
|
||||||
|
thd->change_item_tree(reference, fld);
|
||||||
|
mark_as_dependent(thd, last, thd->lex->current_select, this, fld);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (ref == 0)
|
||||||
|
{
|
||||||
|
/* The item was not a table field and not a reference */
|
||||||
|
my_error(ER_BAD_FIELD_ERROR, MYF(0),
|
||||||
this->full_name(), current_thd->where);
|
this->full_name(), current_thd->where);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
/* Should be checked in resolve_ref_in_select_and_group(). */
|
||||||
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
|
mark_as_dependent(thd, last, current_sel, this, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBUG_ASSERT(*ref);
|
||||||
/*
|
/*
|
||||||
Check if this is an incorrect reference in a group function or forward
|
Check if this is an incorrect reference in a group function or forward
|
||||||
reference. Do not issue an error if this is an unnamed reference inside an
|
reference. Do not issue an error if this is an unnamed reference inside an
|
||||||
@ -3716,11 +3706,12 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
|
|||||||
|
|
||||||
set_properties();
|
set_properties();
|
||||||
|
|
||||||
if (ref && (*ref)->check_cols(1))
|
if ((*ref)->check_cols(1))
|
||||||
return 1;
|
return TRUE;
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Item_ref::set_properties()
|
void Item_ref::set_properties()
|
||||||
{
|
{
|
||||||
max_length= (*ref)->max_length;
|
max_length= (*ref)->max_length;
|
||||||
|
@ -283,7 +283,7 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
|
|||||||
comparators= 0;
|
comparators= 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!(comparators= (Arg_comparator *) sql_alloc(sizeof(Arg_comparator)*n)))
|
if (!(comparators= new Arg_comparator[n]))
|
||||||
return 1;
|
return 1;
|
||||||
for (uint i=0; i < n; i++)
|
for (uint i=0; i < n; i++)
|
||||||
{
|
{
|
||||||
@ -1825,6 +1825,12 @@ in_row::in_row(uint elements, Item * item)
|
|||||||
size= sizeof(cmp_item_row);
|
size= sizeof(cmp_item_row);
|
||||||
compare= (qsort2_cmp) cmp_row;
|
compare= (qsort2_cmp) cmp_row;
|
||||||
tmp.store_value(item);
|
tmp.store_value(item);
|
||||||
|
/*
|
||||||
|
We need to reset these as otherwise we will call sort() with
|
||||||
|
uninitialized (even if not used) elements
|
||||||
|
*/
|
||||||
|
used_count= elements;
|
||||||
|
collation= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
in_row::~in_row()
|
in_row::~in_row()
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
|
#include "sql_select.h"
|
||||||
|
|
||||||
Item_sum::Item_sum(List<Item> &list)
|
Item_sum::Item_sum(List<Item> &list)
|
||||||
:arg_count(list.elements)
|
:arg_count(list.elements)
|
||||||
@ -303,6 +304,21 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
|
||||||
|
uint convert_blob_length)
|
||||||
|
{
|
||||||
|
if (args[0]->type() == Item::FIELD_ITEM)
|
||||||
|
{
|
||||||
|
Field *field= ((Item_field*) args[0])->field;
|
||||||
|
|
||||||
|
if ((field= create_tmp_field_from_field(current_thd, field, this, table,
|
||||||
|
0, convert_blob_length)))
|
||||||
|
field->flags&= ~NOT_NULL_FLAG;
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
return Item_sum::create_tmp_field(group, table, convert_blob_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
** reset and add of sum_func
|
** reset and add of sum_func
|
||||||
@ -2075,8 +2091,6 @@ my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf)
|
|||||||
** COUNT(DISTINCT ...)
|
** COUNT(DISTINCT ...)
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "sql_select.h"
|
|
||||||
|
|
||||||
int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
|
int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
|
||||||
{
|
{
|
||||||
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
|
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
|
||||||
|
@ -94,7 +94,6 @@ public:
|
|||||||
Item *get_tmp_table_item(THD *thd);
|
Item *get_tmp_table_item(THD *thd);
|
||||||
virtual Field *create_tmp_field(bool group, TABLE *table,
|
virtual Field *create_tmp_field(bool group, TABLE *table,
|
||||||
uint convert_blob_length);
|
uint convert_blob_length);
|
||||||
|
|
||||||
bool walk (Item_processor processor, byte *argument);
|
bool walk (Item_processor processor, byte *argument);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -525,6 +524,8 @@ protected:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
bool any_value() { return was_values; }
|
bool any_value() { return was_values; }
|
||||||
void no_rows_in_result();
|
void no_rows_in_result();
|
||||||
|
Field *create_tmp_field(bool group, TABLE *table,
|
||||||
|
uint convert_blob_length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -552,6 +552,7 @@ extern "C" pthread_handler_decl(handle_slave,arg);
|
|||||||
static ulong find_bit_type(const char *x, TYPELIB *bit_lib);
|
static ulong find_bit_type(const char *x, TYPELIB *bit_lib);
|
||||||
static void clean_up(bool print_message);
|
static void clean_up(bool print_message);
|
||||||
static void clean_up_mutexes(void);
|
static void clean_up_mutexes(void);
|
||||||
|
static void wait_for_signal_thread_to_end(void);
|
||||||
static int test_if_case_insensitive(const char *dir_name);
|
static int test_if_case_insensitive(const char *dir_name);
|
||||||
static void create_pid_file();
|
static void create_pid_file();
|
||||||
|
|
||||||
@ -943,6 +944,7 @@ extern "C" void unireg_abort(int exit_code)
|
|||||||
sql_print_error("Aborting\n");
|
sql_print_error("Aborting\n");
|
||||||
clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
|
clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
|
||||||
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
|
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
|
||||||
|
wait_for_signal_thread_to_end();
|
||||||
clean_up_mutexes();
|
clean_up_mutexes();
|
||||||
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
||||||
exit(exit_code); /* purecov: inspected */
|
exit(exit_code); /* purecov: inspected */
|
||||||
@ -1051,6 +1053,29 @@ void clean_up(bool print_message)
|
|||||||
} /* clean_up */
|
} /* clean_up */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is mainly needed when running with purify, but it's still nice to
|
||||||
|
know that all child threads have died when mysqld exits
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void wait_for_signal_thread_to_end()
|
||||||
|
{
|
||||||
|
#ifndef __NETWARE__
|
||||||
|
uint i;
|
||||||
|
/*
|
||||||
|
Wait up to 10 seconds for signal thread to die. We use this mainly to
|
||||||
|
avoid getting warnings that my_thread_end has not been called
|
||||||
|
*/
|
||||||
|
for (i= 0 ; i < 100 && signal_thread_in_use; i++)
|
||||||
|
{
|
||||||
|
if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
|
||||||
|
break;
|
||||||
|
my_sleep(100); // Give it time to die
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void clean_up_mutexes()
|
static void clean_up_mutexes()
|
||||||
{
|
{
|
||||||
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
|
(void) pthread_mutex_destroy(&LOCK_mysql_create_db);
|
||||||
@ -2146,6 +2171,7 @@ extern "C" void *signal_hand(void *arg __attribute__((unused)))
|
|||||||
while ((error=my_sigwait(&set,&sig)) == EINTR) ;
|
while ((error=my_sigwait(&set,&sig)) == EINTR) ;
|
||||||
if (cleanup_done)
|
if (cleanup_done)
|
||||||
{
|
{
|
||||||
|
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
|
||||||
my_thread_end();
|
my_thread_end();
|
||||||
signal_thread_in_use= 0;
|
signal_thread_in_use= 0;
|
||||||
pthread_exit(0); // Safety
|
pthread_exit(0); // Safety
|
||||||
@ -3176,21 +3202,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
|
|||||||
CloseHandle(hEventShutdown);
|
CloseHandle(hEventShutdown);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifndef __NETWARE__
|
wait_for_signal_thread_to_end();
|
||||||
{
|
|
||||||
uint i;
|
|
||||||
/*
|
|
||||||
Wait up to 10 seconds for signal thread to die. We use this mainly to
|
|
||||||
avoid getting warnings that my_thread_end has not been called
|
|
||||||
*/
|
|
||||||
for (i= 0 ; i < 100 && signal_thread_in_use; i++)
|
|
||||||
{
|
|
||||||
if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
|
|
||||||
break;
|
|
||||||
my_sleep(100); // Give it time to die
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
clean_up_mutexes();
|
clean_up_mutexes();
|
||||||
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
||||||
|
|
||||||
@ -4256,7 +4268,7 @@ struct my_option my_long_options[] =
|
|||||||
(gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count,
|
(gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count,
|
||||||
0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
{"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax.", 0, 0, 0,
|
{"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'.", 0, 0, 0,
|
||||||
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{"auto-increment-increment", OPT_AUTO_INCREMENT,
|
{"auto-increment-increment", OPT_AUTO_INCREMENT,
|
||||||
"Auto-increment columns are incremented by this",
|
"Auto-increment columns are incremented by this",
|
||||||
|
@ -6878,6 +6878,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
|
|||||||
SEL_ARG *cur_index_tree= NULL;
|
SEL_ARG *cur_index_tree= NULL;
|
||||||
ha_rows cur_quick_prefix_records= 0;
|
ha_rows cur_quick_prefix_records= 0;
|
||||||
uint cur_param_idx;
|
uint cur_param_idx;
|
||||||
|
key_map cur_used_key_parts;
|
||||||
|
|
||||||
for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
|
for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
|
||||||
cur_index_info++, cur_index++)
|
cur_index_info++, cur_index++)
|
||||||
@ -6925,17 +6926,25 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
|
|||||||
else if (join->select_distinct)
|
else if (join->select_distinct)
|
||||||
{
|
{
|
||||||
select_items_it.rewind();
|
select_items_it.rewind();
|
||||||
|
cur_used_key_parts.clear_all();
|
||||||
while ((item= select_items_it++))
|
while ((item= select_items_it++))
|
||||||
{
|
{
|
||||||
item_field= (Item_field*) item; /* (SA5) already checked above. */
|
item_field= (Item_field*) item; /* (SA5) already checked above. */
|
||||||
/* Find the order of the key part in the index. */
|
/* Find the order of the key part in the index. */
|
||||||
key_part_nr= get_field_keypart(cur_index_info, item_field->field);
|
key_part_nr= get_field_keypart(cur_index_info, item_field->field);
|
||||||
|
/*
|
||||||
|
Check if this attribute was already present in the select list.
|
||||||
|
If it was present, then its corresponding key part was alredy used.
|
||||||
|
*/
|
||||||
|
if (cur_used_key_parts.is_set(key_part_nr))
|
||||||
|
continue;
|
||||||
if (key_part_nr < 1 || key_part_nr > join->fields_list.elements)
|
if (key_part_nr < 1 || key_part_nr > join->fields_list.elements)
|
||||||
goto next_index;
|
goto next_index;
|
||||||
cur_part= cur_index_info->key_part + key_part_nr - 1;
|
cur_part= cur_index_info->key_part + key_part_nr - 1;
|
||||||
cur_group_prefix_len+= cur_part->store_length;
|
cur_group_prefix_len+= cur_part->store_length;
|
||||||
|
cur_used_key_parts.set_bit(key_part_nr);
|
||||||
|
++cur_group_key_parts;
|
||||||
}
|
}
|
||||||
cur_group_key_parts= join->fields_list.elements;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
DBUG_ASSERT(FALSE);
|
DBUG_ASSERT(FALSE);
|
||||||
|
@ -5326,3 +5326,5 @@ ER_PROC_AUTO_REVOKE_FAIL
|
|||||||
eng "Failed to revoke all privileges to dropped routine"
|
eng "Failed to revoke all privileges to dropped routine"
|
||||||
ER_DATA_TOO_LONG 22001
|
ER_DATA_TOO_LONG 22001
|
||||||
eng "Data too long for column '%s' at row %ld"
|
eng "Data too long for column '%s' at row %ld"
|
||||||
|
ER_SP_BAD_SQLSTATE 42000
|
||||||
|
eng "Bad SQLSTATE: '%s'"
|
||||||
|
@ -26,6 +26,30 @@
|
|||||||
#include "sp_pcontext.h"
|
#include "sp_pcontext.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sanity check for SQLSTATEs. Will not check if it's really an existing
|
||||||
|
* state (there are just too many), but will check length and bad characters.
|
||||||
|
* Returns TRUE if it's ok, FALSE if it's bad.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
sp_cond_check(LEX_STRING *sqlstate)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
if (sqlstate->length != 5)
|
||||||
|
return FALSE;
|
||||||
|
for (p= sqlstate->str, i= 0 ; i < 5 ; i++)
|
||||||
|
{
|
||||||
|
char c = p[i];
|
||||||
|
|
||||||
|
if ((c < '0' || '9' < c) &&
|
||||||
|
(c < 'A' || 'Z' < c))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
sp_pcontext::sp_pcontext(sp_pcontext *prev)
|
sp_pcontext::sp_pcontext(sp_pcontext *prev)
|
||||||
: Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0),
|
: Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0),
|
||||||
m_handlers(0), m_parent(prev)
|
m_handlers(0), m_parent(prev)
|
||||||
|
@ -60,6 +60,12 @@ typedef struct sp_cond_type
|
|||||||
uint mysqlerr;
|
uint mysqlerr;
|
||||||
} sp_cond_type_t;
|
} sp_cond_type_t;
|
||||||
|
|
||||||
|
/* Sanity check for SQLSTATEs. Will not check if it's really an existing
|
||||||
|
* state (there are just too many), but will check length bad characters.
|
||||||
|
*/
|
||||||
|
extern bool
|
||||||
|
sp_cond_check(LEX_STRING *sqlstate);
|
||||||
|
|
||||||
typedef struct sp_cond
|
typedef struct sp_cond
|
||||||
{
|
{
|
||||||
LEX_STRING name;
|
LEX_STRING name;
|
||||||
|
@ -183,6 +183,7 @@ THD::THD()
|
|||||||
lock=locked_tables=0;
|
lock=locked_tables=0;
|
||||||
used_tables=0;
|
used_tables=0;
|
||||||
cuted_fields= sent_row_count= 0L;
|
cuted_fields= sent_row_count= 0L;
|
||||||
|
limit_found_rows= 0;
|
||||||
statement_id_counter= 0UL;
|
statement_id_counter= 0UL;
|
||||||
// Must be reset to handle error with THD's created for init of mysqld
|
// Must be reset to handle error with THD's created for init of mysqld
|
||||||
lex->current_select= 0;
|
lex->current_select= 0;
|
||||||
|
@ -668,7 +668,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
|||||||
thd->clear_error();
|
thd->clear_error();
|
||||||
mysql_bin_log.write(&qinfo);
|
mysql_bin_log.write(&qinfo);
|
||||||
}
|
}
|
||||||
|
thd->server_status|= SERVER_STATUS_DB_DROPPED;
|
||||||
send_ok(thd, (ulong) deleted);
|
send_ok(thd, (ulong) deleted);
|
||||||
|
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
@ -948,7 +948,8 @@ public:
|
|||||||
thd.current_tablenr=0;
|
thd.current_tablenr=0;
|
||||||
thd.version=refresh_version;
|
thd.version=refresh_version;
|
||||||
thd.command=COM_DELAYED_INSERT;
|
thd.command=COM_DELAYED_INSERT;
|
||||||
thd.lex->current_select= 0; /* for my_message_sql */
|
thd.lex->current_select= 0; // for my_message_sql
|
||||||
|
thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
|
||||||
|
|
||||||
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
|
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
|
||||||
bzero((char*) &table_list, sizeof(table_list)); // Safety
|
bzero((char*) &table_list, sizeof(table_list)); // Safety
|
||||||
|
@ -171,27 +171,46 @@ enum tablespace_op_type
|
|||||||
/*
|
/*
|
||||||
The state of the lex parsing for selects
|
The state of the lex parsing for selects
|
||||||
|
|
||||||
|
master and slaves are pointers to select_lex.
|
||||||
|
master is pointer to upper level node.
|
||||||
|
slave is pointer to lower level node
|
||||||
|
select_lex is a SELECT without union
|
||||||
|
unit is container of either
|
||||||
|
- One SELECT
|
||||||
|
- UNION of selects
|
||||||
|
select_lex and unit are both inherited form select_lex_node
|
||||||
|
neighbors are two select_lex or units on the same level
|
||||||
|
|
||||||
All select describing structures linked with following pointers:
|
All select describing structures linked with following pointers:
|
||||||
- list of neighbors (next/prev) (prev of first element point to slave
|
- list of neighbors (next/prev) (prev of first element point to slave
|
||||||
pointer of upper structure)
|
pointer of upper structure)
|
||||||
- one level units for unit (union) structure
|
- For select this is a list of UNION's (or one element list)
|
||||||
- member of one union(unit) for ordinary select_lex
|
- For units this is a list of sub queries for the upper level select
|
||||||
- pointer to master
|
|
||||||
- outer select_lex for unit (union)
|
|
||||||
- unit structure for ordinary select_lex
|
|
||||||
- pointer to slave
|
|
||||||
- first list element of select_lex belonged to this unit for unit
|
|
||||||
- first unit in list of units that belong to this select_lex (as
|
|
||||||
subselects or derived tables) for ordinary select_lex
|
|
||||||
- list of all select_lex (for group operation like correcting list of opened
|
|
||||||
tables)
|
|
||||||
- if unit contain several selects (union) then it have special
|
|
||||||
select_lex called fake_select_lex. It used for storing global parameters
|
|
||||||
and executing union. subqueries of global ORDER BY clause will be
|
|
||||||
attached to this fake_select_lex, which will allow them correctly
|
|
||||||
resolve fields of 'upper' union and other more outer selects.
|
|
||||||
|
|
||||||
for example for following query:
|
- pointer to master (master), which is
|
||||||
|
If this is a unit
|
||||||
|
- pointer to outer select_lex
|
||||||
|
If this is a select_lex
|
||||||
|
- pointer to outer unit structure for select
|
||||||
|
|
||||||
|
- pointer to slave (slave), which is either:
|
||||||
|
If this is a unit:
|
||||||
|
- first SELECT that belong to this unit
|
||||||
|
If this is a select_lex
|
||||||
|
- first unit that belong to this SELECT (subquries or derived tables)
|
||||||
|
|
||||||
|
- list of all select_lex (link_next/link_prev)
|
||||||
|
This is to be used for things like derived tables creation, where we
|
||||||
|
go through this list and create the derived tables.
|
||||||
|
|
||||||
|
If unit contain several selects (UNION now, INTERSECT etc later)
|
||||||
|
then it have special select_lex called fake_select_lex. It used for
|
||||||
|
storing global parameters (like ORDER BY, LIMIT) and executing union.
|
||||||
|
Subqueries used in global ORDER BY clause will be attached to this
|
||||||
|
fake_select_lex, which will allow them correctly resolve fields of
|
||||||
|
'upper' UNION and outer selects.
|
||||||
|
|
||||||
|
For example for following query:
|
||||||
|
|
||||||
select *
|
select *
|
||||||
from table1
|
from table1
|
||||||
@ -209,6 +228,11 @@ enum tablespace_op_type
|
|||||||
|
|
||||||
we will have following structure:
|
we will have following structure:
|
||||||
|
|
||||||
|
select1: (select * from table1 ...)
|
||||||
|
select2: (select * from table2 ...)
|
||||||
|
select3: (select * from table3)
|
||||||
|
select1.1.1: (select * from table1_1_1)
|
||||||
|
...
|
||||||
|
|
||||||
main unit
|
main unit
|
||||||
fake0
|
fake0
|
||||||
@ -231,6 +255,11 @@ enum tablespace_op_type
|
|||||||
|
|
||||||
|
|
||||||
relation in main unit will be following:
|
relation in main unit will be following:
|
||||||
|
(bigger picture for:
|
||||||
|
main unit
|
||||||
|
fake0
|
||||||
|
select1 select2 select3
|
||||||
|
in the above picture)
|
||||||
|
|
||||||
main unit
|
main unit
|
||||||
|^^^^|fake_select_lex
|
|^^^^|fake_select_lex
|
||||||
@ -427,7 +456,7 @@ public:
|
|||||||
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
typedef class st_select_lex_unit SELECT_LEX_UNIT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SELECT_LEX - store information of parsed SELECT_LEX statment
|
SELECT_LEX - store information of parsed SELECT statment
|
||||||
*/
|
*/
|
||||||
class st_select_lex: public st_select_lex_node
|
class st_select_lex: public st_select_lex_node
|
||||||
{
|
{
|
||||||
|
@ -214,7 +214,7 @@ bool log_in_use(const char* log_name)
|
|||||||
if ((linfo = tmp->current_linfo))
|
if ((linfo = tmp->current_linfo))
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&linfo->lock);
|
pthread_mutex_lock(&linfo->lock);
|
||||||
result = !memcmp(log_name, linfo->log_file_name, log_name_len);
|
result = !bcmp(log_name, linfo->log_file_name, log_name_len);
|
||||||
pthread_mutex_unlock(&linfo->lock);
|
pthread_mutex_unlock(&linfo->lock);
|
||||||
if (result)
|
if (result)
|
||||||
break;
|
break;
|
||||||
@ -1274,6 +1274,7 @@ bool mysql_show_binlog_events(THD* thd)
|
|||||||
DBUG_ENTER("mysql_show_binlog_events");
|
DBUG_ENTER("mysql_show_binlog_events");
|
||||||
List<Item> field_list;
|
List<Item> field_list;
|
||||||
const char *errmsg = 0;
|
const char *errmsg = 0;
|
||||||
|
bool ret = TRUE;
|
||||||
IO_CACHE log;
|
IO_CACHE log;
|
||||||
File file = -1;
|
File file = -1;
|
||||||
Format_description_log_event *description_event= new
|
Format_description_log_event *description_event= new
|
||||||
@ -1376,6 +1377,8 @@ bool mysql_show_binlog_events(THD* thd)
|
|||||||
pthread_mutex_unlock(log_lock);
|
pthread_mutex_unlock(log_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret= FALSE;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
delete description_event;
|
delete description_event;
|
||||||
if (file >= 0)
|
if (file >= 0)
|
||||||
@ -1395,7 +1398,7 @@ err:
|
|||||||
pthread_mutex_lock(&LOCK_thread_count);
|
pthread_mutex_lock(&LOCK_thread_count);
|
||||||
thd->current_linfo = 0;
|
thd->current_linfo = 0;
|
||||||
pthread_mutex_unlock(&LOCK_thread_count);
|
pthread_mutex_unlock(&LOCK_thread_count);
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -664,7 +664,7 @@ JOIN::optimize()
|
|||||||
!(select_options & SELECT_DESCRIBE))
|
!(select_options & SELECT_DESCRIBE))
|
||||||
{ /* purecov: inspected */
|
{ /* purecov: inspected */
|
||||||
my_message(ER_TOO_BIG_SELECT, ER(ER_TOO_BIG_SELECT), MYF(0));
|
my_message(ER_TOO_BIG_SELECT, ER(ER_TOO_BIG_SELECT), MYF(0));
|
||||||
error= 1; /* purecov: inspected */
|
error= -1;
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
if (const_tables && !thd->locked_tables &&
|
if (const_tables && !thd->locked_tables &&
|
||||||
@ -1194,7 +1194,9 @@ JOIN::exec()
|
|||||||
else
|
else
|
||||||
error=(int) result->send_eof();
|
error=(int) result->send_eof();
|
||||||
}
|
}
|
||||||
thd->limit_found_rows= thd->examined_row_count= 0;
|
/* Single select (without union and limit) always returns 1 row */
|
||||||
|
thd->limit_found_rows= 1;
|
||||||
|
thd->examined_row_count= 0;
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
thd->limit_found_rows= thd->examined_row_count= 0;
|
thd->limit_found_rows= thd->examined_row_count= 0;
|
||||||
@ -7591,10 +7593,10 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
|
|||||||
new_created field
|
new_created field
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
||||||
Item *item, TABLE *table,
|
Item *item, TABLE *table,
|
||||||
bool modify_item,
|
bool modify_item,
|
||||||
uint convert_blob_length)
|
uint convert_blob_length)
|
||||||
{
|
{
|
||||||
Field *new_field;
|
Field *new_field;
|
||||||
|
|
||||||
@ -10920,8 +10922,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
|||||||
{
|
{
|
||||||
byte *key_buffer, *key_pos, *record=table->record[0];
|
byte *key_buffer, *key_pos, *record=table->record[0];
|
||||||
int error;
|
int error;
|
||||||
handler *file=table->file;
|
handler *file= table->file;
|
||||||
ulong extra_length=ALIGN_SIZE(key_length)-key_length;
|
ulong extra_length= ALIGN_SIZE(key_length)-key_length;
|
||||||
uint *field_lengths,*field_length;
|
uint *field_lengths,*field_length;
|
||||||
HASH hash;
|
HASH hash;
|
||||||
DBUG_ENTER("remove_dup_with_hash_index");
|
DBUG_ENTER("remove_dup_with_hash_index");
|
||||||
@ -10935,22 +10937,34 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
|||||||
NullS))
|
NullS))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
Field **ptr;
|
||||||
|
ulong total_length= 0;
|
||||||
|
for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
|
||||||
|
{
|
||||||
|
uint length= (*ptr)->pack_length();
|
||||||
|
(*field_length++)= length;
|
||||||
|
total_length+= length;
|
||||||
|
}
|
||||||
|
DBUG_PRINT("info",("field_count: %u key_length: %lu total_length: %lu",
|
||||||
|
field_count, key_length, total_length));
|
||||||
|
DBUG_ASSERT(total_length <= key_length);
|
||||||
|
key_length= total_length;
|
||||||
|
extra_length= ALIGN_SIZE(key_length)-key_length;
|
||||||
|
}
|
||||||
|
|
||||||
if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0,
|
if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0,
|
||||||
key_length,(hash_get_key) 0, 0, 0))
|
key_length, (hash_get_key) 0, 0, 0))
|
||||||
{
|
{
|
||||||
my_free((char*) key_buffer,MYF(0));
|
my_free((char*) key_buffer,MYF(0));
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
Field **ptr;
|
|
||||||
for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
|
|
||||||
(*field_length++)= (*ptr)->pack_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
file->ha_rnd_init(1);
|
file->ha_rnd_init(1);
|
||||||
key_pos=key_buffer;
|
key_pos=key_buffer;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
byte *org_key_pos;
|
||||||
if (thd->killed)
|
if (thd->killed)
|
||||||
{
|
{
|
||||||
thd->send_kill_message();
|
thd->send_kill_message();
|
||||||
@ -10973,6 +10987,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* copy fields to key buffer */
|
/* copy fields to key buffer */
|
||||||
|
org_key_pos= key_pos;
|
||||||
field_length=field_lengths;
|
field_length=field_lengths;
|
||||||
for (Field **ptr= first_field ; *ptr ; ptr++)
|
for (Field **ptr= first_field ; *ptr ; ptr++)
|
||||||
{
|
{
|
||||||
@ -10980,14 +10995,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
|||||||
key_pos+= *field_length++;
|
key_pos+= *field_length++;
|
||||||
}
|
}
|
||||||
/* Check if it exists before */
|
/* Check if it exists before */
|
||||||
if (hash_search(&hash,key_pos-key_length,key_length))
|
if (hash_search(&hash, org_key_pos, key_length))
|
||||||
{
|
{
|
||||||
/* Duplicated found ; Remove the row */
|
/* Duplicated found ; Remove the row */
|
||||||
if ((error=file->delete_row(record)))
|
if ((error=file->delete_row(record)))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
(void) my_hash_insert(&hash, key_pos-key_length);
|
(void) my_hash_insert(&hash, org_key_pos);
|
||||||
key_pos+=extra_length;
|
key_pos+=extra_length;
|
||||||
}
|
}
|
||||||
my_free((char*) key_buffer,MYF(0));
|
my_free((char*) key_buffer,MYF(0));
|
||||||
|
@ -399,6 +399,10 @@ void copy_funcs(Item **func_ptr);
|
|||||||
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
|
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
|
||||||
int error, bool ignore_last_dupp_error);
|
int error, bool ignore_last_dupp_error);
|
||||||
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
|
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
|
||||||
|
Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
||||||
|
Item *item, TABLE *table,
|
||||||
|
bool modify_item,
|
||||||
|
uint convert_blob_length);
|
||||||
|
|
||||||
/* functions from opt_sum.cc */
|
/* functions from opt_sum.cc */
|
||||||
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
|
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
|
||||||
|
@ -1760,13 +1760,15 @@ sp_cond:
|
|||||||
}
|
}
|
||||||
| SQLSTATE_SYM opt_value TEXT_STRING_literal
|
| SQLSTATE_SYM opt_value TEXT_STRING_literal
|
||||||
{ /* SQLSTATE */
|
{ /* SQLSTATE */
|
||||||
uint len= ($3.length < sizeof($$->sqlstate)-1 ?
|
if (!sp_cond_check(&$3))
|
||||||
$3.length : sizeof($$->sqlstate)-1);
|
{
|
||||||
|
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||||
$$->type= sp_cond_type_t::state;
|
$$->type= sp_cond_type_t::state;
|
||||||
memcpy($$->sqlstate, $3.str, len);
|
memcpy($$->sqlstate, $3.str, 5);
|
||||||
$$->sqlstate[len]= '\0';
|
$$->sqlstate[5]= '\0';
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -67,6 +67,13 @@ static uchar bin_char_array[] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static my_bool
|
||||||
|
my_coll_init_8bit_bin(CHARSET_INFO *cs,
|
||||||
|
void *(*alloc)(uint) __attribute__((unused)))
|
||||||
|
{
|
||||||
|
cs->max_sort_char=255;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)),
|
static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)),
|
||||||
const uchar *s, uint slen,
|
const uchar *s, uint slen,
|
||||||
@ -443,7 +450,7 @@ skip:
|
|||||||
|
|
||||||
MY_COLLATION_HANDLER my_collation_8bit_bin_handler =
|
MY_COLLATION_HANDLER my_collation_8bit_bin_handler =
|
||||||
{
|
{
|
||||||
NULL, /* init */
|
my_coll_init_8bit_bin,
|
||||||
my_strnncoll_8bit_bin,
|
my_strnncoll_8bit_bin,
|
||||||
my_strnncollsp_8bit_bin,
|
my_strnncollsp_8bit_bin,
|
||||||
my_strnxfrm_8bit_bin,
|
my_strnxfrm_8bit_bin,
|
||||||
|
@ -545,7 +545,6 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)),
|
|||||||
register unsigned int cutlim;
|
register unsigned int cutlim;
|
||||||
register ulonglong i;
|
register ulonglong i;
|
||||||
register const char *s, *e;
|
register const char *s, *e;
|
||||||
register unsigned char c;
|
|
||||||
const char *save;
|
const char *save;
|
||||||
int overflow;
|
int overflow;
|
||||||
|
|
||||||
@ -608,8 +607,9 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)),
|
|||||||
|
|
||||||
overflow = 0;
|
overflow = 0;
|
||||||
i = 0;
|
i = 0;
|
||||||
for (c = *s; s != e; c = *++s)
|
for ( ; s != e; s++)
|
||||||
{
|
{
|
||||||
|
register unsigned char c= *s;
|
||||||
if (c>='0' && c<='9')
|
if (c>='0' && c<='9')
|
||||||
c -= '0';
|
c -= '0';
|
||||||
else if (c>='A' && c<='Z')
|
else if (c>='A' && c<='Z')
|
||||||
@ -668,7 +668,6 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs,
|
|||||||
register unsigned int cutlim;
|
register unsigned int cutlim;
|
||||||
register ulonglong i;
|
register ulonglong i;
|
||||||
register const char *s, *e;
|
register const char *s, *e;
|
||||||
register unsigned char c;
|
|
||||||
const char *save;
|
const char *save;
|
||||||
int overflow;
|
int overflow;
|
||||||
|
|
||||||
@ -731,8 +730,10 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs,
|
|||||||
|
|
||||||
overflow = 0;
|
overflow = 0;
|
||||||
i = 0;
|
i = 0;
|
||||||
for (c = *s; s != e; c = *++s)
|
for ( ; s != e; s++)
|
||||||
{
|
{
|
||||||
|
register unsigned char c= *s;
|
||||||
|
|
||||||
if (c>='0' && c<='9')
|
if (c>='0' && c<='9')
|
||||||
c -= '0';
|
c -= '0';
|
||||||
else if (c>='A' && c<='Z')
|
else if (c>='A' && c<='Z')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user