/* =================================================================
 Copyright (C) 2000-2013 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 "table.h"
#include "field.h"
#include "fields.h"
#include "filter.h"
#include "database.h"
#include "bulkInsert.h"
#include <bzs/rtl/strtrim.h>
#include <bzs/db/protocol/tdap/myDateTime.cpp>
#include <bzs/db/blobStructs.h>
#include <bzs/rtl/stringBuffers.h>
#include "stringConverter.h"
#include <boost/timer.hpp>

#pragma package(smart_init)

using namespace bzs::db;
using namespace bzs::rtl;

namespace bzs
{
namespace db
{
namespace protocol
{
namespace tdap
{
namespace client
{

#ifdef _WIN32
#ifdef _INICODE
#define EXEC_CODEPAGE CP_UTF16
#else
#define EXEC_CODEPAGE GetACP()
#endif
#else
#define EXEC_CODEPAGE CP_UTF8
#endif

class recordCache;

struct tbimpl
{

    void* bookMarks;
    client::fields fields;
    filter* filterPtr;
    recordCache* rc;
    multiRecordAlocator* mraPtr;
    void* dataBak;
    void* smartUpDate;
    void* bfAtcPtr;
    void* optionalData;
    int bookMarksMemSize;
    int maxBookMarkedCount;
    char keybuf[MAX_KEYLEN];
    char exNext;
    char keyNumIndex[128];
    short exSlideStat;

    struct
    {
        unsigned char exBookMarking : 1;
        unsigned char smartUpDateFlag : 1;
        unsigned char dataPacked : 1;
    };

    tbimpl(table& tb)
        : bookMarks(NULL), fields(tb), filterPtr(NULL), rc(NULL), mraPtr(NULL),
          dataBak(NULL), smartUpDate(NULL), bfAtcPtr(NULL), optionalData(NULL),
          bookMarksMemSize(0), maxBookMarkedCount(0), smartUpDateFlag(false),
          dataPacked(false)
    {
        memset(&keyNumIndex[0], 0, 128);
    }

    ~tbimpl()
    {
        if (dataBak)
            free(dataBak);
        if (smartUpDate)
            free(smartUpDate);
        if (bookMarks)
            free(bookMarks);

        delete filterPtr;
    }
};

// ---------------------------------------------------------------------------
// class recordCache
// ---------------------------------------------------------------------------

unsigned int hash(const char* s, int len)
{
    unsigned int h = 0;
    for (int i = 0; i < len; i++)
        h = h * 137 + *(s + i);
    return h % 1987;
}

class recordCache
{
    table* m_tb;
    filter* m_pFilter;
    unsigned int m_row;
    unsigned int m_len;
    unsigned int m_unpackLen;
    unsigned int m_rowCount;
    bookmark_td m_bookmark;
    uchar_td* m_ptr;
    uchar_td* m_tmpPtr;
    blobHeader* m_hd;
    short_td m_seekMultiStat;
    int m_memblockType;

public:
    inline recordCache(table* tb) : m_tb(tb), m_memblockType(mra_first)
    {
        reset();
    }

    inline void reset()
    {
        m_pFilter = NULL;
        m_row = 0;
        m_rowCount = 0;
        m_ptr = NULL;
        m_len = 0;
        m_tmpPtr = NULL;
        m_memblockType = mra_first;
    }

    inline void setMemblockType(int v) { m_memblockType = v; }

    inline void hasManyJoin(int rowCount, uchar_td* data)
    {
        int rowOffset = 0;
        int row = 0; // zero start
        int count = 0;
        char* ptr = (char*)data + DATASIZE_BYTE; // rowCount
        unsigned short len = 0;
        for (int i = 0; i < (int)rowCount; ++i)
        {
            ptr += len;
            len = *((unsigned short*)ptr);
            ptr += DATASIZE_BYTE;

            // get sequential number
            int tmp = *((int*)(ptr));
            ptr += sizeof(int);
            // If len == 0 then next first record read error
            if ((len == 0) || (tmp != row))
            {
                if (count)
                {
                    m_tb->m_impl->mraPtr->duplicateRow(row + rowOffset, count);
                    rowOffset += count;
                }
                ++row;
                count = 0;
            }
            else if (i != 0)
                ++count;
        }
        if (count)
            m_tb->m_impl->mraPtr->duplicateRow(row + rowOffset, count);
    }

    inline void reset(filter* p, uchar_td* data, unsigned int totalSize,
                      const blobHeader* hd)
    {
        m_pFilter = p;
        m_row = 0;
        m_rowCount = *((unsigned short*)data);
        m_ptr = data + DATASIZE_BYTE;
        m_len = m_unpackLen =
            *((unsigned short*)m_ptr); // Not include bookmark and size bytes.
        m_ptr += DATASIZE_BYTE;
        if (m_pFilter->bookmarkSize())
        {
            m_bookmark = *((bookmark_td*)(m_ptr));
            m_ptr += m_pFilter->bookmarkSize();
        }
        m_tmpPtr = data + totalSize;
        if (m_tb->m_impl->mraPtr)
        {
            if (m_pFilter->hasManyJoin())
                hasManyJoin(m_rowCount, data);
            size_t recordLen = m_pFilter->fieldSelected()
                                   ? m_pFilter->totalFieldLen()
                                   : m_tb->tableDef()->maxRecordLen;
            m_tb->m_impl->mraPtr->init(m_rowCount, recordLen, m_memblockType,
                                       m_tb);
        }
        m_hd = const_cast<blobHeader*>(hd);
    }

    inline const uchar_td* moveRow(int count)
    {
        // move row data address pointer in result buffer
        for (int i = 0; i < count; i++)
        {
            m_ptr += m_len;
            m_len = m_unpackLen = *((unsigned short*)m_ptr);
            m_ptr += DATASIZE_BYTE;
            if (m_pFilter->bookmarkSize())
            {
                m_bookmark = *((bookmark_td*)(m_ptr));
                m_ptr += m_pFilter->bookmarkSize();
            }
        }
        if (m_hd)
        {
            // blob pointer is allready point to next row
            while (m_row - m_hd->curRow)
            {
                for (int j = 0; j < m_hd->fieldCount; ++j)
                    m_hd->nextField = (blobField*)m_hd->nextField->next();
                ++m_hd->curRow;
            }
        }

        multiRecordAlocator* mra = m_tb->m_impl->mraPtr;

        m_tb->m_fddefs->strBufs()->clear();

        if ((m_len == 0) && m_pFilter->isSeeksMode() && m_pFilter->fieldCount())
        {
            /*seek error*/
            m_seekMultiStat = STATUS_NOT_FOUND_TI;
            if (mra)
            {
                m_tmpPtr = mra->ptr(m_row, mra_current_block);
                mra->setInvalidRecord(m_row, true);
            }
            else
                memset(m_tmpPtr, 0, m_tb->tableDef()->maxRecordLen);
            return m_tmpPtr;
        }
        else
            m_seekMultiStat = 0;

        if (mra)
            m_tmpPtr = mra->ptr(m_row, mra_current_block);

        if (m_pFilter->fieldSelected())
        {
            int resultOffset = 0;
            uchar_td* fieldPtr = m_ptr;
            if (!mra)
                memset(m_tmpPtr, 0, m_tb->tableDef()->maxRecordLen);
            for (int i = 0; i < m_pFilter->fieldCount(); i++)
            {
                const fielddef& fd =
                    m_tb->tableDef()
                        ->fieldDefs[m_pFilter->selectFieldIndexes()[i]];
                if (!mra)
                    resultOffset = fd.pos;
                fieldPtr += fd.unPackCopy(m_tmpPtr + resultOffset, fieldPtr);
                if (mra)
                    resultOffset += fd.len;
            }
            m_tb->setBlobFieldPointer((char*)m_tmpPtr, m_hd);
            return m_tmpPtr;
        }
        else if (m_tb->valiableFormatType())
        {
            memset(m_tmpPtr, 0, m_tb->tableDef()->maxRecordLen);
            memcpy(m_tmpPtr, m_ptr, m_len);
            m_unpackLen = m_tb->unPack((char*)m_tmpPtr, m_len);
            m_tb->setBlobFieldPointer((char*)m_tmpPtr, m_hd);
            return m_tmpPtr;
        }
        else
        {
            if (mra)
            {
                memcpy(m_tmpPtr, m_ptr, m_len);
                m_tb->setBlobFieldPointer((char*)m_tmpPtr, m_hd);
                return m_tmpPtr;
            }
            else
                m_tb->setBlobFieldPointer((char*)m_ptr, m_hd);
            return m_ptr;
        }
    }

    inline const uchar_td* setRow(unsigned int rowNum)
    {
        if (rowNum < m_rowCount)
        {
            unsigned int moveCount = rowNum - m_row;
            m_row = rowNum;
            return moveRow(moveCount);
        }
        return NULL;
    }

    inline unsigned int len() const { return m_unpackLen; };
    inline bookmark_td bookmarkCurRow() const { return m_bookmark; };
    inline int row() const { return m_row; }

    inline int rowCount() const { return m_rowCount; }
    inline bool isEndOfRow(unsigned int row) const
    {
        return (m_rowCount && (row == m_rowCount));
    }
    inline bool withinCache(unsigned int row) const
    {
        return (row < m_rowCount);
    }
    inline short_td seekMultiStat() { return m_seekMultiStat; }
};

table::table(nsdatabase* pbe) : nstable(pbe)
{
    m_fddefs = fielddefs::create();
    m_impl = new tbimpl(*this);

    m_impl->rc = new recordCache(this);

    m_keybuflen = MAX_KEYLEN;
    m_pdata = NULL;
    m_keybuf = &m_impl->keybuf[0];
    m_keynum = 0;
}

table::~table()
{
    delete m_impl->rc;
    m_fddefs->release();
    delete m_impl;
}

void table::setMra(multiRecordAlocator* p)
{
    m_impl->mraPtr = p;
}

multiRecordAlocator* table::mra() const
{
    return m_impl->mraPtr;
}

uchar_td table::charset() const
{
    return m_tableDef->charsetIndex;
};

bool table::trimPadChar() const
{
    return m_fddefs->trimPadChar;
}

void table::setTrimPadChar(bool v)
{
    m_fddefs->trimPadChar = v;
}

bool table::usePadChar() const
{
    return m_fddefs->usePadChar;
};

void table::setUsePadChar(bool v)
{
    m_fddefs->usePadChar = v;
};

void* table::dataBak() const
{
    return m_impl->dataBak;
};

void table::setDataBak(void* v)
{
    m_impl->dataBak = v;
};

void* table::optionalData() const
{
    return m_impl->optionalData;
}

void table::setOptionalData(void* v)
{
    m_impl->optionalData = v;
}

bool table::myDateTimeValueByBtrv() const
{
    return m_fddefs->myDateTimeValueByBtrv;
}

bool table::logicalToString() const
{
    return m_fddefs->logicalToString;
};

void table::setLogicalToString(bool v)
{
    m_fddefs->logicalToString = v;
}

fields& table::fields()
{
    return m_impl->fields;
}

void table::setBookMarks(int StartId, void* Data, ushort_td Count)
{
    long size = (StartId + Count) * 6;

    if (!m_impl->bookMarks)
    {
        m_impl->bookMarks = malloc(BOOKMARK_ALLOC_SIZE);
        if (m_impl->bookMarks)
            m_impl->bookMarksMemSize = BOOKMARK_ALLOC_SIZE;
        else
        {
            m_stat = STATUS_CANT_ALLOC_MEMORY;
            return;
        }
    }

    if (m_impl->bookMarksMemSize < size)
    {

        m_impl->bookMarks =
            realloc(m_impl->bookMarks, size + BOOKMARK_ALLOC_SIZE);
        m_impl->bookMarksMemSize = size + BOOKMARK_ALLOC_SIZE;
    }
    if (m_impl->bookMarks)
    {
        if (StartId + Count - 1 > m_impl->maxBookMarkedCount)
            m_impl->maxBookMarkedCount = StartId + Count - 1;
        memcpy((void*)((char*)m_impl->bookMarks + ((StartId - 1) * 6)), Data,
               Count * 6);
    }
    else
        m_stat = STATUS_CANT_ALLOC_MEMORY;
    return;
}

inline short calcNextReadRecordCount(ushort_td curCount, int eTime)
{
    ushort_td ret = curCount;
    if (eTime == 0)
        ret = (ushort_td)(curCount * (short)2);
    else
        ret = (ushort_td)(curCount * ((float)100 / (float)eTime));

    if (ret > 9500)
        return 9500;
    if (ret == 0)
        return 1;
    return ret;
}

uint_td table::doRecordCount(bool estimate, bool fromCurrent)
{
    uint_td result = 0;

    if (m_impl->filterPtr)
    {
        short_td op = (m_impl->filterPtr->direction() == findForword)
                          ? TD_KEY_NEXT_MULTI
                          : TD_KEY_PREV_MULTI;

        if (tableDef()->keyCount == 0)
            op += TD_POS_NEXT_MULTI - TD_KEY_NEXT_MULTI; // KEY to POS
        short curStat = m_stat;
        m_impl->exBookMarking = true;
        ushort_td recCountOnce = 100;

        bookmark_td bm = bookmark();

        ushort_td tmpRejectCount = m_impl->filterPtr->rejectCount();
        ushort_td tmpRecCount = m_impl->filterPtr->recordCount();

        m_impl->filterPtr->setIgnoreFields(true);
        m_impl->maxBookMarkedCount = 0;
        if (fromCurrent)
            m_stat = curStat;
        else if (op == TD_KEY_NEXT_MULTI)
            seekFirst();
        else if (op == TD_KEY_PREV_MULTI)
            seekLast();
        else if (op == TD_POS_NEXT_MULTI)
            stepFirst();
        else if (op == TD_POS_PREV_MULTI)
            stepLast();

        m_impl->filterPtr->setMaxRows(recCountOnce);
        if (m_stat == 0)
        {
            m_impl->filterPtr->setPosTypeNext(false);
            boost::timer t;
            btrvGetExtend(op);
            int eTime = (int)(t.elapsed() * 1000);
            while ((m_stat == 0) || (m_stat == STATUS_LIMMIT_OF_REJECT) ||
                   (m_stat == STATUS_EOF) ||
                   (m_stat == STATUS_REACHED_FILTER_COND))
            {
                bool Complete = false;
                if ((m_stat == STATUS_EOF) ||
                    (m_stat == STATUS_REACHED_FILTER_COND))
                {
                    Complete = true;
                    m_stat = STATUS_EOF;
                }
                else if (m_stat == STATUS_LIMMIT_OF_REJECT)
                {
                    if (tmpRejectCount != 0)
                    {
                        if (tmpRejectCount == 1)
                            m_stat = STATUS_EOF;
                        Complete = true;
                    }
                }
                recCountOnce = calcNextReadRecordCount(recCountOnce, eTime);
                m_impl->filterPtr->setMaxRows(recCountOnce);
                result += *((ushort_td*)m_impl->dataBak);
                setBookMarks(m_impl->maxBookMarkedCount + 1,
                             (void*)((char*)m_impl->dataBak + 2),
                             *((ushort_td*)m_impl->dataBak));
                m_impl->maxBookMarkedCount = result;
                onRecordCounting(result, Complete);
                if (Complete)
                    break;
                t.restart();
                m_impl->filterPtr->setPosTypeNext(true);
                btrvGetExtend(op);
                eTime = (int)(t.elapsed() * 1000);
            }
        }

        short tmpStat = m_stat;
        m_impl->filterPtr->setIgnoreFields(false);
        m_impl->filterPtr->setMaxRows(tmpRecCount);

        if (bm)
            seekByBookmark(bm);
        m_impl->exBookMarking = false;
        m_stat = tmpStat;
        if (m_stat == STATUS_EOF)
            m_stat = 0;
    }
    else
        return nstable::doRecordCount(estimate, fromCurrent);

    return result;
}

void table::btrvGetExtend(ushort_td op)
{

    if (op >= TD_KEY_GE_NEXT_MULTI)
        m_keylen = writeKeyData();

    m_pdata = m_impl->dataBak;
    if (!m_impl->filterPtr->writeBuffer())
    {
        m_stat = STATUS_WARKSPACE_TOO_SMALL;
        return;
    }
    m_datalen = m_impl->filterPtr->exDataBufLen();

    // cacheing direction
    if ((op == TD_KEY_LE_PREV_MULTI) || (op == TD_KEY_PREV_MULTI) ||
        (op == TD_POS_PREV_MULTI))
        m_impl->filterPtr->setDirection(findBackForword);
    else
        m_impl->filterPtr->setDirection(findForword);

    tdap(op);
    if (m_stat && (m_stat != STATUS_LIMMIT_OF_REJECT) &&
        (m_stat != STATUS_REACHED_FILTER_COND) && (m_stat != STATUS_EOF))
        return;
    short stat = m_stat;
    if (!m_impl->filterPtr->isWriteComleted() &&
        (stat == STATUS_REACHED_FILTER_COND))
        stat = STATUS_LIMMIT_OF_REJECT;

    m_impl->rc->reset(m_impl->filterPtr, (uchar_td*)m_impl->dataBak, m_datalen,
                      blobFieldUsed() ? getBlobHeader() : NULL);

    m_stat = stat;
    m_impl->exSlideStat = m_stat;
    // There is the right record.
    if (m_impl->rc->rowCount() && (!m_impl->exBookMarking))
    {
        m_pdata = (void*)m_impl->rc->setRow(0);
        m_datalen = tableDef()->maxRecordLen;

        m_stat = m_impl->rc->seekMultiStat();
    }
    else if ((m_stat == STATUS_LIMMIT_OF_REJECT) &&
             (m_impl->filterPtr->rejectCount() >= 1))
        m_stat = STATUS_EOF;
}

void table::getRecords(ushort_td op)
{
    do
    {
        btrvGetExtend(op);
        m_impl->filterPtr->setPosTypeNext(true);

    } while (m_stat == STATUS_LIMMIT_OF_REJECT &&
             (m_impl->filterPtr->rejectCount() == 0));
    if ((m_stat == STATUS_REACHED_FILTER_COND) ||
        (m_stat == STATUS_LIMMIT_OF_REJECT))
        m_stat = STATUS_EOF;
}

bookmark_td table::bookmarkFindCurrent() const
{

    if (!m_impl->rc->isEndOfRow(m_impl->rc->row()))
        return m_impl->rc->bookmarkCurRow();
    return 0;
}

inline bool checkStatus(short v)
{
    return ((v == STATUS_SUCCESS) || (v == STATUS_NOT_FOUND_TI) ||
            (v == STATUS_EOF));
}

bool table::doSeekMultiAfter(int row)
{
    const std::vector<short>& fields = m_impl->filterPtr->selectFieldIndexes();

    if (m_stat == STATUS_SUCCESS)
        onReadAfter();
    else if (!checkStatus(m_stat))
        return false;
    if (m_stat)
        m_impl->mraPtr->setInvalidRecord(row, true);
    else
    {
        uchar_td* dst = m_impl->mraPtr->ptr(row, mra_current_block);
        if (m_impl->filterPtr->fieldSelected())
        {
            int resultOffset = 0;

            for (int j = 0; j < (int)fields.size(); ++j)
            {
                fielddef* fd = &tableDef()->fieldDefs[fields[j]];
                fd->unPackCopy(dst + resultOffset,
                               ((uchar_td*)m_pdata) + fd->pos);
                dst += fd->len;
            }
        }
        else
            memcpy(dst, (uchar_td*)m_pdata, m_datalen);
    }
    return true;
}

void table::btrvSeekMulti()
{
    const std::vector<client::seek>& seeks = m_impl->filterPtr->seeks();
    const bool transactd = false;
    bool hasManyJoin = m_impl->filterPtr->hasManyJoin();

    m_impl->rc->reset();
    size_t recordLen = m_impl->filterPtr->fieldSelected()
                           ? m_impl->filterPtr->totalSelectFieldLen()
                           : tableDef()->maxRecordLen;
    if (!hasManyJoin)
        m_impl->mraPtr->init(seeks.size(), recordLen, mra_first, this);

    m_keylen = m_keybuflen;
    m_datalen = m_buflen;
    int type = mra_first;
    int rowOffset = 0;
    for (int i = 0; i < (int)seeks.size(); ++i)
    {
        // 100% need allocate each row
        m_impl->mraPtr->init(1, recordLen, type, this);
        type = mra_nextrows;
        seeks[i].writeBuffer((uchar_td*)m_impl->keybuf, false, true, transactd);
        if (hasManyJoin)
        {
            tdap((ushort_td)(TD_KEY_OR_AFTER));
            if (!checkStatus(m_stat))
                return;

            if (memcmp(seeks[i].data, m_impl->keybuf, seeks[i].len) == 0)
            {
                doSeekMultiAfter(0);
                while (m_stat == 0)
                {
                    tdap((ushort_td)(TD_KEY_NEXT));
                    if (!checkStatus(m_stat))
                        return;
                    if (memcmp(seeks[i].data, m_impl->keybuf, seeks[i].len) !=
                        0)
                        break;
                    m_impl->mraPtr->duplicateRow(i + rowOffset, 1);
                    ++rowOffset;
                    m_impl->mraPtr->removeLastMemBlock(i + rowOffset);
                    m_impl->mraPtr->init(1, recordLen, type, this);
                    doSeekMultiAfter(0);
                }
            }
            else
                m_impl->mraPtr->setInvalidRecord(0, true);
        }
        else
        {
            tdap((ushort_td)(TD_KEY_SEEK));
            if (!doSeekMultiAfter(i))
                return;
        }
    }
    m_stat = STATUS_EOF;
}

void table::find(eFindType type)
{
    if (!m_impl->filterPtr)
    {
        m_stat = STATUS_FILTERSTRING_ERROR;
        return;
    }
    ushort_td op;

    if (m_impl->filterPtr->isSeeksMode())
    {
        m_impl->filterPtr->resetSeeksWrited();
        op = TD_KEY_SEEK_MULTI;
    }
    else
        op =
            (type == findForword) ? TD_KEY_GE_NEXT_MULTI : TD_KEY_LE_PREV_MULTI;

    if (isUseTransactd())
    {
        m_impl->rc->reset();
        doFind(op, true /*notIncCurrent*/);
    }
    else
    {
        if (op == TD_KEY_SEEK_MULTI)
        {

            if (m_impl->mraPtr)
                btrvSeekMulti();
            else
                m_stat = STATUS_FILTERSTRING_ERROR; // P.SQL not support
            // TD_KEY_SEEK_MULTI
            return;
        }
        if (type == findForword)
            seekGreater(true);
        else
            seekLessThan(true);
        if (m_stat == 0)
        {
            if (type == findForword)
                findNext(false);
            else
                findPrev(false);
        }
    }
}

void table::findFirst()
{
    seekFirst();
    if (m_stat == 0)
    {
        if (m_impl->filterPtr)
        {

            m_impl->exNext = 1;
            m_impl->filterPtr->setPosTypeNext(false);
            getRecords(TD_KEY_NEXT_MULTI);
        }
    }
}

void table::findLast()
{
    seekLast();
    if (m_stat == 0)
    {
        if (m_impl->filterPtr)
        {

            m_impl->exNext = -1;
            m_impl->filterPtr->setPosTypeNext(false);
            getRecords(TD_KEY_PREV_MULTI);
        }
    }
}

bool table::checkFindDirection(ushort_td op)
{
    bool ret;
    if ((op == TD_KEY_LE_PREV_MULTI) || (op == TD_KEY_PREV_MULTI))
        ret = (m_impl->filterPtr->direction() == findBackForword);
    else
        ret = (m_impl->filterPtr->direction() == findForword);
    if (!ret)
    {
        assert(0);
        m_stat = 1;
    }
    return ret;
}

void table::doFind(ushort_td op, bool notIncCurrent)
{
    /*
    First, read from cache.
    If whole row readed from cache then select operation by m_impl->exSlideStat

    */
    m_stat = 0;
    int row = m_impl->rc->row() + 1;

    if (m_impl->rc->withinCache(row) && (!m_impl->exBookMarking))
    { /* read from cache */

        /*Is direction same */
        if (!checkFindDirection(op))
            return;

        m_pdata = (void*)m_impl->rc->setRow(row);
        m_stat = m_impl->rc->seekMultiStat();

        /*set keyvalue for keyValueDescription*/
        if (m_stat != 0)
            setSeekValueField(row);

        // m_datalen = m_impl->rc->len();
        m_datalen = tableDef()->maxRecordLen;
    }
    else if (m_impl->rc->isEndOfRow(row))
    {
        /* whole row readed */
        /*Is direction same */
        if (!checkFindDirection(op))
            return;
        /* A special situation that if rejectCount() == 0 and status =
           STATUS_LIMMIT_OF_REJECT
                then it continues . */
        if ((m_impl->exSlideStat == 0) ||
            ((m_impl->exSlideStat == STATUS_LIMMIT_OF_REJECT) &&
             (m_impl->filterPtr->rejectCount() == 0)))
        {
            m_impl->rc->setMemblockType(mra_nextrows);
            getRecords(op);
            return;
        }
        if ((m_impl->exSlideStat == STATUS_LIMMIT_OF_REJECT) ||
            (m_impl->exSlideStat == STATUS_REACHED_FILTER_COND))
            m_stat = STATUS_EOF;
        else
            m_stat = m_impl->exSlideStat;
        m_impl->exSlideStat = 0;
    }
    else
    {
        m_impl->exNext =
            ((op == TD_KEY_NEXT_MULTI) || (op == TD_KEY_GE_NEXT_MULTI)) ? 1
                                                                        : -1;
        m_impl->filterPtr->setPosTypeNext(notIncCurrent);
        getRecords(op);
    }
}

void table::findNext(bool notIncCurrent)
{

    if (m_impl->filterPtr)
    {
        short op = m_impl->filterPtr->isSeeksMode() ? TD_KEY_SEEK_MULTI
                                                    : TD_KEY_NEXT_MULTI;
        doFind(op, notIncCurrent);
    }
    else if (notIncCurrent == true)
        seekNext();
}

void table::findPrev(bool notIncCurrent)
{
    if (m_impl->filterPtr)
        doFind(TD_KEY_PREV_MULTI, notIncCurrent);
    else if (notIncCurrent == true)
        seekPrev();
}

void table::setQuery(const queryBase* query)
{

    m_stat = 0;
    m_pdata = m_impl->dataBak;
    m_impl->rc->reset();
    m_impl->exBookMarking = false;
    m_impl->exSlideStat = 0;
    m_impl->exNext = 0;
    if (query == NULL)
    {
        m_impl->maxBookMarkedCount = 0;
        delete m_impl->filterPtr;
        m_impl->filterPtr = NULL;
        return;
    }
    if (m_impl->filterPtr)
        m_impl->filterPtr->init(this);
    else
        m_impl->filterPtr = new filter(this);
    if (m_impl->filterPtr == NULL)
    {
        m_stat = STATUS_CANT_ALLOC_MEMORY;
        return;
    }
    bool ret = false;
    try
    {
        ret = m_impl->filterPtr->setQuery(query);
    }
    catch (...)
    {
    }

    if (!ret)
    {
        m_stat = STATUS_FILTERSTRING_ERROR;
        delete m_impl->filterPtr;
        m_impl->filterPtr = NULL;
        return;
    }
}

void table::setFilter(const _TCHAR* str, ushort_td RejectCount,
                      ushort_td CashCount, bool autoEscape)
{
    if (!str || (str[0] == 0x00))
        setQuery(NULL);
    else
    {
        queryBase q;
        q.bookmarkAlso(true);
        q.queryString(str, autoEscape).reject(RejectCount).limit(CashCount);
        setQuery(&q);
    }
}

void table::clearBuffer()
{
    m_impl->rc->reset();
    m_pdata = m_impl->dataBak;
    memset(m_pdata, 0x00, m_buflen);
    if ((bulkIns() == NULL) && blobFieldUsed())
        resetSendBlob();
}

void table::getKeySpec(keySpec* ks, bool SpecifyKeyNum)
{
    keydef* KeyDef;
    short FieldNum;
    int j;

    KeyDef = &m_tableDef->keyDefs[m_keynum];
    for (j = 0; j < KeyDef->segmentCount; j++)
    {
        FieldNum = KeyDef->segments[j].fieldNum;
        ks[j].keyPos = (ushort_td)(m_tableDef->fieldDefs[FieldNum].pos + 1);
        ks[j].keyLen = m_tableDef->fieldDefs[FieldNum].len;
        ks[j].keyFlag.all = KeyDef->segments[j].flags.all;
        ks[j].keyCount = 0;
        ks[j].keyType = m_tableDef->fieldDefs[FieldNum].type;

        if (ks[j].keyFlag.bit3 == true)
            ks[j].nullValue = m_tableDef->fieldDefs[FieldNum].nullValue;
        else
            ks[j].nullValue = 0;
        ks[j].reserve2[0] = 0;
        ks[j].reserve2[1] = 0;

        if (SpecifyKeyNum == true)
            ks[j].keyNo = m_keynum;
        else
            ks[j].keyNo = 0;

        ks[j].acsNo = 0;
    }
}

void table::doCreateIndex(bool SpecifyKeyNum)
{
    int segmentCount = m_tableDef->keyDefs[m_keynum].segmentCount;

    keySpec* ks = (keySpec*)malloc(sizeof(keySpec) * segmentCount);
    memset(ks, 0, sizeof(keySpec) * segmentCount);
    getKeySpec(ks, SpecifyKeyNum);
    m_pdata = ks;
    m_datalen = sizeof(keySpec) * segmentCount;
    nstable::doCreateIndex(SpecifyKeyNum);
    m_pdata = m_impl->dataBak;
    free(ks);
}

void table::smartUpdate()
{
    if (!m_impl->smartUpDate)
        m_impl->smartUpDate = malloc(m_buflen);
    if (m_impl->smartUpDate)
    {
        memcpy(m_impl->smartUpDate, data(), m_buflen);
        m_impl->smartUpDateFlag = true;
    }
    else
        m_impl->smartUpDateFlag = false;
}

bool table::isUniqeKey(char_td keynum)
{
    if ((keynum >= 0) && (keynum < m_tableDef->keyCount))
    {
        keydef* kd = &m_tableDef->keyDefs[m_keynum];
        return !(kd->segments[0].flags.bit0);
    }
    return false;
}

bool table::onUpdateCheck(eUpdateType type)
{
    // Check uniqe key
    if (type == changeInKey)
    {
        if (!isUniqeKey(m_keynum))
        {
            m_stat = STATUS_INVALID_KEYNUM;
            return false;
        }
        else
        {
            if (isUseTransactd() == false)
            {
                // backup update data
                smartUpdate();
                seek();
                m_impl->smartUpDateFlag = false;
                if (m_stat)
                    return false;
                memcpy(m_pdata, m_impl->smartUpDate, m_datalen);
            }
        }
    }
    else if (m_impl->smartUpDateFlag)
    {
        m_stat = 0;
        if (memcmp(m_impl->smartUpDate, data(), m_buflen) == 0)
        {
            m_impl->smartUpDateFlag = false;
            return false;
        }
    }
    return true;
}

void table::onUpdateAfter(int beforeResult)
{
    if (blobFieldUsed())
        addSendBlob(NULL);

    if (valiableFormatType() && m_impl->dataPacked)
        m_datalen = unPack((char*)m_pdata, m_datalen);
}

bool table::onDeleteCheck(bool inkey)
{
    client::database* db = static_cast<client::database*>(nsdb());
    deleteRecordFn func = db->onDeleteRecord();
    if (func)
    {
        if (func(db, this, inkey))
        {
            m_stat = STATUS_CANT_DEL_FOR_REL;
            return false;
        }
    }
    return true;
}

ushort_td table::doCommitBulkInsert(bool autoCommit)
{
    ushort_td ret = nstable::doCommitBulkInsert(autoCommit);
    if (blobFieldUsed())
        addSendBlob(NULL);
    return ret;
}

void table::doAbortBulkInsert()
{
    nstable::doAbortBulkInsert();
    if (blobFieldUsed())
        addSendBlob(NULL);
}

void table::onInsertAfter(int beforeResult)
{
    if (valiableFormatType() && m_impl->dataPacked)
        m_datalen = unPack((char*)m_pdata, m_datalen);
    if ((bulkIns() == NULL) && blobFieldUsed())
        addSendBlob(NULL);
}

void* table::attachBuffer(void* NewPtr, bool unpack, size_t size)
{
    void* oldptr;
    if (!m_impl->bfAtcPtr)
        m_impl->bfAtcPtr = m_pdata;
    oldptr = m_pdata;
    m_pdata = NewPtr;
    ushort_td len = recordLength();
    if (len < m_tableDef->maxRecordLen)
        len = m_tableDef->maxRecordLen;
    if (unpack)
        len = unPack((char*)m_pdata, size);
    m_datalen = len;
    return oldptr;
}

void table::dettachBuffer()
{
    if (m_impl->bfAtcPtr)
        m_pdata = m_impl->bfAtcPtr;
    m_impl->bfAtcPtr = NULL;
}

void table::init(tabledef* Def, short fnum, bool regularDir)
{
    doInit(Def, fnum, regularDir);
}

void table::doInit(tabledef* Def, short fnum, bool /*regularDir*/)
{
    m_tableDef = Def;
    m_fddefs->addAllFileds(m_tableDef);
    ushort_td len;

    m_fddefs->cv()->setCodePage(mysql::codePage(m_tableDef->charsetIndex));

    if ((len = recordLength()) < m_tableDef->maxRecordLen)
        len = m_tableDef->maxRecordLen;

    if (len == 0)
    {
        m_stat = STATUS_INVALID_RECLEN;
        return;
    }

    for (short i = 0; i < m_tableDef->keyCount; i++)
    {
        if (m_tableDef->flags.bitA == true)
            m_impl->keyNumIndex[m_tableDef->keyDefs[i].keyNumber] = (char)i;
        else
            m_impl->keyNumIndex[i] = (char)i;
    }
    if (m_impl->dataBak)
        free(m_impl->dataBak);
    m_impl->dataBak = (void*)malloc(len);

    if (m_impl->dataBak == NULL)
    {
        if (m_impl->dataBak)
            free(m_impl->dataBak);
        m_impl->dataBak = NULL;
        m_stat = STATUS_CANT_ALLOC_MEMORY;
        return;
    }
    m_pdata = m_impl->dataBak;
    m_buflen = len;
    m_datalen = len;
    setTableid(fnum);
}

keylen_td table::writeKeyDataTo(uchar_td* to, int keySize)
{
    if (m_tableDef->keyCount)
    {
        keydef& keydef =
            m_tableDef->keyDefs[(short)m_impl->keyNumIndex[m_keynum]];
        uchar_td* start = to;
        if (keySize == 0)
            keySize = keydef.segmentCount;

        for (int j = 0; j < keySize; j++)
        {
            int fdnum = keydef.segments[j].fieldNum;
            fielddef& fd = m_tableDef->fieldDefs[fdnum];
            uchar_td* from = (uchar_td*)m_pdata + fd.pos;
            to = fd.keyCopy(to, from);
        }
        return (keylen_td)(to - start);
    }
    return 0;
}

keylen_td table::writeKeyData()
{
    return writeKeyDataTo((uchar_td*)m_impl->keybuf, 0);
}

uint_td table::pack(char* ptr, size_t size)
{
    char* pos = ptr;
    char* end = pos + size;
    int movelen;
    for (int i = 0; i < m_tableDef->fieldCount; i++)
    {
        fielddef& fd = m_tableDef->fieldDefs[i];
        if (fd.type == ft_myfixedbinary)
        {
            memmove(pos + 2, pos, fd.len - 2); // move as size pace in the field
            *((unsigned short*)(pos)) = fd.len - 2; // fixed size
            pos += fd.len;
        }
        else
        {
            int blen = fd.varLenBytes();
            int dl = fd.len; // length
            if (blen == 1)
                dl = *((unsigned char*)(pos)) + blen;
            else if (blen == 2)
                dl = *((unsigned short*)(pos)) + blen;
            pos += dl;
            if ((movelen = fd.len - dl) != 0)
            {
                end -= movelen;
                memmove(pos, pos + movelen, end - pos);
            }
        }
    }
    m_impl->dataPacked = true;
    return (uint_td)(pos - ptr);
}

uint_td table::doGetWriteImageLen()
{
    if (!blobFieldUsed() && !valiableFormatType() &&
        (m_tableDef->flags.bit0 == false))
        return m_buflen;
    // Make blob pointer list
    if (blobFieldUsed())
    {
        for (ushort_td i = 0; i < m_tableDef->fieldCount; i++)
        {
            fielddef& fd = m_tableDef->fieldDefs[i];
            uint_td bytes = fd.blobLenBytes();
            if (bytes)
            {
                uchar_td* fdptr = (uchar_td*)m_pdata + fd.pos;
                blob b(fd.blobDataLen(fdptr), i, fd.blobDataPtr(fdptr));
                addSendBlob(&b);
            }
        }
        addBlobEndRow(); // end row
    }
    else
        addSendBlob(NULL);

    if (valiableFormatType())
        return pack((char*)m_pdata, m_buflen);
    else
    {
        fielddef* fd = &m_tableDef->fieldDefs[m_tableDef->fieldCount - 1];
        size_t len = 0;
        short* pos;

        if (fd->type == ft_note)
            len = strlen((char*)fieldPtr((short)(m_tableDef->fieldCount - 1))) +
                  1;
        else if (fd->type == ft_lvar)
        {
            // xx................xx.............00
            // ln--data----------ln-----data----00
            pos = (short*)fieldPtr((short)(m_tableDef->fieldCount - 1));
            while (*pos)
            {
                len += 2; // size
                len += *pos;
                pos = (short*)((char*)pos + (*pos + 2)); // next position
            }
            len += 2;
        }
        else
            len = fd->len;

        len += fd->pos;

        return (uint_td)len;
    }
}

uint_td table::unPack(char* ptr, size_t size)
{
    char* pos = ptr;
    const char* end = pos + size;
    const char* max = pos + m_buflen;
    int movelen;
    for (int i = 0; i < m_tableDef->fieldCount; i++)
    {
        fielddef& fd = m_tableDef->fieldDefs[i];
        if (fd.type == ft_myfixedbinary)
        {
            int dl = *((unsigned short*)(pos));
            assert(dl == fd.len - 2);
            memmove(pos, pos + 2, dl);
            pos += dl;
            *((unsigned short*)(pos)) = 0x00;
            ;
            pos += 2;
        }
        else
        {
            int blen = fd.varLenBytes();
            int dl = fd.len; // length
            if (blen == 1)
                dl = *((unsigned char*)(pos)) + blen;
            else if (blen == 2)
                dl = *((unsigned short*)(pos)) + blen;
            if ((movelen = fd.len - dl) != 0)
            {
                if (max < end + movelen)
                    return 0;
                char* src = pos + dl;
                memmove(pos + fd.len, src, end - src);
                memset(src, 0, movelen);
                end += movelen;
            }
            pos += fd.len;
        }
    }
    m_impl->dataPacked = false;
    return (uint_td)(pos - ptr);
}

void table::addBlobEndRow()
{
    char_td knum = m_keynum;
    m_keynum = TD_ASBLOB_ENDROW;
    addSendBlob(NULL);
    m_keynum = knum;
}

void table::resetSendBlob()
{
    addSendBlob(NULL);
    m_fddefs->blobClear();
}

void table::addSendBlob(const blob* blob)
{
    short stat = m_stat;
    /*backup current data buffer*/
    const void* tmp = data();
    setData((void*)blob);
    tdap(TD_ADD_SENDBLOB);
    /*restore data buffer*/
    setData((void*)tmp);
    m_stat = stat;
}

const blobHeader* table::getBlobHeader()
{
    short stat = m_stat;
    const blobHeader* p;
    /*backup current data buffer*/
    const void* tmp = data();
    setData(&p);
    tdap(TD_GET_BLOB_BUF);
    /*restore data buffer*/
    setData((void*)tmp);
    std::swap(stat, m_stat);

    if (stat)
        return NULL;
    return p;
}

void table::setBlobFieldPointer(char* ptr, const blobHeader* hd)
{
    if (hd)
    {
        assert(hd->curRow < hd->rows);
        const blobField* f = hd->nextField;
        for (int i = 0; i < hd->fieldCount; i++)
        {
            fielddef& fd = m_tableDef->fieldDefs[f->fieldNum];
            char* fdptr = ptr + fd.pos;
            int sizeByte = fd.blobLenBytes();
            memcpy(fdptr, &f->size, sizeByte);
            const char* data = f->data();
            memcpy(fdptr + sizeByte, &data, sizeof(char*));
            f = f->next();
        }
        ++hd->curRow;
        hd->nextField = (blobField*)f;
    }
}

void table::onReadAfter()
{
    m_fddefs->strBufs()->clear();
    if (valiableFormatType())
    {
        m_datalen = unPack((char*)m_pdata, m_datalen);
        if (m_datalen == 0)
            m_stat = STATUS_BUFFERTOOSMALL;
    }
    if (blobFieldUsed())
    {
        const blobHeader* hd = getBlobHeader();
        setBlobFieldPointer((char*)m_pdata, hd);
    }
    if (m_buflen - m_datalen > 0)
        memset((char*)m_pdata + m_datalen, 0, m_buflen - m_datalen);
}

short table::fieldNumByName(const _TCHAR* name)
{
    return m_fddefs->indexByName(name);
}

void* table::fieldPtr(short index) const
{
    if (!checkIndex(index))
        return NULL;
    return m_impl->fields[index].ptr();
}

void table::setFVA(short index, const char* data)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFVA(data);
}

#ifdef _WIN32

void table::setFVW(short index, const wchar_t* data)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFVW(data);
}

void table::setFVW(const _TCHAR* FieldName, const wchar_t* data)
{
    short index = fieldNumByName(FieldName);
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFVW(data);
}

#endif //_WIN32

void table::setFV(short index, unsigned char data)
{
    if (!checkIndex(index))
        return;
    int value = (long)data;
    setFV(index, value);
}

void table::setFV(short index, int data)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFV(data);
}

void table::setFV(short index, double data)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFV(data);
}

void table::setFV(short index, short data)
{
    if (!checkIndex(index))
        return;
    int value = (int)data;
    setFV(index, value);
}

void table::setFV(short index, float data)
{
    if (!checkIndex(index))
        return;
    double value = (double)data;
    setFV(index, value);
}

short table::getFVsht(short index)
{
    return (short)getFVlng(index);
}

int table::getFVlng(short index)
{
    if (!checkIndex(index))
        return 0;
    return m_impl->fields[index].getFVlng();
}

float table::getFVflt(short index)
{
    return (float)getFVdbl(index);
}

double table::getFVdbl(short index)
{
    if (!checkIndex(index))
        return 0;
    return m_impl->fields[index].getFVdbl();
}

unsigned char table::getFVbyt(short index)
{
    return (unsigned char)getFVlng(index);
}

#ifdef _WIN32

const wchar_t* table::getFVWstr(short index)
{
    if (!checkIndex(index))
        return NULL;
    return m_impl->fields[index].getFVWstr();
}

const wchar_t* table::getFVWstr(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVWstr(index);
}
#endif //_WIN32

const char* table::getFVAstr(short index)
{
    if (index == -1)
        return NULL;
    return m_impl->fields[index].getFVAstr();
}

int table::getFVint(short index)
{
    return (int)getFVlng(index);
}

int table::getFVint(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return (int)getFVlng(index);
}

int table::getFVlng(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVlng(index);
}

const char* table::getFVAstr(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVAstr(index);
}

double table::getFVdbl(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVdbl(index);
}

unsigned char table::getFVbyt(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVbyt(index);
}

short table::getFVsht(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVsht(index);
}

float table::getFVflt(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFVflt(index);
}

void table::setFV(const _TCHAR* FieldName, int data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

void table::setFVA(const _TCHAR* FieldName, const char* data)
{
    short index = fieldNumByName(FieldName);
    setFVA(index, data);
}

void table::setFV(const _TCHAR* FieldName, double data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

void table::setFV(const _TCHAR* FieldName, float data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

void table::setFV(const _TCHAR* FieldName, unsigned char data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

void table::setFV(const _TCHAR* FieldName, short data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

__int64 table::getFV64(const _TCHAR* FieldName)
{
    short index = fieldNumByName(FieldName);
    return getFV64(index);
}

void table::setFV(const _TCHAR* FieldName, __int64 data)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data);
}

__int64 table::getFV64(short index)
{
    if (!checkIndex(index))
        return 0;
    return m_impl->fields[index].getFV64();
}

void table::setFV(short index, __int64 data)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFV(data);
}

void table::setFV(const _TCHAR* FieldName, const void* data, uint_td size)
{
    short index = fieldNumByName(FieldName);
    setFV(index, data, size);
}

/* if blob and text ,set binary data that is only set pointer. it is not copied.
 *  Caller must hold data until it sends to the server.
 */
void table::setFV(short index, const void* data, uint_td size)
{
    if (!checkIndex(index))
        return;
    m_impl->fields[index].setFV(data, size);
}

void* table::getFVbin(const _TCHAR* FieldName, uint_td& size)
{
    short index = fieldNumByName(FieldName);
    return getFVbin(index, size);
}

/* offset is writen at data that is first address of data
 *  text is not converted to unicode form stored charset.
 */
void* table::getFVbin(short index, uint_td& size)
{
    if (!checkIndex(index))
        return NULL;
    return m_impl->fields[index].getFVbin(size);
}

bool table::checkIndex(short index) const
{
    if ((index >= m_tableDef->fieldCount) || (index < 0))
    {
        m_stat = STATUS_INVARID_FIELD_IDX;
        return false;
    }
    return true;
}

unsigned int table::getRecordHash()
{
    return hash((const char*)fieldPtr(0), datalen());
}

int table::bookMarksCount() const
{
    int ret;
    ret = m_impl->maxBookMarkedCount;
    return ret;
}

void table::moveBookmarksId(long id)
{
    long Point = (id - 1) * 6 + 2;

    if ((id <= m_impl->maxBookMarkedCount) && (m_impl->bookMarks))
        seekByBookmark(*((bookmark_td*)((char*)m_impl->bookMarks + Point)));
    else
        seekByBookmark(0);
}

short_td table::doBtrvErr(HWND hWnd, _TCHAR* retbuf)
{
    return nstable::tdapErr(hWnd, m_stat, m_tableDef->tableName(), retbuf);
}

/* For keyValueDescription */
bool table::setSeekValueField(int row)
{
    const std::vector<client::seek>& keyValues = m_impl->filterPtr->seeks();
    keydef* kd = &tableDef()->keyDefs[keyNum()];
    if (keyValues.size() % kd->segmentCount)
        return false;
    // Check uniqe key
    if (kd->segments[0].flags.bit0)
        return false;

    const uchar_td* ptr = (const uchar_td*)keyValues[row].data;
    const uchar_td* data;
    ushort_td dataSize;
    if (ptr)
    {
        for (int j = 0; j < kd->segmentCount; ++j)
        {
            short filedNum = kd->segments[j].fieldNum;
            fielddef& fd = tableDef()->fieldDefs[filedNum];
            ptr = fd.getKeyValueFromKeybuf(ptr, &data, dataSize);
            if (data)
            {
                if (fd.maxVarDatalen())
                    setFV(filedNum, data, dataSize);
                else
                    memcpy(fieldPtr(filedNum), data, dataSize);
            }
            else
                setFV(filedNum, _T(""));
        }
    }
    else
        return false;
    return true;
}

void table::keyValueDescription(_TCHAR* buf, int bufsize)
{

    std::_tstring s;
    if (stat() == STATUS_NOT_FOUND_TI)
    {

        for (int i = 0; i < tableDef()->keyDefs[keyNum()].segmentCount; i++)
        {
            short fnum = tableDef()->keyDefs[keyNum()].segments[i].fieldNum;
            s += std::_tstring(tableDef()->fieldDefs[fnum].name()) + _T(" = ") +
                 getFVstr(fnum) + _T("\n");
        }
    }
    else if (stat() == STATUS_DUPPLICATE_KEYVALUE)
    {
        _TCHAR tmp[50];
        for (int j = 0; j < tableDef()->keyCount; j++)
        {
            _stprintf_s(tmp, 50, _T("[key%d]\n"), j);
            s += tmp;
            for (int i = 0; i < tableDef()->keyDefs[j].segmentCount; i++)
            {
                short fnum = tableDef()->keyDefs[j].segments[i].fieldNum;
                s += std::_tstring(tableDef()->fieldDefs[fnum].name()) +
                     _T(" = ") + getFVstr(fnum) + _T("\n");
            }
        }
    }

    _stprintf_s(buf, bufsize, _T("table:%s\nstat:%d\n%s"),
                tableDef()->tableName(), stat(), s.c_str());
}

short table::getCurProcFieldCount() const
{
    if (!m_impl->filterPtr || !m_impl->filterPtr->fieldSelected())
        return tableDef()->fieldCount;
    return (short)m_impl->filterPtr->selectFieldIndexes().size();
}

short table::getCurProcFieldIndex(short index) const
{
    if (!m_impl->filterPtr || !m_impl->filterPtr->fieldSelected())
        return index;
    return m_impl->filterPtr->selectFieldIndexes()[index];
}

//-------------------------------------------------------------------
//      class queryBase
//-------------------------------------------------------------------
typedef boost::escaped_list_separator<_TCHAR> esc_sep;
typedef boost::tokenizer<esc_sep, std::_tstring::const_iterator, std::_tstring>
    tokenizer;

void analyzeQuery(const _TCHAR* str, std::vector<std::_tstring>& selects,
                  std::vector<std::_tstring>& where,
                  std::vector<std::_tstring>& keyValues, bool& nofilter)
{
    esc_sep sep(_T('&'), _T(' '), _T('\''));
    std::_tstring s = str;
    std::_tstring tmp;
    tokenizer tokens(s, sep);

    tokenizer::iterator it = tokens.begin();
    if (it == tokens.end())
        return;
    if (*it == _T("*"))
    {
        nofilter = true;
        return;
    }
    tmp = *it;
    boost::algorithm::to_lower(tmp);
    if (tmp == _T("select"))
    {
        tokenizer::iterator itTmp = it;
        tmp = *(++it);
        if (getFilterLogicTypeCode(tmp.c_str()) == 255)
        {
            esc_sep sep(_T('&'), _T(','), _T('\''));
            tokenizer fields(tmp, sep);
            tokenizer::iterator itf = fields.begin();
            while (itf != fields.end())
                selects.push_back(*(itf++));
            ++it;
        }
        else
            it = itTmp; // field name is select
    }
    if (it == tokens.end())
        return;
    tmp = *it;
    boost::algorithm::to_lower(tmp);
    bool enableWhere = true;
    if (tmp == _T("in"))
    {
        tokenizer::iterator itTmp = it;
        tmp = *(++it);
        if (getFilterLogicTypeCode(tmp.c_str()) == 255)
        {
            enableWhere = false;
            esc_sep sep(_T('&'), _T(','), _T('\''));
            tokenizer values(tmp, sep);
            tokenizer::iterator itf = values.begin();
            while (itf != values.end())
                keyValues.push_back(*(itf++));
        }
        else
            it = itTmp; // field name is in
    }
    if (enableWhere)
    {
        while (it != tokens.end())
            where.push_back(*(it++));
    }
}

keyValuePtr::keyValuePtr(const void* p, ushort_td l, short typeStr)
    : len(l), type(typeStr)
{
    if (type & KEYVALUE_NEED_COPY)
    {
        _TCHAR* tmp = new _TCHAR[len + 1];
        _tcsncpy(tmp, (_TCHAR*)p, len);
        tmp[len] = 0x00;
        ptr = tmp;
    }
    else
        ptr = p;
}

keyValuePtr::~keyValuePtr()
{
    if (type & KEYVALUE_NEED_COPY)
        delete[](_TCHAR*)ptr;
}

struct impl
{
    impl()
        : m_reject(1), m_limit(0), m_joinKeySize(0),
          m_optimize(queryBase::none), m_direction(table::findForword),
          m_nofilter(false), m_withBookmark(false)
    {
    }

    mutable std::_tstring m_str;
    std::vector<std::_tstring> m_selects;
    std::vector<std::_tstring> m_wheres;
    std::vector<std::_tstring> m_keyValues;
    std::vector<keyValuePtr> m_keyValuesPtr;
    int m_reject;
    int m_limit;
    int m_joinKeySize;
    queryBase::eOptimize m_optimize;
    table::eFindType m_direction;
    bool m_nofilter;
    bool m_withBookmark;
};

queryBase::queryBase() : m_impl(new impl)
{
}

queryBase::queryBase(const queryBase& r) : m_impl(new impl(*r.m_impl))
{
}

queryBase& queryBase::operator=(const queryBase& r)
{
    if (this != &r)
    {
        *m_impl = *r.m_impl;
    }
    return *this;
}

queryBase::~queryBase()
{
    delete m_impl;
}

void queryBase::reset()
{
    delete m_impl;
    m_impl = new impl;
}

void queryBase::clearSelectFields()
{
    m_impl->m_selects.clear();
}

void queryBase::addField(const _TCHAR* name)
{
    m_impl->m_selects.push_back(name);
    m_impl->m_nofilter = false;
}

void queryBase::addLogic(const _TCHAR* name, const _TCHAR* logic,
                         const _TCHAR* value)
{
    m_impl->m_keyValuesPtr.clear();
    m_impl->m_keyValues.clear();
    m_impl->m_wheres.clear();
    m_impl->m_wheres.push_back(name);
    m_impl->m_wheres.push_back(logic);
    m_impl->m_wheres.push_back(value);
    m_impl->m_nofilter = false;
}

void queryBase::addLogic(const _TCHAR* combine, const _TCHAR* name,
                         const _TCHAR* logic, const _TCHAR* value)
{
    m_impl->m_wheres.push_back(combine);
    m_impl->m_wheres.push_back(name);
    m_impl->m_wheres.push_back(logic);
    m_impl->m_wheres.push_back(value);
    m_impl->m_nofilter = false;
}

void queryBase::reserveSeekKeyValueSize(size_t v)
{
    m_impl->m_keyValues.reserve(v);
}

void queryBase::reserveSeekKeyValuePtrSize(size_t v)
{
    m_impl->m_keyValuesPtr.reserve(v);
}

void queryBase::addSeekKeyValue(const _TCHAR* value, bool reset)
{
    if (reset)
    {
        m_impl->m_wheres.clear();
        m_impl->m_keyValues.clear();
        m_impl->m_keyValuesPtr.clear();
    }
    m_impl->m_keyValues.push_back(value);
    // m_impl->m_reject = 1;
    m_impl->m_nofilter = false;
}

void queryBase::addSeekKeyValuePtr(const void* value, ushort_td len,
                                   short typeStr, bool reset)
{
    if (reset)
    {
        m_impl->m_wheres.clear();
        m_impl->m_keyValues.clear();
        m_impl->m_keyValuesPtr.clear();
    }
    m_impl->m_keyValuesPtr.push_back(keyValuePtr(value, len, typeStr));
    m_impl->m_nofilter = false;
}

void queryBase::clearSeekKeyValues()
{
    m_impl->m_keyValues.clear();
    m_impl->m_keyValuesPtr.clear();
}

std::_tstring escape_value(std::_tstring s)
{
    for (int i = (int)s.size() - 1; i >= 0; --i)
    {
        if (s[i] == _T('&'))
            s.insert(s.begin() + i, _T('&'));
        else if (s[i] == _T('\''))
            s.insert(s.begin() + i, _T('&'));
    }
    return s;
}

std::_tstring& escape_string(std::_tstring& s)
{
    bool begin = false;
    for (int i = 0; i < (int)s.size(); ++i)
    {
        if (s[i] == _T('&'))
        {
            s.insert(s.begin() + i, _T('&'));
            ++i;
        }
        else if (s[i] == _T('\''))
        {
            if (begin)
            {
                if ((i == (int)s.size() - 1) || (s[i + 1] == _T(' ')))
                    begin = false;
                else
                    s.insert(s.begin() + i, _T('&'));
                ++i;
            }
            else if ((i == 0) || (s[i - 1] == _T(' ')))
                begin = true;
            else
            {
                s.insert(s.begin() + i, _T('&'));
                ++i;
            }
        }
    }
    return s;
}

queryBase& queryBase::queryString(const _TCHAR* str, bool autoEscape)
{
    m_impl->m_selects.clear();
    m_impl->m_wheres.clear();
    m_impl->m_keyValues.clear();
    m_impl->m_nofilter = false;
    if (str && str[0])
    {
        std::_tstring s = str;
        boost::trim(s);
        if (autoEscape)
            escape_string(s);
        analyzeQuery(s.c_str(), m_impl->m_selects, m_impl->m_wheres,
                     m_impl->m_keyValues, m_impl->m_nofilter);
    }
    return *this;
}

queryBase& queryBase::reject(int v)
{
    m_impl->m_reject = v;
    return *this;
}

queryBase& queryBase::limit(int v)
{
    m_impl->m_limit = v;
    return *this;
}

queryBase& queryBase::direction(table::eFindType v)
{
    m_impl->m_direction = v;
    return *this;
}

queryBase& queryBase::all()
{
    reset();
    m_impl->m_nofilter = true;
    return *this;
}

queryBase& queryBase::optimize(queryBase::eOptimize v)
{
    m_impl->m_optimize = v;
    return *this;
}

queryBase::eOptimize queryBase::getOptimize() const
{
    return m_impl->m_optimize;
}

queryBase& queryBase::bookmarkAlso(bool v)
{
    m_impl->m_withBookmark = v;
    return *this;
}

bool queryBase::isBookmarkAlso() const
{
    return m_impl->m_withBookmark;
}

queryBase& queryBase::joinKeySize(int v)
{
    m_impl->m_joinKeySize = v;
    return *this;
}

int queryBase::getJoinKeySize() const
{
    return m_impl->m_joinKeySize;
}

table::eFindType queryBase::getDirection() const
{
    return m_impl->m_direction;
}

const _TCHAR* queryBase::toString() const
{
    m_impl->m_str.clear();
    if (m_impl->m_nofilter)
        return _T("*");

    std::_tstring& s = m_impl->m_str;
    std::vector<std::_tstring>& selects = m_impl->m_selects;
    std::vector<std::_tstring>& wheres = m_impl->m_wheres;
    std::vector<std::_tstring>& keyValues = m_impl->m_keyValues;
    if (selects.size())
    {
        s = _T("select ");
        for (int i = 0; i < (int)selects.size(); ++i)
            s += selects[i] + _T(",");

        if (s.size())
            s.replace(s.size() - 1, 1, _T(" "));
    }

    for (size_t i = 0; i < wheres.size(); i += 4)
    {
        if (i + 1 < wheres.size())
            s += wheres[i] + _T(" ") + wheres[i + 1];
        if (i + 2 < wheres.size())
            s += _T(" '") + escape_value(wheres[i + 2]) + _T("' ");
        if (i + 3 < wheres.size())
            s += wheres[i + 3] + _T(" ");
    }

    if (keyValues.size())
    {
        s += _T("in ");
        for (size_t i = 0; i < keyValues.size(); ++i)
            s += _T("'") + escape_value(keyValues[i]) + _T("',");
    }
    if (s.size())
        s.erase(s.end() - 1);

    return s.c_str();
}

int queryBase::getReject() const
{
    return m_impl->m_reject;
}

int queryBase::getLimit() const
{
    return m_impl->m_limit;
}

bool queryBase::isAll() const
{
    return m_impl->m_nofilter;
};

const std::vector<std::_tstring>& queryBase::getSelects() const
{
    return m_impl->m_selects;
}
const std::vector<std::_tstring>& queryBase::getWheres() const
{
    return m_impl->m_wheres;
}
const std::vector<std::_tstring>& queryBase::getSeekKeyValues() const
{
    return m_impl->m_keyValues;
}
const std::vector<keyValuePtr>& queryBase::getSeekValuesPtr() const
{
    return m_impl->m_keyValuesPtr;
}
short queryBase::selectCount() const
{
    return (short)m_impl->m_selects.size();
}

const _TCHAR* queryBase::getSelect(short index) const
{
    assert((index >= 0) && (index < (short)m_impl->m_selects.size()));
    return m_impl->m_selects[index].c_str();
}

short queryBase::whereTokens() const
{
    return (short)m_impl->m_wheres.size();
}

const _TCHAR* queryBase::getWhereToken(short index) const
{
    assert((index >= 0) && (index < (short)m_impl->m_wheres.size()));
    return m_impl->m_wheres[index].c_str();
}

void queryBase::setWhereToken(short index, const _TCHAR* v)
{
    assert((index >= 0) && (index < (short)m_impl->m_wheres.size()));
    m_impl->m_wheres[index] = v;
}

/* alias field name change to original field name */
void queryBase::reverseAliasName(const _TCHAR* alias, const _TCHAR* src)
{
    std::vector<std::_tstring>& selects = m_impl->m_selects;
    std::vector<std::_tstring>& wheres = m_impl->m_wheres;
    std::_tstring s;
    for (size_t i = 0; i < wheres.size(); i += 4)
    {
        if (wheres[i] == alias)
            wheres[i] = src;
        if (i + 2 < wheres.size())
        {
            s = src;
            s.insert(0, _T("["));
            s += _T("[");
            if (wheres[i + 2] == s)
                wheres[i + 2] = s;
        }
    }

    for (size_t i = 0; i < selects.size(); ++i)
        if (selects[i] == alias)
            selects[i] = src;
}

void queryBase::release()
{
    delete this;
}

queryBase* queryBase::create()
{
    return new queryBase();
}

} // namespace client
} // namespace tdap
} // namespace protocol
} // namespace db
} // namespace bzs