ladybug 0.20.2.dev20260907__tar.gz → 0.20.3__tar.gz

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 (91) hide show
  1. sdist/PKG-INFO +1 -1
  2. sdist/ladybug-source/CMakeLists.txt +2 -2
  3. sdist/ladybug-source/docs/multi_parent_lifetime.md +0 -12
  4. sdist/ladybug-source/scripts/antlr4/Cypher.g4 +1 -1
  5. sdist/ladybug-source/scripts/antlr4/hash.md5 +1 -1
  6. sdist/ladybug-source/scripts/pip-package/setup.py +2 -13
  7. sdist/ladybug-source/src/antlr4/Cypher.g4 +1 -1
  8. sdist/ladybug-source/src/binder/bind/bind_export_database.cpp +27 -107
  9. sdist/ladybug-source/src/binder/bind/bind_import_database.cpp +1 -100
  10. sdist/ladybug-source/src/binder/query/query_graph.cpp +8 -15
  11. sdist/ladybug-source/src/c_api/version.cpp +1 -1
  12. sdist/ladybug-source/src/catalog/catalog_entry/node_table_catalog_entry.cpp +0 -3
  13. sdist/ladybug-source/src/catalog/catalog_entry/rel_group_catalog_entry.cpp +1 -5
  14. sdist/ladybug-source/src/function/export/CMakeLists.txt +1 -2
  15. sdist/ladybug-source/src/function/export/export_parquet_function.cpp +2 -18
  16. sdist/ladybug-source/src/include/binder/bound_export_database.h +0 -3
  17. sdist/ladybug-source/src/include/common/constants.h +0 -5
  18. sdist/ladybug-source/src/include/common/data_chunk/data_chunk_state.h +54 -34
  19. sdist/ladybug-source/src/include/function/export/export_function.h +0 -8
  20. sdist/ladybug-source/src/include/function/hash/hash_functions.h +1 -6
  21. sdist/ladybug-source/src/include/main/client_config.h +0 -26
  22. sdist/ladybug-source/src/include/main/client_context.h +0 -3
  23. sdist/ladybug-source/src/include/main/settings.h +0 -10
  24. sdist/ladybug-source/src/include/optimizer/count_rel_table_optimizer.h +0 -12
  25. sdist/ladybug-source/src/include/parser/port_db.h +0 -3
  26. sdist/ladybug-source/src/include/planner/operator/logical_operator.h +0 -2
  27. sdist/ladybug-source/src/include/processor/operator/persistent/writer/parquet/parquet_writer.h +1 -3
  28. sdist/ladybug-source/src/include/processor/operator/physical_operator.h +0 -2
  29. sdist/ladybug-source/src/include/processor/operator/scan/scan_node_table.h +1 -16
  30. sdist/ladybug-source/src/include/processor/operator/scan/scan_rel_table.h +9 -21
  31. sdist/ladybug-source/src/include/processor/plan_mapper.h +0 -4
  32. sdist/ladybug-source/src/include/storage/storage_version_info.h +2 -1
  33. sdist/ladybug-source/src/include/storage/table/arrow_node_table.h +6 -1
  34. sdist/ladybug-source/src/include/storage/table/arrow_rel_table.h +4 -4
  35. sdist/ladybug-source/src/include/storage/table/arrow_table_support.h +18 -5
  36. sdist/ladybug-source/src/include/storage/table/csr_node_group.h +0 -8
  37. sdist/ladybug-source/src/include/storage/table/rel_table.h +0 -33
  38. sdist/ladybug-source/src/include/storage/table/table.h +0 -6
  39. sdist/ladybug-source/src/main/client_context.cpp +0 -15
  40. sdist/ladybug-source/src/main/db_config.cpp +1 -2
  41. sdist/ladybug-source/src/main/settings.cpp +0 -46
  42. sdist/ladybug-source/src/optimizer/CMakeLists.txt +0 -1
  43. sdist/ladybug-source/src/optimizer/count_rel_table_optimizer.cpp +0 -675
  44. sdist/ladybug-source/src/optimizer/filter_push_down_optimizer.cpp +1 -6
  45. sdist/ladybug-source/src/optimizer/optimizer.cpp +0 -95
  46. sdist/ladybug-source/src/parser/transform/transform_port_db.cpp +1 -5
  47. sdist/ladybug-source/src/planner/operator/logical_operator.cpp +0 -2
  48. sdist/ladybug-source/src/planner/operator/scan/CMakeLists.txt +0 -2
  49. sdist/ladybug-source/src/planner/plan/plan_join_order.cpp +24 -54
  50. sdist/ladybug-source/src/planner/plan/plan_port_db.cpp +1 -6
  51. sdist/ladybug-source/src/processor/map/CMakeLists.txt +0 -2
  52. sdist/ladybug-source/src/processor/map/map_aggregate.cpp +1 -11
  53. sdist/ladybug-source/src/processor/map/plan_mapper.cpp +0 -6
  54. sdist/ladybug-source/src/processor/operator/aggregate/packed_filtered_count.cpp +6 -44
  55. sdist/ladybug-source/src/processor/operator/persistent/writer/parquet/parquet_writer.cpp +2 -7
  56. sdist/ladybug-source/src/processor/operator/physical_operator.cpp +0 -4
  57. sdist/ladybug-source/src/processor/operator/scan/CMakeLists.txt +0 -2
  58. sdist/ladybug-source/src/processor/operator/scan/scan_node_table.cpp +4 -71
  59. sdist/ladybug-source/src/processor/operator/scan/scan_rel_table.cpp +35 -22
  60. sdist/ladybug-source/src/processor/operator/simple/export_db.cpp +101 -30
  61. sdist/ladybug-source/src/processor/result/factorized_table.cpp +2 -7
  62. sdist/ladybug-source/src/storage/local_storage/local_node_table.cpp +12 -1
  63. sdist/ladybug-source/src/storage/storage_manager.cpp +12 -31
  64. sdist/ladybug-source/src/storage/table/arrow_node_table.cpp +13 -3
  65. sdist/ladybug-source/src/storage/table/arrow_rel_table.cpp +20 -7
  66. sdist/ladybug-source/src/storage/table/arrow_table_support.cpp +24 -20
  67. sdist/ladybug-source/src/storage/table/csr_node_group.cpp +1 -93
  68. sdist/ladybug-source/src/storage/table/ice_disk_node_table.cpp +2 -26
  69. sdist/ladybug-source/src/storage/table/node_group.cpp +8 -18
  70. sdist/ladybug-source/src/storage/table/node_table.cpp +29 -1
  71. sdist/ladybug-source/src/storage/table/rel_table.cpp +15 -118
  72. sdist/ladybug-source/third_party/antlr4_cypher/cypher_parser.cpp +3272 -3323
  73. sdist/ladybug-source/third_party/antlr4_cypher/include/cypher_parser.h +0 -1
  74. sdist/ladybug-source/tools/python_api/CMakeLists.txt +0 -81
  75. sdist/ladybug.egg-info/PKG-INFO +1 -1
  76. sdist/pyproject.toml +1 -1
  77. sdist/setup.py +2 -13
  78. sdist/ladybug-source/scripts/pip-package/split_debug_symbols.py +0 -262
  79. sdist/ladybug-source/src/function/export/export_indptr_function.cpp +0 -171
  80. sdist/ladybug-source/src/include/optimizer/group_key_predicate_push_down_optimizer.h +0 -49
  81. sdist/ladybug-source/src/include/planner/operator/scan/logical_count_anti_edge_chain.h +0 -116
  82. sdist/ladybug-source/src/include/planner/operator/scan/logical_count_extend_chain.h +0 -108
  83. sdist/ladybug-source/src/include/processor/operator/scan/count_anti_edge_chain.h +0 -114
  84. sdist/ladybug-source/src/include/processor/operator/scan/count_extend_chain.h +0 -109
  85. sdist/ladybug-source/src/optimizer/group_key_predicate_push_down_optimizer.cpp +0 -204
  86. sdist/ladybug-source/src/planner/operator/scan/logical_count_anti_edge_chain.cpp +0 -20
  87. sdist/ladybug-source/src/planner/operator/scan/logical_count_extend_chain.cpp +0 -21
  88. sdist/ladybug-source/src/processor/map/map_count_anti_edge_chain.cpp +0 -52
  89. sdist/ladybug-source/src/processor/map/map_count_extend_chain.cpp +0 -52
  90. sdist/ladybug-source/src/processor/operator/scan/count_anti_edge_chain.cpp +0 -349
  91. sdist/ladybug-source/src/processor/operator/scan/count_extend_chain.cpp +0 -193
sdist/PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ladybug
3
- Version: 0.20.2.dev20260907
3
+ Version: 0.20.3
4
4
  Summary: Highly scalable, extremely fast, easy-to-use embeddable graph database
5
5
  Home-page: https://github.com/LadybugDB/ladybug
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  cmake_minimum_required(VERSION 3.15)
2
2
 
3
- project(Lbug VERSION 0.20.2.20260907 LANGUAGES CXX C)
3
+ project(Lbug VERSION 0.20.3 LANGUAGES CXX C)
4
4
 
5
5
  option(SINGLE_THREADED "Single-threaded mode" FALSE)
6
6
  if(SINGLE_THREADED)
@@ -522,7 +522,7 @@ unset(LBUG_LINK_LIBRARIES)
522
522
 
523
523
  add_definitions(-DLBUG_ROOT_DIRECTORY="${PROJECT_SOURCE_DIR}")
524
524
  add_definitions(-DLBUG_CMAKE_VERSION="${CMAKE_PROJECT_VERSION}")
525
- add_definitions(-DLBUG_EXTENSION_VERSION="dev")
525
+ add_definitions(-DLBUG_EXTENSION_VERSION="0.20.0")
526
526
 
527
527
  if(BUILD_LBUG OR LBUG_API_USE_PRECOMPILED_LIB)
528
528
  include_directories(
@@ -1,17 +1,5 @@
1
1
  # Multi-parent packed child slices: lifetime & representation
2
2
 
3
- > **Status update (multi-parent scan implemented):** the deferred change described below has
4
- > been implemented. `PackedChildSlices` now holds a `shared_ptr<SelectionVector>` aliasing the
5
- > bound (parent) chunk state's selection vector plus a prefix-sum `offsets` over ALL served
6
- > parents (zeros allowed, zero-length ranges skipped by consumers). The CSR scan packs children
7
- > of multiple parents per output batch in `CSRNodeGroupScanState::tryScanCachedTuplesPacked`
8
- > (persistent with-cache path only). Multi-parent batches are enabled per-consumer:
9
- > `ScanRelTable::setMultiParentPackedScanEnabled(true)` is set by the plan mapper only when the
10
- > packed-aware `PackedFilteredCount` consumes the scan output directly. Plans that materialize
11
- > the scan output across a `FactorizedTable` (e.g. when the filter predicate touches an nbr
12
- > node property, which routes the property fetch through a hash join) keep the
13
- > one-parent-per-batch contract — exactly the materialization hazard described below.
14
-
15
3
  Context: the `enable_packed_path_extend` path reuses the adjacency-list (CSR)
16
4
  rel scan to implement a physical packed extend. `ScanRelTable` attaches a
17
5
  `PackedChildSlices` descriptor to the **output** `DataChunkState`
@@ -326,7 +326,7 @@ iC_ExportDatabase
326
326
  : EXPORT SP DATABASE SP StringLiteral ( SP? '(' SP? iC_Options SP? ')' )? ;
327
327
 
328
328
  iC_ImportDatabase
329
- : IMPORT SP DATABASE SP StringLiteral ( SP? '(' SP? iC_Options SP? ')' )? ;
329
+ : IMPORT SP DATABASE SP StringLiteral;
330
330
 
331
331
  iC_AttachDatabase
332
332
  : ATTACH SP StringLiteral (SP AS SP oC_SchemaName)? SP '(' SP? DBTYPE SP oC_SymbolicName (SP? ',' SP? iC_Options)? SP? ')' ;
@@ -1 +1 @@
1
- 6fc9fc9085d4f1028a4fb2b319152cb5
1
+ c6ef6ea298ca84a5c080bae09a29c9db
@@ -91,27 +91,16 @@ class CMakeBuild(build_ext):
91
91
  if os.path.exists(flags_file):
92
92
  with open(flags_file) as f:
93
93
  extra_cmake_flags = f.read().strip()
94
- # Wheel builds must not split symbols at build time: raw .debug/dSYM
95
- # artifacts packaged inside the wheel break auditwheel/delocate
96
- # repair. The split happens on the final repaired wheel instead (see
97
- # split_debug_symbols.py), which needs the debug info to still be
98
- # present in the shipped binary.
99
- no_split_flag = '-DLBUG_SPLIT_DEBUG_SYMBOLS=OFF'
100
94
  if precompiled_lib_path:
101
95
  self.announce("Using precompiled liblbug from %s." % precompiled_lib_path)
102
96
  flags = (
103
- 'EXTRA_CMAKE_FLAGS=%s -DBUILD_LBUG=FALSE -DBUILD_SHELL=FALSE '
97
+ 'EXTRA_CMAKE_FLAGS=-DBUILD_LBUG=FALSE -DBUILD_SHELL=FALSE '
104
98
  '-DLBUG_API_USE_PRECOMPILED_LIB=TRUE '
105
- '-DLBUG_API_PRECOMPILED_LIB_PATH=%s' % (no_split_flag, precompiled_lib_path)
99
+ '-DLBUG_API_PRECOMPILED_LIB_PATH=%s' % precompiled_lib_path
106
100
  )
107
101
  if extra_cmake_flags:
108
102
  flags += ' ' + extra_cmake_flags
109
103
  full_cmd.append(flags)
110
- else:
111
- flags = 'EXTRA_CMAKE_FLAGS=%s' % no_split_flag
112
- if extra_cmake_flags:
113
- flags += ' ' + extra_cmake_flags
114
- full_cmd.append(flags)
115
104
  subprocess.run(full_cmd, cwd=build_dir, check=True, env=env_vars)
116
105
  self.announce("Done building native extension.")
117
106
  self.announce("Copying native extension...")
@@ -57,7 +57,7 @@ iC_ExportDatabase
57
57
  : EXPORT SP DATABASE SP StringLiteral ( SP? '(' SP? iC_Options SP? ')' )? ;
58
58
 
59
59
  iC_ImportDatabase
60
- : IMPORT SP DATABASE SP StringLiteral ( SP? '(' SP? iC_Options SP? ')' )? ;
60
+ : IMPORT SP DATABASE SP StringLiteral;
61
61
 
62
62
  iC_AttachDatabase
63
63
  : ATTACH SP StringLiteral (SP AS SP oC_SchemaName)? SP '(' SP? DBTYPE SP oC_SymbolicName (SP? ',' SP? iC_Options)? SP? ')' ;
@@ -11,7 +11,6 @@
11
11
  #include "parser/parser.h"
12
12
  #include "parser/port_db.h"
13
13
  #include "parser/query/regular_query.h"
14
- #include "storage/table/ice_disk_constants.h"
15
14
  #include "transaction/transaction.h"
16
15
  #include <format>
17
16
 
@@ -36,10 +35,6 @@ FileTypeInfo getFileType(case_insensitive_map_t<Value>& options) {
36
35
  auto valueStr = value.getValue<std::string>();
37
36
  StringUtils::toUpper(valueStr);
38
37
  fileTypeInfo = FileTypeInfo{FileTypeUtils::fromString(valueStr), valueStr};
39
- if (fileTypeInfo.fileType != FileType::PARQUET) {
40
- throw BinderException(
41
- "Export database only supports the parquet (icebug-disk CSR) format.");
42
- }
43
38
  options.erase(PortDBConstants::EXPORT_FORMAT_OPTION);
44
39
  }
45
40
  return fileTypeInfo;
@@ -62,134 +57,66 @@ void bindExportTableData(ExportedTableData& tableData, const std::string& query,
62
57
  tableData.regularQuery = std::move(boundQuery);
63
58
  }
64
59
 
65
- static std::string getPropertyReturnList(const TableCatalogEntry& entry, const std::string& var) {
66
- std::string props;
67
- for (auto& property : entry.getProperties()) {
68
- if (property.getType() == LogicalType::INTERNAL_ID()) {
69
- continue;
70
- }
71
- props += std::format(",{}.{} as {}", var, StringUtils::quoteIdentifier(property.getName()),
72
- StringUtils::quoteIdentifier(property.getName()));
73
- }
74
- return props;
75
- }
76
-
77
60
  static std::string getExportNodeTableDataQuery(const TableCatalogEntry& entry) {
78
- // Order by storage offset so that row i in nodes_<table>.parquet holds the node
79
- // with dense offset i, matching the CSR target/ptr values in the rel tables.
80
- // Properties are aliased explicitly so parquet columns carry clean names.
81
- auto props = getPropertyReturnList(entry, "a");
82
- auto returns = props.empty() ? "a.*" : props.substr(1);
83
- return std::format("match (a:{}) return {} order by offset(id(a))",
84
- StringUtils::quoteIdentifier(entry.getName()), returns);
61
+ return std::format("match (a:{}) return a.*", StringUtils::quoteIdentifier(entry.getName()));
85
62
  }
86
63
 
87
- static std::string getExportRelIndicesQuery(const TableCatalogEntry& relGroupEntry,
64
+ static std::string getExportRelTableDataQuery(const TableCatalogEntry& relGroupEntry,
88
65
  const NodeTableCatalogEntry& srcEntry, const NodeTableCatalogEntry& dstEntry) {
89
- // CSR indices table: dense target offsets (+ edge properties), ordered by
90
- // (source offset, target offset). The source offset is encoded by indptr.
91
- return std::format("match (a:{})-[r:{}]->(b:{}) return offset(id(b)) as target{} "
92
- "order by offset(id(a)), offset(id(b));",
66
+ return std::format("match (a:{})-[r:{}]->(b:{}) return a.{},b.{},r.*;",
93
67
  StringUtils::quoteIdentifier(srcEntry.getName()),
94
68
  StringUtils::quoteIdentifier(relGroupEntry.getName()),
95
69
  StringUtils::quoteIdentifier(dstEntry.getName()),
96
- getPropertyReturnList(relGroupEntry, "r"));
97
- }
98
-
99
- static std::string getExportRelIndptrQuery(const TableCatalogEntry& relGroupEntry,
100
- const NodeTableCatalogEntry& srcEntry, const NodeTableCatalogEntry& dstEntry) {
101
- // One row per source node (OPTIONAL MATCH keeps zero-degree nodes), consumed by
102
- // the indptr export function which sorts by src and prefix-sums the degrees.
103
- return std::format(
104
- "match (a:{}) optional match (a)-[r:{}]->(b:{}) return offset(id(a)) as src, "
105
- "count(r) as degree;",
106
- StringUtils::quoteIdentifier(srcEntry.getName()),
107
- StringUtils::quoteIdentifier(relGroupEntry.getName()),
108
- StringUtils::quoteIdentifier(dstEntry.getName()));
109
- }
110
-
111
- static std::string getExportRelFlatQuery(const TableCatalogEntry& relGroupEntry,
112
- const NodeTableCatalogEntry& srcEntry, const NodeTableCatalogEntry& dstEntry) {
113
- // Flat relationship file for native re-import: source/target primary-key values
114
- // (COPY FROM resolves them back to node offsets) followed by edge properties.
115
- return std::format("match (a:{})-[r:{}]->(b:{}) return {}.{} as {}, {}.{} as {}{};",
116
- StringUtils::quoteIdentifier(srcEntry.getName()),
117
- StringUtils::quoteIdentifier(relGroupEntry.getName()),
118
- StringUtils::quoteIdentifier(dstEntry.getName()), "a",
119
- StringUtils::quoteIdentifier(srcEntry.getPrimaryKeyDefinition().getName()),
120
- StringUtils::quoteIdentifier("from"), "b",
121
- StringUtils::quoteIdentifier(dstEntry.getPrimaryKeyDefinition().getName()),
122
- StringUtils::quoteIdentifier("to"), getPropertyReturnList(relGroupEntry, "r"));
70
+ StringUtils::quoteIdentifier(srcEntry.getPrimaryKeyName()),
71
+ StringUtils::quoteIdentifier(dstEntry.getPrimaryKeyName()));
123
72
  }
124
73
 
125
74
  static std::vector<ExportedTableData> getExportInfo(const Catalog& catalog,
126
- main::ClientContext* context, Binder* binder, [[maybe_unused]] FileTypeInfo& fileTypeInfo) {
75
+ main::ClientContext* context, Binder* binder, FileTypeInfo& fileTypeInfo) {
127
76
  auto transaction = Transaction::Get(*context);
128
77
  std::vector<ExportedTableData> exportData;
129
78
  for (auto entry : catalog.getNodeTableEntries(transaction, false /*useInternal*/)) {
130
79
  ExportedTableData tableData;
131
80
  tableData.tableName = entry->getName();
132
- tableData.fileName = std::format("nodes_{}.parquet", entry->getName());
81
+ tableData.fileName =
82
+ entry->getName() + "." + StringUtils::getLower(fileTypeInfo.fileTypeStr);
133
83
  auto query = getExportNodeTableDataQuery(*entry);
134
84
  bindExportTableData(tableData, query, context, binder);
135
85
  exportData.push_back(std::move(tableData));
136
86
  }
137
87
  for (auto entry : catalog.getRelGroupEntries(transaction, false /* useInternal */)) {
138
88
  auto& relGroupEntry = entry->constCast<RelGroupCatalogEntry>();
139
- if (relGroupEntry.getRelEntryInfos().size() != 1) {
140
- throw BinderException(std::format(
141
- "Export database: rel table {} connects multiple node pairs, which is not "
142
- "supported for icebug-disk CSR export yet.",
143
- relGroupEntry.getName()));
144
- }
145
89
  for (auto& info : relGroupEntry.getRelEntryInfos()) {
90
+ ExportedTableData tableData;
146
91
  auto srcTableID = info.nodePair.srcTableID;
147
92
  auto dstTableID = info.nodePair.dstTableID;
148
93
  auto& srcEntry = catalog.getTableCatalogEntry(transaction, srcTableID)
149
94
  ->constCast<NodeTableCatalogEntry>();
150
95
  auto& dstEntry = catalog.getTableCatalogEntry(transaction, dstTableID)
151
96
  ->constCast<NodeTableCatalogEntry>();
152
- ExportedTableData indicesData;
153
- indicesData.tableName = entry->getName();
154
- indicesData.fileName = std::format("indices_{}.parquet", relGroupEntry.getName());
155
- bindExportTableData(indicesData,
156
- getExportRelIndicesQuery(relGroupEntry, srcEntry, dstEntry), context, binder);
157
- exportData.push_back(std::move(indicesData));
158
- ExportedTableData indptrData;
159
- indptrData.tableName = entry->getName();
160
- indptrData.fileName = std::format("indptr_{}.parquet", relGroupEntry.getName());
161
- indptrData.isIndptr = true;
162
- bindExportTableData(indptrData,
163
- getExportRelIndptrQuery(relGroupEntry, srcEntry, dstEntry), context, binder);
164
- exportData.push_back(std::move(indptrData));
165
- // Flat relationship file (from/to primary-key values + properties), used by
166
- // IMPORT DATABASE to ingest the relationship table into native storage. It is
167
- // ignored when the export is mounted as icebug-disk (which uses the CSR files).
168
- ExportedTableData flatRelData;
169
- flatRelData.tableName = entry->getName();
170
- flatRelData.fileName = std::format("rels_{}.parquet", relGroupEntry.getName());
171
- bindExportTableData(flatRelData,
172
- getExportRelFlatQuery(relGroupEntry, srcEntry, dstEntry), context, binder);
173
- exportData.push_back(std::move(flatRelData));
97
+ tableData.tableName = entry->getName();
98
+ tableData.fileName =
99
+ std::format("{}_{}_{}.{}", relGroupEntry.getName(), srcEntry.getName(),
100
+ dstEntry.getName(), StringUtils::getLower(fileTypeInfo.fileTypeStr));
101
+ auto query = getExportRelTableDataQuery(relGroupEntry, srcEntry, dstEntry);
102
+ bindExportTableData(tableData, query, context, binder);
103
+ exportData.push_back(std::move(tableData));
174
104
  }
175
105
  }
176
106
 
177
- // Index aux tables (e.g. the FTS internal stopwords table) are not part of the CSR
178
- // layout, but index.cypher needs them on import when the original stopwords source
179
- // table no longer exists. Export them as <tableName>.parquet; the FTS index's
180
- // toCypher references the file path in that case.
181
107
  for (auto indexEntry : catalog.getIndexEntries(transaction)) {
182
- auto auxEntry = indexEntry->getTableEntryToExport(context);
183
- if (auxEntry == nullptr) {
108
+ // Export
109
+ ExportedTableData tableData;
110
+ auto entry = indexEntry->getTableEntryToExport(context);
111
+ if (entry == nullptr) {
184
112
  continue;
185
113
  }
186
- ExportedTableData tableData;
187
- tableData.tableName = auxEntry->getName();
188
- tableData.fileName = std::format("{}.parquet", auxEntry->getName());
189
- bindExportTableData(tableData,
190
- std::format("match (a:{}) return a.*",
191
- StringUtils::quoteIdentifier(auxEntry->getName())),
192
- context, binder);
114
+ DASSERT(entry->getTableType() == TableType::NODE);
115
+ tableData.tableName = entry->getName();
116
+ tableData.fileName =
117
+ entry->getName() + "." + StringUtils::getLower(fileTypeInfo.fileTypeStr);
118
+ auto query = getExportNodeTableDataQuery(*entry);
119
+ bindExportTableData(tableData, query, context, binder);
193
120
  exportData.push_back(std::move(tableData));
194
121
  }
195
122
  return exportData;
@@ -231,17 +158,10 @@ std::unique_ptr<BoundStatement> Binder::bindExportDatabaseClause(const Statement
231
158
  }
232
159
  auto exportSchemaOnly = schemaOnly(parsedOptions, exportDB);
233
160
  if (!exportSchemaOnly && fileTypeInfo.fileType != FileType::CSV && parsedOptions.size() != 0) {
234
- throw BinderException{"Export database does not support copy options with the parquet "
235
- "(icebug-disk CSR) format."};
161
+ throw BinderException{"Only export to csv can have options."};
236
162
  }
237
163
  auto exportData =
238
164
  getExportInfo(*Catalog::Get(*clientContext), clientContext, this, fileTypeInfo);
239
- // Stamp every exported parquet file with the icebug-disk format version so that
240
- // IceDiskUtils::checkVersionCompatibility can validate it when the export is mounted.
241
- if (fileTypeInfo.fileType == FileType::PARQUET && !exportSchemaOnly) {
242
- parsedOptions.insert_or_assign(PortDBConstants::ICEBUG_DISK_VERSION_OPTION,
243
- Value{std::string{storage::IceDiskConstants::CURRENT_VERSION}});
244
- }
245
165
  auto boundFilePath = VirtualFileSystem::GetUnsafe(*clientContext)
246
166
  ->expandPath(clientContext, exportDB.getFilePath());
247
167
  return std::make_unique<BoundExportDatabase>(boundFilePath, fileTypeInfo, std::move(exportData),
@@ -6,7 +6,6 @@
6
6
  #include "common/string_utils.h"
7
7
  #include "main/client_context.h"
8
8
  #include "parser/copy.h"
9
- #include "parser/ddl/create_table.h"
10
9
  #include "parser/parser.h"
11
10
  #include "parser/port_db.h"
12
11
  #include <format>
@@ -84,73 +83,6 @@ static std::string getCopyFilePath(const std::string& boundFilePath, const std::
84
83
  return path;
85
84
  }
86
85
 
87
- // Removes the `WITH (storage = '<dir>', format = 'icebug-disk')` clauses that EXPORT DATABASE
88
- // attaches to CREATE TABLE statements in schema.cypher, so that IMPORT DATABASE (without an
89
- // explicit `storage_format = 'icebug-disk'` override) creates native tables instead of mounting
90
- // the exported parquet files in place.
91
- static std::string stripIcebugStorageClauses(const std::string& schema) {
92
- std::string result = schema;
93
- size_t searchFrom = 0;
94
- size_t pos;
95
- while ((pos = result.find(" WITH (", searchFrom)) != std::string::npos) {
96
- auto end = result.find(')', pos);
97
- if (end == std::string::npos) {
98
- break;
99
- }
100
- auto clause = result.substr(pos, end - pos);
101
- if (clause.find("format = 'icebug-disk'") != std::string::npos &&
102
- clause.find("storage = '") != std::string::npos) {
103
- result.erase(pos, end - pos + 1);
104
- searchFrom = pos;
105
- } else {
106
- searchFrom = end;
107
- }
108
- }
109
- return result;
110
- }
111
-
112
- // Generates COPY statements that ingest the exported parquet data files into the native
113
- // tables created by the (stripped) schema.cypher statements. The exported layout is
114
- // nodes_<table>.parquet for node tables and rels_<rel>.parquet (from/to primary-key
115
- // columns + edge properties) for relationship tables.
116
- static std::string getNativeCopyStatements(VirtualFileSystem* vfs, const std::string& boundFilePath,
117
- const std::string& schemaQuery, main::ClientContext* context) {
118
- std::string copies;
119
- if (schemaQuery.empty()) {
120
- return copies;
121
- }
122
- for (auto& parsedStatement : Parser::parseQuery(schemaQuery)) {
123
- if (parsedStatement->getStatementType() != StatementType::CREATE_TABLE) {
124
- continue;
125
- }
126
- auto& createTable = parsedStatement->constCast<CreateTable>();
127
- const auto& info = *createTable.getInfo();
128
- auto isRel = info.type == TableType::REL;
129
- auto dataFile = isRel ? std::format("rels_{}.parquet", info.tableName) :
130
- std::format("nodes_{}.parquet", info.tableName);
131
- auto filePath = getCopyFilePath(boundFilePath, dataFile);
132
- if (!vfs->fileOrPathExists(vfs->joinPath(boundFilePath, dataFile), context)) {
133
- continue;
134
- }
135
- // Explicit column list for node tables (covers SERIAL pk values, which COPY FROM
136
- // would otherwise auto-generate). Rel tables rely on positional binding: the flat
137
- // file already stores from/to key columns followed by properties in schema order.
138
- std::string columns;
139
- if (!isRel) {
140
- std::vector<std::string> columnList;
141
- for (auto& property : info.propertyDefinitions) {
142
- columnList.push_back(StringUtils::quoteIdentifier(property.getName()));
143
- }
144
- if (!columnList.empty()) {
145
- columns = std::format(" ({})", StringUtils::join(columnList, ", "));
146
- }
147
- }
148
- copies += std::format("COPY {}{} FROM \"{}\";\n",
149
- StringUtils::quoteIdentifier(info.tableName), columns, filePath);
150
- }
151
- return copies;
152
- }
153
-
154
86
  std::unique_ptr<BoundStatement> Binder::bindImportDatabaseClause(const Statement& statement) {
155
87
  auto& importDB = statement.constCast<ImportDB>();
156
88
  auto fs = VirtualFileSystem::GetUnsafe(*clientContext);
@@ -158,40 +90,9 @@ std::unique_ptr<BoundStatement> Binder::bindImportDatabaseClause(const Statement
158
90
  if (!fs->fileOrPathExists(boundFilePath, clientContext)) {
159
91
  throw BinderException(std::format("Directory {} does not exist.", boundFilePath));
160
92
  }
161
- // By default the exported icebug-disk data files are ingested into native tables.
162
- // An explicit `storage_format = 'icebug-disk'` override mounts the parquet files in
163
- // place (i.e. the database is opened directly against the exported directory).
164
- bool mountIcebug = false;
165
- auto parsingOptions = bindParsingOptions(importDB.getParsingOptionsRef());
166
- if (parsingOptions.contains(PortDBConstants::IMPORT_STORAGE_FORMAT_OPTION)) {
167
- auto& value = parsingOptions.at(PortDBConstants::IMPORT_STORAGE_FORMAT_OPTION);
168
- if (value.getDataType().getLogicalTypeID() != LogicalTypeID::STRING) {
169
- throw BinderException("The type of storage_format option must be a string.");
170
- }
171
- auto valueStr = StringUtils::getUpper(value.getValue<std::string>());
172
- if (valueStr != "ICEBUG-DISK") {
173
- throw BinderException(std::format(
174
- "Unsupported import database storage format '{}'. Valid options are: icebug-disk.",
175
- value.getValue<std::string>()));
176
- }
177
- mountIcebug = true;
178
- parsingOptions.erase(PortDBConstants::IMPORT_STORAGE_FORMAT_OPTION);
179
- }
180
- if (!parsingOptions.empty()) {
181
- throw BinderException(
182
- std::format("Unrecognized import database option: {}.", parsingOptions.begin()->first));
183
- }
184
93
  std::string finalQueryStatements;
185
- auto schemaQuery =
94
+ finalQueryStatements +=
186
95
  getQueryFromFile(fs, boundFilePath, PortDBConstants::SCHEMA_FILE_NAME, clientContext);
187
- if (!mountIcebug) {
188
- schemaQuery = stripIcebugStorageClauses(schemaQuery);
189
- finalQueryStatements += schemaQuery;
190
- finalQueryStatements +=
191
- getNativeCopyStatements(fs, boundFilePath, schemaQuery, clientContext);
192
- } else {
193
- finalQueryStatements += schemaQuery;
194
- }
195
96
  // replace the path in copy from statements with the bound path
196
97
  auto copyQuery =
197
98
  getQueryFromFile(fs, boundFilePath, PortDBConstants::COPY_FILE_NAME, clientContext);
@@ -47,13 +47,6 @@ std::unordered_set<uint32_t> SubqueryGraph::getNodeNbrPositions() const {
47
47
 
48
48
  std::unordered_set<uint32_t> SubqueryGraph::getRelNbrPositions() const {
49
49
  std::unordered_set<uint32_t> result;
50
- // Nodes in the subgraph: explicitly selected nodes plus endpoints of selected rels
51
- // (rel-implied nodes). A neighboring rel is any unselected rel touching any of them.
52
- // Considering rel-implied nodes allows the join enumerator to attach a rel to a
53
- // subgraph through the node bound by a previous extend (e.g. planning
54
- // (a)-[e1]->(b)-[e2]->(c) as scan(a) -> extend(e1) -> extend(e2)), instead of
55
- // requiring an explicit node-table scan join first.
56
- const auto nodePosSet = getNodePositionsIgnoringNodeSelector();
57
50
  for (auto relPos = 0u; relPos < queryGraph.getNumQueryRels(); ++relPos) {
58
51
  if (queryRelsSelector[relPos]) { // rel already in subgraph, cannot be rel neighbour
59
52
  continue;
@@ -61,7 +54,7 @@ std::unordered_set<uint32_t> SubqueryGraph::getRelNbrPositions() const {
61
54
  auto rel = queryGraph.getQueryRel(relPos);
62
55
  auto srcNodePos = queryGraph.getQueryNodeIdx(*rel->getSrcNode());
63
56
  auto dstNodePos = queryGraph.getQueryNodeIdx(*rel->getDstNode());
64
- if (nodePosSet.contains(srcNodePos) || nodePosSet.contains(dstNodePos)) {
57
+ if (queryNodesSelector[srcNodePos] || queryNodesSelector[dstNodePos]) {
65
58
  result.insert(relPos);
66
59
  }
67
60
  }
@@ -84,13 +77,13 @@ subquery_graph_set_t SubqueryGraph::getNbrSubgraphs(uint32_t size) const {
84
77
 
85
78
  std::vector<uint32_t> SubqueryGraph::getConnectedNodePos(const SubqueryGraph& nbr) const {
86
79
  std::vector<uint32_t> result;
87
- // Join nodes are the intersection of the full node sets (selected nodes plus
88
- // rel-implied nodes), so joins through nodes that are only bound by a previous
89
- // extend (not explicitly scanned) are discovered as well.
90
- const auto thisNodePos = getNodePositionsIgnoringNodeSelector();
91
- const auto nbrNodePos = nbr.getNodePositionsIgnoringNodeSelector();
92
- for (auto& nodePos : thisNodePos) {
93
- if (nbrNodePos.contains(nodePos)) {
80
+ for (auto& nodePos : getNodeNbrPositions()) {
81
+ if (nbr.queryNodesSelector[nodePos]) {
82
+ result.push_back(nodePos);
83
+ }
84
+ }
85
+ for (auto& nodePos : nbr.getNodeNbrPositions()) {
86
+ if (queryNodesSelector[nodePos]) {
94
87
  result.push_back(nodePos);
95
88
  }
96
89
  }
@@ -7,7 +7,7 @@ char* lbug_get_version() {
7
7
  LBUG_C_API_GUARD_BEGIN
8
8
  auto version = lbug::main::Version::getVersion();
9
9
  if (version == nullptr || version[0] == '\0') {
10
- version = "0.20.2";
10
+ version = "0.20.3";
11
11
  }
12
12
  return convertToOwnedCString(version);
13
13
  LBUG_C_API_GUARD_END(nullptr)
@@ -202,9 +202,6 @@ std::string NodeTableCatalogEntry::toCypher(const ToCypherInfo& /*info*/) const
202
202
  base += std::format(" PARTITION BY {}({}) PARTITIONS {}", method,
203
203
  common::StringUtils::quoteIdentifier(partitionColumnName), numPartitions);
204
204
  }
205
- if (storageFormat == common::StorageFormat::ICEBUG_DISK) {
206
- base += std::format(" WITH (storage = '{}', format = 'icebug-disk')", storage);
207
- }
208
205
  return base + ";";
209
206
  }
210
207
 
@@ -248,11 +248,7 @@ std::string RelGroupCatalogEntry::toCypher(const ToCypherInfo& info) const {
248
248
  }
249
249
  }
250
250
  ss << ", " << propertyCollection.toCypher()
251
- << getMultiplicityStr(srcMultiplicity, dstMultiplicity);
252
- if (storageFormat == common::StorageFormat::ICEBUG_DISK) {
253
- ss << std::format(" WITH (storage = '{}', format = 'icebug-disk')", storage);
254
- }
255
- ss << ");";
251
+ << getMultiplicityStr(srcMultiplicity, dstMultiplicity) << ");";
256
252
  return ss.str();
257
253
  }
258
254
 
@@ -1,8 +1,7 @@
1
1
  add_library(lbug_function_export
2
2
  OBJECT
3
3
  export_csv_function.cpp
4
- export_parquet_function.cpp
5
- export_indptr_function.cpp)
4
+ export_parquet_function.cpp)
6
5
 
7
6
  set(ALL_OBJECT_FILES
8
7
  ${ALL_OBJECT_FILES} $<TARGET_OBJECTS:lbug_function_export>
@@ -7,7 +7,6 @@
7
7
  #include "processor/operator/persistent/writer/parquet/parquet_writer.h"
8
8
  #include "processor/result/factorized_table.h"
9
9
  #include "storage/buffer_manager/memory_manager.h"
10
- #include "storage/table/ice_disk_constants.h"
11
10
  #include <format>
12
11
 
13
12
  namespace lbug {
@@ -19,14 +18,11 @@ using namespace processor;
19
18
  struct ParquetOptions {
20
19
  lbug_parquet::format::CompressionCodec::type codec =
21
20
  lbug_parquet::format::CompressionCodec::SNAPPY;
22
- std::vector<lbug_parquet::format::KeyValue> keyValueMetadata;
23
21
 
24
22
  explicit ParquetOptions(case_insensitive_map_t<common::Value> parsingOptions) {
25
23
  for (auto& [name, value] : parsingOptions) {
26
24
  if (name == "COMPRESSION") {
27
25
  setCompression(value);
28
- } else if (name == storage::IceDiskConstants::VERSION_METADATA_KEY) {
29
- setVersionMetadata(value);
30
26
  } else {
31
27
  throw common::RuntimeException{
32
28
  std::format("Unrecognized parquet option: {}.", name)};
@@ -34,18 +30,6 @@ struct ParquetOptions {
34
30
  }
35
31
  }
36
32
 
37
- void setVersionMetadata(common::Value& value) {
38
- if (value.getDataType().getLogicalTypeID() != LogicalTypeID::STRING) {
39
- throw common::RuntimeException{std::format(
40
- "Parquet {} option expects a string value, got: {}.",
41
- storage::IceDiskConstants::VERSION_METADATA_KEY, value.getDataType().toString())};
42
- }
43
- lbug_parquet::format::KeyValue kv;
44
- kv.__set_key(std::string(storage::IceDiskConstants::VERSION_METADATA_KEY));
45
- kv.__set_value(value.getValue<std::string>());
46
- keyValueMetadata.push_back(std::move(kv));
47
- }
48
-
49
33
  void setCompression(common::Value& value) {
50
34
  if (value.getDataType().getLogicalTypeID() != LogicalTypeID::STRING) {
51
35
  throw common::RuntimeException{
@@ -117,8 +101,8 @@ struct ExportParquetSharedState : public ExportFuncSharedState {
117
101
  auto& exportParquetBindData = bindData.constCast<ExportParquetBindData>();
118
102
  writer = std::make_unique<ParquetWriter>(exportParquetBindData.fileName,
119
103
  common::LogicalType::copy(exportParquetBindData.types),
120
- exportParquetBindData.columnNames, exportParquetBindData.parquetOptions.codec, &context,
121
- exportParquetBindData.parquetOptions.keyValueMetadata);
104
+ exportParquetBindData.columnNames, exportParquetBindData.parquetOptions.codec,
105
+ &context);
122
106
  }
123
107
  };
124
108
 
@@ -10,9 +10,6 @@ namespace binder {
10
10
  struct ExportedTableData {
11
11
  std::string tableName;
12
12
  std::string fileName;
13
- // True for CSR indptr tables, which are computed (prefix-summed) rather than
14
- // plain-copied, and need the dedicated indptr export function in the planner.
15
- bool isIndptr = false;
16
13
  std::unique_ptr<BoundRegularQuery> regularQuery;
17
14
  std::vector<std::string> columnNames;
18
15
  std::vector<common::LogicalType> columnTypes;
@@ -213,11 +213,6 @@ struct PortDBConstants {
213
213
  static constexpr const char* SCHEMA_ONLY_OPTION = "SCHEMA_ONLY";
214
214
  static constexpr const char* EXPORT_FORMAT_OPTION = "FORMAT";
215
215
  static constexpr const char* DEFAULT_EXPORT_FORMAT_OPTION = "PARQUET";
216
- // IMPORT DATABASE option: when set to 'icebug-disk', the exported icedisk files are
217
- // mounted in place instead of being ingested into native tables (the default).
218
- static constexpr const char* IMPORT_STORAGE_FORMAT_OPTION = "STORAGE_FORMAT";
219
- // Parquet key-value metadata key written on every EXPORT DATABASE data file.
220
- static constexpr const char* ICEBUG_DISK_VERSION_OPTION = "icebug_disk_version";
221
216
  };
222
217
 
223
218
  struct WarningConstants {