//-< CLASS.CPP >-----------------------------------------------------*--------*
// FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
// (Main Memory Database Management System)                          *   /\|  *
//                                                                   *  /  \  *
//                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
//                          Last update:  1-Jan-99    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Metaclass information
//-------------------------------------------------------------------*--------*

#define INSIDE_FASTDB

#include "fastdb.h"
#include "compiler.h"
#include "symtab.h"

#ifndef CHECK_RECORD_SIZE
#define CHECK_RECORD_SIZE 1
#endif

dbTableDescriptor* dbTableDescriptor::chain;

void* dbFieldDescriptor::operator new(size_t size EXTRA_DEBUG_NEW_PARAMS)
{
    return dbMalloc(size);
}

void  dbFieldDescriptor::operator delete(void* p EXTRA_DEBUG_NEW_PARAMS)
{
    dbFree(p);
}

dbFieldDescriptor::dbFieldDescriptor(const char* name) 
{ 
    next = prev = this;
    this->name = name;
    longName = NULL;
    dbSymbolTable::add((char*&) (this->name), tkn_ident, FASTDB_CLONE_ANY_IDENTIFIER);
    appOffs = dbsOffs = 0;
    defTable = refTable = NULL;
    refTableName = NULL;
    components = NULL;
    inverseRefName = NULL;
    indexType = 0;
    method = NULL;
    attr = OneToOneMapping;
    tTree = 0;
    hashTable = 0;
    comparator = (dbUDTComparator)&memcmp;
}


dbFieldDescriptor::dbFieldDescriptor(const char*              fieldName,
                     int                offs, 
                     int                size,
                     int                index,
                     char*              inverse,
                     dbFieldDescriptor* fieldComponents)
{
    next = prev = this;
    name = fieldName;
    longName = NULL;
    dbSymbolTable::add((char*&) name, tkn_ident, FASTDB_CLONE_ANY_IDENTIFIER);
    appOffs = offs;
    dbsOffs = 0;
    alignment = appSize = dbsSize = size;
    defTable = refTable = NULL;
    indexType = index;
    type = appType = dbField::tpStructure;
    inverseRefName = inverse;
    if (inverseRefName != NULL) { 
    dbSymbolTable::add(inverseRefName, tkn_ident, FASTDB_CLONE_ANY_IDENTIFIER);
    }
    inverseRef = NULL;
    components = fieldComponents;
    method = NULL;
    attr = 0;
    tTree = 0;
    hashTable = 0;
    comparator = (dbUDTComparator)&memcmp;
}

dbFieldDescriptor::~dbFieldDescriptor() 
{
    if (type == dbField::tpString) { 
    delete components;
    }
    delete[] longName; 
}


dbFieldDescriptor* dbFieldDescriptor::find(const char* name)
{
    dbFieldDescriptor* field = components;
    do { 
    if (field->name == name) { 
        return field;
    }
    } while ((field = field->next) != components);
    return NULL;
}


void dbFieldDescriptor::adjustReferences(byte* record,
                     size_t base, size_t size, long shift)
{
    dbFieldDescriptor* fd = this;
    do { 
    
    if (fd->type == dbField::tpArray) { 
        byte** ptr = (byte**)((dbAnyArray*)(record + fd->appOffs) + 1);
        if ((unsigned long)*ptr - base <= size) {
        *ptr += shift;
        } else { 
        if (fd->attr & HasArrayComponents) { 
            int nElems = ((dbAnyArray*)(record+fd->appOffs))->length();
            byte* arrBase = *ptr;
            while (--nElems >= 0) { 
            fd->components->adjustReferences(arrBase, 
                             base, size, shift);
            arrBase += fd->components->appSize;
            }
        }
        } 
    } else if (fd->type == dbField::tpString) { 
        char** str = (char**)(record + fd->appOffs);
        if ((unsigned long)*str - base <= size) {
        *str += shift;
        }
    } else if (fd->attr & HasArrayComponents) { 
        fd->components->adjustReferences(record + fd->appOffs, 
                         base, size, shift);
    }
    } while ((fd = fd->next) != this);
}


size_t dbFieldDescriptor::calculateRecordSize(byte* base, size_t offs)
{
    dbFieldDescriptor* fd = this;
    do { 
    switch (fd->appType) { 
      case dbField::tpArray:
        {
        int nElems = ((dbAnyArray*)(base + fd->appOffs))->length();
        offs = DOALIGN(offs, fd->components->alignment)
            + nElems*fd->components->dbsSize;
        if (fd->attr & HasArrayComponents) { 
            byte* elem = (byte*)((dbAnyArray*)(base+fd->appOffs))->base();
            dbFieldDescriptor* component = fd->components;
            size_t elemSize = component->appSize;
            while (--nElems >= 0) { 
            offs = component->calculateRecordSize(elem, offs);
            elem += elemSize;
            }
        } 
        continue;
        } 
      case dbField::tpString:
        {
        char* str = *(char**)(base + fd->appOffs);
        if (str != NULL) { 
                    offs += strlen(str) + 1;
                } else { 
                    offs += 1;
                }
        continue;
        }
#ifdef USE_STD_STRING
      case dbField::tpStdString:
        offs += ((std::string*)(base + fd->appOffs))->length() + 1;
        continue;
#endif
      default:
        if (fd->attr & HasArrayComponents) { 
        offs = fd->components->calculateRecordSize(base+fd->appOffs, offs);
        }
    }
    } while ((fd = fd->next) != this);
    return offs;
}



size_t dbFieldDescriptor::calculateNewRecordSize(byte* base, size_t offs) 
{
    dbFieldDescriptor* fd = this;
    do { 
    if (fd->type == dbField::tpArray) { 
        if (fd->oldDbsType == dbField::tpUnknown) { 
        continue;
        }
        int nElems = ((dbVarying*)(base + fd->oldDbsOffs))->size;
        offs = DOALIGN(offs, fd->components->alignment)
         + nElems*fd->components->dbsSize;
        if (fd->attr & HasArrayComponents) { 
        byte* elem = base + ((dbVarying*)(base+fd->oldDbsOffs))->offs;
        while (--nElems >= 0) { 
            offs = fd->components->calculateNewRecordSize(elem, offs);
            elem += fd->components->oldDbsSize;
        }
        } 
    } else if (fd->type == dbField::tpString) { 
        if (fd->oldDbsType == dbField::tpUnknown) { 
        offs += 1;
        } else { 
        offs += ((dbVarying*)(base + fd->oldDbsOffs))->size;
        }
    } else if (fd->attr & HasArrayComponents) {
        offs = fd->components->calculateNewRecordSize(base, offs);
    }
    } while ((fd = fd->next) != this);
    return offs;
}

void dbFieldDescriptor::fetchRecordFields(byte* dst, byte* src)
{
    dbFieldDescriptor* fd = this;
    do { 
    switch (fd->appType) { 
      case dbField::tpBool:
        *(bool*)(dst+fd->appOffs) = *(bool*)(src+fd->dbsOffs);
        break;
      case dbField::tpInt1:
        *(int1*)(dst+fd->appOffs) = *(int1*)(src+fd->dbsOffs);
        break;
      case dbField::tpInt2:
        *(int2*)(dst+fd->appOffs) = *(int2*)(src+fd->dbsOffs);
        break;
      case dbField::tpInt4:
        *(int4*)(dst+fd->appOffs) = *(int4*)(src+fd->dbsOffs);
        break;
      case dbField::tpInt8:
        *(db_int8*)(dst+fd->appOffs) = *(db_int8*)(src+fd->dbsOffs);
        break;
      case dbField::tpReal4:
        *(real4*)(dst+fd->appOffs) = *(real4*)(src+fd->dbsOffs);
        break;
      case dbField::tpReal8:
        *(real8*)(dst+fd->appOffs) = *(real8*)(src+fd->dbsOffs);
        break;
      case dbField::tpRawBinary:
            memcpy(dst+fd->appOffs, src+fd->dbsOffs, fd->dbsSize);
        break;      
      case dbField::tpString:
        *(char**)(dst+fd->appOffs) = 
        (char*)(src + ((dbVarying*)(src+fd->dbsOffs))->offs);
        break;
#ifdef USE_STD_STRING
      case dbField::tpStdString:
        ((std::string*)(dst + fd->appOffs))->assign((char*)(src + ((dbVarying*)(src+fd->dbsOffs))->offs), 
                           ((dbVarying*)(src+fd->dbsOffs))->size - 1);
        break;
#endif
      case dbField::tpArray:
        {
        int nElems = ((dbVarying*)(src+fd->dbsOffs))->size;
        byte* srcElem = src + ((dbVarying*)(src+fd->dbsOffs))->offs;
        dbAnyArray* array = (dbAnyArray*)(dst+fd->appOffs);
        if (fd->attr & dbFieldDescriptor::OneToOneMapping) { 
            fd->arrayAllocator(array, srcElem, nElems);
        } else { 
            fd->arrayAllocator(array, NULL, nElems);
            byte* dstElem = (byte*)array->base();
            dbFieldDescriptor* component = fd->components;
            while (--nElems >= 0) { 
            component->fetchRecordFields(dstElem, srcElem);
            dstElem += component->appSize;
            srcElem += component->dbsSize;
            }
        }
        }  
        break;
      case dbField::tpReference:
        ((dbAnyReference*)(dst+fd->appOffs))->oid = 
        *(oid_t*)(src+fd->dbsOffs);
        break;
      case dbField::tpStructure:
        fd->components->fetchRecordFields(dst + fd->appOffs, src);
        break;
      default:
        return;
    }
    } while ((fd = fd->next) != this);
}


size_t dbFieldDescriptor::storeRecordFields(byte* dst, byte* src, size_t offs, bool insert)
{
    dbFieldDescriptor* fd = this;
    do { 
#ifdef AUTOINCREMENT_SUPPORT
    if (insert && (fd->indexType & AUTOINCREMENT) != 0) { 
        assert (fd->appType == dbField::tpInt4);
        *(int4*)(dst+fd->dbsOffs) = *(int4*)(src+fd->appOffs) = fd->defTable->autoincrementCount;
        continue;
    }
#endif
    switch (fd->appType) { 
      case dbField::tpBool:
        *(bool*)(dst+fd->dbsOffs) = *(bool*)(src+fd->appOffs);
        break;
      case dbField::tpInt1:
        *(int1*)(dst+fd->dbsOffs) = *(int1*)(src+fd->appOffs);
        break;
      case dbField::tpInt2:
        *(int2*)(dst+fd->dbsOffs) = *(int2*)(src+fd->appOffs);
        break;
      case dbField::tpInt4:
        *(int4*)(dst+fd->dbsOffs) = *(int4*)(src+fd->appOffs);
        break;
      case dbField::tpInt8:
        *(db_int8*)(dst+fd->dbsOffs) = *(db_int8*)(src+fd->appOffs);
        break;
      case dbField::tpReal4:
        *(real4*)(dst+fd->dbsOffs) = *(real4*)(src+fd->appOffs);
        break;
      case dbField::tpReal8:
        *(real8*)(dst+fd->dbsOffs) = *(real8*)(src+fd->appOffs);
        break;  
      case dbField::tpRawBinary:
            memcpy(dst+fd->dbsOffs, src+fd->appOffs, fd->dbsSize);
        break;      
#ifdef USE_STD_STRING
      case dbField::tpStdString:
        { 
        ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
        std::string* str = (std::string*)(src + fd->appOffs);
        int len = str->length();
        str->copy((char*)dst + offs, len);
        dst[offs + len] = '\0';
        ((dbVarying*)(dst+fd->dbsOffs))->size = len+1;
        offs += (len+1);
        }
        break;
#endif
      case dbField::tpString:
            {
                ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
                char* str = *(char**)(src + fd->appOffs);
                if (str != NULL) { 
                    strcpy((char*)dst + offs, str);
                    offs += ((dbVarying*)(dst+fd->dbsOffs))->size 
                        = strlen(*(char**)(src + fd->appOffs)) + 1;
                } else { 
                    *((char*)dst + offs) = '\0';
                    offs += 1;
                }
            }
            break;
      case dbField::tpArray:
        {
        int nElems = ((dbAnyArray*)(src + fd->appOffs))->length();
        byte* srcElem=(byte*)((dbAnyArray*)(src+fd->appOffs))->base();
        byte* dstElem = (byte*)DOALIGN(long(dst)+offs, 
                           fd->components->alignment);
        offs = dstElem - dst;
        ((dbVarying*)(dst+fd->dbsOffs))->size = nElems;
        ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
        size_t sizeElem = fd->components->dbsSize;
        size_t offsElem = nElems*sizeElem;
        offs += offsElem;
        if (srcElem != NULL) { 
            if (fd->attr & dbFieldDescriptor::OneToOneMapping) { 
            memcpy(dstElem, srcElem, offsElem);
            } else { 
            dbFieldDescriptor* component = fd->components;
            while (--nElems >= 0) { 
                offsElem = 
                component->storeRecordFields(dstElem, 
                                 srcElem, offsElem, insert);
                offsElem -= sizeElem;
                dstElem += sizeElem;
                srcElem += component->appSize;
            }
            offs += offsElem;
            }
        }
        
        }  
        break;
      case dbField::tpReference:
        *(oid_t*)(dst+fd->dbsOffs) = 
        ((dbAnyReference*)(src+fd->appOffs))->oid;
        break;
      case dbField::tpStructure:
        offs = fd->components->storeRecordFields(dst, src+fd->appOffs, offs, insert);
        break;
      default:
        return offs;
    }
    } while ((fd = fd->next) != this);

    return offs;
}


void dbFieldDescriptor::markUpdatedFields(byte* dst, byte* src)
{
    dbFieldDescriptor* fd = this;
    do { 
    if (fd->indexType & (HASHED|INDEXED)) { 
        switch (fd->appType) { 
          case dbField::tpBool:
        if (*(bool*)(dst+fd->dbsOffs) != *(bool*)(src+fd->appOffs)) { 
            fd->attr |= Updated;
        }
        break;
          case dbField::tpInt1:
        if (*(int1*)(dst+fd->dbsOffs) != *(int1*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpInt2:
        if (*(int2*)(dst+fd->dbsOffs) != *(int2*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpInt4:
        if (*(int4*)(dst+fd->dbsOffs) != *(int4*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpInt8:
        if (*(db_int8*)(dst+fd->dbsOffs) != *(db_int8*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpReference:
        if (*(oid_t*)(dst+fd->dbsOffs) != *(oid_t*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpReal4:
        if (*(real4*)(dst+fd->dbsOffs) != *(real4*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;
          case dbField::tpReal8:
        if (*(real8*)(dst+fd->dbsOffs) != *(real8*)(src+fd->appOffs)) {
            fd->attr |= Updated;
        }
        break;  
          case dbField::tpRawBinary:
        if (memcmp(dst+fd->dbsOffs, src+fd->appOffs, fd->dbsSize) != 0) {
            fd->attr |= Updated;
        }
        break;  
          case dbField::tpString:
        if (strcmp((char*)dst + ((dbVarying*)(dst+fd->dbsOffs))->offs,
               *(char**)(src + fd->appOffs)) != 0) 
        {
            fd->attr |= Updated;
        }
        break;
#ifdef USE_STD_STRING
          case dbField::tpStdString:
        if (*(std::string*)(src + fd->appOffs) != (char*)(dst + ((dbVarying*)(dst+fd->dbsOffs))->offs)) {
            fd->attr |= Updated;
        }
        break;
#endif      
          case dbField::tpStructure:
        fd->components->markUpdatedFields(dst, src+fd->appOffs);
        break;
          case dbField::tpArray:
        break;
          default:
        return;
        }
    }
    } while ((fd = fd->next) != this);
}


size_t dbFieldDescriptor::convertRecord(byte* dst, byte* src, size_t offs)
{
    dbFieldDescriptor* fd = this;
    int1  i1;
    int2  i2;
    int4  i4;
    db_int8  i8;
    real4 f4;
    real8 f8;
    bool  b;
    do { 
    switch (fd->type) { 
      case dbField::tpBool:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        b = *(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        b = *(int1*)(src + fd->oldDbsOffs) != 0;
        break;
          case dbField::tpInt2:
        b = *(int2*)(src + fd->oldDbsOffs) != 0;
        break;
          case dbField::tpInt4:
        b = *(int4*)(src + fd->oldDbsOffs) != 0;
        break;
          case dbField::tpInt8:
        b = *(db_int8*)(src + fd->oldDbsOffs) != 0;
        break;
          case dbField::tpReal4:
        b = *(real4*)(src + fd->oldDbsOffs) != 0;
        break;
          case dbField::tpReal8:
        b = *(real8*)(src + fd->oldDbsOffs) != 0;
        break;
          default:
        b = false;
        }
        *(bool*)(dst + fd->dbsOffs) = b;
        break;

      case dbField::tpInt1:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        i1 = *(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        i1 = *(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        i1 = (int1)*(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        i1 = (int1)*(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        i1 = (int1)*(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        i1 = (int1)*(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        i1 = (int1)*(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        i1 = 0;
        }
        *(int1*)(dst + fd->dbsOffs) = i1;
        break;

      case dbField::tpInt2:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        i2 = *(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        i2 = *(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        i2 = *(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        i2 = (int2)*(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        i2 = (int2)*(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        i2 = (int2)*(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        i2 = (int2)*(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        i2 = 0;
        }
        *(int2*)(dst + fd->dbsOffs) = i2;
        break;

      case dbField::tpInt4:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        i4 = *(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        i4 = *(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        i4 = *(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        i4 = *(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        i4 = (int4)*(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        i4 = (int4)*(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        i4 = (int4)*(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        i4 = 0;
        }
        *(int4*)(dst + fd->dbsOffs) = i4;
        break;

      case dbField::tpInt8:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        i8 = *(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        i8 = *(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        i8 = *(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        i8 = *(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        i8 = *(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        i8 = (db_int8)*(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        i8 = (db_int8)*(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        i8 = 0;
        }
        *(db_int8*)(dst + fd->dbsOffs) = i8;
        break;

      case dbField::tpReal4:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        f4 = (real4)*(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        f4 = (real4)*(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        f4 = (real4)*(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        f4 = (real4)*(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        f4 = (real4)*(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        f4 = *(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        f4 = (real4)*(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        f4 = 0;
        }
        *(real4*)(dst + fd->dbsOffs) = f4;
        break;

      case dbField::tpReal8:
        switch (fd->oldDbsType) { 
          case dbField::tpBool:
        f8 = (real8)*(bool*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt1:
        f8 = (real8)*(int1*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt2:
        f8 = (real8)*(int2*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt4:
        f8 = (real8)*(int4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpInt8:
        f8 = (real8)*(db_int8*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal4:
        f8 = *(real4*)(src + fd->oldDbsOffs);
        break;
          case dbField::tpReal8:
        f8 = *(real8*)(src + fd->oldDbsOffs);
        break;
          default:
        f8 = 0;
        }
        *(real8*)(dst + fd->dbsOffs) = f8;
        break;

      case dbField::tpRawBinary:
        if (fd->oldDbsType == dbField::tpRawBinary) {
                memcpy(dst + fd->dbsOffs, src + fd->oldDbsOffs, 
               size_t(fd->oldDbsSize) < fd->dbsSize 
               ? size_t(fd->oldDbsSize) : fd->dbsSize);
            } 
            break;

      case dbField::tpString:
        if (fd->oldDbsType == dbField::tpUnknown) { 
        ((dbVarying*)(dst + fd->dbsOffs))->size = 1;
        ((dbVarying*)(dst + fd->dbsOffs))->offs = offs;
        *(char*)(dst + offs++) = '\0';
        } else { 
        size_t len = 
            ((dbVarying*)(src + fd->oldDbsOffs))->size;
        ((dbVarying*)(dst + fd->dbsOffs))->size = len;
        ((dbVarying*)(dst + fd->dbsOffs))->offs = offs;
        memcpy(dst + offs, 
               src + ((dbVarying*)(src+fd->oldDbsOffs))->offs, len);
        offs += len;
        }
        break;

      case dbField::tpArray:
        if (fd->oldDbsType == dbField::tpUnknown) { 
        ((dbVarying*)(dst + fd->dbsOffs))->size = 0;
        ((dbVarying*)(dst + fd->dbsOffs))->offs = 0;
        } else { 
        int len = ((dbVarying*)(src+fd->oldDbsOffs))->size;
        byte* srcElem = src + ((dbVarying*)(src+fd->oldDbsOffs))->offs;
        ((dbVarying*)(dst + fd->dbsOffs))->size = len;
            byte* dstElem = 
            (byte*)DOALIGN(long(dst)+offs, fd->components->alignment);
        offs = dstElem - dst;
        ((dbVarying*)(dst+fd->dbsOffs))->offs = offs;
        size_t offsElem = len*fd->components->dbsSize; 
        offs += offsElem;
        while (--len >= 0) { 
            offsElem = fd->components->convertRecord(dstElem, srcElem,
                                 offsElem);
            offsElem -= fd->components->dbsSize;
            dstElem += fd->components->dbsSize;
            srcElem += fd->components->oldDbsSize;
        }
        offs += offsElem;
        }
        break;

      case dbField::tpStructure:
        offs = fd->components->convertRecord(dst, src, offs);
        break;

      case dbField::tpReference:
        if (fd->oldDbsType == dbField::tpUnknown) { 
        *(oid_t*)(dst + fd->dbsOffs) = 0;
        } else { 
        *(oid_t*)(dst + fd->dbsOffs) = *(oid_t*)(src + fd->oldDbsOffs);
        }
        break;
      default:
        return offs;
    }
    } while ((fd = fd->next) != this);

    return offs;
}


int dbTableDescriptor::initialAutoincrementCount;


dbTableDescriptor::dbTableDescriptor(char*              tableName, 
                     dbDatabase*        database,
                     size_t             objSize, 
                     describeFunc       func,
                     dbTableDescriptor* original)
{
    cloneOf = original;
    isStatic = true;
    if (original == NULL) {
    next = chain;
    chain = this;
    }
    name = tableName;
    dbSymbolTable::add(name, tkn_ident, FASTDB_CLONE_ANY_IDENTIFIER);
    describeComponentsFunc = func;
    columns = (*func)();
    nextFieldLink = &firstField;
    hashedFields = NULL;
    indexedFields = NULL;
    inverseFields = NULL;
    tableId = 0;
    nFields = 0;
    nColumns = 0;
    db = database;
    fixedDatabase = database != NULL;
    fixedSize = sizeof(dbRecord);
    int attr = dbFieldDescriptor::OneToOneMapping;
    appSize = 0;
    autoincrementCount = initialAutoincrementCount;
    size_t maxAlignment = calculateFieldsAttributes(columns, "", 
                            sizeof(dbRecord), 
                            HASHED|INDEXED, attr);
#if CHECK_RECORD_SIZE
    appSize = DOALIGN(appSize, maxAlignment);
    if (appSize < objSize) { 
    fprintf(stderr, "Warning: may be not all fields of the class '%s' "
        "were described\n", name);
    }
#endif
    *nextFieldLink = NULL;
}


int dbTableDescriptor::calculateFieldsAttributes(dbFieldDescriptor* first,
                         char const*        prefix, 
                         int                offs,
                         int                indexMask, 
                         int&               attr)
{
    dbFieldDescriptor *field = first;
    size_t alignment = 1;
    do { 
    if (field->method) { 
        assert(((void)"Not empty record", field != first));
        do { 
        assert(((void)"Methods should be specified after variables", 
            field->method != NULL));
        field->dbsOffs = first->dbsOffs;
        field->components = first;
        if (attr & dbFieldDescriptor::OneToOneMapping) { 
            field->method = field->method->optimize();
        }
        } while ((field = field->next) != first);
        break;
    }
    if (*prefix != '\0') { 
        char* p = new char[strlen(prefix)+strlen(field->name)+1];
        sprintf(p, "%s%s", prefix, field->name);
        field->longName = p;
    } else { 
        nColumns += 1;
        field->longName = new char[strlen(field->name)+1];
        strcpy(field->longName, field->name);
    }
    field->defTable = this;
    field->indexType &= indexMask|DB_FIELD_INHERITED_MASK;
    field->attr = (attr & dbFieldDescriptor::ComponentOfArray) 
                | dbFieldDescriptor::OneToOneMapping;
    if (field->inverseRefName) { 
        assert(!(attr & dbFieldDescriptor::ComponentOfArray)
           && (field->type == dbField::tpReference 
               || (field->type == dbField::tpArray 
               && field->components->type==dbField::tpReference)));
        field->nextInverseField = inverseFields;
        inverseFields = field; 
    }
    *nextFieldLink = field;
    nextFieldLink = &field->nextField;
    field->fieldNo = nFields++;
           
    switch (field->type) { 
      case dbField::tpArray:
        { 
        size_t saveOffs = fixedSize;
        size_t saveAppSize = appSize;
        fixedSize = 0;
        attr = (attr | dbFieldDescriptor::HasArrayComponents)
             & ~dbFieldDescriptor::OneToOneMapping;
        field->attr |= dbFieldDescriptor::ComponentOfArray;
        calculateFieldsAttributes(field->components, field->longName,
                      0, 0, field->attr);
        if (field->components->dbsSize != field->components->appSize) {
            field->attr &= ~dbFieldDescriptor::OneToOneMapping;
        }
        fixedSize = saveOffs;
        appSize = DOALIGN(saveAppSize, sizeof(void*)) 
            + sizeof(void*)*3;
        break;
        }
      case dbField::tpStructure:
        { 
        char* aggregateName = new char[strlen(field->longName) + 2];
        sprintf(aggregateName, "%s.", field->longName);
        size_t saveOffs = fixedSize;        
        size_t saveAppSize = appSize;       
        appSize = 0;
        size_t struct_alignment = 
            calculateFieldsAttributes(field->components, 
                          aggregateName,
                          offs + field->appOffs, 
                          field->indexType,
                          field->attr);
        field->alignment = struct_alignment;
        field->dbsOffs = field->components->dbsOffs;
        attr |= field->attr & dbFieldDescriptor::HasArrayComponents;
        attr &= field->attr | ~dbFieldDescriptor::OneToOneMapping;
        field->dbsSize = DOALIGN(fixedSize-saveOffs, struct_alignment);
        if ((field->attr & dbFieldDescriptor::HasArrayComponents)
            && struct_alignment < sizeof(void*))
        {
            struct_alignment = sizeof(void*);
        }
        appSize = DOALIGN(appSize, struct_alignment) 
                + DOALIGN(saveAppSize, struct_alignment);
        delete[] aggregateName;
        break;
        } 
      case dbField::tpString:
        attr = (attr | dbFieldDescriptor::HasArrayComponents)
        & ~dbFieldDescriptor::OneToOneMapping;
        // no break
      default:
        appSize = DOALIGN(appSize, field->appSize) + field->appSize;
    }
    if (alignment < field->alignment) { 
        alignment = field->alignment;
    }
    if (field->type != dbField::tpStructure) { 
        field->dbsOffs = fixedSize = DOALIGN(fixedSize, field->alignment);
        fixedSize += field->dbsSize;
        if (field->dbsOffs != offs + field->appOffs) { 
        attr &= ~dbFieldDescriptor::OneToOneMapping;
        }

        if (field->indexType & (HASHED|INDEXED)) {
        assert(!(field->attr & dbFieldDescriptor::ComponentOfArray));
        if (field->indexType & HASHED) { 
            field->nextHashedField = hashedFields;
            hashedFields = field;
        }
        if (field->indexType & INDEXED) { 
            field->nextIndexedField = indexedFields;
            indexedFields = field;
        }
        }
    }
    } while ((field = field->next) != first);

    return alignment;
}


int dbFieldDescriptor::sizeWithoutOneField(dbFieldDescriptor* field, 
                       byte* base, size_t& size)
{
    dbFieldDescriptor* fd = this;
    int offs, last = 0;
    do { 
    if (fd != field) { 
        if (fd->type == dbField::tpArray || fd->type == dbField::tpString){
        dbVarying* arr = (dbVarying*)(base + fd->dbsOffs);
        if (arr->offs > last) { 
            last = arr->offs;
        }
        int n = arr->size;
        size = DOALIGN(size, fd->components->alignment)
             + fd->components->dbsSize * n;
        if (fd->attr & HasArrayComponents) { 
            byte* elem = base + arr->offs;
            while (--n >= 0) {
            offs = fd->components->sizeWithoutOneField(field, 
                                   elem, size);
            if (arr->offs + offs > last) { 
                last = arr->offs + offs;
            }
            elem += fd->components->dbsSize;
            }
        }
        } else if (fd->attr & HasArrayComponents) { 
        offs = fd->components->sizeWithoutOneField(field, base, size);
        if (offs > last) { 
            last = offs;
        }
        }
    }
    } while ((fd = fd->next) != this);

    return last;
}


size_t dbFieldDescriptor::copyRecordExceptOneField(dbFieldDescriptor* field, 
                           byte* dst, byte* src, 
                           size_t offs)
{
    dbFieldDescriptor* fd = this;
    do { 
    if (fd != field) { 
        if (fd->type == dbField::tpArray || fd->type == dbField::tpString){
        dbVarying* srcArr = (dbVarying*)(src + fd->dbsOffs);
        dbVarying* dstArr = (dbVarying*)(dst + fd->dbsOffs);
        int n = srcArr->size;
        byte* srcElem = src + srcArr->offs;
        byte* dstElem = (byte*)DOALIGN(long(dst) + offs, 
                           fd->components->alignment);
        dstArr->offs = offs = dstElem - dst;
        dstArr->size = n;
        size_t sizeElem = fd->components->dbsSize;
        size_t offsElem = sizeElem * n;
        offs += offsElem;
        if (fd->attr & HasArrayComponents) { 
            while (--n >= 0) {
            offsElem = fd->components->
                copyRecordExceptOneField(field, dstElem, srcElem,
                             offsElem);
            offsElem -= sizeElem;
            dstElem += sizeElem;
            srcElem += sizeElem;
            }
            offs += offsElem;
        } else { 
            memcpy(dstElem, srcElem, offsElem);
        } 
        } else if (fd->attr & HasArrayComponents) { 
        offs = fd->components->copyRecordExceptOneField(field, dst, 
                                src, offs);
        } else if (fd->method == NULL) {
        memcpy(dst+fd->dbsOffs, src+fd->dbsOffs, fd->dbsSize);
        }
    }
    } while ((fd = fd->next) != this);

    return offs;
}


void dbTableDescriptor::checkRelationship() 
{
    dbFieldDescriptor* fd;
    for (fd = inverseFields; fd != NULL; fd = fd->nextInverseField) { 
    dbTableDescriptor* refTable = 
        fd->refTable ? fd->refTable : fd->components->refTable;
    fd->inverseRef = refTable->findSymbol(fd->inverseRefName);
    if (fd->inverseRef == NULL 
         || fd->inverseRef->inverseRefName != fd->name)
        {
            char msg[256];
            if (fd->inverseRef == NULL) { 
                sprintf(msg, "Failed to locate inverse reference field %s.%s for field %s.%s", 
                        refTable->name, fd->inverseRefName, fd->defTable->name, fd->longName);
            } else {
                sprintf(msg, "Inverse references for field %s.%s is %s.%s, but its inverse reference is %s", 
                        fd->defTable->name, fd->longName, refTable->name, fd->inverseRefName, fd->inverseRef->inverseRefName);
            }
            db->handleError(dbDatabase::InconsistentInverseReference, msg);
        } 
    }
}

dbFieldDescriptor* dbTableDescriptor::find(char const* name)
{
    char* symnam = (char*)name;
    dbSymbolTable::add(symnam, tkn_ident);
    return findSymbol(symnam);
}

dbFieldDescriptor* dbTableDescriptor::findSymbol(const char* name)
{
    dbFieldDescriptor* first = columns;
    dbFieldDescriptor* field = first;
    do { 
    if (field->name == name) { 
        return field;
    }
    } while ((field = field->next) != first);
    return NULL;
}

dbFieldDescriptor& dbFieldDescriptor::adjustOffsets(long offs)
{
    if (offs != 0) { 
    dbFieldDescriptor* fd = this;
    do { 
        fd->appOffs += offs;
    } while ((fd = fd->next) != this);
    }
    return *this;
}




bool dbTableDescriptor::match(dbTable* table, bool confirmDeleteColumns) 
{
    unsigned nFields = table->fields.size;
    unsigned nMatches = 0;
    bool formatNotChanged = (nFields == this->nFields);

    for (dbFieldDescriptor* fd = firstField; fd != NULL; fd = fd->nextField) { 
    dbField* field = (dbField*)((char*)table + table->fields.offs);
    fd->oldDbsType = dbField::tpUnknown;
    for (int n = nFields; --n >= 0; field++) { 
        if (strcmp(fd->longName, (char*)field + field->name.offs) == 0) {
        assert(((void)"field can be converted to new format",
            (fd->type == dbField::tpReference
             && field->type == dbField::tpReference
             && strcmp((char*)field + field->tableName.offs,
                      fd->refTable->name) == 0)
            || (fd->type <= dbField::tpReal8 
                && field->type <= dbField::tpReal8)
            || (fd->type == dbField::tpString
                && field->type == dbField::tpString)
            || (fd->type >= dbField::tpArray
                && fd->type == field->type)));
        fd->oldDbsType = field->type;
        fd->oldDbsOffs = field->offset;
        fd->oldDbsSize = field->size;
        if (field->type != fd->type || field->offset != fd->dbsOffs) {
            formatNotChanged = false;
        }
        nMatches += 1;
        //
        // Try to reuse indices
        //
        fd->hashTable = 0;
        fd->tTree = 0;

        if (field->type == fd->type) { 
            if ((fd->indexType & HASHED) != 0 && field->hashTable != 0) { 
            fd->hashTable = field->hashTable; // reuse index
            field->hashTable = 0; // prevent index from destruction
            }
            if ((fd->indexType & INDEXED) != 0 && field->tTree != 0) { 
            fd->tTree = field->tTree; // reuse index
            field->tTree = 0; // prevent index from destruction
            }
        }
        break;
        }
    }
    }
    if (!confirmDeleteColumns) {             
        assert(((void)"field can be removed only from empty table",
                nFields==nMatches));
    }
    return formatNotChanged;
}

void dbTableDescriptor::setFlags() { 
    for (dbFieldDescriptor* fd = firstField; fd != NULL; fd = fd->nextField) {
    if (fd->tTree != 0) { 
        fd->indexType |= INDEXED;
    } else if (fd->hashTable != 0) { 
        fd->indexType |= HASHED;
    }
    }
}


bool dbTableDescriptor::equal(dbTable* table) 
{
#ifdef AUTOINCREMENT_SUPPORT
    autoincrementCount = table->count;
#endif
    if (nColumns != table->nColumns || 
    nFields != table->fields.size || fixedSize != table->fixedSize) 
    { 
    return false;
    }
    dbField* field = (dbField*)((char*)table + table->fields.offs);

    for (dbFieldDescriptor* fd = firstField; fd != NULL; fd = fd->nextField) { 
    if (strcmp(fd->longName, (char*)field + field->name.offs) != 0 
        || (!fd->refTable && *((char*)field+field->tableName.offs) != '\0')
        || (fd->refTable && strcmp((char*)field + field->tableName.offs,
                       fd->refTable->name) != 0)
        || (fd->inverseRefName == NULL 
        && *((char*)field + field->inverse.offs) != '\0')
        || (fd->inverseRefName != NULL
        && strcmp((char*)field + field->inverse.offs,
              fd->inverseRefName) != 0)
        || fd->dbsOffs != field->offset
        || fd->type != field->type)
    {
        return false;
    }
    fd->tTree = field->tTree;
    fd->hashTable = field->hashTable;
    field += 1;
    }
    return true;
}


dbTableDescriptor::dbTableDescriptor(dbTable* table)
{
    next = chain;
    chain = this;
    cloneOf = NULL;
    isStatic = false;
    name = (char*)table + table->name.offs;
    dbSymbolTable::add(name, tkn_ident, true);
    nextFieldLink = &firstField;
    hashedFields = NULL;
    indexedFields = NULL;
    inverseFields = NULL;
    nFields = 0;
    nColumns = 0;
    fixedSize = table->fixedSize;
    int attr = 0;
    appSize = 0;
    columns = buildFieldsList(table, "", 0, attr);
    *nextFieldLink = NULL;
    db = NULL;
    tableId = 0;
#ifdef AUTOINCREMENT_SUPPORT
    autoincrementCount = table->count;
#endif
}




class _AlignArray : public dbAnyArray
{
  protected:
    void*  data;
    size_t allocated;
};

class _AlignReference
{
  protected:
    oid_t       oid;
};

union _aligns {
    struct { char n; bool v; } _abool;
    struct { char n; int1 v; } _aint1;
    struct { char n; int2 v; } _aint2;
    struct { char n; int4 v; } _aint4;
    struct { char n; db_int8 v; } _aint8;
    struct { char n; real4 v; } _areal4;
    struct { char n; real8 v; } _areal8;
    struct { char n; char *v; } _astring;
    struct { char n; char v[1]; } _arawbinary;
    struct { char n; _AlignReference v; } _areference;
    struct { char n; _AlignArray v; } _aarray;
} __aligns;

#define COMPUTE_ALIGNED_SIZE(type) \
    (((char *)&(((union _aligns *)0)->_a##type.v)) - ((char *)&(((union _aligns *)0)->_a##type.n)))



dbFieldDescriptor* dbTableDescriptor::buildFieldsList(dbTable*    table, 
                              char const* prefix,
                              int         prefixLen,
                              int&        attr)
{
    dbFieldDescriptor* components = NULL;
    dbField* field = (dbField*)((char*)table+table->fields.offs) + nFields;
    
    while (nFields < table->fields.size
       && strncmp((char*)field + field->name.offs, prefix, prefixLen) == 0)
    {
    char* longName = (char*)field + field->name.offs;
    char* name = longName + prefixLen;
    if (*name == '.') { 
        name += 1;
    } else if (prefixLen != 0 && *name != '[') { 
        break;
    }
    dbSymbolTable::add(name, tkn_ident, true);  
    dbFieldDescriptor* fd = new dbFieldDescriptor(name);
    fd->dbsOffs = field->offset;
    fd->alignment = fd->dbsSize = field->size;
    fd->longName = new char[strlen(longName)+1];
    strcpy(fd->longName, longName);
    fd->type = fd->appType = field->type;
    int appFieldSize, appAlignedFieldSize;
     
    switch (field->type) {
      case dbField::tpBool:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(bool);
        appFieldSize = sizeof(bool);
        break;
          case dbField::tpInt1:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(int1);
        appFieldSize = sizeof(int1);
        break;
      case dbField::tpInt2:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(int2);
        appFieldSize = sizeof(int2);
        break;
      case dbField::tpInt4:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(int4);
        appFieldSize = sizeof(int4);
        break;
      case dbField::tpInt8:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(int8);
        appFieldSize = sizeof(db_int8);
        break;
      case dbField::tpReal4:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(real4);
        appFieldSize = sizeof(real4);
        break;
      case dbField::tpReal8:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(real8);
        appFieldSize = sizeof(real8);
        break;
      case dbField::tpString:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(string);
        appFieldSize = sizeof(char *);
        break;
      case dbField::tpRawBinary:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(rawbinary);
        appFieldSize = field->size;
        break;
      case dbField::tpReference:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(reference);
        appFieldSize = sizeof(dbAnyReference);
        break;
      case dbField::tpArray:
        appAlignedFieldSize = COMPUTE_ALIGNED_SIZE(array);
        appFieldSize = sizeof(_AlignArray);
        break;
      default:
        appAlignedFieldSize = 1;
        appFieldSize = 0;
    }
    fd->appOffs = appSize = DOALIGN(appSize, appAlignedFieldSize);
    appSize += fd->appSize = appFieldSize;

    if ((fd->hashTable = field->hashTable) != 0) { 
        fd->nextHashedField = hashedFields;
        hashedFields = fd;
    }
    if ((fd->tTree = field->tTree) != 0) { 
        fd->nextIndexedField = indexedFields;
        indexedFields = fd;
    }
    fd->fieldNo = nFields++;
    fd->defTable = this;
    fd->refTable = NULL;
    fd->refTableName = NULL;
    if (field->hashTable != 0) { 
        fd->indexType |= HASHED;
    }
    if (field->tTree != 0) { 
        fd->indexType |= INDEXED;
    }
    if (field->tableName.size > 1) { 
        fd->refTableName = (char*)field + field->tableName.offs;
        dbSymbolTable::add(fd->refTableName, tkn_ident, true);  
    }
    fd->inverseRefName = NULL;
    if (field->inverse.size > 1) { 
        fd->nextInverseField = inverseFields;
        inverseFields = fd;         
        fd->inverseRefName = (char*)field + field->inverse.offs;
        dbSymbolTable::add(fd->inverseRefName, tkn_ident, true);    
    }
    fd->attr = (attr & dbFieldDescriptor::ComponentOfArray) | dbFieldDescriptor::OneToOneMapping;

    *nextFieldLink = fd;
    nextFieldLink = &fd->nextField;

    if (prefixLen == 0) { 
        nColumns += 1;
    }
    if (components == NULL) { 
        components = fd;
    } else { 
        fd->next = components;
        fd->prev = components->prev;
        components->prev->next = fd;
        components->prev = fd;
    }
    if (fd->type == dbField::tpArray || fd->type == dbField::tpString) { 
        attr |= dbFieldDescriptor::HasArrayComponents;
        fd->attr |= dbFieldDescriptor::ComponentOfArray;
        fd->alignment = 4;
    }
    if (fd->type == dbField::tpArray || fd->type == dbField::tpStructure) {
        int saveAppSize = appSize;
        if (fd->type == dbField::tpArray) { 
        appSize = 0;
        }
        fd->components = 
        buildFieldsList(table, longName, strlen(longName), fd->attr);
        if (fd->type == dbField::tpArray) { 
        appSize = saveAppSize;
        }
        attr |= fd->attr & dbFieldDescriptor::HasArrayComponents;
        field = (dbField*)((char*)table + table->fields.offs) + nFields;
        if (fd->type == dbField::tpStructure) {
        size_t alignment = 1;
        dbFieldDescriptor* component = fd->components;
        do { 
            if (component->alignment > alignment) { 
            alignment = component->alignment;
            }
        } while ((component = component->next) != fd->components);
        fd->alignment = alignment;
        } else { 
        if (fd->components->type == dbField::tpString) { 
            fd->arrayAllocator = &dbArray<char*>::arrayAllocator;
            fd->attr &= ~dbFieldDescriptor::OneToOneMapping;
        } else { 
            fd->arrayAllocator = &dbAnyArray::arrayAllocator;
        }
        }
    } else { 
        if (fd->type == dbField::tpString) { 
        fd->components = new dbFieldDescriptor("[]");
        fd->components->type = fd->components->appType = dbField::tpInt1;
        fd->components->dbsSize = fd->components->appSize = 1;
        fd->components->alignment = 1; 
        }
        field += 1;
    }
    }
    return components;
}


size_t dbTableDescriptor::totalNamesLength()
{
    dbFieldDescriptor* fd;
    size_t len = strlen(name) + 1;
    for (fd = firstField; fd != NULL; fd = fd->nextField) { 
    if (fd->name != NULL) { 
        len += strlen(fd->longName) + 3;
        if (fd->inverseRefName != NULL) { 
        len += strlen(fd->inverseRefName);
        }
        if (fd->refTable != NULL) { 
        len += strlen(fd->refTable->name);
        }
    }
    }
    return len;
}


dbTableDescriptor* dbTableDescriptor::clone() 
{
    return new dbTableDescriptor(name,
                 DETACHED_TABLE,
                 appSize,
                 describeComponentsFunc,
                 this);
}

dbTableDescriptor::~dbTableDescriptor()
{
    if (cloneOf == NULL) {
     dbTableDescriptor **tpp; 
     for (tpp = &chain; *tpp != this; tpp = &(*tpp)->next);
     *tpp = next;
    }
    dbFieldDescriptor *field, *nextField;
    for (field = firstField; field != NULL; field = nextField) { 
    nextField = field->nextField;
    delete field;
    }
}


void dbTableDescriptor::cleanup()
{
    dbTableDescriptor* next, *desc;
    for (desc = chain; desc != NULL; desc = next) { 
    next = desc->next; 
    if (!desc->isStatic) { 
        delete desc;
    }
    }
}


void dbTableDescriptor::storeInDatabase(dbTable* table)
{
    int offs = sizeof(dbTable) + sizeof(dbField)*nFields;
    table->name.offs = offs;
    table->name.size = strlen(name)+1;
    strcpy((char*)table + offs, name);
    offs += table->name.size;
    table->fields.offs = sizeof(dbTable);
    table->fields.size = nFields;
    table->nRows = 0;
    table->nColumns = nColumns;
    table->fixedSize = fixedSize;
    table->firstRow = 0;
    table->lastRow = 0;
#ifdef AUTOINCREMENT_SUPPORT
    table->count = autoincrementCount;
#endif

    dbFieldDescriptor* fd;
    dbField* field = (dbField*)((char*)table + table->fields.offs);
    offs -= sizeof(dbTable);
    for (fd = firstField; fd != NULL; fd = fd->nextField) { 
    field->name.offs = offs;
    field->name.size = strlen(fd->longName) + 1;
    strcpy((char*)field + offs, fd->longName);
    offs += field->name.size;
    field->tableName.offs = offs;
    if (fd->refTable != NULL) { 
        field->tableName.size = strlen(fd->refTable->name) + 1;
        strcpy((char*)field + offs, fd->refTable->name);
    } else { 
        field->tableName.size = 1;
        *((char*)field + offs) = '\0';
    }
    offs += field->tableName.size;
    
    field->inverse.offs = offs;
    if (fd->inverseRefName != NULL) { 
        field->inverse.size = strlen(fd->inverseRefName) + 1;
        strcpy((char*)field + offs, fd->inverseRefName);
    } else { 
        field->inverse.size = 1;
        *((char*)field + offs) = '\0';
    }
    offs += field->inverse.size;
    
    field->tTree = fd->tTree;
    field->hashTable = fd->hashTable;
    field->type = fd->type;
    field->size = fd->dbsSize;
    field->offset = fd->dbsOffs;
    field += 1;
    offs -= sizeof(dbField);
    }
}

    
dbAnyMethodTrampoline::~dbAnyMethodTrampoline() {}


syntax highlighted by Code2HTML, v. 0.9.1