lbug 0.12.3-dev.29 → 0.12.3-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lbug-source/CMakeLists.txt +1 -1
- package/lbug-source/scripts/antlr4/Cypher.g4 +2 -2
- package/lbug-source/scripts/antlr4/hash.md5 +1 -1
- package/lbug-source/src/antlr4/Cypher.g4 +2 -2
- package/lbug-source/src/binder/bind/bind_ddl.cpp +75 -3
- package/lbug-source/src/catalog/catalog.cpp +2 -1
- package/lbug-source/src/catalog/catalog_entry/rel_group_catalog_entry.cpp +39 -7
- package/lbug-source/src/function/table/show_connection.cpp +6 -1
- package/lbug-source/src/function/table/show_tables.cpp +10 -2
- package/lbug-source/src/include/binder/ddl/bound_create_table_info.h +16 -3
- package/lbug-source/src/include/catalog/catalog_entry/rel_group_catalog_entry.h +19 -2
- package/lbug-source/src/include/common/types/types.h +1 -0
- package/lbug-source/src/include/storage/table/foreign_rel_table.h +56 -0
- package/lbug-source/src/parser/transform/transform_expression.cpp +1 -1
- package/lbug-source/src/parser/transformer.cpp +7 -1
- package/lbug-source/src/processor/operator/scan/scan_rel_table.cpp +7 -1
- package/lbug-source/src/storage/storage_manager.cpp +7 -1
- package/lbug-source/src/storage/table/CMakeLists.txt +1 -0
- package/lbug-source/src/storage/table/foreign_rel_table.cpp +63 -0
- package/lbug-source/third_party/antlr4_cypher/cypher_parser.cpp +504 -483
- package/lbug-source/third_party/antlr4_cypher/include/cypher_parser.h +3 -2
- package/lbug-source/tools/nodejs_api/package.json +4 -2
- package/package.json +9 -2
- package/prebuilt/lbugjs-darwin-arm64.node +0 -0
- package/prebuilt/lbugjs-linux-arm64.node +0 -0
- package/prebuilt/lbugjs-linux-x64.node +0 -0
- package/prebuilt/lbugjs-win32-x64.node +0 -0
|
@@ -896,7 +896,7 @@ oC_PropertyExpression
|
|
|
896
896
|
: oC_Atom SP? oC_PropertyLookup ;
|
|
897
897
|
|
|
898
898
|
oC_PropertyKeyName
|
|
899
|
-
:
|
|
899
|
+
: oC_SymbolicName ;
|
|
900
900
|
|
|
901
901
|
oC_IntegerLiteral
|
|
902
902
|
: DecimalInteger ;
|
|
@@ -956,7 +956,7 @@ RegularDecimalReal
|
|
|
956
956
|
: ( Digit )* '.' ( Digit )+ ;
|
|
957
957
|
|
|
958
958
|
oC_SchemaName
|
|
959
|
-
: oC_SymbolicName ;
|
|
959
|
+
: oC_SymbolicName ( '.' oC_SymbolicName )? ;
|
|
960
960
|
|
|
961
961
|
oC_SymbolicName
|
|
962
962
|
: UnescapedSymbolicName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3f763bc647d64c15286c2616518546df
|
|
@@ -649,7 +649,7 @@ oC_PropertyExpression
|
|
|
649
649
|
: oC_Atom SP? oC_PropertyLookup ;
|
|
650
650
|
|
|
651
651
|
oC_PropertyKeyName
|
|
652
|
-
:
|
|
652
|
+
: oC_SymbolicName ;
|
|
653
653
|
|
|
654
654
|
oC_IntegerLiteral
|
|
655
655
|
: DecimalInteger ;
|
|
@@ -709,7 +709,7 @@ RegularDecimalReal
|
|
|
709
709
|
: ( Digit )* '.' ( Digit )+ ;
|
|
710
710
|
|
|
711
711
|
oC_SchemaName
|
|
712
|
-
: oC_SymbolicName ;
|
|
712
|
+
: oC_SymbolicName ( '.' oC_SymbolicName )? ;
|
|
713
713
|
|
|
714
714
|
oC_SymbolicName
|
|
715
715
|
: UnescapedSymbolicName
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#include <optional>
|
|
2
|
+
|
|
1
3
|
#include "binder/binder.h"
|
|
2
4
|
#include "binder/ddl/bound_alter.h"
|
|
3
5
|
#include "binder/ddl/bound_create_sequence.h"
|
|
@@ -16,7 +18,9 @@
|
|
|
16
18
|
#include "common/types/types.h"
|
|
17
19
|
#include "function/cast/functions/cast_from_string_functions.h"
|
|
18
20
|
#include "function/sequence/sequence_functions.h"
|
|
21
|
+
#include "function/table/table_function.h"
|
|
19
22
|
#include "main/client_context.h"
|
|
23
|
+
#include "main/database_manager.h"
|
|
20
24
|
#include "parser/ddl/alter.h"
|
|
21
25
|
#include "parser/ddl/create_sequence.h"
|
|
22
26
|
#include "parser/ddl/create_table.h"
|
|
@@ -137,7 +141,8 @@ BoundCreateTableInfo Binder::bindCreateTableInfo(const CreateTableInfo* info) {
|
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
void Binder::validateNodeTableType(const TableCatalogEntry* entry) {
|
|
140
|
-
if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY
|
|
144
|
+
if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY &&
|
|
145
|
+
entry->getType() != CatalogEntryType::FOREIGN_TABLE_ENTRY) {
|
|
141
146
|
throw BinderException(stringFormat("{} is not of type NODE.", entry->getName()));
|
|
142
147
|
}
|
|
143
148
|
}
|
|
@@ -203,15 +208,81 @@ BoundCreateTableInfo Binder::bindCreateRelTableGroupInfo(const CreateTableInfo*
|
|
|
203
208
|
auto boundOptions = bindParsingOptions(extraInfo.options);
|
|
204
209
|
auto storageDirection = getStorageDirection(boundOptions);
|
|
205
210
|
auto storage = getStorage(boundOptions);
|
|
211
|
+
std::optional<function::TableFunction> scanFunction = std::nullopt;
|
|
212
|
+
std::optional<std::unique_ptr<function::TableFuncBindData>> scanBindData = std::nullopt;
|
|
213
|
+
if (!storage.empty()) {
|
|
214
|
+
auto dotPos = storage.find('.');
|
|
215
|
+
if (dotPos != std::string::npos) {
|
|
216
|
+
// schema.table format, check if it's a foreign table
|
|
217
|
+
std::string dbName = storage.substr(0, dotPos);
|
|
218
|
+
std::string tableName = storage.substr(dotPos + 1);
|
|
219
|
+
auto transaction = transaction::Transaction::Get(*clientContext);
|
|
220
|
+
if (!dbName.empty()) {
|
|
221
|
+
auto attachedDB =
|
|
222
|
+
main::DatabaseManager::Get(*clientContext)->getAttachedDatabase(dbName);
|
|
223
|
+
if (attachedDB && attachedDB->getCatalog()->containsTable(transaction, tableName,
|
|
224
|
+
clientContext->useInternalCatalogEntry())) {
|
|
225
|
+
auto tableEntry = attachedDB->getCatalog()->getTableCatalogEntry(transaction,
|
|
226
|
+
tableName, clientContext->useInternalCatalogEntry());
|
|
227
|
+
if (tableEntry->getType() == CatalogEntryType::FOREIGN_TABLE_ENTRY) {
|
|
228
|
+
scanFunction = tableEntry->getScanFunction();
|
|
229
|
+
auto boundScanInfo =
|
|
230
|
+
tableEntry->getBoundScanInfo(clientContext, "" /* nodeUniqueName */);
|
|
231
|
+
scanBindData = std::move(boundScanInfo->bindData);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
206
237
|
// Bind from to pairs
|
|
207
238
|
node_table_id_pair_set_t nodePairsSet;
|
|
208
239
|
std::vector<NodeTableIDPair> nodePairs;
|
|
240
|
+
std::string foreignDatabaseName;
|
|
241
|
+
std::string foreignDatabaseType;
|
|
209
242
|
for (auto& [srcTableName, dstTableName] : extraInfo.srcDstTablePairs) {
|
|
210
243
|
auto srcEntry = bindNodeTableEntry(srcTableName);
|
|
211
244
|
validateNodeTableType(srcEntry);
|
|
212
245
|
auto dstEntry = bindNodeTableEntry(dstTableName);
|
|
213
246
|
validateNodeTableType(dstEntry);
|
|
214
|
-
|
|
247
|
+
|
|
248
|
+
// For foreign-backed rel tables, validate that FROM and TO are from same database
|
|
249
|
+
if (scanFunction.has_value()) {
|
|
250
|
+
// Both must be foreign tables
|
|
251
|
+
if (srcEntry->getType() != CatalogEntryType::FOREIGN_TABLE_ENTRY ||
|
|
252
|
+
dstEntry->getType() != CatalogEntryType::FOREIGN_TABLE_ENTRY) {
|
|
253
|
+
throw BinderException("Foreign-backed rel tables require both FROM and TO tables "
|
|
254
|
+
"to be foreign tables.");
|
|
255
|
+
}
|
|
256
|
+
// Extract database names from qualified table names
|
|
257
|
+
auto srcDotPos = srcTableName.find('.');
|
|
258
|
+
auto dstDotPos = dstTableName.find('.');
|
|
259
|
+
if (srcDotPos == std::string::npos || dstDotPos == std::string::npos) {
|
|
260
|
+
throw BinderException(
|
|
261
|
+
"Foreign-backed rel tables require qualified table names (database.table).");
|
|
262
|
+
}
|
|
263
|
+
auto srcDbName = srcTableName.substr(0, srcDotPos);
|
|
264
|
+
auto dstDbName = dstTableName.substr(0, dstDotPos);
|
|
265
|
+
if (srcDbName != dstDbName) {
|
|
266
|
+
throw BinderException(
|
|
267
|
+
stringFormat("Cannot create rel table with FROM and TO tables from different "
|
|
268
|
+
"databases. FROM is from '{}', TO is from '{}'.",
|
|
269
|
+
srcDbName, dstDbName));
|
|
270
|
+
}
|
|
271
|
+
// Get database type for display
|
|
272
|
+
auto attachedDB =
|
|
273
|
+
main::DatabaseManager::Get(*clientContext)->getAttachedDatabase(srcDbName);
|
|
274
|
+
if (attachedDB) {
|
|
275
|
+
foreignDatabaseName = stringFormat("{}({})", srcDbName, attachedDB->getDBType());
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// For foreign-backed rel tables, use FOREIGN_TABLE_ID since we don't reference local node
|
|
280
|
+
// tables
|
|
281
|
+
auto srcTableID =
|
|
282
|
+
scanFunction.has_value() ? common::FOREIGN_TABLE_ID : srcEntry->getTableID();
|
|
283
|
+
auto dstTableID =
|
|
284
|
+
scanFunction.has_value() ? common::FOREIGN_TABLE_ID : dstEntry->getTableID();
|
|
285
|
+
NodeTableIDPair pair{srcTableID, dstTableID};
|
|
215
286
|
if (nodePairsSet.contains(pair)) {
|
|
216
287
|
throw BinderException(
|
|
217
288
|
stringFormat("Found duplicate FROM-TO {}-{} pairs.", srcTableName, dstTableName));
|
|
@@ -221,7 +292,8 @@ BoundCreateTableInfo Binder::bindCreateRelTableGroupInfo(const CreateTableInfo*
|
|
|
221
292
|
}
|
|
222
293
|
auto boundExtraInfo = std::make_unique<BoundExtraCreateRelTableGroupInfo>(
|
|
223
294
|
std::move(propertyDefinitions), srcMultiplicity, dstMultiplicity, storageDirection,
|
|
224
|
-
std::move(nodePairs), std::move(storage))
|
|
295
|
+
std::move(nodePairs), std::move(storage), std::move(scanFunction), std::move(scanBindData),
|
|
296
|
+
std::move(foreignDatabaseName));
|
|
225
297
|
return BoundCreateTableInfo(CatalogEntryType::REL_GROUP_ENTRY, info->tableName,
|
|
226
298
|
info->onConflict, std::move(boundExtraInfo), clientContext->useInternalCatalogEntry());
|
|
227
299
|
}
|
|
@@ -192,7 +192,8 @@ CatalogEntry* Catalog::createRelGroupEntry(Transaction* transaction,
|
|
|
192
192
|
}
|
|
193
193
|
auto relGroupEntry = std::make_unique<RelGroupCatalogEntry>(info.tableName,
|
|
194
194
|
extraInfo->srcMultiplicity, extraInfo->dstMultiplicity, extraInfo->storageDirection,
|
|
195
|
-
std::move(relTableInfos), extraInfo->storage
|
|
195
|
+
std::move(relTableInfos), extraInfo->storage, extraInfo->scanFunction,
|
|
196
|
+
std::move(extraInfo->scanBindData), extraInfo->foreignDatabaseName);
|
|
196
197
|
for (auto& definition : extraInfo->propertyDefinitions) {
|
|
197
198
|
relGroupEntry->addProperty(definition);
|
|
198
199
|
}
|
|
@@ -74,7 +74,10 @@ const RelTableCatalogInfo* RelGroupCatalogEntry::getRelEntryInfo(table_id_t srcT
|
|
|
74
74
|
std::unordered_set<table_id_t> RelGroupCatalogEntry::getSrcNodeTableIDSet() const {
|
|
75
75
|
std::unordered_set<table_id_t> result;
|
|
76
76
|
for (auto& info : relTableInfos) {
|
|
77
|
-
|
|
77
|
+
// Skip FOREIGN_TABLE_ID for foreign-backed rel tables
|
|
78
|
+
if (info.nodePair.srcTableID != FOREIGN_TABLE_ID) {
|
|
79
|
+
result.insert(info.nodePair.srcTableID);
|
|
80
|
+
}
|
|
78
81
|
}
|
|
79
82
|
return result;
|
|
80
83
|
}
|
|
@@ -82,7 +85,10 @@ std::unordered_set<table_id_t> RelGroupCatalogEntry::getSrcNodeTableIDSet() cons
|
|
|
82
85
|
std::unordered_set<table_id_t> RelGroupCatalogEntry::getDstNodeTableIDSet() const {
|
|
83
86
|
std::unordered_set<table_id_t> result;
|
|
84
87
|
for (auto& info : relTableInfos) {
|
|
85
|
-
|
|
88
|
+
// Skip FOREIGN_TABLE_ID for foreign-backed rel tables
|
|
89
|
+
if (info.nodePair.dstTableID != FOREIGN_TABLE_ID) {
|
|
90
|
+
result.insert(info.nodePair.dstTableID);
|
|
91
|
+
}
|
|
86
92
|
}
|
|
87
93
|
return result;
|
|
88
94
|
}
|
|
@@ -97,6 +103,11 @@ void RelGroupCatalogEntry::serialize(Serializer& serializer) const {
|
|
|
97
103
|
serializer.serializeValue(storageDirection);
|
|
98
104
|
serializer.writeDebuggingInfo("storage");
|
|
99
105
|
serializer.serializeValue(storage);
|
|
106
|
+
serializer.writeDebuggingInfo("scanFunction");
|
|
107
|
+
serializer.serializeValue(scanFunction.has_value());
|
|
108
|
+
if (scanFunction.has_value()) {
|
|
109
|
+
// TODO: serialize TableFunction
|
|
110
|
+
}
|
|
100
111
|
serializer.writeDebuggingInfo("relTableInfos");
|
|
101
112
|
serializer.serializeVector(relTableInfos);
|
|
102
113
|
}
|
|
@@ -117,6 +128,13 @@ std::unique_ptr<RelGroupCatalogEntry> RelGroupCatalogEntry::deserialize(
|
|
|
117
128
|
deserializer.deserializeValue(storageDirection);
|
|
118
129
|
deserializer.validateDebuggingInfo(debuggingInfo, "storage");
|
|
119
130
|
deserializer.deserializeValue(storage);
|
|
131
|
+
deserializer.validateDebuggingInfo(debuggingInfo, "scanFunction");
|
|
132
|
+
bool hasScanFunction;
|
|
133
|
+
deserializer.deserializeValue(hasScanFunction);
|
|
134
|
+
std::optional<function::TableFunction> scanFunction = std::nullopt;
|
|
135
|
+
if (hasScanFunction) {
|
|
136
|
+
// TODO: deserialize TableFunction
|
|
137
|
+
}
|
|
120
138
|
deserializer.validateDebuggingInfo(debuggingInfo, "relTableInfos");
|
|
121
139
|
deserializer.deserializeVector(relTableInfos);
|
|
122
140
|
auto relGroupEntry = std::make_unique<RelGroupCatalogEntry>();
|
|
@@ -124,14 +142,24 @@ std::unique_ptr<RelGroupCatalogEntry> RelGroupCatalogEntry::deserialize(
|
|
|
124
142
|
relGroupEntry->dstMultiplicity = dstMultiplicity;
|
|
125
143
|
relGroupEntry->storageDirection = storageDirection;
|
|
126
144
|
relGroupEntry->storage = storage;
|
|
145
|
+
relGroupEntry->scanFunction = scanFunction;
|
|
127
146
|
relGroupEntry->relTableInfos = relTableInfos;
|
|
128
147
|
return relGroupEntry;
|
|
129
148
|
}
|
|
130
149
|
|
|
131
150
|
static std::string getFromToStr(const NodeTableIDPair& pair, const Catalog* catalog,
|
|
132
|
-
const transaction::Transaction* transaction) {
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
const transaction::Transaction* transaction, const std::string& storage) {
|
|
152
|
+
std::string srcTableName, dstTableName;
|
|
153
|
+
// For foreign-backed rel tables, node table IDs are FOREIGN_TABLE_ID
|
|
154
|
+
if (pair.srcTableID == common::FOREIGN_TABLE_ID && !storage.empty()) {
|
|
155
|
+
auto dotPos = storage.find('.');
|
|
156
|
+
srcTableName =
|
|
157
|
+
dotPos != std::string::npos ? storage.substr(0, dotPos) + ".nodes" : "foreign_table";
|
|
158
|
+
dstTableName = srcTableName;
|
|
159
|
+
} else {
|
|
160
|
+
srcTableName = catalog->getTableCatalogEntry(transaction, pair.srcTableID)->getName();
|
|
161
|
+
dstTableName = catalog->getTableCatalogEntry(transaction, pair.dstTableID)->getName();
|
|
162
|
+
}
|
|
135
163
|
return stringFormat("FROM `{}` TO `{}`", srcTableName, dstTableName);
|
|
136
164
|
}
|
|
137
165
|
|
|
@@ -142,9 +170,10 @@ std::string RelGroupCatalogEntry::toCypher(const ToCypherInfo& info) const {
|
|
|
142
170
|
std::stringstream ss;
|
|
143
171
|
ss << stringFormat("CREATE REL TABLE `{}` (", getName());
|
|
144
172
|
KU_ASSERT(!relTableInfos.empty());
|
|
145
|
-
ss << getFromToStr(relTableInfos[0].nodePair, catalog, transaction);
|
|
173
|
+
ss << getFromToStr(relTableInfos[0].nodePair, catalog, transaction, storage);
|
|
146
174
|
for (auto i = 1u; i < relTableInfos.size(); ++i) {
|
|
147
|
-
ss << stringFormat(", {}",
|
|
175
|
+
ss << stringFormat(", {}",
|
|
176
|
+
getFromToStr(relTableInfos[i].nodePair, catalog, transaction, storage));
|
|
148
177
|
}
|
|
149
178
|
ss << ", " << propertyCollection.toCypher() << RelMultiplicityUtils::toString(srcMultiplicity)
|
|
150
179
|
<< "_" << RelMultiplicityUtils::toString(dstMultiplicity) << ");";
|
|
@@ -174,6 +203,9 @@ std::unique_ptr<TableCatalogEntry> RelGroupCatalogEntry::copy() const {
|
|
|
174
203
|
other->dstMultiplicity = dstMultiplicity;
|
|
175
204
|
other->storageDirection = storageDirection;
|
|
176
205
|
other->storage = storage;
|
|
206
|
+
other->scanFunction = scanFunction;
|
|
207
|
+
other->scanBindData = std::nullopt; // TODO: implement copy for bindData if needed
|
|
208
|
+
other->foreignDatabaseName = foreignDatabaseName;
|
|
177
209
|
other->relTableInfos = relTableInfos;
|
|
178
210
|
other->copyFrom(*this);
|
|
179
211
|
return other;
|
|
@@ -71,7 +71,12 @@ static std::unique_ptr<TableFuncBindData> bindFunc(const ClientContext* context,
|
|
|
71
71
|
if (entry->getType() != catalog::CatalogEntryType::REL_GROUP_ENTRY) {
|
|
72
72
|
throw BinderException{"Show connection can only be called on a rel table!"};
|
|
73
73
|
}
|
|
74
|
-
for (auto& info : entry->ptrCast<RelGroupCatalogEntry>()
|
|
74
|
+
for (auto& info : entry->ptrCast<RelGroupCatalogEntry>()
|
|
75
|
+
->getRelEntryInfos()) { // Skip foreign-backed rel tables (they have
|
|
76
|
+
// FOREIGN_TABLE_ID)
|
|
77
|
+
if (info.nodePair.srcTableID == common::FOREIGN_TABLE_ID) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
75
80
|
auto srcEntry = catalog->getTableCatalogEntry(transaction, info.nodePair.srcTableID)
|
|
76
81
|
->ptrCast<NodeTableCatalogEntry>();
|
|
77
82
|
auto dstEntry = catalog->getTableCatalogEntry(transaction, info.nodePair.dstTableID)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "binder/binder.h"
|
|
2
2
|
#include "catalog/catalog.h"
|
|
3
|
+
#include "catalog/catalog_entry/rel_group_catalog_entry.h"
|
|
3
4
|
#include "catalog/catalog_entry/table_catalog_entry.h"
|
|
4
5
|
#include "function/table/bind_data.h"
|
|
5
6
|
#include "function/table/simple_table_function.h"
|
|
@@ -72,9 +73,16 @@ static std::unique_ptr<TableFuncBindData> bindFunc(const main::ClientContext* co
|
|
|
72
73
|
auto catalog = Catalog::Get(*context);
|
|
73
74
|
for (auto& entry :
|
|
74
75
|
catalog->getTableEntries(transaction, context->useInternalCatalogEntry())) {
|
|
76
|
+
std::string dbName = LOCAL_DB_NAME;
|
|
77
|
+
// For foreign-backed rel tables, use the foreign database name
|
|
78
|
+
if (entry->getType() == CatalogEntryType::REL_GROUP_ENTRY) {
|
|
79
|
+
auto relEntry = entry->constPtrCast<RelGroupCatalogEntry>();
|
|
80
|
+
if (!relEntry->getForeignDatabaseName().empty()) {
|
|
81
|
+
dbName = relEntry->getForeignDatabaseName();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
75
84
|
tableInfos.emplace_back(entry->getName(), entry->getTableID(),
|
|
76
|
-
TableTypeUtils::toString(entry->getTableType()),
|
|
77
|
-
entry->getComment());
|
|
85
|
+
TableTypeUtils::toString(entry->getTableType()), dbName, entry->getComment());
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <optional>
|
|
4
|
+
|
|
3
5
|
#include "catalog/catalog_entry/catalog_entry_type.h"
|
|
4
6
|
#include "catalog/catalog_entry/node_table_id_pair.h"
|
|
5
7
|
#include "common/enums/conflict_action.h"
|
|
6
8
|
#include "common/enums/extend_direction.h"
|
|
7
9
|
#include "common/enums/rel_multiplicity.h"
|
|
10
|
+
#include "function/table/bind_data.h"
|
|
11
|
+
#include "function/table/table_function.h"
|
|
8
12
|
#include "property_definition.h"
|
|
9
13
|
|
|
10
14
|
namespace lbug {
|
|
@@ -92,20 +96,29 @@ struct BoundExtraCreateRelTableGroupInfo final : BoundExtraCreateTableInfo {
|
|
|
92
96
|
common::ExtendDirection storageDirection;
|
|
93
97
|
std::vector<catalog::NodeTableIDPair> nodePairs;
|
|
94
98
|
std::string storage;
|
|
99
|
+
std::optional<function::TableFunction> scanFunction;
|
|
100
|
+
std::optional<std::shared_ptr<function::TableFuncBindData>> scanBindData;
|
|
101
|
+
std::string foreignDatabaseName;
|
|
95
102
|
|
|
96
103
|
explicit BoundExtraCreateRelTableGroupInfo(std::vector<PropertyDefinition> definitions,
|
|
97
104
|
common::RelMultiplicity srcMultiplicity, common::RelMultiplicity dstMultiplicity,
|
|
98
105
|
common::ExtendDirection storageDirection, std::vector<catalog::NodeTableIDPair> nodePairs,
|
|
99
|
-
std::string storage = ""
|
|
106
|
+
std::string storage = "",
|
|
107
|
+
std::optional<function::TableFunction> scanFunction = std::nullopt,
|
|
108
|
+
std::optional<std::shared_ptr<function::TableFuncBindData>> scanBindData = std::nullopt,
|
|
109
|
+
std::string foreignDatabaseName = "")
|
|
100
110
|
: BoundExtraCreateTableInfo{std::move(definitions)}, srcMultiplicity{srcMultiplicity},
|
|
101
111
|
dstMultiplicity{dstMultiplicity}, storageDirection{storageDirection},
|
|
102
|
-
nodePairs{std::move(nodePairs)}, storage{std::move(storage)}
|
|
112
|
+
nodePairs{std::move(nodePairs)}, storage{std::move(storage)},
|
|
113
|
+
scanFunction{std::move(scanFunction)}, scanBindData{std::move(scanBindData)},
|
|
114
|
+
foreignDatabaseName{std::move(foreignDatabaseName)} {}
|
|
103
115
|
|
|
104
116
|
BoundExtraCreateRelTableGroupInfo(const BoundExtraCreateRelTableGroupInfo& other)
|
|
105
117
|
: BoundExtraCreateTableInfo{copyVector(other.propertyDefinitions)},
|
|
106
118
|
srcMultiplicity{other.srcMultiplicity}, dstMultiplicity{other.dstMultiplicity},
|
|
107
119
|
storageDirection{other.storageDirection}, nodePairs{other.nodePairs},
|
|
108
|
-
storage{other.storage} {}
|
|
120
|
+
storage{other.storage}, scanFunction{other.scanFunction},
|
|
121
|
+
scanBindData{other.scanBindData}, foreignDatabaseName{other.foreignDatabaseName} {}
|
|
109
122
|
|
|
110
123
|
std::unique_ptr<BoundExtraCreateCatalogEntryInfo> copy() const override {
|
|
111
124
|
return std::make_unique<BoundExtraCreateRelTableGroupInfo>(*this);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <optional>
|
|
4
|
+
|
|
3
5
|
#include "catalog/catalog_entry/table_catalog_entry.h"
|
|
4
6
|
#include "common/enums/extend_direction.h"
|
|
5
7
|
#include "common/enums/rel_direction.h"
|
|
6
8
|
#include "common/enums/rel_multiplicity.h"
|
|
9
|
+
#include "function/table/bind_data.h"
|
|
10
|
+
#include "function/table/table_function.h"
|
|
7
11
|
#include "node_table_id_pair.h"
|
|
8
12
|
|
|
9
13
|
namespace lbug {
|
|
@@ -34,10 +38,15 @@ public:
|
|
|
34
38
|
RelGroupCatalogEntry() = default;
|
|
35
39
|
RelGroupCatalogEntry(std::string tableName, common::RelMultiplicity srcMultiplicity,
|
|
36
40
|
common::RelMultiplicity dstMultiplicity, common::ExtendDirection storageDirection,
|
|
37
|
-
std::vector<RelTableCatalogInfo> relTableInfos, std::string storage = ""
|
|
41
|
+
std::vector<RelTableCatalogInfo> relTableInfos, std::string storage = "",
|
|
42
|
+
std::optional<function::TableFunction> scanFunction = std::nullopt,
|
|
43
|
+
std::optional<std::shared_ptr<function::TableFuncBindData>> scanBindData = std::nullopt,
|
|
44
|
+
std::string foreignDatabaseName = "")
|
|
38
45
|
: TableCatalogEntry{type_, std::move(tableName)}, srcMultiplicity{srcMultiplicity},
|
|
39
46
|
dstMultiplicity{dstMultiplicity}, storageDirection{storageDirection},
|
|
40
|
-
relTableInfos{std::move(relTableInfos)}, storage{std::move(storage)}
|
|
47
|
+
relTableInfos{std::move(relTableInfos)}, storage{std::move(storage)},
|
|
48
|
+
scanFunction{std::move(scanFunction)}, scanBindData{std::move(scanBindData)},
|
|
49
|
+
foreignDatabaseName{std::move(foreignDatabaseName)} {
|
|
41
50
|
propertyCollection =
|
|
42
51
|
PropertyDefinitionCollection{1}; // Skip NBR_NODE_ID column as the first one.
|
|
43
52
|
}
|
|
@@ -54,6 +63,11 @@ public:
|
|
|
54
63
|
|
|
55
64
|
common::ExtendDirection getStorageDirection() const { return storageDirection; }
|
|
56
65
|
const std::string& getStorage() const { return storage; }
|
|
66
|
+
const std::optional<function::TableFunction>& getScanFunction() const { return scanFunction; }
|
|
67
|
+
const std::optional<std::shared_ptr<function::TableFuncBindData>>& getScanBindData() const {
|
|
68
|
+
return scanBindData;
|
|
69
|
+
}
|
|
70
|
+
const std::string& getForeignDatabaseName() const { return foreignDatabaseName; }
|
|
57
71
|
|
|
58
72
|
common::idx_t getNumRelTables() const { return relTableInfos.size(); }
|
|
59
73
|
const std::vector<RelTableCatalogInfo>& getRelEntryInfos() const { return relTableInfos; }
|
|
@@ -99,6 +113,9 @@ private:
|
|
|
99
113
|
common::ExtendDirection storageDirection = common::ExtendDirection::BOTH;
|
|
100
114
|
std::vector<RelTableCatalogInfo> relTableInfos;
|
|
101
115
|
std::string storage;
|
|
116
|
+
std::optional<function::TableFunction> scanFunction;
|
|
117
|
+
std::optional<std::shared_ptr<function::TableFuncBindData>> scanBindData;
|
|
118
|
+
std::string foreignDatabaseName; // Database name for foreign-backed rel tables
|
|
102
119
|
};
|
|
103
120
|
|
|
104
121
|
} // namespace catalog
|
|
@@ -75,6 +75,7 @@ using table_id_set_t = std::unordered_set<table_id_t>;
|
|
|
75
75
|
template<typename T>
|
|
76
76
|
using table_id_map_t = std::unordered_map<table_id_t, T>;
|
|
77
77
|
constexpr table_id_t INVALID_TABLE_ID = INVALID_OID;
|
|
78
|
+
constexpr table_id_t FOREIGN_TABLE_ID = INVALID_OID - 1;
|
|
78
79
|
// offset type alias
|
|
79
80
|
using offset_t = uint64_t;
|
|
80
81
|
constexpr offset_t INVALID_OFFSET = UINT64_MAX;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "catalog/catalog_entry/rel_group_catalog_entry.h"
|
|
4
|
+
#include "common/exception/runtime.h"
|
|
5
|
+
#include "function/table/table_function.h"
|
|
6
|
+
#include "storage/table/rel_table.h"
|
|
7
|
+
|
|
8
|
+
namespace lbug {
|
|
9
|
+
namespace storage {
|
|
10
|
+
|
|
11
|
+
struct ForeignRelTableScanState final : RelTableScanState {
|
|
12
|
+
std::shared_ptr<function::TableFuncSharedState> sharedState;
|
|
13
|
+
std::shared_ptr<function::TableFuncLocalState> localState;
|
|
14
|
+
common::DataChunk dataChunk;
|
|
15
|
+
|
|
16
|
+
ForeignRelTableScanState(MemoryManager& mm, common::ValueVector* nodeIDVector,
|
|
17
|
+
std::vector<common::ValueVector*> outputVectors,
|
|
18
|
+
std::shared_ptr<common::DataChunkState> outChunkState);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
class ForeignRelTable final : public RelTable {
|
|
22
|
+
public:
|
|
23
|
+
ForeignRelTable(catalog::RelGroupCatalogEntry* relGroupEntry, common::table_id_t fromTableID,
|
|
24
|
+
common::table_id_t toTableID, const StorageManager* storageManager,
|
|
25
|
+
MemoryManager* memoryManager, function::TableFunction scanFunction,
|
|
26
|
+
std::shared_ptr<function::TableFuncBindData> scanBindData);
|
|
27
|
+
|
|
28
|
+
void initScanState(transaction::Transaction* transaction, TableScanState& scanState,
|
|
29
|
+
bool resetCachedBoundNodeSelVec = true) const override;
|
|
30
|
+
|
|
31
|
+
bool scanInternal(transaction::Transaction* transaction, TableScanState& scanState) override;
|
|
32
|
+
|
|
33
|
+
// For foreign-backed tables, we don't support modifications
|
|
34
|
+
void insert([[maybe_unused]] transaction::Transaction* transaction,
|
|
35
|
+
[[maybe_unused]] TableInsertState& insertState) override {
|
|
36
|
+
throw common::RuntimeException("Cannot insert into foreign-backed rel table");
|
|
37
|
+
}
|
|
38
|
+
void update([[maybe_unused]] transaction::Transaction* transaction,
|
|
39
|
+
[[maybe_unused]] TableUpdateState& updateState) override {
|
|
40
|
+
throw common::RuntimeException("Cannot update foreign-backed rel table");
|
|
41
|
+
}
|
|
42
|
+
bool delete_([[maybe_unused]] transaction::Transaction* transaction,
|
|
43
|
+
[[maybe_unused]] TableDeleteState& deleteState) override {
|
|
44
|
+
throw common::RuntimeException("Cannot delete from foreign-backed rel table");
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
common::row_idx_t getNumTotalRows(const transaction::Transaction* transaction) override;
|
|
49
|
+
|
|
50
|
+
private:
|
|
51
|
+
function::TableFunction scanFunction;
|
|
52
|
+
std::shared_ptr<function::TableFuncBindData> scanBindData;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
} // namespace storage
|
|
56
|
+
} // namespace lbug
|
|
@@ -663,7 +663,7 @@ std::unique_ptr<ParsedExpression> Transformer::transformProperty(
|
|
|
663
663
|
}
|
|
664
664
|
|
|
665
665
|
std::string Transformer::transformPropertyKeyName(CypherParser::OC_PropertyKeyNameContext& ctx) {
|
|
666
|
-
return
|
|
666
|
+
return transformSymbolicName(*ctx.oC_SymbolicName());
|
|
667
667
|
}
|
|
668
668
|
|
|
669
669
|
std::unique_ptr<ParsedExpression> Transformer::transformIntegerLiteral(
|
|
@@ -88,7 +88,13 @@ std::unique_ptr<ParsedExpression> Transformer::transformWhere(CypherParser::OC_W
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
std::string Transformer::transformSchemaName(CypherParser::OC_SchemaNameContext& ctx) {
|
|
91
|
-
|
|
91
|
+
auto symbolicNames = ctx.oC_SymbolicName();
|
|
92
|
+
if (symbolicNames.size() == 1) {
|
|
93
|
+
return transformSymbolicName(*symbolicNames[0]);
|
|
94
|
+
}
|
|
95
|
+
// Qualified name: db.table
|
|
96
|
+
return transformSymbolicName(*symbolicNames[0]) + "." +
|
|
97
|
+
transformSymbolicName(*symbolicNames[1]);
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
std::string Transformer::transformStringLiteral(antlr4::tree::TerminalNode& stringLiteral) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include "processor/execution_context.h"
|
|
5
5
|
#include "storage/buffer_manager/memory_manager.h"
|
|
6
6
|
#include "storage/local_storage/local_rel_table.h"
|
|
7
|
+
#include "storage/table/foreign_rel_table.h"
|
|
7
8
|
#include "storage/table/parquet_rel_table.h"
|
|
8
9
|
|
|
9
10
|
using namespace lbug::common;
|
|
@@ -68,12 +69,17 @@ void ScanRelTable::initLocalStateInternal(ResultSet* resultSet, ExecutionContext
|
|
|
68
69
|
auto clientContext = context->clientContext;
|
|
69
70
|
auto boundNodeIDVector = resultSet->getValueVector(opInfo.nodeIDPos).get();
|
|
70
71
|
auto nbrNodeIDVector = outVectors[0];
|
|
71
|
-
// Check if this is a ParquetRelTable and create appropriate scan state
|
|
72
|
+
// Check if this is a ParquetRelTable or ForeignRelTable and create appropriate scan state
|
|
72
73
|
auto* parquetTable = dynamic_cast<storage::ParquetRelTable*>(tableInfo.table);
|
|
74
|
+
auto* foreignTable = dynamic_cast<storage::ForeignRelTable*>(tableInfo.table);
|
|
73
75
|
if (parquetTable) {
|
|
74
76
|
scanState =
|
|
75
77
|
std::make_unique<storage::ParquetRelTableScanState>(*MemoryManager::Get(*clientContext),
|
|
76
78
|
boundNodeIDVector, outVectors, nbrNodeIDVector->state);
|
|
79
|
+
} else if (foreignTable) {
|
|
80
|
+
scanState =
|
|
81
|
+
std::make_unique<storage::ForeignRelTableScanState>(*MemoryManager::Get(*clientContext),
|
|
82
|
+
boundNodeIDVector, outVectors, nbrNodeIDVector->state);
|
|
77
83
|
} else {
|
|
78
84
|
scanState = std::make_unique<RelTableScanState>(*MemoryManager::Get(*clientContext),
|
|
79
85
|
boundNodeIDVector, outVectors, nbrNodeIDVector->state);
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include "storage/buffer_manager/buffer_manager.h"
|
|
13
13
|
#include "storage/buffer_manager/memory_manager.h"
|
|
14
14
|
#include "storage/checkpointer.h"
|
|
15
|
+
#include "storage/table/foreign_rel_table.h"
|
|
15
16
|
#include "storage/table/node_table.h"
|
|
16
17
|
#include "storage/table/parquet_node_table.h"
|
|
17
18
|
#include "storage/table/parquet_rel_table.h"
|
|
@@ -94,7 +95,12 @@ void StorageManager::createNodeTable(NodeTableCatalogEntry* entry) {
|
|
|
94
95
|
// rel table. We may have to refactor the existing StorageManager::createTable(TableCatalogEntry*
|
|
95
96
|
// entry).
|
|
96
97
|
void StorageManager::addRelTable(RelGroupCatalogEntry* entry, const RelTableCatalogInfo& info) {
|
|
97
|
-
if (
|
|
98
|
+
if (entry->getScanFunction().has_value()) {
|
|
99
|
+
// Create foreign-backed rel table
|
|
100
|
+
tables[info.oid] = std::make_unique<ForeignRelTable>(entry, info.nodePair.srcTableID,
|
|
101
|
+
info.nodePair.dstTableID, this, &memoryManager, *entry->getScanFunction(),
|
|
102
|
+
std::move(entry->getScanBindData().value()));
|
|
103
|
+
} else if (!entry->getStorage().empty()) {
|
|
98
104
|
// Create parquet-backed rel table
|
|
99
105
|
tables[info.oid] = std::make_unique<ParquetRelTable>(entry, info.nodePair.srcTableID,
|
|
100
106
|
info.nodePair.dstTableID, this, &memoryManager);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#include "storage/table/foreign_rel_table.h"
|
|
2
|
+
|
|
3
|
+
#include "function/table/table_function.h"
|
|
4
|
+
#include "processor/operator/scan/scan_rel_table.h"
|
|
5
|
+
#include "storage/storage_manager.h"
|
|
6
|
+
#include "transaction/transaction.h"
|
|
7
|
+
|
|
8
|
+
namespace lbug {
|
|
9
|
+
namespace storage {
|
|
10
|
+
|
|
11
|
+
ForeignRelTableScanState::ForeignRelTableScanState(MemoryManager& mm,
|
|
12
|
+
common::ValueVector* nodeIDVector, std::vector<common::ValueVector*> outputVectors,
|
|
13
|
+
std::shared_ptr<common::DataChunkState> outChunkState)
|
|
14
|
+
: RelTableScanState{mm, nodeIDVector, std::move(outputVectors), std::move(outChunkState)} {
|
|
15
|
+
dataChunk.valueVectors.resize(this->outputVectors.size());
|
|
16
|
+
for (size_t i = 0; i < this->outputVectors.size(); ++i) {
|
|
17
|
+
dataChunk.valueVectors[i] = std::shared_ptr<common::ValueVector>(this->outputVectors[i],
|
|
18
|
+
[](common::ValueVector*) {});
|
|
19
|
+
}
|
|
20
|
+
dataChunk.state = this->outState;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ForeignRelTable::ForeignRelTable(catalog::RelGroupCatalogEntry* relGroupEntry,
|
|
24
|
+
common::table_id_t fromTableID, common::table_id_t toTableID,
|
|
25
|
+
const StorageManager* storageManager, MemoryManager* memoryManager,
|
|
26
|
+
function::TableFunction scanFunction, std::shared_ptr<function::TableFuncBindData> scanBindData)
|
|
27
|
+
: RelTable{relGroupEntry, fromTableID, toTableID, storageManager, memoryManager},
|
|
28
|
+
scanFunction{std::move(scanFunction)}, scanBindData{std::move(scanBindData)} {}
|
|
29
|
+
|
|
30
|
+
void ForeignRelTable::initScanState([[maybe_unused]] transaction::Transaction* transaction,
|
|
31
|
+
TableScanState& scanState, [[maybe_unused]] bool resetCachedBoundNodeSelVec) const {
|
|
32
|
+
// For foreign tables, we don't need node group initialization
|
|
33
|
+
// RelTable::initScanState(transaction, scanState, resetCachedBoundNodeSelVec);
|
|
34
|
+
auto& foreignRelScanState = static_cast<ForeignRelTableScanState&>(scanState);
|
|
35
|
+
function::TableFuncInitSharedStateInput sharedInput{scanBindData.get(), nullptr /* context */};
|
|
36
|
+
foreignRelScanState.sharedState = scanFunction.initSharedStateFunc(sharedInput);
|
|
37
|
+
function::TableFuncInitLocalStateInput localInput{*foreignRelScanState.sharedState,
|
|
38
|
+
*scanBindData, nullptr /* clientContext */};
|
|
39
|
+
foreignRelScanState.localState = scanFunction.initLocalStateFunc(localInput);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
bool ForeignRelTable::scanInternal([[maybe_unused]] transaction::Transaction* transaction,
|
|
43
|
+
TableScanState& scanState) {
|
|
44
|
+
auto& foreignRelScanState = static_cast<ForeignRelTableScanState&>(scanState);
|
|
45
|
+
function::TableFuncInput input{scanBindData.get(), foreignRelScanState.localState.get(),
|
|
46
|
+
foreignRelScanState.sharedState.get(), nullptr /* clientContext */};
|
|
47
|
+
common::DataChunk dc;
|
|
48
|
+
dc.valueVectors = foreignRelScanState.dataChunk.valueVectors;
|
|
49
|
+
dc.state = foreignRelScanState.dataChunk.state;
|
|
50
|
+
function::TableFuncOutput output{std::move(dc)};
|
|
51
|
+
auto numTuples = scanFunction.tableFunc(input, output);
|
|
52
|
+
return numTuples > 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
common::row_idx_t ForeignRelTable::getNumTotalRows(
|
|
56
|
+
[[maybe_unused]] const transaction::Transaction* transaction) {
|
|
57
|
+
// For foreign tables, we might need to query the foreign table for row count
|
|
58
|
+
// For now, return 0 or implement proper counting
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} // namespace storage
|
|
63
|
+
} // namespace lbug
|