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;
|
||||
}
|
||||
|
||||
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
|
||||
@ -1917,6 +1933,10 @@ com_go(String *buffer,char *line __attribute__((unused)))
|
||||
if (err >= 1)
|
||||
error= put_error(&mysql);
|
||||
|
||||
if (!error && !status.batch &&
|
||||
(mysql.server_status & SERVER_STATUS_DB_DROPPED))
|
||||
get_current_db();
|
||||
|
||||
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
|
||||
(latter one not yet available by the time the comment was written)
|
||||
*/
|
||||
/* Let's reset current_db, assume it's gone */
|
||||
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);
|
||||
}
|
||||
get_current_db();
|
||||
|
||||
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");
|
||||
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
|
||||
cannot reconnect.
|
||||
|
@ -531,7 +531,7 @@ typedef uint32 ha_checksum;
|
||||
|
||||
/* Define the type of function to be passed to process_default_option_files */
|
||||
typedef int (*Process_option_func)(void *ctx, const char *group_name,
|
||||
const char *option);
|
||||
const char *option);
|
||||
|
||||
#include <my_alloc.h>
|
||||
|
||||
@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv,
|
||||
char **defaults, char **extra_defaults);
|
||||
extern int load_defaults(const char *conf_file, const char **groups,
|
||||
int *argc, char ***argv);
|
||||
extern int process_default_option_files(const char *conf_file,
|
||||
Process_option_func func,
|
||||
void *func_ctx);
|
||||
extern int my_search_option_files(const char *conf_file, int *argc,
|
||||
char ***argv, uint *args_used,
|
||||
Process_option_func func, void *func_ctx);
|
||||
extern void free_defaults(char **argv);
|
||||
extern void print_defaults(const char *conf_file, const char **groups);
|
||||
extern my_bool my_compress(byte *, ulong *, ulong *);
|
||||
|
@ -147,6 +147,7 @@ enum enum_server_command
|
||||
COM_FETCH command.
|
||||
*/
|
||||
#define SERVER_STATUS_LAST_ROW_SENT 128
|
||||
#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */
|
||||
|
||||
#define MYSQL_ERRMSG_SIZE 512
|
||||
#define NET_READ_TIMEOUT 30 /* Timeout on read */
|
||||
|
@ -1642,7 +1642,7 @@ btr_cur_optimistic_update(
|
||||
|
||||
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);
|
||||
|
||||
@ -1885,7 +1885,7 @@ btr_cur_pessimistic_update(
|
||||
|
||||
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);
|
||||
|
||||
@ -2401,6 +2401,7 @@ btr_cur_optimistic_delete(
|
||||
mem_heap_t* heap = NULL;
|
||||
ulint offsets_[100] = { 100, };
|
||||
ulint* offsets = offsets_;
|
||||
ibool no_compress_needed;
|
||||
|
||||
ut_ad(mtr_memo_contains(mtr, buf_block_align(btr_cur_get_page(cursor)),
|
||||
MTR_MEMO_PAGE_X_FIX));
|
||||
@ -2414,9 +2415,11 @@ btr_cur_optimistic_delete(
|
||||
offsets = rec_get_offsets(rec, cursor->index, offsets,
|
||||
ULINT_UNDEFINED, &heap);
|
||||
|
||||
if (!rec_offs_any_extern(offsets)
|
||||
no_compress_needed = !rec_offs_any_extern(offsets)
|
||||
&& 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);
|
||||
|
||||
@ -2425,20 +2428,17 @@ btr_cur_optimistic_delete(
|
||||
max_ins_size = page_get_max_insert_size_after_reorganize(page,
|
||||
1);
|
||||
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,
|
||||
mtr);
|
||||
if (heap) {
|
||||
mem_heap_free(heap);
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
if (heap) {
|
||||
mem_heap_free(heap);
|
||||
}
|
||||
return(FALSE);
|
||||
|
||||
return(no_compress_needed);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
@ -2478,6 +2478,7 @@ btr_cur_pessimistic_delete(
|
||||
ibool success;
|
||||
ibool ret = FALSE;
|
||||
mem_heap_t* heap;
|
||||
ulint* offsets;
|
||||
|
||||
page = btr_cur_get_page(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);
|
||||
|
||||
offsets = rec_get_offsets(rec, cursor->index,
|
||||
NULL, ULINT_UNDEFINED, &heap);
|
||||
|
||||
/* Free externally stored fields if the record is neither
|
||||
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
|
||||
? !rec_get_node_ptr_flag(rec)
|
||||
: !rec_get_1byte_offs_flag(rec)) {
|
||||
btr_rec_free_externally_stored_fields(cursor->index,
|
||||
rec, rec_get_offsets(rec, cursor->index,
|
||||
NULL, ULINT_UNDEFINED, &heap),
|
||||
in_rollback, mtr);
|
||||
mem_heap_empty(heap);
|
||||
rec, offsets, in_rollback, mtr);
|
||||
}
|
||||
|
||||
if ((page_get_n_recs(page) < 2)
|
||||
@ -2568,7 +2569,8 @@ btr_cur_pessimistic_delete(
|
||||
|
||||
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));
|
||||
|
||||
|
@ -729,14 +729,17 @@ dict_drop_index_tree(
|
||||
/***********************************************************************
|
||||
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
||||
|
||||
void
|
||||
ulint
|
||||
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 */
|
||||
rec_t* rec, /* in: record in the clustered index of
|
||||
SYS_INDEXES table */
|
||||
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 space;
|
||||
@ -761,7 +764,10 @@ dict_truncate_index_tree(
|
||||
if (root_page_no == FIL_NULL) {
|
||||
/* 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,
|
||||
@ -775,7 +781,10 @@ dict_truncate_index_tree(
|
||||
/* It is a single table tablespace and the .ibd file is
|
||||
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,
|
||||
@ -801,6 +810,20 @@ dict_truncate_index_tree(
|
||||
space, root_page_no, RW_X_LATCH, 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. */
|
||||
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);
|
||||
if (index) {
|
||||
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,
|
||||
DICT_SYS_INDEXES_PAGE_NO_FIELD,
|
||||
root_page_no, mtr);
|
||||
return(root_page_no);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
|
@ -2719,7 +2719,8 @@ scan_more:
|
||||
/* Starting quote: remember the quote character. */
|
||||
quote = *sptr;
|
||||
} else if (*sptr == '#'
|
||||
|| (0 == memcmp("-- ", sptr, 3))) {
|
||||
|| (sptr[0] == '-' && sptr[1] == '-' &&
|
||||
sptr[2] == ' ')) {
|
||||
for (;;) {
|
||||
/* In Unix a newline is 0x0A while in Windows
|
||||
it is 0x0D followed by 0x0A */
|
||||
|
@ -3817,13 +3817,6 @@ fil_io(
|
||||
node = UT_LIST_GET_FIRST(space->chain);
|
||||
|
||||
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) {
|
||||
fprintf(stderr,
|
||||
"InnoDB: Error: trying to access page number %lu in space %lu,\n"
|
||||
@ -3837,6 +3830,13 @@ fil_io(
|
||||
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) {
|
||||
/* Found! */
|
||||
break;
|
||||
|
@ -56,14 +56,17 @@ dict_create_index_step(
|
||||
/***********************************************************************
|
||||
Truncates the index tree associated with a row in SYS_INDEXES table. */
|
||||
|
||||
void
|
||||
ulint
|
||||
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 */
|
||||
rec_t* rec, /* in: record in the clustered index of
|
||||
SYS_INDEXES table */
|
||||
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. */
|
||||
|
||||
|
@ -182,9 +182,10 @@ next record after the deleted one. */
|
||||
void
|
||||
page_cur_delete_rec(
|
||||
/*================*/
|
||||
page_cur_t* cursor, /* in: a page cursor */
|
||||
dict_index_t* index, /* in: record descriptor */
|
||||
mtr_t* mtr); /* in: mini-transaction handle */
|
||||
page_cur_t* cursor, /* in: a page cursor */
|
||||
dict_index_t* index, /* in: record descriptor */
|
||||
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. */
|
||||
UNIV_INLINE
|
||||
|
@ -528,7 +528,7 @@ page_mem_free(
|
||||
/*==========*/
|
||||
page_t* page, /* in: index page */
|
||||
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. */
|
||||
|
||||
|
@ -777,20 +777,31 @@ page_mem_free(
|
||||
/*==========*/
|
||||
page_t* page, /* in: index page */
|
||||
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;
|
||||
ulint garbage;
|
||||
|
||||
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
||||
free = page_header_get_ptr(page, PAGE_FREE);
|
||||
|
||||
page_rec_set_next(rec, free);
|
||||
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);
|
||||
|
||||
page_header_set_field(page, PAGE_GARBAGE,
|
||||
garbage + rec_get_size(rec, index));
|
||||
garbage + rec_offs_size(offsets));
|
||||
}
|
||||
|
||||
#ifdef UNIV_MATERIALIZE
|
||||
|
@ -435,15 +435,6 @@ rec_offs_size(
|
||||
/* out: size */
|
||||
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. */
|
||||
UNIV_INLINE
|
||||
byte*
|
||||
|
@ -24,6 +24,32 @@ Created 12/9/1995 Heikki Tuuri
|
||||
#include "trx0sys.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
|
||||
uninitialized */
|
||||
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
|
||||
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. */
|
||||
|
@ -1267,9 +1267,18 @@ page_cur_parse_delete_rec(
|
||||
ut_a(offset <= UNIV_PAGE_SIZE);
|
||||
|
||||
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);
|
||||
@ -1284,6 +1293,7 @@ page_cur_delete_rec(
|
||||
/*================*/
|
||||
page_cur_t* cursor, /* in: a page cursor */
|
||||
dict_index_t* index, /* in: record descriptor */
|
||||
const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */
|
||||
mtr_t* mtr) /* in: mini-transaction handle */
|
||||
{
|
||||
page_dir_slot_t* cur_dir_slot;
|
||||
@ -1300,6 +1310,7 @@ page_cur_delete_rec(
|
||||
|
||||
page = page_cur_get_page(cursor);
|
||||
current_rec = cursor->rec;
|
||||
ut_ad(rec_offs_validate(current_rec, index, offsets));
|
||||
|
||||
/* The record must not be the supremum or infimum record. */
|
||||
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);
|
||||
|
||||
/* 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.
|
||||
If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the
|
||||
|
@ -416,7 +416,7 @@ page_create(
|
||||
|
||||
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_ptr(page, PAGE_HEAP_TOP, heap_top);
|
||||
@ -428,7 +428,9 @@ page_create(
|
||||
page_header_set_field(page, PAGE_N_DIRECTION, 0);
|
||||
page_header_set_field(page, PAGE_N_RECS, 0);
|
||||
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 */
|
||||
|
||||
/* Set the slots to point to infimum and supremum. */
|
||||
@ -829,12 +831,18 @@ page_delete_rec_list_start(
|
||||
{
|
||||
page_cur_t cur1;
|
||||
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,
|
||||
index->table->comp
|
||||
? MLOG_COMP_LIST_START_DELETE
|
||||
: MLOG_LIST_START_DELETE,
|
||||
mtr);
|
||||
if (index->table->comp) {
|
||||
type = MLOG_COMP_LIST_START_DELETE;
|
||||
} else {
|
||||
type = MLOG_LIST_START_DELETE;
|
||||
}
|
||||
|
||||
page_delete_rec_list_write_log(page, rec, index, type, mtr);
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
|
@ -620,7 +620,7 @@ rec_set_nth_field_extern_bit_new(
|
||||
mlog_write_ulint(lens + 1, len,
|
||||
MLOG_1BYTE, mtr);
|
||||
} else {
|
||||
lens[1] = len;
|
||||
lens[1] = (byte) len;
|
||||
}
|
||||
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.
|
||||
The physical size of the field is not changed. */
|
||||
@ -935,13 +912,13 @@ init:
|
||||
|| dtype_get_mtype(type) == DATA_BLOB);
|
||||
if (len < 128 || (dtype_get_len(type) < 256
|
||||
&& dtype_get_mtype(type) != DATA_BLOB)) {
|
||||
*lens-- = len;
|
||||
*lens-- = (byte) len;
|
||||
}
|
||||
else {
|
||||
/* the extern bits will be set later */
|
||||
ut_ad(len < 16384);
|
||||
*lens-- = len >> 8 | 0x80;
|
||||
*lens-- = len;
|
||||
*lens-- = (byte) (len >> 8) | 0x80;
|
||||
*lens-- = (byte) len;
|
||||
}
|
||||
}
|
||||
copy:
|
||||
|
@ -2615,6 +2615,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */
|
||||
rec_t* rec;
|
||||
const byte* field;
|
||||
ulint len;
|
||||
ulint root_page_no;
|
||||
|
||||
if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) {
|
||||
/* 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)) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
@ -3058,14 +3058,19 @@ row_search_for_mysql(
|
||||
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(
|
||||
"InnoDB: Error: MySQL is trying to perform a SELECT\n"
|
||||
"InnoDB: but it has not locked any tables in ::external_lock()!\n",
|
||||
stderr);
|
||||
trx_print(stderr, trx);
|
||||
fputc('\n', stderr);
|
||||
ut_a(0);
|
||||
}
|
||||
|
||||
/* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode);
|
||||
|
@ -1501,7 +1501,7 @@ srv_suspend_mysql_thread(
|
||||
ut_usectime(&sec, &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_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_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_avg=
|
||||
(srv_n_lock_wait_count > 0) ?
|
||||
(srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count) : 0;
|
||||
if (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);
|
||||
} 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_rows_read= srv_n_rows_read;
|
||||
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 i; /* spin round count */
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
ib_longlong lstart_time = 0, lfinish_time; /* for timing os_wait */
|
||||
ulint ltime_diff;
|
||||
ulint sec;
|
||||
ulint ms;
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
uint timer_started = 0;
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
ut_ad(mutex);
|
||||
@ -535,7 +535,7 @@ finish_timing:
|
||||
ut_usectime(&sec, &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;
|
||||
if (mutex->lmax_spent_time < ltime_diff)
|
||||
{
|
||||
|
@ -440,7 +440,17 @@ loop:
|
||||
if ((trx->sess || (trx->conc_state == TRX_NOT_STARTED))) {
|
||||
trx = UT_LIST_GET_NEXT(trx_list, trx);
|
||||
} 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 {
|
||||
break;
|
||||
}
|
||||
|
@ -435,14 +435,14 @@ trx_lists_init_at_db_start(void)
|
||||
|
||||
if (undo->state == TRX_UNDO_PREPARED) {
|
||||
|
||||
fprintf(stderr,
|
||||
fprintf(stderr,
|
||||
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
||||
ut_dulint_get_high(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 {
|
||||
trx->conc_state =
|
||||
TRX_COMMITTED_IN_MEMORY;
|
||||
@ -498,16 +498,15 @@ trx_lists_init_at_db_start(void)
|
||||
commit or abort decision from MySQL */
|
||||
|
||||
if (undo->state == TRX_UNDO_PREPARED) {
|
||||
|
||||
fprintf(stderr,
|
||||
fprintf(stderr,
|
||||
"InnoDB: Transaction %lu %lu was in the XA prepared state.\n",
|
||||
ut_dulint_get_high(trx->id),
|
||||
ut_dulint_get_low(trx->id));
|
||||
ut_dulint_get_high(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 {
|
||||
trx->conc_state =
|
||||
TRX_COMMITTED_IN_MEMORY;
|
||||
@ -1638,10 +1637,13 @@ trx_print(
|
||||
fputs(", not started", f);
|
||||
break;
|
||||
case TRX_ACTIVE:
|
||||
case TRX_PREPARED:
|
||||
fprintf(f, ", ACTIVE %lu sec",
|
||||
(ulong)difftime(time(NULL), trx->start_time));
|
||||
break;
|
||||
case TRX_PREPARED:
|
||||
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
|
||||
(ulong)difftime(time(NULL), trx->start_time));
|
||||
break;
|
||||
case TRX_COMMITTED_IN_MEMORY:
|
||||
fputs(", COMMITTED IN MEMORY", f);
|
||||
break;
|
||||
@ -1938,7 +1940,7 @@ trx_get_trx_by_xid(
|
||||
|
||||
if (xid->gtrid_length == trx->xid.gtrid_length &&
|
||||
xid->bqual_length == trx->xid.bqual_length &&
|
||||
memcmp(xid, &trx->xid,
|
||||
memcmp(xid->data, trx->xid.data,
|
||||
xid->gtrid_length +
|
||||
xid->bqual_length) == 0) {
|
||||
break;
|
||||
|
@ -599,11 +599,10 @@ trx_undo_read_xid(
|
||||
Adds the XA XID after an undo log old-style header. */
|
||||
static
|
||||
void
|
||||
trx_undo_header_add_xid(
|
||||
/*====================*/
|
||||
trx_undo_header_add_space_for_xid(
|
||||
/*==============================*/
|
||||
page_t* undo_page,/* in: undo log segment header page */
|
||||
trx_ulogf_t* log_hdr,/* in: undo log header */
|
||||
XID* xid, /* in: X/Open XA transaction identification */
|
||||
mtr_t* mtr) /* in: mtr */
|
||||
{
|
||||
trx_upagef_t* page_hdr;
|
||||
@ -620,9 +619,8 @@ trx_undo_header_add_xid(
|
||||
|
||||
new_free = free + (TRX_UNDO_LOG_XA_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 */
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
page_no, offset);
|
||||
@ -1599,7 +1597,7 @@ trx_undo_reuse_cached(
|
||||
|
||||
if (type == TRX_UNDO_INSERT) {
|
||||
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);
|
||||
} else {
|
||||
ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR
|
||||
@ -1607,7 +1605,7 @@ trx_undo_reuse_cached(
|
||||
== TRX_UNDO_UPDATE);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -54,3 +54,19 @@ select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1;
|
||||
collation(a) collation(b) collation(binary 'ccc')
|
||||
cp1251_bin binary binary
|
||||
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(max(a)) coercibility(max(a)) charset(min(a)) coercibility(min(a))
|
||||
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;
|
||||
show create table t2;
|
||||
Table Create Table
|
||||
t2 CREATE TABLE `t2` (
|
||||
`max(a)` varchar(1) character set latin2 default NULL,
|
||||
`min(a)` varchar(1) character set latin2 default NULL
|
||||
`max(a)` char(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
|
||||
drop table t2,t1;
|
||||
create table t1 (a int);
|
||||
@ -757,6 +769,15 @@ one 2
|
||||
two 2
|
||||
three 1
|
||||
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);
|
||||
insert into t1 values (now());
|
||||
create table t2 select f2 from (select max(now()) f2 from t1) a;
|
||||
|
@ -1657,6 +1657,44 @@ a
|
||||
b
|
||||
c
|
||||
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;
|
||||
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
|
||||
|
@ -129,7 +129,7 @@ FOUND_ROWS()
|
||||
1
|
||||
execute stmt1;
|
||||
FOUND_ROWS()
|
||||
0
|
||||
1
|
||||
deallocate prepare stmt1;
|
||||
drop table t1;
|
||||
create table t1
|
||||
|
@ -246,6 +246,31 @@ SELECT FOUND_ROWS();
|
||||
FOUND_ROWS()
|
||||
0
|
||||
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);
|
||||
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;
|
||||
@ -254,3 +279,4 @@ a
|
||||
SELECT FOUND_ROWS();
|
||||
FOUND_ROWS()
|
||||
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
|
||||
SET MAX_SEEKS_FOR_KEY=DEFAULT;
|
||||
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;
|
||||
|
@ -254,7 +254,7 @@ create table t1 (a int not null);
|
||||
create table t2 select max(a) from t1;
|
||||
show columns from t2;
|
||||
Field Type Null Key Default Extra
|
||||
max(a) bigint(20) YES NULL
|
||||
max(a) int(11) YES NULL
|
||||
drop table t1,t2;
|
||||
create table t1 (c decimal, d double, f float, r real);
|
||||
show columns from t1;
|
||||
|
@ -466,4 +466,32 @@ ERROR 70100: Query execution was interrupted
|
||||
call bug6807()|
|
||||
ERROR 70100: Query execution was interrupted
|
||||
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|
|
||||
|
@ -29,6 +29,12 @@ a
|
||||
A
|
||||
a,A
|
||||
a,A
|
||||
select s from t1 order by concat(s);
|
||||
s
|
||||
A
|
||||
a
|
||||
a,A
|
||||
a,A
|
||||
drop table t1;
|
||||
CREATE TABLE t1 (c set('ae','oe','ue','ss') collate latin1_german2_ci);
|
||||
INSERT INTO t1 VALUES ('ä'),('ö'),('ü'),('ß');
|
||||
@ -47,4 +53,16 @@ ss
|
||||
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;
|
||||
|
@ -32,3 +32,17 @@ select * from t1 where lower(b)='bbb';
|
||||
select charset(a), charset(b), charset(binary 'ccc') from t1 limit 1;
|
||||
select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1;
|
||||
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');
|
||||
select charset(max(a)), coercibility(max(a)),
|
||||
charset(min(a)), coercibility(min(a)) from t1;
|
||||
show create table t1;
|
||||
create table t2 select max(a),min(a) from t1;
|
||||
show create table t2;
|
||||
drop table t2;
|
||||
create table t2 select concat(a) from t1;
|
||||
show create table t2;
|
||||
drop table t2,t1;
|
||||
|
||||
#
|
||||
@ -479,6 +483,15 @@ INSERT INTO t1 VALUES
|
||||
select val, count(*) from t1 group by val;
|
||||
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
|
||||
|
@ -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 b from t2 where (a2 >= 'b') and (b = 'a');
|
||||
|
||||
-- BUG 6303
|
||||
-- BUG #6303
|
||||
select distinct t_00.a1
|
||||
from t1 t_00
|
||||
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
|
||||
|
@ -167,6 +167,21 @@ SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a = 0 GROUP BY a HAVING a > 10;
|
||||
SELECT FOUND_ROWS();
|
||||
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
|
||||
#
|
||||
@ -175,3 +190,4 @@ CREATE TABLE t1 (a int, b int);
|
||||
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 FOUND_ROWS();
|
||||
DROP TABLE t1;
|
||||
|
@ -66,4 +66,24 @@ SET MAX_SEEKS_FOR_KEY=DEFAULT;
|
||||
|
||||
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;
|
||||
|
@ -641,6 +641,44 @@ call 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|
|
||||
|
||||
|
@ -23,6 +23,7 @@ create table t1 (s set ('a','A') character set latin1 collate latin1_bin);
|
||||
show create table t1;
|
||||
insert into t1 values ('a'),('a,A'),('A,a'),('A');
|
||||
select s from t1 order by s;
|
||||
select s from t1 order by concat(s);
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
@ -34,4 +35,5 @@ INSERT INTO t1 VALUES ('ae'),('oe'),('ue'),('ss');
|
||||
INSERT INTO t1 VALUES ('ä,ö,ü,ß');
|
||||
INSERT INTO t1 VALUES ('ae,oe,ue,ss');
|
||||
SELECT c FROM t1 ORDER BY c;
|
||||
SELECT c FROM t1 ORDER BY concat(c);
|
||||
DROP TABLE t1;
|
||||
|
@ -83,7 +83,7 @@ static char *remove_end_comment(char *ptr);
|
||||
Process config files in default directories.
|
||||
|
||||
SYNOPSIS
|
||||
search_files()
|
||||
my_search_option_files()
|
||||
conf_file Basename for configuration file to search for.
|
||||
If this is a path, then only this file is read.
|
||||
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
|
||||
*/
|
||||
|
||||
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,
|
||||
void *func_ctx)
|
||||
{
|
||||
const char **dirs, *forced_default_file;
|
||||
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 */
|
||||
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.
|
||||
|
||||
@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups,
|
||||
ctx.args= &args;
|
||||
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);
|
||||
/*
|
||||
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)",
|
||||
"Collation '%s' is not a compiled collation and is not specified in the '%s' file",
|
||||
"File '%s' not found (Errcode: %d)",
|
||||
""
|
||||
};
|
||||
|
||||
void init_glob_errs(void)
|
||||
|
@ -145,6 +145,10 @@ void my_end(int infoflag)
|
||||
{
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
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))
|
||||
fprintf(info_file,"\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
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#pragma message "no mmap!"
|
||||
#else
|
||||
#ifndef __WIN__
|
||||
#warning "no mmap!"
|
||||
#endif
|
||||
|
||||
|
@ -74,8 +74,8 @@ testReadPerf_SOURCES = testReadPerf.cpp
|
||||
testLcp_SOURCES = testLcp.cpp
|
||||
testPartitioning_SOURCES = testPartitioning.cpp
|
||||
testBitfield_SOURCES = testBitfield.cpp
|
||||
DbCreate_SOURCES= bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp
|
||||
DbAsyncGenerator_SOURCES= bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.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 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
|
||||
|
||||
|
@ -30,14 +30,13 @@ liboptions_a_CXXFLAGS= $(CXXFLAGS) \
|
||||
-DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \
|
||||
-DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \
|
||||
-DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \
|
||||
-DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \
|
||||
-DDEFAULT_USER="root" \
|
||||
-DDEFAULT_PASSWORD="" \
|
||||
-DDEFAULT_MONITORING_INTERVAL="5" \
|
||||
-DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \
|
||||
-DDEFAULT_MONITORING_INTERVAL="20" \
|
||||
-DDEFAULT_PORT="2273" \
|
||||
-DPROTOCOL_VERSION=@PROTOCOL_VERSION@
|
||||
|
||||
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
|
||||
# All symlinked files are grouped in libnet.a
|
||||
@ -59,7 +58,7 @@ client_settings.h: Makefile
|
||||
rm -f $(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 \
|
||||
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 \
|
||||
buffer.h buffer.cc parse.cc parse.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 \
|
||||
libnet.a \
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
RETURN
|
||||
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)
|
||||
@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg)
|
||||
|
||||
RETURN
|
||||
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)
|
||||
@ -79,19 +79,31 @@ int Buffer::reserve(uint position, uint len_arg)
|
||||
if (position + len_arg >= MAX_BUFFER_SIZE)
|
||||
goto err;
|
||||
|
||||
if (position + len_arg>= buffer_size)
|
||||
if (position + len_arg >= buffer_size)
|
||||
{
|
||||
buffer= (char *) realloc(buffer,
|
||||
min(MAX_BUFFER_SIZE,
|
||||
max((uint) (buffer_size*1.5),
|
||||
position + len_arg)));
|
||||
if (buffer == NULL)
|
||||
buffer= (char *) my_realloc(buffer,
|
||||
min(MAX_BUFFER_SIZE,
|
||||
max((uint) (buffer_size*1.5),
|
||||
position + len_arg)), MYF(0));
|
||||
if (!(buffer))
|
||||
goto err;
|
||||
buffer_size= (uint) (buffer_size*1.5);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
error= 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 */
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma interface
|
||||
@ -36,11 +37,17 @@ private:
|
||||
/* maximum buffer size is 16Mb */
|
||||
enum { MAX_BUFFER_SIZE= 16777216 };
|
||||
size_t buffer_size;
|
||||
/* Error flag. Triggered if we get an error of some kind */
|
||||
int error;
|
||||
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()
|
||||
@ -50,6 +57,8 @@ public:
|
||||
|
||||
public:
|
||||
char *buffer;
|
||||
int get_size();
|
||||
int is_error();
|
||||
int append(uint position, const char *string, 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;
|
||||
|
||||
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;
|
||||
if (instance->is_running())
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -200,7 +201,7 @@ err:
|
||||
|
||||
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))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
@ -256,52 +257,31 @@ int Show_instance_options::do_command(struct st_net *net,
|
||||
{
|
||||
Instance *instance;
|
||||
|
||||
if ((instance= instance_map->
|
||||
find(instance_name, strlen(instance_name))) == NULL)
|
||||
if (!(instance= instance_map->find(instance_name, strlen(instance_name))))
|
||||
goto err;
|
||||
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))
|
||||
goto err;
|
||||
if (instance->options.mysqld_path != NULL)
|
||||
if ((instance->options.mysqld_path))
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "mysqld-path", &position);
|
||||
store_to_string(&send_buff,
|
||||
(char *) instance->options.mysqld_path,
|
||||
&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;
|
||||
}
|
||||
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if ((instance->options.nonguarded))
|
||||
{
|
||||
position= 0;
|
||||
store_to_string(&send_buff, (char *) "guarded", &position);
|
||||
store_to_string(&send_buff, (char *) "nonguarded", &position);
|
||||
store_to_string(&send_buff, "", &position);
|
||||
if (my_net_write(net, send_buff.buffer, (uint) position))
|
||||
goto err;
|
||||
}
|
||||
|
||||
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))
|
||||
if (send_buff.is_error() ||
|
||||
my_net_write(net, send_buff.buffer, (uint) position))
|
||||
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);
|
||||
/* join name and the value into the same option again */
|
||||
*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;
|
||||
}
|
||||
}
|
||||
@ -335,7 +316,7 @@ err:
|
||||
|
||||
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))
|
||||
return ER_OUT_OF_RESOURCES;
|
||||
@ -369,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (err_code= instance->start())
|
||||
if ((err_code= instance->start()))
|
||||
return err_code;
|
||||
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if (!(instance->options.nonguarded))
|
||||
instance_map->guardian->guard(instance);
|
||||
|
||||
net_send_ok(net, connection_id);
|
||||
@ -403,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (instance->options.is_guarded != NULL)
|
||||
if (!(instance->options.nonguarded))
|
||||
instance_map->guardian->
|
||||
stop_guard(instance);
|
||||
if ((err_code= instance->stop()))
|
||||
|
@ -21,9 +21,15 @@
|
||||
|
||||
#include "guardian.h"
|
||||
#include "instance_map.h"
|
||||
#include "instance.h"
|
||||
#include "mysql_manager_error.h"
|
||||
#include "log.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
|
||||
|
||||
C_MODE_START
|
||||
|
||||
@ -42,13 +48,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg,
|
||||
uint monitoring_interval_arg) :
|
||||
Guardian_thread_args(thread_registry_arg, instance_map_arg,
|
||||
monitoring_interval_arg),
|
||||
thread_info(pthread_self())
|
||||
thread_info(pthread_self()), guarded_instances(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);
|
||||
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 */
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
free_root(&alloc, MYF(0));
|
||||
thread_registry.unregister_thread(&thread_info);
|
||||
pthread_mutex_unlock(&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()
|
||||
{
|
||||
Instance *instance;
|
||||
LIST *loop;
|
||||
LIST *node;
|
||||
struct timespec timeout;
|
||||
|
||||
thread_registry.register_thread(&thread_info);
|
||||
|
||||
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);
|
||||
loop= guarded_instances;
|
||||
while (loop != NULL)
|
||||
node= guarded_instances;
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
instance= (Instance *) loop->data;
|
||||
/* instance-> start already checks whether instance is running */
|
||||
if (instance->start() != ER_INSTANCE_ALREADY_STARTED)
|
||||
log_info("guardian attempted to restart instance %s",
|
||||
instance->options.instance_name);
|
||||
loop= loop->next;
|
||||
struct timespec timeout;
|
||||
|
||||
GUARD_NODE *current_node= (GUARD_NODE *) node->data;
|
||||
instance= ((GUARD_NODE *) node->data)->instance;
|
||||
process_instance(instance, current_node, &guarded_instances, node);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
move_to_list(&starting_instances, &guarded_instances);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
sleep(monitoring_interval);
|
||||
timeout.tv_sec= time(NULL) + monitoring_interval;
|
||||
timeout.tv_nsec= 0;
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
|
||||
int Guardian_thread::start()
|
||||
int Guardian_thread::is_stopped()
|
||||
{
|
||||
Instance *instance;
|
||||
Instance_map::Iterator iterator(instance_map);
|
||||
|
||||
instance_map->lock();
|
||||
while ((instance= iterator.next()))
|
||||
{
|
||||
if ((instance->options.is_guarded != NULL) && (instance->is_running()))
|
||||
if (guard(instance))
|
||||
return 1;
|
||||
}
|
||||
instance_map->unlock();
|
||||
|
||||
return 0;
|
||||
int var;
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
var= stopped;
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
return var;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
guard()
|
||||
instance the instance to be guarded
|
||||
Guardian_thread::init()
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
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.
|
||||
NOTE: One should always lock guardian before calling this routine.
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - error occured
|
||||
*/
|
||||
|
||||
|
||||
int Guardian_thread::guard(Instance *instance)
|
||||
int Guardian_thread::init()
|
||||
{
|
||||
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)
|
||||
{
|
||||
LIST *tmp;
|
||||
|
||||
while (*from)
|
||||
while ((instance= iterator.next()))
|
||||
{
|
||||
tmp= rest(*from);
|
||||
*to= list_add(*to, *from);
|
||||
*from= tmp;
|
||||
if (!(instance->options.nonguarded))
|
||||
if (guard(instance, TRUE)) /* do not lock guardian */
|
||||
{
|
||||
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;
|
||||
GUARD_NODE *content;
|
||||
|
||||
node= (LIST *) alloc_root(&alloc, sizeof(LIST));
|
||||
if (node == NULL)
|
||||
content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE));
|
||||
|
||||
if ((!(node)) || (!(content)))
|
||||
return 1;
|
||||
/* 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);
|
||||
*list= list_add(*list, node);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
if (nolock)
|
||||
guarded_instances= list_add(guarded_instances, node);
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_guardian);
|
||||
guarded_instances= list_add(guarded_instances, node);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
TODO: perhaps it would make sense to create a pool of the LIST elements
|
||||
elements and give them upon request. Now we are loosing a bit of memory when
|
||||
TODO: perhaps it would make sense to create a pool of the LIST nodeents
|
||||
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
|
||||
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
|
||||
instance_map's MEM_ROOT.
|
||||
*/
|
||||
if ((Instance *) node->data == instance)
|
||||
if (((GUARD_NODE *) node->data)->instance == instance)
|
||||
{
|
||||
guarded_instances= list_delete(guarded_instances, node);
|
||||
pthread_mutex_unlock(&LOCK_guardian);
|
||||
@ -212,3 +362,73 @@ int Guardian_thread::stop_guard(Instance *instance)
|
||||
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_sys.h>
|
||||
#include <my_list.h>
|
||||
#include "thread_registry.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma interface
|
||||
#endif
|
||||
|
||||
class Instance;
|
||||
class Instance_map;
|
||||
|
||||
#include "thread_registry.h"
|
||||
#include "instance.h"
|
||||
class Thread_registry;
|
||||
struct GUARD_NODE;
|
||||
|
||||
C_MODE_START
|
||||
|
||||
@ -35,12 +36,11 @@ pthread_handler_decl(guardian, arg);
|
||||
|
||||
C_MODE_END
|
||||
|
||||
|
||||
struct Guardian_thread_args
|
||||
{
|
||||
Thread_registry &thread_registry;
|
||||
Instance_map *instance_map;
|
||||
uint monitoring_interval;
|
||||
int monitoring_interval;
|
||||
|
||||
Guardian_thread_args(Thread_registry &thread_registry_arg,
|
||||
Instance_map *instance_map_arg,
|
||||
@ -60,27 +60,67 @@ struct Guardian_thread_args
|
||||
class Guardian_thread: public Guardian_thread_args
|
||||
{
|
||||
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,
|
||||
Instance_map *instance_map_arg,
|
||||
uint monitoring_interval_arg);
|
||||
~Guardian_thread();
|
||||
/* Main funtion of the thread */
|
||||
void run();
|
||||
/* Initialize or refresh the list of guarded instances */
|
||||
int init();
|
||||
int start();
|
||||
int guard(Instance *instance);
|
||||
/* Request guardian shutdown. Stop instances if needed */
|
||||
void request_shutdown(bool stop_instances);
|
||||
/* Start instance protection */
|
||||
int guard(Instance *instance, bool nolock= FALSE);
|
||||
/* Stop instance protection */
|
||||
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:
|
||||
int add_instance_to_list(Instance *instance, LIST **list);
|
||||
void move_to_list(LIST **from, LIST **to);
|
||||
/* Prepares Guardian shutdown. Stops instances is needed */
|
||||
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:
|
||||
pthread_mutex_t LOCK_guardian;
|
||||
Thread_info thread_info;
|
||||
LIST *guarded_instances;
|
||||
LIST *starting_instances;
|
||||
MEM_ROOT alloc;
|
||||
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 */
|
||||
|
@ -21,11 +21,34 @@
|
||||
#include "instance.h"
|
||||
#include "mysql_manager_error.h"
|
||||
#include "log.h"
|
||||
#include "instance_map.h"
|
||||
#include "priv.h"
|
||||
|
||||
#include <my_sys.h>
|
||||
#include <signal.h>
|
||||
#include <m_string.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.
|
||||
|
||||
@ -43,85 +66,175 @@ int Instance::start()
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* clear crash flag */
|
||||
pthread_mutex_lock(&LOCK_instance);
|
||||
crashed= 0;
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
|
||||
|
||||
if (!is_running())
|
||||
{
|
||||
log_info("trying to start instance %s", options.instance_name);
|
||||
switch (pid= fork()) {
|
||||
case 0:
|
||||
if (fork()) /* zombie protection */
|
||||
exit(0); /* parent goes bye-bye */
|
||||
else
|
||||
{
|
||||
execv(options.mysqld_path, options.argv);
|
||||
exit(1);
|
||||
}
|
||||
case -1:
|
||||
if ((pid= options.get_pid()) != 0) /* check the pidfile */
|
||||
if (options.unlink_pidfile()) /* remove stalled pidfile */
|
||||
log_error("cannot remove pidfile for instance %i, this might be \
|
||||
since IM lacks permmissions or hasn't found the pidifle",
|
||||
options.instance_name);
|
||||
|
||||
/*
|
||||
No need to monitor this thread in the Thread_registry, as all
|
||||
instances are to be stopped during shutdown.
|
||||
*/
|
||||
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;
|
||||
default:
|
||||
waitpid(pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the instance is started already */
|
||||
return ER_INSTANCE_ALREADY_STARTED;
|
||||
}
|
||||
|
||||
int Instance::cleanup()
|
||||
|
||||
void Instance::fork_and_monitor()
|
||||
{
|
||||
/*
|
||||
We cannot close connection in destructor, as mysql_close needs alarm
|
||||
services which are definitely unavailaible at the time of destructor
|
||||
call.
|
||||
*/
|
||||
if (is_connected)
|
||||
mysql_close(&mysql);
|
||||
return 0;
|
||||
pid_t pid;
|
||||
log_info("starting instance %s", options.instance_name);
|
||||
switch (pid= fork()) {
|
||||
case 0:
|
||||
execv(options.mysqld_path, options.argv);
|
||||
/* exec never returns */
|
||||
exit(1);
|
||||
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()
|
||||
{
|
||||
pthread_cond_destroy(&COND_instance_stopped);
|
||||
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()
|
||||
{
|
||||
MYSQL mysql;
|
||||
uint port= 0;
|
||||
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)
|
||||
port= atoi(strchr(options.mysqld_port, '=') + 1);
|
||||
port= options.mysqld_port_val;
|
||||
|
||||
if (options.mysqld_socket)
|
||||
socket= strchr(options.mysqld_socket, '=') + 1;
|
||||
|
||||
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,
|
||||
options.mysqld_password,
|
||||
NullS, port,
|
||||
socket, 0))
|
||||
/*
|
||||
We have successfully connected to the server using fake
|
||||
username/password. Write a warning to the logfile.
|
||||
*/
|
||||
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;
|
||||
is_connected= TRUE;
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return TRUE;
|
||||
return_val= TRUE;
|
||||
}
|
||||
mysql_close(&mysql);
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return FALSE;
|
||||
}
|
||||
else if (!mysql_ping(&mysql))
|
||||
{
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return TRUE;
|
||||
else
|
||||
return_val= FALSE;
|
||||
}
|
||||
|
||||
mysql_close(&mysql);
|
||||
pthread_mutex_unlock(&LOCK_instance);
|
||||
return FALSE;
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
@ -139,22 +252,67 @@ bool Instance::is_running()
|
||||
|
||||
int Instance::stop()
|
||||
{
|
||||
if (is_running())
|
||||
{
|
||||
if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT))
|
||||
goto err;
|
||||
pid_t pid;
|
||||
struct timespec timeout;
|
||||
uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY;
|
||||
|
||||
mysql_close(&mysql);
|
||||
is_connected= FALSE;
|
||||
return 0;
|
||||
if (options.shutdown_delay_val)
|
||||
waitchild= options.shutdown_delay_val;
|
||||
|
||||
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;
|
||||
err:
|
||||
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.
|
||||
Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY.
|
||||
@ -162,7 +320,14 @@ err:
|
||||
|
||||
int Instance::init(const char *name_arg)
|
||||
{
|
||||
pthread_mutex_init(&LOCK_instance, 0);
|
||||
|
||||
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
|
||||
#endif
|
||||
|
||||
class Instance_map;
|
||||
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
Instance(): is_connected(FALSE)
|
||||
{}
|
||||
Instance();
|
||||
|
||||
~Instance();
|
||||
|
||||
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();
|
||||
int start();
|
||||
int stop();
|
||||
int cleanup();
|
||||
/* send a signal to the instance */
|
||||
void kill_instance(int signo);
|
||||
int is_crashed();
|
||||
void fork_and_monitor();
|
||||
|
||||
public:
|
||||
enum { DEFAULT_SHUTDOWN_DELAY= 35 };
|
||||
Instance_options options;
|
||||
|
||||
/* connection to the instance */
|
||||
MYSQL mysql;
|
||||
|
||||
private:
|
||||
/*
|
||||
Mutex protecting the instance. Currently we use it to avoid the
|
||||
double start of the instance. This happens when the instance is starting
|
||||
and we issue the start command once more.
|
||||
*/
|
||||
int crashed;
|
||||
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 */
|
||||
|
@ -74,7 +74,7 @@ static void delete_instance(void *u)
|
||||
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 *instance= NULL;
|
||||
@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option)
|
||||
|
||||
map = (Instance_map*) ctx;
|
||||
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)
|
||||
{
|
||||
@ -110,13 +111,9 @@ C_MODE_END
|
||||
|
||||
|
||||
Instance_map::Instance_map(const char *default_mysqld_path_arg,
|
||||
const char *default_admin_user_arg,
|
||||
const char *default_admin_password_arg)
|
||||
const char *first_option_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);
|
||||
}
|
||||
|
||||
@ -154,12 +151,15 @@ int Instance_map::flush_instances()
|
||||
{
|
||||
int rc;
|
||||
|
||||
guardian->lock();
|
||||
pthread_mutex_lock(&LOCK_instance_map);
|
||||
hash_free(&hash);
|
||||
hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0,
|
||||
get_instance_key, delete_instance, 0);
|
||||
pthread_mutex_unlock(&LOCK_instance_map);
|
||||
rc= load();
|
||||
guardian->init();
|
||||
guardian->unlock();
|
||||
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;
|
||||
uint i= 0;
|
||||
|
||||
while (i < hash.records)
|
||||
|
||||
if (hash.records == 0) /* no instances found */
|
||||
{
|
||||
instance= (Instance *) hash_element(&hash, i);
|
||||
instance->options.complete_initialization(mysqld_path, user, password);
|
||||
i++;
|
||||
if ((instance= new Instance) == 0)
|
||||
goto err;
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
|
||||
int Instance_map::cleanup()
|
||||
{
|
||||
Instance *instance;
|
||||
uint i= 0;
|
||||
|
||||
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;
|
||||
return 0;
|
||||
err:
|
||||
return 1;
|
||||
err_instance:
|
||||
delete instance;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -230,13 +226,30 @@ Instance_map::find(uint instance_number)
|
||||
|
||||
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:
|
||||
/* returns a pointer to the instance or NULL, if there is no such instance */
|
||||
Instance *find(const char *name, uint name_len);
|
||||
Instance *find(uint instance_number);
|
||||
|
||||
int flush_instances();
|
||||
int cleanup();
|
||||
int lock();
|
||||
int unlock();
|
||||
int init();
|
||||
|
||||
Instance_map(const char *default_mysqld_path_arg,
|
||||
const char *default_admin_user_arg,
|
||||
const char *default_admin_password_arg);
|
||||
const char *first_option_arg);
|
||||
~Instance_map();
|
||||
|
||||
/* loads options from config files */
|
||||
@ -75,16 +72,14 @@ public:
|
||||
/* adds instance to internal hash */
|
||||
int add_instance(Instance *instance);
|
||||
/* inits instances argv's after all options have been loaded */
|
||||
void complete_initialization();
|
||||
int complete_initialization();
|
||||
|
||||
public:
|
||||
const char *mysqld_path;
|
||||
/* user an password to shutdown MySQL */
|
||||
const char *user;
|
||||
const char *password;
|
||||
Guardian_thread *guardian;
|
||||
|
||||
private:
|
||||
const char *first_option;
|
||||
enum { START_HASH_SIZE = 16 };
|
||||
pthread_mutex_t LOCK_instance_map;
|
||||
HASH hash;
|
||||
|
@ -19,43 +19,170 @@
|
||||
#endif
|
||||
|
||||
#include "instance_options.h"
|
||||
#include "parse_output.h"
|
||||
#include "buffer.h"
|
||||
#include <my_sys.h>
|
||||
#include <mysql.h>
|
||||
#include <signal.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 */
|
||||
if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1
|
||||
+ MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*))))
|
||||
goto err;
|
||||
int position= 0;
|
||||
int rc= 1;
|
||||
char verbose_option[]= " --no-defaults --verbose --help";
|
||||
|
||||
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)))
|
||||
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))
|
||||
goto err;
|
||||
|
||||
/* the following options are not for argv */
|
||||
if (mysqld_user == NULL)
|
||||
{
|
||||
if (!(mysqld_user= strdup_root(&alloc, default_user)))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (mysqld_password == NULL)
|
||||
{
|
||||
if (!(mysqld_password= strdup_root(&alloc, default_password)))
|
||||
goto err;
|
||||
}
|
||||
if (add_to_argv(tmp))
|
||||
goto err;
|
||||
|
||||
memcpy((gptr) (argv + filled_default_options), options_array.buffer,
|
||||
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},
|
||||
{"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD},
|
||||
{"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE},
|
||||
{"--admin-user=", 13, &mysqld_user, SAVE_VALUE},
|
||||
{"--admin-password=", 17, &mysqld_password, SAVE_VALUE},
|
||||
{"--guarded", 9, &is_guarded, SAVE_WHOLE},
|
||||
{"--nonguarded", 9, &nonguarded, SAVE_WHOLE},
|
||||
{"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
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;
|
||||
|
||||
err:
|
||||
@ -142,12 +271,24 @@ int Instance_options::add_to_argv(const char* option)
|
||||
{
|
||||
DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS);
|
||||
|
||||
if (option != NULL)
|
||||
if ((option))
|
||||
argv[filled_default_options++]= (char *) option;
|
||||
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.
|
||||
Return value: 0 - ok. 1 - unable to allocate memory.
|
||||
|
@ -37,22 +37,31 @@ class Instance_options
|
||||
{
|
||||
public:
|
||||
Instance_options() :
|
||||
mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0),
|
||||
mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0),
|
||||
mysqld_password(0), is_guarded(0), filled_default_options(0)
|
||||
mysqld_socket(0), mysqld_datadir(0),
|
||||
mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0),
|
||||
mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0),
|
||||
shutdown_delay_val(0), filled_default_options(0)
|
||||
{}
|
||||
~Instance_options();
|
||||
/* fills in argv */
|
||||
int complete_initialization(const char *default_path,
|
||||
const char *default_user,
|
||||
const char *default_password);
|
||||
int complete_initialization(const char *default_path, int only_instance);
|
||||
|
||||
int add_option(const char* option);
|
||||
int init(const char *instance_name_arg);
|
||||
pid_t get_pid();
|
||||
int get_pid_filename(char *result);
|
||||
int unlink_pidfile();
|
||||
void print_argv();
|
||||
|
||||
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 };
|
||||
char pid_file_with_path[MAX_PATH_LEN];
|
||||
char **argv;
|
||||
/* We need the some options, so we store them as a separate pointers */
|
||||
const char *mysqld_socket;
|
||||
@ -60,15 +69,19 @@ public:
|
||||
const char *mysqld_bind_address;
|
||||
const char *mysqld_pid_file;
|
||||
const char *mysqld_port;
|
||||
uint instance_name_len;
|
||||
uint mysqld_port_val;
|
||||
const char *instance_name;
|
||||
uint instance_name_len;
|
||||
const char *mysqld_path;
|
||||
const char *mysqld_user;
|
||||
const char *mysqld_password;
|
||||
const char *is_guarded;
|
||||
const char *nonguarded;
|
||||
const char *shutdown_delay;
|
||||
uint shutdown_delay_val;
|
||||
/* this value is computed and cashed here */
|
||||
DYNAMIC_ARRAY options_array;
|
||||
private:
|
||||
int add_to_argv(const char *option);
|
||||
int get_default_option(char *result, size_t result_len,
|
||||
const char *option_name);
|
||||
private:
|
||||
uint filled_default_options;
|
||||
MEM_ROOT alloc;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "instance_map.h"
|
||||
#include "log.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)
|
||||
,thread_info(pthread_self())
|
||||
{
|
||||
thread_registry.register_thread(&thread_info);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
int flags;
|
||||
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 */
|
||||
|
||||
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",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct sockaddr_in ip_socket_address;
|
||||
@ -104,7 +115,7 @@ void Listener_thread::run()
|
||||
}
|
||||
else
|
||||
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_addr.s_addr = im_bind_addr;
|
||||
@ -119,16 +130,14 @@ void Listener_thread::run()
|
||||
{
|
||||
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (listen(ip_socket, LISTEN_BACK_LOG_SIZE))
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
/* set the socket nonblocking */
|
||||
flags= fcntl(ip_socket, F_GETFL, 0);
|
||||
@ -140,13 +149,12 @@ void Listener_thread::run()
|
||||
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)
|
||||
{
|
||||
log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct sockaddr_un unix_socket_address;
|
||||
@ -169,8 +177,7 @@ void Listener_thread::run()
|
||||
log_error("Listener_thread::run(): bind(unix socket) failed, "
|
||||
"socket file name is '%s', error '%s'",
|
||||
unix_socket_address.sun_path, strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
umask(old_mask);
|
||||
|
||||
@ -178,8 +185,7 @@ void Listener_thread::run()
|
||||
{
|
||||
log_error("Listener_thread::run(): listen(unix socket) failed, %s",
|
||||
strerror(errno));
|
||||
thread_registry.request_shutdown();
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* set the socket nonblocking */
|
||||
@ -205,7 +211,15 @@ void Listener_thread::run()
|
||||
while (thread_registry.is_shutdown() == false)
|
||||
{
|
||||
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);
|
||||
|
||||
|
||||
if (rc == -1 && errno != EINTR)
|
||||
log_error("Listener_thread::run(): select() failed, %s",
|
||||
strerror(errno));
|
||||
@ -256,6 +270,16 @@ void Listener_thread::run()
|
||||
close(unix_socket);
|
||||
close(ip_socket);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,8 +32,8 @@
|
||||
SYNOPSYS
|
||||
log()
|
||||
*/
|
||||
|
||||
static inline void log(FILE *file, const char *format, va_list args)
|
||||
|
||||
static inline void log(FILE *file, const char *format, va_list args)
|
||||
{
|
||||
/*
|
||||
log() should be thread-safe; it implies that we either call fprintf()
|
||||
|
@ -17,14 +17,14 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Logging facilities.
|
||||
Logging facilities.
|
||||
|
||||
Two logging streams are supported: error log and info log. Additionally
|
||||
libdbug may be used for debug information output.
|
||||
ANSI C buffered I/O is used to perform logging.
|
||||
Logging is performed via stdout/stder, so one can reopen them to point to
|
||||
ordinary files. To initialize loggin environment log_init() must be called.
|
||||
|
||||
|
||||
Rationale:
|
||||
- no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h
|
||||
- no constructors/desctructors to make logging available all the time
|
||||
|
@ -16,12 +16,7 @@
|
||||
|
||||
#include "manager.h"
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <m_string.h>
|
||||
#include <signal.h>
|
||||
#include <thr_alarm.h>
|
||||
|
||||
#include "priv.h"
|
||||
#include "thread_registry.h"
|
||||
#include "listener.h"
|
||||
#include "instance_map.h"
|
||||
@ -30,6 +25,14 @@
|
||||
#include "log.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)
|
||||
{
|
||||
if (FILE *pid_file= my_fopen(pid_file_name,
|
||||
@ -65,9 +68,7 @@ void manager(const Options &options)
|
||||
*/
|
||||
|
||||
User_map user_map;
|
||||
Instance_map instance_map(options.default_mysqld_path,
|
||||
options.default_admin_user,
|
||||
options.default_admin_password);
|
||||
Instance_map instance_map(options.default_mysqld_path, options.first_option);
|
||||
Guardian_thread guardian_thread(thread_registry,
|
||||
&instance_map,
|
||||
options.monitoring_interval);
|
||||
@ -75,10 +76,22 @@ void manager(const Options &options)
|
||||
Listener_thread_args listener_args(thread_registry, options, user_map,
|
||||
instance_map);
|
||||
|
||||
manager_pid= getpid();
|
||||
instance_map.guardian= &guardian_thread;
|
||||
|
||||
if (instance_map.init() || user_map.init() || instance_map.load() ||
|
||||
user_map.load(options.password_file_name))
|
||||
if (instance_map.init() || user_map.init())
|
||||
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;
|
||||
|
||||
/* write pid file */
|
||||
@ -126,6 +139,12 @@ void manager(const Options &options)
|
||||
pthread_attr_t guardian_thd_attr;
|
||||
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_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED);
|
||||
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.
|
||||
*/
|
||||
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
|
||||
alarm structures initialization as we have to use net_* functions while
|
||||
making the list. And they in their turn need alarms for timeout suppport.
|
||||
After the list of guarded instances have been initialized,
|
||||
Guardian should start them.
|
||||
*/
|
||||
guardian_thread.start();
|
||||
pthread_cond_signal(&guardian_thread.COND_guardian);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (!shutdown_complete)
|
||||
{
|
||||
sigwait(&mask, &signo);
|
||||
int status= 0;
|
||||
|
||||
if ((status= my_sigwait(&mask, &signo)) != 0)
|
||||
{
|
||||
log_error("sigwait() failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (signo)
|
||||
{
|
||||
case THR_SERVER_ALARM:
|
||||
process_alarm(signo);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -181,9 +224,6 @@ err:
|
||||
/* delete the pid file */
|
||||
my_delete(options.pid_file_name, MYF(0));
|
||||
|
||||
/* close permanent connections to the running instances */
|
||||
instance_map.cleanup();
|
||||
|
||||
/* free alarm structures */
|
||||
end_thr_alarm(1);
|
||||
/* 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:
|
||||
return "Bad instance name. Check that the instance with such a name exists";
|
||||
case ER_INSTANCE_IS_NOT_STARTED:
|
||||
return "Cannot stop instance. Perhaps the instance is not started or you"
|
||||
" have specified wrong username/password in the config file";
|
||||
return "Cannot stop instance. Perhaps the instance is not started, or was started"
|
||||
"manually, so IM cannot find the pidfile.";
|
||||
case ER_INSTANCE_ALREADY_STARTED:
|
||||
return "The instance is already started";
|
||||
case ER_CANNOT_START_INSTANCE:
|
||||
|
@ -82,7 +82,6 @@ private:
|
||||
private:
|
||||
/* Names are conventionally the same as in mysqld */
|
||||
int check_connection();
|
||||
int check_user(const char *user, const char *password);
|
||||
int do_command();
|
||||
int dispatch_command(enum enum_server_command command,
|
||||
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()
|
||||
{
|
||||
char *packet;
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <my_sys.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -54,6 +56,8 @@
|
||||
static void init_environment(char *progname);
|
||||
static void daemonize(const char *log_file_name);
|
||||
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]);
|
||||
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)
|
||||
{
|
||||
/* forks, and returns only in child */
|
||||
@ -77,11 +94,84 @@ int main(int argc, char *argv[])
|
||||
angel(options);
|
||||
}
|
||||
manager(options);
|
||||
options.cleanup();
|
||||
my_end(0);
|
||||
return 0;
|
||||
err:
|
||||
my_end(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************* 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
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_getopt.h>
|
||||
#include <m_string.h>
|
||||
#include <mysql_com.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::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
||||
const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH);
|
||||
const char *Options::default_admin_user= QUOTE(DEFAULT_USER);
|
||||
const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD);
|
||||
const char *Options::bind_address= 0; /* No default value */
|
||||
const char *Options::first_option= 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::port_number= DEFAULT_PORT;
|
||||
/* just to declare */
|
||||
char **Options::saved_argv;
|
||||
|
||||
/*
|
||||
List of options, accepted by the instance manager.
|
||||
@ -54,9 +58,6 @@ enum options {
|
||||
OPT_MYSQLD_PATH,
|
||||
OPT_RUN_AS_SERVICE,
|
||||
OPT_USER,
|
||||
OPT_PASSWORD,
|
||||
OPT_DEFAULT_ADMIN_USER,
|
||||
OPT_DEFAULT_ADMIN_PASSWORD,
|
||||
OPT_MONITORING_INTERVAL,
|
||||
OPT_PORT,
|
||||
OPT_BIND_ADDRESS
|
||||
@ -79,13 +80,16 @@ static struct my_option my_long_options[] =
|
||||
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
||||
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.",
|
||||
(gptr *) &Options::bind_address, (gptr *) &Options::bind_address,
|
||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||
|
||||
{ "port", OPT_PORT, "Port number to use for connections",
|
||||
(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"
|
||||
" and passwords here.",
|
||||
@ -98,28 +102,22 @@ static struct my_option my_long_options[] =
|
||||
(gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path,
|
||||
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"
|
||||
" in seconds.",
|
||||
(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,
|
||||
"Daemonize and start angel process.", (gptr *) &Options::run_as_service,
|
||||
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,
|
||||
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
|
||||
|
||||
@ -150,6 +148,38 @@ static void usage()
|
||||
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
|
||||
|
||||
static my_bool
|
||||
@ -161,7 +191,9 @@ get_one_option(int optid,
|
||||
case 'V':
|
||||
version();
|
||||
exit(0);
|
||||
case 'I':
|
||||
case 'P':
|
||||
passwd();
|
||||
exit(0);
|
||||
case '?':
|
||||
usage();
|
||||
exit(0);
|
||||
@ -180,12 +212,28 @@ C_MODE_END
|
||||
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 */
|
||||
load_defaults("my", default_groups, &argc, &argv);
|
||||
|
||||
if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option))
|
||||
exit(rc);
|
||||
if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0)
|
||||
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 *password_file_name;
|
||||
static const char *default_mysqld_path;
|
||||
static const char *default_admin_user;
|
||||
static const char *default_admin_password;
|
||||
static const char *user;
|
||||
/* the option which should be passed to process_default_option_files */
|
||||
static const char *first_option;
|
||||
static uint monitoring_interval;
|
||||
static uint port_number;
|
||||
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
|
||||
|
@ -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
|
||||
TOK_NOT_FOUND
|
||||
|
@ -20,4 +20,34 @@
|
||||
|
||||
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 */
|
||||
|
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"
|
||||
|
||||
/* 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
|
||||
mysql_connection.cc relies on it
|
||||
|
@ -16,6 +16,19 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
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 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, field->name, &position); /* column name */
|
||||
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;
|
||||
send_buff.buffer[position++]= 12;
|
||||
int2store(send_buff.buffer + position, 1); /* charsetnr */
|
||||
|
@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown()
|
||||
if (info->current_cond)
|
||||
pthread_cond_signal(info->current_cond);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
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));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), file))
|
||||
|
@ -276,6 +276,7 @@ public:
|
||||
virtual bool get_date(TIME *ltime,uint fuzzydate);
|
||||
virtual bool get_time(TIME *ltime);
|
||||
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 void set_charset(CHARSET_INFO *charset) { }
|
||||
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 eq_def(Field *field);
|
||||
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; }
|
||||
};
|
||||
|
||||
|
@ -1198,12 +1198,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset)
|
||||
else
|
||||
{
|
||||
sortorder->length=sortorder->field->pack_length();
|
||||
/*
|
||||
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)
|
||||
if (use_strnxfrm((cs=sortorder->field->sort_charset())))
|
||||
{
|
||||
sortorder->need_strxnfrm= 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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -336,14 +336,18 @@ innobase_release_temporary_latches(
|
||||
/*===============================*/
|
||||
THD *thd)
|
||||
{
|
||||
trx_t* trx;
|
||||
|
||||
if (!innodb_inited) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
trx_t *trx= (trx_t*) thd->ha_data[innobase_hton.slot];
|
||||
if (trx)
|
||||
innobase_release_stat_resources(trx);
|
||||
trx = (trx_t*) thd->ha_data[innobase_hton.slot];
|
||||
|
||||
if (trx) {
|
||||
innobase_release_stat_resources(trx);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
@ -743,14 +747,15 @@ transaction internally. */
|
||||
static
|
||||
void
|
||||
register_trans(
|
||||
/*============*/
|
||||
/*===========*/
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1051,7 +1056,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
|
||||
/*************************************************************************
|
||||
Opens an InnoDB database. */
|
||||
|
||||
handlerton *
|
||||
handlerton*
|
||||
innobase_init(void)
|
||||
/*===============*/
|
||||
/* out: TRUE if error */
|
||||
@ -1220,7 +1225,7 @@ innobase_init(void)
|
||||
|
||||
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 */
|
||||
|
||||
data_mysql_default_charset_coll = (ulint)default_charset_info->number;
|
||||
@ -1346,14 +1351,16 @@ innobase_commit_low(
|
||||
return;
|
||||
}
|
||||
|
||||
/* The following will be enabled later when we put the 4.1 functionality back
|
||||
to 5.0. */
|
||||
#ifdef DISABLE_HAVE_REPLICATION
|
||||
if (current_thd->slave_thread) {
|
||||
/* Update the replication position info inside InnoDB */
|
||||
|
||||
trx->mysql_master_log_file_name
|
||||
= active_mi->rli.group_master_log_name;
|
||||
trx->mysql_master_log_pos= ((ib_longlong)
|
||||
active_mi->rli.future_group_master_log_pos);
|
||||
trx->mysql_master_log_pos = ((ib_longlong)
|
||||
active_mi->rli.future_group_master_log_pos);
|
||||
}
|
||||
#endif /* HAVE_REPLICATION */
|
||||
|
||||
@ -1456,7 +1463,8 @@ innobase_commit(
|
||||
"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
|
||||
this is an SQL statement end and autocommit is on */
|
||||
@ -1489,10 +1497,9 @@ innobase_commit(
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
don't delete it - it may be re-enabled later
|
||||
as an optimization for the most common case InnoDB+binlog
|
||||
*/
|
||||
/* The following defined-out code will be enabled later when we put the
|
||||
MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup
|
||||
to work. */
|
||||
#if 0
|
||||
/*********************************************************************
|
||||
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);
|
||||
}
|
||||
|
||||
if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
||||
if (all
|
||||
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
|
||||
|
||||
error = trx_rollback_for_mysql(trx);
|
||||
trx->active_trans = 0;
|
||||
@ -1686,6 +1694,7 @@ innobase_rollback_to_savepoint(
|
||||
ib_longlong mysql_binlog_cache_pos;
|
||||
int error = 0;
|
||||
trx_t* trx;
|
||||
char name[64];
|
||||
|
||||
DBUG_ENTER("innobase_rollback_to_savepoint");
|
||||
|
||||
@ -1698,8 +1707,8 @@ innobase_rollback_to_savepoint(
|
||||
innobase_release_stat_resources(trx);
|
||||
|
||||
/* 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,
|
||||
&mysql_binlog_cache_pos);
|
||||
@ -1708,26 +1717,27 @@ innobase_rollback_to_savepoint(
|
||||
|
||||
/*********************************************************************
|
||||
Release transaction savepoint name. */
|
||||
|
||||
static int
|
||||
static
|
||||
int
|
||||
innobase_release_savepoint(
|
||||
/*===========================*/
|
||||
/*=======================*/
|
||||
/* out: 0 if success, HA_ERR_NO_SAVEPOINT if
|
||||
no savepoint with the given name */
|
||||
THD* thd, /* in: handle to the MySQL thread of the user
|
||||
whose transaction should be rolled back */
|
||||
void *savepoint) /* in: savepoint data */
|
||||
void* savepoint) /* in: savepoint data */
|
||||
{
|
||||
int error = 0;
|
||||
trx_t* trx;
|
||||
char name[64];
|
||||
|
||||
DBUG_ENTER("innobase_release_savepoint");
|
||||
|
||||
trx = check_trx_exists(thd);
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -1736,13 +1746,13 @@ innobase_release_savepoint(
|
||||
|
||||
/*********************************************************************
|
||||
Sets a transaction savepoint. */
|
||||
|
||||
static int
|
||||
static
|
||||
int
|
||||
innobase_savepoint(
|
||||
/*===============*/
|
||||
/* out: always 0, that is, always succeeds */
|
||||
THD* thd, /* in: handle to the MySQL thread */
|
||||
void *savepoint) /* in: savepoint data */
|
||||
void* savepoint) /* in: savepoint data */
|
||||
{
|
||||
int error = 0;
|
||||
trx_t* trx;
|
||||
@ -1911,7 +1921,8 @@ ha_innobase::open(
|
||||
fields when packed actually became 1 byte longer, when we also
|
||||
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;
|
||||
if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME),
|
||||
&upd_buff, upd_and_key_val_buff_len,
|
||||
@ -1963,7 +1974,8 @@ ha_innobase::open(
|
||||
|
||||
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 */
|
||||
|
||||
@ -1985,13 +1997,11 @@ ha_innobase::open(
|
||||
|
||||
((row_prebuilt_t*)innobase_prebuilt)
|
||||
->clust_index_was_generated = FALSE;
|
||||
/*
|
||||
MySQL allocates the buffer for ref. key_info->key_length
|
||||
includes space for all key columns + one byte for each column
|
||||
that may be NULL. ref_length must be as exact as possible to
|
||||
save space, because all row reference buffers are allocated
|
||||
based on ref_length.
|
||||
*/
|
||||
/* MySQL allocates the buffer for ref. key_info->key_length
|
||||
includes space for all key columns + one byte for each column
|
||||
that may be NULL. ref_length must be as exact as possible to
|
||||
save space, because all row reference buffers are allocated
|
||||
based on ref_length. */
|
||||
|
||||
ref_length = table->key_info[primary_key].key_length;
|
||||
} else {
|
||||
@ -2013,15 +2023,13 @@ ha_innobase::open(
|
||||
|
||||
ref_length = DATA_ROW_ID_LEN;
|
||||
|
||||
/*
|
||||
If we automatically created the clustered index, then
|
||||
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
|
||||
update the column of the index. That is why we assert below
|
||||
that key_used_on_scan is the undefined value MAX_KEY.
|
||||
The column is the row id in the automatical generation case,
|
||||
and it will never be updated anyway.
|
||||
*/
|
||||
/* If we automatically created the clustered index, then
|
||||
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
|
||||
update the column of the index. That is why we assert below
|
||||
that key_used_on_scan is the undefined value MAX_KEY.
|
||||
The column is the row id in the automatical generation case,
|
||||
and it will never be updated anyway. */
|
||||
|
||||
if (key_used_on_scan != MAX_KEY) {
|
||||
fprintf(stderr,
|
||||
@ -2611,7 +2619,8 @@ ha_innobase::write_row(
|
||||
"InnoDB: Dump of 200 bytes around transaction.all: ",
|
||||
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);
|
||||
ut_error;
|
||||
}
|
||||
@ -2646,7 +2655,7 @@ ha_innobase::write_row(
|
||||
src_table = lock_get_src_table(
|
||||
prebuilt->trx, prebuilt->table, &mode);
|
||||
if (!src_table) {
|
||||
no_commit:
|
||||
no_commit:
|
||||
/* Unknown situation: do not commit */
|
||||
/*
|
||||
ut_print_timestamp(stderr);
|
||||
@ -2669,6 +2678,7 @@ ha_innobase::write_row(
|
||||
} else {
|
||||
/* Ensure that there are no other table locks than
|
||||
LOCK_IX and LOCK_AUTO_INC on the destination table. */
|
||||
|
||||
if (!lock_is_table_exclusive(prebuilt->table,
|
||||
prebuilt->trx)) {
|
||||
goto no_commit;
|
||||
@ -2746,11 +2756,11 @@ ha_innobase::write_row(
|
||||
|
||||
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
|
||||
value and the value supplied by the user and
|
||||
update the counter accordingly */
|
||||
@ -2762,15 +2772,15 @@ ha_innobase::write_row(
|
||||
The lock is released at each SQL statement's
|
||||
end. */
|
||||
|
||||
error = row_lock_table_autoinc_for_mysql(prebuilt);
|
||||
error = row_lock_table_autoinc_for_mysql(prebuilt);
|
||||
|
||||
if (error != DB_SUCCESS) {
|
||||
|
||||
error = convert_error_code_to_mysql(error, user_thd);
|
||||
goto func_exit;
|
||||
}
|
||||
dict_table_autoinc_update(prebuilt->table, auto_inc);
|
||||
}
|
||||
if (error != DB_SUCCESS) {
|
||||
error = convert_error_code_to_mysql(error,
|
||||
user_thd);
|
||||
goto func_exit;
|
||||
}
|
||||
dict_table_autoinc_update(prebuilt->table, auto_inc);
|
||||
}
|
||||
}
|
||||
|
||||
innodb_srv_conc_exit_innodb(prebuilt->trx);
|
||||
@ -2785,7 +2795,6 @@ func_exit:
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************
|
||||
Converts field data for storage in an InnoDB update vector. */
|
||||
inline
|
||||
@ -3071,9 +3080,6 @@ ha_innobase::unlock_row(void)
|
||||
|
||||
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) {
|
||||
ut_print_timestamp(stderr);
|
||||
fprintf(stderr,
|
||||
@ -4512,10 +4518,10 @@ ha_innobase::records_in_range(
|
||||
dict_index_t* index;
|
||||
mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc(
|
||||
table->s->reclength
|
||||
+ table->s->max_key_length + 100,
|
||||
+ table->s->max_key_length + 100,
|
||||
MYF(MY_WME));
|
||||
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_end;
|
||||
ib_longlong n_rows;
|
||||
@ -4672,21 +4678,27 @@ ha_innobase::read_time(
|
||||
ha_rows total_rows;
|
||||
double time_for_scan;
|
||||
|
||||
if (index != table->s->primary_key)
|
||||
return handler::read_time(index, ranges, rows); // Not clustered
|
||||
if (index != table->s->primary_key) {
|
||||
/* Not clustered */
|
||||
return(handler::read_time(index, ranges, rows));
|
||||
}
|
||||
|
||||
if (rows <= 2)
|
||||
return (double) rows;
|
||||
if (rows <= 2) {
|
||||
|
||||
return((double) rows);
|
||||
}
|
||||
|
||||
/* Assume that the read time is proportional to the scan time for all
|
||||
rows + at most one seek per range. */
|
||||
|
||||
time_for_scan = scan_time();
|
||||
|
||||
if ((total_rows = estimate_rows_upper_bound()) < rows)
|
||||
return time_for_scan;
|
||||
if ((total_rows = estimate_rows_upper_bound()) < rows) {
|
||||
|
||||
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;
|
||||
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;;)
|
||||
{
|
||||
@ -5622,7 +5634,6 @@ innodb_export_status(void)
|
||||
srv_export_innodb_status();
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB
|
||||
Monitor to the client. */
|
||||
@ -5634,6 +5645,8 @@ innodb_show_status(
|
||||
{
|
||||
Protocol *protocol= thd->protocol;
|
||||
trx_t* trx;
|
||||
long flen;
|
||||
char* str;
|
||||
|
||||
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. */
|
||||
|
||||
long flen;
|
||||
char* str;
|
||||
|
||||
mutex_enter_noninline(&srv_monitor_file_mutex);
|
||||
rewind(srv_monitor_file);
|
||||
|
||||
srv_printf_innodb_monitor(srv_monitor_file);
|
||||
flen = ftell(srv_monitor_file);
|
||||
os_file_set_eof(srv_monitor_file);
|
||||
|
||||
if (flen < 0) {
|
||||
flen = 0;
|
||||
} else if (flen > 64000 - 1) {
|
||||
@ -5667,10 +5679,10 @@ innodb_show_status(
|
||||
/* allocate buffer for the string, and
|
||||
read the contents of the temporary file */
|
||||
|
||||
if (!(str = my_malloc(flen + 1, MYF(0))))
|
||||
{
|
||||
mutex_exit_noninline(&srv_monitor_file_mutex);
|
||||
DBUG_RETURN(TRUE);
|
||||
if (!(str = my_malloc(flen + 1, MYF(0)))) {
|
||||
mutex_exit_noninline(&srv_monitor_file_mutex);
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
rewind(srv_monitor_file);
|
||||
@ -5684,7 +5696,6 @@ innodb_show_status(
|
||||
|
||||
if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
|
||||
Protocol::SEND_EOF)) {
|
||||
|
||||
my_free(str, MYF(0));
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -5694,10 +5705,12 @@ innodb_show_status(
|
||||
protocol->store(str, flen, system_charset_info);
|
||||
my_free(str, MYF(0));
|
||||
|
||||
if (protocol->write())
|
||||
DBUG_RETURN(TRUE);
|
||||
if (protocol->write()) {
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
send_eof(thd);
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
@ -6091,7 +6104,7 @@ ha_innobase::get_auto_increment()
|
||||
|
||||
if (error) {
|
||||
|
||||
return(~(ulonglong) 0);
|
||||
return(~(ulonglong) 0);
|
||||
}
|
||||
|
||||
return((ulonglong) nr);
|
||||
@ -6113,7 +6126,8 @@ ha_innobase::cmp_ref(
|
||||
|
||||
/* Do type-aware comparison of Primary Key members. PK members
|
||||
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 + table->key_info[table->s->primary_key].key_parts;
|
||||
for (; key_part != key_part_end; ++key_part) {
|
||||
@ -6258,19 +6272,21 @@ innobase_query_is_update(void)
|
||||
|
||||
thd = (THD *)innobase_current_thd();
|
||||
|
||||
if ( thd->lex->sql_command == SQLCOM_REPLACE ||
|
||||
thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
|
||||
( thd->lex->sql_command == SQLCOM_LOAD &&
|
||||
thd->lex->duplicates == DUP_REPLACE )) {
|
||||
return true;
|
||||
if (thd->lex->sql_command == SQLCOM_REPLACE ||
|
||||
thd->lex->sql_command == SQLCOM_REPLACE_SELECT ||
|
||||
(thd->lex->sql_command == SQLCOM_LOAD &&
|
||||
thd->lex->duplicates == DUP_REPLACE)) {
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
if ( thd->lex->sql_command == SQLCOM_INSERT &&
|
||||
thd->lex->duplicates == DUP_UPDATE ) {
|
||||
return true;
|
||||
if (thd->lex->sql_command == SQLCOM_INSERT &&
|
||||
thd->lex->duplicates == DUP_UPDATE) {
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6306,11 +6322,20 @@ innobase_xa_prepare(
|
||||
"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
|
||||
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);
|
||||
} else {
|
||||
/* 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 */
|
||||
{
|
||||
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(
|
||||
/*===================*/
|
||||
/* out: 0 or error number */
|
||||
XID* xid) /* in: X/Open XA Transaction Identification */
|
||||
XID* xid) /* in: X/Open XA transaction identification */
|
||||
{
|
||||
trx_t* trx;
|
||||
|
||||
@ -6386,7 +6412,7 @@ int
|
||||
innobase_rollback_by_xid(
|
||||
/*=====================*/
|
||||
/* out: 0 or error number */
|
||||
XID *xid) /* in : X/Open XA Transaction Idenfification */
|
||||
XID *xid) /* in: X/Open XA transaction idenfification */
|
||||
{
|
||||
trx_t* trx;
|
||||
|
||||
|
301
sql/item.cc
301
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
|
||||
clauses, and then we search the FROM clause.
|
||||
|
||||
POSTCONDITION
|
||||
Item_ref::ref is 0 or points to a valid item
|
||||
|
||||
RETURN
|
||||
TRUE if error
|
||||
FALSE on success
|
||||
@ -3534,168 +3537,155 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
|
||||
|
||||
if (ref == not_found_item) /* This reference was not resolved. */
|
||||
{
|
||||
/*
|
||||
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
|
||||
reference in the outer select(s).
|
||||
|
||||
We treat each subselect as a separate namespace, so that different
|
||||
subselects may contain columns with the same names. The subselects are
|
||||
searched starting from the innermost.
|
||||
*/
|
||||
if (outer_sel && (current_sel->master_unit()->first_select()->linkage !=
|
||||
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))
|
||||
{
|
||||
TABLE_LIST *table_list;
|
||||
Field *from_field= (Field*) not_found_field;
|
||||
SELECT_LEX *last= 0;
|
||||
|
||||
for ( ; outer_sel ;
|
||||
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
|
||||
{
|
||||
last= outer_sel;
|
||||
Item_subselect *prev_subselect_item= prev_unit->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)))
|
||||
return TRUE; /* Some error occurred (e.g. ambiguous names). */
|
||||
if (ref != not_found_item)
|
||||
{
|
||||
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
|
||||
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
|
||||
*/
|
||||
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);
|
||||
if (!from_field)
|
||||
return TRUE;
|
||||
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
|
||||
this will ensure we get an error if this item would be used
|
||||
elsewhere
|
||||
*/
|
||||
ref= 0; // Safety
|
||||
if (from_field != view_ref_found)
|
||||
{
|
||||
Item_field* fld;
|
||||
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;
|
||||
}
|
||||
/*
|
||||
We can leave expression substituted from view for next PS/SP
|
||||
re-execution (i.e. do not register this substitution for reverting
|
||||
on cleanup() (register_item_tree_changing())), because this subtree
|
||||
will be fix_field'ed during setup_tables()->setup_ancestor()
|
||||
(i.e. before all other expressions of query, and references on
|
||||
tables which do not present in query will not make problems.
|
||||
|
||||
Also we suppose that view can't be changed during PS/SP life.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Should be checked in resolve_ref_in_select_and_group(). */
|
||||
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||
mark_as_dependent(thd, last, current_sel, this, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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
|
||||
not support the use of outer fields for now), try to resolve this
|
||||
reference in the outer select(s).
|
||||
|
||||
We treat each subselect as a separate namespace, so that different
|
||||
subselects may contain columns with the same names. The subselects are
|
||||
searched starting from the innermost.
|
||||
*/
|
||||
from_field= (Field*) not_found_field;
|
||||
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())
|
||||
{
|
||||
last= outer_sel;
|
||||
Item_subselect *prev_subselect_item= prev_unit->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)))
|
||||
return TRUE; /* Some error occurred (e.g. ambiguous names). */
|
||||
if (ref != not_found_item)
|
||||
{
|
||||
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
||||
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
||||
break;
|
||||
}
|
||||
/*
|
||||
Set ref to 0 to ensure that we get an error in case we replaced
|
||||
this item with another item and still use this item in some
|
||||
other place of the parse tree.
|
||||
*/
|
||||
ref= 0;
|
||||
}
|
||||
|
||||
/* 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
|
||||
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::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;
|
||||
}
|
||||
if (from_field != not_found_field)
|
||||
{
|
||||
prev_subselect_item->used_tables_cache|= from_field->table->map;
|
||||
prev_subselect_item->const_item_cache= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DBUG_ASSERT(from_field == not_found_field);
|
||||
|
||||
/* 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(from_field != 0 && from_field != view_ref_found);
|
||||
if (from_field != not_found_field)
|
||||
{
|
||||
Item_field* fld;
|
||||
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);
|
||||
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
|
||||
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();
|
||||
|
||||
if (ref && (*ref)->check_cols(1))
|
||||
return 1;
|
||||
return 0;
|
||||
if ((*ref)->check_cols(1))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void Item_ref::set_properties()
|
||||
{
|
||||
max_length= (*ref)->max_length;
|
||||
|
@ -283,7 +283,7 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
|
||||
comparators= 0;
|
||||
return 1;
|
||||
}
|
||||
if (!(comparators= (Arg_comparator *) sql_alloc(sizeof(Arg_comparator)*n)))
|
||||
if (!(comparators= new Arg_comparator[n]))
|
||||
return 1;
|
||||
for (uint i=0; i < n; i++)
|
||||
{
|
||||
@ -1825,6 +1825,12 @@ in_row::in_row(uint elements, Item * item)
|
||||
size= sizeof(cmp_item_row);
|
||||
compare= (qsort2_cmp) cmp_row;
|
||||
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()
|
||||
|
@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#include "mysql_priv.h"
|
||||
#include "sql_select.h"
|
||||
|
||||
Item_sum::Item_sum(List<Item> &list)
|
||||
:arg_count(list.elements)
|
||||
@ -303,6 +304,21 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
||||
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
|
||||
@ -2075,8 +2091,6 @@ my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf)
|
||||
** COUNT(DISTINCT ...)
|
||||
****************************************************************************/
|
||||
|
||||
#include "sql_select.h"
|
||||
|
||||
int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
|
||||
{
|
||||
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
|
||||
|
@ -94,7 +94,6 @@ public:
|
||||
Item *get_tmp_table_item(THD *thd);
|
||||
virtual Field *create_tmp_field(bool group, TABLE *table,
|
||||
uint convert_blob_length);
|
||||
|
||||
bool walk (Item_processor processor, byte *argument);
|
||||
};
|
||||
|
||||
@ -525,6 +524,8 @@ protected:
|
||||
void cleanup();
|
||||
bool any_value() { return was_values; }
|
||||
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 void clean_up(bool print_message);
|
||||
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 void create_pid_file();
|
||||
|
||||
@ -943,6 +944,7 @@ extern "C" void unireg_abort(int exit_code)
|
||||
sql_print_error("Aborting\n");
|
||||
clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */
|
||||
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
|
||||
wait_for_signal_thread_to_end();
|
||||
clean_up_mutexes();
|
||||
my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
|
||||
exit(exit_code); /* purecov: inspected */
|
||||
@ -1051,6 +1053,29 @@ void clean_up(bool print_message)
|
||||
} /* 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()
|
||||
{
|
||||
(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) ;
|
||||
if (cleanup_done)
|
||||
{
|
||||
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
|
||||
my_thread_end();
|
||||
signal_thread_in_use= 0;
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
#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
|
||||
wait_for_signal_thread_to_end();
|
||||
clean_up_mutexes();
|
||||
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,
|
||||
0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#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},
|
||||
{"auto-increment-increment", OPT_AUTO_INCREMENT,
|
||||
"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;
|
||||
ha_rows cur_quick_prefix_records= 0;
|
||||
uint cur_param_idx;
|
||||
key_map cur_used_key_parts;
|
||||
|
||||
for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
|
||||
cur_index_info++, cur_index++)
|
||||
@ -6925,17 +6926,25 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
|
||||
else if (join->select_distinct)
|
||||
{
|
||||
select_items_it.rewind();
|
||||
cur_used_key_parts.clear_all();
|
||||
while ((item= select_items_it++))
|
||||
{
|
||||
item_field= (Item_field*) item; /* (SA5) already checked above. */
|
||||
/* Find the order of the key part in the index. */
|
||||
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)
|
||||
goto next_index;
|
||||
cur_part= cur_index_info->key_part + key_part_nr - 1;
|
||||
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
|
||||
DBUG_ASSERT(FALSE);
|
||||
|
@ -5326,3 +5326,5 @@ ER_PROC_AUTO_REVOKE_FAIL
|
||||
eng "Failed to revoke all privileges to dropped routine"
|
||||
ER_DATA_TOO_LONG 22001
|
||||
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_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)
|
||||
: Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0),
|
||||
m_handlers(0), m_parent(prev)
|
||||
|
@ -60,6 +60,12 @@ typedef struct sp_cond_type
|
||||
uint mysqlerr;
|
||||
} 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
|
||||
{
|
||||
LEX_STRING name;
|
||||
|
@ -183,6 +183,7 @@ THD::THD()
|
||||
lock=locked_tables=0;
|
||||
used_tables=0;
|
||||
cuted_fields= sent_row_count= 0L;
|
||||
limit_found_rows= 0;
|
||||
statement_id_counter= 0UL;
|
||||
// Must be reset to handle error with THD's created for init of mysqld
|
||||
lex->current_select= 0;
|
||||
|
@ -668,7 +668,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
|
||||
thd->clear_error();
|
||||
mysql_bin_log.write(&qinfo);
|
||||
}
|
||||
thd->server_status|= SERVER_STATUS_DB_DROPPED;
|
||||
send_ok(thd, (ulong) deleted);
|
||||
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
|
||||
}
|
||||
|
||||
exit:
|
||||
|
@ -948,7 +948,8 @@ public:
|
||||
thd.current_tablenr=0;
|
||||
thd.version=refresh_version;
|
||||
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*) &table_list, sizeof(table_list)); // Safety
|
||||
|
@ -171,27 +171,46 @@ enum tablespace_op_type
|
||||
/*
|
||||
The state of the lex parsing for selects
|
||||
|
||||
All select describing structures linked with following pointers:
|
||||
- list of neighbors (next/prev) (prev of first element point to slave
|
||||
pointer of upper structure)
|
||||
- one level units for unit (union) structure
|
||||
- member of one union(unit) for ordinary select_lex
|
||||
- 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.
|
||||
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
|
||||
|
||||
for example for following query:
|
||||
All select describing structures linked with following pointers:
|
||||
- list of neighbors (next/prev) (prev of first element point to slave
|
||||
pointer of upper structure)
|
||||
- For select this is a list of UNION's (or one element list)
|
||||
- For units this is a list of sub queries for the upper level select
|
||||
|
||||
- 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 *
|
||||
from table1
|
||||
@ -209,6 +228,11 @@ enum tablespace_op_type
|
||||
|
||||
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
|
||||
fake0
|
||||
@ -231,7 +255,12 @@ enum tablespace_op_type
|
||||
|
||||
|
||||
relation in main unit will be following:
|
||||
|
||||
(bigger picture for:
|
||||
main unit
|
||||
fake0
|
||||
select1 select2 select3
|
||||
in the above picture)
|
||||
|
||||
main unit
|
||||
|^^^^|fake_select_lex
|
||||
|||||+--------------------------------------------+
|
||||
@ -427,7 +456,7 @@ public:
|
||||
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
|
||||
{
|
||||
|
@ -214,7 +214,7 @@ bool log_in_use(const char* log_name)
|
||||
if ((linfo = tmp->current_linfo))
|
||||
{
|
||||
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);
|
||||
if (result)
|
||||
break;
|
||||
@ -1274,6 +1274,7 @@ bool mysql_show_binlog_events(THD* thd)
|
||||
DBUG_ENTER("mysql_show_binlog_events");
|
||||
List<Item> field_list;
|
||||
const char *errmsg = 0;
|
||||
bool ret = TRUE;
|
||||
IO_CACHE log;
|
||||
File file = -1;
|
||||
Format_description_log_event *description_event= new
|
||||
@ -1376,6 +1377,8 @@ bool mysql_show_binlog_events(THD* thd)
|
||||
pthread_mutex_unlock(log_lock);
|
||||
}
|
||||
|
||||
ret= FALSE;
|
||||
|
||||
err:
|
||||
delete description_event;
|
||||
if (file >= 0)
|
||||
@ -1395,7 +1398,7 @@ err:
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
thd->current_linfo = 0;
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
DBUG_RETURN(TRUE);
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
|
@ -664,7 +664,7 @@ JOIN::optimize()
|
||||
!(select_options & SELECT_DESCRIBE))
|
||||
{ /* purecov: inspected */
|
||||
my_message(ER_TOO_BIG_SELECT, ER(ER_TOO_BIG_SELECT), MYF(0));
|
||||
error= 1; /* purecov: inspected */
|
||||
error= -1;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
if (const_tables && !thd->locked_tables &&
|
||||
@ -1194,7 +1194,9 @@ JOIN::exec()
|
||||
else
|
||||
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;
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
||||
static Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
||||
Item *item, TABLE *table,
|
||||
bool modify_item,
|
||||
uint convert_blob_length)
|
||||
Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
||||
Item *item, TABLE *table,
|
||||
bool modify_item,
|
||||
uint convert_blob_length)
|
||||
{
|
||||
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];
|
||||
int error;
|
||||
handler *file=table->file;
|
||||
ulong extra_length=ALIGN_SIZE(key_length)-key_length;
|
||||
handler *file= table->file;
|
||||
ulong extra_length= ALIGN_SIZE(key_length)-key_length;
|
||||
uint *field_lengths,*field_length;
|
||||
HASH hash;
|
||||
DBUG_ENTER("remove_dup_with_hash_index");
|
||||
@ -10935,22 +10937,34 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
||||
NullS))
|
||||
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,
|
||||
key_length,(hash_get_key) 0, 0, 0))
|
||||
key_length, (hash_get_key) 0, 0, 0))
|
||||
{
|
||||
my_free((char*) key_buffer,MYF(0));
|
||||
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);
|
||||
key_pos=key_buffer;
|
||||
for (;;)
|
||||
{
|
||||
byte *org_key_pos;
|
||||
if (thd->killed)
|
||||
{
|
||||
thd->send_kill_message();
|
||||
@ -10973,6 +10987,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
|
||||
}
|
||||
|
||||
/* copy fields to key buffer */
|
||||
org_key_pos= key_pos;
|
||||
field_length=field_lengths;
|
||||
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++;
|
||||
}
|
||||
/* 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 */
|
||||
if ((error=file->delete_row(record)))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
(void) my_hash_insert(&hash, key_pos-key_length);
|
||||
(void) my_hash_insert(&hash, org_key_pos);
|
||||
key_pos+=extra_length;
|
||||
}
|
||||
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,
|
||||
int error, bool ignore_last_dupp_error);
|
||||
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 */
|
||||
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 */
|
||||
uint len= ($3.length < sizeof($$->sqlstate)-1 ?
|
||||
$3.length : sizeof($$->sqlstate)-1);
|
||||
|
||||
if (!sp_cond_check(&$3))
|
||||
{
|
||||
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
|
||||
YYABORT;
|
||||
}
|
||||
$$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
|
||||
$$->type= sp_cond_type_t::state;
|
||||
memcpy($$->sqlstate, $3.str, len);
|
||||
$$->sqlstate[len]= '\0';
|
||||
memcpy($$->sqlstate, $3.str, 5);
|
||||
$$->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)),
|
||||
const uchar *s, uint slen,
|
||||
@ -443,7 +450,7 @@ skip:
|
||||
|
||||
MY_COLLATION_HANDLER my_collation_8bit_bin_handler =
|
||||
{
|
||||
NULL, /* init */
|
||||
my_coll_init_8bit_bin,
|
||||
my_strnncoll_8bit_bin,
|
||||
my_strnncollsp_8bit_bin,
|
||||
my_strnxfrm_8bit_bin,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user