#ifndef BZS_DB_PROTOCOL_TDAP_CLIENT_MEMRECORDSETIMPLE_H #define BZS_DB_PROTOCOL_TDAP_CLIENT_MEMRECORDSETIMPLE_H /*================================================================= Copyright (C) 2014 BizStation Corp All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =================================================================*/ #include "trdormapi.h" #include "groupQuery.h" #ifdef _DEBUG #include <iostream> #endif namespace bzs { namespace db { namespace protocol { namespace tdap { namespace client { struct sortDescription { short index; bool asc; }; class recordsetSorter { const std::vector<sortDescription>& m_sortDesc; public: recordsetSorter(std::vector<sortDescription>& sortDesc) : m_sortDesc(sortDesc) { } bool operator()(const row_ptr& l, const row_ptr r) const { std::vector<sortDescription>::const_iterator it = m_sortDesc.begin(); while (it != m_sortDesc.end()) { int ret = (*l)[(*it).index].comp((*r)[(*it).index], 0); if (ret) return ((*it).asc) ? (ret < 0) : (ret > 0); ++it; } return false; } }; class multiRecordAlocatorImple : public multiRecordAlocator { class recordsetImple* m_rs; const std::vector<std::vector<int> >* m_joinRowMap; int m_rowOffset; int m_addType; int m_curFirstFiled; public: inline multiRecordAlocatorImple(recordsetImple* rs); inline void init(size_t recordCount, size_t recordLen, int addType, const table* tb); inline unsigned char* ptr(size_t row, int stat); inline void setRowOffset(int v) { m_rowOffset = v; } inline void setJoinType(int v) { m_addType = v; } inline void setInvalidRecord(size_t row, bool v); inline void setCurFirstFiled(int v) { m_curFirstFiled = v; } inline void setJoinRowMap(const std::vector<std::vector<int> >* v /*, size_t size*/) { m_joinRowMap = v; /*m_joinMapSize = size;*/ } inline const std::vector<std::vector<int> >* joinRowMap() const { return m_joinRowMap; } inline void duplicateRow(int row, int count); inline void removeLastMemBlock(int row); }; class recordsetImple { friend class multiRecordAlocatorImple; boost::shared_ptr<fielddefs> m_fds; boost::shared_ptr<multiRecordAlocatorImple> m_mra; std::vector<row_ptr> m_recordset; std::vector<boost::shared_ptr<autoMemory> > m_memblock; std::vector<boost::shared_ptr<fielddefs> > m_unionFds; /* for registerMemoryBlock temp data */ size_t m_joinRows; /* for optimazing join. If the first reading is using by unique key , set that field count. */ short m_uniqueReadMaxField; public: typedef std::vector<row_ptr>::iterator iterator; private: void registerMemoryBlock(unsigned char* ptr, size_t size, size_t recordLen, int addtype, const table* tb = NULL) { autoMemory* am = new autoMemory(ptr, size, 0, true); m_memblock.push_back(boost::shared_ptr<autoMemory>(am)); unsigned char* p = am->ptr; // copy fileds if (addtype & mra_nextrows) { if (addtype == mra_nextrows) m_mra->setRowOffset((int)m_recordset.size()); // no join else m_mra->setRowOffset((int)m_joinRows); // Join } else { // assert(tb); m_joinRows = 0; m_mra->setRowOffset(0); m_mra->setCurFirstFiled((int)m_fds->size()); if (tb) m_fds->copyFrom(tb); if (tb && (addtype == mra_nojoin)) { const keydef& kd = tb->tableDef()->keyDefs[tb->keyNum()]; m_uniqueReadMaxField = (kd.segments[0].flags.bit0 == false) ? (short)m_fds->size() : 0; } } *(am->endFieldIndex) = (short)m_fds->size(); size_t rows = size / recordLen; // set record pointer to each record if ((addtype & mra_innerjoin) || (addtype & mra_outerjoin)) { // Join optimazing const std::vector<std::vector<int> >* jmap = m_mra->joinRowMap(); if (jmap) { // At Join that if some base records reference to a joined // record // that the joined record pointer is shared by some // base records. for (int i = 0; i < (int)rows; ++i) { const std::vector<int>& map = (*jmap)[i + m_joinRows]; for (int j = 0; j < (int)map.size(); ++j) m_recordset[map[j]]->setRecordData( p + recordLen * i, 0, am->endFieldIndex, false); } } else { for (int i = 0; i < (int)rows; ++i) m_recordset[i + m_joinRows]->setRecordData( p + recordLen * i, 0, am->endFieldIndex, false); } m_joinRows += rows; } else { // create new record size_t reserveSize = m_recordset.size() + rows; m_recordset.reserve(reserveSize); for (int i = 0; i < (int)rows; ++i) { row_ptr rec(memoryRecord::create(*m_fds), &memoryRecord::release); rec->setRecordData(p + recordLen * i, 0, am->endFieldIndex, false); m_recordset.push_back(rec); } } } void makeSortFields(const _TCHAR* name, std::vector<sortDescription>& sortDesc, bool asc = true) { sortDescription sd; sd.index = m_fds->indexByName(name); if (sd.index == -1) THROW_BZS_ERROR_WITH_MSG(_T("orderBy:Invalid field name")); sd.asc = asc; sortDesc.push_back(sd); } int getMemBlockIndex(unsigned char* ptr) const { for (int i = 0; i < (int)m_memblock.size(); ++i) { const boost::shared_ptr<autoMemory>& am = m_memblock[i]; if ((ptr >= am->ptr) && (ptr < am->ptr + am->size)) return i; } assert(0); return -1; } // Duplicate row for hasManyJoin void duplicateRow(int row, int count) { row_ptr& r = m_recordset[row]; memoryRecord* p = dynamic_cast<memoryRecord*>(r.get()); m_recordset.reserve(m_recordset.size() + count); m_recordset.insert(m_recordset.begin() + row, count, row_ptr()); for (int i = 0; i < count; ++i) m_recordset[i + row].reset(new memoryRecord(*p), &memoryRecord::release); } public: inline recordsetImple() : m_fds(fielddefs::create(), boost::bind(&fielddefs::release, _1)), m_joinRows(0), m_uniqueReadMaxField(0) { m_mra.reset(new multiRecordAlocatorImple(this)); } inline ~recordsetImple() {} inline void checkIndex(size_t index) { if (index >= m_recordset.size()) THROW_BZS_ERROR_WITH_MSG(_T("Invalid row index of recordset.")); } /* This clone is deep copy. But text and blob field data memory are shared. */ inline recordsetImple* clone() const { recordsetImple* p = new recordsetImple(); p->m_joinRows = m_joinRows; p->m_uniqueReadMaxField = m_uniqueReadMaxField; p->m_unionFds = m_unionFds; p->m_fds.reset(m_fds->clone(), boost::bind(&fielddefs::release, _1)); std::vector<__int64> offsets; for (int i = 0; i < (int)m_memblock.size(); ++i) { autoMemory* am = new autoMemory(m_memblock[i]->ptr, m_memblock[i]->size, 0, true); *am->endFieldIndex = *m_memblock[i]->endFieldIndex; p->m_memblock.push_back(boost::shared_ptr<autoMemory>(am)); offsets.push_back((__int64)(am->ptr - m_memblock[i]->ptr)); } for (int i = 0; i < (int)m_recordset.size(); ++i) { memoryRecord* row = dynamic_cast<memoryRecord*>(m_recordset[i].get()); memoryRecord* mr = memoryRecord::create(*p->m_fds); row_ptr rec(mr, &memoryRecord::release); p->m_recordset.push_back(rec); for (int j = 0; j < (int)row->memBlockSize(); ++j) { const autoMemory& mb = row->memBlock(j); int index = getMemBlockIndex(mb.ptr); #pragma warn -8072 unsigned char* ptr = mb.ptr + offsets[index]; #pragma warn .8072 const boost::shared_ptr<autoMemory>& am = p->m_memblock[index]; mr->setRecordData(ptr, mb.size, am->endFieldIndex, mb.owner); } } return p; } inline short uniqueReadMaxField() const { return m_uniqueReadMaxField; } inline void clearRecords() { m_recordset.clear(); m_uniqueReadMaxField = 0; } inline const fielddefs* fieldDefs() const { return m_fds.get(); } inline void clear() { clearRecords(); m_fds->clear(); m_unionFds.clear(); m_memblock.clear(); } inline row_ptr& getRow(size_t index) { return m_recordset[index]; } inline row& operator[](size_t index) const { return *m_recordset[index].get(); } inline row& first() const { if (m_recordset.size() == 0) THROW_BZS_ERROR_WITH_MSG(_T("Invalid index of recordset.")); return *m_recordset[0].get(); } inline row& last() const { if (m_recordset.size() == 0) THROW_BZS_ERROR_WITH_MSG(_T("Invalid index of recordset.")); return *m_recordset[m_recordset.size() - 1].get(); } inline recordsetImple& top(recordsetImple& c, int n) const { c = *this; c.clearRecords(); n = std::min(n, (int)m_recordset.size()); for (int i = 0; i < n; ++i) c.push_back(m_recordset[i]); return c; } inline iterator begin() { return m_recordset.begin(); } inline iterator end() { return m_recordset.end(); } inline iterator erase(size_t index) { return m_recordset.erase(m_recordset.begin() + index); } inline iterator erase(const iterator& it) { return m_recordset.erase(it); } inline void push_back(row_ptr r) { m_recordset.push_back(r); }; inline size_t size() const { return m_recordset.size(); } inline size_t count() const { return m_recordset.size(); } void readBefore(const table_ptr tb, const aliasMap_type* alias) { tb->setMra(m_mra.get()); m_fds->setAliases(alias); } typedef fielddefs header_type; typedef int key_type; typedef row_ptr row_type; typedef row row_pure_type; key_type resolvKeyValue(const std::_tstring& name, bool noexception = false) { int index = m_fds->indexByName(name); if (index != -1) return index; if (!noexception) THROW_BZS_ERROR_WITH_MSG(_T("groupQuery:Invalid key name")); return (key_type)m_fds->size(); } inline void removeField(int index) { m_fds->remove(index); for (int i = 0; i < (int)m_unionFds.size(); ++i) m_unionFds[i]->remove(index); for (int i = 0; i < (int)m_memblock.size(); ++i) { if (*(m_memblock[i]->endFieldIndex) > index) { short v = *(m_memblock[i]->endFieldIndex) - 1; *(m_memblock[i]->endFieldIndex) = v; } } } inline recordsetImple& matchBy(recordsetQuery& rq) { m_fds->setAliases(NULL); if (m_recordset.size()) { rq.init(fieldDefs()); for (int i = (int)m_recordset.size() - 1; i >= 0; --i) if (!rq.match(m_recordset[i])) erase(i); } return *this; } inline recordsetImple& groupBy(groupQuery& gq) { m_fds->setAliases(NULL); if (m_recordset.size()) gq.grouping(*this); return *this; } inline recordsetImple& orderBy(const _TCHAR* name1, const _TCHAR* name2 = NULL, const _TCHAR* name3 = NULL, const _TCHAR* name4 = NULL, const _TCHAR* name5 = NULL, const _TCHAR* name6 = NULL, const _TCHAR* name7 = NULL, const _TCHAR* name8 = NULL) { if (m_recordset.size()) { std::vector<sortDescription> sds; makeSortFields(name1, sds, true); if (name2) makeSortFields(name2, sds, true); if (name3) makeSortFields(name3, sds, true); if (name4) makeSortFields(name4, sds, true); if (name5) makeSortFields(name5, sds, true); if (name6) makeSortFields(name6, sds, true); if (name7) makeSortFields(name7, sds, true); if (name8) makeSortFields(name8, sds, true); std::sort(begin(), end(), recordsetSorter(sds)); } return *this; } inline recordsetImple& orderBy(const sortFields& orders) { if (m_recordset.size()) { std::vector<sortDescription> sds; for (int i = 0; i < (int)orders.size(); ++i) makeSortFields(orders[i].name.c_str(), sds, orders[i].asc); std::sort(begin(), end(), recordsetSorter(sds)); } return *this; } inline recordsetImple& reverse() { std::reverse(begin(), end()); return *this; } inline void appendField(const _TCHAR* name, int type, short len) { assert(m_fds->size()); fielddef fd((*m_fds)[0]); fd.len = len; fd.pos = 0; fd.type = type; fd.setName(name); m_fds->push_back(&fd); for (int i = 0; i < (int)m_unionFds.size(); ++i) m_unionFds[i]->push_back(&fd); registerMemoryBlock(NULL, fd.len * size(), fd.len, mra_outerjoin); } inline recordsetImple& operator+=(const recordsetImple& r) { if (!m_fds->canUnion(*r.m_fds)) THROW_BZS_ERROR_WITH_MSG(_T("Recordsets are different format")); m_recordset.reserve(m_recordset.size() + r.size()); m_unionFds.push_back(r.m_fds); for (size_t i = 0; i < r.size(); ++i) m_recordset.push_back(r.m_recordset[i]); for (size_t i = 0; i < r.m_memblock.size(); ++i) m_memblock.push_back(r.m_memblock[i]); return *this; } #ifdef _DEBUG void dump() { const fielddefs& fields = *fieldDefs(); for (int j = 0; j < (int)fields.size(); ++j) std::tcout << fields[j].name() << _T("\t"); std::tcout << _T("\n"); for (int i = 0; i < (int)size(); ++i) { row& m = (operator[](i)); for (int j = 0; j < (int)m.size(); ++j) { std::tcout << m[(short)j].c_str() << _T("\t"); if (j == (int)m.size() - 1) std::tcout << _T("\n"); } } } #endif }; inline multiRecordAlocatorImple::multiRecordAlocatorImple(recordsetImple* rs) : m_rs(rs), m_joinRowMap(NULL), m_rowOffset(0), m_addType(0), m_curFirstFiled(0) { } inline void multiRecordAlocatorImple::init(size_t recordCount, size_t recordLen, int addType, const table* tb) { m_rs->registerMemoryBlock(NULL, recordCount * recordLen, recordLen, addType | m_addType, tb); } inline unsigned char* multiRecordAlocatorImple::ptr(size_t row, int stat) { int col = (stat == mra_current_block) ? m_curFirstFiled : 0; size_t rowNum = m_joinRowMap ? (*m_joinRowMap)[row + m_rowOffset][0] : row + m_rowOffset; return (*m_rs)[rowNum].ptr(col); } inline void multiRecordAlocatorImple::setInvalidRecord(size_t row, bool v) { if (m_joinRowMap) { const std::vector<int>& map = (*m_joinRowMap)[row + m_rowOffset]; for (int j = 0; j < (int)map.size(); ++j) (*m_rs)[map[j]].setInvalidRecord(v); } else (*m_rs)[row + m_rowOffset].setInvalidRecord(v); } inline void multiRecordAlocatorImple::duplicateRow(int row, int count) { m_rs->duplicateRow(row, count); } inline void multiRecordAlocatorImple::removeLastMemBlock(int row) { (*m_rs)[row].removeLastMemBlock(); } template <> inline recordsetImple::iterator begin(recordsetImple& m) { return m.begin(); } template <> inline recordsetImple::iterator end(recordsetImple& m) { return m.end(); } template <> inline void push_back(recordsetImple& m, row_ptr c) { } /* for groupby */ template <> inline void clear(recordsetImple& m) { return m.clearRecords(); } /* for groupby */ template <> inline recordsetImple::key_type resolvKeyValue(recordsetImple& m, const std::_tstring& name, bool noexception) { return m.resolvKeyValue(name, noexception); } inline row* create(recordsetImple& m, int) { return NULL; } } // namespace client } // namespace tdap } // namespace protocol } // namespace db } // namespace bzs #endif // BZS_DB_PROTOCOL_TDAP_CLIENT_MEMRECORDSETIMPLE_H