ladybug 0.22.0.dev20261017__tar.gz → 0.22.0.dev20261024__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 (148) hide show
  1. sdist/PKG-INFO +1 -1
  2. sdist/ladybug-source/CMakeLists.txt +1 -1
  3. sdist/ladybug-source/scripts/choco-install.ps1 +26 -0
  4. sdist/ladybug-source/src/binder/bind/bind_projection_clause.cpp +6 -0
  5. sdist/ladybug-source/src/binder/bind_expression/bind_function_expression.cpp +95 -0
  6. sdist/ladybug-source/src/binder/bind_expression/bind_lambda_expression.cpp +7 -0
  7. sdist/ladybug-source/src/binder/ddl/property_definition.cpp +10 -0
  8. sdist/ladybug-source/src/binder/expression_binder.cpp +13 -8
  9. sdist/ladybug-source/src/c_api/database.cpp +3 -1
  10. sdist/ladybug-source/src/c_api/value.cpp +87 -15
  11. sdist/ladybug-source/src/catalog/catalog.cpp +6 -4
  12. sdist/ladybug-source/src/catalog/property_definition_collection.cpp +5 -1
  13. sdist/ladybug-source/src/common/task_system/task_scheduler.cpp +87 -11
  14. sdist/ladybug-source/src/common/types/types.cpp +5 -8
  15. sdist/ladybug-source/src/common/vector/value_vector.cpp +6 -0
  16. sdist/ladybug-source/src/function/gds/gds_utils.cpp +7 -7
  17. sdist/ladybug-source/src/function/table/cache_column.cpp +3 -1
  18. sdist/ladybug-source/src/graph/on_disk_graph.cpp +63 -0
  19. sdist/ladybug-source/src/include/binder/binder_scope.h +6 -0
  20. sdist/ladybug-source/src/include/binder/ddl/property_definition.h +1 -0
  21. sdist/ladybug-source/src/include/binder/expression_binder.h +5 -0
  22. sdist/ladybug-source/src/include/c_api/lbug.h +3 -0
  23. sdist/ladybug-source/src/include/catalog/catalog.h +4 -1
  24. sdist/ladybug-source/src/include/common/constants.h +10 -0
  25. sdist/ladybug-source/src/include/common/task_system/task.h +3 -2
  26. sdist/ladybug-source/src/include/common/task_system/task_scheduler.h +18 -4
  27. sdist/ladybug-source/src/include/graph/on_disk_graph.h +8 -0
  28. sdist/ladybug-source/src/include/main/attached_database.h +2 -3
  29. sdist/ladybug-source/src/include/main/database.h +4 -1
  30. sdist/ladybug-source/src/include/main/db_config.h +1 -0
  31. sdist/ladybug-source/src/include/main/prepared_statement.h +24 -0
  32. sdist/ladybug-source/src/include/main/settings.h +10 -0
  33. sdist/ladybug-source/src/include/optimizer/remove_unnecessary_join_optimizer.h +2 -1
  34. sdist/ladybug-source/src/include/planner/join_order/cost_model.h +4 -0
  35. sdist/ladybug-source/src/include/planner/operator/logical_hash_join.h +1 -1
  36. sdist/ladybug-source/src/include/planner/operator/logical_plan_util.h +2 -0
  37. sdist/ladybug-source/src/include/planner/operator/schema.h +4 -2
  38. sdist/ladybug-source/src/include/planner/planner.h +12 -0
  39. sdist/ladybug-source/src/include/processor/operator/ddl/drop.h +3 -1
  40. sdist/ladybug-source/src/include/processor/operator/persistent/reader/parquet/parquet_reader.h +5 -0
  41. sdist/ladybug-source/src/include/processor/processor_task.h +4 -0
  42. sdist/ladybug-source/src/include/processor/result/factorized_table.h +20 -1
  43. sdist/ladybug-source/src/include/storage/predicate/column_predicate.h +10 -0
  44. sdist/ladybug-source/src/include/storage/predicate/constant_predicate.h +5 -1
  45. sdist/ladybug-source/src/include/storage/predicate/filter_selection.h +32 -0
  46. sdist/ladybug-source/src/include/storage/predicate/null_predicate.h +2 -0
  47. sdist/ladybug-source/src/include/storage/table/chunked_node_group.h +1 -1
  48. sdist/ladybug-source/src/include/storage/table/column_chunk_data.h +8 -11
  49. sdist/ladybug-source/src/include/storage/table/csr_chunked_node_group.h +14 -0
  50. sdist/ladybug-source/src/include/storage/table/csr_node_group.h +12 -4
  51. sdist/ladybug-source/src/include/storage/table/ice_disk_node_table.h +2 -0
  52. sdist/ladybug-source/src/include/storage/table/node_group.h +11 -1
  53. sdist/ladybug-source/src/include/storage/table/node_table.h +2 -3
  54. sdist/ladybug-source/src/include/storage/table/rel_table.h +7 -0
  55. sdist/ladybug-source/src/include/storage/table/rel_table_data.h +17 -3
  56. sdist/ladybug-source/src/include/storage/table/version_info.h +8 -0
  57. sdist/ladybug-source/src/include/transaction/transaction.h +2 -1
  58. sdist/ladybug-source/src/main/attached_database.cpp +1 -1
  59. sdist/ladybug-source/src/main/client_context.cpp +103 -5
  60. sdist/ladybug-source/src/main/database.cpp +3 -2
  61. sdist/ladybug-source/src/main/db_config.cpp +4 -2
  62. sdist/ladybug-source/src/main/settings.cpp +15 -0
  63. sdist/ladybug-source/src/optimizer/count_rel_table_optimizer.cpp +3 -1
  64. sdist/ladybug-source/src/optimizer/remove_unnecessary_join_optimizer.cpp +83 -2
  65. sdist/ladybug-source/src/parser/transform/transform_expression.cpp +4 -4
  66. sdist/ladybug-source/src/planner/join_order/cost_model.cpp +4 -0
  67. sdist/ladybug-source/src/planner/operator/logical_plan_util.cpp +11 -0
  68. sdist/ladybug-source/src/planner/operator/scan/logical_query_primary_key_lookup.cpp +7 -4
  69. sdist/ladybug-source/src/planner/plan/plan_join_order.cpp +286 -0
  70. sdist/ladybug-source/src/processor/operator/aggregate/base_aggregate.cpp +1 -0
  71. sdist/ladybug-source/src/processor/operator/aggregate/base_aggregate_scan.cpp +1 -0
  72. sdist/ladybug-source/src/processor/operator/aggregate/hash_aggregate.cpp +4 -0
  73. sdist/ladybug-source/src/processor/operator/aggregate/hash_aggregate_scan.cpp +1 -0
  74. sdist/ladybug-source/src/processor/operator/aggregate/packed_filtered_count.cpp +4 -0
  75. sdist/ladybug-source/src/processor/operator/aggregate/simple_aggregate.cpp +2 -0
  76. sdist/ladybug-source/src/processor/operator/cross_product.cpp +1 -0
  77. sdist/ladybug-source/src/processor/operator/ddl/drop.cpp +9 -3
  78. sdist/ladybug-source/src/processor/operator/hash_join/hash_join_build.cpp +5 -0
  79. sdist/ladybug-source/src/processor/operator/hash_join/hash_join_probe.cpp +3 -0
  80. sdist/ladybug-source/src/processor/operator/intersect/intersect.cpp +6 -0
  81. sdist/ladybug-source/src/processor/operator/order_by/order_by.cpp +2 -0
  82. sdist/ladybug-source/src/processor/operator/order_by/order_by_scan.cpp +2 -0
  83. sdist/ladybug-source/src/processor/operator/order_by/top_k.cpp +2 -0
  84. sdist/ladybug-source/src/processor/operator/order_by/top_k_scanner.cpp +2 -0
  85. sdist/ladybug-source/src/processor/operator/path_property_probe.cpp +3 -0
  86. sdist/ladybug-source/src/processor/operator/persistent/copy_to.cpp +2 -0
  87. sdist/ladybug-source/src/processor/operator/persistent/merge.cpp +2 -0
  88. sdist/ladybug-source/src/processor/operator/persistent/node_batch_insert.cpp +59 -0
  89. sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/column_reader.cpp +1 -1
  90. sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/list_column_reader.cpp +4 -1
  91. sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/parquet_reader.cpp +4 -0
  92. sdist/ladybug-source/src/processor/operator/persistent/rel_batch_insert.cpp +17 -5
  93. sdist/ladybug-source/src/processor/operator/persistent/writer/parquet/parquet_writer.cpp +8 -4
  94. sdist/ladybug-source/src/processor/operator/physical_operator.cpp +15 -0
  95. sdist/ladybug-source/src/processor/operator/query_primary_key_lookup.cpp +2 -0
  96. sdist/ladybug-source/src/processor/operator/result_collector.cpp +5 -0
  97. sdist/ladybug-source/src/processor/operator/scan/count_rel_table.cpp +3 -87
  98. sdist/ladybug-source/src/processor/operator/scan/grouped_reachable_count.cpp +3 -0
  99. sdist/ladybug-source/src/processor/operator/scan/reachable_count.cpp +2 -0
  100. sdist/ladybug-source/src/processor/operator/scan/scan_table.cpp +1 -0
  101. sdist/ladybug-source/src/processor/operator/table_scan/union_all_scan.cpp +2 -0
  102. sdist/ladybug-source/src/processor/processor.cpp +11 -1
  103. sdist/ladybug-source/src/processor/processor_task.cpp +22 -0
  104. sdist/ladybug-source/src/processor/result/factorized_table.cpp +35 -14
  105. sdist/ladybug-source/src/storage/predicate/column_predicate.cpp +56 -1
  106. sdist/ladybug-source/src/storage/predicate/constant_predicate.cpp +42 -0
  107. sdist/ladybug-source/src/storage/predicate/null_predicate.cpp +8 -0
  108. sdist/ladybug-source/src/storage/table/arrow_node_table.cpp +16 -0
  109. sdist/ladybug-source/src/storage/table/chunked_node_group.cpp +114 -20
  110. sdist/ladybug-source/src/storage/table/column_chunk.cpp +2 -2
  111. sdist/ladybug-source/src/storage/table/column_chunk_data.cpp +19 -13
  112. sdist/ladybug-source/src/storage/table/csr_chunked_node_group.cpp +23 -4
  113. sdist/ladybug-source/src/storage/table/csr_node_group.cpp +297 -112
  114. sdist/ladybug-source/src/storage/table/ice_disk_node_table.cpp +22 -0
  115. sdist/ladybug-source/src/storage/table/node_group.cpp +10 -1
  116. sdist/ladybug-source/src/storage/table/node_table.cpp +46 -2
  117. sdist/ladybug-source/src/storage/table/rel_table.cpp +323 -56
  118. sdist/ladybug-source/src/storage/table/rel_table_data.cpp +17 -13
  119. sdist/ladybug-source/src/storage/table/version_info.cpp +83 -20
  120. sdist/ladybug-source/third_party/antlr4_runtime/src/misc/IntervalSet.cpp +8 -0
  121. sdist/ladybug-source/third_party/ports/antlr4_runtime/Makefile +14 -0
  122. sdist/ladybug-source/third_party/ports/antlr4_runtime/patches/antlr4-common-chrono.patch +13 -0
  123. sdist/ladybug-source/third_party/ports/antlr4_runtime/patches/interval-set-append.patch +22 -0
  124. sdist/ladybug-source/third_party/ports/antlr4_runtime/patches/parse-tree-equality.patch +14 -0
  125. sdist/ladybug-source/third_party/ports/antlr4_runtime/patches/vocabulary-deprecation.patch +29 -0
  126. sdist/ladybug-source/tools/python_api/src_cpp/include/cached_import/py_cached_import.h +2 -0
  127. sdist/ladybug-source/tools/python_api/src_cpp/include/cached_import/py_cached_modules.h +18 -0
  128. sdist/ladybug-source/tools/python_api/src_cpp/py_connection.cpp +146 -5
  129. sdist/ladybug-source/tools/python_api/src_py/_backend.py +26 -2
  130. sdist/ladybug-source/tools/python_api/src_py/_lbug_capi.py +3 -6
  131. sdist/ladybug-source/tools/python_api/src_py/connection.py +63 -7
  132. sdist/ladybug-source/tools/python_api/test/capi_xfails.py +7 -0
  133. sdist/ladybug-source/tools/python_api/test/test_backend_errors.py +34 -0
  134. sdist/ladybug-source/tools/python_api/test/test_connection_close.py +22 -2
  135. sdist/ladybug-source/tools/python_api/test/test_keyboard_interrupt.py +125 -0
  136. sdist/ladybug-source/tools/python_api/test/test_timeout.py +91 -0
  137. sdist/ladybug-source/tools/shell/test/test_shell_esc_edit.py +5 -1
  138. sdist/ladybug.egg-info/PKG-INFO +1 -1
  139. sdist/ladybug.egg-info/SOURCES.txt +2 -0
  140. sdist/pyproject.toml +1 -1
  141. sdist/src_py/_backend.py +26 -2
  142. sdist/src_py/_lbug_capi.py +3 -6
  143. sdist/src_py/connection.py +63 -7
  144. sdist/test/capi_xfails.py +7 -0
  145. sdist/test/test_backend_errors.py +34 -0
  146. sdist/test/test_connection_close.py +22 -2
  147. sdist/test/test_keyboard_interrupt.py +125 -0
  148. sdist/test/test_timeout.py +91 -0
sdist/PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ladybug
3
- Version: 0.22.0.dev20261017
3
+ Version: 0.22.0.dev20261024
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.22.0.20261017 LANGUAGES CXX C)
3
+ project(Lbug VERSION 0.22.0.20261024 LANGUAGES CXX C)
4
4
 
5
5
  option(SINGLE_THREADED "Single-threaded mode" FALSE)
6
6
  if(SINGLE_THREADED)
@@ -0,0 +1,26 @@
1
+ # Installs a Chocolatey package, retrying while the community feed is unavailable.
2
+ # choco can exit 0 after "Unable to find package", so success also requires the
3
+ # package to be listed as installed.
4
+ param(
5
+ [Parameter(Mandatory = $true)][string]$Package,
6
+ [string]$Version,
7
+ [string]$PackageParameters
8
+ )
9
+
10
+ $chocoArgs = @('install', $Package, '-y')
11
+ if ($Version) { $chocoArgs += "--version=$Version" }
12
+ if ($PackageParameters) { $chocoArgs += "--params=$PackageParameters" }
13
+
14
+ $attempts = 3
15
+ for ($attempt = 1; $attempt -le $attempts; $attempt++) {
16
+ & choco @chocoArgs
17
+ if ($LASTEXITCODE -eq 0 -and (choco list --exact $Package --limit-output)) {
18
+ exit 0
19
+ }
20
+ if ($attempt -lt $attempts) {
21
+ Write-Host "choco install $Package failed (attempt $attempt of $attempts), retrying in 60s"
22
+ Start-Sleep -Seconds 60
23
+ }
24
+ }
25
+ Write-Host "choco install $Package failed after $attempts attempts"
26
+ exit 1
@@ -1,5 +1,6 @@
1
1
  #include <algorithm>
2
2
  #include <unordered_set>
3
+ #include <utility>
3
4
 
4
5
  #include "binder/binder.h"
5
6
  #include "binder/expression/expression_util.h"
@@ -124,6 +125,9 @@ static expression_vector getAggregateExpressions(const std::shared_ptr<Expressio
124
125
  }
125
126
 
126
127
  BoundProjectionList Binder::bindProjectionList(const ProjectionBody& projectionBody) {
128
+ auto& config = expressionBinder.config;
129
+ auto outerAggregateNames = std::exchange(config.projectionAggregateNames, {});
130
+ auto outerBindingProjectionList = std::exchange(config.bindingProjectionList, true);
127
131
  expression_vector projectionExprs;
128
132
  std::vector<std::string> aliases;
129
133
  std::vector<const ParsedExpression*> parsedItems;
@@ -162,6 +166,8 @@ BoundProjectionList Binder::bindProjectionList(const ProjectionBody& projectionB
162
166
  parsedItems.push_back(parsedExpr.get());
163
167
  }
164
168
  }
169
+ config.bindingProjectionList = outerBindingProjectionList;
170
+ config.projectionAggregateNames = std::move(outerAggregateNames);
165
171
  return {std::move(projectionExprs), std::move(aliases), std::move(parsedItems)};
166
172
  }
167
173
 
@@ -1,9 +1,18 @@
1
+ #include <algorithm>
2
+
1
3
  #include "binder/binder.h"
2
4
  #include "binder/expression/aggregate_function_expression.h"
5
+ #include "binder/expression/expression_util.h"
6
+ #include "binder/expression/node_expression.h"
7
+ #include "binder/expression/property_expression.h"
3
8
  #include "binder/expression/scalar_function_expression.h"
4
9
  #include "binder/expression_binder.h"
10
+ #include "binder/expression_visitor.h"
5
11
  #include "catalog/catalog.h"
12
+ #include "catalog/catalog_entry/node_table_catalog_entry.h"
13
+ #include "common/enums/storage_format.h"
6
14
  #include "common/exception/binder.h"
15
+ #include "function/aggregate/count.h"
7
16
  #include "function/arithmetic/vector_arithmetic_functions.h"
8
17
  #include "function/built_in_function_utils.h"
9
18
  #include "function/cast/vector_cast_functions.h"
@@ -12,6 +21,8 @@
12
21
  #include "function/uuid/vector_uuid_functions.h"
13
22
  #include "parser/expression/parsed_expression_visitor.h"
14
23
  #include "parser/expression/parsed_function_expression.h"
24
+ #include "storage/storage_manager.h"
25
+ #include "storage/table/node_table.h"
15
26
  #include "transaction/transaction.h"
16
27
  #include <format>
17
28
 
@@ -23,6 +34,14 @@ using namespace lbug::catalog;
23
34
  namespace lbug {
24
35
  namespace binder {
25
36
 
37
+ static bool containsExpressionNamed(const Expression& expression, const std::string& uniqueName) {
38
+ if (expression.getUniqueName() == uniqueName) {
39
+ return true;
40
+ }
41
+ return std::ranges::any_of(ExpressionChildrenCollector::collectChildren(expression),
42
+ [&](const auto& child) { return containsExpressionNamed(*child, uniqueName); });
43
+ }
44
+
26
45
  std::shared_ptr<Expression> ExpressionBinder::bindFunctionExpression(const ParsedExpression& expr) {
27
46
  auto funcExpr = expr.constPtrCast<ParsedFunctionExpression>();
28
47
  auto functionName = funcExpr->getNormalizedFunctionName();
@@ -154,12 +173,76 @@ std::shared_ptr<Expression> ExpressionBinder::bindRewriteFunctionExpression(
154
173
  return function->rewriteFunc(input);
155
174
  }
156
175
 
176
+ // A primary key enforced by the table's primary-key index is unique, never null, and null exactly
177
+ // when the node is unbound, so COUNT over it equals COUNT over the internal ID. Counting the
178
+ // internal ID keeps the planner from scanning the node table only to read the key.
179
+ static std::shared_ptr<Expression> rewritePrimaryKeyToInternalID(const Expression& expr,
180
+ const BinderScope& scope, main::ClientContext* context) {
181
+ if (expr.expressionType != ExpressionType::PROPERTY) {
182
+ return nullptr;
183
+ }
184
+ auto& property = expr.constCast<PropertyExpression>();
185
+ if (!property.isSingleLabel() || !property.isPrimaryKey() ||
186
+ !scope.contains(property.getRawVariableName())) {
187
+ return nullptr;
188
+ }
189
+ auto variable = scope.getExpression(property.getRawVariableName());
190
+ if (!ExpressionUtil::isNodePattern(*variable) ||
191
+ variable->getUniqueName() != property.getVariableName()) {
192
+ return nullptr;
193
+ }
194
+ auto& node = variable->constCast<NodeExpression>();
195
+ if (node.getNumEntries() != 1) {
196
+ return nullptr;
197
+ }
198
+ auto* entry = node.getEntry(0);
199
+ const auto tableID = entry->getTableID();
200
+ if (entry->getType() != CatalogEntryType::NODE_TABLE_ENTRY ||
201
+ tableID != property.getSingleTableID()) {
202
+ return nullptr;
203
+ }
204
+ // Storage is found by table ID, and IDs repeat across attached databases, so only an entry
205
+ // of the active catalog backed by the active storage manager can be checked. Attached tables
206
+ // and partition children keep the property.
207
+ auto transaction = transaction::Transaction::Get(*context);
208
+ auto catalog = Catalog::Get(*context);
209
+ if (!catalog->containsTable(transaction, tableID) ||
210
+ catalog->getTableCatalogEntry(transaction, tableID) != entry) {
211
+ return nullptr;
212
+ }
213
+ // Icebug-disk and Arrow tables carry a default hash index that does not enforce the key of
214
+ // their files.
215
+ auto* nodeEntry = entry->ptrCast<NodeTableCatalogEntry>();
216
+ if (nodeEntry->getScanFunction().has_value() || nodeEntry->getReferencedEntry() != nullptr ||
217
+ !nodeEntry->getForeignDatabaseName().empty() || !nodeEntry->getStorage().empty() ||
218
+ nodeEntry->getStorageFormat() != StorageFormat::NONE) {
219
+ return nullptr;
220
+ }
221
+ auto* storageManager = storage::StorageManager::Get(*context);
222
+ if (!storageManager->containsTable(tableID)) {
223
+ return nullptr;
224
+ }
225
+ auto* table = dynamic_cast<storage::NodeTable*>(storageManager->getTable(tableID));
226
+ if (table == nullptr || table->tryGetPrimaryKeyIndex() == nullptr) {
227
+ return nullptr;
228
+ }
229
+ // The alias keeps the key in the aggregate's text, which names an unaliased result column.
230
+ std::shared_ptr<Expression> internalID = node.getInternalID()->copy();
231
+ internalID->setAlias(property.toString());
232
+ return internalID;
233
+ }
234
+
157
235
  std::shared_ptr<Expression> ExpressionBinder::bindAggregateFunctionExpression(
158
236
  const ParsedExpression& parsedExpression, const std::string& functionName, bool isDistinct) {
159
237
  std::vector<LogicalType> childrenTypes;
160
238
  expression_vector children;
161
239
  for (auto i = 0u; i < parsedExpression.getNumChildren(); ++i) {
162
240
  auto child = bindExpression(*parsedExpression.getChild(i));
241
+ if (functionName == CountFunction::name) {
242
+ if (auto internalID = rewritePrimaryKeyToInternalID(*child, binder->scope, context)) {
243
+ child = std::move(internalID);
244
+ }
245
+ }
163
246
  childrenTypes.push_back(child->dataType.copy());
164
247
  children.push_back(std::move(child));
165
248
  }
@@ -180,6 +263,18 @@ std::shared_ptr<Expression> ExpressionBinder::bindAggregateFunctionExpression(
180
263
  AggregateFunctionExpression::getUniqueName(function.name, children, function.isDistinct);
181
264
  if (children.empty()) {
182
265
  uniqueExpressionName = binder->getUniqueExpressionName(uniqueExpressionName);
266
+ } else if (config.bindingProjectionList &&
267
+ std::ranges::any_of(binder->scope.getExpressions(), [&](const auto& expr) {
268
+ return containsExpressionNamed(*expr, uniqueExpressionName);
269
+ })) {
270
+ // The name is structural, so this aggregate would share it, and its column, with the
271
+ // earlier clause's result, e.g. the group key c in WITH x, count(x) AS c RETURN c,
272
+ // count(x), or count(x) inside c in WITH x, count(x) + x AS c RETURN c, count(x) + x.
273
+ auto [entry, isNew] = config.projectionAggregateNames.try_emplace(uniqueExpressionName);
274
+ if (isNew) {
275
+ entry->second = binder->getUniqueExpressionName(uniqueExpressionName);
276
+ }
277
+ uniqueExpressionName = entry->second;
183
278
  }
184
279
  std::unique_ptr<FunctionBindData> bindData;
185
280
  if (function.bindFunc) {
@@ -1,3 +1,5 @@
1
+ #include <utility>
2
+
1
3
  #include "binder/binder.h"
2
4
  #include "binder/expression/expression_util.h"
3
5
  #include "binder/expression/lambda_expression.h"
@@ -20,8 +22,13 @@ void ExpressionBinder::bindLambdaExpression(const Expression& lambdaInput,
20
22
  for (auto& varName : parsedLambdaExpr.getVarNames()) {
21
23
  binder->createVariable(varName, listChildType);
22
24
  }
25
+ // The projection does not compute aggregates inside a lambda body, so they keep their
26
+ // structural names and read the earlier clause's column, as before.
27
+ auto& config = binder->getExpressionBinder()->config;
28
+ auto bindingProjectionList = std::exchange(config.bindingProjectionList, false);
23
29
  auto funcExpr =
24
30
  binder->getExpressionBinder()->bindExpression(*parsedLambdaExpr.getFunctionExpr());
31
+ config.bindingProjectionList = bindingProjectionList;
25
32
  binder->restoreScope(std::move(prevScope));
26
33
  boundLambdaExpr.cast(funcExpr->getDataType().copy());
27
34
  boundLambdaExpr.setFunctionExpr(std::move(funcExpr));
@@ -15,6 +15,16 @@ PropertyDefinition::PropertyDefinition(ColumnDefinition columnDefinition)
15
15
  defaultExpr = std::make_unique<ParsedLiteralExpression>(Value::createNullValue(), "NULL");
16
16
  }
17
17
 
18
+ bool PropertyDefinition::hasDefault() const {
19
+ if (defaultExpr == nullptr) {
20
+ return false;
21
+ }
22
+ if (defaultExpr->getExpressionType() == common::ExpressionType::LITERAL) {
23
+ return !defaultExpr->constCast<parser::ParsedLiteralExpression>().getValue().isNull();
24
+ }
25
+ return !defaultExpr->getRawName().empty();
26
+ }
27
+
18
28
  void PropertyDefinition::serialize(Serializer& serializer) const {
19
29
  serializer.serializeValue(columnDefinition.name);
20
30
  columnDefinition.type.serialize(serializer);
@@ -183,13 +183,7 @@ std::shared_ptr<Expression> ExpressionBinder::bindExpression(
183
183
  throw NotImplementedException(
184
184
  "bindExpression(" + ExpressionTypeUtil::toString(expressionType) + ").");
185
185
  }
186
- // A direct reference to a variable returns an expression of an earlier clause, simplified when
187
- // it was bound; it is not simplified again, as a new object would escape the binder's identity
188
- // check for expressions an earlier clause computed. References nested in a larger expression
189
- // rely on each simplifier returning an already simplified input unchanged.
190
- if (expressionType != ExpressionType::VARIABLE) {
191
- expression = simplifyExpression(expression);
192
- }
186
+ expression = simplifyExpression(expression);
193
187
  if (ConstantExpressionVisitor::needFold(*expression)) {
194
188
  return tryFoldExpression(expression);
195
189
  }
@@ -198,6 +192,11 @@ std::shared_ptr<Expression> ExpressionBinder::bindExpression(
198
192
 
199
193
  std::shared_ptr<Expression> ExpressionBinder::simplifyExpression(
200
194
  const std::shared_ptr<Expression>& expression) {
195
+ // Earlier clauses' expressions are recognised by identity, so a scope expression is never
196
+ // simplified again, and no simplifier returns a part of one in place of its parent.
197
+ if (binder->scope.contains(*expression)) {
198
+ return expression;
199
+ }
201
200
  switch (expression->expressionType) {
202
201
  case ExpressionType::AND:
203
202
  case ExpressionType::OR:
@@ -263,6 +262,12 @@ std::shared_ptr<Expression> ExpressionBinder::simplifyArithmeticExpression(
263
262
  if (expression->dataType != survivor->dataType) {
264
263
  return expression;
265
264
  }
265
+ // A projection names its item by setting the alias on the expression, so an identity over a
266
+ // scope expression stays as written: returning the operand would let the alias rename the
267
+ // variable, e.g. x in WITH x + 0 AS y, x.
268
+ if (binder->scope.contains(*survivor)) {
269
+ return expression;
270
+ }
266
271
  // Preserve the original expression name, mirroring foldExpression: without this,
267
272
  // e.g. RETURN t.age, t.age + 0 would produce two identically-named columns.
268
273
  if (expression->hasAlias()) {
@@ -286,7 +291,7 @@ std::shared_ptr<Expression> ExpressionBinder::simplifyBooleanExpression(
286
291
  if (isBoolLiteral(*child, false)) {
287
292
  return createLiteralExpression(Value(true));
288
293
  }
289
- if (child->expressionType == ExpressionType::NOT) {
294
+ if (child->expressionType == ExpressionType::NOT && !binder->scope.contains(*child)) {
290
295
  return child->getChild(0);
291
296
  }
292
297
  if (child != expression->getChild(0)) {
@@ -15,7 +15,8 @@ lbug_state lbug_database_init(const char* database_path, lbug_system_config conf
15
15
  auto systemConfig = SystemConfig(config.buffer_pool_size, config.max_num_threads,
16
16
  config.enable_compression, config.read_only, config.max_db_size, config.auto_checkpoint,
17
17
  config.checkpoint_threshold, true, config.throw_on_wal_replay_failure,
18
- config.enable_checksums, config.enable_multi_writes, config.enable_default_hash_index);
18
+ config.enable_checksums, config.enable_multi_writes, config.enable_default_hash_index,
19
+ config.physical_plan_pool_per_thread);
19
20
 
20
21
  #if defined(__APPLE__)
21
22
  systemConfig.threadQos = config.thread_qos;
@@ -54,6 +55,7 @@ lbug_system_config lbug_default_system_config() {
54
55
  cSystemConfig.enable_checksums = config.enableChecksums;
55
56
  cSystemConfig.enable_multi_writes = config.enableMultiWrites;
56
57
  cSystemConfig.enable_default_hash_index = config.enableDefaultHashIndex;
58
+ cSystemConfig.physical_plan_pool_per_thread = config.physicalPlanPoolPerThread;
57
59
  #if defined(__APPLE__)
58
60
  cSystemConfig.thread_qos = config.threadQos;
59
61
  #endif
@@ -3,6 +3,7 @@
3
3
  #include "c_api/helpers.h"
4
4
  #include "c_api/lbug.h"
5
5
  #include "common/constants.h"
6
+ #include "common/type_utils.h"
6
7
  #include "common/types/types.h"
7
8
  #include "common/types/value/nested.h"
8
9
  #include "common/types/value/node.h"
@@ -40,7 +41,7 @@ static bool canCopyValueForNested(const Value& value, const LogicalType& targetT
40
41
  if (value.getDataType() == targetType) {
41
42
  return true;
42
43
  }
43
- if (value.isNull() && value.getDataType().getLogicalTypeID() == LogicalTypeID::ANY) {
44
+ if (value.isNull()) {
44
45
  return true;
45
46
  }
46
47
  if (isEmptyNestedWithAny(value)) {
@@ -51,8 +52,45 @@ static bool canCopyValueForNested(const Value& value, const LogicalType& targetT
51
52
  targetType.getLogicalTypeID() == LogicalTypeID::FLOAT) {
52
53
  return true;
53
54
  }
54
- return value.getDataType().getLogicalTypeID() == targetType.getLogicalTypeID() &&
55
- value.getDataType().containsAny();
55
+ if (LogicalTypeUtils::isIntegral(value.getDataType()) &&
56
+ LogicalTypeUtils::isIntegral(targetType)) {
57
+ return true;
58
+ }
59
+ if (value.getDataType().getLogicalTypeID() != targetType.getLogicalTypeID()) {
60
+ return false;
61
+ }
62
+ const auto* nested = &value;
63
+ if (targetType.getLogicalTypeID() == LogicalTypeID::LIST) {
64
+ const auto& childType = ListType::getChildType(targetType);
65
+ for (auto i = 0u; i < NestedVal::getChildrenSize(nested); ++i) {
66
+ if (!canCopyValueForNested(*NestedVal::getChildVal(nested, i), childType)) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ }
72
+ if (targetType.getLogicalTypeID() == LogicalTypeID::MAP) {
73
+ const auto& keyType = MapType::getKeyType(targetType);
74
+ const auto& valueType = MapType::getValueType(targetType);
75
+ for (auto i = 0u; i < NestedVal::getChildrenSize(nested); ++i) {
76
+ auto* mapEntry = NestedVal::getChildVal(nested, i);
77
+ if (!canCopyValueForNested(*NestedVal::getChildVal(mapEntry, 0), keyType) ||
78
+ !canCopyValueForNested(*NestedVal::getChildVal(mapEntry, 1), valueType)) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+ if (targetType.getLogicalTypeID() == LogicalTypeID::STRUCT) {
85
+ for (auto i = 0u; i < NestedVal::getChildrenSize(nested); ++i) {
86
+ if (!canCopyValueForNested(*NestedVal::getChildVal(nested, i),
87
+ StructType::getFieldType(targetType, static_cast<struct_field_idx_t>(i)))) {
88
+ return false;
89
+ }
90
+ }
91
+ return true;
92
+ }
93
+ return value.getDataType().containsAny();
56
94
  }
57
95
 
58
96
  static std::string incompatibleParameterTypeMessage(const LogicalType& left,
@@ -69,28 +107,54 @@ static lbug_value* createCAPIValue(std::unique_ptr<Value> value) {
69
107
  return c_value;
70
108
  }
71
109
 
110
+ // The target comes from LogicalTypeUtils::tryGetMaxLogicalType, which joins two integer types only
111
+ // into one that holds both: implicit integer casts only widen, and a signed/unsigned pair becomes a
112
+ // wider signed type. So the cast below is exact. CreateListAllIntegerPairs pins this.
113
+ static std::unique_ptr<Value> castIntegerForNested(const Value& value,
114
+ const LogicalType& targetType) {
115
+ // A null of the target type keeps its exact type: SERIAL's default value is an INT64.
116
+ auto result = std::make_unique<Value>(Value::createNullValue(targetType));
117
+ result->setNull(false);
118
+ TypeUtils::visit(
119
+ value.getDataType(),
120
+ [&]<IntegerTypes SRC>(SRC) {
121
+ TypeUtils::visit(
122
+ targetType,
123
+ [&]<IntegerTypes DST>(DST) {
124
+ auto castValue = static_cast<DST>(value.getValue<SRC>());
125
+ result->copyFromRowLayout(reinterpret_cast<const uint8_t*>(&castValue));
126
+ },
127
+ [](auto) { UNREACHABLE_CODE; });
128
+ },
129
+ [](auto) { UNREACHABLE_CODE; });
130
+ return result;
131
+ }
132
+
72
133
  static std::unique_ptr<Value> copyValueForNested(const Value& value,
73
134
  const LogicalType& targetType) {
74
- if (value.isNull() && value.getDataType().getLogicalTypeID() == LogicalTypeID::ANY) {
135
+ if (value.getDataType() == targetType) {
136
+ return value.copy();
137
+ }
138
+ if (value.isNull()) {
75
139
  return std::make_unique<Value>(Value::createNullValue(targetType));
76
140
  }
77
- if (value.getDataType() != targetType && isEmptyNestedWithAny(value)) {
141
+ if (isEmptyNestedWithAny(value)) {
78
142
  return std::make_unique<Value>(Value::createDefaultValue(targetType));
79
143
  }
80
- if (value.getDataType() != targetType &&
81
- targetType.getLogicalTypeID() == LogicalTypeID::STRING) {
144
+ if (targetType.getLogicalTypeID() == LogicalTypeID::STRING) {
82
145
  return std::make_unique<Value>(Value::createValue<std::string>(value.toString()));
83
146
  }
84
- if (value.getDataType() != targetType &&
85
- targetType.getLogicalTypeID() == LogicalTypeID::DOUBLE) {
147
+ if (targetType.getLogicalTypeID() == LogicalTypeID::DOUBLE) {
86
148
  return std::make_unique<Value>(std::stod(value.toString()));
87
149
  }
88
- if (value.getDataType() != targetType &&
89
- targetType.getLogicalTypeID() == LogicalTypeID::FLOAT) {
150
+ if (targetType.getLogicalTypeID() == LogicalTypeID::FLOAT) {
90
151
  return std::make_unique<Value>(static_cast<float>(std::stof(value.toString())));
91
152
  }
92
- if (value.getDataType() != targetType &&
93
- value.getDataType().getLogicalTypeID() == LogicalTypeID::LIST &&
153
+ if (LogicalTypeUtils::isIntegral(value.getDataType()) &&
154
+ LogicalTypeUtils::isIntegral(targetType)) {
155
+ return castIntegerForNested(value, targetType);
156
+ }
157
+ if (value.getDataType().getLogicalTypeID() == LogicalTypeID::LIST &&
94
158
  targetType.getLogicalTypeID() == LogicalTypeID::LIST) {
95
159
  std::vector<std::unique_ptr<Value>> children;
96
160
  const auto& childType = ListType::getChildType(targetType);
@@ -100,8 +164,7 @@ static std::unique_ptr<Value> copyValueForNested(const Value& value,
100
164
  }
101
165
  return std::make_unique<Value>(targetType.copy(), std::move(children));
102
166
  }
103
- if (value.getDataType() != targetType &&
104
- value.getDataType().getLogicalTypeID() == LogicalTypeID::MAP &&
167
+ if (value.getDataType().getLogicalTypeID() == LogicalTypeID::MAP &&
105
168
  targetType.getLogicalTypeID() == LogicalTypeID::MAP) {
106
169
  std::vector<std::unique_ptr<Value>> children;
107
170
  const auto& keyType = MapType::getKeyType(targetType);
@@ -121,6 +184,15 @@ static std::unique_ptr<Value> copyValueForNested(const Value& value,
121
184
  }
122
185
  return std::make_unique<Value>(targetType.copy(), std::move(children));
123
186
  }
187
+ if (value.getDataType().getLogicalTypeID() == LogicalTypeID::STRUCT &&
188
+ targetType.getLogicalTypeID() == LogicalTypeID::STRUCT) {
189
+ std::vector<std::unique_ptr<Value>> children;
190
+ for (auto i = 0u; i < NestedVal::getChildrenSize(&value); ++i) {
191
+ children.push_back(copyValueForNested(*NestedVal::getChildVal(&value, i),
192
+ StructType::getFieldType(targetType, static_cast<struct_field_idx_t>(i))));
193
+ }
194
+ return std::make_unique<Value>(targetType.copy(), std::move(children));
195
+ }
124
196
  return value.copy();
125
197
  }
126
198
 
@@ -15,6 +15,7 @@
15
15
  #include "common/partition_routing_hook.h"
16
16
  #include "common/serializer/deserializer.h"
17
17
  #include "common/serializer/serializer.h"
18
+ #include "common/string_utils.h"
18
19
  #include "extension/extension_manager.h"
19
20
  #include "function/function_collection.h"
20
21
  #include "main/client_context.h"
@@ -587,7 +588,7 @@ std::vector<FunctionCatalogEntry*> Catalog::getFunctionEntries(
587
588
  }
588
589
 
589
590
  bool Catalog::containsMacro(const Transaction* transaction, const std::string& macroName) const {
590
- return macros->containsEntry(transaction, macroName);
591
+ return macros->containsEntry(transaction, StringUtils::getUpper(macroName));
591
592
  }
592
593
 
593
594
  function::ScalarMacroFunction* Catalog::getScalarMacroFunction(const Transaction* transaction,
@@ -627,11 +628,12 @@ std::vector<std::string> Catalog::getMacroNames(const Transaction* transaction)
627
628
  }
628
629
 
629
630
  void Catalog::dropMacro(Transaction* transaction, std::string& name) {
630
- if (!containsMacro(transaction, name)) {
631
+ auto macroName = StringUtils::getUpper(name);
632
+ if (!macros->containsEntry(transaction, macroName)) {
631
633
  throw CatalogException{std::format("Macro {} doesn't exist.", name)};
632
634
  }
633
- auto entry = getFunctionEntry(transaction, name);
634
- macros->dropEntry(transaction, name, entry->getOID());
635
+ auto entry = macros->getEntry(transaction, macroName);
636
+ macros->dropEntry(transaction, macroName, entry->getOID());
635
637
  }
636
638
 
637
639
  void Catalog::registerBuiltInFunctions() {
@@ -134,7 +134,11 @@ std::string PropertyDefinitionCollection::toCypher() const {
134
134
  if (typeStr.find("MAP") != std::string::npos) {
135
135
  StringUtils::replaceAll(typeStr, " ", ",");
136
136
  }
137
- ss << common::StringUtils::quoteIdentifier(def.getName()) << " " << typeStr << ",";
137
+ ss << common::StringUtils::quoteIdentifier(def.getName()) << " " << typeStr;
138
+ if (dataType.getLogicalTypeID() != LogicalTypeID::SERIAL && def.hasDefault()) {
139
+ ss << " DEFAULT " << def.getDefaultExpressionName();
140
+ }
141
+ ss << ",";
138
142
  }
139
143
  return ss.str();
140
144
  }