mariadb/sql/log_event.cc
unknown 518a621adf Fix of LEFT JOIN optimizer bug, problem with key cache on Windows and
a lot of portability issues.


Docs/manual.texi:
  Changed FOREIGN KEY to FOREIGN KEY constraint
client/mysqladmin.c:
  Don't flush MASTER or SLAVE on refresh
configure.in:
  Fix for hpux 11.0
extra/perror.c:
  New error message
include/my_pthread.h:
  Portability fix for windows
myisam/mi_locking.c:
  Ensure that locking doesn't interfere with pread/pwrite on windows
myisam/sort.c:
  checked with purecover
mysys/mf_tempfile.c:
  Fix for windows
mysys/my_lock.c:
  Ensure that locking doesn't interfere with pread/pwrite on windows
mysys/my_winthread.c:
  Portability fix
sql-bench/Results/ATIS-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/RUN-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/alter-table-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/big-tables-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/connect-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/create-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/insert-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/select-mysql-NT_4.0:
  New benchmark runs
sql-bench/Results/wisconsin-mysql-NT_4.0:
  New benchmark runs
sql-bench/crash-me.sh:
  Fixed things for PostgreSQL
sql-bench/limits/mysql-3.23.cfg:
  Update for new crash-me
sql-bench/limits/mysql.cfg:
  Update for new crash-me
sql-bench/print-limit-table:
  Fixed position for alter table rename
sql-bench/test-insert.sh:
  Fix for PostgreSQL
sql/field.cc:
  Fix for default values in CREATE ... SELECT
sql/field.h:
  Fix for default values in CREATE ... SELECT
sql/log.cc:
  Fixed typo
sql/log_event.cc:
  Portability fix
sql/mysqlbinlog.cc:
  Portability fix
sql/mysqld.cc:
  Don't turn of concurrent insert with --skip-new or --safe
sql/sql_base.cc:
  Portability fix
sql/sql_class.cc:
  Portability fix
sql/sql_class.h:
  Portability fix
sql/sql_parse.cc:
  Fix for --log-slow-queries
sql/sql_repl.cc:
  Portability fixes
sql/sql_select.cc:
  Fixed optimizer bug for LEFT JOIN
sql/sql_select.h:
  Fixed optimizer bug for LEFT JOIN
sql/sql_table.cc:
  Fix for default values in CREATE ... SELECT
sql/sql_yacc.yy:
  Added optional AS to: CREATE TABLE foo [ AS ] SELECT ...
2000-10-14 03:16:35 +03:00

747 lines
18 KiB
C++

/* 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 */
#ifndef MYSQL_CLIENT
#ifdef __GNUC__
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
#endif /* MYSQL_CLIENT */
static void pretty_print_char(FILE* file, int c)
{
fputc('\'', file);
switch(c)
{
case '\n': fprintf(file, "\\n"); break;
case '\r': fprintf(file, "\\r"); break;
case '\\': fprintf(file, "\\\\"); break;
case '\b': fprintf(file, "\\b"); break;
case '\'': fprintf(file, "\\'"); break;
case 0 : fprintf(file, "\\0"); break;
default:
fputc(c, file);
break;
}
fputc( '\'', file);
}
int Query_log_event::write(FILE* file)
{
return query ? Log_event::write(file) : -1;
}
int Log_event::write(FILE* file)
{
if (write_header(file)
|| write_data(file) || fflush(file)) return -1;
return 0;
}
int Log_event::write_header(FILE* file)
{
char buf[LOG_EVENT_HEADER_LEN];
// make sure to change this when the header gets bigger
char* pos = buf;
int4store(pos, when); // timestamp
pos += 4;
*pos++ = get_type_code(); // event type code
int4store(pos, server_id);
pos += 4;
int4store(pos, get_data_size() + LOG_EVENT_HEADER_LEN);
pos += 4;
return (my_fwrite(file, (byte*) buf, (uint) (pos - buf),
MYF(MY_NABP | MY_WME)));
}
#ifndef MYSQL_CLIENT
int Log_event::read_log_event(FILE* file, String* packet,
pthread_mutex_t* log_lock)
{
ulong data_len;
char buf[LOG_EVENT_HEADER_LEN];
if(log_lock)
pthread_mutex_lock(log_lock);
if (my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP)))
{
if(log_lock) pthread_mutex_unlock(log_lock);
return feof(file) ? LOG_READ_EOF: LOG_READ_IO;
}
data_len = uint4korr(buf + EVENT_LEN_OFFSET);
if (data_len < LOG_EVENT_HEADER_LEN || data_len > MAX_EVENT_LEN)
{
if(log_lock) pthread_mutex_unlock(log_lock);
return LOG_READ_BOGUS;
}
packet->append(buf, sizeof(buf));
data_len -= LOG_EVENT_HEADER_LEN;
if (!data_len)
{
if(log_lock) pthread_mutex_unlock(log_lock);
return 0; // the event does not have a data section
}
if (packet->append(file, data_len, MYF(MY_WME|MY_NABP)))
{
if(log_lock)
pthread_mutex_unlock(log_lock);
return feof(file) ? LOG_READ_TRUNC: LOG_READ_IO;
}
if(log_lock) pthread_mutex_unlock(log_lock);
return 0;
}
#endif // MYSQL_CLIENT
// allocates memory - the caller is responsible for clean-up
Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
{
time_t timestamp;
uint32 server_id;
char buf[LOG_EVENT_HEADER_LEN-4];
if(log_lock) pthread_mutex_lock(log_lock);
if (my_fread(file, (byte *) buf, sizeof(buf), MY_NABP))
{
if(log_lock) pthread_mutex_unlock(log_lock);
return NULL;
}
timestamp = uint4korr(buf);
server_id = uint4korr(buf + 5);
switch(buf[EVENT_TYPE_OFFSET])
{
case QUERY_EVENT:
{
Query_log_event* q = new Query_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (!q->query)
{
delete q;
return NULL;
}
return q;
}
case LOAD_EVENT:
{
Load_log_event* l = new Load_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (!l->table_name)
{
delete l;
return NULL;
}
return l;
}
case ROTATE_EVENT:
{
Rotate_log_event* r = new Rotate_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (!r->new_log_ident)
{
delete r;
return NULL;
}
return r;
}
case INTVAR_EVENT:
{
Intvar_log_event* e = new Intvar_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (e->type == INVALID_INT_EVENT)
{
delete e;
return NULL;
}
return e;
}
case START_EVENT:
{
Start_log_event* e = new Start_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
return e;
}
case STOP_EVENT:
{
Stop_log_event* e = new Stop_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
return e;
}
default:
if(log_lock) pthread_mutex_unlock(log_lock);
return NULL;
}
//impossible
if(log_lock) pthread_mutex_unlock(log_lock);
return NULL;
}
Log_event* Log_event::read_log_event(const char* buf, int max_buf)
{
switch(buf[EVENT_TYPE_OFFSET])
{
case QUERY_EVENT:
{
Query_log_event* q = new Query_log_event(buf, max_buf);
if (!q->query)
{
delete q;
return NULL;
}
return q;
}
case LOAD_EVENT:
{
Load_log_event* l = new Load_log_event(buf, max_buf);
if (!l->table_name)
{
delete l;
return NULL;
}
return l;
}
case ROTATE_EVENT:
{
Rotate_log_event* r = new Rotate_log_event(buf, max_buf);
if (!r->new_log_ident)
{
delete r;
return NULL;
}
return r;
}
case START_EVENT: return new Start_log_event(buf);
case STOP_EVENT: return new Stop_log_event(buf);
case INTVAR_EVENT: return new Intvar_log_event(buf);
default: return NULL;
}
//impossible
return NULL;
}
void Log_event::print_header(FILE* file)
{
fputc('#', file);
print_timestamp(file);
fprintf(file, " server id %ld ", server_id);
}
void Log_event::print_timestamp(FILE* file, time_t* ts)
{
struct tm tm_tmp;
if (!ts)
{
ts = &when;
}
localtime_r(ts,&tm_tmp);
fprintf(file,"%02d%02d%02d %2d:%02d:%02d",
tm_tmp.tm_year % 100,
tm_tmp.tm_mon+1,
tm_tmp.tm_mday,
tm_tmp.tm_hour,
tm_tmp.tm_min,
tm_tmp.tm_sec);
}
void Start_log_event::print(FILE* file, bool short_form)
{
if (short_form)
return;
print_header(file);
fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
server_version);
print_timestamp(file, (time_t*)&created);
fputc('\n', file);
fflush(file);
}
void Stop_log_event::print(FILE* file, bool short_form)
{
if (short_form)
return;
print_header(file);
fprintf(file, "\tStop\n");
fflush(file);
}
void Rotate_log_event::print(FILE* file, bool short_form)
{
if (short_form)
return;
print_header(file);
fprintf(file, "\tRotate to ");
if (new_log_ident)
my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
MYF(MY_NABP | MY_WME));
fprintf(file, "\n");
fflush(file);
}
Rotate_log_event::Rotate_log_event(FILE* file, time_t when_arg,
uint32 server_id):
Log_event(when_arg, 0, 0, server_id),new_log_ident(NULL),alloced(0)
{
char *tmp_ident;
char buf[4];
if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
return;
ulong event_len;
event_len = uint4korr(buf);
if(event_len < ROTATE_EVENT_OVERHEAD)
return;
ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
if (!(tmp_ident = (char*) my_malloc((uint)ident_len, MYF(MY_WME))))
return;
if (my_fread( file, (byte*) tmp_ident, (uint)ident_len, MYF(MY_NABP | MY_WME)))
{
my_free((gptr) tmp_ident, MYF(0));
return;
}
new_log_ident = tmp_ident;
alloced = 1;
}
Start_log_event::Start_log_event(const char* buf) :Log_event(buf)
{
buf += EVENT_LEN_OFFSET + 4; // skip even length
binlog_version = uint2korr(buf);
memcpy(server_version, buf + 2, sizeof(server_version));
created = uint4korr(buf + 2 + sizeof(server_version));
}
Rotate_log_event::Rotate_log_event(const char* buf, int max_buf):
Log_event(buf),new_log_ident(NULL),alloced(0)
{
ulong event_len;
event_len = uint4korr(buf + EVENT_LEN_OFFSET);
if(event_len < ROTATE_EVENT_OVERHEAD || event_len > (ulong) max_buf)
return;
ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
if (!(new_log_ident = (char*) my_memdup((byte*) buf + LOG_EVENT_HEADER_LEN,
(uint) ident_len, MYF(MY_WME))))
return;
alloced = 1;
}
int Rotate_log_event::write_data(FILE* file)
{
if (my_fwrite(file, (byte*) new_log_ident, (uint) ident_len,
MYF(MY_NABP | MY_WME)))
return -1;
return 0;
}
Query_log_event::Query_log_event(FILE* file, time_t when_arg,
uint32 server_id):
Log_event(when_arg,0,0,server_id),data_buf(0),query(NULL),db(NULL)
{
char buf[QUERY_HEADER_LEN + 4];
ulong data_len;
if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
return; // query == NULL will tell the
// caller there was a problem
data_len = uint4korr(buf);
if (data_len < QUERY_EVENT_OVERHEAD)
return; // tear-drop attack protection :)
data_len -= QUERY_EVENT_OVERHEAD;
exec_time = uint4korr(buf + 8);
db_len = (uint)buf[12];
error_code = uint2korr(buf + 13);
if (!(data_buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
return;
if (my_fread( file, (byte*) data_buf, data_len, MYF(MY_NABP | MY_WME)))
{
my_free((gptr) data_buf, MYF(0));
data_buf = 0;
return;
}
thread_id = uint4korr(buf + 4);
db = data_buf;
query=data_buf + db_len + 1;
q_len = data_len - 1 - db_len;
*((char*)query + q_len) = 0;
}
Query_log_event::Query_log_event(const char* buf, int max_buf):
Log_event(buf),data_buf(0), query(NULL), db(NULL)
{
ulong data_len;
buf += EVENT_LEN_OFFSET;
data_len = uint4korr(buf);
if (data_len < QUERY_EVENT_OVERHEAD || data_len > (ulong) max_buf)
return; // tear-drop attack protection :)
data_len -= QUERY_EVENT_OVERHEAD;
exec_time = uint4korr(buf + 8);
error_code = uint2korr(buf + 13);
if (!(data_buf = (char*) my_malloc( data_len + 1, MYF(MY_WME))))
return;
memcpy(data_buf, buf + QUERY_HEADER_LEN + 4, data_len);
thread_id = uint4korr(buf + 4);
db = data_buf;
db_len = (uint)buf[12];
query=data_buf + db_len + 1;
q_len = data_len - 1 - db_len;
*((char*)query+q_len) = 0;
}
void Query_log_event::print(FILE* file, bool short_form)
{
if (!short_form)
{
print_header(file);
fprintf(file, "\tQuery\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
(ulong) thread_id, (ulong) exec_time, error_code);
}
if(db && db[0])
fprintf(file, "use %s;\n", db);
my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
fprintf(file, ";\n");
}
int Query_log_event::write_data(FILE* file)
{
if(!query) return -1;
char buf[QUERY_HEADER_LEN];
char* pos = buf;
int4store(pos, thread_id);
pos += 4;
int4store(pos, exec_time);
pos += 4;
*pos++ = (char)db_len;
int2store(pos, error_code);
pos += 2;
if (my_fwrite(file, (byte*) buf, (uint)(pos - buf), MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (db) ? (byte*) db : (byte*)"",
db_len + 1, MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)))
return -1;
return 0;
}
Intvar_log_event:: Intvar_log_event(FILE* file, time_t when_arg,
uint32 server_id)
:Log_event(when_arg,0,0,server_id), type(INVALID_INT_EVENT)
{
my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
char buf[9];
if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME))) return;
type = buf[0];
val = uint8korr(buf+1);
}
Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
{
buf += LOG_EVENT_HEADER_LEN;
type = buf[0];
val = uint8korr(buf+1);
}
int Intvar_log_event::write_data(FILE* file)
{
char buf[9];
buf[0] = type;
int8store(buf + 1, val);
return my_fwrite(file, (byte*) buf, sizeof(buf), MYF(MY_NABP|MY_WME));
}
void Intvar_log_event::print(FILE* file, bool short_form)
{
char llbuff[22];
if(!short_form)
{
print_header(file);
fprintf(file, "\tIntvar\n");
}
fprintf(file, "SET ");
switch(type)
{
case LAST_INSERT_ID_EVENT:
fprintf(file, "LAST_INSERT_ID = ");
break;
case INSERT_ID_EVENT:
fprintf(file, "INSERT_ID = ");
break;
}
fprintf(file, "%s;\n", llstr(val,llbuff));
fflush(file);
}
int Load_log_event::write_data(FILE* file __attribute__((unused)))
{
char buf[LOAD_HEADER_LEN];
int4store(buf, thread_id);
int4store(buf + 4, exec_time);
int4store(buf + 8, skip_lines);
buf[12] = (char)table_name_len;
buf[13] = (char)db_len;
int4store(buf + 14, num_fields);
if(my_fwrite(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
return 1;
if(num_fields && fields && field_lens)
{
if(my_fwrite(file, (byte*)field_lens, num_fields, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)fields, field_block_len, MYF(MY_NABP|MY_WME)))
return 1;
}
if(my_fwrite(file, (byte*)table_name, table_name_len + 1, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)db, db_len + 1, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)fname, fname_len, MYF(MY_NABP|MY_WME)) )
return 1;
return 0;
}
Load_log_event::Load_log_event(FILE* file, time_t when, uint32 server_id):
Log_event(when,0,0,server_id),data_buf(0),num_fields(0),
fields(0),field_lens(0),field_block_len(0),
table_name(0),db(0),fname(0)
{
char buf[LOAD_HEADER_LEN + 4];
ulong data_len;
if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
my_fread(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
return;
data_len = uint4korr(buf);
thread_id = uint4korr(buf+4);
exec_time = uint4korr(buf+8);
skip_lines = uint4korr(buf + 12);
table_name_len = (uint)buf[16];
db_len = (uint)buf[17];
num_fields = uint4korr(buf + 18);
data_len -= LOAD_EVENT_OVERHEAD;
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
return;
if(my_fread(file, (byte*)data_buf, data_len, MYF(MY_NABP|MY_WME)))
return;
if(num_fields > data_len) // simple sanity check against corruption
return;
field_lens = (uchar*)data_buf;
uint i;
for(i = 0; i < num_fields; i++)
{
field_block_len += (uint)field_lens[i] + 1;
}
fields = (char*)field_lens + num_fields;
*((char*)data_buf+data_len) = 0;
table_name = fields + field_block_len;
db = table_name + table_name_len + 1;
fname = db + db_len + 1;
fname_len = data_len - 2 - db_len - table_name_len - num_fields - field_block_len;
}
Load_log_event::Load_log_event(const char* buf, int max_buf):
Log_event(when,0,0,server_id),data_buf(0),num_fields(0),fields(0),
field_lens(0),field_block_len(0),
table_name(0),db(0),fname(0)
{
ulong data_len;
if((uint)max_buf < (LOAD_EVENT_OVERHEAD + LOG_EVENT_HEADER_LEN))
return;
buf += EVENT_LEN_OFFSET;
data_len = uint4korr(buf);
if((uint)data_len > (uint)max_buf)
return;
thread_id = uint4korr(buf+4);
exec_time = uint4korr(buf+8);
skip_lines = uint4korr(buf + 12);
table_name_len = (uint)buf[16];
db_len = (uint)buf[17];
num_fields = uint4korr(buf + 18);
data_len -= LOAD_EVENT_OVERHEAD;
memcpy(&sql_ex, buf + 22, sizeof(sql_ex));
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
return;
memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len);
if(num_fields > data_len) // simple sanity check against corruption
return;
field_lens = (uchar*)data_buf;
uint i;
for(i = 0; i < num_fields; i++)
{
field_block_len += (uint)field_lens[i] + 1;
}
fields = (char*)field_lens + num_fields;
*((char*)data_buf+data_len) = 0;
table_name = fields + field_block_len;
db = table_name + table_name_len + 1;
fname = db + db_len + 1;
fname_len = data_len - 2 - db_len - table_name_len - num_fields - field_block_len;
}
void Load_log_event::print(FILE* file, bool short_form)
{
if (!short_form)
{
print_header(file);
fprintf(file, "\tQuery\tthread_id=%d\texec_time=%ld\n",
thread_id, exec_time);
}
if(db && db[0])
fprintf(file, "use %s;\n", db);
fprintf(file, "LOAD DATA INFILE '%s' ", fname);
if(sql_ex.opt_flags && REPLACE_FLAG )
fprintf(file," REPLACE ");
else if(sql_ex.opt_flags && IGNORE_FLAG )
fprintf(file," IGNORE ");
fprintf(file, "INTO TABLE %s ", table_name);
if(!(sql_ex.empty_flags & FIELD_TERM_EMPTY))
{
fprintf(file, " FIELDS TERMINATED BY ");
pretty_print_char(file, sql_ex.field_term);
}
if(!(sql_ex.empty_flags & ENCLOSED_EMPTY))
{
if(sql_ex.opt_flags && OPT_ENCLOSED_FLAG )
fprintf(file," OPTIONALLY ");
fprintf(file, " ENCLOSED BY ");
pretty_print_char(file, sql_ex.enclosed);
}
if(!(sql_ex.empty_flags & ESCAPED_EMPTY))
{
fprintf(file, " ESCAPED BY ");
pretty_print_char(file, sql_ex.escaped);
}
if(!(sql_ex.empty_flags & LINE_TERM_EMPTY))
{
fprintf(file," LINES TERMINATED BY ");
pretty_print_char(file, sql_ex.line_term);
}
if(!(sql_ex.empty_flags & LINE_START_EMPTY))
{
fprintf(file," LINES STARTING BY ");
pretty_print_char(file, sql_ex.line_start);
}
if((int)skip_lines > 0)
fprintf(file, " IGNORE %ld LINES ", skip_lines);
if(num_fields)
{
uint i;
const char* field = fields;
fprintf( file, " (");
for(i = 0; i < num_fields; i++)
{
if(i)
fputc(',', file);
fprintf(file, field);
field += field_lens[i] + 1;
}
fputc(')', file);
}
fprintf(file, ";\n");
}
#ifndef MYSQL_CLIENT
void Load_log_event::set_fields(List<Item> &fields)
{
uint i;
const char* field = this->fields;
for(i = 0; i < num_fields; i++)
{
fields.push_back(new Item_field(db, table_name, field));
field += field_lens[i] + 1;
}
}
#endif