www.gusucode.com > VC++编写的SQL服务端和客户端源码程序 > VC++编写的SQL服务端和客户端源码程序\code\Server\Table.cpp

    //Download by http://www.NewXing.com
// Table.cpp : implementation file
//

#include "stdafx.h"
#include "miniSQL.h"
#include "MainFrm.h"
#include "Table.h"
#include "SelDoc.h"
#include "SelView.h"
#include "BPTree.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

extern CMiniSQLApp theApp;

//////////////////////////////////////////////////////////////
// CEntry

//////////////////////////////////////////////////////////////
// Macro used in table.cpp
#define for_each_REC(DO)  {							\
    CEntry REC( attr.attr );						\
    for( long NO = tableAPI->FirstRec(); NO != -1;	\
         NO = tableAPI->NextRec(NO) )  {			\
        tableAPI->ReadRec( NO, &REC );				\
        { DO }  }  }

#define for_each_RES(DO)  {							\
    CEntry REC( attr.attr );						\
    long  NO;										\
    for( int i = 0; i < RES.GetCount(); i++ ){		\
        tableAPI->ReadRec( NO = RES.GetAt( RES.FindIndex(i) ), &REC );		\
        { DO }  }  }

#define for_each_BPT(DO)  {							\
    for( int i = attr.index_num - 1; i >= 0; i-- ){	\
        CBPTree BPT( attr.index_table[i] );			\
        { DO }  }  } 

#define IEtoE( IE, E )								\
    CEntryAttr  E##NA( convert( IE.first ) );		\
    E##NA.rearrange();								\
    CEntry  E( E##NA );								\
    convert( E, IE );
//////////////////////////////////////////////////////////////

void CEntryAttr::push( const CItemAttr& item )
{
    if( num == FIELD_NUM - 1 )
        throw Error( FIELD_NUM_EXCEED, FIELD_NUM, CString(" ") );
    attr[ num++ ] = item;
}

void CEntryAttr::rearrange()
{
    int i;
    for( length = i = 0; i < num; i++ )
        attr[i].offset = length, length += attr[i].type.size;
}

int CEntryAttr::index( const char* str ) const
{
	for( int i = num - 1; i >= 0; i-- )
		if( !::strncmp( attr[i].name, str, FIELD_NAME_LEN ) )
			break;
	return i;
}

//////////////////////////////////////////////////////////////
// CValue

CValue::~CValue()
{
}

void CValue::in( const double& num )
{
    GET_NULL( ptr ) = false;
    switch( type.id )
    {
        case _INT	: GET_INT( ptr )   = (int)num;  break;
        case _LONG	: GET_LONG( ptr )  = (long)num;  break;
        case _FLOAT	: GET_FLOAT( ptr ) = num;  break;
        case _DATE	: GET_DATE( ptr )  = (long)num;  break;
        default		: throw Error( IN_TYPE_ERROR, (int)type.id, CString("") );
    }
}

void CValue::out( double& num ) const
{
    if( GET_NULL( ptr ) )
        return;
    switch( type.id )
    {
        case _INT	: num = GET_INT( ptr );    break;
        case _LONG	: num = GET_LONG( ptr );   break;
        case _FLOAT	: num = GET_FLOAT( ptr );  break;
        case _DATE	: num = GET_DATE( ptr );   break;
        default		: throw Error( OUT_TYPE_ERROR, (int)type.id, CString("") );
    }
}

void CValue::in( const char* str )
{
    if( *str == -1 )
    {
        GET_NULL( ptr ) = true;
        return;
    }
    GET_NULL( ptr ) = false;
    switch( type.id )
    {
        case _INT    :
        case _LONG   :
        case _FLOAT  :
            {
                double temp;
                if( ::sscanf( str, "%lf", &temp ) != 1 )
                    throw Error( IN_VALUE_ERROR, 0, str );
                in( temp );
                break;
            }
        case _STRING    :
            {
                size_t size = type.size - sizeof( bool ) - 1;
                strncpy( GET_STR( ptr ), str, size );
                GET_STR( ptr )[size] = 0;
                break;
            }
        case _DATE   :
            {
                struct tm temp;
                if( ::sscanf( str, "%d/%d/%d", 
                    &temp.tm_year, &temp.tm_mon, &temp.tm_mday ) != 3 )
                    throw Error( IN_VALUE_ERROR, 0, str );
                temp.tm_hour = temp.tm_min = temp.tm_sec = temp.tm_isdst = 0;
                temp.tm_mon--;				//month starts from 0
                temp.tm_year -= 1900;		//year starts from 1900
                if( ( GET_DATE( ptr ) = ::mktime( &temp ) ) == -1 )    //change into time_t
                    throw Error( IN_VALUE_ERROR, 0, str );
                break;
            }
        default     :   throw ( IN_TYPE_ERROR, (int)type.id, CString("") );
    }
}

void CValue::out( char* str, size_t n ) const
{
	CString sstr;

    if( GET_NULL( ptr ) )
	{
		strncpy( str, "<NULL>", n );
		str[ n - 1 ] = '\0';
        return;
	}
    switch( type.id )
    {
        case _INT    :
        case _LONG   :
        case _FLOAT  :
            {
                double num;
                out( num );
				sstr.Format( "%g", num );
				strncpy( str, (LPTSTR)(LPCTSTR) sstr, n );
				str[ n - 1 ] = '\0';
				break;
            }
        case _STRING    :
            {
                strncpy( str, GET_STR( ptr ), n );
                str[ n - 1 ] = '\0';
                break;
            }
        case _DATE   :
            {
                struct tm *tmptr = localtime( &GET_DATE( ptr ) );
                sstr.Format( "%d/%d/%d",
                    tmptr->tm_year + 1900, tmptr->tm_mon + 1, tmptr->tm_mday );
                strncpy( str, (LPTSTR)(LPCTSTR) sstr, n );
                str[ n - 1 ] = '\0';
                break;
            }
        default     : throw Error( OUT_TYPE_ERROR, (int)type.id, CString("") );
    }
    if( strlen( str ) >= n )
        throw Error( OUT_VALUE_ERROR, 0, str );
}

bool CValue::operator <  ( const CValue& rhs ) const
{
    if( this->type.id != rhs.type.id )
        throw Error( TYPE_MATCH_ERROR, 0, CString("") );
    if( this->null() || rhs.null() )
        return !this->null() && rhs.null();
    if( this->type.id == _STRING )
        return strcmp( GET_STR( this->ptr ), GET_STR( rhs.ptr ) ) < 0;
    else
    {
        double an, bn;
        this->out( an );
        rhs.out( bn );
        return an < bn;
    }
}

bool CValue::operator == ( const CValue& rhs ) const
{
    if( this->type.id != rhs.type.id )
        throw Error( TYPE_MATCH_ERROR, 0, CString("") );
    if( this->null() || rhs.null() )
        return this->null() && rhs.null();
    if( this->type.id == _STRING )
        return !strcmp( GET_STR( this->ptr ), GET_STR( rhs.ptr ) );
    else
    {
        double an, bn;
        this->out( an );
        rhs.out( bn );
        return an == bn;
    }
}

//////////////////////////////////////////////////////////////
// CEntry

bool CEntry::operator < ( const CEntry& rhs ) const
{
    int i, j;
    for( i = j = 0; i < this->attr.num  && j < rhs.attr.num; i++, j++ )
    {
        if( (*this)[i] < rhs[j] )
            return true;
        if( rhs[j] < (*this)[i] )
            return false;
    }
    return i == this->attr.num && j != rhs.attr.num;
}

bool CEntry::operator == ( const CEntry& rhs ) const
{
    if( this->attr.num != rhs.attr.num )
        return false;
    for( int i = 0; i < this->attr.num; i++ )
        if( !( (*this)[i] == rhs[i] ) )
            return false;
    return true;
}

//////////////////////////////////////////////////////////////
// CTable

CTable::CTable( const char* tname, const CEntryAttr& rattr )
{
	m_tname = tname;
    attr.attr = rattr;
    attr.index_num = 0;
    attr.attr.rearrange();
	tableAPI = new CFileAPI( tname, ".msl", attr.attr.length, sizeof( CTableAttr ), true );
	SaveAttr();
}

CTable::CTable( const char* tname )
{
	m_tname = tname;
	tableAPI = new CFileAPI( tname, ".msl" );
	LoadAttr();

	//check index
	for( int i = attr.index_num - 1; i >= 0; i-- )
	{
		CFileStatus status;
		CString path = theApp.dir;
		path = path + "\\" + attr.index_table[i] + ".bpt";
		if( !CFile::GetStatus( path, status ) )
		{
			CString str;
			str.Format( "The index \"%s.bpt\" is not existed.\nMaybe the file is removed or corrupted!",
				attr.index_table[i] );
			AfxMessageBox( str, MB_OK | MB_ICONSTOP );
			remove_index( attr.index_table[i] );
			CMainFrame* pMainFrm = ( CMainFrame* )AfxGetMainWnd();
			pMainFrm->m_FileBar.DropIndex( tname, attr.index_table[i] );
		}
	}
}

CTable::~CTable()
{
	if( tableAPI )
	{
		SaveAttr();
		delete tableAPI;
	}
}

CEntryAttr CTable::convert( const STR_LIST& name_list )
{
    if( !name_list.GetCount() )
        return attr.attr;
    CEntryAttr	ret;
    for( int i = 0; i < name_list.GetCount(); i++ )
        ret.push( attr.attr.attr[ attr.attr.index( (LPCTSTR)name_list.GetAt( name_list.FindIndex( i ) ) ) ] );
	return  ret;
}

void CTable::convert( CEntry& E, const STR_PAIR& IE )
{
	if( !IE.first.GetCount() )
	{
		if( IE.second.GetCount() != attr.attr.num )
			throw Error( ERROR_ATTR_NUM, attr.attr.num, _T("") );
		for( int i = 0; i < attr.attr.num; i++ )
			E[i].in( (LPCTSTR)IE.second.GetAt( IE.second.FindIndex(i) ) );
	}
	else
		for( int i = 0; i < IE.first.GetCount(); i++ )
			E[ IE.first.GetAt( IE.first.FindIndex(i) ) ].in(
			(LPCTSTR)IE.second.GetAt( IE.second.FindIndex(i) ) );
}

void CTable::insert( const STR_PAIR& IE )
{
	CEntry	E( attr.attr );
	convert( E, IE );
	long pos = tableAPI->AllocRec();
	tableAPI->WriteRec( pos, &E );
	for_each_BPT( BPT.insert( E, pos ); )
}

void CTable::close()
{
	tableAPI->Close();
	delete tableAPI;
	tableAPI = 0;
}

void CTable::drop()
{
    for( int i = attr.index_num - 1; i >= 0; i-- )
        drop_index( attr.index_table[i] );
	tableAPI->Drop();
	delete tableAPI;
	tableAPI = 0;
}

void CTable::search( RESULT& res )
{
	Message("Using sequential search!");
    for( long NO = tableAPI->FirstRec(); NO != -1; NO = tableAPI->NextRec(NO) )
		res.AddTail( NO );
}

void CTable::search( RESULT& res, const STR_PAIR& equ )
{
	IEtoE( equ, entry );
    for_each_BPT
		(
        if( BPT.search( res, entry ) )
		{
			Message( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
			return;
		}
		)
	Message( "Using sequential search!" );
	CEntryAttr eattr = convert( equ.first );
	for_each_REC(
		if( CEntry( &REC, eattr ) == entry )
			res.AddTail( NO );  )
}

void CTable::search( RESULT& res, const STR_PAIR& bet1, const STR_PAIR& bet2 )
{
	IEtoE( bet1, entry1 );
	IEtoE( bet2, entry2 );
    for_each_BPT
		(
        if( BPT.search( res, entry1, entry2 ) )
		{
			Message( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
            return ;
		}
		)
	Message( "Using sequential search!" );
	CEntryAttr eattr = convert( bet1.first );
    for_each_REC(
        if( ( entry1 < CEntry( &REC, eattr ) || entry1 == CEntry( &REC, eattr ) ) &&
            ( CEntry( &REC, eattr ) < entry2 || CEntry( &REC, eattr ) == entry2 ) )
			res.AddTail( NO );  )
}

void CTable::concatenate( STR_PAIR& ret, const STR_PAIR& src )
{
	POSITION pos = src.first.GetHeadPosition();
	while( pos )
	{
		CString first = src.first.GetNext( pos );
		ret.first.AddTail( first );
	}

	pos = src.second.GetHeadPosition();
	while( pos )
	{
		CString second = src.second.GetNext( pos );
		ret.second.AddTail( second );
	}
}

void CTable::search( RESULT& res, const STR_PAIR& equ,
					 const STR_PAIR& bet1, const STR_PAIR& bet2 )
{
	IEtoE( equ, entry );
	IEtoE( bet1, entry1 );
	IEtoE( bet2, entry2 );

	STR_PAIR IE1, IE2;
	concatenate( IE1, equ );
	concatenate( IE1, bet1 );
	concatenate( IE2, equ );
	concatenate( IE2, bet2 );
	
	IEtoE( IE1, E1 );
	IEtoE( IE2, E2 );
    for_each_BPT
		(
        if( BPT.search( res, E1, E2 ) )
		{
			Message( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
            return ;
		}
		)

	Message( "Using sequential search!" );
	CEntryAttr eattr = convert( equ.first );
	CEntryAttr eattr1 = convert( bet1.first );
	for_each_REC(
		if( ( CEntry( &REC, eattr ) == entry ) &&
			( entry1 < CEntry( &REC, eattr1 ) || entry1 == CEntry( &REC, eattr1 ) ) &&
            ( CEntry( &REC, eattr1 ) < entry2 || CEntry( &REC, eattr1 ) == entry2 ) )
			res.AddTail( NO );  )
}

void CTable::select( const RESULT& RES, const STR_LIST& name_list )
{
    CEntryAttr sattr = convert( name_list );

	CMiniSQLApp* pApp = (CMiniSQLApp* )AfxGetApp();

	CDocTemplate* curTemplate = pApp->OnSelect();
	POSITION pos = curTemplate->GetFirstDocPosition();
	CSelDoc* pDoc;
	while( pos )
		pDoc = (CSelDoc* )curTemplate->GetNextDoc( pos );
	ASSERT( pDoc );

	pos = pDoc->GetFirstViewPosition();
	CSelView* pView = (CSelView* )pDoc->GetNextView( pos );
	ASSERT( pView );
	pView->InsertCol( sattr );

    for_each_RES(
        CEntry   SREC( &REC, sattr );
		pView->InsertItem( SREC );  )
}

void CTable::remove( const RESULT& RES )
{
	for_each_BPT( for_each_RES( BPT.remove( REC, NO ); ) )
	for( int i = RES.GetCount() - 1; i >= 0; i-- )
		tableAPI->RemoveRec( RES.GetAt( RES.FindIndex(i) ) );
}

void CTable::update( const RESULT& RES, const STR_PAIR& set )
{
	for_each_BPT( for_each_RES( BPT.remove( REC, NO ); ) )
	for_each_RES(
		convert( REC, set );
		tableAPI->WriteRec( NO, &REC );
		)
	for_each_BPT( for_each_RES( BPT.insert( REC, NO ); ) )
}

void CTable::create_index( const char* iname, const STR_LIST& kname, bool dup )
{
    insert_index( iname );
    CBPTree new_bpt( iname, convert( kname ), dup );
    for_each_REC( new_bpt.insert( REC, NO ); )
}

void CTable::drop_index( const char* iname )
{
    remove_index( iname );
    CFileAPI( iname, ".bpt" ).Drop();
    CFileAPI( iname, ".bkt" ).Drop();
}

void CTable::insert_index( const char* iname )
{
    if( attr.index_num == INDEX_NUM - 1 )
        throw Error( ERROR_INDEX_LIMIT, INDEX_NUM, _T("") );
    if( find_index( iname ) != -1 )
        throw Error( ERROR_INDEX_EXIST, 0, iname );
    strncpy( attr.index_table[ attr.index_num ], iname, MAIN_NAME_LEN );
    attr.index_table[ attr.index_num++ ][ MAIN_NAME_LEN ] = '\0';
}

void CTable::remove_index( const char* iname )
{
    int i;
    if( ( i = find_index( iname ) ) == -1 )
        throw Error( ERROR_INDEX_NOT_EXIST, 0, iname );
    for( ; i < attr.index_num - 1; ++i )
        strncpy( attr.index_table[i], attr.index_table[i+1], MAIN_NAME_LEN + 1 );
    attr.index_num--;
}

int CTable::find_index( const char* iname )
{
    int i;
    for( i = attr.index_num - 1; i >= 0; i-- )
        if( !strncmp( iname, attr.index_table[i], MAIN_NAME_LEN ) )
            break;
    return i;
}

///////////////////////////////////////////////////////////////
// socket use

void CTable::search( RESULT& res, CClientSocket* pSocket, CServerDoc* pDoc )
{
	CString temp( "<READ " );
	temp += m_tname + "> :\r\n\tUsing sequential search!";
	pDoc->Message( temp );

	CMsg msg( SEL_START );
	msg.m_msgList.AddTail( "Using sequential search!" );
	pDoc->SendMsg( pSocket, msg );

    for( long NO = tableAPI->FirstRec(); NO != -1; NO = tableAPI->NextRec(NO) )
		res.AddTail( NO );
}

void CTable::search( RESULT& res, const STR_PAIR& equ, CClientSocket* pSocket, CServerDoc* pDoc )
{
	CString temp( "<READ " );
	temp += m_tname + "> :\r\n\t";

	IEtoE( equ, entry );
    for_each_BPT
		(
        if( BPT.search( res, entry ) )
		{
			temp += (CString)"Using index \"" + attr.index_table[i] + ".bpt\"";
			pDoc->Message( temp );

			CMsg msg( SEL_START );
			msg.m_msgList.AddTail( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
			pDoc->SendMsg( pSocket, msg );

			return;
		}
		)

	temp += "Using sequential search!";
	pDoc->Message( temp );

	CMsg msg( SEL_START );
	msg.m_msgList.AddTail( "Using sequential search!" );
	pDoc->SendMsg( pSocket, msg );

	CEntryAttr eattr = convert( equ.first );
	for_each_REC(
		if( CEntry( &REC, eattr ) == entry )
			res.AddTail( NO );  )
}

void CTable::search( RESULT& res, const STR_PAIR& bet1, const STR_PAIR& bet2, CClientSocket* pSocket, CServerDoc* pDoc )
{
	CString temp( "<READ " );
	temp += m_tname + "> :\r\n\t";

	IEtoE( bet1, entry1 );
	IEtoE( bet2, entry2 );
    for_each_BPT
		(
        if( BPT.search( res, entry1, entry2 ) )
		{
			temp += (CString)"Using index \"" + attr.index_table[i] + ".bpt\"";
			pDoc->Message( temp );

			CMsg msg( SEL_START );
			msg.m_msgList.AddTail( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
			pDoc->SendMsg( pSocket, msg );

			return;
		}
		)

	temp += "Using sequential search!";
	pDoc->Message( temp );

	CMsg msg( SEL_START );
	msg.m_msgList.AddTail( "Using sequential search!" );
	pDoc->SendMsg( pSocket, msg );

	CEntryAttr eattr = convert( bet1.first );
    for_each_REC(
        if( ( entry1 < CEntry( &REC, eattr ) || entry1 == CEntry( &REC, eattr ) ) &&
            ( CEntry( &REC, eattr ) < entry2 || CEntry( &REC, eattr ) == entry2 ) )
			res.AddTail( NO );  )
}

void CTable::search( RESULT& res, const STR_PAIR& equ,
					 const STR_PAIR& bet1, const STR_PAIR& bet2, CClientSocket* pSocket, CServerDoc* pDoc )
{
	CString temp( "<READ " );
	temp += m_tname + "> :\r\n\t";

	IEtoE( equ, entry );
	IEtoE( bet1, entry1 );
	IEtoE( bet2, entry2 );

	STR_PAIR IE1, IE2;
	concatenate( IE1, equ );
	concatenate( IE1, bet1 );
	concatenate( IE2, equ );
	concatenate( IE2, bet2 );
	
	IEtoE( IE1, E1 );
	IEtoE( IE2, E2 );
    for_each_BPT
		(
        if( BPT.search( res, E1, E2 ) )
		{
			temp += (CString)"Using index \"" + attr.index_table[i] + ".bpt\"";
			pDoc->Message( temp );

			CMsg msg( SEL_START );
			msg.m_msgList.AddTail( (CString)"Using index \"" + attr.index_table[i] + ".bpt\"" );
			pDoc->SendMsg( pSocket, msg );

			return;
		}
		)

	temp += "Using sequential search!";
	pDoc->Message( temp );

	CMsg msg( SEL_START );
	msg.m_msgList.AddTail( "Using sequential search!" );
	pDoc->SendMsg( pSocket, msg );

	CEntryAttr eattr = convert( equ.first );
	CEntryAttr eattr1 = convert( bet1.first );
	for_each_REC(
		if( ( CEntry( &REC, eattr ) == entry ) &&
			( entry1 < CEntry( &REC, eattr1 ) || entry1 == CEntry( &REC, eattr1 ) ) &&
            ( CEntry( &REC, eattr1 ) < entry2 || CEntry( &REC, eattr1 ) == entry2 ) )
			res.AddTail( NO );  )
}

void CTable::select( const RESULT& RES, const STR_LIST& name_list, CClientSocket* pSocket, CServerDoc* pDoc )
{
    CEntryAttr sattr = convert( name_list );

	SendCol( sattr, pSocket, pDoc );

    for_each_RES(
        CEntry   SREC( &REC, sattr );
		SendItem( SREC, pSocket, pDoc );  )
}

void CTable::SendCol( CEntryAttr& attr, CClientSocket* pSocket, CServerDoc* pDoc )
{
	CMsg msg( SEL_COL );
	int count = attr.num;

	for( int i = 0; i < count; i++ )
	{
		msg.m_msgList.AddTail( attr.attr[i].name );
		CString len;
		len.Format( "%d", GetLength( attr.attr[i].type ) );
		msg.m_msgList.AddTail( len );
	}

	pDoc->SendMsg( pSocket, msg );
}

int CTable::GetLength( const CItemType& type )
{
	switch( type.id )
	{
	case _INT:
	case _LONG:
		return 40;
	case _FLOAT:
	case _DATE:
		return 80;
	case _STRING:
		return type.size * 7;
	default:
		throw Error( OUT_TYPE_ERROR, (int)type.id, CString("") );
	}
}

void CTable::SendItem( CEntry& entry, CClientSocket* pSocket, CServerDoc* pDoc )
{
	char	item[ 1024 ];
	int		count = entry.values();
	CMsg	msg( SEL_ITEM );

	for( int i = 0; i < count; i++ )
	{
		entry[i].out( item, 1024 );
		msg.m_msgList.AddTail( item );
	}

	pDoc->SendMsg( pSocket, msg );
}