Drizzled Public API Documentation

transaction_file_reader.cc
Go to the documentation of this file.
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 David Shrewsbury <shrewsbury.dave@gmail.com>
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; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00025 #include <config.h>
00026 #include "transaction_file_reader.h"
00027 #include <cstdlib>
00028 #include <cstdio>
00029 #include <cerrno>
00030 #include <fcntl.h>
00031 #include <boost/lexical_cast.hpp>
00032 #include <google/protobuf/io/coded_stream.h>
00033 #include <drizzled/definitions.h>
00034 #include <drizzled/algorithm/crc32.h>
00035 #include <drizzled/replication_services.h>
00036 #include <drizzled/gettext.h>
00037 #include <drizzled/util/convert.h>
00038 
00039 using namespace std;
00040 using namespace drizzled;
00041 using namespace google;
00042 
00043 
00044 TransactionFileReader::TransactionFileReader()
00045 {
00046   raw_input= NULL;
00047   buffer= NULL;
00048   temp_buffer= NULL;
00049   previous_length= 0;
00050   file= -1;
00051 }
00052 
00053 TransactionFileReader::~TransactionFileReader()
00054 {
00055   delete raw_input;
00056   close(file);
00057   free(buffer);
00058 }
00059 
00060 bool TransactionFileReader::openFile(const string &filename, int start_pos)
00061 {
00062   file= open(filename.c_str(), O_RDONLY);
00063   if (file == -1)
00064   {
00065     error= _("Cannot open file: ") + filename;
00066     return false;
00067   }
00068 
00069   raw_input= new protobuf::io::FileInputStream(file);
00070 
00071   if (start_pos > 0)
00072   {
00073     if (not raw_input->Skip(start_pos))
00074     {
00075       error= _("Could not skip to position ")
00076                + boost::lexical_cast<string>(start_pos);
00077       return false;
00078     }
00079   }
00080 
00081   return true;
00082 }
00083 
00084 bool TransactionFileReader::getNextTransaction(message::Transaction &transaction,
00085                                               uint32_t *checksum)
00086 {
00087   uint32_t message_type= 0;
00088   uint32_t length= 0;
00089   bool result= true;
00090 
00091   /*
00092    * Odd thing to note about using CodedInputStream: This class wasn't
00093    * intended to read large amounts of GPB messages. It has an upper
00094    * limit on the number of bytes it will read (see Protobuf docs for
00095    * this class for more info). A warning will be produced as you
00096    * get close to this limit. Since this is a pretty lightweight class,
00097    * we should be able to simply create a new one for each message we
00098    * want to read.
00099    */
00100   protobuf::io::CodedInputStream coded_input(raw_input);
00101 
00102   /* Read in the type and length of the command */
00103   if (not coded_input.ReadLittleEndian32(&message_type) ||
00104       not coded_input.ReadLittleEndian32(&length))
00105   {
00106     error= "EOF";
00107     return false;
00108   }
00109 
00110   if (message_type != ReplicationServices::TRANSACTION)
00111   {
00112     error= _("Found a non-transaction message in log. Currently, not supported.\n");
00113     return false;
00114   }
00115 
00116   if (length > INT_MAX)
00117   {
00118     error= _("Attempted to read record bigger than INT_MAX\n");
00119     return false;
00120   }
00121 
00122   if (buffer == NULL)
00123   {
00124     /*
00125      * First time around...just malloc the length.  This block gets rid
00126      * of a GCC warning about uninitialized temp_buffer.
00127      */
00128     temp_buffer= (char *) malloc(static_cast<size_t>(length));
00129   }
00130   /* No need to allocate if we have a buffer big enough... */
00131   else if (length > previous_length)
00132   {
00133     temp_buffer= (char *) realloc(buffer, static_cast<size_t>(length));
00134   }
00135 
00136   if (temp_buffer == NULL)
00137   {
00138     error= _("Memory allocation failure trying to allocate ")
00139             + boost::lexical_cast<string>(length)
00140             + _(" bytes\n");
00141     return false;
00142   }
00143   else
00144     buffer= temp_buffer;
00145 
00146   /* Read the Command */
00147   result= coded_input.ReadRaw(buffer, (int) length);
00148 
00149   if (result == false)
00150   {
00151     char errmsg[STRERROR_MAX];
00152     strerror_r(errno, errmsg, sizeof(errmsg));
00153     error= _("Could not read transaction message.\n");
00154     error += _("GPB ERROR: ") + string(errmsg) + "\n";
00155     string hexdump;
00156     hexdump.reserve(length * 4);
00157     bytesToHexdumpFormat(hexdump,
00158                          reinterpret_cast<const unsigned char *>(buffer),
00159                          length);
00160     error += _("HEXDUMP:\n\n") + hexdump;
00161     return false;
00162   }
00163 
00164   result= transaction.ParseFromArray(buffer, static_cast<int32_t>(length));
00165 
00166   if (result == false)
00167   {
00168     error= _("Unable to parse command. Got error: ")
00169              + transaction.InitializationErrorString();
00170     if (buffer != NULL)
00171     {
00172       string hexdump;
00173       hexdump.reserve(length * 4);
00174       bytesToHexdumpFormat(hexdump,
00175                            reinterpret_cast<const unsigned char *>(buffer),
00176                            length);
00177       error += _("\nHEXDUMP:\n\n") + hexdump + "\n";
00178     }
00179     return false;
00180   }
00181 
00182   /* Read 4 byte checksum */
00183   coded_input.ReadLittleEndian32(checksum);
00184 
00185   previous_length= length;
00186 
00187   return true;
00188 }
00189 
00190 uint32_t TransactionFileReader::checksumLastReadTransaction()
00191 {
00192   return drizzled::algorithm::crc32(buffer, static_cast<size_t>(previous_length));
00193 }