Drizzled Public API Documentation

mf_iocache.cc
00001 /* Copyright (C) 2000 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /*
00017   Cashing of files with only does (sequential) read or writes of fixed-
00018   length records. A read isn't allowed to go over file-length. A read is ok
00019   if it ends at file-length and next read can try to read after file-length
00020   (and get a EOF-error).
00021   Possibly use of asyncronic io.
00022   macros for read and writes for faster io.
00023   Used instead of FILE when reading or writing whole files.
00024   This code makes mf_rec_cache obsolete (currently only used by ISAM)
00025   One can change info->pos_in_file to a higher value to skip bytes in file if
00026   also info->read_pos is set to info->read_end.
00027   If called through open_cached_file(), then the temporary file will
00028   only be created if a write exeeds the file buffer or if one calls
00029   my_b_flush_io_cache().
00030 
00031   If one uses SEQ_READ_APPEND, then two buffers are allocated, one for
00032   reading and another for writing.  Reads are first done from disk and
00033   then done from the write buffer.  This is an efficient way to read
00034   from a log file when one is writing to it at the same time.
00035   For this to work, the file has to be opened in append mode!
00036   Note that when one uses SEQ_READ_APPEND, one MUST write using
00037   my_b_append !  This is needed because we need to lock the mutex
00038   every time we access the write buffer.
00039 
00040 TODO:
00041   When one SEQ_READ_APPEND and we are reading and writing at the same time,
00042   each time the write buffer gets full and it's written to disk, we will
00043   always do a disk read to read a part of the buffer from disk to the
00044   read buffer.
00045   This should be fixed so that when we do a my_b_flush_io_cache() and
00046   we have been reading the write buffer, we should transfer the rest of the
00047   write buffer to the read buffer before we start to reuse it.
00048 */
00049 
00050 #include <config.h>
00051 
00052 #include <drizzled/definitions.h>
00053 #include <drizzled/error_t.h>
00054 #include <drizzled/error.h>
00055 #include <drizzled/internal/my_sys.h>
00056 #include <drizzled/internal/m_string.h>
00057 #include <drizzled/drizzled.h>
00058 #include <drizzled/internal/iocache.h>
00059 #include <errno.h>
00060 #include <drizzled/util/test.h>
00061 #include <stdlib.h>
00062 #include <algorithm>
00063 
00064 using namespace std;
00065 
00066 namespace drizzled {
00067 namespace internal {
00068 
00069 static int _my_b_read(io_cache_st *info, unsigned char *Buffer, size_t Count);
00070 static int _my_b_write(io_cache_st *info, const unsigned char *Buffer, size_t Count);
00071 
00076 inline
00077 static void lock_append_buffer(io_cache_st *, int )
00078 {
00079 }
00080 
00085 inline
00086 static void unlock_append_buffer(io_cache_st *, int )
00087 {
00088 }
00089 
00094 inline
00095 static size_t io_round_up(size_t x)
00096 {
00097   return ((x+IO_SIZE-1) & ~(IO_SIZE-1));
00098 }
00099 
00104 inline
00105 static size_t io_round_dn(size_t x)
00106 {
00107   return (x & ~(IO_SIZE-1));
00108 }
00109 
00110 
00121 void io_cache_st::setup_io_cache()
00122 {
00123   /* Ensure that my_b_tell() and my_b_bytes_in_cache works */
00124   if (type == WRITE_CACHE)
00125   {
00126     current_pos= &write_pos;
00127     current_end= &write_end;
00128   }
00129   else
00130   {
00131     current_pos= &read_pos;
00132     current_end= &read_end;
00133   }
00134 }
00135 
00136 
00137 void io_cache_st::init_functions()
00138 {
00139   switch (type) {
00140   case READ_NET:
00141     /*
00142       Must be initialized by the caller. The problem is that
00143       _my_b_net_read has to be defined in sql directory because of
00144       the dependency on THD, and therefore cannot be visible to
00145       programs that link against mysys but know nothing about THD, such
00146       as myisamchk
00147     */
00148     break;
00149   default:
00150     read_function = _my_b_read;
00151     write_function = _my_b_write;
00152   }
00153 
00154   setup_io_cache();
00155 }
00156 
00174 int io_cache_st::init_io_cache(int file_arg, size_t cachesize,
00175                                enum cache_type type_arg, my_off_t seek_offset,
00176                                bool use_async_io, myf cache_myflags)
00177 {
00178   size_t min_cache;
00179   off_t pos;
00180   my_off_t end_of_file_local= ~(my_off_t) 0;
00181 
00182   file= file_arg;
00183   type= TYPE_NOT_SET;     /* Don't set it until mutex are created */
00184   pos_in_file= seek_offset;
00185   pre_close = pre_read = post_read = 0;
00186   arg = 0;
00187   alloced_buffer = 0;
00188   buffer=0;
00189   seek_not_done= 0;
00190 
00191   if (file >= 0)
00192   {
00193     pos= lseek(file, 0, SEEK_CUR);
00194     if ((pos == MY_FILEPOS_ERROR) && (errno == ESPIPE))
00195     {
00196       /*
00197          This kind of object doesn't support seek() or tell(). Don't set a
00198          flag that will make us again try to seek() later and fail.
00199       */
00200       seek_not_done= 0;
00201       /*
00202         Additionally, if we're supposed to start somewhere other than the
00203         the beginning of whatever this file is, then somebody made a bad
00204         assumption.
00205       */
00206       assert(seek_offset == 0);
00207     }
00208     else
00209       seek_not_done= test(seek_offset != (my_off_t)pos);
00210   }
00211 
00212   if (!cachesize && !(cachesize= my_default_record_cache_size))
00213     return 1;       /* No cache requested */
00214   min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
00215   if (type_arg == READ_CACHE)
00216   {           /* Assume file isn't growing */
00217     if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
00218     {
00219       /* Calculate end of file to avoid allocating oversized buffers */
00220       end_of_file_local=lseek(file,0L,SEEK_END);
00221       /* Need to reset seek_not_done now that we just did a seek. */
00222       seek_not_done= end_of_file_local == seek_offset ? 0 : 1;
00223       if (end_of_file_local < seek_offset)
00224   end_of_file_local=seek_offset;
00225       /* Trim cache size if the file is very small */
00226       if ((my_off_t) cachesize > end_of_file_local-seek_offset+IO_SIZE*2-1)
00227       {
00228   cachesize= (size_t) (end_of_file_local-seek_offset)+IO_SIZE*2-1;
00229   use_async_io=0;       /* No need to use async */
00230       }
00231     }
00232   }
00233   cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
00234   if (type_arg != READ_NET && type_arg != WRITE_NET)
00235   {
00236     /* Retry allocating memory in smaller blocks until we get one */
00237     cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));
00238     if (cachesize < min_cache)
00239       cachesize = min_cache;
00240     size_t buffer_block= cachesize;
00241     if (type_arg == READ_CACHE && not global_read_buffer.add(buffer_block))
00242     {
00243       my_error(ER_OUT_OF_GLOBAL_READMEMORY, MYF(ME_ERROR+ME_WAITTANG));
00244       return 2;
00245     }
00246 
00247     buffer= (unsigned char*) malloc(buffer_block);
00248     write_buffer=buffer;
00249     alloced_buffer= true;
00250   }
00251 
00252   read_length=buffer_length=cachesize;
00253   myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
00254   request_pos= read_pos= write_pos = buffer;
00255 
00256   if (type_arg == WRITE_CACHE)
00257     write_end=
00258       buffer+buffer_length- (seek_offset & (IO_SIZE-1));
00259   else
00260     read_end=buffer;    /* Nothing in cache */
00261 
00262   /* End_of_file may be changed by user later */
00263   end_of_file= end_of_file_local;
00264   error= 0;
00265   type= type_arg;
00266   init_functions();
00267   return 0;
00268 }           /* init_io_cache */
00269 
00280 bool io_cache_st::reinit_io_cache(enum cache_type type_arg,
00281                                   my_off_t seek_offset,
00282                                   bool,
00283                                   bool clear_cache)
00284 {
00285   /* One can't do reinit with the following types */
00286   assert(type_arg != READ_NET && type != READ_NET &&
00287         type_arg != WRITE_NET && type != WRITE_NET);
00288 
00289   /* If the whole file is in memory, avoid flushing to disk */
00290   if (! clear_cache &&
00291       seek_offset >= pos_in_file &&
00292       seek_offset <= tell())
00293   {
00294     /* Reuse current buffer without flushing it to disk */
00295     unsigned char *pos;
00296     if (type == WRITE_CACHE && type_arg == READ_CACHE)
00297     {
00298       read_end=write_pos;
00299       end_of_file= tell();
00300       /*
00301         Trigger a new seek only if we have a valid
00302         file handle.
00303       */
00304       seek_not_done= (file != -1);
00305     }
00306     else if (type_arg == WRITE_CACHE)
00307     {
00308       if (type == READ_CACHE)
00309       {
00310   write_end=write_buffer+buffer_length;
00311   seek_not_done=1;
00312       }
00313       end_of_file = ~(my_off_t) 0;
00314     }
00315     pos=request_pos+(seek_offset-pos_in_file);
00316     if (type_arg == WRITE_CACHE)
00317       write_pos=pos;
00318     else
00319       read_pos= pos;
00320   }
00321   else
00322   {
00323     /*
00324       If we change from WRITE_CACHE to READ_CACHE, assume that everything
00325       after the current positions should be ignored
00326     */
00327     if (type == WRITE_CACHE && type_arg == READ_CACHE)
00328       end_of_file= tell();
00329     /* flush cache if we want to reuse it */
00330     if (!clear_cache && flush(1))
00331       return 1;
00332     pos_in_file=seek_offset;
00333     /* Better to do always do a seek */
00334     seek_not_done=1;
00335     request_pos=read_pos=write_pos=buffer;
00336     if (type_arg == READ_CACHE)
00337     {
00338       read_end=buffer;    /* Nothing in cache */
00339     }
00340     else
00341     {
00342       write_end= (buffer + buffer_length - (seek_offset & (IO_SIZE-1)));
00343       end_of_file= ~(my_off_t) 0;
00344     }
00345   }
00346   type= type_arg;
00347   error=0;
00348   init_functions();
00349 
00350   return 0;
00351 } /* reinit_io_cache */
00352 
00373 static int _my_b_read(io_cache_st *info, unsigned char *Buffer, size_t Count)
00374 {
00375   size_t length_local,diff_length,left_length, max_length;
00376   my_off_t pos_in_file_local;
00377 
00378   if ((left_length= (size_t) (info->read_end-info->read_pos)))
00379   {
00380     assert(Count >= left_length); /* User is not using my_b_read() */
00381     memcpy(Buffer,info->read_pos, left_length);
00382     Buffer+=left_length;
00383     Count-=left_length;
00384   }
00385 
00386   /* pos_in_file always point on where info->buffer was read */
00387   pos_in_file_local=info->pos_in_file+ (size_t) (info->read_end - info->buffer);
00388 
00389   /*
00390     Whenever a function which operates on io_cache_st flushes/writes
00391     some part of the io_cache_st to disk it will set the property
00392     "seek_not_done" to indicate this to other functions operating
00393     on the io_cache_st.
00394   */
00395   if (info->seek_not_done)
00396   {
00397     if ((lseek(info->file,pos_in_file_local,SEEK_SET) != MY_FILEPOS_ERROR))
00398     {
00399       /* No error, reset seek_not_done flag. */
00400       info->seek_not_done= 0;
00401     }
00402     else
00403     {
00404       /*
00405         If the seek failed and the error number is ESPIPE, it is because
00406         info->file is a pipe or socket or FIFO.  We never should have tried
00407         to seek on that.  See Bugs#25807 and #22828 for more info.
00408       */
00409       assert(errno != ESPIPE);
00410       info->error= -1;
00411       return 1;
00412     }
00413   }
00414 
00415   diff_length= (size_t) (pos_in_file_local & (IO_SIZE-1));
00416   if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
00417   {         /* Fill first intern buffer */
00418     size_t read_length;
00419     if (info->end_of_file <= pos_in_file_local)
00420     {         /* End of file */
00421       info->error= (int) left_length;
00422       return 1;
00423     }
00424     length_local=(Count & (size_t) ~(IO_SIZE-1))-diff_length;
00425     if ((read_length= my_read(info->file,Buffer, length_local, info->myflags)) != length_local)
00426     {
00427       info->error= (read_length == (size_t) -1 ? -1 :
00428         (int) (read_length+left_length));
00429       return 1;
00430     }
00431     Count-= length_local;
00432     Buffer+= length_local;
00433     pos_in_file_local+= length_local;
00434     left_length+= length_local;
00435     diff_length=0;
00436   }
00437 
00438   max_length= info->read_length-diff_length;
00439   if (info->type != READ_FIFO &&
00440       max_length > (info->end_of_file - pos_in_file_local))
00441     max_length= (size_t) (info->end_of_file - pos_in_file_local);
00442   if (!max_length)
00443   {
00444     if (Count)
00445     {
00446       info->error= static_cast<int>(left_length); /* We only got this many char */
00447       return 1;
00448     }
00449      length_local=0;        /* Didn't read any chars */
00450   }
00451   else if (( length_local= my_read(info->file,info->buffer, max_length,
00452                             info->myflags)) < Count ||
00453       length_local == (size_t) -1)
00454   {
00455     if ( length_local != (size_t) -1)
00456       memcpy(Buffer, info->buffer,  length_local);
00457     info->pos_in_file= pos_in_file_local;
00458     info->error=  length_local == (size_t) -1 ? -1 : (int) ( length_local+left_length);
00459     info->read_pos=info->read_end=info->buffer;
00460     return 1;
00461   }
00462   info->read_pos=info->buffer+Count;
00463   info->read_end=info->buffer+ length_local;
00464   info->pos_in_file=pos_in_file_local;
00465   memcpy(Buffer, info->buffer, Count);
00466   return 0;
00467 }
00468 
00473 int io_cache_st::get()
00474 {
00475   if (read_pos != read_end)
00476     return *read_pos++;
00477 
00478   if (pre_read)
00479     pre_read(this);
00480 
00481   unsigned char buff;
00482   if (read_function(this, &buff, 1))
00483     return my_b_EOF;
00484 
00485   if (post_read)
00486     post_read(this);
00487 
00488   return buff;
00489 }
00490 
00499 int _my_b_write(io_cache_st *info, const unsigned char *Buffer, size_t Count)
00500 {
00501   size_t rest_length,length_local;
00502 
00503   if (info->pos_in_file+info->buffer_length > info->end_of_file)
00504   {
00505     errno=EFBIG;
00506     return info->error = -1;
00507   }
00508 
00509   rest_length= (size_t) (info->write_end - info->write_pos);
00510   memcpy(info->write_pos,Buffer,(size_t) rest_length);
00511   Buffer+=rest_length;
00512   Count-=rest_length;
00513   info->write_pos+=rest_length;
00514 
00515   if (info->flush(1))
00516     return 1;
00517   if (Count >= IO_SIZE)
00518   {         /* Fill first intern buffer */
00519     length_local=Count & (size_t) ~(IO_SIZE-1);
00520     if (info->seek_not_done)
00521     {
00522       /*
00523         Whenever a function which operates on io_cache_st flushes/writes
00524         some part of the io_cache_st to disk it will set the property
00525         "seek_not_done" to indicate this to other functions operating
00526         on the io_cache_st.
00527       */
00528       if (lseek(info->file,info->pos_in_file,SEEK_SET))
00529       {
00530         info->error= -1;
00531         return (1);
00532       }
00533       info->seek_not_done=0;
00534     }
00535     if (my_write(info->file, Buffer, length_local, info->myflags | MY_NABP))
00536       return info->error= -1;
00537 
00538     Count-=length_local;
00539     Buffer+=length_local;
00540     info->pos_in_file+=length_local;
00541   }
00542   memcpy(info->write_pos,Buffer,(size_t) Count);
00543   info->write_pos+=Count;
00544   return 0;
00545 }
00546 
00553 static int my_block_write(io_cache_st *info, const unsigned char *Buffer, size_t Count, my_off_t pos)
00554 {
00555   size_t length_local;
00556   int error=0;
00557 
00558   if (pos < info->pos_in_file)
00559   {
00560     /* Of no overlap, write everything without buffering */
00561     if (pos + Count <= info->pos_in_file)
00562       return (pwrite(info->file, Buffer, Count, pos) == 0);
00563     /* Write the part of the block that is before buffer */
00564     length_local= (uint32_t) (info->pos_in_file - pos);
00565     if (pwrite(info->file, Buffer, length_local, pos) == 0)
00566       info->error= error= -1;
00567     Buffer+=length_local;
00568     pos+=  length_local;
00569     Count-= length_local;
00570   }
00571 
00572   /* Check if we want to write inside the used part of the buffer.*/
00573   length_local= (size_t) (info->write_end - info->buffer);
00574   if (pos < info->pos_in_file + length_local)
00575   {
00576     size_t offset= (size_t) (pos - info->pos_in_file);
00577     length_local-=offset;
00578     if (length_local > Count)
00579       length_local=Count;
00580     memcpy(info->buffer+offset, Buffer, length_local);
00581     Buffer+=length_local;
00582     Count-= length_local;
00583     /* Fix length_local of buffer if the new data was larger */
00584     if (info->buffer+length_local > info->write_pos)
00585       info->write_pos=info->buffer+length_local;
00586     if (!Count)
00587       return (error);
00588   }
00589   /* Write at the end of the current buffer; This is the normal case */
00590   if (_my_b_write(info, Buffer, Count))
00591     error= -1;
00592   return error;
00593 }
00594 
00595 int io_cache_st::block_write(const void* Buffer, size_t Count, my_off_t pos)
00596 {
00597   return my_block_write(this, reinterpret_cast<const unsigned char*>(Buffer), Count, pos);
00598 }
00599 
00604 static int my_b_flush_io_cache(io_cache_st *info, int need_append_buffer_lock)
00605 {
00606   size_t length_local;
00607   bool append_cache= false;
00608   my_off_t pos_in_file_local;
00609 
00610   if (info->type == WRITE_CACHE || append_cache)
00611   {
00612     if (info->file == -1)
00613     {
00614       if (info->real_open_cached_file())
00615   return((info->error= -1));
00616     }
00617     lock_append_buffer(info, need_append_buffer_lock);
00618 
00619     if ((length_local=(size_t) (info->write_pos - info->write_buffer)))
00620     {
00621       pos_in_file_local=info->pos_in_file;
00622       /*
00623   If we have append cache, we always open the file with
00624   O_APPEND which moves the pos to EOF automatically on every write
00625       */
00626       if (!append_cache && info->seek_not_done)
00627       {         /* File touched, do seek */
00628   if (lseek(info->file,pos_in_file_local,SEEK_SET) == MY_FILEPOS_ERROR)
00629   {
00630     unlock_append_buffer(info, need_append_buffer_lock);
00631     return((info->error= -1));
00632   }
00633   if (!append_cache)
00634     info->seek_not_done=0;
00635       }
00636       if (!append_cache)
00637   info->pos_in_file+=length_local;
00638       info->write_end= (info->write_buffer+info->buffer_length-
00639       ((pos_in_file_local+length_local) & (IO_SIZE-1)));
00640 
00641       if (my_write(info->file,info->write_buffer,length_local,
00642        info->myflags | MY_NABP))
00643   info->error= -1;
00644       else
00645   info->error= 0;
00646       if (!append_cache)
00647       {
00648         set_if_bigger(info->end_of_file,(pos_in_file_local+length_local));
00649       }
00650       else
00651       {
00652   info->end_of_file+=(info->write_pos-info->append_read_pos);
00653   my_off_t tell_ret= lseek(info->file, 0, SEEK_CUR);
00654   assert(info->end_of_file == tell_ret);
00655       }
00656 
00657       info->append_read_pos=info->write_pos=info->write_buffer;
00658       unlock_append_buffer(info, need_append_buffer_lock);
00659       return(info->error);
00660     }
00661   }
00662   unlock_append_buffer(info, need_append_buffer_lock);
00663   return 0;
00664 }
00665 
00666 int io_cache_st::flush(int need_append_buffer_lock)
00667 {
00668   return my_b_flush_io_cache(this, need_append_buffer_lock);
00669 }
00670 
00685 int io_cache_st::end_io_cache()
00686 {
00687   int _error=0;
00688 
00689   if (pre_close)
00690   {
00691     (*pre_close)(this);
00692     pre_close= 0;
00693   }
00694   if (alloced_buffer)
00695   {
00696     if (type == READ_CACHE)
00697       global_read_buffer.sub(buffer_length);
00698     alloced_buffer=0;
00699     if (file != -1)     /* File doesn't exist */
00700       _error= my_b_flush_io_cache(this, 1);
00701     free((unsigned char*) buffer);
00702     buffer=read_pos=(unsigned char*) 0;
00703   }
00704 
00705   return _error;
00706 } /* end_io_cache */
00707 
00708 } /* namespace internal */
00709 } /* namespace drizzled */