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 ); }