diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index c7d6ca928f1..453407127d2 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -54,7 +54,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ item_geofunc.cc item_subselect.cc item_row.cc\ item_xmlfunc.cc \ key.cc lock.cc log.cc sql_state.c \ - log_event.cc \ + log_event.cc rpl_record.cc \ log_event_old.cc rpl_record_old.cc \ protocol.cc net_serv.cc opt_range.cc \ opt_sum.cc procedure.cc records.cc sql_acl.cc \ diff --git a/mysql-test/r/rpl_ndb_circular_simplex.result b/mysql-test/r/rpl_ndb_circular_simplex.result index c9958264d95..fda5ce68db9 100644 --- a/mysql-test/r/rpl_ndb_circular_simplex.result +++ b/mysql-test/r/rpl_ndb_circular_simplex.result @@ -37,7 +37,7 @@ Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 -Exec_Master_Log_Pos 468 +Exec_Master_Log_Pos 754 Relay_Log_Space # Until_Condition None Until_Log_File diff --git a/mysql-test/r/rpl_ndb_log.result b/mysql-test/r/rpl_ndb_log.result index 3720da993be..db2ce27d43b 100644 --- a/mysql-test/r/rpl_ndb_log.result +++ b/mysql-test/r/rpl_ndb_log.result @@ -99,33 +99,33 @@ show binlog events in 'slave-bin.000001' from 4; Log_name Pos Event_type Server_id End_log_pos Info slave-bin.000001 # Format_desc 2 # Server ver: VERSION, Binlog ver: 4 slave-bin.000001 # Query 1 # use `test`; create table t1(n int not null auto_increment primary key)ENGINE=NDB -slave-bin.000001 # Query 1 # BEGIN +slave-bin.000001 # Query 2 # BEGIN slave-bin.000001 # Table_map 2 # table_id: # (test.t1) slave-bin.000001 # Table_map 2 # table_id: # (mysql.ndb_apply_status) slave-bin.000001 # Write_rows 2 # table_id: # slave-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -slave-bin.000001 # Query 1 # COMMIT +slave-bin.000001 # Query 2 # COMMIT slave-bin.000001 # Query 1 # use `test`; drop table t1 slave-bin.000001 # Query 1 # use `test`; create table t1 (word char(20) not null)ENGINE=NDB -slave-bin.000001 # Query 1 # BEGIN +slave-bin.000001 # Query 2 # BEGIN slave-bin.000001 # Table_map 2 # table_id: # (test.t1) slave-bin.000001 # Table_map 2 # table_id: # (mysql.ndb_apply_status) slave-bin.000001 # Write_rows 2 # table_id: # slave-bin.000001 # Write_rows 1 # table_id: # slave-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F -slave-bin.000001 # Query 1 # COMMIT +slave-bin.000001 # Query 2 # COMMIT slave-bin.000001 # Query 1 # use `test`; create table t3 (a int)ENGINE=NDB slave-bin.000001 # Rotate 2 # slave-bin.000002;pos=4 show binlog events in 'slave-bin.000002' from 4; Log_name Pos Event_type Server_id End_log_pos Info slave-bin.000002 # Format_desc 2 # Server ver: VERSION, Binlog ver: 4 slave-bin.000002 # Query 1 # use `test`; create table t2 (n int)ENGINE=NDB -slave-bin.000002 # Query 1 # BEGIN +slave-bin.000002 # Query 2 # BEGIN slave-bin.000002 # Table_map 2 # table_id: # (test.t2) slave-bin.000002 # Table_map 2 # table_id: # (mysql.ndb_apply_status) slave-bin.000002 # Write_rows 2 # table_id: # slave-bin.000002 # Write_rows 1 # table_id: # flags: STMT_END_F -slave-bin.000002 # Query 1 # COMMIT +slave-bin.000002 # Query 2 # COMMIT show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert # 127.0.0.1 root MASTER_PORT 1 master-bin.000002 617 # # master-bin.000002 Yes Yes # 0 0 617 # None 0 No # No diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index f0e7bea74c6..6d0d0473151 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -47,7 +47,8 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc - key.cc log.cc lock.cc log_event.cc message.rc + key.cc log.cc lock.cc message.rc + log_event.cc rpl_record.cc log_event_old.cc rpl_record_old.cc message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c mysqld.cc net_serv.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 0656c1a8f9b..037986e3e45 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -57,8 +57,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ log.h sql_show.h rpl_rli.h rpl_mi.h \ sql_select.h structs.h table.h sql_udf.h hash_filo.h \ lex.h lex_symbol.h sql_acl.h sql_crypt.h \ - log_event.h sql_repl.h slave.h rpl_filter.h \ - rpl_injector.h \ + sql_repl.h slave.h rpl_filter.h rpl_injector.h \ + log_event.h rpl_record.h \ log_event_old.h rpl_record_old.h \ stacktrace.h sql_sort.h sql_cache.h set_var.h \ spatial.h gstream.h client_settings.h tzfile.h \ @@ -87,8 +87,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_prepare.cc sql_error.cc sql_locale.cc \ sql_update.cc sql_delete.cc uniques.cc sql_do.cc \ procedure.cc sql_test.cc \ - log.cc log_event.cc init.cc derror.cc sql_acl.cc \ + log.cc init.cc derror.cc sql_acl.cc \ unireg.cc des_key_file.cc \ + log_event.cc rpl_record.cc \ log_event_old.cc rpl_record_old.cc \ discover.cc time.cc opt_range.cc opt_sum.cc \ records.cc filesort.cc handler.cc \ diff --git a/sql/log_event.cc b/sql/log_event.cc index d484ef55e5e..b67629a2c08 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -26,6 +26,7 @@ #include "rpl_mi.h" #include "rpl_filter.h" #include "rpl_utility.h" +#include "rpl_record.h" #include #endif /* MYSQL_CLIENT */ #include @@ -5811,163 +5812,6 @@ int Rows_log_event::do_add_row_data(byte *const row_data, #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -/* - Unpack a row into table->record[0]. - - SYNOPSIS - unpack_row() - rli Relay log info - table Table to unpack into - colcnt Number of columns to read from record - row Packed row data - cols Pointer to columns data to fill in - row_end Pointer to variable that will hold the value of the - one-after-end position for the row - master_reclength - Pointer to variable that will be set to the length of the - record on the master side - rw_set Pointer to bitmap that holds either the read_set or the - write_set of the table - - DESCRIPTION - - The function will always unpack into the table->record[0] - record. This is because there are too many dependencies on - where the various member functions of Field and subclasses - expect to write. - - The row is assumed to only consist of the fields for which the - bitset represented by 'arr' and 'bits'; the other parts of the - record are left alone. - - At most 'colcnt' columns are read: if the table is larger than - that, the remaining fields are not filled in. - - RETURN VALUE - - Error code, or zero if no error. The following error codes can - be returned: - - ER_NO_DEFAULT_FOR_FIELD - Returned if one of the fields existing on the slave but not on - the master does not have a default value (and isn't nullable) - */ -static int -unpack_row(RELAY_LOG_INFO const *rli, - TABLE *table, uint const colcnt, - char const *const row_data, MY_BITMAP const *cols, - char const **const row_end, ulong *const master_reclength, - MY_BITMAP* const rw_set, Log_event_type const event_type) -{ - DBUG_ENTER("unpack_row"); - DBUG_ASSERT(row_data); - my_size_t const master_null_byte_count= (bitmap_bits_set(cols) + 7) / 8; - int error= 0; - - char const *null_ptr= row_data; - char const *pack_ptr= row_data + master_null_byte_count; - - bitmap_clear_all(rw_set); - - empty_record(table); - - Field **const begin_ptr = table->field; - Field **field_ptr; - Field **const end_ptr= begin_ptr + colcnt; - - DBUG_ASSERT(null_ptr < row_data + master_null_byte_count); - - // Mask to mask out the correct bit among the null bits - unsigned int null_mask= 1U; - // The "current" null bits - unsigned int null_bits= *null_ptr++; - for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr) - { - Field *const f= *field_ptr; - - /* - No need to bother about columns that does not exist: they have - gotten default values when being emptied above. - */ - if (bitmap_is_set(cols, field_ptr - begin_ptr)) - { - if ((null_mask & 0xFF) == 0) - { - DBUG_ASSERT(null_ptr < row_data + master_null_byte_count); - null_mask= 1U; - null_bits= *null_ptr++; - } - - DBUG_ASSERT(null_mask & 0xFF); // One of the 8 LSB should be set - - /* Field...::unpack() cannot return 0 */ - DBUG_ASSERT(pack_ptr != NULL); - - if ((null_bits & null_mask) && f->maybe_null()) - f->set_null(); - else - { - f->set_notnull(); - - /* - We only unpack the field if it was non-null - */ - pack_ptr= f->unpack(f->ptr, pack_ptr); - } - - bitmap_set_bit(rw_set, f->field_index); - null_mask <<= 1; - } - } - - /* - We should now have read all the null bytes, otherwise something is - really wrong. - */ - DBUG_ASSERT(null_ptr == row_data + master_null_byte_count); - - *row_end = pack_ptr; - if (master_reclength) - { - if (*field_ptr) - *master_reclength = (*field_ptr)->ptr - (char*) table->record[0]; - else - *master_reclength = table->s->reclength; - } - - /* - Set properties for remaining columns, if there are any. We let the - corresponding bit in the write_set be set, to write the value if - it was not there already. We iterate over all remaining columns, - even if there were an error, to get as many error messages as - possible. We are still able to return a pointer to the next row, - so redo that. - - This generation of error messages is only relevant when inserting - new rows. - */ - for ( ; *field_ptr ; ++field_ptr) - { - uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; - Field *const f= *field_ptr; - - if (event_type == WRITE_ROWS_EVENT && - ((*field_ptr)->flags & mask) == mask) - { - slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, - "Field `%s` of table `%s`.`%s` " - "has no default value and cannot be NULL", - (*field_ptr)->field_name, table->s->db.str, - table->s->table_name.str); - error = ER_NO_DEFAULT_FOR_FIELD; - } - else - f->set_default(); - } - - DBUG_RETURN(error); -} - int Rows_log_event::do_apply_event(RELAY_LOG_INFO const *rli) { DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)"); diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 1172a3e0ad4..500feab0542 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -5,7 +5,8 @@ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) int -Write_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, +Write_rows_log_event_old::do_prepare_row(THD *thd, + RELAY_LOG_INFO const *rli, TABLE *table, char const *row_start, char const **row_end) @@ -14,7 +15,8 @@ Write_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, DBUG_ASSERT(row_start && row_end); int error; - error= unpack_row_old(rli, table, m_width, table->record[0], + error= unpack_row_old(const_cast(rli), + table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->write_set, PRE_GA_WRITE_ROWS_EVENT); bitmap_copy(table->read_set, table->write_set); @@ -23,7 +25,8 @@ Write_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, int -Delete_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, +Delete_rows_log_event_old::do_prepare_row(THD *thd, + RELAY_LOG_INFO const *rli, TABLE *table, char const *row_start, char const **row_end) @@ -36,7 +39,8 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, */ DBUG_ASSERT(table->s->fields >= m_width); - error= unpack_row_old(rli, table, m_width, table->record[0], + error= unpack_row_old(const_cast(rli), + table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->read_set, PRE_GA_DELETE_ROWS_EVENT); /* @@ -54,7 +58,8 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, } -int Update_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, +int Update_rows_log_event_old::do_prepare_row(THD *thd, + RELAY_LOG_INFO const *rli, TABLE *table, char const *row_start, char const **row_end) @@ -68,12 +73,14 @@ int Update_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, DBUG_ASSERT(table->s->fields >= m_width); /* record[0] is the before image for the update */ - error= unpack_row_old(rli, table, m_width, table->record[0], + error= unpack_row_old(const_cast(rli), + table, m_width, table->record[0], row_start, &m_cols, row_end, &m_master_reclength, table->read_set, PRE_GA_UPDATE_ROWS_EVENT); row_start = *row_end; /* m_after_image is the after image for the update */ - error= unpack_row_old(rli, table, m_width, m_after_image, + error= unpack_row_old(const_cast(rli), + table, m_width, m_after_image, row_start, &m_cols, row_end, &m_master_reclength, table->write_set, PRE_GA_UPDATE_ROWS_EVENT); diff --git a/sql/log_event_old.h b/sql/log_event_old.h index aad0f6cc6cd..87b11aea3a3 100644 --- a/sql/log_event_old.h +++ b/sql/log_event_old.h @@ -1,3 +1,18 @@ +/* Copyright 2007 MySQL AB. All rights reserved. + + 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; version 2 of the License. + + 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 LOG_EVENT_OLD_H #define LOG_EVENT_OLD_H @@ -15,6 +30,13 @@ public: TYPE_CODE = PRE_GA_WRITE_ROWS_EVENT }; +#if !defined(MYSQL_CLIENT) + Write_rows_log_event_old(THD *thd, TABLE *table, ulong table_id, + MY_BITMAP const *cols, bool is_transactional) + : Write_rows_log_event(thd, table, table_id, cols, is_transactional) + { + } +#endif #if defined(HAVE_REPLICATION) Write_rows_log_event_old(const char *buf, uint event_len, const Format_description_log_event *descr) @@ -27,7 +49,7 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, + virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, char const *row_start, char const **row_end); #endif }; @@ -42,6 +64,13 @@ public: TYPE_CODE = PRE_GA_UPDATE_ROWS_EVENT }; +#if !defined(MYSQL_CLIENT) + Update_rows_log_event_old(THD *thd, TABLE *table, ulong table_id, + MY_BITMAP const *cols, bool is_transactional) + : Update_rows_log_event(thd, table, table_id, cols, is_transactional) + { + } +#endif #if defined(HAVE_REPLICATION) Update_rows_log_event_old(const char *buf, uint event_len, const Format_description_log_event *descr) @@ -54,7 +83,7 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, + virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, char const *row_start, char const **row_end); #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ }; @@ -69,6 +98,13 @@ public: TYPE_CODE = PRE_GA_DELETE_ROWS_EVENT }; +#if !defined(MYSQL_CLIENT) + Delete_rows_log_event_old(THD *thd, TABLE *table, ulong table_id, + MY_BITMAP const *cols, bool is_transactional) + : Delete_rows_log_event(thd, table, table_id, cols, is_transactional) + { + } +#endif #if defined(HAVE_REPLICATION) Delete_rows_log_event_old(const char *buf, uint event_len, const Format_description_log_event *descr) @@ -81,7 +117,7 @@ private: virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, + virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*, char const *row_start, char const **row_end); #endif }; diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc new file mode 100644 index 00000000000..414572027b6 --- /dev/null +++ b/sql/rpl_record.cc @@ -0,0 +1,278 @@ +/* Copyright 2007 MySQL AB. All rights reserved. + + 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; version 2 of the License. + + 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 "mysql_priv.h" +#include "rpl_record.h" +#include "slave.h" // Need to pull in slave_print_msg + +/** + Pack a record of data for a table into a format suitable for + transfer via the binary log. + + The format for a row in transfer with N fields is the following: + + ceil(N/8) null bytes: + One null bit for every column *regardless of whether it can be + null or not*. This simplifies the decoding. Observe that the + number of null bits is equal to the number of set bits in the + @c cols bitmap. The number of null bytes is the smallest number + of bytes necessary to store the null bits. + + Padding bits are 1. + + N packets: + Each field is stored in packed format. + + + @param table Table describing the format of the record + + @param cols Bitmap with a set bit for each column that should + be stored in the row + + @param row_data Pointer to memory where row will be written + + @param record Pointer to record that should be packed. It is + assumed that the pointer refers to either @c + record[0] or @c record[1], but no such check is + made since the code does not rely on that. + + @return The number of bytes written at @c row_data. + */ +my_size_t +pack_row(TABLE *table, MY_BITMAP const* cols, + byte *const row_data, const byte *record) +{ + Field **p_field= table->field, *field; + int const null_byte_count= (bitmap_bits_set(cols) + 7) / 8; + byte *pack_ptr = row_data + null_byte_count; + byte *null_ptr = row_data; + my_ptrdiff_t const rec_offset= record - table->record[0]; + my_ptrdiff_t const def_offset= table->s->default_values - table->record[0]; + + /* + We write the null bits and the packed records using one pass + through all the fields. The null bytes are written little-endian, + i.e., the first fields are in the first byte. + */ + unsigned int null_bits= (1U << 8) - 1; + // Mask to mask out the correct but among the null bits + unsigned int null_mask= 1U; + for ( ; (field= *p_field) ; p_field++) + { + DBUG_PRINT("debug", ("null_mask=%d; null_ptr=%p; row_data=%p; null_byte_count=%d", + null_mask, null_ptr, row_data, null_byte_count)); + if (bitmap_is_set(cols, p_field - table->field)) + { + my_ptrdiff_t offset; + if (field->is_null(rec_offset)) + { + offset= def_offset; + null_bits |= null_mask; + } + else + { + offset= rec_offset; + null_bits &= ~null_mask; + + /* + We only store the data of the field if it is non-null + */ + pack_ptr= (byte*)field->pack((char *) pack_ptr, field->ptr + offset); + } + + null_mask <<= 1; + if ((null_mask & 0xFF) == 0) + { + DBUG_ASSERT(null_ptr < row_data + null_byte_count); + null_mask = 1U; + *null_ptr++ = null_bits; + null_bits= (1U << 8) - 1; + } + } + } + + /* + Write the last (partial) byte, if there is one + */ + if ((null_mask & 0xFF) > 1) + { + DBUG_ASSERT(null_ptr < row_data + null_byte_count); + *null_ptr++ = null_bits; + } + + /* + The null pointer should now point to the first byte of the + packed data. If it doesn't, something is very wrong. + */ + DBUG_ASSERT(null_ptr == row_data + null_byte_count); + + return static_cast(pack_ptr - row_data); +} + + +/** + Unpack a row into @c table->record[0]. + + The function will always unpack into the @c table->record[0] + record. This is because there are too many dependencies on where + the various member functions of Field and subclasses expect to + write. + + The row is assumed to only consist of the fields for which the + bitset represented by @c arr and @c bits; the other parts of the + record are left alone. + + At most @c colcnt columns are read: if the table is larger than + that, the remaining fields are not filled in. + + @param rli Relay log info + @param table Table to unpack into + @param colcnt Number of columns to read from record + @param row Packed row data + @param cols Pointer to columns data to fill in + @param row_end Pointer to variable that will hold the value of the + one-after-end position for the row + @param master_reclength + Pointer to variable that will be set to the length of the + record on the master side + @param rw_set Pointer to bitmap that holds either the read_set or the + write_set of the table + + + @retval 0 No error + + @retval ER_NO_DEFAULT_FOR_FIELD + Returned if one of the fields existing on the slave but not on the + master does not have a default value (and isn't nullable) + + */ +#ifdef HAVE_REPLICATION +int +unpack_row(RELAY_LOG_INFO const *rli, + TABLE *table, uint const colcnt, + char const *const row_data, MY_BITMAP const *cols, + char const **const row_end, ulong *const master_reclength, + MY_BITMAP* const rw_set, Log_event_type const event_type) +{ + DBUG_ENTER("unpack_row"); + DBUG_ASSERT(row_data); + my_size_t const master_null_byte_count= (bitmap_bits_set(cols) + 7) / 8; + int error= 0; + + char const *null_ptr= row_data; + char const *pack_ptr= row_data + master_null_byte_count; + + bitmap_clear_all(rw_set); + + empty_record(table); + + Field **const begin_ptr = table->field; + Field **field_ptr; + Field **const end_ptr= begin_ptr + colcnt; + + DBUG_ASSERT(null_ptr < row_data + master_null_byte_count); + + // Mask to mask out the correct bit among the null bits + unsigned int null_mask= 1U; + // The "current" null bits + unsigned int null_bits= *null_ptr++; + for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr) + { + Field *const f= *field_ptr; + + /* + No need to bother about columns that does not exist: they have + gotten default values when being emptied above. + */ + if (bitmap_is_set(cols, field_ptr - begin_ptr)) + { + if ((null_mask & 0xFF) == 0) + { + DBUG_ASSERT(null_ptr < row_data + master_null_byte_count); + null_mask= 1U; + null_bits= *null_ptr++; + } + + DBUG_ASSERT(null_mask & 0xFF); // One of the 8 LSB should be set + + /* Field...::unpack() cannot return 0 */ + DBUG_ASSERT(pack_ptr != NULL); + + if ((null_bits & null_mask) && f->maybe_null()) + f->set_null(); + else + { + f->set_notnull(); + + /* + We only unpack the field if it was non-null + */ + pack_ptr= f->unpack(f->ptr, pack_ptr); + } + + bitmap_set_bit(rw_set, f->field_index); + null_mask <<= 1; + } + } + + /* + We should now have read all the null bytes, otherwise something is + really wrong. + */ + DBUG_ASSERT(null_ptr == row_data + master_null_byte_count); + + *row_end = pack_ptr; + if (master_reclength) + { + if (*field_ptr) + *master_reclength = (*field_ptr)->ptr - (char*) table->record[0]; + else + *master_reclength = table->s->reclength; + } + + /* + Set properties for remaining columns, if there are any. We let the + corresponding bit in the write_set be set, to write the value if + it was not there already. We iterate over all remaining columns, + even if there were an error, to get as many error messages as + possible. We are still able to return a pointer to the next row, + so redo that. + + This generation of error messages is only relevant when inserting + new rows. + */ + for ( ; *field_ptr ; ++field_ptr) + { + uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; + Field *const f= *field_ptr; + + if (event_type == WRITE_ROWS_EVENT && + ((*field_ptr)->flags & mask) == mask) + { + slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, + "Field `%s` of table `%s`.`%s` " + "has no default value and cannot be NULL", + (*field_ptr)->field_name, table->s->db.str, + table->s->table_name.str); + error = ER_NO_DEFAULT_FOR_FIELD; + } + else + f->set_default(); + } + + DBUG_RETURN(error); +} + +#endif // HAVE_REPLICATION diff --git a/sql/rpl_record.h b/sql/rpl_record.h new file mode 100644 index 00000000000..7ff1bbc7c8e --- /dev/null +++ b/sql/rpl_record.h @@ -0,0 +1,33 @@ +/* Copyright 2007 MySQL AB. All rights reserved. + + 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; version 2 of the License. + + 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 RPL_RECORD_H +#define RPL_RECORD_H + +#if !defined(MYSQL_CLIENT) +my_size_t pack_row(TABLE* table, MY_BITMAP const* cols, + byte *row_data, const byte *data); +#endif + +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) +int unpack_row(RELAY_LOG_INFO const *rli, + TABLE *table, uint const colcnt, + char const *const row_data, MY_BITMAP const *cols, + char const **const row_end, ulong *const master_reclength, + MY_BITMAP* const rw_set, + Log_event_type const event_type); +#endif + +#endif diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index ccee1b2211b..73e2f4e7344 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -3,7 +3,7 @@ #include "rpl_record_old.h" my_size_t -pack_row_old(THD *thd, TABLE *table, MY_BITMAP const* cols, +pack_row_old(TABLE *table, MY_BITMAP const* cols, byte *row_data, const byte *record) { Field **p_field= table->field, *field; diff --git a/sql/rpl_record_old.h b/sql/rpl_record_old.h index 7249d98c7b1..6ddb01de07c 100644 --- a/sql/rpl_record_old.h +++ b/sql/rpl_record_old.h @@ -1,8 +1,23 @@ +/* Copyright 2007 MySQL AB. All rights reserved. + + 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; version 2 of the License. + + 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 RPL_RECORD_OLD_H #define RPL_RECORD_OLD_H #ifndef MYSQL_CLIENT -my_size_t pack_row_old(THD *thd, TABLE *table, MY_BITMAP const* cols, +my_size_t pack_row_old(TABLE *table, MY_BITMAP const* cols, byte *row_data, const byte *record); #ifdef HAVE_REPLICATION diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 72e307d828b..57de3bc6838 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1088,7 +1088,7 @@ bool st_relay_log_info::cached_charset_compare(char *charset) const void st_relay_log_info::stmt_done(my_off_t const event_master_log_pos, - time_t event_creation_time) + my_time_t event_creation_time) { clear_flag(IN_STMT); diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 234d24a9569..7727c35ae53 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -184,7 +184,7 @@ typedef struct st_relay_log_info ulonglong future_group_master_log_pos; #endif - time_t last_master_timestamp; + my_time_t last_master_timestamp; void clear_slave_error(); void clear_until_condition(); @@ -345,7 +345,7 @@ typedef struct st_relay_log_info the Seconds_behind_master field. */ void stmt_done(my_off_t event_log_pos, - time_t event_creation_time); + my_time_t event_creation_time); /** diff --git a/sql/slave.cc b/sql/slave.cc index 4282139a453..4f20235b78d 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1334,8 +1334,7 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && mi->rli.slave_running) { - long time_diff= ((long)((time_t)time((time_t*) 0) - - mi->rli.last_master_timestamp) + long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp) - mi->clock_diff_with_master); /* Apparently on some systems time_diff can be <0. Here are possible @@ -3461,7 +3460,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) the events have old timestamps (then you get "many", 0, "many"). Transient phases like this can't really be fixed. */ - time_t save_timestamp= rli->last_master_timestamp; + my_time_t save_timestamp= rli->last_master_timestamp; rli->last_master_timestamp= 0; DBUG_ASSERT(rli->relay_log.get_open_count() == diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6ba9d0bc8f1..8ea6bb4de2a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -27,6 +27,7 @@ #include "mysql_priv.h" #include "rpl_rli.h" +#include "rpl_record.h" #include #include "log_event.h" #include @@ -2554,112 +2555,6 @@ my_size_t THD::max_row_length_blob(TABLE *table, const byte *data) const } -/* - Pack a record of data for a table into a format suitable for - transfer via the binary log. - - SYNOPSIS - THD::pack_row() - table Table describing the format of the record - cols Bitmap with a set bit for each column that should be - stored in the row - row_data Pointer to memory where row will be written - record Pointer to record that should be packed. It is assumed - that the pointer refers to either record[0] or - record[1], but no such check is made since the code does - not rely on that. - - DESCRIPTION - - The format for a row in transfer with N fields is the following: - - ceil(N/8) null bytes: - One null bit for every column *regardless of whether it can be - null or not*. This simplifies the decoding. Observe that the - number of null bits is equal to the number of set bits in the - 'cols' bitmap. The number of null bytes is the smallest number - of bytes necessary to store the null bits. - - Padding bits are 1. - - N packets: - Each field is stored in packed format. - - - RETURN VALUE - - The number of bytes written at 'row_data'. - */ -my_size_t -THD::pack_row(TABLE *table, MY_BITMAP const* cols, - byte *const row_data, const byte *record) const -{ - Field **p_field= table->field, *field; - int const null_byte_count= (bitmap_bits_set(cols) + 7) / 8; - byte *pack_ptr = row_data + null_byte_count; - byte *null_ptr = row_data; - my_ptrdiff_t const rec_offset= record - table->record[0]; - my_ptrdiff_t const def_offset= table->s->default_values - table->record[0]; - - /* - We write the null bits and the packed records using one pass - through all the fields. The null bytes are written little-endian, - i.e., the first fields are in the first byte. - */ - unsigned int null_bits= (1U << 8) - 1; - // Mask to mask out the correct but among the null bits - unsigned int null_mask= 1U; - for ( ; (field= *p_field) ; p_field++) - { - DBUG_PRINT("debug", ("null_mask=%d; null_ptr=%p; row_data=%p; null_byte_count=%d", - null_mask, null_ptr, row_data, null_byte_count)); - if (bitmap_is_set(cols, p_field - table->field)) - { - my_ptrdiff_t offset; - if (field->is_null(rec_offset)) - { - offset= def_offset; - null_bits |= null_mask; - } - else - { - offset= rec_offset; - null_bits &= ~null_mask; - - /* - We only store the data of the field if it is non-null - */ - pack_ptr= (byte*)field->pack((char *) pack_ptr, field->ptr + offset); - } - - null_mask <<= 1; - if ((null_mask & 0xFF) == 0) - { - DBUG_ASSERT(null_ptr < row_data + null_byte_count); - null_mask = 1U; - *null_ptr++ = null_bits; - null_bits= (1U << 8) - 1; - } - } - } - - /* - Write the last (partial) byte, if there is one - */ - if ((null_mask & 0xFF) > 1) - { - DBUG_ASSERT(null_ptr < row_data + null_byte_count); - *null_ptr++ = null_bits; - } - - /* - The null pointer should now point to the first byte of the - packed data. If it doesn't, something is very wrong. - */ - DBUG_ASSERT(null_ptr == row_data + null_byte_count); - - return static_cast(pack_ptr - row_data); -} namespace { @@ -2830,9 +2725,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, byte *before_row= row_data.slot(0); byte *after_row= row_data.slot(1); - my_size_t const before_size= pack_row(table, cols, before_row, + my_size_t const before_size= pack_row(table, cols, before_row, before_record); - my_size_t const after_size= pack_row(table, cols, after_row, + my_size_t const after_size= pack_row(table, cols, after_row, after_record); /* diff --git a/sql/sql_class.h b/sql/sql_class.h index dc420976572..ad08fc194c9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1043,9 +1043,6 @@ public: return (length+max_row_length_blob(table,data)); } - my_size_t pack_row(TABLE* table, MY_BITMAP const* cols, byte *row_data, - const byte *data) const; - int binlog_flush_pending_rows_event(bool stmt_end); void binlog_delete_pending_rows_event();