1455 lines
49 KiB
C++
1455 lines
49 KiB
C++
//------------------------------------------------------------------------------
|
||
#include "tiptopbd.h"
|
||
#include "wxTools.h"
|
||
#include "mathTools.h"
|
||
#include "tcDebug.h"
|
||
#include <wx/wfstream.h>
|
||
#include <math.h>
|
||
|
||
//------------------------------------------------------------------------------
|
||
//Преобразовать в строку в соответствии стипом
|
||
wxString getSQLValue(wxString t,wxString v)
|
||
{
|
||
if( t==_T("object") )
|
||
{
|
||
if (v==_T("-1") || v==_T("")) v=_T("NULL");
|
||
}
|
||
else if( t==_T("i4") )
|
||
{
|
||
if( v==_T("") ) v=_T("NULL");
|
||
}
|
||
else if( t==_T("f8") )
|
||
{
|
||
if( v==_T("") ) v=_T("NULL");
|
||
replaseChars(v,_T(','),_T('.')); //Разделитель целой и дробной части точка
|
||
}
|
||
else if(t==_T("b"))
|
||
{
|
||
if(v==_T("")) v=_T("NULL");
|
||
else if(v==_T("1") || v==_T("t")) v=_T("true");
|
||
else if(v==_T("0") || v==_T("f")) v=_T("false");
|
||
}
|
||
else if(t==_T("string"))
|
||
{
|
||
if(v==_T(""))
|
||
{
|
||
v=_T("NULL");
|
||
}
|
||
else
|
||
{
|
||
v=replaceStrings(v,_T("'"),_T("\\'")); //так как в SQL строку вставляется
|
||
v=_T("'")+v+_T("'");
|
||
}
|
||
}
|
||
else
|
||
v=_T("'")+v+_T("'");
|
||
return v;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Сохранить UTF8 строку не больше 256 байт
|
||
void saveUTF8StringBD(TiptopStream *os, wxString str)
|
||
{
|
||
const wxCharBuffer buff = str.mb_str(wxConvUTF8);
|
||
size_t len0 = strlen(buff);
|
||
unsigned char len1=0;
|
||
if(len0<=256) len1=(unsigned char)len0;
|
||
os->Write(&len1,1);
|
||
if(len1>0) os->Write(buff,len1);
|
||
};
|
||
//------------------------------------------------------------------------------
|
||
//Загрузить UTF8 строку из файла, строка не больше 256 байт
|
||
wxString loadUTF8StringBD(TiptopStream *is)
|
||
{
|
||
char c;
|
||
is->Read(&c,1);
|
||
char* buf = new char[c];
|
||
is->Read(buf,c);
|
||
wxString str=wxString::FromUTF8(buf,c);
|
||
delete[] buf;
|
||
return str;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
/*TiptopIndexFreeSpase::TiptopIndexFreeSpase(TiptopTable* table)
|
||
{
|
||
m_table=table;
|
||
m_count=0;
|
||
//пытаемся открыть файл для чтения
|
||
m_fis=new wxFileInputStream(m_table->m_bd->m_path+IntToStr(m_table->m_id)+_T("_0"));
|
||
m_fos=new wxFileOutputStream(m_table->m_bd->m_path+IntToStr(m_table->m_id)+_T("_0"));
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
TiptopIndexFreeSpase::~TiptopIndexFreeSpase()
|
||
{
|
||
delete m_fis;
|
||
delete m_fos;
|
||
}*/
|
||
//------------------------------------------------------------------------------
|
||
//------------------------------------------------------------------------------
|
||
//Самый простой тип индекса просто список упоряд по возрастанию
|
||
TiptopIndex::TiptopIndex(TiptopField* fl)
|
||
{
|
||
/*
|
||
m_count=0;
|
||
m_fl=fl; //Поле(столбец) таблицы
|
||
wxString file=m_fl->m_tb->m_bd->m_path+IntToStr(m_fl->m_tb->m_id)+_T('_')+IntToStr(m_fl->m_id)+_T(".i");
|
||
|
||
if(wxFileExists(file))
|
||
{
|
||
m_file=new wxFFile(file,_T("a+b"));
|
||
m_file->Seek(0,wxFromStart);
|
||
uint4 i=0;
|
||
m_file->Read(&i,4); //id файла
|
||
m_file->Read(&i,4); //Версия
|
||
m_file->Read(&m_type,4); //Под версия
|
||
m_file->Read(&m_count,4);
|
||
}else
|
||
{
|
||
m_file=new wxFFile(file,_T("a+b"));
|
||
m_file->Seek(0,wxFromStart);
|
||
uint4 i=0;
|
||
m_file->Write(&file_id,4); //id файла
|
||
m_file->Write(&file_version,4); //Версия
|
||
m_type=1;
|
||
m_file->Write(&m_type,4); //Под версия
|
||
m_file->Write(&i,4); //Количество
|
||
}
|
||
*/
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
TiptopIndex::~TiptopIndex()
|
||
{
|
||
delete m_file;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Добавить позицию в список (значение храниться в файле таблицы)
|
||
void TiptopIndex::add(uint4 pos,uint4 id)
|
||
{
|
||
uint4 p;
|
||
int4 num;
|
||
getPos(id,p,num); //Узнаём номер элемента совподающего с текущем либо меньшим
|
||
//Вставляем следующем элементом за num
|
||
m_file->Seek(16+(num*4)+4+1,wxFromStart);
|
||
for(uint4 i=num+1; i<m_count; i++) //Сдвигаем элементы
|
||
{
|
||
uint4 buf;
|
||
m_file->Read(&buf,4); //надо проверить
|
||
m_file->Write(&buf,4);
|
||
}
|
||
m_file->Seek(16+(num*4)+4+1,wxFromStart);
|
||
m_file->Write(&pos,4);
|
||
m_count++;
|
||
m_file->Seek(12,wxFromStart);
|
||
m_file->Write(&m_count,4);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Поиск позиции по значению в списке (Список по возрастанию)
|
||
//return точное совпадение то true вернулась меньшая запись то false
|
||
//val - Значение которое мы ищем
|
||
//pos - позиция с начала файла Указывает на найденное значение либо на меньшее (если меньшее return false)
|
||
bool TiptopIndex::getPos(uint4 val,uint4& pos,int4& num)
|
||
{
|
||
/*
|
||
if(m_count==0) //В списке ещё нет ни одного элемента
|
||
{
|
||
pos=0; num=-1;
|
||
return false;
|
||
}else
|
||
if(m_count==1) //в списке 1 элемент
|
||
{
|
||
m_file->Seek(16+1,wxFromStart); //Курсор на 1й элемент
|
||
m_file->Read(&pos,4);
|
||
if(!m_fl->m_tb->ReadRecord(pos)) {pos=0; return false;}
|
||
//Прочитали теперь проверяем
|
||
if(m_fl->getUint4Val() < val) l = center; else r = center;
|
||
|
||
}else
|
||
{
|
||
//Ищём среди сортированного списка значений делением на 2
|
||
unsigned int l = 0;
|
||
unsigned int r = m_count-1;
|
||
unsigned int center=0;
|
||
while (r-l>1)
|
||
{
|
||
center = (l+r) / 2;
|
||
//Читаем запись по центру
|
||
m_file->Seek(16+center,wxFromStart);
|
||
unsigned int pos;
|
||
m_file->Read(&pos,4);
|
||
if(!m_fl->m_tb->ReadRecord(pos)) {pos=0; return false;}
|
||
//Прочитали теперь проверяем
|
||
if(m_fl->getUint4Val() < val) l = center; else r = center;
|
||
}
|
||
num=center;
|
||
if(m_fl->getUint4Val()==val) return true; else return false;
|
||
}
|
||
*/
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//------------------------------------------------------------------------------
|
||
TiptopField::TiptopField(TiptopTable* tb)
|
||
{
|
||
m_tb=tb;
|
||
m_size=0;
|
||
m_value=NULL;
|
||
m_idx=NULL;
|
||
m_NULL=false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
TiptopField::~TiptopField()
|
||
{
|
||
if(m_value!=NULL) free(m_value);
|
||
if(m_idx!=NULL) delete m_idx;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//value - данные (не забирает во владения)
|
||
//size - Размер
|
||
void TiptopField::setValue(void* value,uint4 size)
|
||
{
|
||
if(m_value!=NULL)
|
||
{
|
||
free(m_value);
|
||
m_value=NULL;
|
||
}
|
||
m_size=size;
|
||
if(size==0) return;
|
||
m_value=malloc(m_size);
|
||
//copy data
|
||
for(uint4 i=0; i<m_size; i++) ((char*)m_value)[i]=((char*)value)[i];
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Читаем из потока
|
||
void TiptopField::setValue(TiptopStream* is,uint4 size)
|
||
{
|
||
if(m_value!=NULL)
|
||
{
|
||
free(m_value);
|
||
m_value=NULL;
|
||
}
|
||
m_size=size;
|
||
if(size==0) return;
|
||
m_value=malloc(m_size);
|
||
//read data
|
||
is->Read(m_value,size);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
void TiptopField::setValue(int value)
|
||
{
|
||
setValue(&value,4);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
void TiptopField::setValue(bool value)
|
||
{
|
||
setValue(&value,1);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
void TiptopField::setValue(double value)
|
||
{
|
||
setValue(&value,8);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
void TiptopField::setValue(wxString value)
|
||
{
|
||
const wxCharBuffer buff = value.mb_str(wxConvUTF8);
|
||
setValue((void*)buff.data(),strlen(buff));
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
uint4 TiptopField::getUint4Val()
|
||
{
|
||
return *((uint4*)m_value);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
int4 TiptopField::getIntVal()
|
||
{
|
||
return *((int4*)m_value);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
double TiptopField::getDblVal()
|
||
{
|
||
return *((double*)m_value);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Клонировать узел
|
||
TiptopField* TiptopField::cloneNode()
|
||
{
|
||
TiptopField* f=new TiptopField(NULL);
|
||
f->m_type_id=m_type_id;
|
||
f->m_size=m_size;
|
||
f->m_value=malloc(m_size);
|
||
memcpy(f->m_value,m_value,m_size);
|
||
return f;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Размер данных с учётом размера под количество байт
|
||
uint4 TiptopField::getAllSize()
|
||
{
|
||
uint4 size=m_size;
|
||
switch(m_type_id)
|
||
{
|
||
case BD_UTF8_1:
|
||
size+=1;
|
||
break;
|
||
case BD_UTF8_2:
|
||
size+=2;
|
||
break;
|
||
case BD_GPS_8:
|
||
size+=4;
|
||
break;
|
||
case BD_GPSS_8:
|
||
size+=4;
|
||
break;
|
||
case BD_BLOB_4:
|
||
size+=4;
|
||
break;
|
||
}
|
||
return size;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
bool TiptopField::getBoolVal()
|
||
{
|
||
return *((bool*)m_value);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Преобразовать данные в строку
|
||
wxString TiptopField::getStrVal()
|
||
{
|
||
if(m_value==NULL)
|
||
return _T(""); //Этого недолжно происходить
|
||
|
||
if(m_type_id==BD_BOOL)
|
||
{
|
||
if(*((uint1*)m_value)==0) return _T("false");
|
||
else return _T("true");
|
||
}
|
||
else if(m_type_id==BD_UINT1)
|
||
return IntToStr(*((uint1*)m_value));
|
||
else if(m_type_id==BD_UINT2)
|
||
return IntToStr(*((uint2*)m_value));
|
||
else if(m_type_id==BD_UINT4)
|
||
return IntToStr(*((uint4*)m_value));
|
||
else if(m_type_id==BD_INT4)
|
||
return IntToStr(*((int4*)m_value));
|
||
else if(m_type_id==BD_FLOAT4)
|
||
return FloatToStr(*((float*)m_value),-1);
|
||
else if(m_type_id==BD_FLOAT8)
|
||
return DoubleToStr(*((double*)m_value),-1);
|
||
else if(m_type_id==BD_UTF8_1 || m_type_id==BD_UTF8_2)
|
||
{
|
||
return wxString::FromUTF8((char*)m_value, m_size);
|
||
}
|
||
return _T("");
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Преобразовать данные в строку в соответствии с типом для вставки в SQL
|
||
wxString TiptopField::getStrSQLVal()
|
||
{
|
||
wxString val=getStrVal();
|
||
switch(m_type_id)
|
||
{
|
||
case BD_BOOL:
|
||
case BD_UINT1:
|
||
case BD_UINT2:
|
||
case BD_UINT4:
|
||
case BD_UINT8:
|
||
case BD_INT1:
|
||
case BD_INT2:
|
||
case BD_INT4:
|
||
case BD_INT8:
|
||
case BD_FLOAT4:
|
||
case BD_FLOAT8:
|
||
if(val==_T("")) val=_T("NULL");
|
||
replaseChars(val,_T(','),_T('.')); //Разделитель целой и дробной части точка
|
||
break;
|
||
case BD_UTF8_1:
|
||
case BD_UTF8_2:
|
||
if(val==_T(""))
|
||
{
|
||
val=_T("NULL");
|
||
}
|
||
else
|
||
{
|
||
//$v=str_replace('\'','\\\'',$v); //так как в SQL строку вставляется
|
||
val=_T("'")+val+_T("'");
|
||
}
|
||
break;
|
||
default:
|
||
val=_T("'")+val+_T("'");
|
||
break;
|
||
}
|
||
return val;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//------------------------------------------------------------------------------
|
||
//Конструктор таблицы базы данных
|
||
//bd - База данных
|
||
//id - id таблицы
|
||
TiptopTable::TiptopTable(TiptopBD* bd,uint4 id)
|
||
{
|
||
m_bd=bd; //База данных
|
||
m_id=id; //id таблицы (Либо запроса если таблица используется для передачи данных)
|
||
m_Null=NULL;//байты для чтения NULL значений
|
||
m_NullSize=0;//количество байт для NULL, = cell(count fields/8)
|
||
m_Type=0; //По умолчанию плотная таблица
|
||
|
||
//m_is=NULL;
|
||
//m_os=NULL;
|
||
|
||
//m_fos=NULL;
|
||
m_file=NULL;
|
||
m_stream=new TiptopStream();
|
||
|
||
//m_maxid=0; //максимальный id для поля (Теперь по позиции)
|
||
//m_ok=true;
|
||
|
||
fields=new TSimpleList<TiptopField*>(10,true);
|
||
m_count=0; //Кол-во записей
|
||
|
||
|
||
m_IndexFreeSize=new TiptopIndexFreeSize();
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
TiptopTable::~TiptopTable()
|
||
{
|
||
delete fields;
|
||
if(m_file!=NULL) delete m_file; //TODO удалить через потоки надо делать
|
||
|
||
//if(m_is!=NULL) delete m_is;
|
||
//if(m_osOwner && m_os!=NULL) delete m_os;
|
||
delete m_stream;
|
||
|
||
if(m_Null!=NULL)delete m_Null;
|
||
|
||
delete m_IndexFreeSize;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Добавить поле к таблице становится владельцем
|
||
bool TiptopTable::AddField(TiptopField* fl)
|
||
{
|
||
if(fields->count()>255) return false;
|
||
fields->add(fl);
|
||
m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
|
||
if(m_Null!=NULL) delete m_Null;
|
||
m_Null=new uint1[m_NullSize];//байты для чтения NULL значений
|
||
for(uint1 i=0; i<m_NullSize; i++) m_Null[i]=0;
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Добавить поле к таблице становится владельцем
|
||
TiptopField* TiptopTable::AddField(wxString name,uint1 type_id)
|
||
{
|
||
TiptopField* fl=new TiptopField(this);
|
||
fl->m_name=name;
|
||
fl->m_type_id=type_id;
|
||
AddField(fl);
|
||
return fl;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Добавить поле к таблице + преобразование типа из текста
|
||
TiptopField* TiptopTable::AddField(wxString name,wxString type)
|
||
{
|
||
uint1 type_id=0;
|
||
//Типы данных из metadata.xml
|
||
if(type==_T("b")) type_id=BD_BOOL;
|
||
if(type==_T("i1")) type_id=BD_INT1;
|
||
if(type==_T("i4") || type==_T("object")) type_id=BD_INT4;
|
||
if(type==_T("i8")) type_id=BD_INT8;
|
||
if(type==_T("f4")) type_id=BD_FLOAT4;
|
||
if(type==_T("f8")) type_id=BD_FLOAT8;
|
||
if(type==_T("string")) type_id=BD_UTF8_2; //max 65535 символов
|
||
|
||
return AddField(name,type_id);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать таблицу из текущего открытого потока
|
||
bool TiptopTable::OpenTable()
|
||
{
|
||
//if(!is->IsOk()) return false;
|
||
|
||
uint4 i;
|
||
uint2 s;
|
||
uint1 count;
|
||
//*****читаем заголовок таблицы*****
|
||
m_stream->Read(&s,2);
|
||
if(s!=file_id) return false; //id файла
|
||
m_stream->Read(&s,2);
|
||
if(s!=file_version) return false; //версия файла
|
||
m_stream->Read(&m_id,4); //id файла (или запроса данных)
|
||
m_stream->Read(&m_Type,1); //Тип таблицы 0-"плотная" или 1-"жидкая"
|
||
m_name=loadUTF8StringBD(m_stream); //название таблицы
|
||
m_stream->Read(&count,1); //количество полей (столбцов)
|
||
for(i=0; i<count; i++)
|
||
{
|
||
TiptopField* tf=new TiptopField(this);
|
||
tf->m_name=loadUTF8StringBD(m_stream); //название поля
|
||
m_stream->Read(&tf->m_type_id,1);//id типа поля
|
||
fields->add(tf);
|
||
}
|
||
m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
|
||
m_Null=new uint1[m_NullSize];//байты для чтения NULL значений
|
||
|
||
return true;
|
||
|
||
/*
|
||
wxString path=m_bd->m_path+IntToStr(m_id)+_T(".t"); //по id ищем файл в этой же папке и считываем настройки
|
||
if(wxFileExists(path))
|
||
{
|
||
m_is=new wxFileInputStream(path);
|
||
//wxFileOutputStream* m_fos=new wxFileOutputStream(path);
|
||
//m_file=new wxFFile(path,_T("a+b"));
|
||
//m_file->Seek(0,wxFromStart);
|
||
|
||
OpenTable(m_is);
|
||
|
||
}else return false;
|
||
return true;
|
||
*/
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать из файла (создаётся поток файл всегда открыт)
|
||
bool TiptopTable::OpenTable(wxString path)
|
||
{
|
||
if(!wxFileExists(path)) return false;
|
||
wxFileInputStream* is=new wxFileInputStream(path);
|
||
|
||
//Читаем в память (TODO надо сделать работу с файлом)
|
||
m_stream->Clear();
|
||
m_stream->Write(is);
|
||
delete is;
|
||
return OpenTable();
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать заголовок таблицы из потока
|
||
///\param is - Откуда читаем данне (не становится владельцем)
|
||
/*bool TiptopTable::OpenTable(wxInputStream* is)
|
||
{
|
||
if(!is->IsOk()) return false;
|
||
|
||
uint4 i; uint2 s;
|
||
uint1 count;
|
||
//*****читаем заголовок таблицы*****
|
||
is->Read(&s,2); if(s!=file_id) return false; //id файла
|
||
is->Read(&s,2); if(s!=file_version) return false; //версия файла
|
||
is->Read(&m_id,4); //id файла (или запроса данных)
|
||
is->Read(&m_Type,1); //Тип таблицы 0-"плотная" или 1-"жидкая"
|
||
m_name=loadUTF8String(is); //название таблицы
|
||
is->Read(&count,1); //количество полей (столбцов)
|
||
for(i=0;i<count;i++)
|
||
{
|
||
TiptopField* tf=new TiptopField(this);
|
||
tf->m_name=loadUTF8String(is); //название поля
|
||
is->Read(&tf->m_type_id,1);//id типа поля
|
||
fields->add(tf);
|
||
}
|
||
m_NullSize=ceil(fields->count()/8.0f); //подсчитываем кол-во байтов нужных для NULL значений
|
||
m_Null=new uint1[m_NullSize];//байты для чтения NULL значений
|
||
|
||
return true;
|
||
}*/
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать таблицу из потока в оперативную память
|
||
///\param is - Содержит заголовок и данные (не становится вледельцем)
|
||
bool TiptopTable::ReadTableToMem(wxInputStream* is)
|
||
{
|
||
if(is==NULL) return false;
|
||
|
||
m_stream->Clear(); //Очищяем память
|
||
m_stream->SeekWrite(0);
|
||
m_stream->Write(is);
|
||
|
||
return OpenTable(); //Читаем заголовок
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Переместить курсор чтения на первую запись (чтоб потом можно было последовательно прочитать всю таблицу)
|
||
bool TiptopTable::SeekToStart()
|
||
{
|
||
if(m_Pos.count()<1) return false;
|
||
m_stream->SeekRead(m_Pos[0]);
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
/*bool TiptopTable::SetOutputStream(wxMemoryOutputStream* os, bool owner)
|
||
{
|
||
if(m_osOwner && m_os!=NULL) delete m_os;
|
||
m_os=os;
|
||
m_osOwner=owner;
|
||
return true;
|
||
}*/
|
||
//------------------------------------------------------------------------------
|
||
//Найти и прочитать запись с заданным значением (использую для поска по идентификатору поля)
|
||
//field - Название поля
|
||
//val - Значение поля
|
||
bool TiptopTable::ReadRecord(wxString field, uint4 val)
|
||
{
|
||
TiptopField* fl=getField(field);
|
||
if(fl && fl->m_idx)
|
||
{
|
||
//uint4 pos=fl->m_idx->getPos(val);
|
||
uint4 pos;
|
||
int4 num;
|
||
return fl->m_idx->getPos(val,pos,num);
|
||
//return ReadRecord(pos);
|
||
}
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//is - Уже установлен на позицию с которой надо читать запись
|
||
bool TiptopTable::ReadRecord(TiptopStream* is)
|
||
{
|
||
//if(is==NULL || !is->CanRead() || is->Eof()) return false;
|
||
uint4 size=0; //Сколько байт прочитали из потока
|
||
uint4 pos=0; //Позиция начала записи
|
||
|
||
pos=is->TellRead();
|
||
if(pos>=is->GetSize()) return false;
|
||
|
||
is->Read(m_Null,m_NullSize);
|
||
size+=m_NullSize;//is->LastRead(); //Размер под NULL
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
if(testBit(m_Null,i)) //Если есть данные то читаем
|
||
{
|
||
TiptopField* fl=fields->get(i);
|
||
|
||
if(fl->m_type_id==BD_UINT1 || fl->m_type_id==BD_BOOL) //0
|
||
{
|
||
fl->setValue(is,1);
|
||
size+=1;
|
||
}
|
||
else if(fl->m_type_id==BD_UINT4) //3
|
||
{
|
||
fl->setValue(is,4);
|
||
size+=4;
|
||
}
|
||
else if(fl->m_type_id==BD_INT4) //13
|
||
{
|
||
fl->setValue(is,4);
|
||
size+=4;
|
||
}
|
||
else if(fl->m_type_id==BD_INT8) //17
|
||
{
|
||
fl->setValue(is,8);
|
||
size+=8;
|
||
}
|
||
else if(fl->m_type_id==BD_FLOAT4) //20
|
||
{
|
||
fl->setValue(is,4);
|
||
size+=4;
|
||
}
|
||
else if(fl->m_type_id==BD_FLOAT8) //22
|
||
{
|
||
fl->setValue(is,8);
|
||
size+=8;
|
||
}
|
||
else if(fl->m_type_id==BD_UTF8_1) //Не более 255 байт
|
||
{
|
||
uint1 i;
|
||
is->Read(&i,1);
|
||
fl->setValue(is,i);
|
||
size+=1+i;
|
||
}
|
||
else if(fl->m_type_id==BD_UTF8_2) //Не более 65535 байт
|
||
{
|
||
uint2 i;
|
||
is->Read(&i,2);
|
||
fl->setValue(is,i);
|
||
size+=2+i;
|
||
}
|
||
else if(fl->m_type_id==BD_GPS_8) //131
|
||
{
|
||
uint4 i;
|
||
is->Read(&i,4);
|
||
fl->setValue(is,i);
|
||
size+=4+i;
|
||
}
|
||
else if(fl->m_type_id==BD_GPSS_8) //132 как двоичные данные
|
||
{
|
||
uint4 i;
|
||
is->Read(&i,4);
|
||
fl->setValue(is,i);
|
||
size+=4+i;
|
||
}
|
||
else if(fl->m_type_id==BD_BLOB_4) //143
|
||
{
|
||
uint4 i;
|
||
is->Read(&i,4);
|
||
fl->setValue(is,i);
|
||
size+=4+i;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
else
|
||
fields->get(i)->setValue((void*)NULL,0); //NULL запись
|
||
}
|
||
|
||
if(size>0)
|
||
{
|
||
m_RecSize = size;
|
||
m_RecPos = pos;
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Перемещяем курсор в заданную позицию и читаем запись
|
||
//pos - позиция в файле если -1 то не смещяемся
|
||
bool TiptopTable::ReadRecord(int pos)
|
||
{
|
||
if(pos>0)
|
||
m_stream->SeekRead(pos);
|
||
return ReadRecord(m_stream);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать следующую запись из таблицы
|
||
//savePos - Сохранять ли позицию записи при чтении
|
||
bool TiptopTable::ReadNextRecord(bool savePos)
|
||
{
|
||
if(m_Type!=0) return false; //Пока только для плотной таблицы те. записи идут подподрят
|
||
|
||
wxFileOffset ff=m_stream->TellRead(); //Начальная позиция записи
|
||
|
||
if(ReadRecord(-1))
|
||
{
|
||
if(savePos) m_Pos.add(ff); //Для плотной таблицы нет индекса поэтому пишем позицию записи в файле
|
||
m_count++; //Количество записей
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Прочитать запись по её порядковому номеру (Если списка позиций нет то последовательно читать с начала (TODO и создаёт этот список))
|
||
//num - Номер записи нумерация с 0
|
||
bool TiptopTable::ReadRecordByNum(uint4 num)
|
||
{
|
||
if(num<m_Pos.count())
|
||
return ReadRecord(m_Pos[num]);
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Записать заголовок таблицы в поток m_os
|
||
bool TiptopTable::WriteHeader()
|
||
{
|
||
//if(m_os==NULL || !m_os->IsOk()) return false;
|
||
m_stream->Write(&file_id,2);
|
||
m_stream->Write(&file_version,2);
|
||
m_stream->Write(&m_id,4);
|
||
uint1 t=0;
|
||
m_stream->Write(&t,1); //Протная таблица
|
||
saveUTF8StringBD(m_stream,m_name); //max 256 байт
|
||
t=fields->count();
|
||
m_stream->Write(&t,1); //Не больше 256 столбцов
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
saveUTF8StringBD(m_stream,fields->get(i)->m_name);
|
||
m_stream->Write(&fields->get(i)->m_type_id,1);
|
||
}
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Записать 1 запись в m_os из полей
|
||
//savePos - сохранять позицию записи
|
||
//findPos - Искать свободное место в потоке, если нет то надо установить курсор в нужное место заранее.
|
||
bool TiptopTable::WriteRecord(bool savePos, bool findPos)
|
||
{
|
||
uint4 newSize=0;
|
||
uint4 newPos=0;
|
||
|
||
if(findPos) //Потому что некоторые потоки не умеют выполнять Seek
|
||
{
|
||
//подсчитываем размер (потом сделать автоматический подсчёт при обновлении полей)
|
||
newSize=m_NullSize;
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
newSize+=fields->get(i)->getAllSize();
|
||
}
|
||
|
||
newPos = findFreeSize(newSize); //Находим пустое место куда можно записать данные заданного размера (обычно в конец потока)
|
||
m_stream->SeekWrite(newPos);
|
||
}
|
||
if(savePos) addRecPos(m_stream->TellWrite()); //Сохраняем адрес в потоке для текущей записи
|
||
|
||
if(!WriteRecord()) return false;
|
||
|
||
//закончили запись в поток отмечаем что место не пустое в индексе пустых мест
|
||
if(findPos)
|
||
m_IndexFreeSize->upd(newPos,newSize);
|
||
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Записать запись из полей в текущее положение потока m_os
|
||
bool TiptopTable::WriteRecord()
|
||
{
|
||
//if(m_os==NULL || !m_os->IsOk() || m_Null==NULL) return false;
|
||
|
||
//Записываем NULL значения
|
||
for(uint1 i=0; i<m_NullSize; i++) m_Null[i]=0;
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
if(fields->get(i)->m_value==NULL || fields->get(i)->m_size==0)
|
||
setBit(m_Null,i,false);
|
||
else
|
||
setBit(m_Null,i,true);
|
||
}
|
||
|
||
m_stream->Write(m_Null,m_NullSize);
|
||
//if(m_stream->LastWrite()!=m_NullSize) return false;
|
||
|
||
//Записываем сами данные
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
if(fields->get(i)->m_value!=NULL && fields->get(i)->m_size!=0)
|
||
{
|
||
switch(fields->get(i)->m_type_id) //Так как у разных типов своя структура.
|
||
{
|
||
case BD_UTF8_1:
|
||
{
|
||
uint1 ch=fields->get(i)->m_size;
|
||
m_stream->Write(&ch,1);
|
||
m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
|
||
}
|
||
break;
|
||
case BD_UTF8_2:
|
||
{
|
||
uint2 ch=fields->get(i)->m_size;
|
||
m_stream->Write(&ch,2);
|
||
m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
|
||
}
|
||
break;
|
||
case BD_BLOB_4:
|
||
{
|
||
uint4 ch=fields->get(i)->m_size;
|
||
m_stream->Write(&ch,4);
|
||
m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
|
||
}
|
||
break;
|
||
default: //Для типов которым ненадо записывать размер
|
||
m_stream->Write(fields->get(i)->m_value,fields->get(i)->m_size);
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Обновить(удалить потом записать) 1 запись перед удалением запись должна быть прочитана для выяснения её позиции и размера.
|
||
bool TiptopTable::UpdateRecord()
|
||
{
|
||
|
||
//if(m_os==NULL || !m_os->IsOk() || m_Null==NULL) return false;
|
||
|
||
//if(m_noindex) return false; //Проверяем есть ли индекс позиций для всей таблицы
|
||
|
||
//Подсчитываем размер новой записи если она равна старой то записываем в туже позицию
|
||
uint4 newPos=m_RecPos;
|
||
uint4 newSize=m_NullSize;
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
newSize+=fields->get(i)->getAllSize();
|
||
}
|
||
|
||
if(newSize==m_RecSize) //Позиция и размер записи не меняется
|
||
{
|
||
m_stream->SeekWrite(m_RecPos);
|
||
WriteRecord();
|
||
}
|
||
else
|
||
{
|
||
m_IndexFreeSize->add(m_RecPos, m_RecSize); //Добавляем пустое место в список
|
||
m_Type=1; //Ставим признак не плотной таблицы
|
||
newPos = findFreeSize(newSize); //Находим пустое место куда можно записать данные заданного размера (обычно в конец потока)
|
||
m_stream->SeekWrite(newPos);
|
||
|
||
if(WriteRecord())
|
||
{
|
||
unsigned int p;
|
||
if(m_Pos.Position(m_RecPos,p))
|
||
m_Pos.get(p)=newPos; //Обновляем позицию записи
|
||
m_IndexFreeSize->upd(newPos,newSize); //Обновляем размер
|
||
}
|
||
}
|
||
return true;
|
||
// return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
bool TiptopTable::delRecPos(uint8 pos) //Удалить позицию записи в памяти
|
||
{
|
||
unsigned int p;
|
||
if(m_Pos.Position(pos,p))
|
||
{
|
||
m_Pos.del(p);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
//return false;
|
||
};
|
||
//------------------------------------------------------------------------------
|
||
/*bool TiptopTable::isOk()
|
||
{ return m_ok;
|
||
}*/
|
||
//------------------------------------------------------------------------------
|
||
//получить поле по имени
|
||
TiptopField* TiptopTable::getField(wxString name)
|
||
{
|
||
name.Trim(true);
|
||
name.Trim(false); //удалить пробелы с права и с лева
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
if(fields->get(i)->m_name==name) return fields->get(i);
|
||
return NULL;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Добавить записи из заданной в текущую (поля должны совпадать по имени и по типу)
|
||
//Начинает читать со следующей позиции из tb без сохранения позиции
|
||
//tb - Таблица с которой читаем записи
|
||
//spw - сохранять ли позицию при записи
|
||
//spr - сохранять ли позицию при чтении
|
||
bool TiptopTable::AppendTable(TiptopTable* tb, bool spw, bool spr)
|
||
{
|
||
if(tb==NULL) return false;
|
||
bool result=true;
|
||
|
||
int* fi0=new int[fields->count()]; //Позиции полей в локальной таблице
|
||
int* fi1=new int[fields->count()]; //Позиции полей в присваиваемой таблице
|
||
int pos=0; //Сколько нашли полей
|
||
for(unsigned int i=0; i<fields->count(); i++)
|
||
{
|
||
for(unsigned int j=0; j<tb->fields->count(); j++)
|
||
{
|
||
if(fields->get(i)->m_name==tb->fields->get(j)->m_name && fields->get(i)->m_type_id == tb->fields->get(j)->m_type_id)
|
||
{
|
||
fi0[pos]=i;
|
||
fi1[pos]=j;
|
||
pos++;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
//Обнулим чтоб в данные не попал мусор
|
||
for(unsigned int i=0; i<fields->count(); i++)
|
||
fields->get(i)->setValue((void*)NULL,0);
|
||
//Ищем соответствующие поля
|
||
if(pos>0)
|
||
{
|
||
while(tb->ReadNextRecord(spr)) //При считывании сохраняем позицию записи
|
||
{
|
||
for(int i=0; i<pos; i++)
|
||
{
|
||
fields->get(fi0[i])->setValue(tb->fields->get(fi1[i])->m_value,tb->fields->get(fi1[i])->m_size);
|
||
}
|
||
if(!WriteRecord(spw,true))
|
||
{
|
||
result=false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
delete fi0;
|
||
delete fi1;
|
||
return result;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Обновить записи из заданной в текущую (поля должны совпадать по имени и по типу)
|
||
//tb - Таблица с которой читаем записи
|
||
bool TiptopTable::UpdateTable(TiptopTable* tb)
|
||
{
|
||
if(tb==NULL) return false;
|
||
bool result=true;
|
||
|
||
//строим табличку соответствия номеров одинаковых полей
|
||
int* fi0=new int[fields->count()]; //Позиции полей в локальной таблице
|
||
int* fi1=new int[fields->count()]; //Позиции полей в присваиваемой таблице
|
||
unsigned int cnt=0; //Сколько нашли одинаковых полей
|
||
for(unsigned int i=0; i<fields->count(); i++)
|
||
{
|
||
for(unsigned int j=0; j<tb->fields->count(); j++)
|
||
{
|
||
if(fields->get(i)->m_name==tb->fields->get(j)->m_name && fields->get(i)->m_type_id == tb->fields->get(j)->m_type_id)
|
||
{
|
||
fi0[cnt]=i;
|
||
fi1[cnt]=j;
|
||
cnt++;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//Обновляем записи (пока по простому по равенству с полем id потом надо передавать условия)
|
||
if(cnt>0)
|
||
{
|
||
buildPosIndex(); //Строим индекс если ещё не создан
|
||
|
||
tb->SeekToStart();
|
||
while(tb->ReadNextRecord(true)) //При считывании сохраняем позицию записи
|
||
{
|
||
//Ищем и читаем локальную запись
|
||
uint4 pos,num;
|
||
if(FindFirstRecord(tb->getField(_T("id")),pos,num))
|
||
{
|
||
//Переписываем поля
|
||
for(unsigned int i=0; i<cnt; i++)
|
||
{
|
||
fields->get(fi0[i])->setValue(tb->fields->get(fi1[i])->m_value,tb->fields->get(fi1[i])->m_size);
|
||
}
|
||
//Записываем в память
|
||
if(!UpdateRecord())
|
||
{
|
||
result=false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
delete fi0;
|
||
delete fi1;
|
||
return result;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Построить индекс позиций прочитав таблицу он начала до конца (только для плотной таблицы)
|
||
bool TiptopTable::buildPosIndex()
|
||
{
|
||
if(m_Type!=0) return false; //только для плотной таблицы
|
||
if(m_Pos.count()>0) ReadRecord(m_Pos.get(m_Pos.count()-1)); //Если есть хоть 1 позиция в списке позиций то перемещяемся на неё
|
||
while(ReadNextRecord(true)) {} //В цикле читаем оставшиеся и запоминаем позиции
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//создать таблицу по SQL запросу
|
||
bool TiptopTable::CreateTable(wxString SQL)
|
||
{
|
||
m_name=getAfterLast(getBeforeFirst(SQL,_T("(")),' ');
|
||
if(m_bd!=NULL && m_bd->TableExist(m_name)) return false;
|
||
//добавляем поля по умолчанию id uint4,seq uint4,del uint1
|
||
TiptopField* field;
|
||
field=new TiptopField(this);
|
||
//field->m_id=++m_maxid; //id поля
|
||
field->m_name=_T("id"); //название поля
|
||
field->m_type_id=BD_UINT4; //id типа данных
|
||
field->m_NULL=false; //Может ли быть NULL
|
||
field->m_index=true; //Нуждается ли в индексации
|
||
field->m_idx=new TiptopIndex(field); //Индекс поля (пока только для поля id)
|
||
fields->add(field);
|
||
|
||
field=new TiptopField(this);
|
||
//field->m_id=++m_maxid; //id поля
|
||
field->m_name=_T("seq"); //название поля
|
||
field->m_type_id=BD_UINT4; //id типа данных
|
||
field->m_NULL=false;
|
||
fields->add(field);
|
||
|
||
field=new TiptopField(this);
|
||
//field->m_id=++m_maxid; //id поля
|
||
field->m_name=_T("del"); //название поля
|
||
field->m_type_id=BD_UINT1; //id типа данных
|
||
field->m_NULL=false;
|
||
fields->add(field);
|
||
|
||
//перебираем поля в SQL запросе и добавляем к списку
|
||
//CREATE TABLE tablename(id uint1,data uint2,data3 double);
|
||
wxString fields=getBeforeLast(getAfterFirst(SQL,_T("(")),')')+_T(",");
|
||
while (fields!=_T(""))
|
||
{
|
||
wxString sField=getBeforeFirst(fields,_T(","));
|
||
sField.Trim(true);
|
||
sField.Trim(false); //удалить пробелы с права и с лева
|
||
wxString sName=getBeforeFirst(sField,_T(" "));
|
||
wxString sType=getAfterLast(sField,' ');
|
||
|
||
field=new TiptopField(this);
|
||
field->m_type_id=0;
|
||
field->m_NULL=true;
|
||
field->m_index=false;
|
||
field->m_name=sName; //название поля
|
||
if(sType.Lower()==_T("uint1"))
|
||
field->m_type_id=BD_UINT1; //id типа данных
|
||
else if(sType.Lower()==_T("uint2"))
|
||
field->m_type_id=BD_UINT2; //id типа данных
|
||
else if(sType.Lower()==_T("uint4"))
|
||
field->m_type_id=BD_UINT4; //id типа данных
|
||
else if(sType.Lower()==_T("utf8_1"))
|
||
field->m_type_id=BD_UTF8_1; //id типа данных
|
||
else if(sType.Lower()==_T("gps_8"))
|
||
field->m_type_id=BD_GPS_8; //id типа данных
|
||
else if(sType.Lower()==_T("gpss_8"))
|
||
field->m_type_id=BD_GPSS_8; //id типа данных
|
||
|
||
if(field->m_type_id!=0)
|
||
{
|
||
//field->m_id=++m_maxid; //id поля
|
||
this->fields->add(field);
|
||
}
|
||
else
|
||
delete field;
|
||
|
||
fields=getAfterFirst(fields,_T(","));
|
||
}
|
||
//байты для чтения NULL значений
|
||
m_NullSize=ceil(this->fields->count()/8.0f);
|
||
m_Null=new uint1[m_NullSize];
|
||
|
||
//создаём файл таблицы по собранным данным
|
||
m_file=new wxFFile(m_bd->m_path+IntToStr(m_id)+_T(".t"),_T("a+b"));
|
||
m_file->Seek(0,wxFromStart);
|
||
|
||
uint1 i=0,t=0;
|
||
m_file->Write(&file_id,4);
|
||
m_file->Write(&file_version,4);
|
||
m_file->Write(&t,1); //тип таблицы 0=системный, 1=пользовательский,...
|
||
m_file->Write(&i,4); //резерв
|
||
saveUTF8String(m_file,m_name);
|
||
m_file->Write(&i,4); //время последнего обновления таблицы секунд с 2000 года
|
||
m_file->Write(&i,4); //резерв
|
||
//m_file->Write(&m_maxid,4); //max id поля
|
||
uint4 count=this->fields->count();
|
||
m_file->Write(&count,4);
|
||
for(uint4 i=0; i<count; i++)
|
||
{
|
||
TiptopField* tf=this->fields->get(i);
|
||
|
||
//m_file->Write(&tf->m_id,4); //id поля
|
||
saveUTF8String(m_file,tf->m_name); //название
|
||
m_file->Write(&tf->m_type_id,4); //id типа данных
|
||
m_file->Write(&tf->m_NULL,1); //Может ли быть пустым
|
||
m_file->Read(&tf->m_index,1); //Нужен ли индекс для этого поля (резерв)
|
||
}
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//добавить запись в таблицу а если есть индексы то и в индексы
|
||
//INSERT INTO test(id,seq,del,name)values(1,2,0,\"Проба"" текста\");
|
||
bool TiptopTable::InsertInto(wxString SQL)
|
||
{
|
||
for(uint4 i=0; i<fields->count(); i++) fields->get(i)->setValue((void*)NULL,0); //обнуляем список столбцов
|
||
|
||
wxString sFields=getBeforeFirst(getAfterFirst(SQL,_T("(")),_T(")"))+_T(","); //название полей через запятую
|
||
wxString sValues=getBeforeLast(getAfterFirst(getAfterFirst(SQL,_T(")")),_T("(")),')')+_T(","); //значения через запятую
|
||
while(sFields!=_T(""))
|
||
{
|
||
wxString name=cutFirstSubStr(sFields,_T(','));
|
||
wxString value=_T("");
|
||
TiptopField* fld=getField(name);
|
||
if(fld==NULL) return false;
|
||
//кроме текстового поля значения выбираются до запятой
|
||
if(fld->m_type_id==BD_UTF8_1)
|
||
{
|
||
value=cutFirstSubStr(sValues,_T(',')); //TODO изменить только для тестирования (сейчас в тексте не должно быть запятых)
|
||
value=_T("_")+value.SubString(1,value.length()-2); //1й символ размер строки
|
||
|
||
wxCharBuffer buff = value.mb_str(wxConvUTF8);
|
||
uint1 len=(uint1)strlen(buff);
|
||
buff.data()[0]=len-1; //первый символ размер строки
|
||
fld->setValue(buff.data(),len);
|
||
}
|
||
else if(fld->m_type_id==BD_UINT1)
|
||
{
|
||
value=cutFirstSubStr(sValues,_T(','));
|
||
|
||
uint1 v_uint=StrToInt(value);
|
||
fld->setValue(&v_uint,1);
|
||
}
|
||
else if(fld->m_type_id==BD_UINT4)
|
||
{
|
||
value=cutFirstSubStr(sValues,_T(','));
|
||
|
||
uint4 v_uint=StrToInt(value);
|
||
fld->setValue(&v_uint,4);
|
||
}
|
||
else if((fld->m_type_id==BD_GPS_4)||(fld->m_type_id==BD_GPS_8)||(fld->m_type_id==BD_GPSS_8)||(fld->m_type_id==BD_BLOB_4)) //если это массив байт то в качестве значения передаётся размер для выделения памяти
|
||
{
|
||
value=cutFirstSubStr(sValues,_T(','));
|
||
|
||
uint4 v_uint=StrToInt(value);
|
||
if(v_uint>0) fld->setValue(&v_uint,4);
|
||
}
|
||
}
|
||
|
||
//все данные подготовленны теперь ищется свободное место в файле для записи данных
|
||
uint4 size=m_NullSize; //байт под NULL
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
{
|
||
if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4))
|
||
{
|
||
uint4 s=*(uint4*)fields->get(i)->m_value;
|
||
size+=s+4; //размер + место под размер
|
||
}
|
||
else
|
||
size+=fields->get(i)->getSize();
|
||
}
|
||
uint4 pos=findFreeSize(size); //позиция в файле куда можно записать данную запись
|
||
getField(_T("id"))->m_idx->add(pos,getField(_T("id"))->getUint4Val()); //сохраняем в индексе позицию
|
||
|
||
m_file->Seek(pos,wxFromStart);
|
||
for(uint4 i=0; i<fields->count(); i++) setBit(m_Null,i,(fields->get(i)->m_size!=0));
|
||
m_file->Write(m_Null,m_NullSize);
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
if(fields->get(i)->m_size>0)
|
||
{
|
||
m_file->Write(fields->get(i)->m_value,fields->get(i)->m_size); //пишем данные
|
||
//если это даные переменной длины то записываем только размер а курсор перемещяем в конец данных
|
||
if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4))
|
||
{
|
||
uint4 s=*(uint4*)fields->get(i)->m_value;
|
||
m_file->Seek(s,wxFromCurrent);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//найти позицию с свободным местом для записи данных (из индекса свободных мест с начала файла)
|
||
//size - Размер необходимой свободной памяти 0 то макс свободное место
|
||
uint4 TiptopTable::findFreeSize(uint4 size)
|
||
{
|
||
unsigned int pos=0;
|
||
if(m_IndexFreeSize->get(size,pos))
|
||
{
|
||
return pos;
|
||
}
|
||
else
|
||
{
|
||
return m_stream->GetSize(); //Позиция конца файла
|
||
}
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
///<Найти позицию первой попавшейся записи с заданными значениями поля, результат позиция записи в памяти.
|
||
//fl - Поле не пренадлежащие таблице по которому будет искаться запись
|
||
//pos - результат позиция записи в потоке
|
||
//num - результат порядковый номер записи
|
||
bool TiptopTable::FindFirstRecord(TiptopField* fl,uint4 &pos,uint4 &num)
|
||
{
|
||
int4 p=getNumFieldOnName(fl->m_name); //Найти позицию записи с таким же именем
|
||
if(fields->get(p)->m_type_id!=fl->m_type_id) return false; //Несовпали типы полей
|
||
|
||
//Если нету индекса то поиск будет осуществлятся простым перебором
|
||
if(m_Type==0) SeekToStart();
|
||
num=0;
|
||
while(true)
|
||
{
|
||
if(m_Type!=0) //Не плотная таблица обязаны ходить по индексам
|
||
{
|
||
if(num>=m_Pos.count()) return true;
|
||
m_stream->SeekRead(m_Pos[num]);
|
||
}
|
||
|
||
wxFileOffset ff=m_stream->TellRead(); //Начальная позиция записи
|
||
if(!ReadRecord(-1)) return false;
|
||
if(fields->get(p)->m_size==fl->m_size)
|
||
{
|
||
if(memcmp(fields->get(p)->m_value,fl->m_value,fl->m_size)==0)
|
||
{
|
||
pos=ff;
|
||
return true;
|
||
}
|
||
}
|
||
num++;
|
||
}
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
int4 TiptopTable::getNumFieldOnName(wxString name)
|
||
{
|
||
for(uint4 i=0; i<fields->count(); i++)
|
||
if(fields->get(i)->m_name==name) return (int4)i;
|
||
return -1;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Установить курсор в начало BLOB блока
|
||
//TODO надо сделать свой защищённый "поток"
|
||
wxFFile* TiptopTable::getStream(uint4 id,wxString field)
|
||
{
|
||
/* //Через индекстный файл находим позицию первого байта записи по id
|
||
getField(_T("id"))->
|
||
|
||
|
||
//Ищем
|
||
uint4 seek=0;
|
||
m_file->Read(m_Null,m_NullSize);
|
||
int4 num=getNumFieldOnName(field);
|
||
if(!testBit(m_Null,num)) return NULL; //если поле NULL то вернём NULL
|
||
for(uint1 i=0;i<num;i++)
|
||
{
|
||
if(testBit(m_Null,i)) //если не NULL то учитываем размер
|
||
{
|
||
if(fields->get(i)->m_type_id==BD_UTF8_1) //Размер в 1м байте
|
||
{
|
||
m_file->Seek(seek,wxFromCurrent);
|
||
uint1 size=0;
|
||
m_file->Read(&size,1);
|
||
m_file->Seek(size,wxFromCurrent);
|
||
seek=0;
|
||
}else
|
||
if((fields->get(i)->m_type_id==BD_GPS_4)||(fields->get(i)->m_type_id==BD_GPS_8)||(fields->get(i)->m_type_id==BD_GPSS_8)||(fields->get(i)->m_type_id==BD_BLOB_4)) //Размер в 4х байтах
|
||
{
|
||
m_file->Seek(seek,wxFromCurrent);
|
||
uint4 size=0;
|
||
m_file->Read(&size,4);
|
||
m_file->Seek(size,wxFromCurrent);
|
||
seek=0;
|
||
}
|
||
else
|
||
seek+=fields->get(i)->m_size;
|
||
}
|
||
}
|
||
|
||
m_file->Seek(seek,wxFromCurrent);
|
||
*/
|
||
return m_file; //TODO надо как поток вернуть
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Создаёт новый поток в который помещяет таблицу для чтения (TiptopTable не остаётся владельцем)
|
||
/*wxMemoryInputStream* TiptopTable::GetInputStream()
|
||
{
|
||
wxStreamBuffer* osb=m_os->GetOutputStreamBuffer();
|
||
wxMemoryInputStream* mis = new wxMemoryInputStream(osb->GetBufferStart(),osb->GetBufferSize());
|
||
return mis;
|
||
}*/
|
||
//------------------------------------------------------------------------------
|
||
//------------------------------------------------------------------------------
|
||
TiptopBD::TiptopBD()
|
||
{
|
||
m_maxid=0;
|
||
m_fis=NULL;
|
||
m_fos=NULL;
|
||
list=new TSimpleList<TiptopTable*>(10,true);
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
TiptopBD::~TiptopBD()
|
||
{
|
||
delete list;
|
||
delete m_fos;
|
||
delete m_fis;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//задаётся путь к ini файлу в котором список таблиц
|
||
bool TiptopBD::Open(wxString path)
|
||
{
|
||
m_path=path;
|
||
//открываем файл базы и читаем настройки
|
||
if(wxFileExists(path+_T("bd.bd")))
|
||
{
|
||
uint4 i,cnt,id;
|
||
m_fis=new wxFileInputStream(path+_T("bd.bd"));
|
||
m_fos=new wxFileOutputStream(path+_T("bd.bd"));
|
||
m_fis->Read(&i,4);
|
||
if(i!=file_id) goto Exit; //id файла
|
||
m_fis->Read(&i,4);
|
||
if(i!=file_version) goto Exit; //версия файла
|
||
m_fis->Read(&m_maxid,4); //max id таблиц
|
||
m_fis->Read(&cnt,4); //количество таблиц
|
||
for(i=0; i<cnt; i++)
|
||
{
|
||
m_fis->Read(&id,4);//id таблицы
|
||
TiptopTable* tb=new TiptopTable(this,id);
|
||
if(tb->OpenTable()) list->add(tb);
|
||
else delete tb;
|
||
}
|
||
Exit:
|
||
m_ok=false;
|
||
}
|
||
else //создаём новый файл
|
||
{
|
||
if(!wxDirExists(path)) wxMkdir(path);
|
||
m_fos=new wxFileOutputStream(path+_T("bd.bd"));
|
||
m_fis=new wxFileInputStream(path+_T("bd.bd"));
|
||
m_fos->Write(&file_id,4);
|
||
m_fos->Write(&file_version,4);
|
||
uint4 i=0;
|
||
m_fos->Write(&i,4); //max id таблиц
|
||
m_fos->Write(&i,4); //количество таблиц
|
||
}
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//существует ли таблица
|
||
bool TiptopBD::TableExist(wxString name)
|
||
{
|
||
for(uint4 i=0; i<list->count(); i++)
|
||
if(list->get(i)->m_name==name) return true;
|
||
return false;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//взять таблицу
|
||
TiptopTable* TiptopBD::getTable(wxString name)
|
||
{
|
||
for(uint4 i=0; i<list->count(); i++)
|
||
if(list->get(i)->m_name==name) return list->get(i);
|
||
return NULL;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
bool TiptopBD::ExecSQL(wxString SQL) //выполнить SQL запрос без результата
|
||
{
|
||
//TODO анализатор SQL надо сделать по символам
|
||
|
||
//CREATE TABLE tablename(id uint1,data uint2,data3 double);
|
||
//id,seq,del - создаются автоматически
|
||
if(SQL.Find(_T("CREATE TABLE"))!=wxNOT_FOUND)
|
||
{
|
||
TiptopTable* tb=new TiptopTable(this,m_maxid+1);
|
||
if(tb->CreateTable(SQL))
|
||
{
|
||
if(addTable(m_maxid+1)) //Запишем в файл списка таблиц
|
||
list->add(tb);
|
||
}
|
||
else
|
||
{
|
||
delete tb;
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
//INSERT INTO test(id,seq,del,name)values(1,2,0,"Проба текста");
|
||
if(SQL.Find(_T("INSERT INTO"))!=wxNOT_FOUND)
|
||
{
|
||
TiptopTable* tb=getTable(getAfterLast(getBeforeFirst(SQL,_T("(")),' '));
|
||
if(tb!=NULL) tb->InsertInto(SQL);
|
||
else return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Записать id в файл списка таблиц, текущее количество +1
|
||
bool TiptopBD::addTable(uint4 id)
|
||
{
|
||
uint4 cnt,max;
|
||
m_fis->SeekI(8,wxFromStart);
|
||
m_fis->Read(&max,4);
|
||
m_fis->Read(&cnt,4);
|
||
if(max>=id) return false;
|
||
|
||
m_fos->SeekO(8,wxFromStart);
|
||
m_fos->Write(&id,4);
|
||
cnt++;
|
||
m_fos->Write(&cnt,4);
|
||
m_fos->SeekO((cnt-1)*4,wxFromCurrent);
|
||
m_fos->Write(&id,4);
|
||
|
||
m_maxid=id;
|
||
return true;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Загрузить UTF8 строку из файла, строка не больше 256 байт
|
||
wxString loadUTF8String(wxInputStream *is)
|
||
{
|
||
char c;
|
||
is->Read(&c,1);
|
||
char* buf = new char[c];
|
||
is->Read(buf,c);
|
||
wxString str=wxString::FromUTF8(buf,c);
|
||
delete[] buf;
|
||
return str;
|
||
}
|
||
//------------------------------------------------------------------------------
|
||
//Загрузить UTF8 строку из файла, строка не больше 256 байт
|
||
wxString loadUTF8String(wxFFile *is)
|
||
{
|
||
char c;
|
||
is->Read(&c,1);
|
||
char* buf = new char[c];
|
||
is->Read(buf,c);
|
||
wxString str=wxString::FromUTF8(buf,c);
|
||
delete[] buf;
|
||
return str;
|
||
}
|
||
//------------------------------------------------------------------------------
|