Skip to content

Commit eebf00a

Browse files
authored
Merge branch 'ClickHouse:master' into master
2 parents 32dc692 + 516a6ed commit eebf00a

File tree

7 files changed

+70
-83
lines changed

7 files changed

+70
-83
lines changed

ci/praktika/json.html

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,29 +1283,35 @@
12831283
td.appendChild(link);
12841284
}
12851285

1286-
// Create a container for labels and linklabels
1287-
const labelsContainer = document.createElement('span');
1288-
labelsContainer.className = 'labels-container';
1289-
labelsContainer.style.userSelect = 'none'; // Prevent selection
1290-
labelsContainer.style.marginLeft = '4px';
1286+
// Helper to apply common label styling
1287+
const applyLabelStyles = (element, text) => {
1288+
element.textContent = text;
1289+
element.style.marginLeft = '6px';
1290+
element.style.padding = '2px 8px';
1291+
element.style.backgroundColor = stringToColor(text, true);
1292+
element.style.color = 'white';
1293+
element.style.fontSize = '12px';
1294+
element.style.borderRadius = '8px';
1295+
element.style.fontWeight = 'bold';
1296+
element.style.display = 'inline-block';
1297+
element.style.verticalAlign = 'middle';
1298+
};
1299+
1300+
// Add whitespace before labels if any exist
1301+
if ((labels && labels.length > 0) || (linklabels && linklabels.length > 0)) {
1302+
td.appendChild(document.createTextNode(' '));
1303+
}
12911304

1305+
// Render plain labels
12921306
if (Array.isArray(labels)) {
12931307
labels.forEach(labelStr => {
12941308
const label = document.createElement('span');
1295-
label.textContent = labelStr;
1296-
label.style.marginLeft = '6px';
1297-
label.style.padding = '2px 8px';
1298-
label.style.backgroundColor = stringToColor(labelStr, true);
1299-
label.style.color = 'white';
1300-
label.style.fontSize = '12px';
1301-
label.style.borderRadius = '8px';
1302-
label.style.fontWeight = 'bold';
1303-
label.style.display = 'inline-block';
1304-
label.style.verticalAlign = 'middle';
1305-
labelsContainer.appendChild(label);
1309+
applyLabelStyles(label, labelStr);
1310+
td.appendChild(label);
13061311
});
13071312
}
13081313

1314+
// Render link labels
13091315
if (Array.isArray(linklabels)) {
13101316
linklabels.forEach(item => {
13111317
let text, href;
@@ -1319,42 +1325,18 @@
13191325
href = '';
13201326
}
13211327
if (!text) return;
1328+
1329+
const element = href ? document.createElement('a') : document.createElement('span');
13221330
if (href) {
1323-
const a = document.createElement('a');
1324-
a.textContent = text;
1325-
a.href = href;
1326-
a.target = '_blank';
1327-
a.rel = 'noopener noreferrer';
1328-
a.style.marginLeft = '6px';
1329-
a.style.padding = '2px 8px';
1330-
a.style.backgroundColor = stringToColor(text, true);
1331-
a.style.color = 'white';
1332-
a.style.fontSize = '12px';
1333-
a.style.borderRadius = '8px';
1334-
a.style.fontWeight = 'bold';
1335-
a.style.display = 'inline-block';
1336-
labelsContainer.appendChild(a);
1337-
} else {
1338-
const label = document.createElement('span');
1339-
label.textContent = text;
1340-
label.style.marginLeft = '6px';
1341-
label.style.padding = '2px 8px';
1342-
label.style.backgroundColor = stringToColor(text, true);
1343-
label.style.color = 'white';
1344-
label.style.fontSize = '12px';
1345-
label.style.borderRadius = '8px';
1346-
label.style.fontWeight = 'bold';
1347-
label.style.display = 'inline-block';
1348-
labelsContainer.appendChild(label);
1331+
element.href = href;
1332+
element.target = '_blank';
1333+
element.rel = 'noopener noreferrer';
13491334
}
1335+
applyLabelStyles(element, text);
1336+
td.appendChild(element);
13501337
});
13511338
}
13521339

1353-
// Only append if there are labels
1354-
if (labelsContainer.childNodes.length > 0) {
1355-
td.appendChild(labelsContainer);
1356-
}
1357-
13581340
} else if (column === 'start_time') {
13591341
td.textContent = value ? formatTimestamp(value, false) : '';
13601342

@@ -1399,11 +1381,9 @@
13991381
infoCell.classList.add('info-text');
14001382

14011383
if (infoText) {
1402-
let content = document.createElement('div');
1403-
infoCell.innerHTML = '<pre></pre>';
1404-
// Append content securely, without interpreting HTML tags.
1405-
infoCell.firstChild.textContent = infoText;
1406-
infoCell.appendChild(content);
1384+
const pre = document.createElement('pre');
1385+
pre.textContent = infoText;
1386+
infoCell.appendChild(pre);
14071387
}
14081388

14091389
// Handle subResults

src/Columns/ColumnObject.cpp

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,6 @@ void ColumnObject::doInsertFrom(const IColumn & src, size_t n)
643643

644644
/// Finally, insert paths from shared data.
645645
insertFromSharedDataAndFillRemainingDynamicPaths(src_object_column, std::move(src_dynamic_paths_for_shared_data), n, 1);
646-
validateDynamicPathsSizes();
647646
}
648647

649648
#if !defined(DEBUG_OR_SANITIZER_BUILD)
@@ -678,7 +677,6 @@ void ColumnObject::doInsertRangeFrom(const IColumn & src, size_t start, size_t l
678677

679678
/// Finally, insert paths from shared data.
680679
insertFromSharedDataAndFillRemainingDynamicPaths(src_object_column, std::move(src_dynamic_paths_for_shared_data), start, length);
681-
validateDynamicPathsSizes();
682680
}
683681

684682
void ColumnObject::insertFromSharedDataAndFillRemainingDynamicPaths(const DB::ColumnObject & src_object_column, std::vector<std::string_view> && src_dynamic_paths_for_shared_data, size_t start, size_t length)
@@ -742,32 +740,15 @@ void ColumnObject::insertFromSharedDataAndFillRemainingDynamicPaths(const DB::Co
742740
/// Deserialize binary value into dynamic column from shared data.
743741
if (it->second->size() != current_size)
744742
{
745-
if (src_object_column.getDynamicPaths().contains(path))
746-
throw Exception(
747-
ErrorCodes::LOGICAL_ERROR,
748-
"Path {} is present both in shared data and in dynamic paths at row {}. Dynamic path value type: {}. Shared data path value type: {}",
749-
path,
750-
row,
751-
src_object_column.getDynamicPathsPtrs().at(toString(path))->getTypeNameAt(row),
752-
decodeDataType(src_shared_data_values->getDataAt(i).toString())->getName());
753-
754-
for (size_t j = offset; j != end; ++j)
755-
{
756-
if (j != i && src_shared_data_paths->getDataAt(j).toView() == path)
757-
throw Exception(
758-
ErrorCodes::LOGICAL_ERROR,
759-
"Path {} is duplicated inside shared data at offsets {} and {}. First value type: {}. Second value type: {}",
760-
path,
761-
i,
762-
j,
763-
decodeDataType(src_shared_data_values->getDataAt(i).toString())->getName(),
764-
decodeDataType(src_shared_data_values->getDataAt(j).toString())->getName());
765-
}
766-
743+
src_object_column.validateDynamicPathsAndSharedData();
767744
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unexpected size of dynamic path {}: {} != {}", path, it->second->size(), current_size);
768745
}
769746
deserializeValueFromSharedData(src_shared_data_values, i, *it->second);
770747
}
748+
else if (auto * dynamic_path_column = tryToAddNewDynamicPath(path))
749+
{
750+
deserializeValueFromSharedData(src_shared_data_values, i, *dynamic_path_column);
751+
}
771752
else
772753
{
773754
/// Before inserting this path into shared data check if we need to
@@ -2094,14 +2075,28 @@ int ColumnObject::doCompareAt(size_t n, size_t m, const IColumn & rhs, int nan_d
20942075
return 1;
20952076
}
20962077

2097-
void ColumnObject::validateDynamicPathsSizes() const
2078+
void ColumnObject::validateDynamicPathsAndSharedData(size_t shared_data_offset) const
20982079
{
2080+
if (dynamic_paths.empty())
2081+
return;
2082+
20992083
size_t expected_size = shared_data->size();
21002084
for (const auto & [path, column] : dynamic_paths)
21012085
{
21022086
if (column->size() != expected_size)
21032087
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unexpected size of dynamic path {}: {} != {}", path, column->size(), expected_size);
21042088
}
2089+
2090+
const auto & shared_data_offsets = getSharedDataOffsets();
2091+
const auto [shared_data_paths, _] = getSharedDataPathsAndValues();
2092+
size_t shared_data_paths_start = shared_data_offsets[ssize_t(shared_data_offset) - 1];
2093+
size_t shared_data_paths_end = shared_data_offsets.back();
2094+
for (size_t i = shared_data_paths_start; i != shared_data_paths_end; ++i)
2095+
{
2096+
auto path = shared_data_paths->getDataAt(i);
2097+
if (dynamic_paths.contains(path))
2098+
throw Exception(ErrorCodes::LOGICAL_ERROR, "Path {} is present both in dynamic paths and in shared data", path.toString());
2099+
}
21052100
}
21062101

21072102
}

src/Columns/ColumnObject.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ class ColumnObject final : public COWHelper<IColumnHelper<ColumnObject>, ColumnO
282282
/// Insert all the data from shared data with specified path to dynamic column.
283283
static void fillPathColumnFromSharedData(IColumn & path_column, StringRef path, const ColumnPtr & shared_data_column, size_t start, size_t end);
284284

285-
/// Validate that all dynamic paths have correct sizes.
286-
void validateDynamicPathsSizes() const;
285+
/// Validate that all dynamic paths have correct sizes and that shared data doesn't contain any dynamic paths.
286+
void validateDynamicPathsAndSharedData(size_t shared_data_offset = 0) const;
287287

288288
/// Class that allows to iterate over paths inside single row in ColumnObject in sorted order.
289289
class SortedPathsIterator

src/DataTypes/Serializations/SerializationObject.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,6 @@ void SerializationObject::serializeBinaryBulkWithMultipleStreams(
789789
}
790790

791791
const auto & column_object = assert_cast<const ColumnObject &>(column);
792-
column_object.validateDynamicPathsSizes();
793792
const auto & typed_paths = column_object.getTypedPaths();
794793

795794
if (object_state->serialization_version.value == SerializationVersion::FLATTENED)
@@ -1041,12 +1040,13 @@ void SerializationObject::deserializeBinaryBulkWithMultipleStreams(
10411040
settings.path.pop_back();
10421041
}
10431042

1043+
size_t shared_data_previous_size = shared_data->size();
10441044
settings.path.push_back(Substream::ObjectSharedData);
10451045
object_state->shared_data_serialization->deserializeBinaryBulkWithMultipleStreams(shared_data, rows_offset, limit, settings, object_state->shared_data_state, cache);
10461046
settings.path.pop_back();
10471047
settings.path.pop_back();
10481048

1049-
column_object.validateDynamicPathsSizes();
1049+
column_object.validateDynamicPathsAndSharedData(shared_data_previous_size);
10501050
}
10511051

10521052
void SerializationObject::serializeBinary(const Field & field, WriteBuffer & ostr, const DB::FormatSettings & settings) const
@@ -1319,8 +1319,6 @@ void SerializationObject::deserializeBinary(IColumn & col, ReadBuffer & istr, co
13191319
if (column->size() == prev_size)
13201320
column->insertDefault();
13211321
}
1322-
1323-
column_object.validateDynamicPathsSizes();
13241322
}
13251323

13261324
SerializationPtr SerializationObject::TypedPathSubcolumnCreator::create(const DB::SerializationPtr & prev, const DataTypePtr &) const

src/DataTypes/Serializations/SerializationVariantElement.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ void SerializationVariantElement::deserializeBinaryBulkWithMultipleStreams(
250250
nested_serialization->deserializeBinaryBulkWithMultipleStreams(variant_element_state->variant, *variant_rows_offset, *variant_limit, nested_settings, variant_element_state->variant_element_state, cache);
251251
removeVariantFromPath(settings.path);
252252

253+
/// We want to keep dynamic structure of the variant during deserialization.
254+
/// Keeping dynamic structure improves performance of insertFrom/insertRangeFrom methods.
255+
if (mutable_column->empty())
256+
mutable_column->takeDynamicStructureFromColumn(variant_element_state->variant);
257+
253258
/// If there was nothing to deserialize or nothing was actually deserialized when variant_limit > 0, just insert defaults.
254259
/// The second case means that we don't have a stream for such sub-column. It may happen during ALTER MODIFY column with Variant extension.
255260
if (variant_limit == 0 || variant_element_state->variant->empty())

tests/queries/0_stateless/03732_json_duplicated_path_in_dynamic_paths_and_shared_data_bug.reference

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
drop table if exists test;
2+
drop table if exists test2;
3+
create table test (id UInt64, json JSON) engine=MergeTree order by id;
4+
insert into test select number, '{}' from numbers(100000);
5+
alter table test update json = '{"a" : 42}' where id > 50000 settings mutations_sync=1;
6+
create table test2 (json JSON) engine=MergeTree order by tuple();
7+
insert into test2 select if(id < 75000, json, '{"a" : 42}'::JSON) from test;
8+
select * from test2 format Null;
9+

0 commit comments

Comments
 (0)