//--------------------------------------------------------------------------- #ifndef TIPTOPBD_H_ #define TIPTOPBD_H_ //--------------------------------------------------------------------------- #include #include #include #include //Поток в память #include //Поток буф #include "structs.h" #include "tcDebug.h" //--------------------------------------------------------------------------- typedef unsigned char uint1; typedef unsigned short uint2; typedef unsigned int uint4; typedef unsigned __int64 uint8; typedef char int1; typedef short int2; typedef int int4; typedef __int64 int8; //--------------------------------------------------------------------------- const uint2 file_id=65500;//*((uint2*)"TB");//16980 id файла базы данных и таблицы 2 байта const uint2 file_version=1; //текущая версия файла базы данных 2 байта //--------------------------------------------------------------------------- //типы данных которые можно хранить в базе под них 1 байт #define BD_UINT1 0 //1 байт без знаковый #define BD_UINT2 1 //2 байт без знаковый //#define BD_UINT3 2 //3 байт без знаковый #define BD_UINT4 3 //4 байт без знаковый //#define BD_UINT5 4 //5 байт без знаковый //#define BD_UINT6 5 //6 байт без знаковый //#define BD_UINT7 6 //7 байт без знаковый #define BD_UINT8 7 //8 байт без знаковый #define BD_INT1 10 //1 байт со знаковый #define BD_INT2 11 //2 байт со знаковый //#define BD_INT3 12 //3 байт со знаковый #define BD_INT4 13 //4 байт со знаковый //#define BD_INT5 14 //5 байт со знаковый //#define BD_INT6 15 //6 байт со знаковый //#define BD_INT7 16 //7 байт со знаковый #define BD_INT8 17 //8 байт со знаковый #define BD_FLOAT4 20 //17 - float 4_байта //#define BD_FLOAT6 21 //18 - real 6_байт #define BD_FLOAT8 22 //19 - double 8_байт #define BD_BOOL 30 //1 байт 0-false 1-true #define BD_UTF8_1 100 //100 - utf8_1 string 1й байт размер строки в байтах #define BD_UTF8_2 101 //101 - utf8_2 string 1х 2 байта размер строки в байтах //#define BD_UTF8_4 102 //101 - utf8_2 string 1х 4 байта размер строки в байтах //#define BD_ASCII_1 110 //102 - asci_1 строка 1й байт размер строки в байтах //#define BD_ASCII_2 112 //103 - asci_2 строка 1х 2 байта размер строки в байтах //-#define BD_ASCII_0 120 //Строка с 0 на конце //для этих двоичных объектов в начале передаются не данные а только размер потом обновляют запись по id записи #define BD_GPS_4 130 //250 - GPS коордионаты в формате: uint4 количество байт,float,float (широта lat,долгота lon в WGS-84) #define BD_GPS_8 131 //251 - GPS коордионаты в формате: uint4 количество байт, double,double (широта lat,долгота lon в WGS-84 ) #define BD_GPSS_8 132 //252 - GPS координаты в формате: uint4 количество байт, uint4 количество сегментов, uint4 количество точек в первом сегменте,(double,double точки первого сегмента) //#define BD_BLOB_1 140 //254 - двоичные данные uint1 количество байт //#define BD_BLOB_2 141 //254 - двоичные данные uint2 количество байт //#define BD_BLOB_3 142 //254 - двоичные данные uint3 количество байт #define BD_BLOB_4 143 //254 - двоичные данные uint4 количество байт //#define BD_BLOB_8 144 //255 - двоичные данные uint8 количество байт //--------------------------------------------------------------------------- //"Типа" свой поток для записи в память либо в файл class TiptopStream { private: uint4 m_posRead; //Текущая позиция при записи uint4 m_posWrite; //Текущая позиция при чтении uint4 m_size; //Размер данных //список страниц базы данных unsigned int m_pageSize; //Размер страницы данных TSimpleList* m_pages; //список страниц данных public: TiptopStream(){ m_pageSize=1024;/*4096;*/ m_pages=new TSimpleList(10,true); m_posRead=0; m_posWrite=0; m_size=0; }; ~TiptopStream(){ delete m_pages; }; void Clear() { m_pages->clear(); } uint4 GetSize(){ return m_size; }; void Read(const void* buf, unsigned int size) //Прочитать данные в buffer размером { unsigned char* buffer=(unsigned char*)buf; while(size>0) { unsigned int b=(m_posRead / (float)m_pageSize); //Блок unsigned int p=m_posRead - b * m_pageSize; //Позиция в блоке unsigned char* ch=m_pages->get(b) + p; //Буфер на нужной позиции unsigned int s = m_pageSize - p; //сколько можно и нужно прочитать из блока if(s > size) s=size; memcpy(buffer, ch, s); buffer+=s; size-=s; m_posRead+=s; } }; void Read(wxOutputStream* os) //Записать все данные в os { uint4 s=m_size; for(uint4 i=0;icount();i++) { unsigned char* ch=m_pages->get(i); if(s>m_pageSize) { s-=m_pageSize; os->Write(ch,m_pageSize); } else os->Write(ch,s); } } void Write(const void* buf, unsigned int size) { unsigned char* buffer=(unsigned char*)buf; if(m_posWrite+size>m_pages->count()*m_pageSize) //Расширить память { unsigned int s=ceil((m_posWrite+size) / (float)m_pageSize); if(s>m_pages->count()) { s-=m_pages->count(); for(unsigned int i=0;iadd( new unsigned char[m_pageSize] ); } } while(size>0) { unsigned int b=floor(m_posWrite / (float)m_pageSize); //Находим нужный блок памяти unsigned int p=m_posWrite - b * m_pageSize; //Позиция с начала блока памяти unsigned char* ch=m_pages->get(b) + p; unsigned int s = m_pageSize - p; if(s > size) s=size; memcpy(ch, buffer, s); buffer+=s; size-=s; m_posWrite+=s; } if(m_sizeRead(buf,1024); unsigned int r=s->LastRead(); if(r==0) break; Write(buf,r); } delete buf; }; void SeekRead(uint4 pos){ m_posRead=pos; }; uint4 TellRead(){ return m_posRead; }; void SeekWrite(uint4 pos) { m_posWrite=pos; } uint4 TellWrite(){ return m_posWrite; }; }; //--------------------------------------------------------------------------- //Преобразовать значние в соответствии с типом если int и пустая строка то NULL wxString getSQLValue(wxString t,wxString v); //--------------------------------------------------------------------------- class TiptopTable; //--------------------------------------------------------------------------- /* //http://slady.net/java/bt/view.php?w=1024&h=768 //узел двоичного дерева фиксированной длины (высчитать размер надо чтоб с диска читалось примерно по 512 байта) //По определению, двоичное дерево называется АВЛ деревом в том и только в том случае, когда высоты двух поддеревьев каждой из вершин дерева отличаются не более, чем на единицу class BTreeNode { private: public: int1 balance; //Баланс для АВЛ дерева -1 - левое длиней, 0 - сбалансированно, 1 - правое длиней uint4 val; //размеры свободного пространства uint4 pos; //позиции в файле для размера uint4 fllink,frlink; //ссылки на дочерние узлы в файле BTreeNode *llink,*rlink; //ссылки на дочерние узлы в памяти BTreeNode *parent; //родительмкий узел дла нормализации BTreeNode(){llink=NULL; rlink=NULL;}; ~BTreeNode(){}; }; //Индекс пустых мест в таблице создаётся для каждой таблицы (потом наверно и для всего файла надо будет сделать) //отсортированно по возрастанию свободного места "кол-во места->позиция в файле" /*class TiptopIndexFreeSpase { private: // TiptopTable* m_table; uint4 m_count; //количество элементов wxFFile* m_file; //чтение и запись bool LLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Одинарный LL-поворот. bool RRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Одинарный RR-поворот. bool LRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Двойной LR-поворот. bool RLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Двойной RL-поворот. public: TiptopIndexFreeSpase(TiptopTable* table); ~TiptopIndexFreeSpase(); uint4 findFree(uint4 size); //найти позицию в файле с минимальным подходящим размером для записи bool setUseSpace(uint4 pos,uint4 size); //сообщить что данная позиция занята bool setFreeSpase(uint4 pos,uint4 size); //сообщить что данные освободились void updateLast(); //обновить размер по данным поледнего вызова findFree }; //Есть 4 варианта нарушения балансировки АВЛ-дерева //Одинарный LL-поворот. Выполняется, когда <перевес> идет по пути L-L от узла с нарушенной балансировкой. bool TiptopIndexFreeSpase::LLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2) { p1 := p^.llink; p^.llink := p1^.rlink; p1^.rlink := p; p := p1; } //Одинарный RR-поворот. Выполняется, когда <перевес> идет по пути R-R от узла с нарушенной балансировкой. bool TiptopIndexFreeSpase::RRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2) { p1 := p^.rlink; p^.rlink := p1^.llink; p1^.llink := p; p := p1; } //Двойной LR-поворот. Выполняется, когда <перевес> идет по пути L-R от узла с нарушенной балансировкой. bool TiptopIndexFreeSpase::LRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2) { p1 := p^.llink; p2 := p1^.rlink; p1^.rlink := p2^.llink; p2^.llink := p1; p^.llink := p2^.rlink; p2^.rlink := p; p := p2; } //Двойной RL-поворот. Выполняется, когда <перевес> идет по пути R-L от узла с нарушенной балансировкой. bool TiptopIndexFreeSpase::RLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2) { p1 := p^.rlink; p2 := p1^.llink; p1^.llink := p2^.rlink; p2^.rlink := p1; p^.rlink := p2^.llink; p2^.llink := p; p := p2; } */ //--------------------------------------------------------------------------- class TiptopField; //--------------------------------------------------------------------------- //числовой индекс базы данных (для id поля он обязателен) //Создаётся файл вида "id таблицы_id поля.i" (если id поля = 0 то это индекс свободных мест) class TiptopIndex { private: uint4 m_type; //Тип индекса (1 - самый простой) uint4 m_count; //количество элементов wxFFile* m_file; //чтение и запись public: TiptopField* m_fl; //Поле таблицы для которого строится индекс TiptopIndex(TiptopField* fl); ~TiptopIndex(); void add(uint4 pos,uint4 id); bool getPos(uint4 val,uint4& pos,int4& num); //Поиск значения среди упорядоченного списка }; //--------------------------------------------------------------------------- struct RuiPointPS { unsigned int pos; //позиция в файле unsigned int size; //Размер }; //--------------------------------------------------------------------------- //Индекс пустых мест пока как таблица (потом переделать в двоичный список) class TiptopIndexFreeSize { private: TSimpleList* m_Pos; ///<Массив позиции public: TiptopIndexFreeSize() { m_Pos=new TSimpleList(10,true); }; ~TiptopIndexFreeSize(){ delete m_Pos; }; void add(unsigned int pos, unsigned int size) //Добавить "пустое место" в список { RuiPointPS* p=new RuiPointPS; p->pos=pos; p->size=size; m_Pos->add(p); }; bool del(unsigned int pos) //Удалить из списка свободных мест { for(uint4 i=0;icount();i++) if(m_Pos->get(i)->pos==pos) { return m_Pos->del(i); } return false; }; bool upd(unsigned int pos, unsigned int size) //Сдвинуть позицию на заданый размер и уменьшить размер { for(uint4 i=0;icount();i++) { if(m_Pos->get(i)->pos==pos) { unsigned int s=m_Pos->get(i)->size; if(s==size) { m_Pos->del(i); break; } if(sget(i)->pos+=size; m_Pos->get(i)->size-=size; } break; } } return false; }; bool get(unsigned int size,unsigned int &pos) //Получить позицию подходящую под заданный размер { bool b=false; unsigned int min=0xffffffff; for(unsigned int i=0;icount();i++) { if(m_Pos->get(i)->size>=size && m_Pos->get(i)->sizeget(i)->pos; min=m_Pos->get(i)->size; b=true; } } return b; }; }; //--------------------------------------------------------------------------- class TiptopTable; //--------------------------------------------------------------------------- //поле базы данных class TiptopField { private: //Для 1 го поля 1н индекс public: TiptopTable* m_tb; //Если NULL то поле не присоединено к таблице void* m_value; //ссылка на данные (может быть NULL) uint4 m_size; //размер данных (Если 0 то в базе NULL) wxString m_name; //название поля uint1 m_type_id; //id типа данных bool m_NULL; //может ли быть пустым bool m_index; //Нужен ли индекс для этого поля (не используется, может быть: первичным, вторичным, уникальным, просто индексом для поиска) TiptopIndex* m_idx; //Индекс поля (пока только для поля id) TiptopField(TiptopTable* tb); //Конструктор задаётся какой таблице принадлежит ~TiptopField(); void setValue(void* value,uint4 size); //Данные копируются void setValue(TiptopStream* is,uint4 size); //Записать данные void setValue(int value); void setValue(wxString value); //Прочитать данные из строки в соответствии с типом void setValue(bool value); //Присвоить bool значение void setValue(double value); //Присвоить double значение TiptopField* cloneNode(); ///< Клонировать узел uint4 getSize() { if(m_value==NULL) return 0; return m_size; }; ///<Размер без учёта размера под данные uint4 getAllSize(); ///<Размер с учётом типа поля //Функции для взятия значений bool getBoolVal(); uint4 getUint4Val(); int4 getIntVal(); double getDblVal(); wxString getStrVal(); wxString getStrSQLVal(); }; //--------------------------------------------------------------------------- class TiptopBD; //--------------------------------------------------------------------------- //таблица данных может быть присоединённой к данным а может и отдельно волятся... //Если отдельно то для неё не предназначенны вторичные ключи только 1 первичный и индексы... class TiptopTable { private: uint1 m_Type; //Тип таблицы 0-Плотная(Индекс не обязателен последовательный доступ) 1-Жидкая (Индекс обязателен) //wxOutputStream* m_os; ///<Для записи заголовка и данных данных //wxMemoryOutputStream* m_os; ///<Для записи заголовка и данных //bool m_osOwner; ///<Владелец ли m_os смотри деструктор //bool m_ok; //выполнилась ли последняя операция успешно //wxString m_path; //Путь к файлу таблицы int4 getNumFieldOnName(wxString name); //вернуть позицию поля по имени нумерация с 0, если -1 то нет такого TSimpleList2 m_Pos; ///<Массив позиции полей от начала файла либо потока TiptopIndexFreeSize* m_IndexFreeSize; //Индекс свободного пространства bool buildPosIndex(); ///<Построить индекс позиций прочитав таблацу он начала до конца public: //wxFileOutputStream* m_fos; //для записи в файл таблицы //wxFileInputStream* m_fis; //для чтения из файла таблицы //wxInputStream* m_is; ///<Для чтения заголовка таблицы и данных wxFFile* m_file; //чтение и запись TiptopStream* m_stream; ///<С этим потоком работает таблица для чтения и записи uint1* m_Null;//Массив байт для чтения и записи NULL значений (равно количеству столбцов не зависимо возможно ли NULL) uint1 m_NullSize;//количество байт для NULL, подсчитываются = cell(count fields/8) uint4 m_RecSize; ///<Размер последней прочитанной записи в байтах с учётом null и типов uint4 m_RecPos; ///<Позиция последней прочитаной записи TSimpleList* fields; //список столбцов владелец (идут попорядку id поля нет есть позиция) TiptopBD* m_bd; //Если NULL то таблица не присоединена к базе данных uint4 m_id; //id таблицы в нутри базы (или запроса) wxString m_name; //Название таблицы (читается из файла) int m_count; //Количество записей (заранее неизвестно если нет индекса) //uint4 rCount; //Количество записей в таблице (Информационное поле) TiptopTable(TiptopBD* bd,uint4 id); ~TiptopTable(); //bool isOk(); bool AddField(TiptopField* fl); ///<Добавить поле к таблице становится владельцем (без записи в поток) TiptopField* AddField(wxString name,uint1 type_id); ///<Добавить поле к таблице (без записи в поток) TiptopField* AddField(wxString name,wxString type); ///<Добавить поле к таблице (без записи в поток) bool OpenTable(); //открыть таблицу (если обьект TiptopTable не в базе если таблица не открылась то результат false а путь к файлу сохраниться) //bool OpenTable(wxInputStream* is); //Подгрузить таблицу из потока (Читает только заголовок) bool OpenTable(wxString path); //Прочитать из файла // bool SetOutputStream(wxMemoryOutputStream* os, bool owner=false); // const wxMemoryOutputStream* GetOutputStream(){return m_os;}; bool ReadTableToMem(wxInputStream* is); ///<Прочитать таблицу из потока в оперативную память bool CreateTable(wxString SQL); //Создать файл таблицы через SQL запрос bool InsertInto(wxString SQL); //Добавить запись в таблицу и в индексы через SQL запрос bool UpdateTable(wxString SQL); //"простое" обновление записей через SQL запрос wxFFile* getStream(uint4 id,wxString field); //TODO надо сделать свой защищённый "поток" //wxMemoryInputStream* GetInputStream(); ///<Создаёт новый поток в который помещяет таблицу с данными для чтения (TiptopTable не остаётся владельцем) TiptopField* getField(wxString name); //Получить поле по имени (Для того чтобы получить по индексу используй TSimpleList) uint4 findFreeSize(uint4 size = 0); //Найти позицию с свободным местом для записи данных (из индекса свободных мест) bool FindFirstRecord(TiptopField* fl,uint4 &pos,uint4 &num); ///<Найти позицию первой попавшейся записи с заданными значениями поля, результат позиция записи в памяти. bool SeekToStart(); //Переместить курсор чтения на первую запись (чтоб потом можно было последовательно прочитать всю таблицу) bool ReadRecord(wxString field,uint4 val); //найти и прочитать запись bool ReadRecord(TiptopStream* is); //Позиция на нужном месте читаем запись из потока bool ReadRecord(int4 pos); //Перемещяем курсор в заданную позицию и читаем запись bool ReadNextRecord(bool savePos = false); //Прочитать следующую запись из таблицы bool ReadRecordByNum(uint4 num); //Читать запись по её порядковому номеру (должны заранее знать позиции записей) bool WriteHeader(); ///<Записать заголовок таблицы в поток m_os (Используется при создании таблицы) bool WriteRecord(bool savePos , bool findPos); ///<Записать 1 запись в конец m_os из полей (TODO как сделаю findFreeSize то в пустую позицию) bool WriteRecord(); //Записать запись в текущее положение потока m_os //bool WriteTableToStream(wxOutputStream* os); ///<Записать заголовок и тело таблицы в предоставленный поток void addRecPos(uint8 pos) //Добавить позицию записи в памяти { m_Pos.add(pos); }; bool delRecPos(uint8 pos); //Удалить позицию записи в памяти //uint4 getNumByRecPos(uint8 pos)//Получить порядковый номер записи по позиции /*uint4 GetRecPos(uint4 num) //Получить адрес записи по позиции { if(m_Pos.count()==0) rerturn 0; //TODO исправить добавить bool к параметрам либо возвращять -1 return m_Pos.get(num); };*/ bool AppendTable(TiptopTable* tb, bool spw, bool spr); //Добавить записи из заданной в текущую (поля должны совпадать по имени и по типу) bool UpdateTable(TiptopTable* tb); //Обновить записи из заданной в текущую (поля должны совпадать по имени и по типу) bool UpdateRecord(); ///<Обновить 1 запись удаляем потом пишем }; //--------------------------------------------------------------------------- //база данных (набор таблиц) class TiptopBD { private: wxFileOutputStream* m_fos; //поток чтения из файла wxFileInputStream* m_fis; //поток записи в файл TSimpleList* list;//список таблиц uint4 m_maxid; //макс id таблиц (нумерация с 1) bool m_ok; //Нормально ли открылась база public: wxString m_path; //путь к папке с файлами базы TiptopBD(); ~TiptopBD(); bool Open(wxString path); //задаётся путь к папке где лежит bd.bd файл bool TableExist(wxString name); //существует ли таблица //bool AddTable(wxString file); //добавить существующую таблицу в базу данных TiptopTable* getTable(wxString name); //Получить объект таблицы по её имени bool ExecSQL(wxString SQL); //выполнить SQL запрос без результата bool addTable(uint4 id); //Добавить таблицу в базу под заданным id }; //--------------------------------------------------------------------------- #endif