ladybug 0.22.0.dev20261022__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.
- sdist/PKG-INFO +1 -1
- sdist/ladybug-source/CMakeLists.txt +1 -1
- sdist/ladybug-source/src/binder/ddl/property_definition.cpp +10 -0
- sdist/ladybug-source/src/c_api/value.cpp +87 -15
- sdist/ladybug-source/src/catalog/catalog.cpp +6 -4
- sdist/ladybug-source/src/catalog/property_definition_collection.cpp +5 -1
- sdist/ladybug-source/src/common/vector/value_vector.cpp +6 -0
- sdist/ladybug-source/src/function/table/cache_column.cpp +3 -1
- sdist/ladybug-source/src/include/binder/ddl/property_definition.h +1 -0
- sdist/ladybug-source/src/include/catalog/catalog.h +4 -1
- sdist/ladybug-source/src/include/common/constants.h +2 -2
- sdist/ladybug-source/src/include/optimizer/remove_unnecessary_join_optimizer.h +2 -1
- sdist/ladybug-source/src/include/planner/join_order/cost_model.h +2 -3
- sdist/ladybug-source/src/include/planner/operator/logical_hash_join.h +1 -1
- sdist/ladybug-source/src/include/processor/operator/ddl/drop.h +3 -1
- sdist/ladybug-source/src/include/processor/operator/persistent/reader/parquet/parquet_reader.h +5 -0
- sdist/ladybug-source/src/include/processor/result/factorized_table.h +3 -0
- sdist/ladybug-source/src/include/storage/table/csr_chunked_node_group.h +14 -0
- sdist/ladybug-source/src/include/storage/table/csr_node_group.h +12 -4
- sdist/ladybug-source/src/include/storage/table/node_group.h +8 -2
- sdist/ladybug-source/src/include/storage/table/node_table.h +2 -3
- sdist/ladybug-source/src/include/storage/table/rel_table_data.h +17 -3
- sdist/ladybug-source/src/include/storage/table/version_info.h +3 -1
- sdist/ladybug-source/src/optimizer/remove_unnecessary_join_optimizer.cpp +83 -2
- sdist/ladybug-source/src/parser/transform/transform_expression.cpp +4 -4
- sdist/ladybug-source/src/planner/join_order/cost_model.cpp +2 -3
- sdist/ladybug-source/src/planner/plan/plan_join_order.cpp +14 -7
- sdist/ladybug-source/src/processor/operator/ddl/drop.cpp +6 -3
- sdist/ladybug-source/src/processor/operator/persistent/node_batch_insert.cpp +59 -0
- sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/column_reader.cpp +1 -1
- sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/list_column_reader.cpp +4 -1
- sdist/ladybug-source/src/processor/operator/persistent/reader/parquet/parquet_reader.cpp +4 -0
- sdist/ladybug-source/src/processor/operator/persistent/rel_batch_insert.cpp +17 -5
- sdist/ladybug-source/src/processor/result/factorized_table.cpp +7 -2
- sdist/ladybug-source/src/storage/predicate/column_predicate.cpp +16 -3
- sdist/ladybug-source/src/storage/table/chunked_node_group.cpp +19 -9
- sdist/ladybug-source/src/storage/table/column_chunk.cpp +2 -2
- sdist/ladybug-source/src/storage/table/csr_chunked_node_group.cpp +23 -4
- sdist/ladybug-source/src/storage/table/csr_node_group.cpp +297 -112
- sdist/ladybug-source/src/storage/table/node_group.cpp +10 -1
- sdist/ladybug-source/src/storage/table/node_table.cpp +7 -2
- sdist/ladybug-source/src/storage/table/rel_table.cpp +19 -9
- sdist/ladybug-source/src/storage/table/rel_table_data.cpp +17 -13
- sdist/ladybug-source/src/storage/table/table.cpp +0 -2
- sdist/ladybug-source/src/storage/table/version_info.cpp +8 -4
- sdist/ladybug-source/tools/python_api/src_cpp/include/cached_import/py_cached_import.h +2 -0
- sdist/ladybug-source/tools/python_api/src_cpp/include/cached_import/py_cached_modules.h +18 -0
- sdist/ladybug-source/tools/python_api/src_cpp/py_connection.cpp +146 -5
- sdist/ladybug-source/tools/python_api/src_py/_backend.py +26 -2
- sdist/ladybug-source/tools/python_api/src_py/_lbug_capi.py +3 -6
- sdist/ladybug-source/tools/python_api/src_py/connection.py +63 -7
- sdist/ladybug-source/tools/python_api/test/capi_xfails.py +7 -0
- sdist/ladybug-source/tools/python_api/test/test_backend_errors.py +34 -0
- sdist/ladybug-source/tools/python_api/test/test_connection_close.py +22 -2
- sdist/ladybug-source/tools/python_api/test/test_keyboard_interrupt.py +125 -0
- sdist/ladybug-source/tools/python_api/test/test_timeout.py +91 -0
- sdist/ladybug.egg-info/PKG-INFO +1 -1
- sdist/ladybug.egg-info/SOURCES.txt +2 -0
- sdist/pyproject.toml +1 -1
- sdist/src_py/_backend.py +26 -2
- sdist/src_py/_lbug_capi.py +3 -6
- sdist/src_py/connection.py +63 -7
- sdist/test/capi_xfails.py +7 -0
- sdist/test/test_backend_errors.py +34 -0
- sdist/test/test_connection_close.py +22 -2
- sdist/test/test_keyboard_interrupt.py +125 -0
- sdist/test/test_timeout.py +91 -0
sdist/PKG-INFO
CHANGED
|
@@ -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);
|
|
@@ -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()
|
|
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
|
-
|
|
55
|
-
|
|
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.
|
|
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 (
|
|
141
|
+
if (isEmptyNestedWithAny(value)) {
|
|
78
142
|
return std::make_unique<Value>(Value::createDefaultValue(targetType));
|
|
79
143
|
}
|
|
80
|
-
if (
|
|
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 (
|
|
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 (
|
|
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()
|
|
93
|
-
|
|
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()
|
|
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
|
-
|
|
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 =
|
|
634
|
-
macros->dropEntry(transaction,
|
|
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
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include <numeric>
|
|
4
4
|
|
|
5
|
+
#include "common/assert.h"
|
|
5
6
|
#include "common/exception/runtime.h"
|
|
6
7
|
#include "common/null_buffer.h"
|
|
7
8
|
#include "common/serializer/deserializer.h"
|
|
@@ -26,6 +27,11 @@ ValueVector::ValueVector(LogicalType dataType, storage::MemoryManager* memoryMan
|
|
|
26
27
|
"Data type is expected to be resolved during binding.");
|
|
27
28
|
// LCOV_EXCL_STOP
|
|
28
29
|
}
|
|
30
|
+
// Strings too long to inline live in overflow blocks from the memory manager; nested types
|
|
31
|
+
// pass it to their child vectors, so the check covers strings at any depth.
|
|
32
|
+
DASSERT(
|
|
33
|
+
memoryManager != nullptr || (this->dataType.getPhysicalType() != PhysicalTypeID::STRING &&
|
|
34
|
+
this->dataType.getPhysicalType() != PhysicalTypeID::JSON));
|
|
29
35
|
numBytesPerValue = getDataTypeSize(this->dataType);
|
|
30
36
|
initializeValueBuffer();
|
|
31
37
|
auxiliaryBuffer = AuxiliaryBufferFactory::getAuxiliaryBuffer(this->dataType, memoryManager);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include "function/table/bind_input.h"
|
|
7
7
|
#include "function/table/simple_table_function.h"
|
|
8
8
|
#include "processor/execution_context.h"
|
|
9
|
+
#include "storage/buffer_manager/memory_manager.h"
|
|
9
10
|
#include "storage/local_cached_column.h"
|
|
10
11
|
#include "storage/partition_storage_registry.h"
|
|
11
12
|
#include "storage/storage_manager.h"
|
|
@@ -98,7 +99,8 @@ struct CacheArrayColumnLocalState final : TableFuncLocalState {
|
|
|
98
99
|
->cast<storage::NodeTable>();
|
|
99
100
|
dataChunk.insert(0, std::make_shared<ValueVector>(LogicalType::INTERNAL_ID()));
|
|
100
101
|
dataChunk.insert(1,
|
|
101
|
-
std::make_shared<ValueVector>(table.getColumn(columnID).getDataType().copy()
|
|
102
|
+
std::make_shared<ValueVector>(table.getColumn(columnID).getDataType().copy(),
|
|
103
|
+
storage::MemoryManager::Get(context)));
|
|
102
104
|
std::vector<column_id_t> columnIDs;
|
|
103
105
|
columnIDs.push_back(columnID);
|
|
104
106
|
scanState =
|
|
@@ -33,6 +33,7 @@ struct LBUG_API PropertyDefinition {
|
|
|
33
33
|
std::string getName() const { return columnDefinition.name; }
|
|
34
34
|
const common::LogicalType& getType() const { return columnDefinition.type; }
|
|
35
35
|
std::string getDefaultExpressionName() const { return defaultExpr->getRawName(); }
|
|
36
|
+
bool hasDefault() const;
|
|
36
37
|
void rename(const std::string& newName) { columnDefinition.name = newName; }
|
|
37
38
|
|
|
38
39
|
void serialize(common::Serializer& serializer) const;
|
|
@@ -190,9 +190,11 @@ public:
|
|
|
190
190
|
|
|
191
191
|
// ----------------------------- Macro ----------------------------
|
|
192
192
|
|
|
193
|
-
//
|
|
193
|
+
// Macro names are stored uppercased (see bindCreateMacro). containsMacro and dropMacro
|
|
194
|
+
// uppercase the given name the same way; the catalog set itself only folds ASCII letters.
|
|
194
195
|
bool containsMacro(const transaction::Transaction* transaction,
|
|
195
196
|
const std::string& macroName) const;
|
|
197
|
+
// Takes the stored (uppercased) name.
|
|
196
198
|
void addScalarMacroFunction(transaction::Transaction* transaction, std::string name,
|
|
197
199
|
std::unique_ptr<function::ScalarMacroFunction> macro);
|
|
198
200
|
ScalarMacroCatalogEntry* getScalarMacroCatalogEntry(const transaction::Transaction* transaction,
|
|
@@ -200,6 +202,7 @@ public:
|
|
|
200
202
|
void dropMacroEntry(transaction::Transaction* transaction, const lbug::common::oid_t macroID);
|
|
201
203
|
void dropMacroEntry(transaction::Transaction* transaction,
|
|
202
204
|
const ScalarMacroCatalogEntry* entry);
|
|
205
|
+
// Takes the stored (uppercased) name.
|
|
203
206
|
function::ScalarMacroFunction* getScalarMacroFunction(
|
|
204
207
|
const transaction::Transaction* transaction, const std::string& name) const;
|
|
205
208
|
std::vector<std::string> getMacroNames(const transaction::Transaction* transaction) const;
|
|
@@ -178,8 +178,8 @@ struct PlannerKnobs {
|
|
|
178
178
|
// cost vs per-output-pair cost is ~1000:1, so seed cardinality dominates direction choice.
|
|
179
179
|
static constexpr uint64_t RECURSIVE_EXTEND_SOURCE_COST = 1000;
|
|
180
180
|
// Lookup join (per-tuple PK lookup instead of scan + hash join) pays off only when the
|
|
181
|
-
// outer side is small: each outer row costs one index probe/payload fetch, so cap the
|
|
182
|
-
// estimated outer
|
|
181
|
+
// outer side is small: each flat outer row costs one index probe/payload fetch, so cap the
|
|
182
|
+
// estimated number of flat outer rows this rewrite applies to (Q1's Person fetch: ~9 rows).
|
|
183
183
|
static constexpr uint64_t LOOKUP_JOIN_MAX_OUTER_CARDINALITY = 1024;
|
|
184
184
|
// Lookup join eligibility additionally requires the outer plan to be free of
|
|
185
185
|
// unmodeled fan-out (no recursion/aggregation/etc.) and caps the extend chain
|
|
@@ -15,7 +15,8 @@ namespace optimizer {
|
|
|
15
15
|
* E(e) S(b)
|
|
16
16
|
* |
|
|
17
17
|
* S(a)
|
|
18
|
-
* This optimizer prunes such redundant joins.
|
|
18
|
+
* This optimizer prunes such redundant joins. A join whose key an OPTIONAL MATCH can leave
|
|
19
|
+
* NULL is not redundant: it drops the rows with a NULL key, so it stays.
|
|
19
20
|
*/
|
|
20
21
|
class RemoveUnnecessaryJoinOptimizer : public LogicalOperatorVisitor {
|
|
21
22
|
public:
|
|
@@ -22,11 +22,10 @@ public:
|
|
|
22
22
|
const LogicalPlan& probe, const LogicalPlan& build);
|
|
23
23
|
static uint64_t computeIntersectCost(const LogicalPlan& probePlan,
|
|
24
24
|
const std::vector<LogicalPlan>& buildPlans);
|
|
25
|
-
// Lookup join: one PK probe/payload fetch per outer row on top of the outer plan.
|
|
25
|
+
// Lookup join: one PK probe/payload fetch per flat outer row on top of the outer plan.
|
|
26
26
|
// Like the INL-join precedent, this only pays off for small outer cardinalities,
|
|
27
27
|
// which callers enforce via PlannerKnobs::LOOKUP_JOIN_MAX_OUTER_CARDINALITY.
|
|
28
|
-
static uint64_t computeLookupJoinCost(const LogicalPlan& outerPlan,
|
|
29
|
-
cardinality_t outerCardinality);
|
|
28
|
+
static uint64_t computeLookupJoinCost(const LogicalPlan& outerPlan, cardinality_t numProbes);
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
} // namespace planner
|
|
@@ -37,7 +37,7 @@ public:
|
|
|
37
37
|
static binder::expression_vector getJoinNodeIDs(
|
|
38
38
|
const std::vector<join_condition_t>& joinConditions);
|
|
39
39
|
|
|
40
|
-
std::vector<join_condition_t
|
|
40
|
+
const std::vector<join_condition_t>& getJoinConditions() const { return joinConditions; }
|
|
41
41
|
common::JoinType getJoinType() const { return joinType; }
|
|
42
42
|
bool hasMark() const { return mark != nullptr; }
|
|
43
43
|
std::shared_ptr<binder::Expression> getMark() const { return mark; }
|
|
@@ -42,7 +42,9 @@ private:
|
|
|
42
42
|
void dropMacro(const main::ClientContext* context);
|
|
43
43
|
void dropGraph(const main::ClientContext* context);
|
|
44
44
|
void dropIndex(const main::ClientContext* context);
|
|
45
|
-
|
|
45
|
+
// Returns false if the macro is missing and IF EXISTS was given; throws if it is missing
|
|
46
|
+
// otherwise.
|
|
47
|
+
bool handleMacroExistence(const main::ClientContext* context);
|
|
46
48
|
void dropRelGroup(const main::ClientContext* context);
|
|
47
49
|
|
|
48
50
|
private:
|
sdist/ladybug-source/src/include/processor/operator/persistent/reader/parquet/parquet_reader.h
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
#include "resizable_buffer.h"
|
|
12
12
|
|
|
13
13
|
namespace lbug {
|
|
14
|
+
namespace storage {
|
|
15
|
+
class MemoryManager;
|
|
16
|
+
} // namespace storage
|
|
17
|
+
|
|
14
18
|
namespace processor {
|
|
15
19
|
|
|
16
20
|
struct ParquetReaderPrefetchConfig {
|
|
@@ -58,6 +62,7 @@ public:
|
|
|
58
62
|
const common::LogicalType& getColumnType(uint32_t idx) const { return columnTypes[idx]; }
|
|
59
63
|
|
|
60
64
|
lbug_parquet::format::FileMetaData* getMetadata() const { return metadata.get(); }
|
|
65
|
+
storage::MemoryManager* getMemoryManager() const;
|
|
61
66
|
|
|
62
67
|
private:
|
|
63
68
|
std::unique_ptr<lbug_apache::thrift::protocol::TProtocol> createThriftProtocol(
|
|
@@ -45,6 +45,9 @@ public:
|
|
|
45
45
|
}
|
|
46
46
|
return data;
|
|
47
47
|
}
|
|
48
|
+
// Gives back the claimed bytes past the first numBytes. They may still hold old tuples, so
|
|
49
|
+
// they are zeroed again if claimed later.
|
|
50
|
+
void truncate(uint64_t numBytes);
|
|
48
51
|
void resetNumTuplesAndFreeSize();
|
|
49
52
|
void resetToZero();
|
|
50
53
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <algorithm>
|
|
4
|
+
#include <utility>
|
|
5
|
+
#include <vector>
|
|
4
6
|
|
|
5
7
|
#include "storage/enums/residency_state.h"
|
|
6
8
|
#include "storage/table/chunked_node_group.h"
|
|
@@ -62,6 +64,18 @@ struct LBUG_API InMemChunkedCSRHeader {
|
|
|
62
64
|
bool randomLookup = false;
|
|
63
65
|
// Logical header index stored at position 0. A full-group header uses 0.
|
|
64
66
|
common::offset_t indexBase = 0;
|
|
67
|
+
// Scan-private cache: a full on-disk header, or the [start, end) index ranges last decoded
|
|
68
|
+
// for a bound-node batch. Nodes at or past headerLimit, the on-disk header size, have no
|
|
69
|
+
// lists and read as empty. Checkpoint headers leave these at the defaults.
|
|
70
|
+
bool wholeHeader = false;
|
|
71
|
+
common::offset_t headerLimit = 0;
|
|
72
|
+
std::vector<std::pair<common::offset_t, common::offset_t>> decodedRanges;
|
|
73
|
+
|
|
74
|
+
// Scans read the header only for nodes of the current bound-node batch (plus the group's end
|
|
75
|
+
// after a whole-header decode), and CSRNodeGroup::initializeScanState covers each batch by
|
|
76
|
+
// the decode it runs for it or by checking these ranges. The getters assert it; an uncovered
|
|
77
|
+
// read returns stale bytes from a gap, or the last entry at or past numValues.
|
|
78
|
+
bool coversNode(common::offset_t nodeOffset) const;
|
|
65
79
|
|
|
66
80
|
InMemChunkedCSRHeader(MemoryManager& memoryManager, bool enableCompression, uint64_t capacity);
|
|
67
81
|
InMemChunkedCSRHeader(std::unique_ptr<ColumnChunkData> offset,
|
|
@@ -145,7 +145,8 @@ struct CSRNodeGroupScanState final : NodeGroupScanState {
|
|
|
145
145
|
// The number of rows (i.e., rels) that have been scanned so far in current node group.
|
|
146
146
|
common::row_idx_t numCachedRows;
|
|
147
147
|
common::row_idx_t nextCachedRowToScan;
|
|
148
|
-
// Entries and spans
|
|
148
|
+
// Entries decoded and partial decodes (spans or cluster sets) so far for the current node
|
|
149
|
+
// group's header.
|
|
149
150
|
common::offset_t numHeaderEntriesDecoded = 0;
|
|
150
151
|
common::idx_t numHeaderSpansDecoded = 0;
|
|
151
152
|
|
|
@@ -178,6 +179,12 @@ struct CSRNodeGroupScanState final : NodeGroupScanState {
|
|
|
178
179
|
// node vector's chunk state, which holds the served parents in its selection vector. See
|
|
179
180
|
// docs/multi_parent_lifetime.md.
|
|
180
181
|
bool tryScanCachedTuplesPacked(RelTableScanState& tableScanState);
|
|
182
|
+
|
|
183
|
+
// Copies rows into the cache starting in the current bound node's list and running through
|
|
184
|
+
// the next lists up to a gap (see refillEnd), or moves past that node when its list is empty
|
|
185
|
+
// or already scanned. False once the batch has no rows left.
|
|
186
|
+
bool refillCache(const transaction::Transaction* transaction,
|
|
187
|
+
RelTableScanState& tableScanState);
|
|
181
188
|
};
|
|
182
189
|
|
|
183
190
|
struct CSRNodeGroupCheckpointState final : NodeGroupCheckpointState {
|
|
@@ -264,6 +271,7 @@ public:
|
|
|
264
271
|
return NodeGroup::hasRows() ||
|
|
265
272
|
(persistentChunkGroup && persistentChunkGroup->getNumRows() != 0);
|
|
266
273
|
}
|
|
274
|
+
void rollbackInsert(common::row_idx_t startRow) override;
|
|
267
275
|
|
|
268
276
|
ChunkedNodeGroup* getPersistentChunkedGroup() const { return persistentChunkGroup.get(); }
|
|
269
277
|
const CSRIndex* getCSRIndex() const { return csrIndex.get(); }
|
|
@@ -286,8 +294,8 @@ private:
|
|
|
286
294
|
static void initScanForCommittedInMem(RelTableScanState& relScanState,
|
|
287
295
|
CSRNodeGroupScanState& nodeGroupScanState);
|
|
288
296
|
|
|
289
|
-
void updateCSRIndex(
|
|
290
|
-
common::length_t length)
|
|
297
|
+
static void updateCSRIndex(CSRIndex& index, common::offset_t boundNodeOffsetInGroup,
|
|
298
|
+
common::row_idx_t startRow, common::length_t length);
|
|
291
299
|
|
|
292
300
|
NodeGroupScanResult scanCommittedPersistent(const transaction::Transaction* transaction,
|
|
293
301
|
RelTableScanState& tableState, CSRNodeGroupScanState& nodeGroupScanState) const;
|
|
@@ -313,7 +321,7 @@ private:
|
|
|
313
321
|
const transaction::Transaction* transaction, CSRNodeGroupScanPin& pin) const;
|
|
314
322
|
void pruneRetainedSnapshotsNoLock(const std::vector<common::transaction_t>& openStartTSs);
|
|
315
323
|
bool needsRetainedSnapshotNoLock(const std::vector<common::transaction_t>& openStartTSs) const;
|
|
316
|
-
|
|
324
|
+
CSRIndex& getCSRIndexForAppend(const common::UniqLock& lock);
|
|
317
325
|
|
|
318
326
|
void checkpointInMemOnly(const common::UniqLock& lock, NodeGroupCheckpointState& state);
|
|
319
327
|
void checkpointInMemAndOnDisk(const common::UniqLock& lock, NodeGroupCheckpointState& state);
|
|
@@ -25,7 +25,8 @@ struct NodeGroupScanState {
|
|
|
25
25
|
// Index of committed but not yet checkpointed chunked group to scan.
|
|
26
26
|
common::idx_t chunkedGroupIdx = 0;
|
|
27
27
|
// Batches to scan without filtering on predicates first, after a batch where that kept too
|
|
28
|
-
// many rows, and the length of the last such pause.
|
|
28
|
+
// many rows, and the length of the last such pause. Reset on entering a node group, not a
|
|
29
|
+
// chunked group: an in-memory chunked group holds a single batch.
|
|
29
30
|
uint8_t numBatchesWithoutEarlyFilter = 0;
|
|
30
31
|
uint8_t earlyFilterBackoff = 0;
|
|
31
32
|
common::row_idx_t nextRowToScan = 0;
|
|
@@ -37,6 +38,11 @@ struct NodeGroupScanState {
|
|
|
37
38
|
explicit NodeGroupScanState() {}
|
|
38
39
|
explicit NodeGroupScanState(common::idx_t numChunks) { chunkStates.resize(numChunks); }
|
|
39
40
|
|
|
41
|
+
void resetEarlyFilterBackoff() {
|
|
42
|
+
numBatchesWithoutEarlyFilter = 0;
|
|
43
|
+
earlyFilterBackoff = 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
virtual ~NodeGroupScanState() = default;
|
|
41
47
|
DELETE_COPY_DEFAULT_MOVE(NodeGroupScanState);
|
|
42
48
|
|
|
@@ -189,7 +195,7 @@ public:
|
|
|
189
195
|
|
|
190
196
|
void applyFuncToChunkedGroups(version_record_handler_op_t func, common::row_idx_t startRow,
|
|
191
197
|
common::row_idx_t numRows, common::transaction_t commitTS) const;
|
|
192
|
-
void rollbackInsert(common::row_idx_t startRow);
|
|
198
|
+
virtual void rollbackInsert(common::row_idx_t startRow);
|
|
193
199
|
void reclaimStorage(PageAllocator& pageAllocator) const;
|
|
194
200
|
virtual void reclaimStorage(PageAllocator& pageAllocator, const common::UniqLock& lock) const;
|
|
195
201
|
|
|
@@ -274,9 +274,8 @@ private:
|
|
|
274
274
|
std::optional<uint64_t> queryID = std::nullopt) const;
|
|
275
275
|
|
|
276
276
|
private:
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
// unique_lock during the brief column swap.
|
|
277
|
+
// Guards the `columns` vector: checkpoint's column vacuum swaps it and ALTER TABLE ADD grows
|
|
278
|
+
// it under a unique lock; readers take a shared lock.
|
|
280
279
|
mutable std::shared_mutex schemaMtx;
|
|
281
280
|
std::vector<std::unique_ptr<Column>> columns;
|
|
282
281
|
std::unique_ptr<NodeGroupCollection> nodeGroups;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <cmath>
|
|
4
|
+
#include <shared_mutex>
|
|
4
5
|
|
|
5
6
|
#include "common/enums/rel_direction.h"
|
|
6
7
|
#include "common/enums/rel_multiplicity.h"
|
|
@@ -71,12 +72,20 @@ public:
|
|
|
71
72
|
bool checkIfNodeHasRels(transaction::Transaction* transaction,
|
|
72
73
|
common::ValueVector* srcNodeIDVector) const;
|
|
73
74
|
|
|
74
|
-
Column* getNbrIDColumn() const { return
|
|
75
|
+
Column* getNbrIDColumn() const { return getColumn(NBR_ID_COLUMN_ID); }
|
|
75
76
|
Column* getCSROffsetColumn() const { return csrHeaderColumns.offset.get(); }
|
|
76
77
|
Column* getCSRLengthColumn() const { return csrHeaderColumns.length.get(); }
|
|
77
|
-
common::column_id_t getNumColumns() const {
|
|
78
|
-
|
|
78
|
+
common::column_id_t getNumColumns() const {
|
|
79
|
+
std::shared_lock lck{columnsMtx};
|
|
80
|
+
return columns.size();
|
|
81
|
+
}
|
|
82
|
+
Column* getColumn(common::column_id_t columnID) const {
|
|
83
|
+
std::shared_lock lck{columnsMtx};
|
|
84
|
+
DASSERT(columnID < columns.size());
|
|
85
|
+
return columns[columnID].get();
|
|
86
|
+
}
|
|
79
87
|
std::vector<const Column*> getColumns() const {
|
|
88
|
+
std::shared_lock lck{columnsMtx};
|
|
80
89
|
std::vector<const Column*> result;
|
|
81
90
|
result.reserve(columns.size());
|
|
82
91
|
for (const auto& column : columns) {
|
|
@@ -139,6 +148,7 @@ private:
|
|
|
139
148
|
}
|
|
140
149
|
|
|
141
150
|
std::vector<common::LogicalType> getColumnTypes() const {
|
|
151
|
+
std::shared_lock lck{columnsMtx};
|
|
142
152
|
std::vector<common::LogicalType> types;
|
|
143
153
|
types.reserve(columns.size());
|
|
144
154
|
for (const auto& column : columns) {
|
|
@@ -161,6 +171,10 @@ private:
|
|
|
161
171
|
std::unique_ptr<NodeGroupCollection> nodeGroups;
|
|
162
172
|
|
|
163
173
|
CSRHeaderColumns csrHeaderColumns;
|
|
174
|
+
// Guards the `columns` vector, as NodeTable::schemaMtx guards the node table's: checkpoint
|
|
175
|
+
// swaps it and ALTER TABLE ADD grows it under a unique lock while other connections' scans
|
|
176
|
+
// read it under a shared lock. A Column* stays valid for every column a checkpoint keeps.
|
|
177
|
+
mutable std::shared_mutex columnsMtx;
|
|
164
178
|
std::vector<std::unique_ptr<Column>> columns;
|
|
165
179
|
|
|
166
180
|
PersistentVersionRecordHandler persistentVersionRecordHandler;
|
|
@@ -32,6 +32,7 @@ public:
|
|
|
32
32
|
void clearVectorInfo(common::idx_t vectorIdx);
|
|
33
33
|
|
|
34
34
|
bool hasDeletions() const;
|
|
35
|
+
// Rows whose delete the transaction can see. Insert stamps are not checked.
|
|
35
36
|
common::row_idx_t getNumDeletions(const transaction::Transaction* transaction,
|
|
36
37
|
common::row_idx_t startRow, common::length_t numRows) const;
|
|
37
38
|
common::row_idx_t getNumVisibleRows(const transaction::Transaction* transaction,
|
|
@@ -39,7 +40,8 @@ public:
|
|
|
39
40
|
// Rows whose insert the transaction can see, deleted or not.
|
|
40
41
|
common::row_idx_t getNumInsertedRows(const transaction::Transaction* transaction,
|
|
41
42
|
common::row_idx_t startRow, common::length_t numRows) const;
|
|
42
|
-
// Appends the rows in [startRow, startRow + numRows) whose delete the transaction can
|
|
43
|
+
// Appends to `rows` the rows in [startRow, startRow + numRows) whose delete the transaction can
|
|
44
|
+
// see, as row indices in the group, not offsets from startRow. Insert stamps are not checked.
|
|
43
45
|
void getDeletedRows(const transaction::Transaction* transaction, common::row_idx_t startRow,
|
|
44
46
|
common::length_t numRows, std::vector<common::row_idx_t>& rows) const;
|
|
45
47
|
bool hasInsertions() const;
|