Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "util/variant/variant_builder.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cstdint> |
22 | | #include <cstring> |
23 | | #include <limits> |
24 | | #include <string> |
25 | | #include <utility> |
26 | | |
27 | | #include "common/exception.h" |
28 | | #include "util/utf8_check.h" |
29 | | #include "util/variant/variant_block_builder.h" |
30 | | #include "util/variant/variant_encoded_block.h" |
31 | | #include "util/variant/variant_encoding.h" |
32 | | #include "util/variant/variant_scalar_encoding.h" |
33 | | #include "util/variant/variant_tracked_storage.h" |
34 | | |
35 | | namespace doris { |
36 | | namespace { |
37 | | |
38 | | constexpr uint32_t INVALID_INDEX = std::numeric_limits<uint32_t>::max(); |
39 | | constexpr uint64_t FINISHED_ROW_GENERATION = std::numeric_limits<uint64_t>::max(); |
40 | | constexpr uint64_t ABORTED_ROW_GENERATION = FINISHED_ROW_GENERATION - 1; |
41 | | constexpr size_t INLINE_SCALAR_CAPACITY = sizeof(uint32_t); |
42 | | constexpr size_t SMALL_OBJECT_SORT_THRESHOLD = 16; |
43 | | |
44 | 0 | constexpr unsigned __int128 max_decimal38() { |
45 | 0 | unsigned __int128 value = 1; |
46 | 0 | for (uint8_t digit = 0; digit < 38; ++digit) { |
47 | 0 | value *= 10; |
48 | 0 | } |
49 | 0 | return value - 1; |
50 | 0 | } |
51 | | |
52 | | constexpr unsigned __int128 MAX_DECIMAL38 = max_decimal38(); |
53 | | |
54 | | template <typename Container, typename Less> |
55 | 538 | void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) { |
56 | 538 | if (count < 2) { |
57 | 405 | return; |
58 | 405 | } |
59 | 133 | if (count <= SMALL_OBJECT_SORT_THRESHOLD) { |
60 | 273 | for (size_t index = begin + 1; index < begin + count; ++index) { |
61 | 156 | typename Container::value_type current = entries[index]; |
62 | 156 | size_t insertion = index; |
63 | 305 | while (insertion > begin && less(current, entries[insertion - 1])) { |
64 | 149 | entries[insertion] = entries[insertion - 1]; |
65 | 149 | --insertion; |
66 | 149 | } |
67 | 156 | entries[insertion] = current; |
68 | 156 | } |
69 | 117 | return; |
70 | 117 | } |
71 | 16 | std::sort(entries.begin() + begin, entries.begin() + begin + count, less); |
72 | 16 | } variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorINS_21VariantCollectionCore10FinalChildENS_18CustomStdAllocatorIS4_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS3_22validate_import_objectENS_15VariantValueRefEjEUlRKS4_SD_E_EEvRT_mmT0_ Line | Count | Source | 55 | 12 | void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) { | 56 | 12 | if (count < 2) { | 57 | 5 | return; | 58 | 5 | } | 59 | 7 | if (count <= SMALL_OBJECT_SORT_THRESHOLD) { | 60 | 14 | for (size_t index = begin + 1; index < begin + count; ++index) { | 61 | 7 | typename Container::value_type current = entries[index]; | 62 | 7 | size_t insertion = index; | 63 | 10 | while (insertion > begin && less(current, entries[insertion - 1])) { | 64 | 3 | entries[insertion] = entries[insertion - 1]; | 65 | 3 | --insertion; | 66 | 3 | } | 67 | 7 | entries[insertion] = current; | 68 | 7 | } | 69 | 7 | return; | 70 | 7 | } | 71 | 0 | std::sort(entries.begin() + begin, entries.begin() + begin + count, less); | 72 | 0 | } |
variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorIjNS_18CustomStdAllocatorIjNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS_21VariantCollectionCore27validate_unique_object_keysERKNS9_9ContainerEEUljjE_EEvRT_mmT0_ Line | Count | Source | 55 | 270 | void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) { | 56 | 270 | if (count < 2) { | 57 | 202 | return; | 58 | 202 | } | 59 | 68 | if (count <= SMALL_OBJECT_SORT_THRESHOLD) { | 60 | 140 | for (size_t index = begin + 1; index < begin + count; ++index) { | 61 | 80 | typename Container::value_type current = entries[index]; | 62 | 80 | size_t insertion = index; | 63 | 85 | while (insertion > begin && less(current, entries[insertion - 1])) { | 64 | 5 | entries[insertion] = entries[insertion - 1]; | 65 | 5 | --insertion; | 66 | 5 | } | 67 | 80 | entries[insertion] = current; | 68 | 80 | } | 69 | 60 | return; | 70 | 60 | } | 71 | 8 | std::sort(entries.begin() + begin, entries.begin() + begin + count, less); | 72 | 8 | } |
variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorINS_21VariantCollectionCore10FinalChildENS_18CustomStdAllocatorIS4_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS3_20sort_object_childrenEmmEUlRKS4_SC_E_EEvRT_mmT0_ Line | Count | Source | 55 | 256 | void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) { | 56 | 256 | if (count < 2) { | 57 | 198 | return; | 58 | 198 | } | 59 | 58 | if (count <= SMALL_OBJECT_SORT_THRESHOLD) { | 60 | 119 | for (size_t index = begin + 1; index < begin + count; ++index) { | 61 | 69 | typename Container::value_type current = entries[index]; | 62 | 69 | size_t insertion = index; | 63 | 210 | while (insertion > begin && less(current, entries[insertion - 1])) { | 64 | 141 | entries[insertion] = entries[insertion - 1]; | 65 | 141 | --insertion; | 66 | 141 | } | 67 | 69 | entries[insertion] = current; | 68 | 69 | } | 69 | 50 | return; | 70 | 50 | } | 71 | 8 | std::sort(entries.begin() + begin, entries.begin() + begin + count, less); | 72 | 8 | } |
|
73 | | |
74 | 4 | unsigned __int128 magnitude(__int128 value) { |
75 | 4 | const auto unsigned_value = static_cast<unsigned __int128>(value); |
76 | 4 | return value < 0 ? ~unsigned_value + 1 : unsigned_value; |
77 | 4 | } |
78 | | |
79 | 25.9k | uint8_t minimum_unsigned_width(uint64_t value) { |
80 | 25.9k | if (value <= std::numeric_limits<uint8_t>::max()) { |
81 | 25.6k | return 1; |
82 | 25.6k | } |
83 | 311 | if (value <= std::numeric_limits<uint16_t>::max()) { |
84 | 309 | return 2; |
85 | 309 | } |
86 | 2 | if (value <= 0xFFFFFFU) { |
87 | 2 | return 3; |
88 | 2 | } |
89 | 0 | return 4; |
90 | 2 | } |
91 | | |
92 | 2.05M | void write_unsigned(char*& output, uint64_t value, uint8_t width) noexcept { |
93 | 8.11M | for (uint8_t byte = 0; byte < width; ++byte) { |
94 | 6.05M | *output++ = static_cast<char>(value >> (byte * 8)); |
95 | 6.05M | } |
96 | 2.05M | } |
97 | | |
98 | 1.01M | void validate_string_ref(StringRef value, const char* description) { |
99 | 1.01M | if (value.data == nullptr && value.size != 0) { |
100 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant {} has a null data pointer", |
101 | 0 | description); |
102 | 0 | } |
103 | 1.01M | } |
104 | | |
105 | 24 | void validate_import_root(VariantValueRef value) { |
106 | 24 | if (value.metadata.data == nullptr && value.metadata.size != 0) { |
107 | 0 | throw Exception(ErrorCode::CORRUPTION, |
108 | 0 | "Variant imported metadata has a null data pointer for {} bytes", |
109 | 0 | value.metadata.size); |
110 | 0 | } |
111 | 24 | if (value.data == nullptr && value.size != 0) { |
112 | 0 | throw Exception(ErrorCode::CORRUPTION, |
113 | 0 | "Variant imported value has a null data pointer for {} bytes", value.size); |
114 | 0 | } |
115 | 24 | value.metadata.validate(); |
116 | 24 | } |
117 | | |
118 | 199 | void require_import_depth(uint32_t depth) { |
119 | 199 | if (depth > VARIANT_MAX_NESTING_DEPTH) { |
120 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
121 | 1 | "Variant import exceeds maximum nesting depth {}", |
122 | 1 | VARIANT_MAX_NESTING_DEPTH); |
123 | 1 | } |
124 | 199 | } |
125 | | |
126 | 198 | void require_exact_import_value(VariantValueRef value) { |
127 | 198 | const size_t encoded_size = value.value_size(); |
128 | 198 | if (encoded_size != value.size) { |
129 | 1 | throw Exception(ErrorCode::CORRUPTION, |
130 | 1 | "Variant imported value has {} trailing bytes after its {} byte root", |
131 | 1 | value.size - encoded_size, encoded_size); |
132 | 1 | } |
133 | 198 | } |
134 | | |
135 | 29 | void require_import_utf8(StringRef value, const char* description) { |
136 | 29 | if (value.size != 0 && !validate_utf8(value.data, value.size)) { |
137 | 3 | throw Exception(ErrorCode::CORRUPTION, "Variant imported {} is not valid UTF-8", |
138 | 3 | description); |
139 | 3 | } |
140 | 29 | } |
141 | | |
142 | | } // namespace |
143 | | |
144 | | class VariantCollectionCore { |
145 | | public: |
146 | | enum class NodeKind : uint8_t { SCALAR, INLINE_SCALAR, OBJECT, ARRAY }; |
147 | | enum class State : uint8_t { COLLECTING, COLLECTED, FINISHED, ABORTED }; |
148 | | |
149 | | struct Node { |
150 | 0 | Node() noexcept : kind(NodeKind::SCALAR), payload_index(0), payload_size(0) {} |
151 | | Node(NodeKind kind_, uint32_t payload_index_, uint32_t payload_size_) noexcept |
152 | 28.7k | : kind(kind_), payload_index(payload_index_), payload_size(payload_size_) {} |
153 | | Node(std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes_, |
154 | | uint32_t payload_size_) noexcept |
155 | 1.06M | : kind(NodeKind::INLINE_SCALAR), |
156 | 1.06M | inline_bytes(inline_bytes_), |
157 | 1.06M | payload_size(payload_size_) {} |
158 | | |
159 | | NodeKind kind; |
160 | | union { |
161 | | uint32_t payload_index; |
162 | | std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes; |
163 | | }; |
164 | | uint32_t payload_size; |
165 | | }; |
166 | | static_assert(sizeof(Node) == 12); |
167 | | |
168 | | struct Child { |
169 | | uint32_t node_index; |
170 | | uint32_t temporary_field_id; |
171 | | uint32_t next; |
172 | | }; |
173 | | |
174 | | struct Container { |
175 | 14.1k | explicit Container(NodeKind kind_) : kind(kind_) {} |
176 | | |
177 | | NodeKind kind; |
178 | | uint32_t first_child = INVALID_INDEX; |
179 | | uint32_t last_child = INVALID_INDEX; |
180 | | uint32_t child_count = 0; |
181 | | uint32_t pending_field_id = 0; |
182 | | uint32_t previous_or_source_token = INVALID_INDEX; |
183 | | uint32_t previous_child_cursor = INVALID_INDEX; |
184 | | bool has_pending_field = false; |
185 | | }; |
186 | | |
187 | | struct Checkpoint { |
188 | | size_t scalar_bytes; |
189 | | size_t nodes; |
190 | | size_t containers; |
191 | | size_t children; |
192 | | size_t key_references; |
193 | | }; |
194 | | |
195 | | struct CapacitySnapshot { |
196 | | size_t scalar_bytes; |
197 | | size_t nodes; |
198 | | size_t containers; |
199 | | size_t children; |
200 | | size_t scope_stack; |
201 | | size_t object_id_scratch; |
202 | | size_t key_references; |
203 | | size_t container_plans; |
204 | | size_t planned_object_children; |
205 | | }; |
206 | | |
207 | | struct FinalChild { |
208 | | uint32_t node_index; |
209 | | uint32_t final_field_id; |
210 | | }; |
211 | | |
212 | | struct ContainerPlan { |
213 | | size_t encoded_size = 0; |
214 | | size_t object_children_begin = 0; |
215 | | uint32_t values_size = 0; |
216 | | uint8_t count_width = 0; |
217 | | uint8_t offset_width = 0; |
218 | | uint8_t id_width = 0; |
219 | | }; |
220 | | |
221 | 743 | explicit VariantCollectionCore(VariantMetadataBuilder& metadata_) : metadata(metadata_) {} |
222 | | |
223 | | void reserve(size_t scalar_byte_count, size_t node_count, size_t container_count, |
224 | 334 | size_t child_count) { |
225 | 334 | scalar_bytes.reserve(scalar_byte_count); |
226 | 334 | nodes.reserve(node_count); |
227 | 334 | containers.reserve(container_count); |
228 | 334 | children.reserve(child_count); |
229 | 334 | scope_stack.reserve(container_count); |
230 | 334 | object_id_scratch.reserve(child_count); |
231 | 334 | key_references.reserve(child_count); |
232 | 334 | container_plans.reserve(container_count); |
233 | 334 | planned_object_children.reserve(child_count); |
234 | 334 | } |
235 | | |
236 | | CapacitySnapshot capacity_snapshot() const noexcept { |
237 | | return {.scalar_bytes = scalar_bytes.capacity(), |
238 | | .nodes = nodes.capacity(), |
239 | | .containers = containers.capacity(), |
240 | | .children = children.capacity(), |
241 | | .scope_stack = scope_stack.capacity(), |
242 | | .object_id_scratch = object_id_scratch.capacity(), |
243 | | .key_references = key_references.capacity(), |
244 | | .container_plans = container_plans.capacity(), |
245 | | .planned_object_children = planned_object_children.capacity()}; |
246 | | } |
247 | | |
248 | 80.3k | Checkpoint checkpoint() const noexcept { |
249 | 80.3k | return {.scalar_bytes = scalar_bytes.size(), |
250 | 80.3k | .nodes = nodes.size(), |
251 | 80.3k | .containers = containers.size(), |
252 | 80.3k | .children = children.size(), |
253 | 80.3k | .key_references = key_references.size()}; |
254 | 80.3k | } |
255 | | |
256 | | void begin_collection(DorisVector<uint32_t>* previous_tokens = nullptr, |
257 | 80.3k | DorisVector<uint32_t>* pending_tokens = nullptr) noexcept { |
258 | 80.3k | DCHECK(scope_stack.empty()); |
259 | 80.3k | DCHECK_EQ(active_container, INVALID_INDEX); |
260 | 80.3k | DCHECK_EQ(previous_tokens == nullptr, pending_tokens == nullptr); |
261 | 80.3k | DCHECK(pending_tokens == nullptr || pending_tokens->empty()); |
262 | 80.3k | previous_object_tokens = previous_tokens; |
263 | 80.3k | pending_object_tokens = pending_tokens; |
264 | 80.3k | root_node = INVALID_INDEX; |
265 | 80.3k | state = State::COLLECTING; |
266 | 80.3k | } |
267 | | |
268 | 38 | void rollback(const Checkpoint& start) noexcept { |
269 | 38 | DCHECK_LE(start.scalar_bytes, scalar_bytes.size()); |
270 | 38 | DCHECK_LE(start.nodes, nodes.size()); |
271 | 38 | DCHECK_LE(start.containers, containers.size()); |
272 | 38 | DCHECK_LE(start.children, children.size()); |
273 | 38 | DCHECK_LE(start.key_references, key_references.size()); |
274 | 38 | scalar_bytes.resize(start.scalar_bytes); |
275 | 38 | nodes.resize(start.nodes); |
276 | 38 | containers.erase(containers.begin() + start.containers, containers.end()); |
277 | 38 | children.resize(start.children); |
278 | 38 | key_references.resize(start.key_references); |
279 | 38 | scope_stack.clear(); |
280 | 38 | active_container = INVALID_INDEX; |
281 | 38 | object_id_scratch.clear(); |
282 | 38 | planned_object_children.clear(); |
283 | 38 | if (pending_object_tokens != nullptr) { |
284 | 38 | pending_object_tokens->clear(); |
285 | 38 | } |
286 | 38 | previous_object_tokens = nullptr; |
287 | 38 | pending_object_tokens = nullptr; |
288 | 38 | root_node = INVALID_INDEX; |
289 | 38 | state = State::ABORTED; |
290 | 38 | } |
291 | | |
292 | 80.2k | void publish_object_links() noexcept { |
293 | 80.2k | if (pending_object_tokens == nullptr) { |
294 | 0 | return; |
295 | 0 | } |
296 | 80.2k | DCHECK(previous_object_tokens != nullptr); |
297 | 80.2k | DCHECK_NE(root_node, INVALID_INDEX); |
298 | 80.2k | previous_object_tokens->swap(*pending_object_tokens); |
299 | 80.2k | pending_object_tokens->clear(); |
300 | 80.2k | previous_object_tokens = nullptr; |
301 | 80.2k | pending_object_tokens = nullptr; |
302 | 80.2k | } |
303 | | |
304 | 80.2k | void discard_key_references(size_t begin) noexcept { |
305 | 80.2k | DCHECK_LE(begin, key_references.size()); |
306 | 80.2k | key_references.resize(begin); |
307 | 80.2k | } |
308 | | |
309 | 38 | const uint32_t* key_reference_data(size_t begin) const noexcept { |
310 | 38 | DCHECK_LE(begin, key_references.size()); |
311 | 38 | return key_references.empty() ? nullptr : key_references.data() + begin; |
312 | 38 | } |
313 | | |
314 | 38 | size_t key_reference_count(size_t begin) const noexcept { |
315 | 38 | DCHECK_LE(begin, key_references.size()); |
316 | 38 | return key_references.size() - begin; |
317 | 38 | } |
318 | | |
319 | 2.12M | void ensure_collecting() const { |
320 | 2.12M | if (state != State::COLLECTING) { |
321 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
322 | 1 | "Cannot add to a Variant row builder after collection completes"); |
323 | 1 | } |
324 | 2.12M | if (metadata.is_sealed()) { |
325 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
326 | 0 | "Cannot add a Variant value after metadata seal"); |
327 | 0 | } |
328 | 2.12M | } |
329 | | |
330 | 1.09M | void ensure_can_add_value() const { |
331 | 1.09M | ensure_collecting(); |
332 | 1.09M | if (active_container == INVALID_INDEX) { |
333 | 80.4k | if (root_node != INVALID_INDEX) { |
334 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
335 | 0 | "Variant row builder accepts exactly one root value"); |
336 | 0 | } |
337 | 80.4k | return; |
338 | 80.4k | } |
339 | | |
340 | 1.09M | DCHECK_LT(active_container, containers.size()); |
341 | 1.01M | const Container& parent = containers[active_container]; |
342 | 1.01M | if (parent.child_count == std::numeric_limits<uint32_t>::max()) { |
343 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
344 | 0 | "Variant container exceeds the uint32 element limit"); |
345 | 0 | } |
346 | 1.01M | if (children.size() == std::numeric_limits<uint32_t>::max()) { |
347 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
348 | 0 | "Variant encoding unit exceeds the uint32 temporary child limit"); |
349 | 0 | } |
350 | 1.01M | if (parent.kind == NodeKind::OBJECT && !parent.has_pending_field) { |
351 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
352 | 0 | "Variant object value must be preceded by add_key"); |
353 | 0 | } |
354 | 1.01M | } |
355 | | |
356 | 1.09M | void prepare_value_node() const { |
357 | 1.09M | ensure_can_add_value(); |
358 | 1.09M | if (nodes.size() == std::numeric_limits<uint32_t>::max()) { |
359 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
360 | 0 | "Variant encoding unit exceeds the uint32 temporary node limit"); |
361 | 0 | } |
362 | 1.09M | } |
363 | | |
364 | 14.5k | uint32_t prepare_arena_scalar(size_t encoded_size) const { |
365 | 14.5k | prepare_value_node(); |
366 | 14.5k | if (encoded_size > std::numeric_limits<uint32_t>::max() - scalar_bytes.size()) { |
367 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
368 | 0 | "Variant encoding unit scalar scratch exceeds the uint32 byte limit"); |
369 | 0 | } |
370 | 14.5k | return static_cast<uint32_t>(scalar_bytes.size()); |
371 | 14.5k | } |
372 | | |
373 | 1.08M | void add_scalar(const VariantScalarEncodingPlan& plan) { |
374 | 1.08M | if (plan.size() <= INLINE_SCALAR_CAPACITY) { |
375 | 1.06M | prepare_value_node(); |
376 | 1.06M | std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes {}; |
377 | 1.06M | plan.write(inline_bytes.data(), plan.size()); |
378 | 1.06M | complete_inline_scalar(inline_bytes, plan.size()); |
379 | 1.06M | return; |
380 | 1.06M | } |
381 | | |
382 | 14.5k | const uint32_t offset = prepare_arena_scalar(plan.size()); |
383 | 14.5k | scalar_bytes.resize(scalar_bytes.size() + plan.size()); |
384 | 14.5k | plan.write(scalar_bytes.data() + offset, plan.size()); |
385 | 14.5k | complete_scalar(offset); |
386 | 14.5k | } |
387 | | |
388 | 1.06M | void add_null() { add_scalar(VariantScalarEncodingPlan::null_value()); } |
389 | | |
390 | 54 | void add_bool(bool value) { add_scalar(VariantScalarEncodingPlan::boolean(value)); } |
391 | | |
392 | 13.7k | void add_int(int64_t value) { add_scalar(VariantScalarEncodingPlan::integer(value)); } |
393 | | |
394 | 24 | void add_value(VariantValueRef value) { |
395 | 24 | ensure_can_add_value(); |
396 | 24 | planned_object_children.clear(); |
397 | 24 | try { |
398 | 24 | validate_import_root(value); |
399 | 24 | if (scope_stack.size() > VARIANT_MAX_NESTING_DEPTH) { |
400 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
401 | 0 | "Variant import exceeds maximum nesting depth {}", |
402 | 0 | VARIANT_MAX_NESTING_DEPTH); |
403 | 0 | } |
404 | 24 | import_node(value, static_cast<uint32_t>(scope_stack.size())); |
405 | 24 | } catch (...) { |
406 | | // The caller still owns row rollback; only release importer scratch here. |
407 | 10 | planned_object_children.clear(); |
408 | 10 | throw; |
409 | 10 | } |
410 | 24 | DCHECK(planned_object_children.empty()); |
411 | 14 | } |
412 | | |
413 | 42 | void import_primitive(VariantValueRef value) { |
414 | 42 | const VariantPrimitiveId id = value.primitive_id(); |
415 | 42 | switch (id) { |
416 | 7 | case VariantPrimitiveId::NULL_VALUE: |
417 | 7 | add_null(); |
418 | 7 | return; |
419 | 5 | case VariantPrimitiveId::TRUE_VALUE: |
420 | 9 | case VariantPrimitiveId::FALSE_VALUE: |
421 | 9 | add_bool(value.get_bool()); |
422 | 9 | return; |
423 | 7 | case VariantPrimitiveId::INT8: |
424 | 9 | case VariantPrimitiveId::INT16: |
425 | 10 | case VariantPrimitiveId::INT32: |
426 | 11 | case VariantPrimitiveId::INT64: |
427 | 11 | add_int(value.get_int()); |
428 | 11 | return; |
429 | 1 | case VariantPrimitiveId::FLOAT: |
430 | 1 | add_scalar(VariantScalarEncodingPlan::float32(value.get_float())); |
431 | 1 | return; |
432 | 1 | case VariantPrimitiveId::DOUBLE: |
433 | 1 | add_scalar(VariantScalarEncodingPlan::float64(value.get_double())); |
434 | 1 | return; |
435 | 1 | case VariantPrimitiveId::DECIMAL4: |
436 | 2 | case VariantPrimitiveId::DECIMAL8: |
437 | 4 | case VariantPrimitiveId::DECIMAL16: { |
438 | 4 | const VariantDecimal decimal = value.get_decimal(); |
439 | 4 | if (magnitude(decimal.unscaled) > MAX_DECIMAL38) { |
440 | 1 | throw Exception(ErrorCode::CORRUPTION, |
441 | 1 | "Variant imported decimal exceeds precision 38"); |
442 | 1 | } |
443 | 3 | add_scalar(VariantScalarEncodingPlan::decimal(decimal.unscaled, decimal.scale)); |
444 | 3 | return; |
445 | 4 | } |
446 | 1 | case VariantPrimitiveId::DATE: |
447 | 1 | add_scalar(VariantScalarEncodingPlan::date(value.get_date())); |
448 | 1 | return; |
449 | 1 | case VariantPrimitiveId::TIMESTAMP_MICROS: |
450 | 1 | add_scalar(VariantScalarEncodingPlan::timestamp_micros(value.get_timestamp_micros(), |
451 | 1 | true)); |
452 | 1 | return; |
453 | 1 | case VariantPrimitiveId::TIMESTAMP_NTZ_MICROS: |
454 | 1 | add_scalar(VariantScalarEncodingPlan::timestamp_micros(value.get_timestamp_ntz_micros(), |
455 | 1 | false)); |
456 | 1 | return; |
457 | 1 | case VariantPrimitiveId::BINARY: |
458 | 1 | add_scalar(VariantScalarEncodingPlan::binary(value.get_binary())); |
459 | 1 | return; |
460 | 1 | case VariantPrimitiveId::STRING: { |
461 | 1 | const StringRef string = value.get_string(); |
462 | 1 | require_import_utf8(string, "string"); |
463 | 1 | add_scalar(VariantScalarEncodingPlan::string(string)); |
464 | 1 | return; |
465 | 4 | } |
466 | 1 | case VariantPrimitiveId::TIME_NTZ_MICROS: |
467 | 1 | add_scalar(VariantScalarEncodingPlan::time_ntz_micros(value.get_time_ntz_micros())); |
468 | 1 | return; |
469 | 1 | case VariantPrimitiveId::TIMESTAMP_NANOS: |
470 | 1 | add_scalar( |
471 | 1 | VariantScalarEncodingPlan::timestamp_nanos(value.get_timestamp_nanos(), true)); |
472 | 1 | return; |
473 | 1 | case VariantPrimitiveId::TIMESTAMP_NTZ_NANOS: |
474 | 1 | add_scalar(VariantScalarEncodingPlan::timestamp_nanos(value.get_timestamp_ntz_nanos(), |
475 | 1 | false)); |
476 | 1 | return; |
477 | 1 | case VariantPrimitiveId::UUID: |
478 | 1 | add_scalar(VariantScalarEncodingPlan::uuid(value.get_uuid())); |
479 | 1 | return; |
480 | 42 | } |
481 | 42 | } |
482 | | |
483 | 14 | size_t object_values_offset(VariantValueRef value, uint32_t count) const noexcept { |
484 | 14 | const uint8_t value_header = |
485 | 14 | static_cast<uint8_t>(value.data[0]) >> VARIANT_VALUE_HEADER_SHIFT; |
486 | 14 | const auto offset_width = static_cast<uint8_t>( |
487 | 14 | ((value_header >> VARIANT_OBJECT_OFFSET_SIZE_SHIFT) & 0x03) + 1); |
488 | 14 | const auto id_width = |
489 | 14 | static_cast<uint8_t>(((value_header >> VARIANT_OBJECT_ID_SIZE_SHIFT) & 0x03) + 1); |
490 | 14 | const uint8_t count_width = (value_header & VARIANT_OBJECT_LARGE_MASK) != 0 |
491 | 14 | ? sizeof(uint32_t) |
492 | 14 | : sizeof(uint8_t); |
493 | 14 | return 1 + count_width + static_cast<size_t>(count) * id_width + |
494 | 14 | (static_cast<size_t>(count) + 1) * offset_width; |
495 | 14 | } |
496 | | |
497 | 14 | void validate_import_object(VariantValueRef value, uint32_t count) { |
498 | 14 | const size_t values_offset = object_values_offset(value, count); |
499 | 14 | DCHECK_LE(values_offset, value.size); |
500 | 14 | const char* values_begin = value.data + values_offset; |
501 | 14 | const auto values_size = static_cast<uint32_t>(value.size - values_offset); |
502 | | // Pass-2 planning is idle during collection, so reuse its retained capacity to validate |
503 | | // the source object's physical value intervals without adding a per-row scratch buffer. |
504 | 14 | planned_object_children.clear(); |
505 | 14 | planned_object_children.reserve(count); |
506 | | |
507 | 14 | StringRef previous_key; |
508 | 35 | for (uint32_t index = 0; index < count; ++index) { |
509 | 22 | uint32_t field_id = 0; |
510 | 22 | const VariantValueRef child = value.object_value_at(index, &field_id); |
511 | 22 | const StringRef key = value.metadata.key_at(field_id); |
512 | 22 | require_import_utf8(key, "object key"); |
513 | 22 | if (index != 0 && previous_key.compare(key) >= 0) { |
514 | 1 | throw Exception(ErrorCode::CORRUPTION, |
515 | 1 | "Variant imported object keys are not strictly byte-sorted at " |
516 | 1 | "field {}", |
517 | 1 | index); |
518 | 1 | } |
519 | 21 | previous_key = key; |
520 | 21 | const auto begin = static_cast<uint32_t>(child.data - values_begin); |
521 | 21 | DCHECK_LE(child.size, std::numeric_limits<uint32_t>::max()); |
522 | 21 | planned_object_children.push_back( |
523 | 21 | {.node_index = begin, .final_field_id = static_cast<uint32_t>(child.size)}); |
524 | 21 | } |
525 | | |
526 | 13 | sort_object_entries(planned_object_children, 0, planned_object_children.size(), |
527 | 13 | [](const FinalChild& left, const FinalChild& right) { |
528 | 7 | return left.node_index < right.node_index; |
529 | 7 | }); |
530 | 13 | uint32_t expected_begin = 0; |
531 | 19 | for (const FinalChild& interval : planned_object_children) { |
532 | 19 | if (interval.node_index != expected_begin) { |
533 | 2 | throw Exception(ErrorCode::CORRUPTION, |
534 | 2 | "Variant imported object values are not a complete non-overlapping " |
535 | 2 | "partition at byte {}", |
536 | 2 | expected_begin); |
537 | 2 | } |
538 | 17 | expected_begin += interval.final_field_id; |
539 | 17 | } |
540 | 11 | if (expected_begin != values_size) { |
541 | 1 | throw Exception(ErrorCode::CORRUPTION, |
542 | 1 | "Variant imported object values contain {} unreferenced trailing bytes", |
543 | 1 | values_size - expected_begin); |
544 | 1 | } |
545 | 10 | planned_object_children.clear(); |
546 | 10 | } |
547 | | |
548 | 14 | void import_object(VariantValueRef value, uint32_t depth) { |
549 | 14 | const uint32_t count = value.num_elements(); |
550 | 14 | validate_import_object(value, count); |
551 | 14 | const uint32_t token = start_container(NodeKind::OBJECT); |
552 | 27 | for (uint32_t index = 0; index < count; ++index) { |
553 | 13 | uint32_t field_id = 0; |
554 | 13 | const VariantValueRef child = value.object_value_at(index, &field_id); |
555 | 13 | add_key(token, value.metadata.key_at(field_id)); |
556 | 13 | import_node(child, depth + 1); |
557 | 13 | } |
558 | 14 | finish_container(token, NodeKind::OBJECT); |
559 | 14 | } |
560 | | |
561 | 135 | void import_array(VariantValueRef value, uint32_t depth) { |
562 | 135 | const uint32_t count = value.num_elements(); |
563 | 135 | const uint32_t token = start_container(NodeKind::ARRAY); |
564 | 297 | for (uint32_t index = 0; index < count; ++index) { |
565 | 162 | import_node(value.array_at(index), depth + 1); |
566 | 162 | } |
567 | 135 | finish_container(token, NodeKind::ARRAY); |
568 | 135 | } |
569 | | |
570 | 199 | void import_node(VariantValueRef value, uint32_t depth) { |
571 | 199 | require_import_depth(depth); |
572 | 199 | require_exact_import_value(value); |
573 | 199 | switch (value.basic_type()) { |
574 | 42 | case VariantBasicType::PRIMITIVE: |
575 | 42 | import_primitive(value); |
576 | 42 | return; |
577 | 6 | case VariantBasicType::SHORT_STRING: { |
578 | 6 | const StringRef string = value.get_string(); |
579 | 6 | require_import_utf8(string, "string"); |
580 | 6 | add_scalar(VariantScalarEncodingPlan::string(string)); |
581 | 6 | return; |
582 | 0 | } |
583 | 14 | case VariantBasicType::OBJECT: |
584 | 14 | import_object(value, depth); |
585 | 14 | return; |
586 | 135 | case VariantBasicType::ARRAY: |
587 | 135 | import_array(value, depth); |
588 | 135 | return; |
589 | 199 | } |
590 | 199 | } |
591 | | |
592 | 1.09M | void attach_node(uint32_t node_index) { |
593 | 1.09M | if (active_container == INVALID_INDEX) { |
594 | 80.4k | DCHECK_EQ(root_node, INVALID_INDEX); |
595 | 80.4k | root_node = node_index; |
596 | 80.4k | if (nodes[node_index].kind == NodeKind::INLINE_SCALAR || |
597 | 80.4k | nodes[node_index].kind == NodeKind::SCALAR) { |
598 | 67.8k | state = State::COLLECTED; |
599 | 67.8k | } |
600 | 80.4k | return; |
601 | 80.4k | } |
602 | | |
603 | 1.09M | DCHECK_LT(active_container, containers.size()); |
604 | 1.01M | Container& parent = containers[active_container]; |
605 | 1.01M | const uint32_t field_id = parent.kind == NodeKind::OBJECT ? parent.pending_field_id : 0; |
606 | 1.01M | const auto child_index = static_cast<uint32_t>(children.size()); |
607 | 1.01M | children.push_back({node_index, field_id, INVALID_INDEX}); |
608 | 1.01M | if (parent.first_child == INVALID_INDEX) { |
609 | 14.0k | parent.first_child = child_index; |
610 | 1.00M | } else { |
611 | 1.00M | DCHECK_NE(parent.last_child, INVALID_INDEX); |
612 | 1.00M | children[parent.last_child].next = child_index; |
613 | 1.00M | } |
614 | 1.01M | parent.last_child = child_index; |
615 | 1.01M | ++parent.child_count; |
616 | 1.01M | parent.has_pending_field = false; |
617 | 1.01M | } |
618 | | |
619 | 14.5k | void complete_scalar(uint32_t offset) { |
620 | 14.5k | DCHECK_LE(scalar_bytes.size() - offset, std::numeric_limits<uint32_t>::max()); |
621 | 14.5k | const auto node_index = static_cast<uint32_t>(nodes.size()); |
622 | 14.5k | nodes.emplace_back(NodeKind::SCALAR, offset, |
623 | 14.5k | static_cast<uint32_t>(scalar_bytes.size() - offset)); |
624 | 14.5k | attach_node(node_index); |
625 | 14.5k | } |
626 | | |
627 | | void complete_inline_scalar(const std::array<char, INLINE_SCALAR_CAPACITY>& inline_bytes, |
628 | 1.06M | size_t encoded_size) { |
629 | 1.06M | DCHECK_GT(encoded_size, 0); |
630 | 1.06M | DCHECK_LE(encoded_size, INLINE_SCALAR_CAPACITY); |
631 | 1.06M | const auto node_index = static_cast<uint32_t>(nodes.size()); |
632 | 1.06M | nodes.emplace_back(inline_bytes, static_cast<uint32_t>(encoded_size)); |
633 | 1.06M | attach_node(node_index); |
634 | 1.06M | } |
635 | | |
636 | | void add_inline_scalar(const std::array<char, INLINE_SCALAR_CAPACITY>& inline_bytes, |
637 | 0 | size_t encoded_size) { |
638 | 0 | prepare_value_node(); |
639 | 0 | complete_inline_scalar(inline_bytes, encoded_size); |
640 | 0 | } |
641 | | |
642 | 0 | uint32_t start_container(NodeKind kind) { |
643 | 0 | prepare_value_node(); |
644 | 0 | if (containers.size() == std::numeric_limits<uint32_t>::max()) { |
645 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
646 | 0 | "Variant encoding unit exceeds the uint32 temporary container limit"); |
647 | 0 | } |
648 | | |
649 | 0 | const auto token = static_cast<uint32_t>(containers.size()); |
650 | 0 | uint32_t previous_token = INVALID_INDEX; |
651 | 0 | uint32_t previous_child_cursor = INVALID_INDEX; |
652 | 0 | if (kind == NodeKind::OBJECT && pending_object_tokens != nullptr) { |
653 | 0 | DCHECK(previous_object_tokens != nullptr); |
654 | 0 | const size_t object_ordinal = pending_object_tokens->size(); |
655 | 0 | if (object_ordinal < previous_object_tokens->size()) { |
656 | 0 | previous_token = (*previous_object_tokens)[object_ordinal]; |
657 | 0 | DCHECK_LT(previous_token, token); |
658 | 0 | DCHECK(containers[previous_token].kind == NodeKind::OBJECT); |
659 | 0 | previous_child_cursor = containers[previous_token].first_child; |
660 | 0 | } |
661 | | #ifdef BE_TEST |
662 | | const size_t old_capacity = pending_object_tokens->capacity(); |
663 | | #endif |
664 | 0 | try { |
665 | 0 | pending_object_tokens->push_back(token); |
666 | 0 | } catch (...) { |
667 | 0 | state = State::ABORTED; |
668 | 0 | throw; |
669 | 0 | } |
670 | | #ifdef BE_TEST |
671 | | object_token_capacity_growths += old_capacity != pending_object_tokens->capacity(); |
672 | | #endif |
673 | 0 | } |
674 | 0 | try { |
675 | 0 | containers.emplace_back(kind); |
676 | 0 | containers.back().previous_or_source_token = previous_token; |
677 | 0 | containers.back().previous_child_cursor = previous_child_cursor; |
678 | 0 | const auto node_index = static_cast<uint32_t>(nodes.size()); |
679 | 0 | nodes.emplace_back(kind, token, 0); |
680 | 0 | attach_node(node_index); |
681 | 0 | scope_stack.push_back(token); |
682 | 0 | active_container = token; |
683 | 0 | } catch (...) { |
684 | 0 | if (kind == NodeKind::OBJECT && pending_object_tokens != nullptr) { |
685 | 0 | DCHECK(!pending_object_tokens->empty()); |
686 | 0 | DCHECK_EQ(pending_object_tokens->back(), token); |
687 | 0 | pending_object_tokens->pop_back(); |
688 | 0 | } |
689 | 0 | if (pending_object_tokens != nullptr) { |
690 | | // Block rows own rollback; legacy rows must stay collecting so abort releases |
691 | | // their metadata references. |
692 | 0 | state = State::ABORTED; |
693 | 0 | } |
694 | 0 | throw; |
695 | 0 | } |
696 | 0 | return token; |
697 | 0 | } |
698 | | |
699 | 1.01M | void ensure_can_add_key(uint32_t token) const { |
700 | 1.01M | ensure_collecting(); |
701 | 1.01M | if (token >= containers.size() || active_container != token || |
702 | 1.01M | containers[token].kind != NodeKind::OBJECT) { |
703 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
704 | 1 | "Variant object scope is not the active scope"); |
705 | 1 | } |
706 | 1.01M | if (containers[token].has_pending_field) { |
707 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object key is missing its value"); |
708 | 1 | } |
709 | 1.01M | } |
710 | | |
711 | 1.01M | void commit_key(uint32_t token, uint32_t temporary_id) noexcept { |
712 | 1.01M | DCHECK_LT(token, containers.size()); |
713 | 1.01M | Container& object = containers[token]; |
714 | 1.01M | DCHECK(object.kind == NodeKind::OBJECT); |
715 | 1.01M | DCHECK(!object.has_pending_field); |
716 | 1.01M | object.pending_field_id = temporary_id; |
717 | 1.01M | object.has_pending_field = true; |
718 | 1.01M | } |
719 | | |
720 | 1.01M | void add_key(uint32_t token, StringRef key) { |
721 | 1.01M | ensure_can_add_key(token); |
722 | 1.01M | validate_string_ref(key, "metadata key"); |
723 | 1.01M | Container& object = containers[token]; |
724 | | |
725 | 1.01M | bool key_hit = false; |
726 | 1.01M | uint32_t next_previous_child = INVALID_INDEX; |
727 | 1.01M | uint32_t temporary_id = INVALID_INDEX; |
728 | 1.01M | if (object.previous_or_source_token != INVALID_INDEX && |
729 | 1.01M | object.previous_child_cursor != INVALID_INDEX) { |
730 | 13.0k | const Child& previous_child = children[object.previous_child_cursor]; |
731 | 13.0k | const StringRef previous_key = |
732 | 13.0k | metadata._temporary_key(previous_child.temporary_field_id); |
733 | 13.0k | if (previous_key.size == key.size && |
734 | 13.0k | (key.size == 0 || std::memcmp(previous_key.data, key.data, key.size) == 0)) { |
735 | 13.0k | key_hit = true; |
736 | 13.0k | next_previous_child = previous_child.next; |
737 | 13.0k | temporary_id = previous_child.temporary_field_id; |
738 | 13.0k | } |
739 | 13.0k | } |
740 | 1.01M | if (!key_hit) { |
741 | 1.00M | temporary_id = metadata.register_key(key); |
742 | 1.00M | } |
743 | | |
744 | 1.01M | key_references.push_back(temporary_id); |
745 | 1.01M | metadata._retain_key(temporary_id); |
746 | 1.01M | commit_key(token, temporary_id); |
747 | 1.01M | if (key_hit) { |
748 | 13.0k | object.previous_child_cursor = next_previous_child; |
749 | 1.00M | } else { |
750 | 1.00M | object.previous_or_source_token = INVALID_INDEX; |
751 | 1.00M | object.previous_child_cursor = INVALID_INDEX; |
752 | 1.00M | } |
753 | 1.01M | } |
754 | | |
755 | 270 | void validate_unique_object_keys(const Container& object) { |
756 | 270 | object_id_scratch.clear(); |
757 | 270 | object_id_scratch.reserve(object.child_count); |
758 | 270 | uint32_t child_index = object.first_child; |
759 | 1.00M | for (uint32_t index = 0; index < object.child_count; ++index) { |
760 | 1.00M | DCHECK_NE(child_index, INVALID_INDEX); |
761 | 1.00M | const Child& child = children[child_index]; |
762 | 1.00M | object_id_scratch.push_back(child.temporary_field_id); |
763 | 1.00M | child_index = child.next; |
764 | 1.00M | } |
765 | 270 | DCHECK_EQ(child_index, INVALID_INDEX); |
766 | 270 | sort_object_entries(object_id_scratch, 0, object_id_scratch.size(), |
767 | 25.6M | [](uint32_t left, uint32_t right) { return left < right; }); |
768 | 1.00M | for (size_t index = 1; index < object_id_scratch.size(); ++index) { |
769 | 1.00M | if (object_id_scratch[index - 1] == object_id_scratch[index]) { |
770 | 8 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Duplicate Variant object key"); |
771 | 8 | } |
772 | 1.00M | } |
773 | 270 | } |
774 | | |
775 | 13.4k | void finish_container(uint32_t token, NodeKind expected_kind) { |
776 | 13.4k | ensure_collecting(); |
777 | 13.4k | if (token >= containers.size() || active_container != token || |
778 | 13.4k | containers[token].kind != expected_kind) { |
779 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
780 | 1 | "Variant container scopes must finish in nesting order"); |
781 | 1 | } |
782 | 13.4k | if (expected_kind == NodeKind::OBJECT && containers[token].has_pending_field) { |
783 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object key is missing its value"); |
784 | 1 | } |
785 | 13.4k | if (expected_kind == NodeKind::OBJECT) { |
786 | 12.5k | Container& object = containers[token]; |
787 | 12.5k | if (object.previous_or_source_token != INVALID_INDEX && |
788 | 12.5k | object.previous_child_cursor == INVALID_INDEX) { |
789 | 12.3k | DCHECK_LT(object.previous_or_source_token, token); |
790 | 12.3k | DCHECK_EQ(containers[object.previous_or_source_token].child_count, |
791 | 12.3k | object.child_count); |
792 | | #ifdef BE_TEST |
793 | | ++object_schema_hits; |
794 | | #endif |
795 | 12.3k | } else { |
796 | 270 | validate_unique_object_keys(object); |
797 | 270 | object.previous_or_source_token = token; |
798 | | #ifdef BE_TEST |
799 | | ++object_schema_fallbacks; |
800 | | #endif |
801 | 270 | } |
802 | 12.5k | } |
803 | 13.4k | DCHECK(!scope_stack.empty()); |
804 | 13.4k | DCHECK_EQ(scope_stack.back(), token); |
805 | 13.4k | scope_stack.pop_back(); |
806 | 13.4k | if (scope_stack.empty()) { |
807 | 12.6k | active_container = INVALID_INDEX; |
808 | 12.6k | state = State::COLLECTED; |
809 | 12.6k | } else { |
810 | 782 | active_container = scope_stack.back(); |
811 | 782 | } |
812 | 13.4k | } |
813 | | |
814 | 256 | void sort_object_children(size_t begin, size_t count) { |
815 | 25.6M | const auto less_by_id = [](const FinalChild& left, const FinalChild& right) { |
816 | 25.6M | return left.final_field_id < right.final_field_id; |
817 | 25.6M | }; |
818 | 256 | sort_object_entries(planned_object_children, begin, count, less_by_id); |
819 | 1.00M | for (size_t index = begin + 1; index < begin + count; ++index) { |
820 | 1.00M | DCHECK_LT(planned_object_children[index - 1].final_field_id, |
821 | 1.00M | planned_object_children[index].final_field_id); |
822 | 1.00M | } |
823 | 256 | } |
824 | | |
825 | | // Recursive pass-2 is a hot path; splitting it adds call layers in ASAN and production. |
826 | | // NOLINTNEXTLINE(readability-function-cognitive-complexity,readability-function-size) |
827 | 1.09M | size_t plan_node(uint32_t node_index) { |
828 | 1.09M | DCHECK_LT(node_index, nodes.size()); |
829 | 1.09M | const Node& node = nodes[node_index]; |
830 | 1.09M | if (node.kind == NodeKind::INLINE_SCALAR || node.kind == NodeKind::SCALAR) { |
831 | 1.08M | return node.payload_size; |
832 | 1.08M | } |
833 | | |
834 | 13.3k | const Container& container = containers[node.payload_index]; |
835 | 13.3k | ContainerPlan& plan = container_plans[node.payload_index]; |
836 | 13.3k | const uint32_t count = container.child_count; |
837 | 13.3k | plan.count_width = |
838 | 13.3k | count > std::numeric_limits<uint8_t>::max() ? sizeof(uint32_t) : sizeof(uint8_t); |
839 | | |
840 | 13.3k | if (node.kind == NodeKind::OBJECT) { |
841 | 12.5k | plan.object_children_begin = planned_object_children.size(); |
842 | 12.5k | DCHECK_NE(container.previous_or_source_token, INVALID_INDEX); |
843 | 12.5k | if (container.previous_or_source_token != node.payload_index) { |
844 | 12.3k | const uint32_t source_token = container.previous_or_source_token; |
845 | 12.3k | DCHECK_LT(source_token, node.payload_index); |
846 | 12.3k | const Container& source = containers[source_token]; |
847 | 12.3k | const ContainerPlan& source_plan = container_plans[source_token]; |
848 | 12.3k | DCHECK(source.kind == NodeKind::OBJECT); |
849 | 12.3k | DCHECK_EQ(source.child_count, count); |
850 | 12.3k | DCHECK_GT(source_plan.encoded_size, 0); |
851 | | |
852 | 12.3k | uint32_t child_index = container.first_child; |
853 | 25.1k | for (uint32_t index = 0; index < count; ++index) { |
854 | 12.8k | DCHECK_NE(child_index, INVALID_INDEX); |
855 | 12.8k | const Child& child = children[child_index]; |
856 | 12.8k | const uint32_t final_id = metadata.final_id(child.temporary_field_id); |
857 | 12.8k | DCHECK_LT(final_id, object_id_scratch.size()); |
858 | 12.8k | DCHECK_EQ(object_id_scratch[final_id], INVALID_INDEX); |
859 | 12.8k | object_id_scratch[final_id] = child.node_index; |
860 | 12.8k | child_index = child.next; |
861 | 12.8k | } |
862 | 12.3k | DCHECK_EQ(child_index, INVALID_INDEX); |
863 | | |
864 | 25.1k | for (uint32_t index = 0; index < count; ++index) { |
865 | 12.8k | const uint32_t final_id = |
866 | 12.8k | planned_object_children[source_plan.object_children_begin + index] |
867 | 12.8k | .final_field_id; |
868 | 12.8k | DCHECK_LT(final_id, object_id_scratch.size()); |
869 | 12.8k | const uint32_t current_node = object_id_scratch[final_id]; |
870 | 12.8k | DCHECK_NE(current_node, INVALID_INDEX); |
871 | 12.8k | planned_object_children.push_back({current_node, final_id}); |
872 | 12.8k | object_id_scratch[final_id] = INVALID_INDEX; |
873 | 12.8k | } |
874 | | #ifdef BE_TEST |
875 | | ++object_plan_reuses; |
876 | | #endif |
877 | 12.3k | } else { |
878 | 256 | uint32_t child_index = container.first_child; |
879 | 1.00M | for (uint32_t index = 0; index < count; ++index) { |
880 | 1.00M | DCHECK_NE(child_index, INVALID_INDEX); |
881 | 1.00M | const Child& child = children[child_index]; |
882 | 1.00M | planned_object_children.push_back( |
883 | 1.00M | {child.node_index, metadata.final_id(child.temporary_field_id)}); |
884 | 1.00M | child_index = child.next; |
885 | 1.00M | } |
886 | 256 | DCHECK_EQ(child_index, INVALID_INDEX); |
887 | 256 | sort_object_children(plan.object_children_begin, count); |
888 | | #ifdef BE_TEST |
889 | | ++object_plan_fallbacks; |
890 | | #endif |
891 | 256 | } |
892 | 12.5k | } |
893 | | |
894 | 13.3k | uint32_t values_size = 0; |
895 | 13.3k | uint32_t array_child_index = container.first_child; |
896 | 1.02M | for (uint32_t index = 0; index < count; ++index) { |
897 | 1.01M | uint32_t child_node = 0; |
898 | 1.01M | if (node.kind == NodeKind::OBJECT) { |
899 | 1.01M | child_node = planned_object_children[plan.object_children_begin + index].node_index; |
900 | 1.01M | } else { |
901 | 1.56k | DCHECK_NE(array_child_index, INVALID_INDEX); |
902 | 1.56k | child_node = children[array_child_index].node_index; |
903 | 1.56k | array_child_index = children[array_child_index].next; |
904 | 1.56k | } |
905 | 1.01M | const size_t child_size = plan_node(child_node); |
906 | 1.01M | if (child_size > std::numeric_limits<uint32_t>::max() - values_size) { |
907 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
908 | 0 | "Variant container values exceed the uint32 byte limit"); |
909 | 0 | } |
910 | 1.01M | values_size += static_cast<uint32_t>(child_size); |
911 | 1.01M | } |
912 | 13.3k | if (node.kind == NodeKind::ARRAY) { |
913 | 825 | DCHECK_EQ(array_child_index, INVALID_INDEX); |
914 | 825 | } |
915 | 13.3k | plan.values_size = values_size; |
916 | 13.3k | plan.offset_width = minimum_unsigned_width(values_size); |
917 | 13.3k | if (node.kind == NodeKind::OBJECT) { |
918 | 12.5k | const uint32_t maximum_id = |
919 | 12.5k | count == 0 ? 0 |
920 | 12.5k | : planned_object_children[plan.object_children_begin + count - 1] |
921 | 12.5k | .final_field_id; |
922 | 12.5k | plan.id_width = minimum_unsigned_width(maximum_id); |
923 | 12.5k | } |
924 | | |
925 | 13.3k | uint64_t encoded_size = 1 + plan.count_width + values_size; |
926 | 13.3k | encoded_size += (static_cast<uint64_t>(count) + 1) * plan.offset_width; |
927 | 13.3k | if (node.kind == NodeKind::OBJECT) { |
928 | 12.5k | encoded_size += static_cast<uint64_t>(count) * plan.id_width; |
929 | 12.5k | } |
930 | 13.3k | if (encoded_size > std::numeric_limits<size_t>::max()) { |
931 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
932 | 0 | "Variant row exceeds the addressable output size"); |
933 | 0 | } |
934 | 13.3k | plan.encoded_size = static_cast<size_t>(encoded_size); |
935 | 13.3k | return plan.encoded_size; |
936 | 13.3k | } |
937 | | |
938 | 447 | void reset_plan() { |
939 | 447 | container_plans.assign(containers.size(), {}); |
940 | 447 | planned_object_children.clear(); |
941 | 447 | size_t object_child_count = 0; |
942 | 447 | bool has_object_plan_reuse = false; |
943 | 13.8k | for (size_t index = 0; index < containers.size(); ++index) { |
944 | 13.3k | const Container& container = containers[index]; |
945 | 13.3k | if (container.kind != NodeKind::OBJECT) { |
946 | 825 | continue; |
947 | 825 | } |
948 | 13.3k | DCHECK_NE(container.previous_or_source_token, INVALID_INDEX); |
949 | 12.5k | has_object_plan_reuse |= container.previous_or_source_token != index; |
950 | 12.5k | if (container.child_count > planned_object_children.max_size() - object_child_count) { |
951 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
952 | 0 | "Variant object plan exceeds the addressable size"); |
953 | 0 | } |
954 | 12.5k | object_child_count += container.child_count; |
955 | 12.5k | } |
956 | 447 | planned_object_children.reserve(object_child_count); |
957 | 447 | if (has_object_plan_reuse) { |
958 | 10 | object_id_scratch.assign(metadata.num_keys(), INVALID_INDEX); |
959 | 437 | } else { |
960 | 437 | object_id_scratch.clear(); |
961 | 437 | } |
962 | 447 | } |
963 | | |
964 | 80.4k | size_t prepare_plan(uint32_t root_index) { return plan_node(root_index); } |
965 | | |
966 | 1.01M | size_t node_encoded_size(uint32_t node_index) const noexcept { |
967 | 1.01M | DCHECK_LT(node_index, nodes.size()); |
968 | 1.01M | const Node& node = nodes[node_index]; |
969 | 1.01M | return node.kind == NodeKind::INLINE_SCALAR || node.kind == NodeKind::SCALAR |
970 | 1.01M | ? node.payload_size |
971 | 1.01M | : container_plans[node.payload_index].encoded_size; |
972 | 1.01M | } |
973 | | |
974 | 1.09M | void write_node(uint32_t node_index, char*& output) const noexcept { |
975 | 1.09M | DCHECK_LT(node_index, nodes.size()); |
976 | 1.09M | const Node& node = nodes[node_index]; |
977 | 1.09M | if (node.kind == NodeKind::INLINE_SCALAR) { |
978 | 1.06M | std::memcpy(output, node.inline_bytes.data(), node.payload_size); |
979 | 1.06M | output += node.payload_size; |
980 | 1.06M | return; |
981 | 1.06M | } |
982 | 27.9k | if (node.kind == NodeKind::SCALAR) { |
983 | 14.5k | std::memcpy(output, scalar_bytes.data() + node.payload_index, node.payload_size); |
984 | 14.5k | output += node.payload_size; |
985 | 14.5k | return; |
986 | 14.5k | } |
987 | | |
988 | 13.3k | const Container& container = containers[node.payload_index]; |
989 | 13.3k | const ContainerPlan& plan = container_plans[node.payload_index]; |
990 | 13.3k | const uint32_t count = container.child_count; |
991 | 13.3k | const bool is_large = plan.count_width == sizeof(uint32_t); |
992 | 13.3k | if (node.kind == NodeKind::OBJECT) { |
993 | 12.5k | const auto value_header = static_cast<uint8_t>( |
994 | 12.5k | ((plan.offset_width - 1) << VARIANT_OBJECT_OFFSET_SIZE_SHIFT) | |
995 | 12.5k | ((plan.id_width - 1) << VARIANT_OBJECT_ID_SIZE_SHIFT) | |
996 | 12.5k | (is_large ? VARIANT_OBJECT_LARGE_MASK : 0)); |
997 | 12.5k | *output++ = static_cast<char>((value_header << VARIANT_VALUE_HEADER_SHIFT) | |
998 | 12.5k | static_cast<uint8_t>(VariantBasicType::OBJECT)); |
999 | 12.5k | write_unsigned(output, count, plan.count_width); |
1000 | 1.02M | for (uint32_t index = 0; index < count; ++index) { |
1001 | 1.01M | write_unsigned( |
1002 | 1.01M | output, |
1003 | 1.01M | planned_object_children[plan.object_children_begin + index].final_field_id, |
1004 | 1.01M | plan.id_width); |
1005 | 1.01M | } |
1006 | 12.5k | } else { |
1007 | 825 | const auto value_header = static_cast<uint8_t>( |
1008 | 825 | ((plan.offset_width - 1) << VARIANT_ARRAY_OFFSET_SIZE_SHIFT) | |
1009 | 825 | (is_large ? VARIANT_ARRAY_LARGE_MASK : 0)); |
1010 | 825 | *output++ = static_cast<char>((value_header << VARIANT_VALUE_HEADER_SHIFT) | |
1011 | 825 | static_cast<uint8_t>(VariantBasicType::ARRAY)); |
1012 | 825 | write_unsigned(output, count, plan.count_width); |
1013 | 825 | } |
1014 | | |
1015 | 13.3k | uint32_t offset = 0; |
1016 | 13.3k | write_unsigned(output, offset, plan.offset_width); |
1017 | 13.3k | uint32_t array_child_index = container.first_child; |
1018 | 1.02M | for (uint32_t index = 0; index < count; ++index) { |
1019 | 1.01M | uint32_t child_node = 0; |
1020 | 1.01M | if (node.kind == NodeKind::OBJECT) { |
1021 | 1.01M | child_node = planned_object_children[plan.object_children_begin + index].node_index; |
1022 | 1.01M | } else { |
1023 | 1.56k | DCHECK_NE(array_child_index, INVALID_INDEX); |
1024 | 1.56k | child_node = children[array_child_index].node_index; |
1025 | 1.56k | array_child_index = children[array_child_index].next; |
1026 | 1.56k | } |
1027 | 1.01M | offset += static_cast<uint32_t>(node_encoded_size(child_node)); |
1028 | 1.01M | write_unsigned(output, offset, plan.offset_width); |
1029 | 1.01M | } |
1030 | 13.3k | DCHECK_EQ(offset, plan.values_size); |
1031 | 13.3k | if (node.kind == NodeKind::ARRAY) { |
1032 | 825 | DCHECK_EQ(array_child_index, INVALID_INDEX); |
1033 | 825 | } |
1034 | | |
1035 | 13.3k | array_child_index = container.first_child; |
1036 | 1.02M | for (uint32_t index = 0; index < count; ++index) { |
1037 | 1.01M | uint32_t child_node = 0; |
1038 | 1.01M | if (node.kind == NodeKind::OBJECT) { |
1039 | 1.01M | child_node = planned_object_children[plan.object_children_begin + index].node_index; |
1040 | 1.01M | } else { |
1041 | 1.56k | DCHECK_NE(array_child_index, INVALID_INDEX); |
1042 | 1.56k | child_node = children[array_child_index].node_index; |
1043 | 1.56k | array_child_index = children[array_child_index].next; |
1044 | 1.56k | } |
1045 | 1.01M | write_node(child_node, output); |
1046 | 1.01M | } |
1047 | 13.3k | if (node.kind == NodeKind::ARRAY) { |
1048 | 825 | DCHECK_EQ(array_child_index, INVALID_INDEX); |
1049 | 825 | } |
1050 | 13.3k | } |
1051 | | |
1052 | 310 | void release_scratch() noexcept { |
1053 | 310 | release_variant_tracked_container(scalar_bytes); |
1054 | 310 | release_variant_tracked_container(nodes); |
1055 | 310 | release_variant_tracked_container(containers); |
1056 | 310 | release_variant_tracked_container(children); |
1057 | 310 | release_variant_tracked_container(scope_stack); |
1058 | 310 | release_variant_tracked_container(object_id_scratch); |
1059 | 310 | release_variant_tracked_container(key_references); |
1060 | 310 | release_variant_tracked_container(container_plans); |
1061 | 310 | release_variant_tracked_container(planned_object_children); |
1062 | 310 | previous_object_tokens = nullptr; |
1063 | 310 | pending_object_tokens = nullptr; |
1064 | 310 | active_container = INVALID_INDEX; |
1065 | 310 | root_node = INVALID_INDEX; |
1066 | 310 | } |
1067 | | |
1068 | | VariantMetadataBuilder& metadata; |
1069 | | DorisVector<char> scalar_bytes; |
1070 | | DorisVector<Node> nodes; |
1071 | | DorisVector<Container> containers; |
1072 | | DorisVector<Child> children; |
1073 | | DorisVector<uint32_t> scope_stack; |
1074 | | DorisVector<uint32_t> object_id_scratch; |
1075 | | DorisVector<uint32_t> key_references; |
1076 | | DorisVector<ContainerPlan> container_plans; |
1077 | | DorisVector<FinalChild> planned_object_children; |
1078 | | DorisVector<uint32_t>* previous_object_tokens = nullptr; |
1079 | | DorisVector<uint32_t>* pending_object_tokens = nullptr; |
1080 | | #ifdef BE_TEST |
1081 | | size_t object_token_capacity_growths = 0; |
1082 | | size_t object_schema_hits = 0; |
1083 | | size_t object_schema_fallbacks = 0; |
1084 | | size_t object_plan_reuses = 0; |
1085 | | size_t object_plan_fallbacks = 0; |
1086 | | #endif |
1087 | | uint32_t active_container = INVALID_INDEX; |
1088 | | uint32_t root_node = INVALID_INDEX; |
1089 | | State state = State::COLLECTING; |
1090 | | }; |
1091 | | |
1092 | | struct VariantBuilder::Impl : VariantCollectionCore { |
1093 | | Impl(VariantBuilder* owner_, VariantMetadataBuilder& metadata_) |
1094 | 409 | : VariantCollectionCore(metadata_), owner(owner_) {} |
1095 | | |
1096 | 183 | void add_scalar(const VariantScalarEncodingPlan& plan) { |
1097 | 183 | VariantCollectionCore::add_scalar(plan); |
1098 | 183 | if (state == State::COLLECTED) { |
1099 | 50 | owner->_complete_collection(); |
1100 | 50 | } |
1101 | 183 | } |
1102 | | |
1103 | 1.00M | void add_null() { |
1104 | 1.00M | VariantCollectionCore::add_null(); |
1105 | 1.00M | if (state == State::COLLECTED) { |
1106 | 8 | owner->_complete_collection(); |
1107 | 8 | } |
1108 | 1.00M | } |
1109 | | |
1110 | 24 | void add_bool(bool value) { |
1111 | 24 | VariantCollectionCore::add_bool(value); |
1112 | 24 | if (state == State::COLLECTED) { |
1113 | 4 | owner->_complete_collection(); |
1114 | 4 | } |
1115 | 24 | } |
1116 | | |
1117 | 107 | void add_int(int64_t value) { |
1118 | 107 | VariantCollectionCore::add_int(value); |
1119 | 107 | if (state == State::COLLECTED) { |
1120 | 15 | owner->_complete_collection(); |
1121 | 15 | } |
1122 | 107 | } |
1123 | | |
1124 | 13 | void add_value(VariantValueRef value) { |
1125 | 13 | VariantCollectionCore::add_value(value); |
1126 | 13 | if (state == State::COLLECTED) { |
1127 | 2 | owner->_complete_collection(); |
1128 | 2 | } |
1129 | 13 | } |
1130 | | |
1131 | 639 | void finish_container(uint32_t token, NodeKind expected_kind) { |
1132 | 639 | VariantCollectionCore::finish_container(token, expected_kind); |
1133 | 639 | if (state == State::COLLECTED) { |
1134 | 62 | owner->_complete_collection(); |
1135 | 62 | } |
1136 | 639 | } |
1137 | | |
1138 | | VariantBuilder* owner; |
1139 | | }; |
1140 | | |
1141 | | struct VariantBlockBuilder::Impl { |
1142 | 334 | Impl() = default; |
1143 | | |
1144 | 334 | void reserve(const ReserveHint& hint) { |
1145 | 334 | collection.reserve(hint.scalar_bytes, hint.nodes, hint.containers, hint.children); |
1146 | 334 | roots.reserve(hint.rows); |
1147 | 334 | previous_object_tokens.reserve(hint.containers); |
1148 | 334 | pending_object_tokens.reserve(hint.containers); |
1149 | 334 | } |
1150 | | |
1151 | 282k | void ensure_open() const { |
1152 | 282k | if (terminal) { |
1153 | 4 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1154 | 4 | "Variant block builder is already finished"); |
1155 | 4 | } |
1156 | 282k | } |
1157 | | |
1158 | 202k | void ensure_active(uint64_t generation) const { |
1159 | 202k | ensure_open(); |
1160 | 202k | if (!row_active || generation != active_generation) { |
1161 | 4 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1162 | 4 | "Variant block row handle is not the active row"); |
1163 | 4 | } |
1164 | 202k | } |
1165 | | |
1166 | | #ifdef BE_TEST |
1167 | | void observe_collection_growth(const VariantCollectionCore::CapacitySnapshot& before) noexcept { |
1168 | | const VariantCollectionCore::CapacitySnapshot after = collection.capacity_snapshot(); |
1169 | | counters.scalar_capacity_growths += before.scalar_bytes != after.scalar_bytes; |
1170 | | counters.node_capacity_growths += before.nodes != after.nodes; |
1171 | | counters.container_capacity_growths += before.containers != after.containers; |
1172 | | counters.child_capacity_growths += before.children != after.children; |
1173 | | counters.scope_stack_capacity_growths += before.scope_stack != after.scope_stack; |
1174 | | counters.object_id_scratch_capacity_growths += |
1175 | | before.object_id_scratch != after.object_id_scratch; |
1176 | | counters.key_reference_capacity_growths += before.key_references != after.key_references; |
1177 | | counters.container_plan_capacity_growths += before.container_plans != after.container_plans; |
1178 | | counters.planned_object_child_capacity_growths += |
1179 | | before.planned_object_children != after.planned_object_children; |
1180 | | } |
1181 | | |
1182 | | void observe_root_growth(size_t before) { |
1183 | | counters.row_root_capacity_growths += before != roots.capacity(); |
1184 | | } |
1185 | | |
1186 | | void refresh_counters(size_t metadata_capacity, size_t metadata_growths, |
1187 | | size_t metadata_unique_keys) const noexcept { |
1188 | | const VariantCollectionCore::CapacitySnapshot capacity = collection.capacity_snapshot(); |
1189 | | counters.metadata_capacity_growths = metadata_growths; |
1190 | | counters.metadata_unique_keys = metadata_unique_keys; |
1191 | | counters.metadata_key_capacity = metadata_capacity; |
1192 | | counters.scalar_byte_capacity = capacity.scalar_bytes; |
1193 | | counters.node_capacity = capacity.nodes; |
1194 | | counters.container_capacity = capacity.containers; |
1195 | | counters.child_capacity = capacity.children; |
1196 | | counters.scope_stack_capacity = capacity.scope_stack; |
1197 | | counters.object_id_scratch_capacity = capacity.object_id_scratch; |
1198 | | counters.key_reference_capacity = capacity.key_references; |
1199 | | counters.container_plan_capacity = capacity.container_plans; |
1200 | | counters.planned_object_child_capacity = capacity.planned_object_children; |
1201 | | counters.row_root_capacity = roots.capacity(); |
1202 | | counters.object_token_capacity_growths = collection.object_token_capacity_growths; |
1203 | | counters.object_schema_hits = collection.object_schema_hits; |
1204 | | counters.object_schema_fallbacks = collection.object_schema_fallbacks; |
1205 | | counters.object_plan_reuses = collection.object_plan_reuses; |
1206 | | counters.object_plan_fallbacks = collection.object_plan_fallbacks; |
1207 | | counters.previous_object_token_capacity = previous_object_tokens.capacity(); |
1208 | | counters.pending_object_token_capacity = pending_object_tokens.capacity(); |
1209 | | } |
1210 | | |
1211 | | mutable TestCounters counters; |
1212 | | VariantCollectionCore::CapacitySnapshot row_capacity_start {}; |
1213 | | #endif |
1214 | | |
1215 | | VariantMetadataBuilder metadata; |
1216 | | VariantCollectionCore collection {metadata}; |
1217 | | DorisVector<uint32_t> roots; |
1218 | | DorisVector<uint32_t> previous_object_tokens; |
1219 | | DorisVector<uint32_t> pending_object_tokens; |
1220 | | VariantCollectionCore::Checkpoint row_start {}; |
1221 | | uint64_t current_generation = 0; |
1222 | | uint64_t active_generation = 0; |
1223 | | bool row_active = false; |
1224 | | bool terminal = false; |
1225 | | #ifdef BE_TEST |
1226 | | bool counters_finalized = false; |
1227 | | #endif |
1228 | | }; |
1229 | | |
1230 | 272 | VariantBlockBuilder::VariantBlockBuilder() : VariantBlockBuilder(ReserveHint {}) {} |
1231 | | |
1232 | 334 | VariantBlockBuilder::VariantBlockBuilder(ReserveHint hint) : _impl(std::make_unique<Impl>()) { |
1233 | 334 | _impl->metadata._reserve_keys(hint.metadata_keys); |
1234 | 334 | _impl->reserve(hint); |
1235 | | #ifdef BE_TEST |
1236 | | _impl->refresh_counters(_impl->metadata._key_capacity(), |
1237 | | _impl->metadata._key_capacity_growths(), _impl->metadata.num_keys()); |
1238 | | #endif |
1239 | 334 | } |
1240 | | |
1241 | 334 | VariantBlockBuilder::~VariantBlockBuilder() { |
1242 | 334 | if (_impl != nullptr && _impl->row_active) { |
1243 | 0 | _abort_row_noexcept(_impl->active_generation); |
1244 | 0 | } |
1245 | 334 | } |
1246 | | |
1247 | 2.10k | void VariantBlockBuilder::_add_scalar(uint64_t generation, const VariantScalarEncodingPlan& plan) { |
1248 | 2.10k | _impl->ensure_active(generation); |
1249 | 2.10k | _impl->collection.add_scalar(plan); |
1250 | 2.10k | } |
1251 | | |
1252 | 66.6k | void VariantBlockBuilder::_add_null(uint64_t generation) { |
1253 | 66.6k | _impl->ensure_active(generation); |
1254 | 66.6k | _impl->collection.add_null(); |
1255 | 66.6k | } |
1256 | | |
1257 | 22 | void VariantBlockBuilder::_add_bool(uint64_t generation, bool value) { |
1258 | 22 | _impl->ensure_active(generation); |
1259 | 22 | _impl->collection.add_bool(value); |
1260 | 22 | } |
1261 | | |
1262 | 13.6k | void VariantBlockBuilder::_add_int(uint64_t generation, int64_t value) { |
1263 | 13.6k | _impl->ensure_active(generation); |
1264 | 13.6k | _impl->collection.add_int(value); |
1265 | 13.6k | } |
1266 | | |
1267 | 11 | void VariantBlockBuilder::_add_value(uint64_t generation, VariantValueRef value) { |
1268 | 11 | _impl->ensure_active(generation); |
1269 | 11 | _impl->collection.add_value(value); |
1270 | 11 | } |
1271 | | |
1272 | 13.2k | uint32_t VariantBlockBuilder::_start_container(uint64_t generation, bool object) { |
1273 | 13.2k | _impl->ensure_active(generation); |
1274 | 13.2k | return _impl->collection.start_container(object ? VariantCollectionCore::NodeKind::OBJECT |
1275 | 13.2k | : VariantCollectionCore::NodeKind::ARRAY); |
1276 | 13.2k | } |
1277 | | |
1278 | 13.5k | void VariantBlockBuilder::_add_key(uint64_t generation, uint32_t token, StringRef key) { |
1279 | 13.5k | _impl->ensure_active(generation); |
1280 | 13.5k | _impl->collection.add_key(token, key); |
1281 | 13.5k | } |
1282 | | |
1283 | 12.7k | void VariantBlockBuilder::_finish_container(uint64_t generation, uint32_t token, bool object) { |
1284 | 12.7k | _impl->ensure_active(generation); |
1285 | 12.7k | _impl->collection.finish_container(token, object ? VariantCollectionCore::NodeKind::OBJECT |
1286 | 12.7k | : VariantCollectionCore::NodeKind::ARRAY); |
1287 | 12.7k | } |
1288 | | |
1289 | 80.2k | void VariantBlockBuilder::_finish_row(uint64_t generation) { |
1290 | 80.2k | _impl->ensure_active(generation); |
1291 | 80.2k | if (_impl->collection.state == VariantCollectionCore::State::COLLECTING) { |
1292 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an incomplete Variant row"); |
1293 | 0 | } |
1294 | 80.2k | DCHECK(_impl->collection.state == VariantCollectionCore::State::COLLECTED); |
1295 | | #ifdef BE_TEST |
1296 | | const size_t root_capacity = _impl->roots.capacity(); |
1297 | | #endif |
1298 | 80.2k | _impl->roots.push_back(_impl->collection.root_node); |
1299 | | #ifdef BE_TEST |
1300 | | _impl->observe_root_growth(root_capacity); |
1301 | | #endif |
1302 | 80.2k | _impl->metadata._complete_row(); |
1303 | 80.2k | _impl->collection.discard_key_references(_impl->row_start.key_references); |
1304 | 80.2k | _impl->collection.publish_object_links(); |
1305 | 80.2k | _impl->collection.state = VariantCollectionCore::State::FINISHED; |
1306 | | #ifdef BE_TEST |
1307 | | _impl->observe_collection_growth(_impl->row_capacity_start); |
1308 | | #endif |
1309 | 80.2k | _impl->row_active = false; |
1310 | 80.2k | } |
1311 | | |
1312 | 80.3k | void VariantBlockBuilder::_abort_row_noexcept(uint64_t generation) noexcept { |
1313 | 80.3k | if (!_impl->row_active || generation != _impl->active_generation) { |
1314 | 80.2k | return; |
1315 | 80.2k | } |
1316 | 38 | _impl->metadata._abort_row( |
1317 | 38 | _impl->collection.key_reference_data(_impl->row_start.key_references), |
1318 | 38 | _impl->collection.key_reference_count(_impl->row_start.key_references), true); |
1319 | 38 | _impl->collection.rollback(_impl->row_start); |
1320 | | #ifdef BE_TEST |
1321 | | _impl->observe_collection_growth(_impl->row_capacity_start); |
1322 | | #endif |
1323 | 38 | _impl->row_active = false; |
1324 | 38 | } |
1325 | | |
1326 | 9 | void VariantBlockBuilder::_abort_row(uint64_t generation) { |
1327 | 9 | _impl->ensure_active(generation); |
1328 | 9 | _abort_row_noexcept(generation); |
1329 | 9 | } |
1330 | | |
1331 | 80.3k | VariantBlockBuilder::Row VariantBlockBuilder::begin_row() { |
1332 | 80.3k | _impl->ensure_open(); |
1333 | 80.3k | if (_impl->row_active) { |
1334 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1335 | 1 | "Variant block builder already has an active row"); |
1336 | 1 | } |
1337 | 80.3k | if (_impl->current_generation >= ABORTED_ROW_GENERATION - 1) { |
1338 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1339 | 0 | "Variant block row generation exceeds the uint64 limit"); |
1340 | 0 | } |
1341 | 80.3k | ++_impl->current_generation; |
1342 | 80.3k | DCHECK(_impl->pending_object_tokens.empty()); |
1343 | | #ifdef BE_TEST |
1344 | | _impl->row_capacity_start = _impl->collection.capacity_snapshot(); |
1345 | | #endif |
1346 | 80.3k | _impl->collection.begin_collection(&_impl->previous_object_tokens, |
1347 | 80.3k | &_impl->pending_object_tokens); |
1348 | 80.3k | _impl->row_start = _impl->collection.checkpoint(); |
1349 | 80.3k | _impl->metadata._begin_row(); |
1350 | 80.3k | _impl->active_generation = _impl->current_generation; |
1351 | 80.3k | _impl->row_active = true; |
1352 | 80.3k | return {this, _impl->active_generation}; |
1353 | 80.3k | } |
1354 | | |
1355 | 313 | VariantEncodedBlock VariantBlockBuilder::finish_block() { |
1356 | 313 | _impl->ensure_open(); |
1357 | 313 | if (_impl->row_active) { |
1358 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1359 | 1 | "Cannot finish a Variant block while a row is active"); |
1360 | 1 | } |
1361 | 312 | _impl->terminal = true; |
1362 | 312 | _impl->metadata.seal(); |
1363 | | #ifdef BE_TEST |
1364 | | const VariantCollectionCore::CapacitySnapshot before = _impl->collection.capacity_snapshot(); |
1365 | | #endif |
1366 | 312 | _impl->collection.reset_plan(); |
1367 | | |
1368 | 312 | DorisVector<uint32_t> offsets; |
1369 | 312 | if (_impl->roots.size() == offsets.max_size()) { |
1370 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1371 | 0 | "Variant block row offsets exceed the addressable size"); |
1372 | 0 | } |
1373 | 312 | offsets.reserve(_impl->roots.size() + 1); |
1374 | 312 | offsets.push_back(0); |
1375 | 312 | uint32_t total_size = 0; |
1376 | 80.2k | for (uint32_t root : _impl->roots) { |
1377 | 80.2k | const size_t row_size = _impl->collection.prepare_plan(root); |
1378 | 80.2k | if (row_size > std::numeric_limits<uint32_t>::max() - total_size) { |
1379 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1380 | 0 | "Variant block values exceed the ColumnString uint32 byte limit"); |
1381 | 0 | } |
1382 | 80.2k | total_size += static_cast<uint32_t>(row_size); |
1383 | 80.2k | offsets.push_back(total_size); |
1384 | 80.2k | } |
1385 | | #ifdef BE_TEST |
1386 | | _impl->observe_collection_growth(before); |
1387 | | #endif |
1388 | 312 | auto storage = std::make_unique<VariantEncodedBlockStorage>(); |
1389 | 312 | storage->offsets = std::move(offsets); |
1390 | 312 | storage->values.resize(total_size); |
1391 | 312 | char* output = storage->values.data(); |
1392 | 80.2k | for (uint32_t root : _impl->roots) { |
1393 | 80.2k | _impl->collection.write_node(root, output); |
1394 | 80.2k | } |
1395 | 312 | DCHECK_EQ(output, storage->values.data() + storage->values.size()); |
1396 | | |
1397 | | #ifdef BE_TEST |
1398 | | _impl->refresh_counters(_impl->metadata._key_capacity(), |
1399 | | _impl->metadata._key_capacity_growths(), _impl->metadata.num_keys()); |
1400 | | _impl->counters_finalized = true; |
1401 | | #endif |
1402 | 312 | _impl->metadata._move_encoded_metadata(*storage); |
1403 | 312 | _impl->collection.release_scratch(); |
1404 | 312 | release_variant_tracked_container(_impl->roots); |
1405 | 312 | release_variant_tracked_container(_impl->previous_object_tokens); |
1406 | 312 | release_variant_tracked_container(_impl->pending_object_tokens); |
1407 | 312 | return VariantEncodedBlock(std::move(storage)); |
1408 | 312 | } |
1409 | | |
1410 | | #ifdef BE_TEST |
1411 | | const VariantBlockBuilder::TestCounters& VariantBlockBuilder::test_counters() const noexcept { |
1412 | | if (!_impl->counters_finalized) { |
1413 | | _impl->refresh_counters(_impl->metadata._key_capacity(), |
1414 | | _impl->metadata._key_capacity_growths(), |
1415 | | _impl->metadata.num_keys()); |
1416 | | } |
1417 | | return _impl->counters; |
1418 | | } |
1419 | | #endif |
1420 | | |
1421 | | VariantBuilder::ObjectScope::ObjectScope(VariantBuilder* builder, uint32_t token) noexcept |
1422 | 48 | : _builder(builder), _token(token) {} |
1423 | | |
1424 | | VariantBuilder::ObjectScope::ObjectScope(ObjectScope&& other) noexcept |
1425 | 0 | : _builder(std::exchange(other._builder, nullptr)), _token(other._token) {} |
1426 | | |
1427 | 1.00M | void VariantBuilder::ObjectScope::add_key(StringRef key) { |
1428 | 1.00M | if (_builder == nullptr) { |
1429 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object scope is already finished"); |
1430 | 0 | } |
1431 | 1.00M | _builder->_add_key(_token, key); |
1432 | 1.00M | } |
1433 | | |
1434 | 47 | void VariantBuilder::ObjectScope::finish() { |
1435 | 47 | if (_builder == nullptr) { |
1436 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object scope is already finished"); |
1437 | 1 | } |
1438 | 46 | _builder->_finish_object(_token); |
1439 | 46 | _builder = nullptr; |
1440 | 46 | } |
1441 | | |
1442 | | VariantBuilder::ArrayScope::ArrayScope(VariantBuilder* builder, uint32_t token) noexcept |
1443 | 693 | : _builder(builder), _token(token) {} |
1444 | | |
1445 | | VariantBuilder::ArrayScope::ArrayScope(ArrayScope&& other) noexcept |
1446 | 714 | : _builder(std::exchange(other._builder, nullptr)), _token(other._token) {} |
1447 | | |
1448 | 593 | void VariantBuilder::ArrayScope::finish() { |
1449 | 593 | if (_builder == nullptr) { |
1450 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant array scope is already finished"); |
1451 | 0 | } |
1452 | 593 | _builder->_finish_array(_token); |
1453 | 593 | _builder = nullptr; |
1454 | 593 | } |
1455 | | |
1456 | | VariantBuilder::VariantBuilder(VariantMetadataBuilder& metadata) |
1457 | 409 | : _impl(std::make_unique<Impl>(this, metadata)) { |
1458 | 409 | metadata._begin_row(); |
1459 | 409 | } |
1460 | | |
1461 | 408 | VariantBuilder::~VariantBuilder() { |
1462 | 408 | _abort_noexcept(); |
1463 | 408 | } |
1464 | | |
1465 | 1.00M | void VariantBuilder::add_null() { |
1466 | 1.00M | _impl->add_null(); |
1467 | 1.00M | } |
1468 | | |
1469 | 24 | void VariantBuilder::add_bool(bool value) { |
1470 | 24 | _impl->add_bool(value); |
1471 | 24 | } |
1472 | | |
1473 | 107 | void VariantBuilder::add_int(int64_t value) { |
1474 | 107 | _impl->add_int(value); |
1475 | 107 | } |
1476 | | |
1477 | 10 | void VariantBuilder::add_float(float value) { |
1478 | 10 | _impl->add_scalar(VariantScalarEncodingPlan::float32(value)); |
1479 | 10 | } |
1480 | | |
1481 | 9 | void VariantBuilder::add_double(double value) { |
1482 | 9 | _impl->add_scalar(VariantScalarEncodingPlan::float64(value)); |
1483 | 9 | } |
1484 | | |
1485 | 21 | void VariantBuilder::add_decimal(__int128 unscaled, uint8_t scale) { |
1486 | 21 | _impl->add_scalar(VariantScalarEncodingPlan::decimal(unscaled, scale)); |
1487 | 21 | } |
1488 | | |
1489 | 35 | void VariantBuilder::add_decimal(__int128 unscaled, uint8_t scale, uint8_t width) { |
1490 | 35 | if (width != 4 && width != 8 && width != 16) { |
1491 | 4 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1492 | 4 | "Variant decimal width {} must be one of 4, 8, or 16", width); |
1493 | 4 | } |
1494 | 31 | _impl->add_scalar(VariantScalarEncodingPlan::decimal(unscaled, scale, width)); |
1495 | 31 | } |
1496 | | |
1497 | 6 | void VariantBuilder::add_date(int32_t days_since_epoch) { |
1498 | 6 | _impl->add_scalar(VariantScalarEncodingPlan::date(days_since_epoch)); |
1499 | 6 | } |
1500 | | |
1501 | 17 | void VariantBuilder::add_timestamp_micros(int64_t value, bool utc_adjusted) { |
1502 | 17 | _impl->add_scalar(VariantScalarEncodingPlan::timestamp_micros(value, utc_adjusted)); |
1503 | 17 | } |
1504 | | |
1505 | 17 | void VariantBuilder::add_timestamp_nanos(int64_t value, bool utc_adjusted) { |
1506 | 17 | _impl->add_scalar(VariantScalarEncodingPlan::timestamp_nanos(value, utc_adjusted)); |
1507 | 17 | } |
1508 | | |
1509 | 7 | void VariantBuilder::add_time_ntz_micros(int64_t value) { |
1510 | 7 | _impl->add_scalar(VariantScalarEncodingPlan::time_ntz_micros(value)); |
1511 | 7 | } |
1512 | | |
1513 | 9 | void VariantBuilder::add_binary(StringRef value) { |
1514 | 9 | _impl->add_scalar(VariantScalarEncodingPlan::binary(value)); |
1515 | 9 | } |
1516 | | |
1517 | 41 | void VariantBuilder::add_string(StringRef value) { |
1518 | 41 | _impl->add_scalar(VariantScalarEncodingPlan::string(value)); |
1519 | 41 | } |
1520 | | |
1521 | 6 | void VariantBuilder::add_uuid(const std::array<uint8_t, 16>& value) { |
1522 | 6 | _impl->add_scalar(VariantScalarEncodingPlan::uuid(value)); |
1523 | 6 | } |
1524 | | |
1525 | 16 | void VariantBuilder::add_largeint(__int128 value) { |
1526 | 16 | _impl->add_scalar(VariantScalarEncodingPlan::largeint(value)); |
1527 | 16 | } |
1528 | | |
1529 | 13 | void VariantBuilder::add_value(VariantValueRef value) { |
1530 | 13 | _impl->add_value(value); |
1531 | 13 | } |
1532 | | |
1533 | 48 | VariantBuilder::ObjectScope VariantBuilder::start_object() { |
1534 | 48 | return {this, _start_object()}; |
1535 | 48 | } |
1536 | | |
1537 | 693 | VariantBuilder::ArrayScope VariantBuilder::start_array() { |
1538 | 693 | return {this, _start_array()}; |
1539 | 693 | } |
1540 | | |
1541 | 48 | uint32_t VariantBuilder::_start_object() { |
1542 | 48 | return _impl->start_container(Impl::NodeKind::OBJECT); |
1543 | 48 | } |
1544 | | |
1545 | 693 | uint32_t VariantBuilder::_start_array() { |
1546 | 693 | return _impl->start_container(Impl::NodeKind::ARRAY); |
1547 | 693 | } |
1548 | | |
1549 | 1.00M | void VariantBuilder::_add_key(uint32_t token, StringRef key) { |
1550 | 1.00M | _impl->add_key(token, key); |
1551 | 1.00M | } |
1552 | | |
1553 | 46 | void VariantBuilder::_finish_object(uint32_t token) { |
1554 | 46 | _impl->finish_container(token, Impl::NodeKind::OBJECT); |
1555 | 46 | } |
1556 | | |
1557 | 593 | void VariantBuilder::_finish_array(uint32_t token) { |
1558 | 593 | _impl->finish_container(token, Impl::NodeKind::ARRAY); |
1559 | 593 | } |
1560 | | |
1561 | 141 | void VariantBuilder::_complete_collection() { |
1562 | 141 | DCHECK(_impl->state == Impl::State::COLLECTED); |
1563 | 141 | _impl->metadata._complete_row(); |
1564 | 141 | } |
1565 | | |
1566 | 263 | void VariantBuilder::abort() { |
1567 | 263 | if (_impl->metadata.is_sealed()) { |
1568 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1569 | 1 | "Cannot abort a Variant row after metadata seal"); |
1570 | 1 | } |
1571 | 262 | if (_impl->state == Impl::State::ABORTED) { |
1572 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already aborted"); |
1573 | 0 | } |
1574 | 262 | if (_impl->state == Impl::State::FINISHED) { |
1575 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already finished"); |
1576 | 0 | } |
1577 | 262 | _abort_noexcept(); |
1578 | 262 | } |
1579 | | |
1580 | 670 | void VariantBuilder::_abort_noexcept() noexcept { |
1581 | 670 | if (_impl == nullptr || |
1582 | 670 | (_impl->state != Impl::State::COLLECTING && _impl->state != Impl::State::COLLECTED) || |
1583 | 670 | _impl->metadata.is_sealed()) { |
1584 | 399 | return; |
1585 | 399 | } |
1586 | 271 | const bool was_collecting = _impl->state == Impl::State::COLLECTING; |
1587 | 271 | _impl->metadata._abort_row(_impl->key_references.data(), _impl->key_references.size(), |
1588 | 271 | was_collecting); |
1589 | 271 | _impl->state = Impl::State::ABORTED; |
1590 | 271 | } |
1591 | | |
1592 | 392 | void VariantBuilder::finish_row(std::string& destination) { |
1593 | 392 | if (_impl->state == Impl::State::FINISHED) { |
1594 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already finished"); |
1595 | 1 | } |
1596 | 391 | if (_impl->state == Impl::State::ABORTED) { |
1597 | 251 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an aborted Variant row"); |
1598 | 251 | } |
1599 | 140 | if (_impl->state == Impl::State::COLLECTING) { |
1600 | 1 | throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an incomplete Variant row"); |
1601 | 1 | } |
1602 | 139 | if (!_impl->metadata.is_sealed()) { |
1603 | 2 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1604 | 2 | "Variant metadata must be sealed before finishing a row"); |
1605 | 2 | } |
1606 | | |
1607 | 137 | _impl->reset_plan(); |
1608 | 137 | const size_t encoded_size = _impl->prepare_plan(_impl->root_node); |
1609 | 137 | if (encoded_size > destination.max_size() - destination.size()) { |
1610 | 0 | throw Exception(ErrorCode::INVALID_ARGUMENT, |
1611 | 0 | "Variant destination exceeds the addressable string size"); |
1612 | 0 | } |
1613 | | |
1614 | 137 | const size_t old_size = destination.size(); |
1615 | 137 | destination.resize(old_size + encoded_size); |
1616 | 137 | char* output = destination.data() + old_size; |
1617 | 137 | _impl->write_node(_impl->root_node, output); |
1618 | 137 | DCHECK_EQ(output, destination.data() + destination.size()); |
1619 | 137 | _impl->state = Impl::State::FINISHED; |
1620 | 137 | } |
1621 | | |
1622 | 0 | bool VariantBuilder::is_finished() const noexcept { |
1623 | 0 | return _impl->state == Impl::State::FINISHED; |
1624 | 0 | } |
1625 | | |
1626 | | } // namespace doris |