Skip to content

Commit

Permalink
Support opening an already opened database
Browse files Browse the repository at this point in the history
  • Loading branch information
paladine committed Sep 29, 2024
1 parent da125fe commit d76e2ee
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 41 deletions.
2 changes: 2 additions & 0 deletions btrieve/BtrieveDriver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ BtrieveError BtrieveDriver::open(const tchar *fileName) {
std::filesystem::path(fileName).replace_extension(
sqlDatabase->getFileExtension());

openedFilename = fileName;

bool datExists = fileExists(fileName, fileModificationTimeDat);
bool dbExists = fileExists(dbPath.c_str(), fileModificationTimeDb);
if (!dbExists) {
Expand Down
3 changes: 3 additions & 0 deletions btrieve/BtrieveDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ class BtrieveDriver {
return ret;
}

std::basic_string<tchar> getOpenedFilename() { return openedFilename; }

private:
std::unique_ptr<SqlDatabase> sqlDatabase;
std::unique_ptr<Query> previousQuery;
std::basic_string<tchar> openedFilename;
};
} // namespace btrieve
#endif
13 changes: 7 additions & 6 deletions btrieve/SqliteTransaction.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#ifndef __SQLITE_TRANSACTION_H_
#define __SQLITE_TRANSACTION_H_

#include "SqliteUtil.h"
#include "sqlite/sqlite3.h"
#include <atomic>
#include <memory>

#include "SqliteUtil.h"
#include "sqlite/sqlite3.h"

namespace btrieve {

class SqliteTransaction {
public:
public:
SqliteTransaction(std::shared_ptr<sqlite3> database_) : database(database_) {
beginTransaction();
}
Expand All @@ -18,7 +19,7 @@ class SqliteTransaction {

void rollback() { execute("ROLLBACK"); }

private:
private:
void beginTransaction() { execute("BEGIN"); }

void execute(const char *sql) {
Expand All @@ -31,6 +32,6 @@ class SqliteTransaction {

std::shared_ptr<sqlite3> database;
};
} // namespace btrieve
} // namespace btrieve

#endif
#endif
121 changes: 86 additions & 35 deletions vstudio/wbtrv32/wbtrv32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,25 @@
#include "combaseapi.h"
#include "framework.h"

static std::unordered_map<std::wstring, std::unique_ptr<btrieve::BtrieveDriver>>
static std::unordered_map<std::wstring, std::shared_ptr<btrieve::BtrieveDriver>>
_openFiles;

static void debug(const char *format, ...) {
char buf[256];

va_list args;
va_start(args, format);
int len = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);

// append cr/lf
buf[len] = '\r';
buf[len + 1] = '\n';
buf[len + 2] = 0;

OutputDebugStringA(buf);
}

enum BtrieveOpenMode {
Normal = 0,
Accelerated = -1,
Expand All @@ -34,45 +50,62 @@ struct BtrieveCommand {
char keyNumber;
};

static void AddToOpenFiles(BtrieveCommand &command,
std::shared_ptr<btrieve::BtrieveDriver> driver) {
// add to my list of open files
GUID guid;
CoCreateGuid(&guid);

WCHAR guidStr[64];
StringFromGUID2(guid, guidStr, ARRAYSIZE(guidStr));

_openFiles.emplace(std::make_pair(std::wstring(guidStr), driver));

// write the GUID in the pos block for other calls
memset(command.lpPositionBlock, 0, POSBLOCK_LENGTH);
memcpy(command.lpPositionBlock, &guid, sizeof(UUID));
}

static btrieve::BtrieveError Open(BtrieveCommand &command) {
tchar fileName[MAX_PATH];
tchar fullPathFileName[MAX_PATH];
size_t unused;

const char *lpszFilename =
reinterpret_cast<const char *>(command.lpKeyBuffer);
auto openMode = static_cast<BtrieveOpenMode>(command.keyNumber);

btrieve::BtrieveDriver *db =
new btrieve::BtrieveDriver(new btrieve::SqliteDatabase());
debug("Attempting to open %s with openMode %d", lpszFilename, openMode);

mbstowcs_s(&unused, fileName, ARRAYSIZE(fileName), lpszFilename, _TRUNCATE);

GetFullPathName(fileName, ARRAYSIZE(fullPathFileName), fullPathFileName,
nullptr);

// see if we've already opened this file
for (auto iterator = _openFiles.begin(); iterator != _openFiles.end();
++iterator) {
if (!_wcsicmp(iterator->second->getOpenedFilename().c_str(),
fullPathFileName)) {
// already got one? let's reuse it
AddToOpenFiles(command, iterator->second);
return btrieve::BtrieveError::Success;
}
}

std::shared_ptr<btrieve::BtrieveDriver> driver =
std::make_shared<btrieve::BtrieveDriver>(new btrieve::SqliteDatabase());

try {
btrieve::BtrieveError error = db->open(fileName);
btrieve::BtrieveError error = driver->open(fullPathFileName);
if (error != btrieve::BtrieveError::Success) {
delete db;
return error;
}

// add to my list of open files
GUID guid;
CoCreateGuid(&guid);

WCHAR guidStr[64];
StringFromGUID2(guid, guidStr, ARRAYSIZE(guidStr));

_openFiles.emplace(std::make_pair(std::wstring(guidStr), db));
db = nullptr;

// write the GUID in the pos block for other calls
memset(command.lpPositionBlock, 0, POSBLOCK_LENGTH);
memcpy(command.lpPositionBlock, &guid, sizeof(UUID));
AddToOpenFiles(command, driver);

return btrieve::BtrieveError::Success;
} catch (const btrieve::BtrieveException &ex) {
if (db != nullptr) {
delete db;
}
return btrieve::BtrieveError::FileNotFound;
}
}
Expand Down Expand Up @@ -400,15 +433,21 @@ static btrieve::BtrieveError Stop(const BtrieveCommand &command) {
}

static btrieve::BtrieveError handle(BtrieveCommand &command) {
btrieve::BtrieveError error;

switch (command.operation) {
case btrieve::OperationCode::Open:
return Open(command);
error = Open(command);
break;
case btrieve::OperationCode::Close:
return Close(command);
error = Close(command);
break;
case btrieve::OperationCode::Stat:
return Stat(command);
error = Stat(command);
break;
case btrieve::OperationCode::Delete:
return Delete(command);
error = Delete(command);
break;
case btrieve::OperationCode::StepFirst:
case btrieve::OperationCode::StepFirst + 100:
case btrieve::OperationCode::StepFirst + 200:
Expand All @@ -429,7 +468,8 @@ static btrieve::BtrieveError handle(BtrieveCommand &command) {
case btrieve::OperationCode::StepPrevious + 200:
case btrieve::OperationCode::StepPrevious + 300:
case btrieve::OperationCode::StepPrevious + 400:
return Step(command);
error = Step(command);
break;
case btrieve::OperationCode::AcquireFirst:
case btrieve::OperationCode::AcquireLast:
case btrieve::OperationCode::AcquireNext:
Expand All @@ -449,33 +489,44 @@ static btrieve::BtrieveError handle(BtrieveCommand &command) {
case btrieve::OperationCode::QueryLess:
case btrieve::OperationCode::QueryLessOrEqual:
// TODO don't forget all +100-400 for these queries as well
return Query(command);
error = Query(command);
break;
case btrieve::OperationCode::GetPosition:
return GetPosition(command);
error = GetPosition(command);
break;
case btrieve::OperationCode::GetDirectChunkOrRecord:
case btrieve::OperationCode::GetDirectChunkOrRecord + 100:
case btrieve::OperationCode::GetDirectChunkOrRecord + 200:
case btrieve::OperationCode::GetDirectChunkOrRecord + 300:
case btrieve::OperationCode::GetDirectChunkOrRecord + 400:
return GetDirectRecord(command);
error = GetDirectRecord(command);
break;
case btrieve::OperationCode::Update:
// TODO update logical currency
return Upsert(command, [](btrieve::BtrieveDriver *driver,
std::basic_string_view<uint8_t> record) {
error = Upsert(command, [](btrieve::BtrieveDriver *driver,
std::basic_string_view<uint8_t> record) {
auto position = driver->getPosition();
return std::make_pair(driver->updateRecord(position, record), position);
});
break;
case btrieve::OperationCode::Insert:
// TODO update logical currency
return Upsert(command, [](btrieve::BtrieveDriver *driver,
std::basic_string_view<uint8_t> record) {
error = Upsert(command, [](btrieve::BtrieveDriver *driver,
std::basic_string_view<uint8_t> record) {
return driver->insertRecord(record);
});
break;
case btrieve::OperationCode::Stop:
return Stop(command);
error = Stop(command);
break;
default:
return btrieve::BtrieveError::InvalidOperation;
error = btrieve::BtrieveError::InvalidOperation;
break;
}

debug("handled %d, returned %d", command.operation, error);

return error;
}

extern "C" int __stdcall BTRCALL(WORD wOperation, LPVOID lpPositionBlock,
Expand Down
36 changes: 36 additions & 0 deletions vstudio/wbtrv32/wbtrv32_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,42 @@ TEST_F(wbtrv32Test, LoadsAndClosesDatabase) {
btrieve::BtrieveError::FileNotOpen);
}

TEST_F(wbtrv32Test, LoadsSameDatabaseTwice) {
char posBlock2[128];
auto mbbsEmuDb = tempPath->copyToTempPath("assets/MBBSEMU.DB");
ASSERT_FALSE(mbbsEmuDb.empty());

DWORD dwDataBufferLength = 0;

ASSERT_EQ(btrcall(btrieve::OperationCode::Open, posBlock, nullptr,
&dwDataBufferLength,
const_cast<LPVOID>(reinterpret_cast<LPCVOID>(
toStdString(mbbsEmuDb.c_str()).c_str())),
-1, 0),
btrieve::BtrieveError::Success);

ASSERT_EQ(btrcall(btrieve::OperationCode::Open, posBlock2, nullptr,
&dwDataBufferLength,
const_cast<LPVOID>(reinterpret_cast<LPCVOID>(
toStdString(mbbsEmuDb.c_str()).c_str())),
-1, 0),
btrieve::BtrieveError::Success);

ASSERT_EQ(btrcall(btrieve::OperationCode::Close, posBlock, nullptr,
&dwDataBufferLength, nullptr, 0, 0),
btrieve::BtrieveError::Success);
ASSERT_EQ(btrcall(btrieve::OperationCode::Close, posBlock, nullptr,
&dwDataBufferLength, nullptr, 0, 0),
btrieve::BtrieveError::FileNotOpen);

ASSERT_EQ(btrcall(btrieve::OperationCode::Close, posBlock2, nullptr,
&dwDataBufferLength, nullptr, 0, 0),
btrieve::BtrieveError::Success);
ASSERT_EQ(btrcall(btrieve::OperationCode::Close, posBlock2, nullptr,
&dwDataBufferLength, nullptr, 0, 0),
btrieve::BtrieveError::FileNotOpen);
}

TEST_F(wbtrv32Test, CannotStatUnopenedDatabase) {
unsigned char buffer[256];
char fileName[MAX_PATH] = "test";
Expand Down

0 comments on commit d76e2ee

Please sign in to comment.