Drizzled Public API Documentation

table_ms.cc

00001 /* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
00002  *
00003  * PrimeBase Media Stream for MySQL
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
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  * Original author: Paul McCullagh
00020  * Continued development: Barry Leslie
00021  *
00022  * 2007-05-25
00023  *
00024  * H&G2JCtL
00025  *
00026  * Contains the information about a table.
00027  *
00028  */
00029 
00030 #include "cslib/CSConfig.h"
00031 
00032 #include <stdlib.h>
00033 #include <stddef.h>
00034 
00035 #include "defs_ms.h"
00036 
00037 #include "cslib/CSGlobal.h"
00038 #include "cslib/CSLog.h"
00039 #include "cslib/CSPath.h"
00040 #include "cslib/CSStrUtil.h"
00041 
00042 #include "database_ms.h"
00043 #include "open_table_ms.h"
00044 
00045 MSTable::MSTable():
00046 CSSharedRefObject(),
00047 myTableName(NULL),
00048 myTableID(0),
00049 myDatabase(NULL),
00050 iTableFileSize(0),
00051 iTableHeadSize(0),
00052 iFreeList(0),
00053 iToDelete(0),
00054 iTabDeleteTime(0),
00055 iTabTempLogID(0),
00056 iTabTempLogOffset(0)
00057 {
00058 }
00059 
00060 MSTable::~MSTable()
00061 {
00062   if (myTableName)
00063     myTableName->release();
00064 }
00065 
00066 #define MS_DELETE_MARK    "#DEL#"
00067 #define MS_DELETE_MARK_LEN  5
00068 
00069 CSPath *MSTable::getTableFile(const char *table_name, bool to_delete)
00070 {
00071   char file_name[MS_TABLE_NAME_SIZE + 50];
00072 
00073   if ((table_name && to_delete) || iToDelete) {
00074     cs_strcpy(MS_TABLE_NAME_SIZE + 50, file_name, "bs-logs");
00075     // Make sure it exists
00076     cs_add_dir_char(MS_TABLE_NAME_SIZE + 50, file_name);
00077   }
00078   else
00079     *file_name = 0;
00080   if (table_name)
00081     cs_strcat(MS_TABLE_NAME_SIZE + 50, file_name, table_name);
00082   else {
00083     cs_strcat(MS_TABLE_NAME_SIZE + 50, file_name, myTableName->getCString());
00084     if (iToDelete) {
00085       char *str = file_name + strlen(file_name) - MS_DELETE_MARK_LEN;
00086       
00087       while (str > file_name) {
00088         if (strncmp(str, MS_DELETE_MARK, MS_DELETE_MARK_LEN) == 0) {
00089           *str = 0;
00090           break;
00091         }
00092         str--;
00093       }
00094     }
00095   }
00096   cs_strcat(MS_TABLE_NAME_SIZE + 50, file_name, "-");
00097   cs_strcat(MS_TABLE_NAME_SIZE + 50, file_name, myTableID);
00098   cs_strcat(MS_TABLE_NAME_SIZE + 50, file_name, ".bst");
00099 
00100   return CSPath::newPath(RETAIN(myDatabase->myDatabasePath), file_name);
00101 }
00102 
00103 CSPath *MSTable::getTableFile()
00104 {
00105   return getTableFile(NULL, false);
00106 }
00107 
00108 CSFile *MSTable::openTableFile()
00109 {
00110   CSPath  *path;
00111   CSFile  *fh;
00112 
00113   enter_();
00114   path = getTableFile();
00115   push_(path);
00116   fh = iTableFileSize ? path->openFile(CSFile::DEFAULT) : path->openFile(CSFile::CREATE);
00117   push_(fh);
00118   if (!iTableHeadSize) {
00119     MSTableHeadRec tab_head;
00120 
00121     lock_(this);
00122     /* Check again after locking: */
00123     if (!iTableHeadSize) {
00124       size_t rem;
00125 
00126       if (fh->read(&tab_head, 0, offsetof(MSTableHeadRec, th_reserved_4), 0) < offsetof(MSTableHeadRec, th_reserved_4)) {
00127         CS_SET_DISK_4(tab_head.th_magic_4, MS_TABLE_FILE_MAGIC);
00128         CS_SET_DISK_2(tab_head.th_version_2, MS_TABLE_FILE_VERSION);
00129         CS_SET_DISK_2(tab_head.th_head_size_2, MS_TABLE_FILE_HEAD_SIZE);
00130         CS_SET_DISK_8(tab_head.th_free_list_8, 0);
00131         CS_SET_DISK_4(tab_head.th_del_time_4, 0);
00132         CS_SET_DISK_4(tab_head.th_temp_log_id_4, 0);
00133         CS_SET_DISK_4(tab_head.th_temp_log_offset_4, 0);
00134         CS_SET_DISK_4(tab_head.th_reserved_4, 0);
00135         fh->write(&tab_head, 0, sizeof(MSTableHeadRec));
00136       }
00137       
00138       /* Check the file header: */
00139       if (CS_GET_DISK_4(tab_head.th_magic_4) != MS_TABLE_FILE_MAGIC)
00140         CSException::throwFileError(CS_CONTEXT, path->getString(), CS_ERR_BAD_HEADER_MAGIC);
00141       if (CS_GET_DISK_2(tab_head.th_version_2) > MS_TABLE_FILE_VERSION)
00142         CSException::throwFileError(CS_CONTEXT, path->getString(), CS_ERR_VERSION_TOO_NEW);
00143 
00144       /* Load the header details: */
00145       iFreeList = CS_GET_DISK_8(tab_head.th_free_list_8);
00146       iTableHeadSize = CS_GET_DISK_2(tab_head.th_head_size_2);
00147       iTabDeleteTime = CS_GET_DISK_4(tab_head.th_del_time_4);
00148       iTabTempLogID = CS_GET_DISK_4(tab_head.th_temp_log_id_4);
00149       iTabTempLogOffset = CS_GET_DISK_4(tab_head.th_temp_log_offset_4);
00150 
00151       /* Round file size up to a header boundary: */
00152       if (iTableFileSize < iTableHeadSize)
00153         iTableFileSize = iTableHeadSize;
00154       if ((rem = (iTableFileSize - iTableHeadSize) % sizeof(MSTableBlobRec)))
00155         iTableFileSize += sizeof(MSTableBlobRec) - rem;
00156     }
00157     unlock_(this);
00158   }
00159   pop_(fh);
00160   release_(path);
00161   return_(fh);
00162 }
00163 
00164 void MSTable::prepareToDelete()
00165 {
00166   MSOpenTable *otab;
00167   uint32_t    delete_time = 0;
00168 
00169   enter_();
00170   iToDelete = true;
00171   otab = MSOpenTable::newOpenTable(NULL);
00172   push_(otab);
00173   otab->myTableFile = openTableFile();
00174   if (iTabTempLogID) {
00175     MSTempLogFile   *log;
00176     MSTempLogItemRec  log_item;
00177 
00178     log = myDatabase->openTempLogFile(iTabTempLogID, NULL, NULL);
00179     if (log) {
00180       push_(log);
00181       if (log->read(&log_item, iTabTempLogOffset, sizeof(MSTempLogItemRec), 0) == sizeof(MSTempLogItemRec)) {
00182         delete_time = CS_GET_DISK_4(log_item.ti_time_4);
00183         if (delete_time != iTabDeleteTime)
00184           delete_time = 0;
00185       }
00186       release_(log);
00187     }
00188   }
00189 
00190   if (!delete_time) {
00191     MSTableHeadRec tab_head;
00192 
00193     myDatabase->queueForDeletion(otab, MS_TL_TABLE_REF, myTableID, 0, 0, &iTabTempLogID, &iTabTempLogOffset, &iTabDeleteTime);
00194     CS_SET_DISK_4(tab_head.th_del_time_4, iTabDeleteTime);
00195     CS_SET_DISK_4(tab_head.th_temp_log_id_4, iTabTempLogID);
00196     CS_SET_DISK_4(tab_head.th_temp_log_offset_4, iTabTempLogOffset);
00197     otab->myTableFile->write(&tab_head.th_del_time_4, offsetof(MSTableHeadRec, th_del_time_4), 12);
00198   }
00199 
00200   release_(otab);
00201   exit_();
00202 }
00203 
00204 uint64_t MSTable::findBlobHandle(MSOpenTable *otab, uint32_t repo_id, uint64_t file_offset, uint64_t size, uint16_t head_size, uint32_t auth_code)
00205 {
00206   uint64_t      blob_id = 0;
00207   off64_t     offset = iTableHeadSize;
00208   MSTableBlobRec  blob;
00209   enter_();
00210   lock_(this);
00211 
00212   while (offset < iTableFileSize && !blob_id) {
00213     otab->myTableFile->read(&blob, offset, sizeof(MSTableBlobRec), sizeof(MSTableBlobRec));
00214      if ( (CS_GET_DISK_1(blob.tb_status_1) == 1) &&
00215         (CS_GET_DISK_3(blob.tb_repo_id_3) == repo_id) &&
00216         (CS_GET_DISK_6(blob.tb_offset_6) == file_offset) &&
00217         (CS_GET_DISK_6(blob.tb_size_6) == size) &&
00218         (CS_GET_DISK_2(blob.tb_header_size_2) == head_size) &&
00219         (CS_GET_DISK_4(blob.tb_auth_code_4) == auth_code) ) {
00220         
00221       blob_id = offset;
00222     }
00223     offset += sizeof(MSTableBlobRec);
00224   }
00225   unlock_(this);
00226   return_(blob_id);
00227 }
00228 
00229 uint64_t MSTable::createBlobHandle(MSOpenTable *otab, uint32_t repo_id, uint64_t file_offset, uint64_t size, uint16_t head_size, uint32_t auth_code)
00230 {
00231   uint64_t      blob_id;
00232   MSTableBlobRec  blob;
00233 
00234   enter_();
00235   lock_(this);
00236   if (iFreeList) {
00237     MSTableFreeBlobRec  freeb;
00238     MSTableHeadRec    tab_head;
00239 
00240     blob_id = iFreeList;
00241     otab->myTableFile->read(&freeb, iFreeList, sizeof(MSTableFreeBlobRec), sizeof(MSTableFreeBlobRec));
00242     iFreeList = CS_GET_DISK_6(freeb.tf_next_6);
00243     CS_SET_DISK_8(tab_head.th_free_list_8, iFreeList);
00244     otab->myTableFile->write(&tab_head.th_free_list_8, offsetof(MSTableHeadRec, th_free_list_8), 8);
00245   }
00246   else {
00247     blob_id = iTableFileSize;
00248     iTableFileSize += sizeof(MSTableBlobRec);
00249   }
00250   unlock_(this);
00251 
00252   CS_SET_DISK_1(blob.tb_status_1, 1);
00253   CS_SET_DISK_3(blob.tb_repo_id_3, repo_id);
00254   CS_SET_DISK_6(blob.tb_offset_6, file_offset);
00255   CS_SET_DISK_6(blob.tb_size_6, size);
00256   CS_SET_DISK_2(blob.tb_header_size_2, head_size);
00257   CS_SET_DISK_4(blob.tb_auth_code_4, auth_code);
00258   otab->myTableFile->write(&blob, blob_id, sizeof(MSTableBlobRec));
00259 
00260   return_(blob_id);
00261 }
00262 
00263 void MSTable::setBlobHandle(MSOpenTable *otab, uint64_t blob_id, uint32_t repo_id, uint64_t file_offset, uint64_t size, uint16_t head_size, uint32_t auth_code)
00264 {
00265   MSTableBlobRec  blob;
00266 
00267   if (!otab->myTableFile && !otab->isNotATable)
00268     otab->myTableFile = openTableFile();
00269     
00270   CS_SET_DISK_1(blob.tb_status_1, 1);
00271   CS_SET_DISK_3(blob.tb_repo_id_3, repo_id);
00272   CS_SET_DISK_6(blob.tb_offset_6, file_offset);
00273   CS_SET_DISK_6(blob.tb_size_6, size);
00274   CS_SET_DISK_2(blob.tb_header_size_2, head_size);
00275   CS_SET_DISK_4(blob.tb_auth_code_4, auth_code);
00276   otab->myTableFile->write(&blob, blob_id, sizeof(MSTableBlobRec));
00277 }
00278 
00279 void MSTable::updateBlobHandle(MSOpenTable *otab, uint64_t blob_id, uint32_t repo_id, uint64_t offset, uint16_t head_size)
00280 {
00281   MSTableBlobRec  blob;
00282 
00283   if (!otab->myTableFile && !otab->isNotATable)
00284     otab->myTableFile = openTableFile();
00285     
00286   CS_SET_DISK_3(blob.tb_repo_id_3, repo_id);
00287   CS_SET_DISK_6(blob.tb_offset_6, offset);
00288   if (head_size) {
00289     CS_SET_DISK_2(blob.tb_header_size_2, head_size);
00290     otab->myTableFile->write(&blob.tb_repo_id_3, blob_id + offsetof(MSTableBlobRec, tb_repo_id_3), 11);
00291   }
00292   else
00293     otab->myTableFile->write(&blob.tb_repo_id_3, blob_id + offsetof(MSTableBlobRec, tb_repo_id_3), 9);
00294 }
00295 
00296 bool MSTable::readBlobHandle(MSOpenTable *otab, uint64_t blob_id, uint32_t *auth_code,
00297   uint32_t *repo_id, uint64_t *offset, uint64_t *data_size, uint16_t *head_size, bool throw_error)
00298 {
00299   MSTableBlobRec  blob;
00300   uint32_t      ac;
00301 
00302   if (!otab->myTableFile && !otab->isNotATable)
00303     otab->myTableFile = openTableFile();
00304 
00305   otab->myTableFile->read(&blob, blob_id, sizeof(MSTableBlobRec), sizeof(MSTableBlobRec));
00306   if (!(*repo_id = CS_GET_DISK_3(blob.tb_repo_id_3))) {
00307     if (throw_error)
00308       CSException::throwException(CS_CONTEXT, MS_ERR_NOT_FOUND, "BLOB has already been freed");
00309     return false;
00310   }
00311   *offset = CS_GET_DISK_6(blob.tb_offset_6);
00312   *data_size = CS_GET_DISK_6(blob.tb_size_6);
00313   *head_size = CS_GET_DISK_2(blob.tb_header_size_2);
00314   ac = CS_GET_DISK_4(blob.tb_auth_code_4);
00315   if (!*auth_code)
00316     *auth_code = ac;
00317   else if (*auth_code != ac) {
00318     if (throw_error)
00319       CSException::throwException(CS_CONTEXT, MS_ERR_AUTH_FAILED, "Invalid BLOB identifier");
00320     return false;
00321   }
00322   return true;
00323 }
00324 
00325 void MSTable::freeBlobHandle(MSOpenTable *otab, uint64_t blob_id, uint32_t repo_id, uint64_t file_offset, uint32_t auth_code)
00326 {
00327   MSTableBlobRec    blob;
00328   MSTableFreeBlobPtr  fblob = (MSTableFreeBlobPtr) &blob;
00329   MSTableHeadRec    tab_head;
00330 
00331   enter_();
00332   otab->openForReading();
00333   otab->myTableFile->read(&blob, blob_id, sizeof(MSTableBlobRec), sizeof(MSTableBlobRec));
00334   if (CS_GET_DISK_1(blob.tb_status_1) == 1 &&
00335     CS_GET_DISK_3(blob.tb_repo_id_3) == repo_id &&
00336     CS_GET_DISK_6(blob.tb_offset_6) == file_offset &&
00337     CS_GET_DISK_4(blob.tb_auth_code_4) == auth_code) {
00338     lock_(this);
00339     memset(&blob, 0, sizeof(MSTableBlobRec));
00340     CS_SET_DISK_6(fblob->tf_next_6, iFreeList);
00341     iFreeList = blob_id;
00342     CS_SET_DISK_8(tab_head.th_free_list_8, iFreeList);
00343     otab->myTableFile->write(&blob, blob_id, sizeof(MSTableBlobRec));
00344     otab->myTableFile->write(&tab_head.th_free_list_8, offsetof(MSTableHeadRec, th_free_list_8), 8);
00345     unlock_(this);
00346   }
00347   exit_();
00348 }
00349 
00350 CSObject *MSTable::getKey()
00351 {
00352   return (CSObject *) myTableName;
00353 }
00354 
00355 int MSTable::compareKey(CSObject *key)
00356 {
00357   return myTableName->compare((CSString *) key);
00358 }
00359 
00360 uint32_t MSTable::hashKey()
00361 {
00362   return myTableName->hashKey();
00363 }
00364 
00365 CSString *MSTable::getTableName()
00366 {
00367   return myTableName;
00368 }
00369 
00370 void MSTable::getDeleteInfo(uint32_t *log, uint32_t *offs, time_t *tim)
00371 {
00372   if (!iTableHeadSize) {
00373     CSFile *fh;
00374 
00375     fh = openTableFile();
00376     fh->release();
00377   }
00378 
00379   *log = iTabTempLogID;
00380   *offs = iTabTempLogOffset;
00381   *tim = iTabDeleteTime; 
00382 }
00383 
00384 MSTable *MSTable::newTable(uint32_t tab_id, CSString *tab_name, MSDatabase *db, off64_t file_size, bool to_delete)
00385 {
00386   MSTable *tab;
00387 
00388   if (!(tab = new MSTable())) {
00389     tab_name->release();
00390     CSException::throwOSError(CS_CONTEXT, ENOMEM);
00391   }
00392   if (to_delete) {
00393     /* Create a new table name: */
00394     char name_buffer[MS_TABLE_NAME_SIZE + 40];
00395     
00396     cs_strcpy(MS_TABLE_NAME_SIZE + 40, name_buffer, tab_name->getCString());
00397     cs_strcat(MS_TABLE_NAME_SIZE + 40, name_buffer, MS_DELETE_MARK);
00398     cs_strcat(MS_TABLE_NAME_SIZE + 40, name_buffer, tab_id);
00399     tab_name->release();
00400     tab_name = CSString::newString(name_buffer);
00401   }
00402 
00403   tab->myTableID = tab_id;
00404   tab->myTableName = tab_name;
00405   tab->myDatabase = db;
00406   tab->iTableFileSize = file_size;
00407   tab->iToDelete = to_delete;
00408   return tab;
00409 }
00410 
00411 MSTable *MSTable::newTable(uint32_t tab_id, const char *name, MSDatabase *db, off64_t file_size, bool to_delete)
00412 {
00413   return newTable(tab_id, CSString::newString(name), db, file_size, to_delete);
00414 }
00415 
00416