Skip to content

Commit

Permalink
Implement read-only mode
Browse files Browse the repository at this point in the history
  • Loading branch information
paladine committed Oct 4, 2024
1 parent 3fa0f54 commit 8b41bdd
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 58 deletions.
12 changes: 9 additions & 3 deletions btrieve/BtrieveDriver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static std::basic_string<tchar> toUpper(const std::basic_string<tchar> &value) {
#define unlink _wunlink
#endif

BtrieveError BtrieveDriver::open(const tchar *fileName) {
BtrieveError BtrieveDriver::open(const tchar *fileName, OpenMode openMode) {
int64_t fileModificationTimeDat;
int64_t fileModificationTimeDb;

Expand Down Expand Up @@ -92,20 +92,26 @@ BtrieveError BtrieveDriver::open(const tchar *fileName) {
BtrieveError error;

if (dbExists) {
error = sqlDatabase->open(dbPath.c_str());
error = sqlDatabase->open(dbPath.c_str(), openMode);
} else {
BtrieveDatabase btrieveDatabase;
std::unique_ptr<RecordLoader> recordLoader;
error = btrieveDatabase.parseDatabase(
fileName,
[this, &dbPath, &btrieveDatabase, &recordLoader]() {
[this, &dbPath, &btrieveDatabase, &recordLoader, openMode]() {
recordLoader = sqlDatabase->create(dbPath.c_str(), btrieveDatabase);
return true;
},
[&recordLoader](const std::basic_string_view<uint8_t> record) {
return recordLoader->onRecordLoaded(record);
},
[&recordLoader]() { recordLoader->onRecordsComplete(); });

// if newly created and they want read-only, close and reopen
if (openMode == OpenMode::ReadOnly) {
sqlDatabase->close();
error = sqlDatabase->open(dbPath.c_str(), openMode);
}
}

if (error == BtrieveError::Success) {
Expand Down
4 changes: 3 additions & 1 deletion btrieve/BtrieveDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <memory>

#include "ErrorCode.h"
#include "OpenMode.h"
#include "OperationCode.h"
#include "Record.h"
#include "SqlDatabase.h"
Expand All @@ -21,7 +22,8 @@ class BtrieveDriver {

~BtrieveDriver();

BtrieveError open(const tchar *fileName);
BtrieveError open(const tchar *fileName,
OpenMode openMode = OpenMode::Normal);

// Closes an opened database.
void close();
Expand Down
4 changes: 2 additions & 2 deletions btrieve/Key_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ createACSReplacementMultipleKey() {
INSTANTIATE_TEST_CASE_P(Key, ParameterizedACSReplacementMultipleKeyFixture,
::testing::ValuesIn(createACSReplacementMultipleKey()));

TEST(FloatKeys) {
TEST(Key, FloatKeys) {
KeyDefinition keyDefinition(0, sizeof(float), 0, KeyDataType::Float,
UseExtendedDataType, false, 0, 0, 0, "",
std::vector<char>());
Expand All @@ -341,7 +341,7 @@ TEST(FloatKeys) {
EXPECT_EQ(actual, value);
}

TEST(DoubleKeys) {
TEST(Key, DoubleKeys) {
KeyDefinition keyDefinition(0, sizeof(double), 0, KeyDataType::Float,
UseExtendedDataType, false, 0, 0, 0, "",
std::vector<char>());
Expand Down
12 changes: 6 additions & 6 deletions btrieve/LRUCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
#include <unordered_map>

namespace btrieve {
template <typename K, typename V> class LRUCache {

public:
template <typename K, typename V>
class LRUCache {
public:
LRUCache(size_t maxSize_) : maxSize(maxSize_) {}

V &cache(const K &key, const V &value) {
Expand Down Expand Up @@ -67,13 +67,13 @@ template <typename K, typename V> class LRUCache {
keyValuesMap.erase(key);
}

private:
private:
size_t maxSize;
// most recently used is at the front, least recently used at the back
std::list<K> orderedKeys;
std::unordered_map<
K, std::pair<std::shared_ptr<V>, typename std::list<K>::iterator>>
keyValuesMap;
};
} // namespace btrieve
#endif
} // namespace btrieve
#endif
14 changes: 14 additions & 0 deletions btrieve/OpenMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef __OPEN_MODE_H_
#define __OPEN_MODE_H_

namespace btrieve {
enum OpenMode {
Normal = 0,
Accelerated = -1,
ReadOnly = -2,
VerifyWriteOperations = -3,
ExclusiveAccess = -4
};
}

#endif
4 changes: 3 additions & 1 deletion btrieve/SqlDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "BtrieveDatabase.h"
#include "ErrorCode.h"
#include "LRUCache.h"
#include "OpenMode.h"
#include "OperationCode.h"
#include "Query.h"
#include "Text.h"
Expand All @@ -32,7 +33,8 @@ class SqlDatabase {
virtual const tchar *getFileExtension() = 0;

// Opens a Btrieve database as a sql backed file.
virtual BtrieveError open(const tchar *fileName) = 0;
virtual BtrieveError open(const tchar *fileName,
OpenMode openMode = OpenMode::Normal) = 0;

// Creates a new sql backed file using database as the source of records
virtual std::unique_ptr<RecordLoader> create(
Expand Down
15 changes: 12 additions & 3 deletions btrieve/SqliteDatabase.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,14 @@ class SqliteCreationRecordLoader : public RecordLoader {

// Opens a Btrieve database as a sql backed file. Will convert a legacy file
// in place if required. Throws a BtrieveException if something fails.
BtrieveError SqliteDatabase::open(const tchar *fileName) {
BtrieveError SqliteDatabase::open(const tchar *fileName, OpenMode openMode) {
sqlite3 *db;
int errorCode = sqlite3_open_v2(toStdString(fileName).c_str(), &db,
SQLITE_OPEN_READWRITE | openFlags, nullptr);
unsigned int openFlags = this->openFlags | (openMode == OpenMode::ReadOnly)
? SQLITE_OPEN_READONLY
: SQLITE_OPEN_READWRITE;

int errorCode =
sqlite3_open_v2(toStdString(fileName).c_str(), &db, openFlags, nullptr);
if (errorCode != SQLITE_OK) {
throwException(errorCode);
return BtrieveError::IOError;
Expand Down Expand Up @@ -367,6 +371,9 @@ void SqliteDatabase::createSqliteTriggers(const BtrieveDatabase &database) {
void SqliteDatabase::close() {
preparedStatements.clear();
database.reset();

keys.clear();
cache.clear();
}

SqlitePreparedStatement &SqliteDatabase::getPreparedStatement(
Expand Down Expand Up @@ -513,6 +520,8 @@ class SqliteErrorConverter {
} else if (extendedErrorCode == SQLITE_CONSTRAINT_TRIGGER) {
error = BtrieveError::NonModifiableKeyValue;
}
} else if (errorCode == SQLITE_READONLY) {
error = BtrieveError::AccessDenied;
}
}

Expand Down
3 changes: 2 additions & 1 deletion btrieve/SqliteDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class SqliteDatabase : public SqlDatabase {

virtual const tchar *getFileExtension() override { return _TEXT("db"); };

virtual BtrieveError open(const tchar *fileName) override;
virtual BtrieveError open(const tchar *fileName,
OpenMode openMode = OpenMode::Normal) override;

virtual std::unique_ptr<RecordLoader> create(
const tchar *fileName, const BtrieveDatabase &database) override;
Expand Down
1 change: 1 addition & 0 deletions vstudio/btrieve_driver/btrieve_driver.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ClInclude Include="..\..\btrieve\KeyDataType.h" />
<ClInclude Include="..\..\btrieve\KeyDefinition.h" />
<ClInclude Include="..\..\btrieve\LRUCache.h" />
<ClInclude Include="..\..\btrieve\OpenMode.h" />
<ClInclude Include="..\..\btrieve\OperationCode.h" />
<ClInclude Include="..\..\btrieve\Query.h" />
<ClInclude Include="..\..\btrieve\Reader.h" />
Expand Down
3 changes: 3 additions & 0 deletions vstudio/btrieve_driver/btrieve_driver.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
<ClInclude Include="..\..\btrieve\Text.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\btrieve\OpenMode.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\btrieve\BtrieveDatabase.cc">
Expand Down
14 changes: 9 additions & 5 deletions vstudio/wbtrv32/dllmain.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "framework.h"

#include "wbtrv32.h"
// #define DEBUG_ATTACH

#ifdef DEBUG_ATTACH
Expand All @@ -9,19 +9,23 @@

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved) {
TCHAR buf[MAX_PATH];
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
#ifdef DEBUG_ATTACH
{
TCHAR buf[MAX_PATH];

GetProcessImageFileName(GetCurrentProcess(), buf, ARRAYSIZE(buf));
MessageBox(NULL, TEXT("Attach now"), buf, MB_OK);
#else
;
}
#endif
wbtrv32::processAttach();
break;
case DLL_PROCESS_DETACH:
wbtrv32::processDetach();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
Expand Down
95 changes: 59 additions & 36 deletions vstudio/wbtrv32/wbtrv32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,30 @@ using namespace btrieve;
static std::unordered_map<std::wstring, std::shared_ptr<BtrieveDriver>>
_openFiles;

static void debug(const char *format, ...) {
char buf[256];
static std::unique_ptr<FILE, decltype(&fclose)> _logFile(nullptr, &fclose);

va_list args;
va_start(args, format);
int len = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
void wbtrv32::processAttach() {
_logFile.reset(_wfsopen(L"wbtrv32.log", L"ab", _SH_DENYWR));
}

// append cr/lf
buf[len] = '\r';
buf[len + 1] = '\n';
buf[len + 2] = 0;
void wbtrv32::processDetach() { _logFile.reset(nullptr); }

OutputDebugStringA(buf);
}
static BtrieveDriver *getOpenDatabase(LPVOID lpPositioningBlock) {
WCHAR guidStr[64];

enum BtrieveOpenMode {
Normal = 0,
Accelerated = -1,
ReadOnly = -2,
VerifyWriteOperations = -3,
ExclusiveAccess = -4
};
if (lpPositioningBlock == nullptr) {
return nullptr;
}

StringFromGUID2(*reinterpret_cast<GUID *>(lpPositioningBlock), guidStr,
ARRAYSIZE(guidStr));

auto iterator = _openFiles.find(std::wstring(guidStr));
if (iterator == _openFiles.end()) {
return nullptr;
}
return iterator->second.get();
}

struct BtrieveCommand {
OperationCode operation;
Expand All @@ -54,6 +55,38 @@ struct BtrieveCommand {
char keyNumber;
};

static void debug(const BtrieveCommand &command, const char *format, ...) {
BtrieveDriver *driver = getOpenDatabase(command.lpPositionBlock);
char buf[256];
int len;

if (driver != nullptr) {
len = snprintf(buf, sizeof(buf),
"[%S]: ", driver->getOpenedFilename().c_str());
OutputDebugStringA(buf);
if (_logFile) {
fwrite(buf, 1, len, _logFile.get());
}
}

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

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

OutputDebugStringA(buf);

if (_logFile) {
fwrite(buf, 1, len + 2, _logFile.get());
fflush(_logFile.get());
}
}

static void AddToOpenFiles(BtrieveCommand &command,
std::shared_ptr<BtrieveDriver> driver) {
// add to my list of open files
Expand All @@ -77,9 +110,10 @@ static BtrieveError Open(BtrieveCommand &command) {

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

debug("Attempting to open %s with openMode %d", lpszFilename, openMode);
debug(command, "Attempting to open %s with openMode %d", lpszFilename,
openMode);

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

Expand All @@ -101,7 +135,7 @@ static BtrieveError Open(BtrieveCommand &command) {
std::make_shared<BtrieveDriver>(new SqliteDatabase());

try {
BtrieveError error = driver->open(fullPathFileName);
BtrieveError error = driver->open(fullPathFileName, openMode);
if (error != BtrieveError::Success) {
return error;
}
Expand All @@ -114,18 +148,6 @@ static BtrieveError Open(BtrieveCommand &command) {
}
}

static BtrieveDriver *getOpenDatabase(LPVOID lpPositioningBlock) {
WCHAR guidStr[64];
StringFromGUID2(*reinterpret_cast<GUID *>(lpPositioningBlock), guidStr,
ARRAYSIZE(guidStr));

auto iterator = _openFiles.find(std::wstring(guidStr));
if (iterator == _openFiles.end()) {
return nullptr;
}
return iterator->second.get();
}

static BtrieveError Close(BtrieveCommand &command) {
WCHAR guidStr[64];
StringFromGUID2(*reinterpret_cast<GUID *>(command.lpPositionBlock), guidStr,
Expand Down Expand Up @@ -613,7 +635,9 @@ static BtrieveError handle(BtrieveCommand &command) {
break;
}

debug("handled %d, returned %d", command.operation, error);
if (error != BtrieveError::Success) {
debug(command, "handled %d, returned %d", command.operation, error);
}

return error;
}
Expand All @@ -623,7 +647,6 @@ extern "C" int __stdcall BTRCALL(WORD wOperation, LPVOID lpPositionBlock,
LPDWORD lpdwDataBufferLength,
LPVOID lpKeyBuffer, BYTE bKeyLength,
CHAR sbKeyNumber) {
// TODO change to initializer
BtrieveCommand btrieveCommand;
btrieveCommand.operation = static_cast<OperationCode>(wOperation);
btrieveCommand.lpPositionBlock = lpPositionBlock;
Expand Down
4 changes: 4 additions & 0 deletions vstudio/wbtrv32/wbtrv32.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ typedef struct _tagKEYSPEC {

static_assert(sizeof(FILESPEC) == 16);
static_assert(sizeof(KEYSPEC) == 16);

void processAttach();

void processDetach();
} // namespace wbtrv32

#endif
Loading

0 comments on commit 8b41bdd

Please sign in to comment.