|
|
/*++
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; }
|