//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996-1998 // // File: tlsdb.h // // Contents: basic class for license table // // History: // //--------------------------------------------------------------------------- #ifndef __TLSDB_H__ #define __TLSDB_H__ #include "JetBlue.h" #include "locks.h" #include "tlsrpc.h" #define ENUMERATE_COMPARE_NO_FIELDS 0x00000000 #define PROCESS_ALL_COLUMNS 0xFFFFFFFF #define TLSTABLE_INDEX_DEFAULT_DENSITY TLS_TABLE_INDEX_DEFAULT_DENSITY #define TLSTABLE_MAX_BINARY_LENGTH 8192 #define RECORD_ENTRY_DELETED 0x01 #if DBG #define REPORT_IF_FETCH_FAILED( Table, Column, ErrCode ) \ if(ErrCode < JET_errSuccess) { \ DebugOutput(_TEXT("Table %s, Column %s - fetch failed with error code %d\n"), \ Table, Column, ErrCode ); \ } #define REPORT_IF_INSERT_FAILED( Table, Column, ErrCode ) \ if(ErrCode < JET_errSuccess) { \ DebugOutput(_TEXT("Table %s, Column %s - insert failed with error code %d\n"), \ Table, Column, ErrCode ); \ } #define REPORTPROCESSFAILED(bFetch, tablename, columnname, jeterror) \ if(bFetch) \ { \ REPORT_IF_FETCH_FAILED(tablename, columnname, jeterror); \ } \ else \ { \ REPORT_IF_INSERT_FAILED(tablename, columnname, jeterror); \ } #else #define REPORT_IF_FETCH_FAILED( a, b, c ) #define REPORT_IF_INSERT_FAILED( a, b, c ) #define REPORTPROCESSFAILED( a, b, c, d) #endif // //////////////////////////////////////////////////////////////// // // This is to force compiler to check for require member // function // struct TLSColumnBase { virtual JET_ERR FetchColumnValue( PVOID pbData, DWORD cbData, DWORD offset, PDWORD pcbReturnData ) = 0; virtual JET_ERR InsertColumnValue( PVOID pbData, DWORD cbData, DWORD offset ) = 0; }; // //////////////////////////////////////////////////////////////// // template struct TLSColumn : public JBColumnBufferBase, TLSColumnBase { private: JBColumn* m_JetColumn; //-------------------------------------------- JET_ERR RetrieveColumnValue( PVOID pbData, DWORD cbData, DWORD offset ) /* */ { JB_ASSERT(IsValid() == TRUE); if(m_JetColumn == NULL) { #ifdef DBG OutputDebugString( _TEXT("Column buffer not attach to any table...\n") ); #endif return JET_errNotInitialized; } //JB_ASSERT(pbData != NULL); // // TODO - supply conversion routine ??? // if(m_JetColumn->GetJetColumnType() != JB_COLTYPE_TEXT) { // we are using long binary type as long text so ignore // this one if(m_JetColumn->GetJetColumnType() != JetColType) { // // this is an internal error // JB_ASSERT(m_JetColumn->GetJetColumnType() == JetColType); m_JetColumn->SetLastJetError( JET_errInvalidParameter ); return FALSE; } } BOOL bSuccess; bSuccess = m_JetColumn->FetchColumn( pbData, cbData, offset ); return (bSuccess == TRUE) ? JET_errSuccess : m_JetColumn->GetLastJetError(); } public: //-------------------------------------------- TLSColumn( TLSColumn& src ) : m_JetColumn(src.m_JetColumn) /* */ { } //-------------------------------------------- TLSColumn( JBTable& jbTable, LPCTSTR pszColumnName ) /* */ { if(AttachToTable(jbTable, pszColumnName) == FALSE) { JB_ASSERT(FALSE); } } //-------------------------------------------- TLSColumn() : m_JetColumn(NULL) {} //-------------------------------------------- JET_ERR AttachToTable( JBTable& jbTable, LPCTSTR pszColumnName ) /* */ { m_JetColumn = jbTable.FindColumnByName(pszColumnName); return (m_JetColumn != NULL) ? JET_errSuccess : jbTable.GetLastJetError(); } //-------------------------------------------- BOOL IsValid() { return (m_JetColumn != NULL); } //-------------------------------------------- virtual JET_ERR FetchColumnValue( PVOID pbData, // buffer for returning data DWORD cbData, // size of buffer DWORD offset, PDWORD pcbReturnData // actual data returned. ) /* */ { JET_ERR jetErr; jetErr = RetrieveColumnValue( pbData, cbData, offset ); if(pcbReturnData) { *pcbReturnData = GetActualDataSize(); } return jetErr; } //-------------------------------------------- virtual JET_ERR InsertColumnValue( PVOID pbData, DWORD cbData, DWORD offset ) /* */ { JB_ASSERT(IsValid() == TRUE); if(m_JetColumn == NULL) { #ifdef DBG OutputDebugString( _TEXT("Column buffer not attach to any table...\n") ); #endif return JET_errNotInitialized; } BOOL bSuccess; bSuccess = m_JetColumn->InsertColumn( pbData, cbData, offset ); return (bSuccess == TRUE) ? JET_errSuccess : m_JetColumn->GetLastJetError(); } //-------------------------------------------- JET_ERR GetLastJetError() { return (m_JetColumn) ? m_JetColumn->GetLastJetError() : JET_errNotInitialized; } //-------------------------------------------- DWORD GetActualDataSize() { return m_JetColumn->GetDataSize(); } //------------------------------------------- JET_COLTYP GetJetColumnType() { return JetColType; } // // Always require calling function to pass in buffer // PVOID GetInputBuffer() { JB_ASSERT(FALSE); return NULL; } //----------------------------------------- PVOID GetOutputBuffer() { JB_ASSERT(FALSE); return NULL; } //----------------------------------------- DWORD GetInputBufferLength() { return 0; } //----------------------------------------- DWORD GetOutputBufferLength() { return 0; } }; // ---------------------------------------------------------- // // Out text is unicode, JetBlue only support fix length text up // to 255 characters so we use Long text instead. // // JET_coltypBinary // JET_coltypText // JET_coltypLongBinary // JET_coltypLongText // // See esent.h // typedef TLSColumn TLSColumnText; typedef TLSColumn TLSColumnBinary; // // unsigned byte typedef TLSColumn TLSColumnUchar; // // 2-byte integer, signed typedef TLSColumn TLSColumnShort; // // 4-byte integer, signed typedef TLSColumn TLSColumnLong; // // typedef TLSColumn TLSColumnDword; // // 4-byte IEEE single precision typedef TLSColumn TLSColumnFloat; // // 8-byte IEEE double precision typedef TLSColumn TLSColumnDouble; // // File Time typedef TLSColumn TLSColumnFileTime; //-------------------------------------------------------------- JET_ERR TLSColumnText::InsertColumnValue( PVOID pbData, DWORD cbData, DWORD offset ) /* */ { JB_ASSERT(IsValid() == TRUE); JET_ERR jetErr; jetErr = m_JetColumn->InsertColumn( pbData, _tcslen((LPTSTR) pbData) * sizeof(TCHAR), offset ); return jetErr; } //-------------------------------------------------------------- JET_ERR TLSColumnText::FetchColumnValue( PVOID pbData, DWORD cbData, DWORD offset, PDWORD pcbDataReturn ) /* */ { PVOID pbBuffer = pbData; DWORD cbBuffer = cbData; // Cause recursive call - stack overflow // if(TLSColumn::FetchColumnValue(offset, pbData, cbData) == FALSE) // return m_JetColumn->GetLastJetError(); JET_ERR jetErr = RetrieveColumnValue( pbBuffer, cbBuffer, offset ); if(jetErr == JET_errSuccess) { ((LPTSTR)pbBuffer)[(min(cbBuffer, GetActualDataSize())) / sizeof(TCHAR)] = _TEXT('\0'); } if(pcbDataReturn) { *pcbDataReturn = _tcslen((LPTSTR)pbBuffer); } return jetErr; } //--------------------------------------------------- JET_ERR TLSColumnFileTime::InsertColumnValue( PVOID pbData, DWORD cbData, DWORD offset ) /* */ { FILETIME ft; SYSTEMTIME sysTime; JB_ASSERT(IsValid() == TRUE); JB_ASSERT(cbData == sizeof(FILETIME)); JET_ERR jetErr; if(IsValid() == FALSE) { jetErr = JET_errNotInitialized; } else if(cbData != sizeof(FILETIME) || pbData == NULL) { m_JetColumn->SetLastJetError(jetErr = JET_errInvalidParameter); } else { memset(&ft, 0, sizeof(ft)); if(CompareFileTime(&ft, (FILETIME *)pbData) == 0) { GetSystemTime(&sysTime); SystemTimeToFileTime(&sysTime, &ft); ((FILETIME *)pbData)->dwLowDateTime = ft.dwLowDateTime; ((FILETIME *)pbData)->dwHighDateTime = ft.dwHighDateTime; } else { ft.dwLowDateTime = ((FILETIME *)pbData)->dwLowDateTime; ft.dwHighDateTime = ((FILETIME *)pbData)->dwHighDateTime; } jetErr = m_JetColumn->InsertColumn( (PVOID)&ft, sizeof(ft), 0 ); } return jetErr; } //--------------------------------------------------- JET_ERR TLSColumnBinary::FetchColumnValue( PVOID pbData, DWORD cbData, DWORD offset, PDWORD pcbDataReturn ) /* */ { // // don't worry about buffer size, calling function // should trap it. JET_ERR jetErr = RetrieveColumnValue( pbData, cbData, offset ); if(jetErr == JET_errSuccess && pcbDataReturn != NULL) { *pcbDataReturn = GetActualDataSize(); } return jetErr; } // ///////////////////////////////////////////////////////////// // typedef enum { RECORD_ENUM_ERROR=0, RECORD_ENUM_MORE_DATA, RECORD_ENUM_END } RECORD_ENUM_RETCODE; template class TLSTable : public JBTable { /* Virtual base template class for table used in TLSLicensing database, template is due to 1) Static member variable which include column and indexes in the table. 2) Type checking - KEYPACK structure only good for one table. Class derive from this template must define 1) static g_Columns, g_NumColumns. 2) static g_TableIndex, g_NumTableIndex. 3) static g_TableLock (Might not be necessary) 4) GetTableName() 5) FetchRecord 6) InsertRecord 7) ResolveToTableColumn() 8) EnumerationBegin() 9) EqualValue(). See comment for each member function. */ protected: // // Class derive or inst. from TLSTable<> must define following // static TLSJBColumn g_Columns[]; static int g_NumColumns; static TLSJBIndex g_TableIndex[]; static int g_NumTableIndex; T m_EnumValue; BOOL m_EnumMatchAll; DWORD m_EnumParam; DWORD m_EnumState; // HIWORD - in enumeration, TRUE/FALSE // LOWORD - MoveToNext record before fetch. BYTE m_Key[sizeof(T)]; DWORD m_KeyLength; BOOL m_bCompareKey; // should we compare key? BOOL IsInEnumeration() { return HIWORD(m_EnumState); } BOOL IsMoveBeforeFetch() { return LOWORD(m_EnumState); } void SetInEnumeration( BOOL b ) { m_EnumState = MAKELONG(LOWORD(m_EnumState), b); } void SetMoveBeforeFetch( BOOL b ) { m_EnumState = MAKELONG(b, HIWORD(m_EnumState)); } public: // // JetBlue has its own locking // static CCriticalSection g_TableLock; CCriticalSection& GetTableLock() { return g_TableLock; } //------------------------------------------------------- static void LockTable() /* Lock table for exclusive access, JBTable provides ReadLock/WriteLock for current record */ { g_TableLock.Lock(); } //------------------------------------------------------- static void UnlockTable() /* Unlock table. */ { g_TableLock.UnLock(); } //------------------------------------------------------- TLSTable( JBDatabase& database ) : JBTable(database), m_EnumMatchAll(FALSE), m_EnumParam(0), m_EnumState(0), m_KeyLength(0) /* Constructor, must have JBDatabase object. */ { memset(&m_EnumValue, 0, sizeof(T)); memset(m_Key, 0, sizeof(m_Key)); } //------------------------------------------------------- virtual BOOL CreateTable() /* Create the table, must have g_Columns and g_NumColumns defined. */ { DebugOutput( _TEXT("TLSTable - Creating Table %s...\n"), GetTableName() ); if(BeginTransaction() == FALSE) return FALSE; if(CreateOpenTable(GetTableName()) == TRUE) { // // AddColumn() return num of col. created if successful // if(AddColumn(g_NumColumns, g_Columns) == g_NumColumns) { // // AddIndex() return 0 if success // AddIndex(g_NumTableIndex, g_TableIndex); } } if(IsSuccess() == TRUE) { CloseTable(); CommitTransaction(); } else { RollbackTransaction(); } return IsSuccess(); } //-------------------------------------------------------- virtual BOOL UpgradeTable( IN DWORD dwOldVersion, IN DWORD dwNewVersion ) /*++ ++*/ { if(dwOldVersion == 0) { if(OpenTable(TRUE, JET_bitTableUpdatable) == FALSE) return FALSE; return CloseTable(); } else if(dwOldVersion == dwNewVersion) { return TRUE; } // We only have one version. JB_ASSERT(FALSE); return FALSE; } //-------------------------------------------------------- virtual BOOL OpenTable( IN BOOL bCreateIfNotExist, IN JET_GRBIT grbit ) /* Abstract: Open the table for access. Parameter: bCreateIfNoExist - TRUE, if table does not exist, create it, FALSE return error if table not exist */ { if( JBTable::OpenTable(GetTableName(), NULL, 0, grbit) == FALSE && GetLastJetError() == JET_errObjectNotFound && bCreateIfNotExist) { // // Close table after it created it // if( CreateTable() == FALSE || JBTable::OpenTable(GetTableName(), NULL, 0, grbit) == FALSE ) { return FALSE; } } if(IsSuccess() == TRUE) { ResolveToTableColumn(); } return IsSuccess(); } //--------------------------------------------------------- // // pure virtual function to return table name // virtual LPCTSTR GetTableName() = 0; //--------------------------------------------------------- virtual BOOL UpdateTable( IN DWORD dwOldVersion, // unuse IN DWORD dwNewVersion ) /* Abstract: Upgrade the table. Parameter: dwOldVersion - previous table version. dwNewVersion - current table version. */ { // currently nothing to upgrade. return TRUE; } // // should have fetch/insert record with buffer passed in // //--------------------------------------------------------- virtual BOOL InsertRecord( T& value, DWORD dwParam = PROCESS_ALL_COLUMNS ) = 0; virtual BOOL UpdateRecord( T& value, DWORD dwParam = PROCESS_ALL_COLUMNS ) = 0; //-------------------------------------------------------- virtual BOOL FetchRecord( T& value, DWORD dwParam = PROCESS_ALL_COLUMNS ) = 0; //--------------------------------------------------------- virtual BOOL Cleanup() { EnumerateEnd(); return TRUE; } //--------------------------------------------------------- virtual BOOL ResolveToTableColumn() = 0; //--------------------------------------------------------- virtual JBKeyBase* EnumerationIndex( BOOL bMatchAll, DWORD dwParam, T* value, BOOL* bCompareKey ) = 0; //------------------------------------------------------ virtual BOOL EqualValue( T& src, T& dest, BOOL bMatchAll, DWORD dwMatchParam ) = 0; //------------------------------------------------------- // // Use user defined comparision function instead of calling // EqualValue() ??? // virtual BOOL EnumerateBegin( BOOL bMatchAll, DWORD dwParam, T* start_value ) /* */ { return EnumerateBegin( bMatchAll, dwParam, start_value, JET_bitSeekGE ); } virtual BOOL EnumerateBegin( BOOL bMatchAll, DWORD dwParam, T* start_value, JET_GRBIT jet_seek_grbit ) /* */ { BOOL bRetCode = FALSE; if(dwParam != ENUMERATE_COMPARE_NO_FIELDS && start_value == NULL) { SetLastJetError(JET_errInvalidParameter); JB_ASSERT(FALSE); return FALSE; } if(IsInEnumeration() == TRUE) { SetLastJetError(JET_errInvalidObject); JB_ASSERT(FALSE); return FALSE; } JBKeyBase* index; index = EnumerationIndex( bMatchAll, dwParam, start_value, &m_bCompareKey ); if(index == NULL) { SetLastJetError(JET_errInvalidParameter); return FALSE; } if(start_value == NULL || dwParam == ENUMERATE_COMPARE_NO_FIELDS) { // // position the cursor to first record // bRetCode = JBTable::EnumBegin(index); m_EnumParam = ENUMERATE_COMPARE_NO_FIELDS; m_EnumMatchAll = FALSE; // enumerate all record m_bCompareKey = FALSE; } else { bRetCode = JBTable::SeekToKey( index, dwParam, jet_seek_grbit ); if(bRetCode == TRUE && m_bCompareKey) { bRetCode = JBTable::RetrieveKey( m_Key, sizeof(m_Key), &m_KeyLength ); JB_ASSERT(bRetCode == TRUE); } } if(bRetCode == FALSE) { if(GetLastJetError() == JET_errRecordNotFound) { // // reset error code to provide same functionality as SQL // SetLastJetError(JET_errSuccess); bRetCode = TRUE; } else { DebugOutput( _TEXT("Enumeration on table %s failed with error %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(bRetCode); } } if(bRetCode == TRUE) { m_EnumParam = dwParam; m_EnumMatchAll = bMatchAll; if(start_value) { m_EnumValue = *start_value; } SetInEnumeration(TRUE); // cursor in on the the record we want. SetMoveBeforeFetch(FALSE); } delete index; return bRetCode; } //------------------------------------------------------ virtual RECORD_ENUM_RETCODE EnumerateNext( IN OUT T& retBuffer, IN BOOL bReverse=FALSE, IN BOOL bAnyRecord = FALSE ) /* */ { if(IsInEnumeration() == FALSE) { SetLastJetError(JET_errInvalidParameter); return RECORD_ENUM_ERROR; } //CCriticalSectionLocker Lock(GetTableLock()); RECORD_ENUM_RETCODE retCode=RECORD_ENUM_MORE_DATA; BYTE current_key[sizeof(T)]; unsigned long current_key_length=0; // // Support for matching // while(TRUE) { if(IsMoveBeforeFetch() == TRUE) { // // Position the cursor to next record for next fetch // JBTable::ENUM_RETCODE enumCode; enumCode = EnumNext( (bReverse == TRUE) ? JET_MovePrevious : JET_MoveNext ); switch(enumCode) { case JBTable::ENUM_SUCCESS: retCode = RECORD_ENUM_MORE_DATA; break; case JBTable::ENUM_ERROR: retCode = RECORD_ENUM_ERROR; break; case JBTable::ENUM_END: retCode = RECORD_ENUM_END; } if(retCode != RECORD_ENUM_MORE_DATA) break; } // fetch entire record // TODO - fetch necessary fields for comparision, if // equal then fetch remaining fields if(FetchRecord(retBuffer, PROCESS_ALL_COLUMNS) == FALSE) { retCode = (GetLastJetError() == JET_errNoCurrentRecord || GetLastJetError() == JET_errRecordNotFound) ? RECORD_ENUM_END : RECORD_ENUM_ERROR; break; } SetMoveBeforeFetch(TRUE); // compare the value if( bAnyRecord == TRUE || m_EnumParam == ENUMERATE_COMPARE_NO_FIELDS || EqualValue(retBuffer, m_EnumValue, m_EnumMatchAll, m_EnumParam) == TRUE ) { break; } if(m_bCompareKey == TRUE) { // compare key to break out of loop if(JBTable::RetrieveKey(current_key, sizeof(current_key), ¤t_key_length) == FALSE) { retCode = RECORD_ENUM_ERROR; break; } if(memcmp(current_key, m_Key, min(m_KeyLength, current_key_length)) != 0) { retCode = RECORD_ENUM_END; break; } } } // // Terminate enumeration if end // //if(retCode != RECORD_ENUM_MORE_DATA) //{ // EnumerateEnd(); //} return retCode; } //------------------------------------------------------ virtual BOOL EnumerateEnd() { SetInEnumeration(FALSE); SetMoveBeforeFetch(FALSE); m_bCompareKey = FALSE; return TRUE; } //------------------------------------------------------- virtual DWORD GetCount( BOOL bMatchAll, DWORD dwParam, T* searchValue ) /* */ { DWORD count = 0; T value; RECORD_ENUM_RETCODE retCode; if(EnumerateBegin(bMatchAll, dwParam, searchValue) == TRUE) { while( (retCode=EnumerateNext(value)) == RECORD_ENUM_MORE_DATA ) { count++; } if(retCode == RECORD_ENUM_ERROR) { DebugOutput( _TEXT("GetCount for table %s : EnumerationNext() return %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(FALSE); } EnumerateEnd(); } else { DebugOutput( _TEXT("GetCount for table %s : EnumerationBegin return %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(FALSE); } return count; } //----------------------------------------------------- virtual BOOL FindRecord( BOOL bMatchAll, DWORD dwParam, T& seachValue, T& retValue ) /* */ { RECORD_ENUM_RETCODE retCode; BOOL bSuccess=TRUE; //CCriticalSectionLocker Lock(GetTableLock()); if(EnumerateBegin(bMatchAll, dwParam, &seachValue) == FALSE) { DebugOutput( _TEXT("SearchValue for table %s : EnumerationBegin return %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(FALSE); bSuccess = FALSE; goto cleanup; } retCode = EnumerateNext(retValue); EnumerateEnd(); if(retCode == RECORD_ENUM_ERROR) { DebugOutput( _TEXT("SearchValue for table %s : EnumerationNext() return %d\n"), GetTableName(), GetLastJetError() ); bSuccess = FALSE; JB_ASSERT(FALSE); } else if(retCode == RECORD_ENUM_END) { SetLastJetError(JET_errRecordNotFound); bSuccess = FALSE; } cleanup: return bSuccess; } //------------------------------------------------- virtual BOOL DeleteRecord() { //CCriticalSectionLocker Lock(GetTableLock()); return JBTable::DeleteRecord(); } //------------------------------------------------- virtual BOOL DeleteRecord( DWORD dwParam, T& value ) /* */ { BOOL bSuccess; T Dummy; //CCriticalSectionLocker Lock(GetTableLock()); // // Position the current record bSuccess = FindRecord( TRUE, dwParam, value, Dummy ); if(bSuccess == FALSE) return FALSE; // // Delete the record bSuccess = JBTable::DeleteRecord(); return bSuccess; } //---------------------------------------------- virtual BOOL DeleteAllRecord( BOOL bMatchAll, DWORD dwParam, T& searchValue ) /* */ { int count=0; BOOL bSuccess; JET_ERR jetErr=JET_errSuccess; RECORD_ENUM_RETCODE retCode; T value; //CCriticalSectionLocker Lock(GetTableLock()); if(EnumerateBegin(bMatchAll, dwParam, &searchValue) == TRUE) { while( (retCode=EnumerateNext(value)) == RECORD_ENUM_MORE_DATA ) { count++; if(JBTable::DeleteRecord() == FALSE) { DebugOutput( _TEXT("DeleteAllRecord for table %s : return %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(FALSE); jetErr = GetLastJetError(); break; } } // // End the enumeration // if(retCode == RECORD_ENUM_ERROR) { DebugOutput( _TEXT("DeleteAllRecord for table %s : EnumerationNext() return %d\n"), GetTableName(), GetLastJetError() ); JB_ASSERT(FALSE); } if(EnumerateEnd() == TRUE) { // restore error code from DeleteRecord(); SetLastJetError(jetErr); } } else { DebugOutput( _TEXT("DeleteAllRecord for table %s : EnumerationBegin return %d\n"), GetTableName(), GetLastJetError() ); } // return code is based on number of record deleted and its operation if(IsSuccess() == TRUE) { if(count == 0) SetLastJetError(JET_errRecordNotFound); } return IsSuccess(); } }; #ifdef __cplusplus extern "C" { #endif BOOL TLSDBCopySid( PSID pbSrcSid, DWORD cbSrcSid, PSID* pbDestSid, DWORD* cbDestSid ); BOOL TLSDBCopyBinaryData( PBYTE pbSrcData, DWORD cbSrcData, PBYTE* ppbDestData, DWORD* pcbDestData ); #ifdef __cplusplus } #endif #endif