#include "main/schema_graph.h"
#include <map>
#include <tuple>
#include "catalog/catalog.h"
#include "catalog/catalog_entry/rel_group_catalog_entry.h"
#include "catalog/schema_graph.h"
#include "common/constants.h"
#include "common/enums/rel_multiplicity.h"
#include "common/string_utils.h"
#include "main/client_context.h"
#include "main/query_result.h"
#include "processor/result/flat_tuple.h"
#include "transaction/transaction.h"
#include "transaction/transaction_context.h"
namespace lbug {
namespace main {
bool QueryReferencesSchemaGraph(std::string_view query) {
if (common::StringUtils::getUpper(query).find("MATCH") == std::string::npos) {
return false;
}
return query.find(catalog::SCHEMA_TABLE_NAME) != std::string_view::npos ||
query.find(catalog::SCHEMA_REL_NAME) != std::string_view::npos;
}
static std::string escapeCypherStringLiteral(const std::string& s) {
std::string out;
out.reserve(s.size() + 2);
for (char c : s) {
if (c == '\'') {
out += "\\'";
} else if (c == '\\') {
out += "\\\\";
} else {
out += c;
}
}
return out;
}
static bool execIgnoreErrors(ClientContext* context, const std::string& query) {
auto result = context->query(query);
return result && result->isSuccess();
}
void EnsureSchemaGraphFresh(ClientContext* context) {
if (context == nullptr) {
return;
}
try {
if (!execIgnoreErrors(context,
"CREATE NODE TABLE IF NOT EXISTS schema_table(name STRING, id UINT64, "
"type STRING, PRIMARY KEY(name))")) {
return;
}
if (!execIgnoreErrors(context,
"CREATE REL TABLE IF NOT EXISTS schema_rel(FROM schema_table TO schema_table, "
"name STRING, id UINT64, multiplicity STRING, MANY_MANY)")) {
return;
}
execIgnoreErrors(context,
"ALTER TABLE schema_rel ADD multiplicity STRING DEFAULT 'MANY_MANY'");
std::map<std::tuple<std::string, std::string, std::string>, std::string> multMap;
try {
auto* txnCtx = transaction::TransactionContext::Get(*context);
bool startedTxn = false;
if (transaction::Transaction::Get(*context) == nullptr) {
txnCtx->beginReadTransaction();
startedTxn = true;
}
try {
auto* txn = transaction::Transaction::Get(*context);
auto* catalog = catalog::Catalog::Get(*context);
for (auto* relEntry : catalog->getRelGroupEntries(txn, false)) {
if (catalog::isSchemaGraphTableName(relEntry->getName())) {
continue;
}
for (auto& info : relEntry->getRelEntryInfos()) {
auto* srcEntry =
catalog->getTableCatalogEntry(txn, info.nodePair.srcTableID);
auto* dstEntry =
catalog->getTableCatalogEntry(txn, info.nodePair.dstTableID);
if (srcEntry == nullptr || dstEntry == nullptr) {
continue;
}
multMap[{relEntry->getName(), srcEntry->getName(), dstEntry->getName()}] =
common::RelMultiplicityUtils::toString(info.srcMultiplicity) + "_" +
common::RelMultiplicityUtils::toString(info.dstMultiplicity);
}
}
} catch (...) {}
if (startedTxn) {
try {
txnCtx->commit();
} catch (...) {}
}
} catch (...) {}
auto tablesResult = context->query("CALL show_tables() RETURN *");
if (tablesResult == nullptr || !tablesResult->isSuccess()) {
return;
}
struct TableRow {
uint64_t id;
std::string name;
std::string type;
};
std::vector<TableRow> tables;
while (tablesResult->hasNext()) {
auto row = tablesResult->getNext();
auto dbName = row->getValue(3)->toString();
if (dbName != common::LOCAL_DB_NAME) {
continue;
}
auto name = row->getValue(1)->toString();
if (catalog::isSchemaGraphTableName(name)) {
continue;
}
TableRow r;
r.id = row->getValue(0)->getValue<uint64_t>();
r.name = std::move(name);
r.type = row->getValue(2)->toString();
tables.push_back(std::move(r));
}
execIgnoreErrors(context, "MATCH (t:schema_table) DETACH DELETE t");
for (auto& t : tables) {
auto q = "CREATE (:schema_table {name: '" + escapeCypherStringLiteral(t.name) +
"', id: " + std::to_string(t.id) + ", type: '" +
escapeCypherStringLiteral(t.type) + "'})";
execIgnoreErrors(context, q);
}
for (auto& t : tables) {
if (t.type != "NODE" && t.type != "REL") {
continue;
}
if (t.type != "REL") {
continue;
}
auto connQuery =
"CALL show_connection('" + escapeCypherStringLiteral(t.name) + "') RETURN *";
auto connResult = context->query(connQuery);
if (connResult == nullptr || !connResult->isSuccess()) {
continue;
}
while (connResult->hasNext()) {
auto row = connResult->getNext();
auto srcName = row->getValue(0)->toString();
auto dstName = row->getValue(1)->toString();
auto multIt = multMap.find({t.name, srcName, dstName});
auto multiplicity = multIt == multMap.end() ? "MANY_MANY" : multIt->second;
auto q = "MATCH (s:schema_table), (d:schema_table) WHERE s.name = '" +
escapeCypherStringLiteral(srcName) + "' AND d.name = '" +
escapeCypherStringLiteral(dstName) + "' CREATE (s)-[:schema_rel {name: '" +
escapeCypherStringLiteral(t.name) + "', id: " + std::to_string(t.id) +
", multiplicity: '" + multiplicity + "'}]->(d)";
execIgnoreErrors(context, q);
}
}
} catch (...) {
}
}
} }