2048 lines
50 KiB
C++
2048 lines
50 KiB
C++
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
database.cpp
|
||
|
||
Abstract:
|
||
|
||
SIS Groveler Jet-Blue database front-end
|
||
|
||
Authors:
|
||
|
||
Cedric Krumbein, 1998
|
||
|
||
Environment:
|
||
|
||
User Mode
|
||
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "all.hxx"
|
||
|
||
/*****************************************************************************/
|
||
/*************** SGDatabase class static value initializations ***************/
|
||
/*****************************************************************************/
|
||
|
||
DWORD SGDatabase::numInstances = 0;
|
||
|
||
JET_INSTANCE SGDatabase::instance = 0;
|
||
|
||
BOOL SGDatabase::jetInitialized = FALSE;
|
||
|
||
TCHAR * SGDatabase::logDir = NULL;
|
||
|
||
/*****************************************************************************/
|
||
/****************** SGDatabase class private static methods ******************/
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::set_log_drive(const _TCHAR *drive_name)
|
||
{
|
||
int drive_name_len = _tcslen(drive_name);
|
||
int cs_dir_path_len = _tcslen(CS_DIR_PATH);
|
||
|
||
ASSERT(NULL == logDir);
|
||
logDir = new TCHAR[drive_name_len + cs_dir_path_len + 1 - 1];
|
||
ASSERT(NULL != logDir);
|
||
|
||
_tcsncpy(SGDatabase::logDir, drive_name, drive_name_len - 1);
|
||
_tcscpy(&SGDatabase::logDir[drive_name_len-1], CS_DIR_PATH);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL SGDatabase::InitializeEngine()
|
||
{
|
||
DWORD_PTR maxVerPages;
|
||
DWORD_PTR minCacheSize;
|
||
DWORD_PTR newCacheSize;
|
||
DWORDLONG cacheSize;
|
||
DWORD circularLog;
|
||
MEMORYSTATUSEX memStatus;
|
||
SYSTEM_INFO sysInfo;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(!jetInitialized);
|
||
ASSERT(logDir);
|
||
|
||
if (!SetCurrentDirectory(logDir)) {
|
||
DPRINTF((_T("SGDatabase::InitializeEngine: can't cd to \"%s\", %ld\n"), logDir, GetLastError()));
|
||
return FALSE;
|
||
}
|
||
|
||
circularLog = 1;
|
||
jetErr = JetSetSystemParameter(&instance, 0,
|
||
JET_paramCircularLog, circularLog, NULL);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("(2) JetSetSystemParameter: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Set the maximum cache size used by the database engine to min(4% phys mem, 6M).
|
||
//
|
||
|
||
jetErr = JetGetSystemParameter(instance, 0,
|
||
JET_paramCacheSizeMin, &minCacheSize, NULL, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetGetSystemParameter: jetErr=%ld\n"), jetErr));
|
||
TerminateEngine();
|
||
return FALSE;
|
||
}
|
||
|
||
memStatus.dwLength = sizeof memStatus;
|
||
GlobalMemoryStatusEx(&memStatus); // get total physical memory
|
||
GetSystemInfo(&sysInfo); // get page size
|
||
|
||
cacheSize = memStatus.ullTotalPhys / 25; // 4%
|
||
newCacheSize = (DWORD) min(cacheSize, MAX_DATABASE_CACHE_SIZE);
|
||
newCacheSize = newCacheSize / sysInfo.dwPageSize;
|
||
|
||
if (newCacheSize < minCacheSize)
|
||
newCacheSize = minCacheSize;
|
||
|
||
jetErr = JetSetSystemParameter(&instance, 0,
|
||
JET_paramCacheSizeMax, newCacheSize, NULL);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("(3) JetSetSystemParameter: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Set Version Cache size
|
||
//
|
||
|
||
jetErr = JetGetSystemParameter(instance, 0,
|
||
JET_paramMaxVerPages, &maxVerPages, NULL, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("(2) JetGetSystemParameter: jetErr=%ld\n"), jetErr));
|
||
TerminateEngine();
|
||
return FALSE;
|
||
}
|
||
|
||
if (maxVerPages >= MIN_VER_PAGES) {
|
||
DPRINTF((_T("JetGetSystemParameter(instance=%lu): MaxVerPages=%lu\n"),
|
||
instance, maxVerPages));
|
||
} else {
|
||
maxVerPages = MIN_VER_PAGES;
|
||
jetErr = JetSetSystemParameter(&instance, 0,
|
||
JET_paramMaxVerPages, maxVerPages, NULL);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("(4) JetSetSystemParameter: jetErr=%ld\n"), jetErr));
|
||
TerminateEngine();
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetSetSystemParameter(instance=%lu, MaxVerPages)=%lu\n"),
|
||
instance, maxVerPages));
|
||
}
|
||
|
||
//
|
||
// Initialize Jet
|
||
//
|
||
|
||
jetErr = JetInit(&instance);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetInit: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
jetInitialized = TRUE;
|
||
DPRINTF((_T("JetInit: instance=%lu\n"), instance));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::TerminateEngine()
|
||
{
|
||
JET_ERR jetErr;
|
||
BOOL rc;
|
||
|
||
ASSERT(jetInitialized);
|
||
|
||
jetErr = JetTerm(instance);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetTerm: jetErr=%ld\n"), jetErr));
|
||
rc = FALSE;
|
||
} else {
|
||
rc = TRUE;
|
||
|
||
// Delete no longer needed jet files.
|
||
|
||
if (logDir) {
|
||
WIN32_FIND_DATA findData;
|
||
HANDLE fHandle;
|
||
BOOL success;
|
||
TFileName fName,
|
||
delName;
|
||
|
||
delName.assign(logDir);
|
||
delName.append(_T("\\"));
|
||
delName.append(DATABASE_DELETE_RES_FILE_NAME);
|
||
|
||
fHandle = FindFirstFile(delName.name, &findData);
|
||
|
||
if (fHandle != INVALID_HANDLE_VALUE) {
|
||
do {
|
||
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||
success = GetParentName(delName.name, &fName);
|
||
ASSERT(success); // internal error if failed
|
||
|
||
fName.append(_T("\\"));
|
||
fName.append(findData.cFileName);
|
||
|
||
if (!DeleteFile(fName.name)) {
|
||
DPRINTF((_T("SGDatabase::Close: can't delete \"%s\", %d\n"), delName.name, GetLastError()));
|
||
}
|
||
}
|
||
} while (FindNextFile(fHandle, &findData));
|
||
|
||
success = FindClose(fHandle);
|
||
ASSERT(success);
|
||
fHandle = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
jetInitialized = FALSE;
|
||
DPRINTF((_T("JetTerm\n")));
|
||
return rc;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
/********************** SGDatabase class private methods *********************/
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::CreateTable(
|
||
const CHAR *tblName,
|
||
DWORD numColumns,
|
||
ColumnSpec **columnSpecs,
|
||
JET_COLUMNID *columnIDs,
|
||
JET_TABLEID *tblID)
|
||
{
|
||
JET_COLUMNDEF columnDef;
|
||
|
||
JET_COLUMNID colIDcount;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ColumnSpec *columnSpec;
|
||
|
||
DWORD i, j;
|
||
|
||
ASSERT(sesID != ~0);
|
||
ASSERT(dbID != ~0);
|
||
|
||
ASSERT(numColumns <= MAX_COLUMNS);
|
||
|
||
jetErr = JetCreateTable(sesID, dbID, tblName,
|
||
TABLE_PAGES, TABLE_DENSITY, tblID);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCreateTable: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetCreateTable: tblID=%lu colIDs={"), *tblID));
|
||
|
||
columnDef.cbStruct = sizeof(JET_COLUMNDEF);
|
||
columnDef.wCountry = COUNTRY_CODE;
|
||
columnDef.langid = LANG_ID;
|
||
columnDef.cp = CODE_PAGE;
|
||
columnDef.wCollate = COLLATE;
|
||
colIDcount = 1;
|
||
|
||
for (i = 0; i < numColumns; i++) {
|
||
columnSpec = columnSpecs[i];
|
||
columnDef.columnid = colIDcount;
|
||
columnDef.coltyp = columnSpec->coltyp;
|
||
columnDef.cbMax = columnSpec->size;
|
||
columnDef.grbit = columnSpec->grbit;
|
||
|
||
jetErr = JetAddColumn(sesID, *tblID, columnSpec->name,
|
||
&columnDef, NULL, 0, &columnIDs[i]);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("\nJetAddColumn: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
DPRINTF((_T(" %lu"), columnIDs[i]));
|
||
|
||
if (i+1 < numColumns && colIDcount == columnIDs[i]) {
|
||
ColIDCollision:
|
||
colIDcount++;
|
||
for (j = 0; j < i; j++)
|
||
if (colIDcount == columnIDs[j])
|
||
goto ColIDCollision;
|
||
}
|
||
}
|
||
|
||
DPRINTF((_T(" }\n")));
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::CreateIndex(
|
||
JET_TABLEID tblID,
|
||
const CHAR *keyName,
|
||
DWORD numKeys,
|
||
ColumnSpec **keyColumnSpecs)
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
CHAR indexStr[MAX_PATH];
|
||
|
||
ColumnSpec *keyColumnSpec;
|
||
|
||
DWORD indexStrLen,
|
||
i;
|
||
|
||
ASSERT(sesID != ~0);
|
||
ASSERT(numKeys <= MAX_KEYS);
|
||
|
||
indexStrLen = 0;
|
||
|
||
for (i = 0; i < numKeys; i++) {
|
||
keyColumnSpec = keyColumnSpecs[i];
|
||
indexStr[indexStrLen++] = '+';
|
||
strcpy(indexStr + indexStrLen, keyColumnSpec->name);
|
||
indexStrLen += strlen(keyColumnSpec->name) + 1;
|
||
}
|
||
|
||
indexStr[indexStrLen++] = '\0';
|
||
|
||
jetErr = JetCreateIndex(sesID, tblID, keyName, 0,
|
||
indexStr, indexStrLen, TABLE_DENSITY);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCreateIndex: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::OpenTable(
|
||
const CHAR *tblName,
|
||
DWORD numColumns,
|
||
ColumnSpec **columnSpecs,
|
||
JET_COLUMNID *columnIDs,
|
||
JET_TABLEID *tblID)
|
||
{
|
||
JET_COLUMNDEF columnDef;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ColumnSpec *columnSpec;
|
||
|
||
DWORD i;
|
||
|
||
ASSERT(sesID != ~0);
|
||
ASSERT(dbID != ~0);
|
||
|
||
ASSERT(numColumns <= MAX_COLUMNS);
|
||
|
||
jetErr = JetOpenTable(sesID, dbID, tblName, NULL, 0, 0, tblID);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetOpenTable: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetOpenTable: tblID=%lu colIDs={"), *tblID));
|
||
|
||
for (i = 0; i < numColumns; i++) {
|
||
columnSpec = columnSpecs[i];
|
||
jetErr = JetGetTableColumnInfo(sesID, *tblID, columnSpec->name,
|
||
&columnDef, sizeof(JET_COLUMNDEF), JET_ColInfo);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("\nJetGetTableColumnInfo: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
columnIDs[i] = columnDef.columnid;
|
||
DPRINTF((_T(" %lu"), columnIDs[i]));
|
||
}
|
||
|
||
DPRINTF((_T(" }\n")));
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::CloseTable(JET_TABLEID tblID)
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(sesID != ~0);
|
||
ASSERT(tblID != ~0);
|
||
|
||
jetErr = JetCloseTable(sesID, tblID);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCloseTable: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::PositionCursor(
|
||
JET_TABLEID tblID,
|
||
const CHAR *keyName,
|
||
const VOID *entry,
|
||
DWORD numKeys,
|
||
ColumnSpec **keyColumnSpecs) const
|
||
{
|
||
JET_COLTYP coltyp;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ColumnSpec *keyColumnSpec;
|
||
|
||
const BYTE *dataPtr[MAX_KEYS];
|
||
|
||
DWORD cbData[MAX_KEYS],
|
||
i;
|
||
|
||
ASSERT(sesID != ~0);
|
||
ASSERT(numKeys <= MAX_KEYS);
|
||
|
||
jetErr = JetSetCurrentIndex(sesID, tblID, keyName);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetSetCurrentIndex: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
for (i = 0; i < numKeys; i++) {
|
||
keyColumnSpec = keyColumnSpecs[i];
|
||
coltyp = keyColumnSpec->coltyp;
|
||
dataPtr[i] = (const BYTE *)entry + keyColumnSpec->offset;
|
||
|
||
if (coltyp == JET_coltypBinary) {
|
||
dataPtr[i] = *(BYTE **)dataPtr[i];
|
||
ASSERT(dataPtr[i] != NULL);
|
||
cbData[i] = (_tcslen((const TCHAR *)dataPtr[i]) + 1) * sizeof(TCHAR);
|
||
} else
|
||
cbData[i] = keyColumnSpec->size;
|
||
|
||
jetErr = JetMakeKey(sesID, tblID, dataPtr[i], cbData[i],
|
||
i == 0 ? JET_bitNewKey : 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetMakeKey: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
jetErr = JetSeek(sesID, tblID, JET_bitSeekEQ);
|
||
if (jetErr != JET_errSuccess) {
|
||
if (jetErr == JET_errRecordNotFound)
|
||
return 0;
|
||
DPRINTF((_T("JetSeek: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
for (i = 0; i < numKeys; i++) {
|
||
jetErr = JetMakeKey(sesID, tblID, dataPtr[i], cbData[i],
|
||
i == 0 ? JET_bitNewKey : 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetMakeKey: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
jetErr = JetSetIndexRange(sesID, tblID,
|
||
JET_bitRangeUpperLimit | JET_bitRangeInclusive);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetSetIndexRange: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::PositionCursorFirst(
|
||
JET_TABLEID tblID,
|
||
const CHAR *keyName) const
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
jetErr = JetSetCurrentIndex(sesID, tblID, keyName);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetSetCurrentIndex: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
jetErr = JetMove(sesID, tblID, JET_MoveFirst, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
if (jetErr == JET_errNoCurrentRecord)
|
||
return 0;
|
||
DPRINTF((_T("JetMove: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::PositionCursorNext(JET_TABLEID tblID) const
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
jetErr = JetMove(sesID, tblID, JET_MoveNext, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
if (jetErr == JET_errNoCurrentRecord)
|
||
return 0;
|
||
DPRINTF((_T("JetMove: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::PositionCursorLast(
|
||
JET_TABLEID tblID,
|
||
const CHAR *keyName) const
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
jetErr = JetSetCurrentIndex(sesID, tblID, keyName);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetSetCurrentIndex: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
jetErr = JetMove(sesID, tblID, JET_MoveLast, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
if (jetErr == JET_errNoCurrentRecord)
|
||
return 0;
|
||
DPRINTF((_T("JetMove: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::PutData(
|
||
JET_TABLEID tblID,
|
||
const VOID *entry,
|
||
DWORD numColumns,
|
||
ColumnSpec **columnSpecs,
|
||
const JET_COLUMNID *columnIDs)
|
||
{
|
||
JET_COLTYP coltyp;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ColumnSpec *columnSpec;
|
||
|
||
const BYTE *dataPtr;
|
||
|
||
DWORD cbData,
|
||
i;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
ASSERT(numColumns <= MAX_COLUMNS);
|
||
|
||
jetErr = JetPrepareUpdate(sesID, tblID, JET_prepInsert);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetPrepareUpdate: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < numColumns; i++) {
|
||
columnSpec = columnSpecs[i];
|
||
coltyp = columnSpec->coltyp;
|
||
|
||
if (columnSpec->grbit != JET_bitColumnAutoincrement) {
|
||
dataPtr = (const BYTE *)entry + columnSpec->offset;
|
||
if (coltyp == JET_coltypBinary
|
||
|| coltyp == JET_coltypLongBinary) {
|
||
dataPtr = *(BYTE **)dataPtr;
|
||
cbData = dataPtr != NULL
|
||
? (_tcslen((const TCHAR *)dataPtr) + 1) * sizeof(TCHAR)
|
||
: 0;
|
||
} else
|
||
cbData = columnSpec->size;
|
||
|
||
// May want to convert to JetSetColumns
|
||
|
||
jetErr = JetSetColumn(sesID, tblID, columnIDs[i],
|
||
dataPtr, cbData, 0, NULL);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetSetColumn: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
jetErr = JetUpdate(sesID, tblID, NULL, 0, NULL);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetUpdate: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::RetrieveData(
|
||
JET_TABLEID tblID,
|
||
VOID *entry,
|
||
DWORD numColumns,
|
||
ColumnSpec **columnSpecs,
|
||
const JET_COLUMNID *columnIDs,
|
||
DWORD includeMask) const
|
||
{
|
||
JET_COLTYP coltyp;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ColumnSpec *columnSpec;
|
||
|
||
BYTE *dataPtr;
|
||
|
||
DWORD cbData,
|
||
cbActual,
|
||
i;
|
||
|
||
BOOL varCol;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
ASSERT(numColumns <= MAX_COLUMNS);
|
||
|
||
// May want to convert to JetRetrieveColumns
|
||
|
||
for (i = 0; i < numColumns; i++)
|
||
if ((includeMask & (1U << i)) != 0) {
|
||
columnSpec = columnSpecs[i];
|
||
coltyp = columnSpec->coltyp;
|
||
varCol = coltyp == JET_coltypBinary
|
||
|| coltyp == JET_coltypLongBinary;
|
||
|
||
dataPtr = (BYTE *)entry + columnSpec->offset;
|
||
if (varCol)
|
||
dataPtr = *(BYTE **)dataPtr;
|
||
|
||
if (dataPtr != NULL) {
|
||
jetErr = JetRetrieveColumn(sesID, tblID, columnIDs[i],
|
||
dataPtr, columnSpec->size, &cbActual, 0, NULL);
|
||
|
||
if (jetErr == JET_errSuccess)
|
||
cbData = varCol
|
||
? (_tcslen((TCHAR *)dataPtr) + 1) * sizeof(TCHAR)
|
||
: columnSpec->size;
|
||
else if (varCol && jetErr == JET_wrnColumnNull) {
|
||
*(TCHAR *)dataPtr = _T('\0');
|
||
cbData = 0;
|
||
} else {
|
||
DPRINTF((_T("JetRetrieveColumn: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
if (cbActual != cbData) {
|
||
DPRINTF((_T("JetRetrieveColumn: cbActual=%lu!=%lu\n"),
|
||
cbActual, cbData));
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::Delete(JET_TABLEID tblID)
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
LONG count,
|
||
status;
|
||
|
||
count = 0;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
while (TRUE) {
|
||
jetErr = JetDelete(sesID, tblID);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetDelete: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
count++;
|
||
|
||
status = PositionCursorNext(tblID);
|
||
if (status < 0)
|
||
return status;
|
||
if (status == 0)
|
||
return count;
|
||
}
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::Count(
|
||
JET_TABLEID tblID,
|
||
const CHAR *keyName) const
|
||
{
|
||
JET_ERR jetErr;
|
||
LONG count,
|
||
status;
|
||
|
||
count = 0;
|
||
|
||
status = PositionCursorFirst(tblID, keyName);
|
||
|
||
if (status < 0)
|
||
return status;
|
||
if (status == 0)
|
||
return 0;
|
||
|
||
ASSERT(sesID != ~0);
|
||
|
||
jetErr = JetIndexRecordCount(sesID, tblID, (ULONG *) &count, MAXLONG);
|
||
|
||
if (jetErr != JET_errSuccess) {
|
||
if (jetErr == JET_errNoCurrentRecord)
|
||
return 0;
|
||
DPRINTF((_T("JetIndexRecordCount: jetErr=%ld\n"), jetErr));
|
||
return -1;
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
/********************** SGDatabase class public methods **********************/
|
||
/*****************************************************************************/
|
||
|
||
SGDatabase::SGDatabase()
|
||
{
|
||
fileName = NULL;
|
||
|
||
sesID =
|
||
tableID =
|
||
queueID =
|
||
stackID =
|
||
listID = ~0U;
|
||
dbID = ~0U;
|
||
|
||
numTableEntries =
|
||
numQueueEntries =
|
||
numStackEntries =
|
||
numListEntries = 0;
|
||
|
||
numUncommittedTableEntries =
|
||
numUncommittedQueueEntries =
|
||
numUncommittedStackEntries =
|
||
numUncommittedListEntries = 0;
|
||
|
||
inTransaction = FALSE;
|
||
|
||
if (!jetInitialized)
|
||
InitializeEngine();
|
||
|
||
numInstances++;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
SGDatabase::~SGDatabase()
|
||
{
|
||
Close();
|
||
|
||
ASSERT(fileName == NULL);
|
||
|
||
ASSERT(sesID == ~0U);
|
||
ASSERT(dbID == ~0U);
|
||
ASSERT(tableID == ~0U);
|
||
ASSERT(queueID == ~0U);
|
||
ASSERT(stackID == ~0U);
|
||
ASSERT(listID == ~0U);
|
||
|
||
ASSERT(numTableEntries == 0);
|
||
ASSERT(numQueueEntries == 0);
|
||
ASSERT(numStackEntries == 0);
|
||
ASSERT(numListEntries == 0);
|
||
|
||
ASSERT(numUncommittedTableEntries == 0);
|
||
ASSERT(numUncommittedQueueEntries == 0);
|
||
ASSERT(numUncommittedStackEntries == 0);
|
||
ASSERT(numUncommittedListEntries == 0);
|
||
|
||
ASSERT(!inTransaction);
|
||
|
||
if (--numInstances == 0 && jetInitialized) {
|
||
TerminateEngine();
|
||
}
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::Create(const TCHAR *dbName)
|
||
{
|
||
CHAR szConnect[MAX_PATH];
|
||
|
||
DWORD strLen1,
|
||
strLen2;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(fileName == NULL);
|
||
|
||
ASSERT(sesID == ~0U);
|
||
ASSERT(dbID == ~0U);
|
||
ASSERT(tableID == ~0U);
|
||
ASSERT(queueID == ~0U);
|
||
ASSERT(stackID == ~0U);
|
||
ASSERT(listID == ~0U);
|
||
|
||
ASSERT(numTableEntries == 0);
|
||
ASSERT(numQueueEntries == 0);
|
||
ASSERT(numStackEntries == 0);
|
||
ASSERT(numListEntries == 0);
|
||
|
||
ASSERT(numUncommittedTableEntries == 0);
|
||
ASSERT(numUncommittedQueueEntries == 0);
|
||
ASSERT(numUncommittedStackEntries == 0);
|
||
ASSERT(numUncommittedListEntries == 0);
|
||
|
||
ASSERT(!inTransaction);
|
||
|
||
if (!jetInitialized && !InitializeEngine())
|
||
return FALSE;
|
||
ASSERT(jetInitialized);
|
||
|
||
jetErr = JetBeginSession(instance, &sesID, USERNAME, PASSWORD);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetBeginSession: jetErr=%ld\n"), jetErr));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetBeginSession: sesID=%lu\n"), sesID));
|
||
|
||
ASSERT(fileName == NULL);
|
||
strLen1 = _tcslen(dbName);
|
||
fileName = new CHAR[strLen1+1];
|
||
ASSERT(fileName != NULL);
|
||
|
||
#ifdef _UNICODE
|
||
strLen2 = sprintf(fileName, "%S", dbName);
|
||
#else
|
||
strLen2 = sprintf(fileName, "%s", dbName);
|
||
#endif
|
||
ASSERT(strLen1 == strLen2);
|
||
|
||
sprintf(szConnect, ";COUNTRY=%u;LANGID=0x%04x;CP=%u",
|
||
COUNTRY_CODE, LANG_ID, CODE_PAGE);
|
||
|
||
//
|
||
// Create the database
|
||
//
|
||
|
||
jetErr = JetCreateDatabase(sesID, fileName, szConnect, &dbID, 0);
|
||
if (jetErr == JET_errSuccess) {
|
||
DPRINTF((_T("JetCreateDatabase(\"%s\"): dbID=%lu\n"),dbName, dbID));
|
||
} else {
|
||
if (jetErr != JET_errDatabaseDuplicate) {
|
||
DPRINTF((_T("JetCreateDatabase(\"%s\"): jetErr=%ld\n"),
|
||
dbName, jetErr));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!DeleteFile(dbName)) {
|
||
DPRINTF((_T("JetCreateDatabase: \"%s\" already exists and can't be deleted: %lu\n"),
|
||
dbName, GetLastError()));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
jetErr = JetCreateDatabase(sesID, fileName, szConnect, &dbID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCreateDatabase: deleted old \"%s\"; jetErr=%ld\n"),
|
||
dbName, jetErr));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
DPRINTF((_T("JetCreateDatabase: deleted old \"%s\"; new dbID=%lu\n"),
|
||
dbName, dbID));
|
||
}
|
||
|
||
if (!CreateTable(TABLE_NAME, TABLE_NCOLS,
|
||
tableColumnSpecs, tableColumnIDs, &tableID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateIndex(tableID, TABLE_KEY_NAME_FILE_ID,
|
||
TABLE_KEY_NCOLS_FILE_ID, tableKeyFileID)
|
||
|| !CreateIndex(tableID, TABLE_KEY_NAME_ATTR,
|
||
TABLE_KEY_NCOLS_ATTR, tableKeyAttr)
|
||
|| !CreateIndex(tableID, TABLE_KEY_NAME_CSID,
|
||
TABLE_KEY_NCOLS_CSID, tableKeyCSID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateTable(QUEUE_NAME, QUEUE_NCOLS,
|
||
queueColumnSpecs, queueColumnIDs, &queueID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateIndex(queueID, QUEUE_KEY_NAME_READY_TIME,
|
||
QUEUE_KEY_NCOLS_READY_TIME, queueKeyReadyTime)
|
||
|| !CreateIndex(queueID, QUEUE_KEY_NAME_FILE_ID,
|
||
QUEUE_KEY_NCOLS_FILE_ID, queueKeyFileID)
|
||
|| !CreateIndex(queueID, QUEUE_KEY_NAME_ORDER,
|
||
QUEUE_KEY_NCOLS_ORDER, queueKeyOrder)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateTable(STACK_NAME, STACK_NCOLS,
|
||
stackColumnSpecs, stackColumnIDs, &stackID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateIndex(stackID, STACK_KEY_NAME_FILE_ID,
|
||
STACK_KEY_NCOLS_FILE_ID, stackKeyFileID)
|
||
|| !CreateIndex(stackID, STACK_KEY_NAME_ORDER,
|
||
STACK_KEY_NCOLS_ORDER, stackKeyOrder)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateTable(LIST_NAME, LIST_NCOLS,
|
||
listColumnSpecs, listColumnIDs, &listID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!CreateIndex(listID, LIST_KEY_NAME_NAME,
|
||
LIST_KEY_NCOLS_NAME, listKeyName)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::Open(const TCHAR *dbName, BOOL is_log_drive)
|
||
{
|
||
SGNativeStackEntry stackEntry;
|
||
|
||
JET_ERR jetErr;
|
||
|
||
DWORD strLen1;
|
||
#ifdef _UNICODE
|
||
DWORD strLen2;
|
||
#endif
|
||
|
||
LONG status;
|
||
|
||
ASSERT(sesID == ~0U);
|
||
ASSERT(dbID == ~0U);
|
||
ASSERT(tableID == ~0U);
|
||
ASSERT(queueID == ~0U);
|
||
ASSERT(stackID == ~0U);
|
||
ASSERT(listID == ~0U);
|
||
|
||
ASSERT(numTableEntries == 0);
|
||
ASSERT(numQueueEntries == 0);
|
||
ASSERT(numStackEntries == 0);
|
||
ASSERT(numListEntries == 0);
|
||
|
||
ASSERT(numUncommittedTableEntries == 0);
|
||
ASSERT(numUncommittedQueueEntries == 0);
|
||
ASSERT(numUncommittedStackEntries == 0);
|
||
ASSERT(numUncommittedListEntries == 0);
|
||
|
||
ASSERT(!inTransaction);
|
||
|
||
// If this isn't the log drive, delete any log files that may exist
|
||
// from a previous run. This is an abnormal condition that can arise
|
||
// when the log drive is changing because of problems detected during
|
||
// a previous startup.
|
||
|
||
if (!is_log_drive) {
|
||
WIN32_FIND_DATA findData;
|
||
HANDLE fHandle;
|
||
BOOL success;
|
||
TFileName fName,
|
||
delName;
|
||
|
||
delName.assign(logDir);
|
||
delName.append(_T("\\"));
|
||
delName.append(DATABASE_DELETE_LOG_FILE_NAME);
|
||
|
||
fHandle = FindFirstFile(delName.name, &findData);
|
||
|
||
if (fHandle != INVALID_HANDLE_VALUE) {
|
||
do {
|
||
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||
success = GetParentName(delName.name, &fName);
|
||
ASSERT(success); // internal error if failed
|
||
|
||
fName.append(_T("\\"));
|
||
fName.append(findData.cFileName);
|
||
|
||
if (!DeleteFile(fName.name)) {
|
||
DPRINTF((_T("SGDatabase::Open: can't delete \"%s\", %d\n"), delName.name, GetLastError()));
|
||
}
|
||
}
|
||
} while (FindNextFile(fHandle, &findData));
|
||
|
||
success = FindClose(fHandle);
|
||
ASSERT(success);
|
||
fHandle = NULL;
|
||
}
|
||
}
|
||
|
||
if (!jetInitialized && !InitializeEngine()) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
ASSERT(jetInitialized);
|
||
|
||
jetErr = JetBeginSession(instance, &sesID, USERNAME, PASSWORD);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetBeginSession: jetErr=%ld\n"), jetErr));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetBeginSession: sesID=%lu\n"), sesID));
|
||
|
||
ASSERT(fileName == NULL);
|
||
strLen1 = _tcslen(dbName);
|
||
fileName = new CHAR[strLen1 + 1];
|
||
ASSERT(fileName != NULL);
|
||
|
||
#ifdef _UNICODE
|
||
strLen2 = sprintf(fileName, "%S", dbName);
|
||
#else
|
||
strLen2 = sprintf(fileName, "%s", dbName);
|
||
#endif
|
||
ASSERT(strLen1 == strLen2);
|
||
|
||
//
|
||
// Open the database
|
||
//
|
||
|
||
jetErr = JetAttachDatabase(sesID, fileName, 0);
|
||
if (jetErr != JET_errSuccess && jetErr != JET_wrnDatabaseAttached) {
|
||
if (jetErr == JET_errFileNotFound) {
|
||
DPRINTF((_T("JetAttachDatabase: \"%s\" not found\n"), dbName));
|
||
} else {
|
||
DPRINTF((_T("JetAttachDatabase(\"%s\"): jetErr=%ld\n"),
|
||
dbName, jetErr));
|
||
}
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
jetErr = JetOpenDatabase(sesID, fileName, NULL, &dbID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetOpenDatabase(\"%s\"): jetErr=%ld\n"), dbName, jetErr));
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
DPRINTF((_T("JetOpenDatabase(\"%s\"): dbID=%lu\n"), dbName, dbID));
|
||
|
||
if (!OpenTable(TABLE_NAME, TABLE_NCOLS, tableColumnSpecs,
|
||
tableColumnIDs, &tableID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!OpenTable(QUEUE_NAME, QUEUE_NCOLS, queueColumnSpecs,
|
||
queueColumnIDs, &queueID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!OpenTable(STACK_NAME, STACK_NCOLS, stackColumnSpecs,
|
||
stackColumnIDs, &stackID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if (!OpenTable(LIST_NAME, LIST_NCOLS, listColumnSpecs,
|
||
listColumnIDs, &listID)) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
if ((numTableEntries = Count(tableID, TABLE_KEY_NAME_FILE_ID)) < 0
|
||
|| (numQueueEntries = Count(queueID, QUEUE_KEY_NAME_READY_TIME)) < 0
|
||
|| (numStackEntries = Count(stackID, STACK_KEY_NAME_FILE_ID)) < 0
|
||
|| (numListEntries = Count(listID, LIST_KEY_NAME_NAME)) < 0) {
|
||
Close();
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::Close()
|
||
{
|
||
JET_ERR jetErr;
|
||
int strLen;
|
||
BOOL success = TRUE;
|
||
|
||
if (inTransaction) {
|
||
success = CommitTransaction();
|
||
inTransaction = FALSE;
|
||
}
|
||
|
||
ASSERT(numUncommittedTableEntries == 0);
|
||
ASSERT(numUncommittedQueueEntries == 0);
|
||
ASSERT(numUncommittedStackEntries == 0);
|
||
ASSERT(numUncommittedListEntries == 0);
|
||
|
||
if (tableID != ~0U) {
|
||
if (!CloseTable(tableID))
|
||
success = FALSE;
|
||
tableID = ~0U;
|
||
}
|
||
|
||
if (queueID != ~0U) {
|
||
if (!CloseTable(queueID))
|
||
success = FALSE;
|
||
queueID = ~0U;
|
||
}
|
||
|
||
if (stackID != ~0U) {
|
||
if (!CloseTable(stackID))
|
||
success = FALSE;
|
||
stackID = ~0U;
|
||
}
|
||
|
||
if (listID != ~0U) {
|
||
if (!CloseTable(listID))
|
||
success = FALSE;
|
||
listID = ~0U;
|
||
}
|
||
|
||
if (dbID != ~0U) {
|
||
ASSERT(fileName != NULL);
|
||
ASSERT(sesID != ~0U);
|
||
|
||
jetErr = JetCloseDatabase(sesID, dbID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCloseDatabase: jetErr=%ld\n"), jetErr));
|
||
success = FALSE;
|
||
}
|
||
|
||
jetErr = JetDetachDatabase(sesID, fileName);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetDetachDatabase: jetErr=%ld\n"), jetErr));
|
||
success = FALSE;
|
||
}
|
||
|
||
dbID = ~0U;
|
||
}
|
||
|
||
if (sesID != ~0U) {
|
||
jetErr = JetEndSession(sesID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetEndSession: jetErr=%ld\n"), jetErr));
|
||
success = FALSE;
|
||
}
|
||
sesID = ~0U;
|
||
}
|
||
|
||
if (fileName != NULL) {
|
||
delete[] fileName;
|
||
fileName = NULL;
|
||
}
|
||
|
||
numTableEntries =
|
||
numQueueEntries =
|
||
numStackEntries =
|
||
numListEntries = 0;
|
||
|
||
return success;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::BeginTransaction()
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(!inTransaction);
|
||
ASSERT(numUncommittedTableEntries == 0);
|
||
ASSERT(numUncommittedQueueEntries == 0);
|
||
ASSERT(numUncommittedStackEntries == 0);
|
||
ASSERT(numUncommittedListEntries == 0);
|
||
|
||
if (sesID == ~0U)
|
||
return -1;
|
||
|
||
jetErr = JetBeginTransaction(sesID);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetBeginTransaction: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
inTransaction = TRUE;
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::CommitTransaction()
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
if (sesID == ~0U)
|
||
return -1;
|
||
|
||
jetErr = JetCommitTransaction(sesID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetCommitTransaction: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
numTableEntries += numUncommittedTableEntries;
|
||
numQueueEntries += numUncommittedQueueEntries;
|
||
numStackEntries += numUncommittedStackEntries;
|
||
numListEntries += numUncommittedListEntries;
|
||
|
||
numUncommittedTableEntries = 0;
|
||
numUncommittedQueueEntries = 0;
|
||
numUncommittedStackEntries = 0;
|
||
numUncommittedListEntries = 0;
|
||
|
||
inTransaction = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
BOOL SGDatabase::AbortTransaction()
|
||
{
|
||
JET_ERR jetErr;
|
||
|
||
ASSERT(inTransaction);
|
||
inTransaction = FALSE;
|
||
|
||
if (sesID == ~0U)
|
||
return -1;
|
||
|
||
jetErr = JetRollback(sesID, 0);
|
||
if (jetErr != JET_errSuccess) {
|
||
DPRINTF((_T("JetRollback: jetErr=%ld\n"), jetErr));
|
||
return FALSE;
|
||
}
|
||
|
||
numUncommittedTableEntries = 0;
|
||
numUncommittedQueueEntries = 0;
|
||
numUncommittedStackEntries = 0;
|
||
numUncommittedListEntries = 0;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/******************************* Table methods *******************************/
|
||
|
||
LONG SGDatabase::TablePut(const SGNativeTableEntry *entry)
|
||
{
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
if (!PutData(tableID, entry, TABLE_NCOLS,
|
||
tableColumnSpecs, tableColumnIDs)) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedTableEntries++;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableGetFirstByFileID(SGNativeTableEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(tableID, TABLE_KEY_NAME_FILE_ID,
|
||
entry, TABLE_KEY_NCOLS_FILE_ID, tableKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(tableID, entry, TABLE_NCOLS, tableColumnSpecs,
|
||
tableColumnIDs, TABLE_EXCLUDE_FILE_ID_MASK ) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableGetFirstByAttr(SGNativeTableEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(tableID, TABLE_KEY_NAME_ATTR,
|
||
entry, TABLE_KEY_NCOLS_ATTR, tableKeyAttr);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(tableID, entry, TABLE_NCOLS, tableColumnSpecs,
|
||
tableColumnIDs, TABLE_EXCLUDE_ATTR_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableGetFirstByCSIndex(SGNativeTableEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(tableID, TABLE_KEY_NAME_CSID,
|
||
entry, TABLE_KEY_NCOLS_CSID, tableKeyCSID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(tableID, entry, TABLE_NCOLS, tableColumnSpecs,
|
||
tableColumnIDs, TABLE_EXCLUDE_CS_INDEX_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableGetNext(SGNativeTableEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursorNext(tableID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(tableID, entry, TABLE_NCOLS,
|
||
tableColumnSpecs, tableColumnIDs, GET_ALL_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableDeleteByFileID(DWORDLONG fileID)
|
||
{
|
||
SGNativeTableEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
entry.fileID = fileID;
|
||
status = PositionCursor(tableID, TABLE_KEY_NAME_FILE_ID,
|
||
&entry, TABLE_KEY_NCOLS_FILE_ID, tableKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(tableID);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedTableEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableDeleteByCSIndex(const CSID *csIndex)
|
||
{
|
||
SGNativeTableEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(csIndex != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
entry.csIndex = *csIndex;
|
||
status = PositionCursor(tableID, TABLE_KEY_NAME_CSID,
|
||
&entry, TABLE_KEY_NCOLS_CSID, tableKeyCSID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(tableID);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedTableEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::TableCount() const
|
||
{
|
||
LONG numEntries;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| tableID == ~0U)
|
||
return -1;
|
||
|
||
numEntries = numTableEntries + numUncommittedTableEntries;
|
||
|
||
ASSERT(numEntries >= 0);
|
||
ASSERT(Count(tableID, TABLE_KEY_NAME_FILE_ID) == numEntries);
|
||
|
||
return numEntries;
|
||
}
|
||
|
||
/******************************* Queue methods *******************************/
|
||
|
||
LONG SGDatabase::QueuePut(SGNativeQueueEntry *entry)
|
||
{
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
if (!PutData(queueID, entry, QUEUE_NCOLS,
|
||
queueColumnSpecs, queueColumnIDs)) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedQueueEntries++;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueGetFirst(SGNativeQueueEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursorFirst(queueID, QUEUE_KEY_NAME_READY_TIME);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(queueID, entry, QUEUE_NCOLS,
|
||
queueColumnSpecs, queueColumnIDs, GET_ALL_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueGetFirstByFileID(SGNativeQueueEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(queueID, QUEUE_KEY_NAME_FILE_ID,
|
||
entry, QUEUE_KEY_NCOLS_FILE_ID, queueKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(queueID, entry, QUEUE_NCOLS, queueColumnSpecs,
|
||
queueColumnIDs, QUEUE_EXCLUDE_FILE_ID_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueGetNext(SGNativeQueueEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursorNext(queueID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(queueID, entry, QUEUE_NCOLS,
|
||
queueColumnSpecs, queueColumnIDs, GET_ALL_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueDelete(DWORD order)
|
||
{
|
||
SGNativeQueueEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(sesID != ~0U);
|
||
ASSERT(dbID != ~0U);
|
||
ASSERT(queueID != ~0U);
|
||
|
||
entry.order = order;
|
||
status = PositionCursor(queueID, QUEUE_KEY_NAME_ORDER,
|
||
&entry, QUEUE_KEY_NCOLS_ORDER, queueKeyOrder);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(queueID);
|
||
ASSERT(status <= 1);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedQueueEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueDeleteByFileID(DWORDLONG fileID)
|
||
{
|
||
SGNativeQueueEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
entry.fileID = fileID;
|
||
status = PositionCursor(queueID, QUEUE_KEY_NAME_FILE_ID,
|
||
&entry, QUEUE_KEY_NCOLS_FILE_ID, queueKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(queueID);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedQueueEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::QueueCount() const
|
||
{
|
||
LONG numEntries;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| queueID == ~0U)
|
||
return -1;
|
||
|
||
numEntries = numQueueEntries + numUncommittedQueueEntries;
|
||
|
||
ASSERT(numEntries >= 0);
|
||
ASSERT(Count(queueID, QUEUE_KEY_NAME_READY_TIME) == numEntries);
|
||
|
||
return numEntries;
|
||
}
|
||
|
||
/******************************* Stack methods *******************************/
|
||
|
||
LONG SGDatabase::StackPut(DWORDLONG fileID, BOOL done)
|
||
{
|
||
SGNativeStackEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
if (done)
|
||
entry.order = 0;
|
||
else {
|
||
status = PositionCursorLast(stackID, STACK_KEY_NAME_ORDER);
|
||
if (status < 0)
|
||
return -1;
|
||
if (status == 0)
|
||
entry.order = 1;
|
||
else {
|
||
if (!RetrieveData(stackID, &entry, STACK_NCOLS,
|
||
stackColumnSpecs, stackColumnIDs, STACK_GET_ORDER_ONLY_MASK))
|
||
return -1;
|
||
entry.order++;
|
||
}
|
||
}
|
||
|
||
entry.fileID = fileID;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
if (!PutData(stackID, &entry, STACK_NCOLS,
|
||
stackColumnSpecs, stackColumnIDs)) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedStackEntries++;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackGetTop(SGNativeStackEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursorLast(stackID, STACK_KEY_NAME_ORDER);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
status = RetrieveData(stackID, entry, STACK_NCOLS,
|
||
stackColumnSpecs, stackColumnIDs, GET_ALL_MASK);
|
||
if (status < 0)
|
||
return status;
|
||
ASSERT(status == 1);
|
||
|
||
return entry->order == 0 ? 0 : 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackGetFirstByFileID(SGNativeStackEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(stackID, STACK_KEY_NAME_FILE_ID,
|
||
entry, STACK_KEY_NCOLS_FILE_ID, stackKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(stackID, entry, STACK_NCOLS, stackColumnSpecs,
|
||
stackColumnIDs, STACK_EXCLUDE_FILE_ID_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackGetNext(SGNativeStackEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursorNext(stackID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(stackID, entry, STACK_NCOLS,
|
||
stackColumnSpecs, stackColumnIDs, GET_ALL_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackDelete(DWORD order)
|
||
{
|
||
SGNativeStackEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
entry.order = order;
|
||
status = PositionCursor(stackID, STACK_KEY_NAME_ORDER,
|
||
&entry, STACK_KEY_NCOLS_ORDER, stackKeyOrder);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(stackID);
|
||
ASSERT(order == 0 || status <= 1);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedStackEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackDeleteByFileID(DWORDLONG fileID)
|
||
{
|
||
SGNativeStackEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
entry.fileID = fileID;
|
||
status = PositionCursor(stackID, STACK_KEY_NAME_FILE_ID,
|
||
&entry, STACK_KEY_NCOLS_FILE_ID, stackKeyFileID);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(stackID);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
numUncommittedStackEntries -= status;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::StackCount() const
|
||
{
|
||
LONG numEntries;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| stackID == ~0U)
|
||
return -1;
|
||
|
||
numEntries = numStackEntries + numUncommittedStackEntries;
|
||
|
||
ASSERT(numEntries >= 0);
|
||
ASSERT(Count(stackID, STACK_KEY_NAME_ORDER) == numEntries);
|
||
|
||
return numEntries;
|
||
}
|
||
|
||
/******************************* List methods ********************************/
|
||
|
||
LONG SGDatabase::ListWrite(const SGNativeListEntry *entry)
|
||
{
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(entry != NULL);
|
||
ASSERT(entry->name != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| listID == ~0U)
|
||
return -1;
|
||
|
||
// May want to overwrite the entry directly instead of deleting and inserting
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = ListDelete(entry->name);
|
||
ASSERT(status <= 1);
|
||
if (status < 0
|
||
|| !PutData(listID, entry, LIST_NCOLS,
|
||
listColumnSpecs, listColumnIDs)) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
if (status == 0)
|
||
numUncommittedListEntries++;
|
||
|
||
if (!alreadyInTransaction && !CommitTransaction())
|
||
return -1;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::ListRead(SGNativeListEntry *entry) const
|
||
{
|
||
LONG status;
|
||
|
||
ASSERT(entry != NULL);
|
||
ASSERT(entry->name != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| listID == ~0U)
|
||
return -1;
|
||
|
||
status = PositionCursor(listID, LIST_KEY_NAME_NAME,
|
||
entry, LIST_KEY_NCOLS_NAME, listKeyName);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
return RetrieveData(listID, entry, LIST_NCOLS, listColumnSpecs,
|
||
listColumnIDs, LIST_EXCLUDE_NAME_MASK) ? 1 : -1;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::ListDelete(const TCHAR *name)
|
||
{
|
||
SGNativeListEntry entry;
|
||
|
||
LONG status;
|
||
|
||
BOOL alreadyInTransaction = inTransaction;
|
||
|
||
ASSERT(name != NULL);
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| listID == ~0U)
|
||
return -1;
|
||
|
||
entry.name = name;
|
||
entry.value = NULL;
|
||
|
||
status = PositionCursor(listID, LIST_KEY_NAME_NAME,
|
||
&entry, LIST_KEY_NCOLS_NAME, listKeyName);
|
||
if (status <= 0)
|
||
return status;
|
||
|
||
if (!inTransaction && !BeginTransaction())
|
||
return -1;
|
||
|
||
ASSERT(inTransaction);
|
||
|
||
status = Delete(listID);
|
||
if (status < 0) {
|
||
if (!alreadyInTransaction)
|
||
AbortTransaction();
|
||
return -1;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/*****************************************************************************/
|
||
|
||
LONG SGDatabase::ListCount() const
|
||
{
|
||
LONG numEntries;
|
||
|
||
if (sesID == ~0U
|
||
|| dbID == ~0U
|
||
|| listID == ~0U)
|
||
return -1;
|
||
|
||
numEntries = numListEntries + numUncommittedListEntries;
|
||
|
||
ASSERT(numEntries >= 0);
|
||
ASSERT(Count(listID, LIST_KEY_NAME_NAME) == numEntries);
|
||
|
||
return numEntries;
|
||
}
|
||
|