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.
Files changed (27) hide show
  1. package/lbug-source/CMakeLists.txt +1 -1
  2. package/lbug-source/scripts/antlr4/Cypher.g4 +2 -2
  3. package/lbug-source/scripts/antlr4/hash.md5 +1 -1
  4. package/lbug-source/src/antlr4/Cypher.g4 +2 -2
  5. package/lbug-source/src/binder/bind/bind_ddl.cpp +75 -3
  6. package/lbug-source/src/catalog/catalog.cpp +2 -1
  7. package/lbug-source/src/catalog/catalog_entry/rel_group_catalog_entry.cpp +39 -7
  8. package/lbug-source/src/function/table/show_connection.cpp +6 -1
  9. package/lbug-source/src/function/table/show_tables.cpp +10 -2
  10. package/lbug-source/src/include/binder/ddl/bound_create_table_info.h +16 -3
  11. package/lbug-source/src/include/catalog/catalog_entry/rel_group_catalog_entry.h +19 -2
  12. package/lbug-source/src/include/common/types/types.h +1 -0
  13. package/lbug-source/src/include/storage/table/foreign_rel_table.h +56 -0
  14. package/lbug-source/src/parser/transform/transform_expression.cpp +1 -1
  15. package/lbug-source/src/parser/transformer.cpp +7 -1
  16. package/lbug-source/src/processor/operator/scan/scan_rel_table.cpp +7 -1
  17. package/lbug-source/src/storage/storage_manager.cpp +7 -1
  18. package/lbug-source/src/storage/table/CMakeLists.txt +1 -0
  19. package/lbug-source/src/storage/table/foreign_rel_table.cpp +63 -0
  20. package/lbug-source/third_party/antlr4_cypher/cypher_parser.cpp +504 -483
  21. package/lbug-source/third_party/antlr4_cypher/include/cypher_parser.h +3 -2
  22. package/lbug-source/tools/nodejs_api/package.json +4 -2
  23. package/package.json +9 -2
  24. package/prebuilt/lbugjs-darwin-arm64.node +0 -0
  25. package/prebuilt/lbugjs-linux-arm64.node +0 -0
  26. package/prebuilt/lbugjs-linux-x64.node +0 -0
  27. package/prebuilt/lbugjs-win32-x64.node +0 -0
@@ -1,6 +1,6 @@
1
1
  cmake_minimum_required(VERSION 3.15)
2
2
 
3
- project(Lbug VERSION 0.12.3.29 LANGUAGES CXX C)
3
+ project(Lbug VERSION 0.12.3.30 LANGUAGES CXX C)
4
4
 
5
5
  option(SINGLE_THREADED "Single-threaded mode" FALSE)
6
6
  if(SINGLE_THREADED)
@@ -896,7 +896,7 @@ oC_PropertyExpression
896
896
  : oC_Atom SP? oC_PropertyLookup ;
897
897
 
898
898
  oC_PropertyKeyName
899
- : oC_SchemaName ;
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
- d606604ea3991978c8b514d4ac36b8f6
1
+ 3f763bc647d64c15286c2616518546df
@@ -649,7 +649,7 @@ oC_PropertyExpression
649
649
  : oC_Atom SP? oC_PropertyLookup ;
650
650
 
651
651
  oC_PropertyKeyName
652
- : oC_SchemaName ;
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
- NodeTableIDPair pair{srcEntry->getTableID(), dstEntry->getTableID()};
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
- result.insert(info.nodePair.srcTableID);
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
- result.insert(info.nodePair.dstTableID);
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
- auto srcTableName = catalog->getTableCatalogEntry(transaction, pair.srcTableID)->getName();
134
- auto dstTableName = catalog->getTableCatalogEntry(transaction, pair.dstTableID)->getName();
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(", {}", getFromToStr(relTableInfos[i].nodePair, catalog, transaction));
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>()->getRelEntryInfos()) {
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()), LOCAL_DB_NAME,
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 transformSchemaName(*ctx.oC_SchemaName());
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
- return transformSymbolicName(*ctx.oC_SymbolicName());
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 (!entry->getStorage().empty()) {
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);
@@ -13,6 +13,7 @@ add_library(lbug_storage_store
13
13
  compression_flush_buffer.cpp
14
14
  dictionary_chunk.cpp
15
15
  dictionary_column.cpp
16
+ foreign_rel_table.cpp
16
17
  in_mem_chunked_node_group_collection.cpp
17
18
  in_memory_exception_chunk.cpp
18
19
  lazy_segment_scanner.cpp
@@ -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