lbug 0.12.3-dev.8 → 0.13.0

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 (134) hide show
  1. package/lbug-source/.github/workflows/ci-workflow.yml +9 -2
  2. package/lbug-source/CMakeLists.txt +16 -7
  3. package/lbug-source/Makefile +15 -4
  4. package/lbug-source/benchmark/serializer.py +24 -3
  5. package/lbug-source/dataset/demo-db/csv/copy.cypher +4 -4
  6. package/lbug-source/dataset/demo-db/graph-std/demo_indices_follows.parquet +0 -0
  7. package/lbug-source/dataset/demo-db/graph-std/demo_indices_livesin.parquet +0 -0
  8. package/lbug-source/dataset/demo-db/graph-std/demo_indptr_follows.parquet +0 -0
  9. package/lbug-source/dataset/demo-db/graph-std/demo_indptr_livesin.parquet +0 -0
  10. package/lbug-source/dataset/demo-db/graph-std/demo_mapping_city.parquet +0 -0
  11. package/lbug-source/dataset/demo-db/graph-std/demo_mapping_user.parquet +0 -0
  12. package/lbug-source/dataset/demo-db/graph-std/demo_metadata.parquet +0 -0
  13. package/lbug-source/dataset/demo-db/graph-std/demo_nodes_city.parquet +0 -0
  14. package/lbug-source/dataset/demo-db/graph-std/demo_nodes_user.parquet +0 -0
  15. package/lbug-source/dataset/demo-db/graph-std/schema.cypher +4 -0
  16. package/lbug-source/dataset/demo-db/parquet/copy.cypher +4 -4
  17. package/lbug-source/extension/duckdb/src/catalog/duckdb_catalog.cpp +1 -1
  18. package/lbug-source/extension/duckdb/src/catalog/duckdb_table_catalog_entry.cpp +43 -4
  19. package/lbug-source/extension/duckdb/src/connector/duckdb_result_converter.cpp +6 -0
  20. package/lbug-source/extension/duckdb/src/connector/duckdb_secret_manager.cpp +1 -1
  21. package/lbug-source/extension/duckdb/src/function/duckdb_scan.cpp +49 -4
  22. package/lbug-source/extension/duckdb/src/include/catalog/duckdb_table_catalog_entry.h +6 -1
  23. package/lbug-source/extension/duckdb/src/include/function/duckdb_scan.h +2 -0
  24. package/lbug-source/extension/duckdb/test/test_files/duckdb.test +28 -0
  25. package/lbug-source/extension/extension_config.cmake +3 -2
  26. package/lbug-source/extension/httpfs/test/test_files/http.test +1 -0
  27. package/lbug-source/scripts/antlr4/Cypher.g4 +4 -4
  28. package/lbug-source/scripts/antlr4/hash.md5 +1 -1
  29. package/lbug-source/scripts/generate_binary_demo.sh +1 -1
  30. package/lbug-source/src/antlr4/Cypher.g4 +4 -4
  31. package/lbug-source/src/binder/bind/bind_ddl.cpp +97 -15
  32. package/lbug-source/src/binder/bind/bind_graph_pattern.cpp +30 -3
  33. package/lbug-source/src/catalog/catalog.cpp +6 -4
  34. package/lbug-source/src/catalog/catalog_entry/node_table_catalog_entry.cpp +8 -1
  35. package/lbug-source/src/catalog/catalog_entry/rel_group_catalog_entry.cpp +46 -7
  36. package/lbug-source/src/catalog/catalog_set.cpp +1 -0
  37. package/lbug-source/src/function/function_collection.cpp +2 -1
  38. package/lbug-source/src/function/table/CMakeLists.txt +1 -0
  39. package/lbug-source/src/function/table/disk_size_info.cpp +322 -0
  40. package/lbug-source/src/function/table/show_connection.cpp +6 -1
  41. package/lbug-source/src/function/table/show_tables.cpp +10 -2
  42. package/lbug-source/src/function/table/table_function.cpp +11 -2
  43. package/lbug-source/src/include/binder/ddl/bound_create_table_info.h +23 -6
  44. package/lbug-source/src/include/catalog/catalog_entry/node_table_catalog_entry.h +5 -3
  45. package/lbug-source/src/include/catalog/catalog_entry/rel_group_catalog_entry.h +21 -2
  46. package/lbug-source/src/include/catalog/catalog_entry/table_catalog_entry.h +7 -0
  47. package/lbug-source/src/include/common/constants.h +1 -0
  48. package/lbug-source/src/include/common/string_format.h +2 -2
  49. package/lbug-source/src/include/common/types/types.h +1 -0
  50. package/lbug-source/src/include/function/table/bind_data.h +12 -1
  51. package/lbug-source/src/include/function/table/simple_table_function.h +6 -0
  52. package/lbug-source/src/include/function/table/table_function.h +2 -0
  53. package/lbug-source/src/include/optimizer/count_rel_table_optimizer.h +49 -0
  54. package/lbug-source/src/include/optimizer/logical_operator_visitor.h +6 -0
  55. package/lbug-source/src/include/optimizer/order_by_push_down_optimizer.h +21 -0
  56. package/lbug-source/src/include/parser/ddl/create_table_info.h +3 -1
  57. package/lbug-source/src/include/planner/operator/logical_operator.h +1 -0
  58. package/lbug-source/src/include/planner/operator/logical_table_function_call.h +14 -1
  59. package/lbug-source/src/include/planner/operator/scan/logical_count_rel_table.h +84 -0
  60. package/lbug-source/src/include/processor/operator/physical_operator.h +1 -0
  61. package/lbug-source/src/include/processor/operator/scan/count_rel_table.h +62 -0
  62. package/lbug-source/src/include/processor/operator/scan/scan_node_table.h +2 -2
  63. package/lbug-source/src/include/processor/plan_mapper.h +2 -0
  64. package/lbug-source/src/include/storage/storage_manager.h +1 -0
  65. package/lbug-source/src/include/storage/storage_version_info.h +1 -1
  66. package/lbug-source/src/include/storage/table/foreign_rel_table.h +56 -0
  67. package/lbug-source/src/include/storage/table/node_table.h +6 -1
  68. package/lbug-source/src/include/storage/table/parquet_node_table.h +103 -0
  69. package/lbug-source/src/include/storage/table/parquet_rel_table.h +91 -0
  70. package/lbug-source/src/include/storage/table/rel_table.h +2 -2
  71. package/lbug-source/src/include/transaction/transaction.h +2 -0
  72. package/lbug-source/src/optimizer/CMakeLists.txt +3 -1
  73. package/lbug-source/src/optimizer/count_rel_table_optimizer.cpp +217 -0
  74. package/lbug-source/src/optimizer/limit_push_down_optimizer.cpp +12 -0
  75. package/lbug-source/src/optimizer/logical_operator_visitor.cpp +6 -0
  76. package/lbug-source/src/optimizer/optimizer.cpp +10 -0
  77. package/lbug-source/src/optimizer/order_by_push_down_optimizer.cpp +123 -0
  78. package/lbug-source/src/optimizer/projection_push_down_optimizer.cpp +5 -1
  79. package/lbug-source/src/parser/transform/transform_ddl.cpp +6 -1
  80. package/lbug-source/src/parser/transform/transform_expression.cpp +1 -1
  81. package/lbug-source/src/parser/transform/transform_graph_pattern.cpp +6 -1
  82. package/lbug-source/src/parser/transformer.cpp +7 -1
  83. package/lbug-source/src/planner/join_order/cardinality_estimator.cpp +11 -2
  84. package/lbug-source/src/planner/operator/logical_operator.cpp +2 -0
  85. package/lbug-source/src/planner/operator/logical_table_function_call.cpp +4 -0
  86. package/lbug-source/src/planner/operator/scan/CMakeLists.txt +1 -0
  87. package/lbug-source/src/planner/operator/scan/logical_count_rel_table.cpp +24 -0
  88. package/lbug-source/src/planner/plan/plan_join_order.cpp +16 -1
  89. package/lbug-source/src/processor/map/CMakeLists.txt +1 -0
  90. package/lbug-source/src/processor/map/map_count_rel_table.cpp +55 -0
  91. package/lbug-source/src/processor/map/plan_mapper.cpp +3 -0
  92. package/lbug-source/src/processor/operator/index_lookup.cpp +31 -23
  93. package/lbug-source/src/processor/operator/persistent/reader/parquet/parquet_reader.cpp +4 -0
  94. package/lbug-source/src/processor/operator/physical_operator.cpp +2 -0
  95. package/lbug-source/src/processor/operator/scan/CMakeLists.txt +1 -0
  96. package/lbug-source/src/processor/operator/scan/count_rel_table.cpp +137 -0
  97. package/lbug-source/src/processor/operator/scan/scan_multi_rel_tables.cpp +24 -2
  98. package/lbug-source/src/processor/operator/scan/scan_node_table.cpp +44 -8
  99. package/lbug-source/src/processor/operator/scan/scan_rel_table.cpp +18 -2
  100. package/lbug-source/src/storage/storage_manager.cpp +43 -6
  101. package/lbug-source/src/storage/table/CMakeLists.txt +3 -0
  102. package/lbug-source/src/storage/table/foreign_rel_table.cpp +63 -0
  103. package/lbug-source/src/storage/table/parquet_node_table.cpp +338 -0
  104. package/lbug-source/src/storage/table/parquet_rel_table.cpp +388 -0
  105. package/lbug-source/test/common/string_format.cpp +9 -1
  106. package/lbug-source/test/copy/copy_test.cpp +4 -4
  107. package/lbug-source/test/graph_test/CMakeLists.txt +1 -1
  108. package/lbug-source/test/include/test_runner/test_group.h +11 -1
  109. package/lbug-source/test/optimizer/optimizer_test.cpp +46 -0
  110. package/lbug-source/test/runner/e2e_test.cpp +7 -1
  111. package/lbug-source/test/test_files/demo_db/demo_db_graph_std.test +77 -0
  112. package/lbug-source/test/test_helper/CMakeLists.txt +1 -1
  113. package/lbug-source/test/test_helper/test_helper.cpp +33 -1
  114. package/lbug-source/test/test_runner/CMakeLists.txt +1 -1
  115. package/lbug-source/test/test_runner/insert_by_row.cpp +6 -8
  116. package/lbug-source/test/test_runner/multi_copy_split.cpp +2 -4
  117. package/lbug-source/test/test_runner/test_parser.cpp +3 -0
  118. package/lbug-source/test/transaction/checkpoint_test.cpp +1 -1
  119. package/lbug-source/test/transaction/transaction_test.cpp +19 -15
  120. package/lbug-source/third_party/antlr4_cypher/cypher_parser.cpp +2805 -2708
  121. package/lbug-source/third_party/antlr4_cypher/include/cypher_parser.h +7 -3
  122. package/lbug-source/tools/benchmark/count_rel_table.benchmark +5 -0
  123. package/lbug-source/tools/nodejs_api/package.json +4 -2
  124. package/lbug-source/tools/shell/embedded_shell.cpp +78 -3
  125. package/lbug-source/tools/shell/include/embedded_shell.h +2 -0
  126. package/lbug-source/tools/shell/linenoise.cpp +3 -3
  127. package/lbug-source/tools/shell/test/test_helper.py +1 -1
  128. package/lbug-source/tools/shell/test/test_shell_basics.py +12 -0
  129. package/lbug-source/tools/shell/test/test_shell_commands.py +19 -0
  130. package/package.json +9 -2
  131. package/prebuilt/lbugjs-darwin-arm64.node +0 -0
  132. package/prebuilt/lbugjs-linux-arm64.node +0 -0
  133. package/prebuilt/lbugjs-linux-x64.node +0 -0
  134. package/prebuilt/lbugjs-win32-x64.node +0 -0
@@ -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"
@@ -136,18 +140,9 @@ BoundCreateTableInfo Binder::bindCreateTableInfo(const CreateTableInfo* info) {
136
140
  }
137
141
  }
138
142
 
139
- BoundCreateTableInfo Binder::bindCreateNodeTableInfo(const CreateTableInfo* info) {
140
- auto propertyDefinitions = bindPropertyDefinitions(info->propertyDefinitions, info->tableName);
141
- auto& extraInfo = info->extraInfo->constCast<ExtraCreateNodeTableInfo>();
142
- validatePrimaryKey(extraInfo.pKName, propertyDefinitions);
143
- auto boundExtraInfo = std::make_unique<BoundExtraCreateNodeTableInfo>(extraInfo.pKName,
144
- std::move(propertyDefinitions));
145
- return BoundCreateTableInfo(CatalogEntryType::NODE_TABLE_ENTRY, info->tableName,
146
- info->onConflict, std::move(boundExtraInfo), clientContext->useInternalCatalogEntry());
147
- }
148
-
149
143
  void Binder::validateNodeTableType(const TableCatalogEntry* entry) {
150
- if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY) {
144
+ if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY &&
145
+ entry->getType() != CatalogEntryType::FOREIGN_TABLE_ENTRY) {
151
146
  throw BinderException(stringFormat("{} is not of type NODE.", entry->getName()));
152
147
  }
153
148
  }
@@ -168,6 +163,13 @@ void Binder::validateColumnExistence(const TableCatalogEntry* entry,
168
163
  }
169
164
  }
170
165
 
166
+ static std::string getStorage(const case_insensitive_map_t<Value>& options) {
167
+ if (options.contains(TableOptionConstants::REL_STORAGE_OPTION)) {
168
+ return options.at(TableOptionConstants::REL_STORAGE_OPTION).toString();
169
+ }
170
+ return "";
171
+ }
172
+
171
173
  static ExtendDirection getStorageDirection(const case_insensitive_map_t<Value>& options) {
172
174
  if (options.contains(TableOptionConstants::REL_STORAGE_DIRECTION_OPTION)) {
173
175
  return ExtendDirectionUtil::fromString(
@@ -176,6 +178,18 @@ static ExtendDirection getStorageDirection(const case_insensitive_map_t<Value>&
176
178
  return DEFAULT_EXTEND_DIRECTION;
177
179
  }
178
180
 
181
+ BoundCreateTableInfo Binder::bindCreateNodeTableInfo(const CreateTableInfo* info) {
182
+ auto propertyDefinitions = bindPropertyDefinitions(info->propertyDefinitions, info->tableName);
183
+ auto& extraInfo = info->extraInfo->constCast<ExtraCreateNodeTableInfo>();
184
+ validatePrimaryKey(extraInfo.pKName, propertyDefinitions);
185
+ auto boundOptions = bindParsingOptions(extraInfo.options);
186
+ auto storage = getStorage(boundOptions);
187
+ auto boundExtraInfo = std::make_unique<BoundExtraCreateNodeTableInfo>(extraInfo.pKName,
188
+ std::move(propertyDefinitions), std::move(storage));
189
+ return BoundCreateTableInfo(CatalogEntryType::NODE_TABLE_ENTRY, info->tableName,
190
+ info->onConflict, std::move(boundExtraInfo), clientContext->useInternalCatalogEntry());
191
+ }
192
+
179
193
  std::vector<PropertyDefinition> Binder::bindRelPropertyDefinitions(const CreateTableInfo& info) {
180
194
  std::vector<PropertyDefinition> propertyDefinitions;
181
195
  propertyDefinitions.emplace_back(
@@ -193,15 +207,82 @@ BoundCreateTableInfo Binder::bindCreateRelTableGroupInfo(const CreateTableInfo*
193
207
  auto dstMultiplicity = RelMultiplicityUtils::getBwd(extraInfo.relMultiplicity);
194
208
  auto boundOptions = bindParsingOptions(extraInfo.options);
195
209
  auto storageDirection = getStorageDirection(boundOptions);
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
+ }
196
237
  // Bind from to pairs
197
238
  node_table_id_pair_set_t nodePairsSet;
198
239
  std::vector<NodeTableIDPair> nodePairs;
240
+ std::string foreignDatabaseName;
241
+ std::string foreignDatabaseType;
199
242
  for (auto& [srcTableName, dstTableName] : extraInfo.srcDstTablePairs) {
200
243
  auto srcEntry = bindNodeTableEntry(srcTableName);
201
244
  validateNodeTableType(srcEntry);
202
245
  auto dstEntry = bindNodeTableEntry(dstTableName);
203
246
  validateNodeTableType(dstEntry);
204
- 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};
205
286
  if (nodePairsSet.contains(pair)) {
206
287
  throw BinderException(
207
288
  stringFormat("Found duplicate FROM-TO {}-{} pairs.", srcTableName, dstTableName));
@@ -209,9 +290,10 @@ BoundCreateTableInfo Binder::bindCreateRelTableGroupInfo(const CreateTableInfo*
209
290
  nodePairsSet.insert(pair);
210
291
  nodePairs.emplace_back(pair);
211
292
  }
212
- auto boundExtraInfo =
213
- std::make_unique<BoundExtraCreateRelTableGroupInfo>(std::move(propertyDefinitions),
214
- srcMultiplicity, dstMultiplicity, storageDirection, std::move(nodePairs));
293
+ auto boundExtraInfo = std::make_unique<BoundExtraCreateRelTableGroupInfo>(
294
+ std::move(propertyDefinitions), srcMultiplicity, dstMultiplicity, storageDirection,
295
+ std::move(nodePairs), std::move(storage), std::move(scanFunction), std::move(scanBindData),
296
+ std::move(foreignDatabaseName));
215
297
  return BoundCreateTableInfo(CatalogEntryType::REL_GROUP_ENTRY, info->tableName,
216
298
  info->onConflict, std::move(boundExtraInfo), clientContext->useInternalCatalogEntry());
217
299
  }
@@ -15,6 +15,7 @@
15
15
  #include "function/rewrite_function.h"
16
16
  #include "function/schema/vector_node_rel_functions.h"
17
17
  #include "main/client_context.h"
18
+ #include "main/database_manager.h"
18
19
  #include "transaction/transaction.h"
19
20
 
20
21
  using namespace lbug::common;
@@ -644,7 +645,8 @@ std::vector<TableCatalogEntry*> Binder::bindNodeTableEntries(
644
645
  } else {
645
646
  for (auto& name : tableNames) {
646
647
  auto entry = bindNodeTableEntry(name);
647
- if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY) {
648
+ if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY &&
649
+ entry->getType() != CatalogEntryType::FOREIGN_TABLE_ENTRY) {
648
650
  throw BinderException(
649
651
  stringFormat("Cannot bind {} as a node pattern label.", entry->getName()));
650
652
  }
@@ -658,10 +660,35 @@ TableCatalogEntry* Binder::bindNodeTableEntry(const std::string& name) const {
658
660
  auto transaction = transaction::Transaction::Get(*clientContext);
659
661
  auto catalog = Catalog::Get(*clientContext);
660
662
  auto useInternal = clientContext->useInternalCatalogEntry();
661
- if (!catalog->containsTable(transaction, name, useInternal)) {
663
+
664
+ std::string dbName;
665
+ std::string tableName = name;
666
+ auto dotPos = name.find('.');
667
+ if (dotPos != std::string::npos) {
668
+ dbName = name.substr(0, dotPos);
669
+ tableName = name.substr(dotPos + 1);
670
+ }
671
+
672
+ if (!dbName.empty()) {
673
+ // Qualified name: db.table
674
+ auto attachedDB = main::DatabaseManager::Get(*clientContext)->getAttachedDatabase(dbName);
675
+ if (!attachedDB) {
676
+ throw BinderException(stringFormat("Attached database {} does not exist.", dbName));
677
+ }
678
+ auto attachedCatalog = attachedDB->getCatalog();
679
+ if (!attachedCatalog->containsTable(transaction, tableName, useInternal)) {
680
+ throw BinderException(stringFormat("Table {} does not exist in attached database {}.",
681
+ tableName, dbName));
682
+ }
683
+ return attachedCatalog->getTableCatalogEntry(transaction, tableName, useInternal);
684
+ } else {
685
+ // Unqualified name: only search main catalog
686
+ // Foreign tables require qualified names (db.table) to avoid ambiguity
687
+ if (catalog->containsTable(transaction, name, useInternal)) {
688
+ return catalog->getTableCatalogEntry(transaction, name, useInternal);
689
+ }
662
690
  throw BinderException(stringFormat("Table {} does not exist.", name));
663
691
  }
664
- return catalog->getTableCatalogEntry(transaction, name, useInternal);
665
692
  }
666
693
 
667
694
  std::vector<TableCatalogEntry*> Binder::bindRelGroupEntries(
@@ -190,9 +190,10 @@ CatalogEntry* Catalog::createRelGroupEntry(Transaction* transaction,
190
190
  for (auto& nodePair : extraInfo->nodePairs) {
191
191
  relTableInfos.emplace_back(nodePair, tables->getNextOID());
192
192
  }
193
- auto relGroupEntry =
194
- std::make_unique<RelGroupCatalogEntry>(info.tableName, extraInfo->srcMultiplicity,
195
- extraInfo->dstMultiplicity, extraInfo->storageDirection, std::move(relTableInfos));
193
+ auto relGroupEntry = std::make_unique<RelGroupCatalogEntry>(info.tableName,
194
+ extraInfo->srcMultiplicity, extraInfo->dstMultiplicity, extraInfo->storageDirection,
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
  }
@@ -541,7 +542,8 @@ CatalogEntry* Catalog::createTableEntry(Transaction* transaction,
541
542
  CatalogEntry* Catalog::createNodeTableEntry(Transaction* transaction,
542
543
  const BoundCreateTableInfo& info) {
543
544
  const auto extraInfo = info.extraInfo->constPtrCast<BoundExtraCreateNodeTableInfo>();
544
- auto entry = std::make_unique<NodeTableCatalogEntry>(info.tableName, extraInfo->primaryKeyName);
545
+ auto entry = std::make_unique<NodeTableCatalogEntry>(info.tableName, extraInfo->primaryKeyName,
546
+ extraInfo->storage);
545
547
  for (auto& definition : extraInfo->propertyDefinitions) {
546
548
  entry->addProperty(definition);
547
549
  }
@@ -21,16 +21,22 @@ void NodeTableCatalogEntry::serialize(common::Serializer& serializer) const {
21
21
  TableCatalogEntry::serialize(serializer);
22
22
  serializer.writeDebuggingInfo("primaryKeyName");
23
23
  serializer.write(primaryKeyName);
24
+ serializer.writeDebuggingInfo("storage");
25
+ serializer.write(storage);
24
26
  }
25
27
 
26
28
  std::unique_ptr<NodeTableCatalogEntry> NodeTableCatalogEntry::deserialize(
27
29
  common::Deserializer& deserializer) {
28
30
  std::string debuggingInfo;
29
31
  std::string primaryKeyName;
32
+ std::string storage;
30
33
  deserializer.validateDebuggingInfo(debuggingInfo, "primaryKeyName");
31
34
  deserializer.deserializeValue(primaryKeyName);
35
+ deserializer.validateDebuggingInfo(debuggingInfo, "storage");
36
+ deserializer.deserializeValue(storage);
32
37
  auto nodeTableEntry = std::make_unique<NodeTableCatalogEntry>();
33
38
  nodeTableEntry->primaryKeyName = primaryKeyName;
39
+ nodeTableEntry->storage = storage;
34
40
  return nodeTableEntry;
35
41
  }
36
42
 
@@ -42,6 +48,7 @@ std::string NodeTableCatalogEntry::toCypher(const ToCypherInfo& /*info*/) const
42
48
  std::unique_ptr<TableCatalogEntry> NodeTableCatalogEntry::copy() const {
43
49
  auto other = std::make_unique<NodeTableCatalogEntry>();
44
50
  other->primaryKeyName = primaryKeyName;
51
+ other->storage = storage;
45
52
  other->copyFrom(*this);
46
53
  return other;
47
54
  }
@@ -49,7 +56,7 @@ std::unique_ptr<TableCatalogEntry> NodeTableCatalogEntry::copy() const {
49
56
  std::unique_ptr<BoundExtraCreateCatalogEntryInfo> NodeTableCatalogEntry::getBoundExtraCreateInfo(
50
57
  transaction::Transaction*) const {
51
58
  return std::make_unique<BoundExtraCreateNodeTableInfo>(primaryKeyName,
52
- copyVector(getProperties()));
59
+ copyVector(getProperties()), storage);
53
60
  }
54
61
 
55
62
  } // namespace catalog
@@ -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
  }
@@ -95,6 +101,13 @@ void RelGroupCatalogEntry::serialize(Serializer& serializer) const {
95
101
  serializer.serializeValue(dstMultiplicity);
96
102
  serializer.writeDebuggingInfo("storageDirection");
97
103
  serializer.serializeValue(storageDirection);
104
+ serializer.writeDebuggingInfo("storage");
105
+ serializer.serializeValue(storage);
106
+ serializer.writeDebuggingInfo("scanFunction");
107
+ serializer.serializeValue(scanFunction.has_value());
108
+ if (scanFunction.has_value()) {
109
+ // TODO: serialize TableFunction
110
+ }
98
111
  serializer.writeDebuggingInfo("relTableInfos");
99
112
  serializer.serializeVector(relTableInfos);
100
113
  }
@@ -105,6 +118,7 @@ std::unique_ptr<RelGroupCatalogEntry> RelGroupCatalogEntry::deserialize(
105
118
  auto srcMultiplicity = RelMultiplicity::MANY;
106
119
  auto dstMultiplicity = RelMultiplicity::MANY;
107
120
  auto storageDirection = ExtendDirection::BOTH;
121
+ std::string storage;
108
122
  std::vector<RelTableCatalogInfo> relTableInfos;
109
123
  deserializer.validateDebuggingInfo(debuggingInfo, "srcMultiplicity");
110
124
  deserializer.deserializeValue(srcMultiplicity);
@@ -112,20 +126,40 @@ std::unique_ptr<RelGroupCatalogEntry> RelGroupCatalogEntry::deserialize(
112
126
  deserializer.deserializeValue(dstMultiplicity);
113
127
  deserializer.validateDebuggingInfo(debuggingInfo, "storageDirection");
114
128
  deserializer.deserializeValue(storageDirection);
129
+ deserializer.validateDebuggingInfo(debuggingInfo, "storage");
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
+ }
115
138
  deserializer.validateDebuggingInfo(debuggingInfo, "relTableInfos");
116
139
  deserializer.deserializeVector(relTableInfos);
117
140
  auto relGroupEntry = std::make_unique<RelGroupCatalogEntry>();
118
141
  relGroupEntry->srcMultiplicity = srcMultiplicity;
119
142
  relGroupEntry->dstMultiplicity = dstMultiplicity;
120
143
  relGroupEntry->storageDirection = storageDirection;
144
+ relGroupEntry->storage = storage;
145
+ relGroupEntry->scanFunction = scanFunction;
121
146
  relGroupEntry->relTableInfos = relTableInfos;
122
147
  return relGroupEntry;
123
148
  }
124
149
 
125
150
  static std::string getFromToStr(const NodeTableIDPair& pair, const Catalog* catalog,
126
- const transaction::Transaction* transaction) {
127
- auto srcTableName = catalog->getTableCatalogEntry(transaction, pair.srcTableID)->getName();
128
- 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
+ }
129
163
  return stringFormat("FROM `{}` TO `{}`", srcTableName, dstTableName);
130
164
  }
131
165
 
@@ -136,9 +170,10 @@ std::string RelGroupCatalogEntry::toCypher(const ToCypherInfo& info) const {
136
170
  std::stringstream ss;
137
171
  ss << stringFormat("CREATE REL TABLE `{}` (", getName());
138
172
  KU_ASSERT(!relTableInfos.empty());
139
- ss << getFromToStr(relTableInfos[0].nodePair, catalog, transaction);
173
+ ss << getFromToStr(relTableInfos[0].nodePair, catalog, transaction, storage);
140
174
  for (auto i = 1u; i < relTableInfos.size(); ++i) {
141
- ss << stringFormat(", {}", getFromToStr(relTableInfos[i].nodePair, catalog, transaction));
175
+ ss << stringFormat(", {}",
176
+ getFromToStr(relTableInfos[i].nodePair, catalog, transaction, storage));
142
177
  }
143
178
  ss << ", " << propertyCollection.toCypher() << RelMultiplicityUtils::toString(srcMultiplicity)
144
179
  << "_" << RelMultiplicityUtils::toString(dstMultiplicity) << ");";
@@ -167,6 +202,10 @@ std::unique_ptr<TableCatalogEntry> RelGroupCatalogEntry::copy() const {
167
202
  other->srcMultiplicity = srcMultiplicity;
168
203
  other->dstMultiplicity = dstMultiplicity;
169
204
  other->storageDirection = storageDirection;
205
+ other->storage = storage;
206
+ other->scanFunction = scanFunction;
207
+ other->scanBindData = std::nullopt; // TODO: implement copy for bindData if needed
208
+ other->foreignDatabaseName = foreignDatabaseName;
170
209
  other->relTableInfos = relTableInfos;
171
210
  other->copyFrom(*this);
172
211
  return other;
@@ -246,6 +246,7 @@ void CatalogSet::serialize(Serializer serializer) const {
246
246
  case CatalogEntryType::COPY_FUNCTION_ENTRY:
247
247
  case CatalogEntryType::TABLE_FUNCTION_ENTRY:
248
248
  case CatalogEntryType::STANDALONE_TABLE_FUNCTION_ENTRY:
249
+ case CatalogEntryType::FOREIGN_TABLE_ENTRY:
249
250
  continue;
250
251
  default: {
251
252
  auto committedEntry = getCommittedEntryNoLock(entry.get());
@@ -228,7 +228,8 @@ FunctionCollection* FunctionCollection::getFunctions() {
228
228
  TABLE_FUNCTION(StatsInfoFunction), TABLE_FUNCTION(StorageInfoFunction),
229
229
  TABLE_FUNCTION(ShowAttachedDatabasesFunction), TABLE_FUNCTION(ShowSequencesFunction),
230
230
  TABLE_FUNCTION(ShowFunctionsFunction), TABLE_FUNCTION(BMInfoFunction),
231
- TABLE_FUNCTION(FileInfoFunction), TABLE_FUNCTION(ShowLoadedExtensionsFunction),
231
+ TABLE_FUNCTION(FileInfoFunction), TABLE_FUNCTION(DiskSizeInfoFunction),
232
+ TABLE_FUNCTION(ShowLoadedExtensionsFunction),
232
233
  TABLE_FUNCTION(ShowOfficialExtensionsFunction), TABLE_FUNCTION(ShowIndexesFunction),
233
234
  TABLE_FUNCTION(ShowProjectedGraphsFunction), TABLE_FUNCTION(ProjectedGraphInfoFunction),
234
235
  TABLE_FUNCTION(ShowMacrosFunction),
@@ -8,6 +8,7 @@ add_library(lbug_table_function
8
8
  clear_warnings.cpp
9
9
  current_setting.cpp
10
10
  db_version.cpp
11
+ disk_size_info.cpp
11
12
  drop_project_graph.cpp
12
13
  file_info.cpp
13
14
  free_space_info.cpp