Drizzled Public API Documentation

create_replication.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Brian Aker
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <drizzled/message.h>
00024 #include "read_replication.h"
00025 #include "create_replication.h"
00026 
00027 #ifdef UNIV_NONINL
00028 #include "dict0crea.ic"
00029 #endif
00030 
00031 #include "btr0pcur.h"
00032 #include "btr0btr.h"
00033 #include "page0page.h"
00034 #include "mach0data.h"
00035 #include "dict0boot.h"
00036 #include "dict0dict.h"
00037 #include "que0que.h"
00038 #include "row0ins.h"
00039 #include "row0mysql.h"
00040 #include "pars0pars.h"
00041 #include "trx0roll.h"
00042 #include "usr0sess.h"
00043 #include "ut0vec.h"
00044 #include "row0merge.h"
00045 #include "row0mysql.h"
00046 
00047 UNIV_INTERN ulint dict_create_sys_replication_log(void)
00048 {
00049   dict_table_t* table1;
00050   ulint error;
00051   trx_t *trx;
00052 
00053   mutex_enter(&(dict_sys->mutex));
00054 
00055   table1 = dict_table_get_low("SYS_REPLICATION_LOG");
00056 
00057   trx_sys_read_commit_id();
00058 
00059   if (table1) 
00060   {
00061     mutex_exit(&(dict_sys->mutex));
00062 
00063     return(DB_SUCCESS);
00064   }
00065 
00066   mutex_exit(&(dict_sys->mutex));
00067 
00068   trx= trx_allocate_for_mysql();
00069 
00070   trx->op_info= "creating replication sys table";
00071 
00072   row_mysql_lock_data_dictionary(trx);
00073 
00074   pars_info_t *info= pars_info_create();
00075 
00076 
00077   error = que_eval_sql(info,
00078                        "PROCEDURE CREATE_SYS_REPLICATION_LOG_PROC () IS\n"
00079                        "BEGIN\n"
00080                        "CREATE TABLE SYS_REPLICATION_LOG(ID INT(8), SEGID INT, COMMIT_ID INT(8), END_TIMESTAMP INT(8), ORIGINATING_SERVER_UUID BLOB, ORIGINATING_COMMIT_ID INT(8), MESSAGE_LEN INT, MESSAGE BLOB);\n" 
00081                        "CREATE UNIQUE CLUSTERED INDEX PRIMARY ON SYS_REPLICATION_LOG (ID, SEGID);\n"
00082                        "CREATE INDEX COMMIT_IDX ON SYS_REPLICATION_LOG (COMMIT_ID, ID);\n"
00083                        "END;\n"
00084                        , FALSE, trx);
00085 
00086 
00087 
00088   if (error != DB_SUCCESS)
00089   {
00090     fprintf(stderr, "InnoDB: error %lu in creation.\n", (ulong) error);
00091 
00092     ut_a(error == DB_OUT_OF_FILE_SPACE || error == DB_TOO_MANY_CONCURRENT_TRXS);
00093 
00094     fprintf(stderr,
00095             "InnoDB: creation failed\n"
00096             "InnoDB: tablespace is full\n"
00097             "InnoDB: dropping incompletely created SYS_REPLICATION_LOG table.\n");
00098 
00099     row_drop_table_for_mysql("SYS_REPLICATION_LOG", trx, TRUE);
00100 
00101     error = DB_MUST_GET_MORE_FILE_SPACE;
00102   }
00103 
00104   trx_commit_for_mysql(trx);
00105 
00106   row_mysql_unlock_data_dictionary(trx);
00107 
00108   trx_free_for_mysql(trx);
00109 
00110   return(error);
00111 }
00112 
00113 UNIV_INTERN int read_replication_log_table_message(const char* table_name, drizzled::message::Table *table_message)
00114 {
00115   std::string search_string(table_name);
00116   boost::algorithm::to_lower(search_string);
00117 
00118   if (search_string.compare("sys_replication_log") != 0)
00119     return -1;
00120 
00121   drizzled::message::Engine *engine= table_message->mutable_engine();
00122   engine->set_name("InnoDB");
00123   table_message->set_name("SYS_REPLICATION_LOG");
00124   table_message->set_schema("DATA_DICTIONARY");
00125   table_message->set_type(drizzled::message::Table::STANDARD);
00126   table_message->set_creation_timestamp(0);
00127   table_message->set_update_timestamp(0);
00128 
00129   drizzled::message::Table::TableOptions *options= table_message->mutable_options();
00130   options->set_collation_id(drizzled::my_charset_bin.number);
00131   options->set_collation(drizzled::my_charset_bin.name);
00132   drizzled::message::set_is_replicated(*table_message, false);
00133 
00134   drizzled::message::Table::Field *field= table_message->add_field();
00135   field->set_name("ID");
00136   field->set_type(drizzled::message::Table::Field::BIGINT);
00137 
00138   field= table_message->add_field();
00139   field->set_name("SEGID");
00140   field->set_type(drizzled::message::Table::Field::INTEGER);
00141 
00142   field= table_message->add_field();
00143   field->set_name("COMMIT_ID");
00144   field->set_type(drizzled::message::Table::Field::BIGINT);
00145 
00146   field= table_message->add_field();
00147   field->set_name("END_TIMESTAMP");
00148   field->set_type(drizzled::message::Table::Field::BIGINT);
00149 
00150   field= table_message->add_field();
00151   field->set_name("ORIGINATING_SERVER_UUID");
00152   field->set_type(drizzled::message::Table::Field::BLOB);
00153 
00154   field= table_message->add_field();
00155   field->set_name("ORIGINATING_COMMIT_ID");
00156   field->set_type(drizzled::message::Table::Field::BIGINT);
00157 
00158   field= table_message->add_field();
00159   field->set_name("MESSAGE_LEN");
00160   field->set_type(drizzled::message::Table::Field::INTEGER);
00161 
00162   field= table_message->add_field();
00163   field->set_name("MESSAGE");
00164   field->set_type(drizzled::message::Table::Field::BLOB);
00165   drizzled::message::Table::Field::StringFieldOptions *stropt= field->mutable_string_options();
00166   stropt->set_collation_id(drizzled::my_charset_bin.number);
00167   stropt->set_collation(drizzled::my_charset_bin.name);
00168 
00169   drizzled::message::Table::Index *index= table_message->add_indexes();
00170   index->set_name("PRIMARY");
00171   index->set_is_primary(true);
00172   index->set_is_unique(true);
00173   index->set_type(drizzled::message::Table::Index::BTREE);
00174   index->set_key_length(12);
00175   drizzled::message::Table::Index::IndexPart *part= index->add_index_part();
00176   part->set_fieldnr(0);
00177   part->set_compare_length(8);
00178   part= index->add_index_part();
00179   part->set_fieldnr(1);
00180   part->set_compare_length(4);
00181 
00182   index= table_message->add_indexes();
00183   index->set_name("COMMIT_IDX");
00184   index->set_is_primary(false);
00185   index->set_is_unique(false);
00186   index->set_type(drizzled::message::Table::Index::BTREE);
00187   index->set_key_length(16);
00188   part= index->add_index_part();
00189   part->set_fieldnr(2);
00190   part->set_compare_length(8);
00191   part= index->add_index_part();
00192   part->set_fieldnr(0);
00193   part->set_compare_length(8);
00194 
00195   return 0;
00196 }
00197 
00198 extern dtuple_t* row_get_prebuilt_insert_row(row_prebuilt_t*  prebuilt);
00199 
00200 ulint insert_replication_message(const char *message, size_t size, 
00201                                  trx_t *trx, uint64_t trx_id, 
00202                                  uint64_t end_timestamp, bool is_end_segment, 
00203                                  uint32_t seg_id, const char *server_uuid,
00204                                  bool use_originating_server_uuid,
00205                                  const char *originating_server_uuid,
00206                                  uint64_t originating_commit_id)
00207 {
00208   ulint error;
00209   row_prebuilt_t* prebuilt; /* For reading rows */
00210   dict_table_t *table;
00211   que_thr_t*  thr;
00212   byte*  data;
00213 
00214   table = dict_table_get("SYS_REPLICATION_LOG",TRUE);
00215 
00216   prebuilt = row_create_prebuilt(table);
00217 
00218   if (prebuilt->trx != trx) 
00219   {
00220     row_update_prebuilt_trx(prebuilt, trx);
00221   }
00222 
00223   /* DDL operations create table/drop table call
00224    * innobase_commit_low() which will commit the trx
00225    * that leaves the operation of committing to the
00226    * log in a new trx. If that is the case we need
00227    * to keep track and commit the trx later in this
00228    * function. 
00229    */ 
00230   bool is_started= true;
00231   if (trx->conc_state == TRX_NOT_STARTED)
00232   {
00233     is_started= false;
00234   }
00235 
00236   dtuple_t* dtuple= row_get_prebuilt_insert_row(prebuilt);
00237   dfield_t *dfield;
00238 
00239   dfield = dtuple_get_nth_field(dtuple, 0);
00240   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 8));
00241   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&trx_id, 8, dict_table_is_comp(prebuilt->table));
00242   dfield_set_data(dfield, data, 8);
00243 
00244   dfield = dtuple_get_nth_field(dtuple, 1);
00245 
00246   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 4));
00247   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&seg_id, 4, dict_table_is_comp(prebuilt->table));
00248   dfield_set_data(dfield, data, 4);
00249   
00250   uint64_t commit_id= 0;
00251   if (is_end_segment)
00252   {
00253     commit_id= trx_sys_commit_id.increment();
00254   } 
00255 
00256   dfield = dtuple_get_nth_field(dtuple, 2);
00257   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 8));
00258   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&commit_id, 8, dict_table_is_comp(prebuilt->table));
00259   dfield_set_data(dfield, data, 8);
00260 
00261   dfield = dtuple_get_nth_field(dtuple, 3);
00262   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 8));
00263   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&end_timestamp, 8, dict_table_is_comp(prebuilt->table));
00264   dfield_set_data(dfield, data, 8);
00265 
00266   if (not use_originating_server_uuid)
00267   {
00268     /* This transaction originated from this server, rather then being
00269        replicated to this server reset the values to reflect that */
00270     originating_server_uuid= server_uuid;
00271     originating_commit_id= commit_id;
00272   }
00273 
00274   dfield = dtuple_get_nth_field(dtuple, 4);
00275   dfield_set_data(dfield, originating_server_uuid, 36);
00276 
00277   dfield = dtuple_get_nth_field(dtuple, 5);
00278   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 8));
00279   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&originating_commit_id, 8, dict_table_is_comp(prebuilt->table));
00280   dfield_set_data(dfield, data, 8);
00281 
00282   dfield = dtuple_get_nth_field(dtuple, 6);
00283   data= static_cast<byte*>(mem_heap_alloc(prebuilt->heap, 4));
00284   row_mysql_store_col_in_innobase_format(dfield, data, TRUE, (byte*)&size, 4, dict_table_is_comp(prebuilt->table));
00285   dfield_set_data(dfield, data, 4);
00286 
00287   dfield = dtuple_get_nth_field(dtuple, 7);
00288   dfield_set_data(dfield, message, size);
00289 
00290   ins_node_t* node    = prebuilt->ins_node;
00291 
00292   thr = que_fork_get_first_thr(prebuilt->ins_graph);
00293 
00294   if (prebuilt->sql_stat_start) {
00295     node->state = INS_NODE_SET_IX_LOCK;
00296     prebuilt->sql_stat_start = FALSE;
00297   } else {
00298     node->state = INS_NODE_ALLOC_ROW_ID;
00299   }
00300 
00301   que_thr_move_to_run_state_for_mysql(thr, trx);
00302 
00303 //run_again:
00304   thr->run_node = node;
00305   thr->prev_node = node;
00306 
00307   row_ins_step(thr);
00308 
00309   error = trx->error_state;
00310 
00311   que_thr_stop_for_mysql_no_error(thr, trx);
00312   row_prebuilt_free(prebuilt, FALSE);
00313 
00314   if (! is_started)
00315   {
00316     trx_commit_for_mysql(trx);
00317   }
00318 
00319   return error;
00320 }
00321 
00322 UNIV_INTERN read_replication_state_st *replication_read_init(void)
00323 {
00324   read_replication_state_st *state= new read_replication_state_st;
00325 
00326   mutex_enter(&(dict_sys->mutex));
00327 
00328   mtr_start(&state->mtr);
00329   state->sys_tables= dict_table_get_low("SYS_REPLICATION_LOG");
00330   state->sys_index= UT_LIST_GET_FIRST(state->sys_tables->indexes);
00331 
00332   mutex_exit(&(dict_sys->mutex));
00333 
00334   btr_pcur_open_at_index_side(TRUE, state->sys_index, BTR_SEARCH_LEAF, &state->pcur, TRUE, &state->mtr);
00335 
00336   return state;
00337 }
00338 
00339 UNIV_INTERN void replication_read_deinit(struct read_replication_state_st *state)
00340 {
00341   btr_pcur_close(&state->pcur);
00342   mtr_commit(&state->mtr);
00343   delete state;
00344 }
00345 
00346 UNIV_INTERN struct read_replication_return_st replication_read_next(struct read_replication_state_st *state)
00347 {
00348   struct read_replication_return_st ret;
00349   const rec_t *rec;
00350 
00351   btr_pcur_move_to_next_user_rec(&state->pcur, &state->mtr);
00352 
00353   rec= btr_pcur_get_rec(&state->pcur);
00354 
00355   while (btr_pcur_is_on_user_rec(&state->pcur))
00356   {
00357     const byte* field;
00358     ulint len;
00359 
00360     // Is the row deleted? If so go fetch the next
00361     if (rec_get_deleted_flag(rec, 0))
00362       continue;
00363 
00364     // Store transaction id
00365     field = rec_get_nth_field_old(rec, 0, &len);
00366     byte idbyte[8];
00367     convert_to_mysql_format(idbyte, field, 8);
00368     ret.id= *(uint64_t *)idbyte;
00369 
00370     // Store segment id
00371     field = rec_get_nth_field_old(rec, 1, &len);
00372     byte segbyte[4];
00373     convert_to_mysql_format(segbyte, field, 4);
00374     ret.seg_id= *(uint32_t *)segbyte;
00375 
00376     field = rec_get_nth_field_old(rec, 4, &len);
00377     byte commitbyte[8];
00378     convert_to_mysql_format(commitbyte, field, 8);
00379     ret.commit_id= *(uint64_t *)commitbyte;
00380 
00381     field = rec_get_nth_field_old(rec, 5, &len);
00382     byte timestampbyte[8];
00383     convert_to_mysql_format(timestampbyte, field, 8);
00384     ret.end_timestamp= *(uint64_t *)timestampbyte;
00385 
00386     field = rec_get_nth_field_old(rec, 6, &len);
00387     ret.originating_server_uuid= (char *)field;
00388 
00389     field = rec_get_nth_field_old(rec, 7, &len);
00390     byte originatingcommitbyte[8];
00391     convert_to_mysql_format(originatingcommitbyte, field, 8);
00392     ret.originating_commit_id= *(uint64_t *)originatingcommitbyte;
00393 
00394     // Handler message
00395     field = rec_get_nth_field_old(rec, 9, &len);
00396     ret.message= (char *)field;
00397     ret.message_length= len;
00398 
00399     // @todo double check that "field" will continue to be value past this
00400     // point.
00401     btr_pcur_store_position(&state->pcur, &state->mtr);
00402     mtr_commit(&state->mtr);
00403 
00404     mtr_start(&state->mtr);
00405 
00406     btr_pcur_restore_position(BTR_SEARCH_LEAF, &state->pcur, &state->mtr);
00407 
00408     return ret;
00409   }
00410 
00411   /* end of index */
00412   memset(&ret, 0, sizeof(ret));
00413 
00414   return ret;
00415 }
00416 
00417 UNIV_INTERN void convert_to_mysql_format(byte* out, const byte* in, int len)
00418 {
00419   byte *ptr;
00420   ptr = out + len;
00421 
00422   for (;;) {
00423     ptr--;
00424     *ptr = *in;
00425     if (ptr == out) {
00426       break;
00427     }
00428     in++;
00429   }
00430 
00431   out[len - 1] = (byte) (out[len - 1] ^ 128);
00432 
00433 }