#ifndef BZS_DB_PROTOCOL_TDAP_CLIENT_ACTIVETABLEIMPLE_H #define BZS_DB_PROTOCOL_TDAP_CLIENT_ACTIVETABLEIMPLE_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 "trdboostapi.h" #include "trdormapi.h" #include "recordsetImple.h" #include "memRecord.h" namespace bzs { namespace db { namespace protocol { namespace tdap { namespace client { class map_orm_fdi { friend class map_orm; const table* m_tb; public: void init(table* tb) { m_tb = tb; } }; inline map_orm_fdi* createFdi(map_orm_fdi*) { return new map_orm_fdi(); } inline void destroyFdi(map_orm_fdi* p) { delete p; } inline void initFdi(map_orm_fdi* fdi, table* tb) { fdi->init(tb); } class map_orm { const map_orm_fdi& m_fdi; short m_autoIncFiled; int comp(row& lm, row& rm, const _TCHAR* name, int index) const { return lm[index].comp(rm[index], 0); } public: map_orm(const map_orm_fdi& fdi) : m_fdi(fdi), m_autoIncFiled(-2) {} bool compKeyValue(row& l, row& r, int keyNum) const { const tabledef* def = m_fdi.m_tb->tableDef(); const keydef* kd = &def->keyDefs[keyNum]; for (int i = 0; i < kd->segmentCount; ++i) { short n = kd->segments[i].fieldNum; const fielddef* fd = &def->fieldDefs[n]; int ret = comp(l, r, fd->name(), n); if (ret) return (ret < 0); } return 0; } template <class T> void readMap(T& m, const fields& fds, int optipn) { // needlessness } typedef row mdl_typename; typedef map_orm_fdi fdi_typename; typedef mdlsHandler<map_orm, recordsetImple> collection_orm_typename; }; #define FIXED_VALUE_BUF_SIZE 256 struct joinInfo { unsigned char fixedValue[FIXED_VALUE_BUF_SIZE]; ushort_td len; ushort_td type; }; #define JOIN_KEYVALUE_TYPE_PTR 0 #define JOIN_KEYVALUE_TYPE_STR 1 #define MAX_JOIN_KEY_SIZE 8 class activeTableImple : public activeObject<map_orm> { typedef recordsetImple Container; typedef writableRecord* record; typedef activeObject<map_orm> baseClass_type; typedef std::vector<std::vector<int> > joinmap_type; record m_record; int m_tmpIndex; // return can memcpy uchar_td convertFieldType(uchar_td v) { if (v == ft_autoinc) v = ft_integer; else if (v == ft_autoIncUnsigned) v = ft_integer; else if (v == ft_uinteger) v = ft_integer; else if (v == ft_logical) v = ft_integer; else if (v == ft_bit) v = ft_integer; return v; } template <class Container> inline bool makeJoinFieldInfo(Container& mdls, const fielddefs* fds, const _TCHAR* fns[], int fnsCount, std::vector<typename Container::key_type>& fieldIndexes, std::vector<joinInfo>& joinFields) { joinFields.resize(fnsCount); fieldIndexes.resize(fnsCount); const tabledef* td = table()->tableDef(); const keydef* kd = &td->keyDefs[(int)table()->keyNum()]; if (kd->segmentCount < fnsCount) THROW_BZS_ERROR_WITH_MSG(_T("Join key fields are too many.\n ") _T("Check index number and field count.")); bool hasMany = ((kd->segmentCount > fnsCount) || kd->segments[0].flags.bit0);/* duplicate key*/ for (int i = 0; i < fnsCount; ++i) { std::_tstring s = fns[i]; if (s[0] == '[') { fieldIndexes[i] = -1; const fielddef& f = td->fieldDefs[kd->segments[i].fieldNum]; if (f.len > FIXED_VALUE_BUF_SIZE) THROW_BZS_ERROR_WITH_MSG(_T("Join fixed key field is too long") _T(" field.\n This field can not use join.")); field fd(joinFields[i].fixedValue - f.pos, f, fds); size_t pos = s.find(_T(']'), 1); if (pos == std::_tstring::npos) THROW_BZS_ERROR_WITH_MSG(_T("Join fixed key field is invalid format") _T(".\n This field can not use join.")); fd = s.substr(1, pos - 1).c_str(); joinFields[i].len = f.isStringType() ? 0xff : f.len; joinFields[i].type = JOIN_KEYVALUE_TYPE_PTR; } else { ushort_td index = resolvKeyValue(mdls, s, false); fieldIndexes[i] = index; const fielddef& fd = (*fds)[index]; // Check the fieldType if (convertFieldType(fd.type) == convertFieldType( td->fieldDefs[kd->segments[i].fieldNum].type)) { joinFields[i].len = fd.isStringType() ? 0xff : fd.len; joinFields[i].type = JOIN_KEYVALUE_TYPE_PTR; } else { joinFields[i].len = 0; joinFields[i].type = JOIN_KEYVALUE_TYPE_STR; } } } return hasMany; } template <class Container> void makeJoinMap(Container& mdls, joinmap_type& joinRowMap, std::vector<typename Container::key_type>& keyFields) { grouping_comp<Container> groupingComp(mdls, keyFields); std::vector<int> index; std::vector<int> tmp; for (int n = 0; n < (int)mdls.size(); ++n) { bool found = false; int i = binary_search(n, index, 0, (int)index.size(), groupingComp, found); if (!found) { index.insert(index.begin() + i, n); joinRowMap.insert(joinRowMap.begin() + i, tmp); } joinRowMap[i].push_back(n); } } template <class Container> inline void addSeekValues(row& mdl, pq_handle& q, std::vector<typename Container::key_type>& fieldIndexes, std::vector<joinInfo>& joinFields, const fielddefs* fds) { const uchar_td* ptr[8]; int len[8]; uchar_td buf[MAX_KEYLEN]; uchar_td* buf_ptr = buf; const tabledef* td = table()->tableDef(); const keydef* kd = &td->keyDefs[(int)table()->keyNum()]; for (int i = 0; i < (int)fieldIndexes.size(); ++i) { if (fieldIndexes[i] == -1) { ptr[i] = joinFields[i].fixedValue; len[i] = joinFields[i].len; } else if (joinFields[i].type == JOIN_KEYVALUE_TYPE_PTR) { ptr[i] = (mdl[fieldIndexes[i]].isNull()) ? NULL : (const uchar_td*)mdl[fieldIndexes[i]].ptr(); len[i] = joinFields[i].len; } else { // if target field type is different then we need convrt type const fielddef& f = td->fieldDefs[kd->segments[i].fieldNum]; field fd(buf_ptr - f.pos, f, fds); len[i] = f.isStringType() ? 0xff : f.len; if (mdl[fieldIndexes[i]].isNull()) ptr[i] = NULL; else { fd = mdl[fieldIndexes[i]].c_str();// operator=(const _TCHAR* p) ptr[i] = buf_ptr; buf_ptr += f.len; } } } if (!q->supplySeekValue(ptr, len, (int)fieldIndexes.size(), m_tmpIndex)) THROW_BZS_ERROR_WITH_MSG(_T("Join key value(s) are invalid at supply values to prepared statement or query.\n ") _T("Check prepared statement or query.")); } int makeJoinKeys(const _TCHAR* fns[MAX_JOIN_KEY_SIZE], int &fnsCount, 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) { int count = 0; fns[0] = name1; fns[1] = name2; fns[2] = name3; fns[3] = name4; fns[4] = name5; fns[5] = name6; fns[6] = name7; fns[7] = name8; for (count = 0; count < MAX_JOIN_KEY_SIZE; ++count) if ((fns[count] == NULL) || (fns[count][0] == 0x00)) break; return count; } inline void reserveSeekSize(pq_handle& q, size_t size, int keySize) { q->beginSupplySeekValues(size, keySize); m_tmpIndex = 0; } template <class Container> void doJoin(bool innner, Container& mdls, pq_handle& stmt, const _TCHAR* fns[8], int fnsCount) { if (mdls.size() == 0) return; stmt->clearSeeks(); mraResetter mras(m_tb); typename Container::iterator it = mdls.begin(), ite = mdls.end(); joinmap_type joinRowMap; std::vector<typename Container::key_type> fieldIndexes; std::vector<joinInfo> joinFields; const fielddefs* fds = mdls.fieldDefs(); bool hasMany = makeJoinFieldInfo<Container>(mdls, fds, fns, fnsCount, fieldIndexes, joinFields); if (!hasMany) hasMany = (stmt->cachedOptimaize() & queryBase::joinHasOneOrHasMany); // optimizing join // if base recordsetImple is made by unique key and join by uniqe field, // that can not opitimize. // if (!hasMany) { makeJoinMap(mdls, joinRowMap, fieldIndexes); reserveSeekSize(stmt, joinRowMap.size() * fieldIndexes.size(), fnsCount); std::vector<std::vector<int> >::iterator it1 = joinRowMap.begin(), ite1 = joinRowMap.end(); while (it1 != ite1) { row& mdl = *(mdls.getRow((*it1)[0])); addSeekValues<Container>(mdl, stmt, fieldIndexes, joinFields, fds); ++it1; } } else { reserveSeekSize(stmt, mdls.size() * fieldIndexes.size(), fnsCount); while (it != ite) { row& mdl = *(*it); addSeekValues<Container>(mdl, stmt, fieldIndexes, joinFields, fds); ++it; } } if (m_tb->stat() != 0) nstable::throwError(_T("activeObject Join Query"), &(*m_tb)); typename map_orm::collection_orm_typename map(mdls); // std::vector<typename Container::iterator> ignores; it = mdls.begin(); map.init(m_option, m_fdi, m_map, m_tb, &m_alias); if (m_tb->mra()) { m_tb->mra()->setJoinType(innner ? mra_innerjoin : mra_outerjoin); if (!hasMany) m_tb->mra()->setJoinRowMap(&joinRowMap); } m_tb->find(); while (1) { if (m_tb->stat()) { if ((m_tb->stat() == STATUS_EOF) || ((m_tb->stat() != STATUS_SUCCESS) && (m_tb->stat() != STATUS_NOT_FOUND_TI))) break; } ++it; m_tb->findNext(); // mra copy value to memrecord } readStatusCheck(*m_tb, _T("join")); m_tb->mra()->setJoinRowMap(NULL); // remove record see ignore list for inner join if (innner) { for (int i = (int)mdls.size() - 1; i >= 0; --i) { if (mdls[i].isInvalidRecord()) mdls.erase(i); } } } inline void checkPreparedQuery(pq_handle& q) { if (!q) THROW_BZS_ERROR_WITH_MSG(_T("Invalid query or prepqredQuery.\n ")); } public: explicit activeTableImple(idatabaseManager* mgr, const _TCHAR* tableName) : baseClass_type(mgr, tableName), m_record(NULL){}; explicit activeTableImple(database_ptr& db, const _TCHAR* tableName, short mode) : baseClass_type(db, tableName, mode), m_record(NULL){}; explicit activeTableImple(database* db, const _TCHAR* tableName, short mode) : baseClass_type(db, tableName, mode), m_record(NULL){}; explicit activeTableImple(database_ptr& db, short tableIndex, short mode) : baseClass_type(db, tableIndex, mode), m_record(NULL){}; explicit activeTableImple(database* db, short tableIndex, short mode) : baseClass_type(db, tableIndex, mode), m_record(NULL){}; ~activeTableImple() { if (m_record) m_record->release(); } inline writableRecord& getWritableRecord() { if (m_record == NULL) { m_record = writableRecord::create(m_tb.get(), &m_alias); m_record->addref(); } return *m_record; } inline void join(Container& mdls, queryBase& q, 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) { const _TCHAR* fns[MAX_JOIN_KEY_SIZE]; m_alias.reverseAliasNamesQuery(q); int fnsCount = makeJoinKeys(fns, fnsCount, name1, name2, name3, name4, name5, name6, name7, name8); q.joinKeySize(fnsCount); pq_handle pq = setQuery(m_tb, q); checkPreparedQuery(pq); doJoin(true, mdls, pq, fns, fnsCount); } inline void outerJoin(Container& mdls, queryBase& q, 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) { const _TCHAR* fns[MAX_JOIN_KEY_SIZE]; m_alias.reverseAliasNamesQuery(q); int fnsCount = makeJoinKeys(fns, fnsCount, name1, name2, name3, name4, name5, name6, name7, name8); q.joinKeySize(fnsCount); pq_handle pq = setQuery(m_tb, q); checkPreparedQuery(pq); doJoin(false, mdls, pq, fns, fnsCount); } inline void join(Container& mdls, pq_handle& q, 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) { const _TCHAR* fns[MAX_JOIN_KEY_SIZE]; int fnsCount = makeJoinKeys(fns, fnsCount, name1, name2, name3, name4, name5, name6, name7, name8); pq_handle pq = setQuery(m_tb, q); checkPreparedQuery(pq); doJoin(true, mdls, pq, fns, fnsCount); } inline void outerJoin(Container& mdls, pq_handle& q, 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) { const _TCHAR* fns[MAX_JOIN_KEY_SIZE]; int fnsCount = makeJoinKeys(fns, fnsCount, name1, name2, name3, name4, name5, name6, name7, name8); pq_handle pq = setQuery(m_tb, q); checkPreparedQuery(pq); doJoin(false, mdls, pq, fns, fnsCount); } void releaseTable() { m_tb.reset(); } }; } // namespace client } // namespace tdap } // namespace protocol } // namespace db } // namespace bzs #endif // BZS_DB_PROTOCOL_TDAP_CLIENT_ACTIVETABLEIMPLE_H