Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ find_rpm = use_extension(
use_repo(find_rpm, "rules_pkg_rpmbuild")

register_toolchains("@rules_pkg_rpmbuild//:all")

# Dependency for JSON
bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1")
3 changes: 2 additions & 1 deletion src/MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/db_adapter/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ cc_library(
name = "db_adapter_lib",
includes = ["."],
deps = [
":context_loader",
":data_mapper",
":data_types",
":db_wrapper",
"//commons:commons_lib",
"//commons/atoms:atoms_lib",
"//db_adapter/postgres:postgres_lib",
],
)
Expand All @@ -19,6 +22,7 @@ cc_library(
hdrs = ["DataMapper.h"],
includes = ["."],
deps = [
":data_types",
"//commons:commons_lib",
"//commons/atoms:atoms_lib",
],
Expand All @@ -44,3 +48,15 @@ cc_library(
"//commons/atoms:atoms_lib",
],
)

cc_library(
name = "context_loader",
srcs = ["ContextLoader.cc"],
hdrs = ["ContextLoader.h"],
includes = ["."],
deps = [
":data_types",
"//commons:commons_lib",
"@nlohmann_json//:json",
],
)
122 changes: 122 additions & 0 deletions src/db_adapter/ContextLoader.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "ContextLoader.h"

#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>

#include "Logger.h"
#include "Utils.h"

using namespace std;
using namespace commons;

using json = nlohmann::json;

namespace fs = std::filesystem;

vector<TableMapping> ContextLoader::load_context_file(const string& file_path) {
if (!fs::exists(file_path)) {
Utils::error("Context file " + file_path + " does not exist");
}

ifstream f(file_path);

json tables = json::parse(f);

vector<TableMapping> out;

bool has_error = false;

for (size_t i = 0; i < tables.size(); ++i) {
string msg_base = "table[" + to_string(i) + "]";

const json& table = tables[i];

TableMapping tm;

if (!table.contains("table_name")) {
LOG_ERROR(msg_base + " missing required key: 'table_name'");
has_error = true;
} else if (!table["table_name"].is_string()) {
LOG_ERROR(msg_base + ".table_name must be a string in a table entry");
has_error = true;
} else {
string tn = table["table_name"].get<string>();
size_t count_dot = 0;
for (char c : tn) {
if (c == '.') ++count_dot;
}
if (count_dot != 1) {
LOG_ERROR(msg_base + ".table_name must be in format 'schema.table'");
has_error = true;
} else {
size_t pos = tn.find('.');
if (pos == 0 || pos + 1 >= tn.size()) {
LOG_ERROR(msg_base + "table_name must be in format 'schema.table'");
has_error = true;
}
}
}

if (!table.contains("skip_columns")) {
LOG_ERROR(msg_base + " missing required key: 'skip_columns'");
has_error = true;
} else {
const json& sc = table["skip_columns"];
if (!sc.is_null()) {
if (!sc.is_array()) {
LOG_ERROR(msg_base +
".skip_columns must be an array of strings or null in a table entry");
has_error = true;
} else {
tm.skip_columns.emplace();
for (size_t k = 0; k < sc.size(); ++k) {
if (!sc[k].is_string()) {
LOG_ERROR(msg_base + ".skip_columns[" + to_string(k) +
"] must be a string in a table entry");
has_error = true;
} else {
tm.skip_columns->push_back(sc[k]);
}
}
}
}
}

if (!table.contains("where_clauses")) {
LOG_ERROR(msg_base + " missing required key: 'where_clauses'");
has_error = true;
} else {
const json& wc = table["where_clauses"];
if (!wc.is_null()) {
if (!wc.is_array()) {
LOG_ERROR(msg_base +
".where_clauses must be an array of strings or null in a table entry");
has_error = true;
} else {
tm.where_clauses.emplace();
for (size_t k = 0; k < wc.size(); ++k) {
if (!wc[k].is_string()) {
LOG_ERROR(msg_base + ".where_clauses[" + to_string(k) +
"] must be a string in a table entry");
has_error = true;
} else {
tm.where_clauses->push_back(wc[k]);
}
}
}
}
}

if (!has_error) {
tm.table_name = table["table_name"];
out.push_back(tm);
}
}

if (has_error) {
LOG_ERROR("Context file validation failed with errors. Please fix the issues and try again.");
return vector<TableMapping>{};
}
return out;
}
12 changes: 12 additions & 0 deletions src/db_adapter/ContextLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <string>
#include <vector>

#include "DataTypes.h"

using namespace std;
using namespace db_adapter;

class ContextLoader {
public:
static vector<TableMapping> load_context_file(const string& file_path);
};
6 changes: 6 additions & 0 deletions src/db_adapter/DataTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,10 @@ struct Table {
*/
enum class MAPPER_TYPE { SQL2METTA, SQL2ATOMS };

struct TableMapping {
string table_name;
optional<vector<string>> where_clauses = nullopt;
optional<vector<string>> skip_columns = nullopt;
};

} // namespace db_adapter
83 changes: 80 additions & 3 deletions src/tests/cpp/db_adapter_test.cc
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#include <gtest/gtest.h>

#include <atomic>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>

#include "Atom.h"
#include "ContextLoader.h"
#include "DataTypes.h"
#include "Logger.h"
#include "Node.h"
#include "PostgresWrapper.h"
#include "TestConfig.h"

Expand Down Expand Up @@ -53,14 +55,61 @@ class PostgresWrapperTest : public ::testing::Test {
int TOTAL_ROWS_CVTERMS = 10;
int TOTAL_ROWS_FEATURES = 26;

void SetUp() override {}
void SetUp() override {
temp_file_path_1 = "/tmp/context_1.json";
temp_file_path_2 = "/tmp/context_2.json";

ofstream file_1(temp_file_path_1);
file_1 << R"([
{
"table_name": "public.organism",
"skip_columns": [],
"where_clauses": ["organism_id = 1"]
},
{
"table_name": "public.feature",
"skip_columns": [],
"where_clauses": ["feature_id = 1"]
},
{
"table_name": "public.cvterm",
"skip_columns": [],
"where_clauses": ["cvterm_id = 1"]
}])";
file_1.close();

ofstream file_2(temp_file_path_2);
file_2 << R"([
{
"table_name": "public.organism",
"skip_columns": [2, "genus"],
"where_clauses": ["organism_id = 1"]
},
{
"table_name": "feature",
"skip_columns": [],
"where_clauses": ["feature_id = 1"]
},
{
"table_name": "public.cvterm",
"skip_columns": [],
"where_clauses": ["cvterm_id = 1"]
}])";
file_2.close();
}

void TearDown() override {}
void TearDown() override {
std::remove(temp_file_path_1.c_str());
std::remove(temp_file_path_2.c_str());
}

shared_ptr<PostgresWrapper> create_wrapper(MAPPER_TYPE mapper_type = MAPPER_TYPE::SQL2ATOMS) {
return make_shared<PostgresWrapper>(
TEST_HOST, TEST_PORT, TEST_DB, TEST_USER, TEST_PASSWORD, mapper_type);
}

string temp_file_path_1;
string temp_file_path_2;
};

TEST_F(PostgresWrapperTest, Connection) {
Expand Down Expand Up @@ -611,6 +660,34 @@ TEST_F(PostgresWrapperTest, MapSqlQueryWithInvalidClauseMetta) {
EXPECT_EQ(wrapper->mapper_handle_trie_size(), 0);
}

TEST_F(PostgresWrapperTest, MapTablesFirstRowAtomsWithContextFile) {
vector<TableMapping> tables_mapping = ContextLoader::load_context_file("/tmp/context_1.json");

EXPECT_FALSE(tables_mapping.empty());

auto wrapper = create_wrapper();

vector<unsigned int> atoms_sizes;

for (const auto& tm : tables_mapping) {
string table_name = tm.table_name;
vector<string> skip_columns = tm.skip_columns.value_or(vector<string>{});
vector<string> where_clauses = tm.where_clauses.value_or(vector<string>{});

Table table = wrapper->get_table(table_name);
EXPECT_NO_THROW({ wrapper->map_table(table, where_clauses, skip_columns, false); });
atoms_sizes.push_back(wrapper->mapper_handle_trie_size());
}
EXPECT_EQ(atoms_sizes.size(), 3);
EXPECT_EQ(atoms_sizes[0], 34);
EXPECT_EQ(atoms_sizes[1], 81);
EXPECT_EQ(atoms_sizes[2], 101);

vector<TableMapping> tables_mapping_2 = ContextLoader::load_context_file("/tmp/context_2.json");

EXPECT_TRUE(tables_mapping_2.empty());
}

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new PostgresWrapperTestEnvironment);
Expand Down