Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into serg.mylan:/usr/home/serg/Abk/m51 sql/log.cc: Auto merged
This commit is contained in:
commit
0d8d39c8c6
@ -4944,10 +4944,10 @@ val
|
||||
UPDATE bug13894 SET val=6 WHERE val=10;
|
||||
SELECT * FROM bug13894;
|
||||
val
|
||||
6
|
||||
6
|
||||
5
|
||||
11
|
||||
6
|
||||
6
|
||||
DROP TABLE bug13894;
|
||||
DROP TABLE IF EXISTS bug14672;
|
||||
CREATE TABLE bug14672 (c1 integer) engine = CSV;
|
||||
|
@ -453,7 +453,7 @@ static void ndbcluster_binlog_wait(THD *thd)
|
||||
}
|
||||
|
||||
/*
|
||||
Called from MYSQL_LOG::reset_logs in log.cc when binlog is emptied
|
||||
Called from MYSQL_BIN_LOG::reset_logs in log.cc when binlog is emptied
|
||||
*/
|
||||
static int ndbcluster_reset_logs(THD *thd)
|
||||
{
|
||||
@ -477,7 +477,7 @@ static int ndbcluster_reset_logs(THD *thd)
|
||||
}
|
||||
|
||||
/*
|
||||
Called from MYSQL_LOG::purge_logs in log.cc when the binlog "file"
|
||||
Called from MYSQL_BIN_LOG::purge_logs in log.cc when the binlog "file"
|
||||
is removed
|
||||
*/
|
||||
|
||||
|
1098
sql/log.cc
1098
sql/log.cc
File diff suppressed because it is too large
Load Diff
133
sql/log.h
133
sql/log.h
@ -147,33 +147,85 @@ typedef struct st_log_info
|
||||
class Log_event;
|
||||
class Rows_log_event;
|
||||
|
||||
enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN};
|
||||
enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN };
|
||||
enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
|
||||
|
||||
/*
|
||||
TODO split MYSQL_LOG into base MYSQL_LOG and
|
||||
MYSQL_QUERY_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG
|
||||
most of the code from MYSQL_LOG should be in the MYSQL_BIN_LOG
|
||||
only (TC_LOG included)
|
||||
|
||||
TODO use mmap instead of IO_CACHE for binlog
|
||||
(mmap+fsync is two times faster than write+fsync)
|
||||
*/
|
||||
|
||||
class MYSQL_LOG: public TC_LOG
|
||||
class MYSQL_LOG
|
||||
{
|
||||
public:
|
||||
MYSQL_LOG();
|
||||
void init_pthread_objects();
|
||||
void cleanup();
|
||||
bool open(const char *log_name,
|
||||
enum_log_type log_type,
|
||||
const char *new_name,
|
||||
enum cache_type io_cache_type_arg);
|
||||
void init(enum_log_type log_type_arg,
|
||||
enum cache_type io_cache_type_arg);
|
||||
void close(uint exiting);
|
||||
inline bool is_open() { return log_state != LOG_CLOSED; }
|
||||
const char *generate_name(const char *log_name, const char *suffix,
|
||||
bool strip_ext, char *buff);
|
||||
int generate_new_name(char *new_name, const char *log_name);
|
||||
protected:
|
||||
/* LOCK_log is inited by init_pthread_objects() */
|
||||
pthread_mutex_t LOCK_log;
|
||||
char *name;
|
||||
char log_file_name[FN_REFLEN];
|
||||
char time_buff[20], db[NAME_LEN + 1];
|
||||
bool write_error, inited;
|
||||
IO_CACHE log_file;
|
||||
enum_log_type log_type;
|
||||
volatile enum_log_state log_state;
|
||||
enum cache_type io_cache_type;
|
||||
friend class Log_event;
|
||||
};
|
||||
|
||||
class MYSQL_QUERY_LOG: public MYSQL_LOG
|
||||
{
|
||||
public:
|
||||
MYSQL_QUERY_LOG() : last_time(0) {}
|
||||
void reopen_file();
|
||||
bool write(time_t event_time, const char *user_host,
|
||||
uint user_host_len, int thread_id,
|
||||
const char *command_type, uint command_type_len,
|
||||
const char *sql_text, uint sql_text_len);
|
||||
bool write(THD *thd, time_t current_time, time_t query_start_arg,
|
||||
const char *user_host, uint user_host_len,
|
||||
longlong query_time, longlong lock_time, bool is_command,
|
||||
const char *sql_text, uint sql_text_len);
|
||||
bool open_slow_log(const char *log_name)
|
||||
{
|
||||
char buf[FN_REFLEN];
|
||||
return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0,
|
||||
WRITE_CACHE);
|
||||
}
|
||||
bool open_query_log(const char *log_name)
|
||||
{
|
||||
char buf[FN_REFLEN];
|
||||
return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0,
|
||||
WRITE_CACHE);
|
||||
}
|
||||
private:
|
||||
time_t last_time;
|
||||
};
|
||||
|
||||
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
||||
{
|
||||
private:
|
||||
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
|
||||
pthread_mutex_t LOCK_log, LOCK_index;
|
||||
pthread_mutex_t LOCK_index;
|
||||
pthread_mutex_t LOCK_prep_xids;
|
||||
pthread_cond_t COND_prep_xids;
|
||||
pthread_cond_t update_cond;
|
||||
ulonglong bytes_written;
|
||||
time_t last_time,query_start;
|
||||
IO_CACHE log_file;
|
||||
IO_CACHE index_file;
|
||||
char *name;
|
||||
char time_buff[20],db[NAME_LEN+1];
|
||||
char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
|
||||
char index_file_name[FN_REFLEN];
|
||||
/*
|
||||
The max size before rotation (usable only if log_type == LOG_BIN: binary
|
||||
logs and relay logs).
|
||||
@ -186,13 +238,10 @@ class MYSQL_LOG: public TC_LOG
|
||||
*/
|
||||
ulong max_size;
|
||||
ulong prepared_xids; /* for tc log - number of xids to remember */
|
||||
volatile enum_log_type log_type;
|
||||
enum cache_type io_cache_type;
|
||||
// current file sequence number for load data infile binary logging
|
||||
uint file_id;
|
||||
uint open_count; // For replication
|
||||
int readers_count;
|
||||
bool write_error, inited;
|
||||
bool need_start_event;
|
||||
/*
|
||||
no_auto_events means we don't want any of these automatic events :
|
||||
@ -202,13 +251,21 @@ class MYSQL_LOG: public TC_LOG
|
||||
In 5.0 it's 0 for relay logs too!
|
||||
*/
|
||||
bool no_auto_events;
|
||||
friend class Log_event;
|
||||
|
||||
ulonglong m_table_map_version;
|
||||
|
||||
int write_to_file(IO_CACHE *cache);
|
||||
/*
|
||||
This is used to start writing to a new log file. The difference from
|
||||
new_file() is locking. new_file_without_locking() does not acquire
|
||||
LOCK_log.
|
||||
*/
|
||||
void new_file_without_locking();
|
||||
void new_file_impl(bool need_lock);
|
||||
|
||||
public:
|
||||
MYSQL_LOG::generate_name;
|
||||
MYSQL_LOG::is_open;
|
||||
/*
|
||||
These describe the log's format. This is used only for relay logs.
|
||||
_for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
|
||||
@ -220,9 +277,9 @@ public:
|
||||
Format_description_log_event *description_event_for_exec,
|
||||
*description_event_for_queue;
|
||||
|
||||
MYSQL_LOG();
|
||||
MYSQL_BIN_LOG();
|
||||
/*
|
||||
note that there's no destructor ~MYSQL_LOG() !
|
||||
note that there's no destructor ~MYSQL_BIN_LOG() !
|
||||
The reason is that we don't want it to be automatically called
|
||||
on exit() - but only during the correct shutdown process
|
||||
*/
|
||||
@ -264,9 +321,7 @@ public:
|
||||
void signal_update();
|
||||
void wait_for_update(THD* thd, bool master_or_slave);
|
||||
void set_need_start_event() { need_start_event = 1; }
|
||||
void init(enum_log_type log_type_arg,
|
||||
enum cache_type io_cache_type_arg,
|
||||
bool no_auto_events_arg, ulong max_size);
|
||||
void init(bool no_auto_events_arg, ulong max_size);
|
||||
void init_pthread_objects();
|
||||
void cleanup();
|
||||
bool open(const char *log_name,
|
||||
@ -275,35 +330,10 @@ public:
|
||||
enum cache_type io_cache_type_arg,
|
||||
bool no_auto_events_arg, ulong max_size,
|
||||
bool null_created);
|
||||
const char *generate_name(const char *log_name, const char *suffix,
|
||||
bool strip_ext, char *buff);
|
||||
/* simplified open_xxx wrappers for the gigantic open above */
|
||||
bool open_query_log(const char *log_name)
|
||||
{
|
||||
char buf[FN_REFLEN];
|
||||
return open(generate_name(log_name, ".log", 0, buf),
|
||||
LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0);
|
||||
}
|
||||
bool open_slow_log(const char *log_name)
|
||||
{
|
||||
char buf[FN_REFLEN];
|
||||
return open(generate_name(log_name, "-slow.log", 0, buf),
|
||||
LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0);
|
||||
}
|
||||
bool open_index_file(const char *index_file_name_arg,
|
||||
const char *log_name);
|
||||
void new_file(bool need_lock);
|
||||
/* log a command to the old-fashioned general log */
|
||||
bool write(time_t event_time, const char *user_host,
|
||||
uint user_host_len, int thread_id,
|
||||
const char *command_type, uint command_type_len,
|
||||
const char *sql_text, uint sql_text_len);
|
||||
|
||||
/* log a query to the old-fashioned slow query log */
|
||||
bool write(THD *thd, time_t current_time, time_t query_start_arg,
|
||||
const char *user_host, uint user_host_len,
|
||||
longlong query_time, longlong lock_time, bool is_command,
|
||||
const char *sql_text, uint sql_text_len);
|
||||
/* Use this to start writing a new log file */
|
||||
void new_file();
|
||||
|
||||
bool write(Log_event* event_info); // binary log write
|
||||
bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
|
||||
@ -319,7 +349,6 @@ public:
|
||||
bool appendv(const char* buf,uint len,...);
|
||||
bool append(Log_event* ev);
|
||||
|
||||
int generate_new_name(char *new_name,const char *old_name);
|
||||
void make_log_name(char* buf, const char* log_ident);
|
||||
bool is_active(const char* log_file_name);
|
||||
int update_log_index(LOG_INFO* linfo, bool need_update_threads);
|
||||
@ -339,7 +368,6 @@ public:
|
||||
int find_next_log(LOG_INFO* linfo, bool need_mutex);
|
||||
int get_current_log(LOG_INFO* linfo);
|
||||
uint next_file_id();
|
||||
inline bool is_open() { return log_type != LOG_CLOSED; }
|
||||
inline char* get_index_fname() { return index_file_name;}
|
||||
inline char* get_log_fname() { return log_file_name; }
|
||||
inline char* get_name() { return name; }
|
||||
@ -416,7 +444,8 @@ public:
|
||||
|
||||
class Log_to_file_event_handler: public Log_event_handler
|
||||
{
|
||||
MYSQL_LOG mysql_log, mysql_slow_log;
|
||||
MYSQL_QUERY_LOG mysql_log;
|
||||
MYSQL_QUERY_LOG mysql_slow_log;
|
||||
bool is_initialized;
|
||||
public:
|
||||
Log_to_file_event_handler(): is_initialized(FALSE)
|
||||
|
@ -470,7 +470,7 @@ enum Int_event_type
|
||||
|
||||
#ifndef MYSQL_CLIENT
|
||||
class String;
|
||||
class MYSQL_LOG;
|
||||
class MYSQL_BIN_LOG;
|
||||
class THD;
|
||||
#endif
|
||||
|
||||
|
@ -1537,7 +1537,7 @@ extern char *default_tz_name;
|
||||
extern my_bool opt_large_pages;
|
||||
extern uint opt_large_page_size;
|
||||
|
||||
extern MYSQL_LOG mysql_bin_log;
|
||||
extern MYSQL_BIN_LOG mysql_bin_log;
|
||||
extern LOGGER logger;
|
||||
extern TABLE_LIST general_log, slow_log;
|
||||
extern FILE *bootstrap_file;
|
||||
|
@ -2635,10 +2635,10 @@ static int init_common_variables(const char *conf_file_name, int argc,
|
||||
global_system_variables.time_zone= my_tz_SYSTEM;
|
||||
|
||||
/*
|
||||
Init mutexes for the global MYSQL_LOG objects.
|
||||
Init mutexes for the global MYSQL_BIN_LOG objects.
|
||||
As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of
|
||||
global MYSQL_LOGs in their constructors, because then they would be inited
|
||||
before MY_INIT(). So we do it here.
|
||||
global MYSQL_BIN_LOGs in their constructors, because then they would be
|
||||
inited before MY_INIT(). So we do it here.
|
||||
*/
|
||||
mysql_bin_log.init_pthread_objects();
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
/* inline since it's called below */
|
||||
inline
|
||||
injector::transaction::transaction(MYSQL_LOG *log, THD *thd)
|
||||
injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd)
|
||||
: m_state(START_STATE), m_thd(thd)
|
||||
{
|
||||
/*
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
/* Forward declarations */
|
||||
class handler;
|
||||
class MYSQL_LOG;
|
||||
class MYSQL_BIN_LOG;
|
||||
class st_table;
|
||||
|
||||
typedef st_table TABLE;
|
||||
@ -219,7 +219,7 @@ public:
|
||||
|
||||
private:
|
||||
/* Only the injector may construct these object */
|
||||
transaction(MYSQL_LOG *, THD *);
|
||||
transaction(MYSQL_BIN_LOG *, THD *);
|
||||
|
||||
void swap(transaction& o) {
|
||||
/* std::swap(m_start_pos, o.m_start_pos); */
|
||||
|
@ -69,7 +69,7 @@ typedef struct st_relay_log_info
|
||||
Protected with internal locks.
|
||||
Must get data_lock when resetting the logs.
|
||||
*/
|
||||
MYSQL_LOG relay_log;
|
||||
MYSQL_BIN_LOG relay_log;
|
||||
LOG_INFO linfo;
|
||||
IO_CACHE cache_buf,*cur_log;
|
||||
|
||||
|
@ -4658,7 +4658,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli)
|
||||
When the relay log is created when the I/O thread starts, easy: the
|
||||
master will send the description event and we will queue it.
|
||||
But if the relay log is created by new_file(): then the solution is:
|
||||
MYSQL_LOG::open() will write the buffered description event.
|
||||
MYSQL_BIN_LOG::open() will write the buffered description event.
|
||||
*/
|
||||
if ((ev=Log_event::read_log_event(cur_log,0,
|
||||
rli->relay_log.description_event_for_exec)))
|
||||
@ -4920,7 +4920,8 @@ err:
|
||||
Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation
|
||||
because of size is simpler because when we do it we already have all relevant
|
||||
locks; here we don't, so this function is mainly taking locks).
|
||||
Returns nothing as we cannot catch any error (MYSQL_LOG::new_file() is void).
|
||||
Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file()
|
||||
is void).
|
||||
*/
|
||||
|
||||
void rotate_relay_log(MASTER_INFO* mi)
|
||||
@ -4942,7 +4943,7 @@ void rotate_relay_log(MASTER_INFO* mi)
|
||||
}
|
||||
|
||||
/* If the relay log is closed, new_file() will do nothing. */
|
||||
rli->relay_log.new_file(1);
|
||||
rli->relay_log.new_file();
|
||||
|
||||
/*
|
||||
We harvest now, because otherwise BIN_LOG_HEADER_SIZE will not immediately
|
||||
|
@ -73,7 +73,7 @@
|
||||
run_lock protects all information about the run state: slave_running, and the
|
||||
existence of the I/O thread (to stop/start it, you need this mutex).
|
||||
data_lock protects some moving members of the struct: counters (log name,
|
||||
position) and relay log (MYSQL_LOG object).
|
||||
position) and relay log (MYSQL_BIN_LOG object).
|
||||
|
||||
In RELAY_LOG_INFO: run_lock, data_lock
|
||||
see MASTER_INFO
|
||||
@ -81,7 +81,7 @@
|
||||
Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you
|
||||
must acquire LOCK_active_mi first.
|
||||
|
||||
In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log
|
||||
In MYSQL_BIN_LOG: LOCK_log, LOCK_index of the binlog and the relay log
|
||||
LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog
|
||||
(so that you have to update the .index file).
|
||||
*/
|
||||
|
@ -61,7 +61,7 @@ TODO:
|
||||
|
||||
/* The file extension */
|
||||
#define CSV_EXT ".CSV" // The data file
|
||||
#define CSN_EXT ".CSN" // Files used during repair
|
||||
#define CSN_EXT ".CSN" // Files used during repair and update
|
||||
#define CSM_EXT ".CSM" // Meta file
|
||||
|
||||
|
||||
@ -77,6 +77,53 @@ static int tina_init= 0;
|
||||
static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root);
|
||||
static int tina_init_func();
|
||||
|
||||
off_t Transparent_file::read_next()
|
||||
{
|
||||
off_t bytes_read;
|
||||
|
||||
/*
|
||||
No need to seek here, as the file managed by Transparent_file class
|
||||
always points to upper_bound byte
|
||||
*/
|
||||
if ((bytes_read= my_read(filedes, buff, buff_size, MYF(0))) == MY_FILE_ERROR)
|
||||
return -1;
|
||||
|
||||
/* end of file */
|
||||
if (!bytes_read)
|
||||
return -1;
|
||||
|
||||
lower_bound= upper_bound;
|
||||
upper_bound+= bytes_read;
|
||||
|
||||
return lower_bound;
|
||||
}
|
||||
|
||||
|
||||
char Transparent_file::get_value(off_t offset)
|
||||
{
|
||||
off_t bytes_read;
|
||||
|
||||
/* check boundaries */
|
||||
if ((lower_bound <= offset) && (offset < upper_bound))
|
||||
return buff[offset - lower_bound];
|
||||
else
|
||||
{
|
||||
VOID(my_seek(filedes, offset, MY_SEEK_SET, MYF(0)));
|
||||
/* read appropriate portion of the file */
|
||||
if ((bytes_read= my_read(filedes, buff, buff_size,
|
||||
MYF(0))) == MY_FILE_ERROR)
|
||||
return 0;
|
||||
|
||||
lower_bound= offset;
|
||||
upper_bound= lower_bound + bytes_read;
|
||||
|
||||
/* end of file */
|
||||
if (upper_bound == offset)
|
||||
return 0;
|
||||
|
||||
return buff[0];
|
||||
}
|
||||
}
|
||||
handlerton tina_hton;
|
||||
|
||||
/*****************************************************************************
|
||||
@ -92,7 +139,7 @@ int sort_set (tina_set *a, tina_set *b)
|
||||
We assume that intervals do not intersect. So, it is enought to compare
|
||||
any two points. Here we take start of intervals for comparison.
|
||||
*/
|
||||
return ( a->begin > b->begin ? -1 : ( a->begin < b->begin ? 1 : 0 ) );
|
||||
return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) );
|
||||
}
|
||||
|
||||
static byte* tina_get_key(TINA_SHARE *share,uint *length,
|
||||
@ -102,55 +149,6 @@ static byte* tina_get_key(TINA_SHARE *share,uint *length,
|
||||
return (byte*) share->table_name;
|
||||
}
|
||||
|
||||
/*
|
||||
Reloads the mmap file.
|
||||
*/
|
||||
int get_mmap(TINA_SHARE *share, int write)
|
||||
{
|
||||
DBUG_ENTER("ha_tina::get_mmap");
|
||||
#ifdef __NETWARE__
|
||||
my_message(errno, "Sorry, no mmap() on Netware", 0);
|
||||
DBUG_ASSERT(0);
|
||||
DBUG_RETURN(1);
|
||||
#else
|
||||
if (share->mapped_file && my_munmap(share->mapped_file,
|
||||
share->file_stat.st_size))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (my_fstat(share->data_file, &share->file_stat, MYF(MY_WME)) == -1)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (share->file_stat.st_size)
|
||||
{
|
||||
if (write)
|
||||
share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size,
|
||||
PROT_READ|PROT_WRITE, MAP_SHARED,
|
||||
share->data_file, 0);
|
||||
else
|
||||
share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size,
|
||||
PROT_READ, MAP_PRIVATE,
|
||||
share->data_file, 0);
|
||||
if ((share->mapped_file == MAP_FAILED))
|
||||
{
|
||||
/*
|
||||
Bad idea you think? See the problem is that nothing actually checks
|
||||
the return value of ::rnd_init(), so tossing an error is about
|
||||
it for us.
|
||||
Never going to happen right? :)
|
||||
*/
|
||||
my_message(errno, "Woops, blew up opening a mapped file", 0);
|
||||
DBUG_ASSERT(0);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
share->mapped_file= NULL;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
#endif /* __NETWARE__ */
|
||||
}
|
||||
|
||||
|
||||
static int tina_init_func()
|
||||
{
|
||||
if (!tina_init)
|
||||
@ -191,6 +189,7 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table)
|
||||
{
|
||||
TINA_SHARE *share;
|
||||
char meta_file_name[FN_REFLEN];
|
||||
MY_STAT file_stat; /* Stat information for the data file */
|
||||
char *tmp_name;
|
||||
uint length;
|
||||
|
||||
@ -223,6 +222,8 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table)
|
||||
share->table_name= tmp_name;
|
||||
share->crashed= FALSE;
|
||||
share->rows_recorded= 0;
|
||||
share->update_file_opened= FALSE;
|
||||
share->tina_write_opened= FALSE;
|
||||
strmov(share->table_name, table_name);
|
||||
fn_format(share->data_file_name, table_name, "", CSV_EXT,
|
||||
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
||||
@ -244,33 +245,21 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table)
|
||||
share->crashed= TRUE;
|
||||
|
||||
/*
|
||||
After we read, we set the file to dirty. When we close, we will do the
|
||||
opposite. If the meta file will not open we assume it is crashed and
|
||||
If the meta file will not open we assume it is crashed and
|
||||
mark it as such.
|
||||
*/
|
||||
if (read_meta_file(share->meta_file, &share->rows_recorded))
|
||||
share->crashed= TRUE;
|
||||
else
|
||||
(void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
|
||||
|
||||
if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
|
||||
MYF(0))) == -1)
|
||||
if (my_stat(share->data_file_name, &file_stat, MYF(MY_WME)) == NULL)
|
||||
goto error2;
|
||||
|
||||
share->mapped_file= NULL; // We don't know the state as we just allocated it
|
||||
if (get_mmap(share, 0) > 0)
|
||||
goto error3;
|
||||
|
||||
/* init file length value used by readers */
|
||||
share->saved_data_file_length= share->file_stat.st_size;
|
||||
share->saved_data_file_length= file_stat.st_size;
|
||||
}
|
||||
share->use_count++;
|
||||
pthread_mutex_unlock(&tina_mutex);
|
||||
|
||||
return share;
|
||||
|
||||
error3:
|
||||
my_close(share->data_file,MYF(0));
|
||||
error2:
|
||||
thr_lock_delete(&share->lock);
|
||||
pthread_mutex_destroy(&share->mutex);
|
||||
@ -398,6 +387,30 @@ bool ha_tina::check_and_repair(THD *thd)
|
||||
}
|
||||
|
||||
|
||||
int ha_tina::init_tina_writer()
|
||||
{
|
||||
DBUG_ENTER("ha_tina::init_tina_writer");
|
||||
|
||||
/*
|
||||
Mark the file as crashed. We will set the flag back when we close
|
||||
the file. In the case of the crash it will remain marked crashed,
|
||||
which enforce recovery.
|
||||
*/
|
||||
(void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
|
||||
|
||||
if ((share->tina_write_filedes=
|
||||
my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(0))) == -1)
|
||||
{
|
||||
DBUG_PRINT("info", ("Could not open tina file writes"));
|
||||
share->crashed= TRUE;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
share->tina_write_opened= TRUE;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
bool ha_tina::is_crashed() const
|
||||
{
|
||||
DBUG_ENTER("ha_tina::is_crashed");
|
||||
@ -418,10 +431,13 @@ static int free_share(TINA_SHARE *share)
|
||||
share->crashed ? TRUE :FALSE);
|
||||
if (my_close(share->meta_file, MYF(0)))
|
||||
result_code= 1;
|
||||
if (share->mapped_file)
|
||||
my_munmap(share->mapped_file, share->file_stat.st_size);
|
||||
share->mapped_file= NULL;
|
||||
result_code= my_close(share->data_file,MYF(0));
|
||||
if (share->tina_write_opened)
|
||||
{
|
||||
if (my_close(share->tina_write_filedes, MYF(0)))
|
||||
result_code= 1;
|
||||
share->tina_write_opened= FALSE;
|
||||
}
|
||||
|
||||
hash_delete(&tina_open_tables, (byte*) share);
|
||||
thr_lock_delete(&share->lock);
|
||||
pthread_mutex_destroy(&share->mutex);
|
||||
@ -437,30 +453,41 @@ int tina_end(ha_panic_function type)
|
||||
return tina_done_func();
|
||||
}
|
||||
|
||||
/*
|
||||
Finds the end of a line.
|
||||
Supports DOS, Unix, or Mac OS line endings.
|
||||
*/
|
||||
byte * find_eoln(byte *data, off_t begin, off_t end, int *eoln_len)
|
||||
{
|
||||
off_t dataend= begin;
|
||||
*eoln_len= 0;
|
||||
|
||||
for (off_t x= begin; x < end; x++)
|
||||
if (data[x] == '\r' || data[x] == '\n')
|
||||
(*eoln_len)++;
|
||||
else if (!(*eoln_len))
|
||||
dataend++;
|
||||
else
|
||||
return data+dataend;
|
||||
|
||||
/*
|
||||
if we only have one record in the file then our for loop will break
|
||||
before we return. we should still have seen end of line markers and
|
||||
so we just return the line here
|
||||
*/
|
||||
if (*eoln_len > 0)
|
||||
return data+dataend;
|
||||
/*
|
||||
This function finds the end of a line and returns the length
|
||||
of the line ending.
|
||||
|
||||
We support three kinds of line endings:
|
||||
'\r' -- Old Mac OS line ending
|
||||
'\n' -- Traditional Unix and Mac OS X line ending
|
||||
'\r''\n' -- DOS\Windows line ending
|
||||
*/
|
||||
|
||||
off_t find_eoln_buff(Transparent_file *data_buff, off_t begin,
|
||||
off_t end, int *eoln_len)
|
||||
{
|
||||
*eoln_len= 0;
|
||||
|
||||
for (off_t x= begin; x < end; x++)
|
||||
{
|
||||
/* Unix (includes Mac OS X) */
|
||||
if (data_buff->get_value(x) == '\n')
|
||||
*eoln_len= 1;
|
||||
else
|
||||
if (data_buff->get_value(x) == '\r') // Mac or Dos
|
||||
{
|
||||
/* old Mac line ending */
|
||||
if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
|
||||
*eoln_len= 1;
|
||||
else // DOS style ending
|
||||
*eoln_len= 2;
|
||||
}
|
||||
|
||||
if (*eoln_len) // end of line was found
|
||||
return x;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -478,12 +505,13 @@ ha_tina::ha_tina(TABLE_SHARE *table_arg)
|
||||
They are not probably completely right.
|
||||
*/
|
||||
current_position(0), next_position(0), local_saved_data_file_length(0),
|
||||
chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH),
|
||||
file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH),
|
||||
records_is_known(0)
|
||||
{
|
||||
/* Set our original buffers from pre-allocated memory */
|
||||
buffer.set((char*)byte_buffer, IO_SIZE, system_charset_info);
|
||||
chain= chain_buffer;
|
||||
file_buff= new Transparent_file();
|
||||
}
|
||||
|
||||
|
||||
@ -609,20 +637,18 @@ int ha_tina::chain_append()
|
||||
*/
|
||||
int ha_tina::find_current_row(byte *buf)
|
||||
{
|
||||
byte *mapped_ptr;
|
||||
byte *end_ptr;
|
||||
off_t end_offset, curr_offset= current_position;
|
||||
int eoln_len;
|
||||
my_bitmap_map *org_bitmap;
|
||||
DBUG_ENTER("ha_tina::find_current_row");
|
||||
|
||||
mapped_ptr= (byte *)share->mapped_file + current_position;
|
||||
|
||||
/*
|
||||
We do not read further then local_saved_data_file_length in order
|
||||
not to conflict with undergoing concurrent insert.
|
||||
*/
|
||||
if ((end_ptr= find_eoln(share->mapped_file, current_position,
|
||||
local_saved_data_file_length, &eoln_len)) == 0)
|
||||
if ((end_offset=
|
||||
find_eoln_buff(file_buff, current_position,
|
||||
local_saved_data_file_length, &eoln_len)) == 0)
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE);
|
||||
|
||||
/* Avoid asserts in ::store() for columns that are not going to be updated */
|
||||
@ -631,36 +657,39 @@ int ha_tina::find_current_row(byte *buf)
|
||||
for (Field **field=table->field ; *field ; field++)
|
||||
{
|
||||
buffer.length(0);
|
||||
if (*mapped_ptr == '"')
|
||||
mapped_ptr++; // Increment past the first quote
|
||||
if (file_buff->get_value(curr_offset) == '"')
|
||||
curr_offset++; // Incrementpast the first quote
|
||||
else
|
||||
{
|
||||
dbug_tmp_restore_column_map(table->write_set, org_bitmap);
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
}
|
||||
for(;mapped_ptr != end_ptr; mapped_ptr++)
|
||||
for(;curr_offset != end_offset; curr_offset++)
|
||||
{
|
||||
// Need to convert line feeds!
|
||||
if (*mapped_ptr == '"' &&
|
||||
(((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) ||
|
||||
(mapped_ptr == end_ptr -1 )))
|
||||
if (file_buff->get_value(curr_offset) == '"' &&
|
||||
(((file_buff->get_value(curr_offset + 1) == ',') &&
|
||||
(file_buff->get_value(curr_offset + 2) == '"')) ||
|
||||
(curr_offset == end_offset -1 )))
|
||||
{
|
||||
mapped_ptr += 2; // Move past the , and the "
|
||||
curr_offset+= 2; // Move past the , and the "
|
||||
break;
|
||||
}
|
||||
if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1))
|
||||
if (file_buff->get_value(curr_offset) == '\\' &&
|
||||
curr_offset != (end_offset - 1))
|
||||
{
|
||||
mapped_ptr++;
|
||||
if (*mapped_ptr == 'r')
|
||||
curr_offset++;
|
||||
if (file_buff->get_value(curr_offset) == 'r')
|
||||
buffer.append('\r');
|
||||
else if (*mapped_ptr == 'n' )
|
||||
else if (file_buff->get_value(curr_offset) == 'n' )
|
||||
buffer.append('\n');
|
||||
else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"'))
|
||||
buffer.append(*mapped_ptr);
|
||||
else if ((file_buff->get_value(curr_offset) == '\\') ||
|
||||
(file_buff->get_value(curr_offset) == '"'))
|
||||
buffer.append(file_buff->get_value(curr_offset));
|
||||
else /* This could only happed with an externally created file */
|
||||
{
|
||||
buffer.append('\\');
|
||||
buffer.append(*mapped_ptr);
|
||||
buffer.append(file_buff->get_value(curr_offset));
|
||||
}
|
||||
}
|
||||
else // ordinary symbol
|
||||
@ -669,18 +698,18 @@ int ha_tina::find_current_row(byte *buf)
|
||||
We are at final symbol and no last quote was found =>
|
||||
we are working with a damaged file.
|
||||
*/
|
||||
if (mapped_ptr == end_ptr -1)
|
||||
if (curr_offset == end_offset - 1)
|
||||
{
|
||||
dbug_tmp_restore_column_map(table->write_set, org_bitmap);
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
}
|
||||
buffer.append(*mapped_ptr);
|
||||
buffer.append(file_buff->get_value(curr_offset));
|
||||
}
|
||||
}
|
||||
if (bitmap_is_set(table->read_set, (*field)->field_index))
|
||||
(*field)->store(buffer.ptr(), buffer.length(), system_charset_info);
|
||||
}
|
||||
next_position= (end_ptr - share->mapped_file)+eoln_len;
|
||||
next_position= end_offset + eoln_len;
|
||||
/* Maybe use \N for null? */
|
||||
memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */
|
||||
dbug_tmp_restore_column_map(table->write_set, org_bitmap);
|
||||
@ -779,7 +808,7 @@ void ha_tina::get_status()
|
||||
void ha_tina::update_status()
|
||||
{
|
||||
/* correct local_saved_data_file_length for writers */
|
||||
share->saved_data_file_length= share->file_stat.st_size;
|
||||
share->saved_data_file_length= local_saved_data_file_length;
|
||||
}
|
||||
|
||||
|
||||
@ -830,6 +859,9 @@ int ha_tina::open(const char *name, int mode, uint open_options)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
}
|
||||
|
||||
if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
/*
|
||||
Init locking. Pass handler object to the locking routines,
|
||||
so that they could save/update local_saved_data_file_length value
|
||||
@ -848,12 +880,14 @@ int ha_tina::open(const char *name, int mode, uint open_options)
|
||||
|
||||
/*
|
||||
Close a database file. We remove ourselves from the shared strucutre.
|
||||
If it is empty we destroy it and free the mapped file.
|
||||
If it is empty we destroy it.
|
||||
*/
|
||||
int ha_tina::close(void)
|
||||
{
|
||||
int rc= 0;
|
||||
DBUG_ENTER("ha_tina::close");
|
||||
DBUG_RETURN(free_share(share));
|
||||
rc= my_close(data_file, MYF(0));
|
||||
DBUG_RETURN(free_share(share) || rc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -876,22 +910,17 @@ int ha_tina::write_row(byte * buf)
|
||||
|
||||
size= encode_quote(buf);
|
||||
|
||||
if (my_write(share->data_file, (byte*)buffer.ptr(), size,
|
||||
if (!share->tina_write_opened)
|
||||
if (init_tina_writer())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* use pwrite, as concurrent reader could have changed the position */
|
||||
if (my_write(share->tina_write_filedes, (byte*)buffer.ptr(), size,
|
||||
MYF(MY_WME | MY_NABP)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/*
|
||||
Ok, this is means that we will be doing potentially bad things
|
||||
during a bulk insert on some OS'es. What we need is a cleanup
|
||||
call for ::write_row that would let us fix up everything after the bulk
|
||||
insert. The archive handler does this with an extra mutx call, which
|
||||
might be a solution for this.
|
||||
*/
|
||||
if (get_mmap(share, 0) > 0)
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* update local copy of the max position to see our own changes */
|
||||
local_saved_data_file_length= share->file_stat.st_size;
|
||||
local_saved_data_file_length+= size;
|
||||
|
||||
/* update shared info */
|
||||
pthread_mutex_lock(&share->mutex);
|
||||
@ -906,6 +935,23 @@ int ha_tina::write_row(byte * buf)
|
||||
}
|
||||
|
||||
|
||||
int ha_tina::open_update_temp_file_if_needed()
|
||||
{
|
||||
char updated_fname[FN_REFLEN];
|
||||
|
||||
if (!share->update_file_opened)
|
||||
{
|
||||
if ((update_temp_file=
|
||||
my_create(fn_format(updated_fname, share->table_name,
|
||||
"", CSN_EXT,
|
||||
MY_REPLACE_EXT | MY_UNPACK_FILENAME),
|
||||
0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
|
||||
return 1;
|
||||
share->update_file_opened= TRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
This is called for an update.
|
||||
Make sure you put in code to increment the auto increment, also
|
||||
@ -929,16 +975,16 @@ int ha_tina::update_row(const byte * old_data, byte * new_data)
|
||||
if (chain_append())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if (my_write(share->data_file, (byte*)buffer.ptr(), size,
|
||||
if (open_update_temp_file_if_needed())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if (my_write(update_temp_file, (byte*)buffer.ptr(), size,
|
||||
MYF(MY_WME | MY_NABP)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* UPDATE should never happen on the log tables */
|
||||
DBUG_ASSERT(!share->is_log_table);
|
||||
|
||||
/* update local copy of the max position to see our own changes */
|
||||
local_saved_data_file_length= share->file_stat.st_size;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
@ -1004,6 +1050,8 @@ int ha_tina::rnd_init(bool scan)
|
||||
{
|
||||
DBUG_ENTER("ha_tina::rnd_init");
|
||||
|
||||
/* set buffer to the beginning of the file */
|
||||
file_buff->init_buff(data_file);
|
||||
if (share->crashed)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
|
||||
@ -1011,11 +1059,6 @@ int ha_tina::rnd_init(bool scan)
|
||||
stats.records= 0;
|
||||
records_is_known= 0;
|
||||
chain_ptr= chain;
|
||||
#ifdef HAVE_MADVISE
|
||||
if (scan)
|
||||
(void) madvise(share->mapped_file, share->file_stat.st_size,
|
||||
MADV_SEQUENTIAL);
|
||||
#endif
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -1045,8 +1088,11 @@ int ha_tina::rnd_next(byte *buf)
|
||||
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
|
||||
|
||||
current_position= next_position;
|
||||
if (!share->mapped_file)
|
||||
|
||||
/* don't scan an empty file */
|
||||
if (!local_saved_data_file_length)
|
||||
DBUG_RETURN(HA_ERR_END_OF_FILE);
|
||||
|
||||
if ((rc= find_current_row(buf)))
|
||||
DBUG_RETURN(rc);
|
||||
|
||||
@ -1115,6 +1161,22 @@ int ha_tina::extra(enum ha_extra_function operation)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Set end_pos to the last valid byte of continuous area, closest
|
||||
to the given "hole", stored in the buffer. "Valid" here means,
|
||||
not listed in the chain of deleted records ("holes").
|
||||
*/
|
||||
bool ha_tina::get_write_pos(off_t *end_pos, tina_set *closest_hole)
|
||||
{
|
||||
if (closest_hole == chain_ptr) /* no more chains */
|
||||
*end_pos= file_buff->end();
|
||||
else
|
||||
*end_pos= min(file_buff->end(),
|
||||
closest_hole->begin);
|
||||
return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Called after each table scan. In particular after deletes,
|
||||
and updates. In the last case we employ chain of deleted
|
||||
@ -1123,53 +1185,107 @@ int ha_tina::extra(enum ha_extra_function operation)
|
||||
*/
|
||||
int ha_tina::rnd_end()
|
||||
{
|
||||
char updated_fname[FN_REFLEN];
|
||||
off_t file_buffer_start= 0;
|
||||
DBUG_ENTER("ha_tina::rnd_end");
|
||||
|
||||
records_is_known= 1;
|
||||
|
||||
/* First position will be truncate position, second will be increment */
|
||||
if ((chain_ptr - chain) > 0)
|
||||
{
|
||||
tina_set *ptr;
|
||||
size_t length;
|
||||
tina_set *ptr= chain;
|
||||
|
||||
/*
|
||||
Setting up writable map, this will contain all of the data after the
|
||||
get_mmap call that we have added to the file.
|
||||
Re-read the beginning of a file (as the buffer should point to the
|
||||
end of file after the scan).
|
||||
*/
|
||||
if (get_mmap(share, 1) > 0)
|
||||
DBUG_RETURN(-1);
|
||||
length= share->file_stat.st_size;
|
||||
file_buff->init_buff(data_file);
|
||||
|
||||
/*
|
||||
The sort handles updates/deletes with random orders.
|
||||
It also sorts so that we move the final blocks to the
|
||||
beginning so that we move the smallest amount of data possible.
|
||||
The sort is needed when there were updates/deletes with random orders.
|
||||
It sorts so that we move the firts blocks to the beginning.
|
||||
*/
|
||||
qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set),
|
||||
(qsort_cmp)sort_set);
|
||||
for (ptr= chain; ptr < chain_ptr; ptr++)
|
||||
|
||||
off_t write_begin= 0, write_end;
|
||||
|
||||
/* create the file to write updated table if it wasn't yet created */
|
||||
if (open_update_temp_file_if_needed())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* write the file with updated info */
|
||||
while ((file_buffer_start != -1)) // while not end of file
|
||||
{
|
||||
memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end,
|
||||
length - (size_t)ptr->end);
|
||||
length= length - (size_t)(ptr->end - ptr->begin);
|
||||
bool in_hole= get_write_pos(&write_end, ptr);
|
||||
|
||||
/* if there is something to write, write it */
|
||||
if ((write_end - write_begin) &&
|
||||
(my_write(update_temp_file,
|
||||
(byte*)(file_buff->ptr() +
|
||||
(write_begin - file_buff->start())),
|
||||
write_end - write_begin, MYF_RW)))
|
||||
goto error;
|
||||
|
||||
if (in_hole)
|
||||
{
|
||||
/* skip hole */
|
||||
while (file_buff->end() <= ptr->end && file_buffer_start != -1)
|
||||
file_buffer_start= file_buff->read_next();
|
||||
write_begin= ptr->end;
|
||||
ptr++;
|
||||
}
|
||||
else
|
||||
write_begin= write_end;
|
||||
|
||||
if (write_end == file_buff->end())
|
||||
file_buffer_start= file_buff->read_next(); /* shift the buffer */
|
||||
|
||||
}
|
||||
|
||||
/* Unmap the file before the new size is set */
|
||||
if (my_munmap(share->mapped_file, share->file_stat.st_size))
|
||||
DBUG_RETURN(-1);
|
||||
/* We set it to null so that get_mmap() won't try to unmap it */
|
||||
share->mapped_file= NULL;
|
||||
|
||||
/* Set the file to the new size */
|
||||
if (my_chsize(share->data_file, length, 0, MYF(MY_WME)))
|
||||
if (my_sync(update_temp_file, MYF(MY_WME)) ||
|
||||
my_close(update_temp_file, MYF(0)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if (get_mmap(share, 0) > 0)
|
||||
share->update_file_opened= FALSE;
|
||||
|
||||
if (share->tina_write_opened)
|
||||
{
|
||||
if (my_close(share->tina_write_filedes, MYF(0)))
|
||||
DBUG_RETURN(-1);
|
||||
/*
|
||||
Mark that the writer fd is closed, so that init_tina_writer()
|
||||
will reopen it later.
|
||||
*/
|
||||
share->tina_write_opened= FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
Close opened fildes's. Then move updated file in place
|
||||
of the old datafile.
|
||||
*/
|
||||
if (my_close(data_file, MYF(0)) ||
|
||||
my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT,
|
||||
MY_REPLACE_EXT | MY_UNPACK_FILENAME),
|
||||
share->data_file_name, MYF(0)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Open the file again */
|
||||
if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1))
|
||||
DBUG_RETURN(-1);
|
||||
/*
|
||||
The datafile is consistent at this point and the write filedes is
|
||||
closed, so nothing worrying will happen to it in case of a crash.
|
||||
Here we record this fact to the meta-file.
|
||||
*/
|
||||
(void)write_meta_file(share->meta_file, share->rows_recorded, FALSE);
|
||||
}
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
my_close(update_temp_file, MYF(0));
|
||||
share->update_file_opened= FALSE;
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
@ -1198,10 +1314,11 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
File repair_file;
|
||||
int rc;
|
||||
ha_rows rows_repaired= 0;
|
||||
off_t write_begin= 0, write_end;
|
||||
DBUG_ENTER("ha_tina::repair");
|
||||
|
||||
/* empty file */
|
||||
if (!share->mapped_file)
|
||||
if (!share->saved_data_file_length)
|
||||
{
|
||||
share->rows_recorded= 0;
|
||||
goto end;
|
||||
@ -1212,12 +1329,15 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
|
||||
|
||||
/* position buffer to the start of the file */
|
||||
file_buff->init_buff(data_file);
|
||||
|
||||
/*
|
||||
Local_saved_data_file_length is initialized during the lock phase.
|
||||
Sometimes this is not getting executed before ::repair (e.g. for
|
||||
the log tables). We set it manually here.
|
||||
*/
|
||||
local_saved_data_file_length= share->file_stat.st_size;
|
||||
local_saved_data_file_length= share->saved_data_file_length;
|
||||
/* set current position to the beginning of the file */
|
||||
current_position= next_position= 0;
|
||||
|
||||
@ -1232,11 +1352,10 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
|
||||
if (rc == HA_ERR_END_OF_FILE)
|
||||
{
|
||||
/* All rows were read ok until end of file, the file does not need repair. */
|
||||
|
||||
/*
|
||||
If rows_recorded != rows_repaired, we should update
|
||||
rows_recorded value to the current amount of rows.
|
||||
All rows were read ok until end of file, the file does not need repair.
|
||||
If rows_recorded != rows_repaired, we should update rows_recorded value
|
||||
to the current amount of rows.
|
||||
*/
|
||||
share->rows_recorded= rows_repaired;
|
||||
goto end;
|
||||
@ -1252,36 +1371,45 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
|
||||
|
||||
if (my_write(repair_file, (byte*)share->mapped_file, current_position,
|
||||
MYF(MY_NABP)))
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
my_close(repair_file, MYF(0));
|
||||
file_buff->init_buff(data_file);
|
||||
|
||||
|
||||
/* we just truncated the file up to the first bad row. update rows count. */
|
||||
share->rows_recorded= rows_repaired;
|
||||
|
||||
if (my_munmap(share->mapped_file, share->file_stat.st_size))
|
||||
DBUG_RETURN(-1);
|
||||
/* We set it to null so that get_mmap() won't try to unmap it */
|
||||
share->mapped_file= NULL;
|
||||
/* write repaired file */
|
||||
while (1)
|
||||
{
|
||||
write_end= min(file_buff->end(), current_position);
|
||||
if ((write_end - write_begin) &&
|
||||
(my_write(repair_file, (byte*)file_buff->ptr(),
|
||||
write_end - write_begin, MYF_RW)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
write_begin= write_end;
|
||||
if (write_end== current_position)
|
||||
break;
|
||||
else
|
||||
file_buff->read_next(); /* shift the buffer */
|
||||
}
|
||||
|
||||
/*
|
||||
Close the "to"-file before renaming
|
||||
On Windows one cannot rename a file, which descriptor
|
||||
is still open. EACCES will be returned when trying to delete
|
||||
the "to"-file in my_rename()
|
||||
Close the files and rename repaired file to the datafile.
|
||||
We have to close the files, as on Windows one cannot rename
|
||||
a file, which descriptor is still open. EACCES will be returned
|
||||
when trying to delete the "to"-file in my_rename().
|
||||
*/
|
||||
my_close(share->data_file,MYF(0));
|
||||
|
||||
if (my_rename(repaired_fname, share->data_file_name, MYF(0)))
|
||||
if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) ||
|
||||
my_rename(repaired_fname, share->data_file_name, MYF(0)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Open the file again, it should now be repaired */
|
||||
if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
|
||||
MYF(0))) == -1)
|
||||
if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
|
||||
MYF(0))) == -1)
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if (get_mmap(share, 0) > 0)
|
||||
DBUG_RETURN(-1);
|
||||
/* Set new file size. The file size will be updated by ::update_status() */
|
||||
local_saved_data_file_length= (size_t) current_position;
|
||||
|
||||
end:
|
||||
share->crashed= FALSE;
|
||||
@ -1300,17 +1428,12 @@ int ha_tina::delete_all_rows()
|
||||
if (!records_is_known)
|
||||
DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
|
||||
|
||||
/* Unmap the file before the new size is set */
|
||||
if (share->mapped_file && my_munmap(share->mapped_file,
|
||||
share->file_stat.st_size))
|
||||
DBUG_RETURN(-1);
|
||||
share->mapped_file= NULL;
|
||||
if (!share->tina_write_opened)
|
||||
if (init_tina_writer())
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* Truncate the file to zero size */
|
||||
rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME));
|
||||
|
||||
if (get_mmap(share, 0) > 0)
|
||||
DBUG_RETURN(-1);
|
||||
rc= my_chsize(share->tina_write_filedes, 0, 0, MYF(MY_WME));
|
||||
|
||||
stats.records=0;
|
||||
DBUG_RETURN(rc);
|
||||
@ -1372,12 +1495,15 @@ int ha_tina::check(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
|
||||
|
||||
/* position buffer to the start of the file */
|
||||
file_buff->init_buff(data_file);
|
||||
|
||||
/*
|
||||
Local_saved_data_file_length is initialized during the lock phase.
|
||||
Check does not use store_lock in certain cases. So, we set it
|
||||
manually here.
|
||||
*/
|
||||
local_saved_data_file_length= share->file_stat.st_size;
|
||||
local_saved_data_file_length= share->saved_data_file_length;
|
||||
/* set current position to the beginning of the file */
|
||||
current_position= next_position= 0;
|
||||
/* Read the file row-by-row. If everything is ok, repair is not needed. */
|
||||
|
@ -29,15 +29,12 @@
|
||||
typedef struct st_tina_share {
|
||||
char *table_name;
|
||||
char data_file_name[FN_REFLEN];
|
||||
byte *mapped_file; /* mapped region of file */
|
||||
uint table_name_length, use_count;
|
||||
/*
|
||||
Below flag is needed to make log tables work with concurrent insert.
|
||||
For more details see comment to ha_tina::update_status.
|
||||
*/
|
||||
my_bool is_log_table;
|
||||
MY_STAT file_stat; /* Stat information for the data file */
|
||||
File data_file; /* Current open data file */
|
||||
/*
|
||||
Here we save the length of the file for readers. This is updated by
|
||||
inserts, updates and deletes. The var is initialized along with the
|
||||
@ -46,7 +43,10 @@ typedef struct st_tina_share {
|
||||
off_t saved_data_file_length;
|
||||
pthread_mutex_t mutex;
|
||||
THR_LOCK lock;
|
||||
bool update_file_opened;
|
||||
bool tina_write_opened;
|
||||
File meta_file; /* Meta file we use */
|
||||
File tina_write_filedes; /* File handler for readers */
|
||||
bool crashed; /* Meta file is crashed */
|
||||
ha_rows rows_recorded; /* Number of rows in tables */
|
||||
} TINA_SHARE;
|
||||
@ -56,6 +56,49 @@ struct tina_set {
|
||||
off_t end;
|
||||
};
|
||||
|
||||
class Transparent_file
|
||||
{
|
||||
File filedes;
|
||||
byte *buff; /* in-memory window to the file or mmaped area */
|
||||
/* current window sizes */
|
||||
off_t lower_bound;
|
||||
off_t upper_bound;
|
||||
uint buff_size;
|
||||
|
||||
public:
|
||||
|
||||
Transparent_file() : lower_bound(0), buff_size(IO_SIZE)
|
||||
{ buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); }
|
||||
|
||||
~Transparent_file()
|
||||
{ my_free((gptr)buff, MYF(MY_ALLOW_ZERO_PTR)); }
|
||||
|
||||
void init_buff(File filedes_arg)
|
||||
{
|
||||
filedes= filedes_arg;
|
||||
/* read the beginning of the file */
|
||||
lower_bound= 0;
|
||||
VOID(my_seek(filedes, 0, MY_SEEK_SET, MYF(0)));
|
||||
if (filedes && buff)
|
||||
upper_bound= my_read(filedes, buff, buff_size, MYF(0));
|
||||
}
|
||||
|
||||
byte *ptr()
|
||||
{ return buff; }
|
||||
|
||||
off_t start()
|
||||
{ return lower_bound; }
|
||||
|
||||
off_t end()
|
||||
{ return upper_bound; }
|
||||
|
||||
/* get a char from the given position in the file */
|
||||
char get_value (off_t offset);
|
||||
/* shift a buffer windows to see the next part of the file */
|
||||
off_t read_next();
|
||||
|
||||
};
|
||||
|
||||
class ha_tina: public handler
|
||||
{
|
||||
THR_LOCK_DATA lock; /* MySQL lock */
|
||||
@ -64,6 +107,9 @@ class ha_tina: public handler
|
||||
off_t next_position; /* Next position in the file scan */
|
||||
off_t local_saved_data_file_length; /* save position for reads */
|
||||
byte byte_buffer[IO_SIZE];
|
||||
Transparent_file *file_buff;
|
||||
File data_file; /* File handler for readers */
|
||||
File update_temp_file;
|
||||
String buffer;
|
||||
/*
|
||||
The chain contains "holes" in the file, occured because of
|
||||
@ -77,12 +123,19 @@ class ha_tina: public handler
|
||||
uint32 chain_size;
|
||||
bool records_is_known;
|
||||
|
||||
private:
|
||||
bool get_write_pos(off_t *end_pos, tina_set *closest_hole);
|
||||
int open_update_temp_file_if_needed();
|
||||
int init_tina_writer();
|
||||
|
||||
public:
|
||||
ha_tina(TABLE_SHARE *table_arg);
|
||||
~ha_tina()
|
||||
~ha_tina()
|
||||
{
|
||||
if (chain_alloced)
|
||||
my_free((gptr)chain,0);
|
||||
my_free((gptr)chain, 0);
|
||||
if (file_buff)
|
||||
delete file_buff;
|
||||
}
|
||||
const char *table_type() const { return "CSV"; }
|
||||
const char *index_type(uint inx) { return "NONE"; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user