Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/en/engines/table-engines/integrations/iceberg.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,16 @@ ClickHouse supports reading Iceberg tables that use the following deletion metho

- [Position deletes](https://iceberg.apache.org/spec/#position-delete-files)
- [Equality deletes](https://iceberg.apache.org/spec/#equality-delete-files) (supported from version 25.8+)
- [Deletion vectors](https://iceberg.apache.org/spec/#deletion-vectors) stored in Puffin files (Iceberg v3, read-only)

The following deletion method is **not supported**:
- [Deletion vectors](https://iceberg.apache.org/spec/#deletion-vectors) (introduced in v3)
The following limitations apply to deletion vectors:

- Only `deletion-vector-v1` Puffin blobs are supported
- Data files must be in Parquet format
- Column-scoped deletion vectors (`fields` in the Puffin blob) are not supported
- Writing deletion vectors is not supported

Parsed deletion vectors can be cached in memory when `use_puffin_files_cache` is enabled and the puffin file has a non-empty `etag`. Empty deletion vectors are cached as well, so repeated reads do not re-fetch the puffin file. The cache can be cleared with `SYSTEM DROP PUFFIN_FILES_CACHE`.

### Basic usage {#basic-usage}
```sql
Expand Down
2 changes: 2 additions & 0 deletions docs/en/interfaces/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ The supported formats are:
| [AvroConfluent](./formats/Avro/AvroConfluent.md) |||
| [Parquet](./formats/Parquet/Parquet.md) |||
| [ParquetMetadata](./formats/Parquet/ParquetMetadata.md) |||
| [Puffin](./formats/Puffin/Puffin.md) |||
| [PuffinMetadata](./formats/Puffin/PuffinMetadata.md) |||
| [Arrow](./formats/Arrow/Arrow.md) |||
| [ArrowStream](./formats/Arrow/ArrowStream.md) |||
| [ORC](./formats/ORC.md) |||
Expand Down
50 changes: 50 additions & 0 deletions docs/en/interfaces/formats/Puffin/Puffin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add mandatory sidebar fields to the new Puffin docs

Both this new page and docs/en/interfaces/formats/Puffin/PuffinMetadata.md omit the mandatory sidebar_label and sidebar_position frontmatter fields, leaving the new documentation pages noncompliant with the repository's required metadata. Add both fields to each page's frontmatter.

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

description: 'Documentation for the Puffin format'
input_format: true
output_format: false
keywords: ['Puffin']
sidebar_label: 'Puffin'
sidebar_position: 1
slug: /interfaces/formats/Puffin
title: 'Puffin'
doc_type: 'reference'
---

## Description {#description}

Input format for reading [Apache Iceberg Puffin](https://iceberg.apache.org/puffin-spec/) files.

The format exposes deleted row positions from `deletion-vector-v1` blobs. Other blob types (for example `apache-datasketches-theta-v1`) are skipped.
If a puffin file contains multiple `deletion-vector-v1` blobs, the format outputs one row per such blob.

Fixed output columns:
- `referenced_data_file` (`String`) - location of the data file the deletion vector applies to (`referenced-data-file` blob property)
- `deleted_rows` (`Array(UInt64)`) - 64-bit row positions deleted according to the deletion vector roaring bitmap

Deletion vectors whose declared `cardinality` exceeds an absolute materialization ceiling are rejected when `deleted_rows` is requested. Footer `deletion-vector-v1` properties (including that `cardinality` parses as an unsigned integer) are always validated. Selecting only `referenced_data_file` skips on-disk payload I/O and therefore also skips envelope, CRC, roaring deserialize, and the materialization ceiling — intentionally, so a path-only projection does not read up to the blob-size cap.

On-disk `deletion-vector-v1` blob length is bounded by an absolute ceiling (aligned with Iceberg's 2 GiB content-size check). When `deleted_rows` is requested, the reader peeks the envelope header (combined length and magic) before allocating the full payload; CRC is verified after the bounded read.

LZ4-compressed and uncompressed puffin footers are supported. Footer payload size (and declared LZ4 content size) is bounded by a compression ratio where applicable and an absolute ceiling; oversized footers are rejected before allocation.

Only a subset of output columns can be requested. A user-provided structure with unexpected column names or types is rejected when the format is created.

## Example usage {#example-usage}

Read deleted row positions with the referenced data file:

```sql
SELECT referenced_data_file, deleted_rows
FROM file(deletes.puffin, Puffin);
```

Expand deleted positions into individual rows:

```sql
SELECT referenced_data_file, row_number
FROM file(deletes.puffin, Puffin)
ARRAY JOIN deleted_rows AS row_number
ORDER BY referenced_data_file, row_number;
```

Use `PuffinMetadata` to inspect footer blob descriptors before reading deletion vectors.
42 changes: 42 additions & 0 deletions docs/en/interfaces/formats/Puffin/PuffinMetadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
description: 'Documentation for the PuffinMetadata format'
input_format: true
output_format: false
keywords: ['PuffinMetadata']
sidebar_label: 'PuffinMetadata'
sidebar_position: 2
slug: /interfaces/formats/PuffinMetadata
title: 'PuffinMetadata'
doc_type: 'reference'
---

## Description {#description}

Special input format for reading [Apache Iceberg Puffin](https://iceberg.apache.org/puffin-spec/) file footer metadata.
It outputs one row per blob entry from the footer `BlobMetadata` list.

Fixed output columns:
- `blob_type` (`String`) - blob type, for example `deletion-vector-v1`
- `snapshot_id` (`Int64`) - snapshot id of the blob
- `sequence_number` (`Int64`) - sequence number of the blob
- `fields` (`Array(Int32)`) - list of field ids the blob applies to
- `offset` (`Int64`) - offset of the blob payload in the file
- `length` (`Int64`) - length of the blob payload in bytes
- `compression_codec` (`String`) - compression codec of the blob payload, if present
- `properties` (`Map(String, String)`) - blob-specific properties

Optional top-level `FileMetadata.properties` in the footer (for example `created-by`) are type-checked when present but are not returned as columns. If the key is present it must be a JSON object with string values (null is rejected).

LZ4-compressed and uncompressed puffin footers are supported. Footer payload size (and declared LZ4 content size) is bounded by a compression ratio where applicable and an absolute ceiling; oversized footers are rejected before allocation.

## Example usage {#example-usage}

Inspect footer blobs:

```sql
SELECT blob_type, snapshot_id, sequence_number, offset, length, compression_codec,
mapKeys(properties), mapValues(properties)
FROM file(deletes.puffin, PuffinMetadata);
```

Pair with the `Puffin` format to read `deletion-vector-v1` blob payloads.
4 changes: 4 additions & 0 deletions docs/en/sql-reference/statements/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Clears the per-URL Confluent Schema Registry caches used by the `AvroConfluent`

Clears the parquet metadata cache.

## SYSTEM DROP PUFFIN_FILES_CACHE {#drop-puffin-files-cache}

Clears the Puffin files cache used for parsed Iceberg puffin file content such as deletion vectors.

## SYSTEM CLEAR|DROP TEXT INDEX CACHES {#drop-text-index-caches}

Clears the text index's header, dictionary and postings caches.
Expand Down
17 changes: 13 additions & 4 deletions docs/en/sql-reference/table-functions/iceberg.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,20 @@ ClickHouse supports time travel for Iceberg tables, allowing you to query histor

## Processing of tables with deleted rows {#deleted-rows}

Currently, only Iceberg tables with [position deletes](https://iceberg.apache.org/spec/#position-delete-files) are supported.
ClickHouse supports reading Iceberg tables that use the following deletion methods:

The following deletion methods are **not supported**:
- [Equality deletes](https://iceberg.apache.org/spec/#equality-delete-files)
- [Deletion vectors](https://iceberg.apache.org/spec/#deletion-vectors) (introduced in v3)
- [Position deletes](https://iceberg.apache.org/spec/#position-delete-files)
- [Equality deletes](https://iceberg.apache.org/spec/#equality-delete-files) (supported from version 25.8+)
- [Deletion vectors](https://iceberg.apache.org/spec/#deletion-vectors) stored in Puffin files (Iceberg v3, read-only)

The following limitations apply to deletion vectors:

- Only `deletion-vector-v1` Puffin blobs are supported
- Data files must be in Parquet format
- Column-scoped deletion vectors (`fields` in the Puffin blob) are not supported
- Writing deletion vectors is not supported

Parsed deletion vectors can be cached in memory when `use_puffin_files_cache` is enabled and the puffin file has a non-empty `etag`. Empty deletion vectors are cached as well, so repeated reads do not re-fetch the puffin file. The cache can be cleared with `SYSTEM DROP PUFFIN_FILES_CACHE`.

### Basic usage {#basic-usage}

Expand Down
15 changes: 15 additions & 0 deletions programs/local/LocalServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ namespace ServerSetting
extern const ServerSettingsUInt64 parquet_metadata_cache_size;
extern const ServerSettingsUInt64 parquet_metadata_cache_max_entries;
extern const ServerSettingsDouble parquet_metadata_cache_size_ratio;
extern const ServerSettingsString puffin_files_cache_policy;
extern const ServerSettingsUInt64 puffin_files_cache_size;
extern const ServerSettingsUInt64 puffin_files_cache_max_entries;
extern const ServerSettingsDouble puffin_files_cache_size_ratio;
extern const ServerSettingsUInt64 max_active_parts_loading_thread_pool_size;
extern const ServerSettingsUInt64 max_io_thread_pool_free_size;
extern const ServerSettingsUInt64 max_io_thread_pool_size;
Expand Down Expand Up @@ -1540,6 +1544,17 @@ void LocalServer::processConfig()
global_context->setParquetMetadataCache(parquet_metadata_cache_policy, parquet_metadata_cache_size, parquet_metadata_cache_max_entries, parquet_metadata_cache_size_ratio);
#endif

String puffin_files_cache_policy = server_settings[ServerSetting::puffin_files_cache_policy];
size_t puffin_files_cache_size = server_settings[ServerSetting::puffin_files_cache_size];
size_t puffin_files_cache_max_entries = server_settings[ServerSetting::puffin_files_cache_max_entries];
double puffin_files_cache_size_ratio = server_settings[ServerSetting::puffin_files_cache_size_ratio];
if (puffin_files_cache_size > max_cache_size)
{
puffin_files_cache_size = max_cache_size;
LOG_INFO(log, "Lowered Puffin files cache size to {} because the system has limited RAM", formatReadableSizeWithBinarySuffix(puffin_files_cache_size));
}
global_context->setPuffinFilesCache(puffin_files_cache_policy, puffin_files_cache_size, puffin_files_cache_max_entries, puffin_files_cache_size_ratio);

Names allowed_disks_table_engines;
splitInto<','>(allowed_disks_table_engines, server_settings[ServerSetting::allowed_disks_for_table_engines].value);
global_context->setAllowedDisksForTableEngines(std::unordered_set<String>(allowed_disks_table_engines.begin(), allowed_disks_table_engines.end()));
Expand Down
15 changes: 15 additions & 0 deletions programs/server/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ namespace ServerSetting
extern const ServerSettingsUInt64 parquet_metadata_cache_size;
extern const ServerSettingsUInt64 parquet_metadata_cache_max_entries;
extern const ServerSettingsDouble parquet_metadata_cache_size_ratio;
extern const ServerSettingsString puffin_files_cache_policy;
extern const ServerSettingsUInt64 puffin_files_cache_size;
extern const ServerSettingsUInt64 puffin_files_cache_max_entries;
extern const ServerSettingsDouble puffin_files_cache_size_ratio;
extern const ServerSettingsUInt64 io_thread_pool_queue_size;
extern const ServerSettingsBool jemalloc_enable_global_profiler;
extern const ServerSettingsBool jemalloc_collect_global_profile_samples_in_trace_log;
Expand Down Expand Up @@ -2276,6 +2280,16 @@ try
}
global_context->setParquetMetadataCache(parquet_metadata_cache_policy, parquet_metadata_cache_size, parquet_metadata_cache_max_entries, parquet_metadata_cache_size_ratio);
#endif
String puffin_files_cache_policy = server_settings[ServerSetting::puffin_files_cache_policy];
size_t puffin_files_cache_size = server_settings[ServerSetting::puffin_files_cache_size];
size_t puffin_files_cache_max_entries = server_settings[ServerSetting::puffin_files_cache_max_entries];
double puffin_files_cache_size_ratio = server_settings[ServerSetting::puffin_files_cache_size_ratio];
if (puffin_files_cache_size > max_cache_size)
{
puffin_files_cache_size = max_cache_size;
LOG_INFO(log, "Lowered Puffin files cache size to {} because the system has limited RAM", formatReadableSizeWithBinarySuffix(puffin_files_cache_size));
}
global_context->setPuffinFilesCache(puffin_files_cache_policy, puffin_files_cache_size, puffin_files_cache_max_entries, puffin_files_cache_size_ratio);

Names allowed_disks_table_engines;
splitInto<','>(allowed_disks_table_engines, server_settings[ServerSetting::allowed_disks_for_table_engines].value);
Expand Down Expand Up @@ -2705,6 +2719,7 @@ try
#if USE_PARQUET
global_context->updateParquetMetadataCacheConfiguration(config(), max_cache_size_in_bytes);
#endif
global_context->updatePuffinFilesCacheConfiguration(config(), max_cache_size_in_bytes);
}

#if USE_SSL
Expand Down
1 change: 1 addition & 0 deletions src/Access/Common/AccessType.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ enum class AccessType : uint8_t
M(SYSTEM_DROP_ICEBERG_METADATA_CACHE, "SYSTEM CLEAR ICEBERG_METADATA_CACHE, SYSTEM DROP ICEBERG_METADATA_CACHE", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_DROP_AVRO_SCHEMA_CACHE, "SYSTEM CLEAR AVRO SCHEMA CACHE, SYSTEM DROP AVRO SCHEMA CACHE, DROP AVRO SCHEMA CACHE", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_DROP_PARQUET_METADATA_CACHE, "SYSTEM DROP PARQUET_METADATA_CACHE", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_DROP_PUFFIN_FILES_CACHE, "SYSTEM DROP PUFFIN_FILES_CACHE", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_PREWARM_PRIMARY_INDEX_CACHE, "SYSTEM PREWARM PRIMARY INDEX, PREWARM PRIMARY INDEX CACHE, PREWARM PRIMARY INDEX", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_DROP_PRIMARY_INDEX_CACHE, "SYSTEM CLEAR PRIMARY INDEX CACHE, SYSTEM DROP PRIMARY INDEX, DROP PRIMARY INDEX CACHE, DROP PRIMARY INDEX", GLOBAL, SYSTEM_DROP_CACHE) \
M(SYSTEM_DROP_UNCOMPRESSED_CACHE, "SYSTEM CLEAR UNCOMPRESSED CACHE, SYSTEM DROP UNCOMPRESSED, DROP UNCOMPRESSED CACHE, DROP UNCOMPRESSED", GLOBAL, SYSTEM_DROP_CACHE) \
Expand Down
7 changes: 7 additions & 0 deletions src/AggregateFunctions/AggregateFunctionGroupBitmapData.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ class RoaringBitmapWithSmallSet : private boost::noncopyable
return roaring_bitmap->cardinality();
}

UInt64 getAllocatedBytes() const
{
if (isSmall())
return sizeof(small);
return roaring_bitmap->getSizeInBytes();
}

void merge(const RoaringBitmapWithSmallSet & r1)
{
if (r1.isLarge())
Expand Down
1 change: 1 addition & 0 deletions src/Client/BuzzHouse/Generator/SessionSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,7 @@ static std::unordered_map<String, CHSetting> serverSettings2 = {
{"use_page_cache_for_local_disks", trueOrFalseSetting},
{"use_page_cache_for_object_storage", trueOrFalseSetting},
{"use_parquet_metadata_cache", trueOrFalseSetting},
{"use_puffin_files_cache", trueOrFalseSetting},
{"use_query_cache", trueOrFalseSetting},
{"use_roaring_bitmap_iceberg_positional_deletes", trueOrFalseSetting},
{"use_skip_indexes_if_final_exact_mode", CHSetting(trueOrFalse, {"0", "1"}, true)},
Expand Down
2 changes: 2 additions & 0 deletions src/Common/CurrentMetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@
M(IcebergMetadataFilesCacheFiles, "Number of cached files in the Iceberg metadata cache") \
M(ParquetMetadataCacheBytes, "Size of the Parquet metadata cache in bytes") \
M(ParquetMetadataCacheFiles, "Number of cached files in the Parquet metadata cache") \
M(PuffinFilesCacheBytes, "Size of the Puffin files cache in bytes") \
M(PuffinFilesCacheFiles, "Number of cached entries in the Puffin files cache") \
M(AvroSchemaCacheBytes, "Size of the Avro schema cache in bytes") \
M(AvroSchemaCacheCells, "Number of cached Avro schemas, including both registered and fetched schemas.") \
M(AvroSchemaRegistryCacheBytes, "Size of the Avro schema registry cache in bytes") \
Expand Down
5 changes: 5 additions & 0 deletions src/Common/ProfileEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
M(IcebergMetadataReturnedObjectInfos, "Total number of returned object infos from iceberg iterator.", ValueType::Number) \
M(IcebergMinMaxNonPrunedDeleteFiles, "Total number of accepted data files-position delete file pairs by minmax analysis from pairs suitable by partitioning and sequence number.", ValueType::Number) \
M(IcebergMinMaxPrunedDeleteFiles, "Total number of accepted data files-position delete file pairs by minmax analysis from pairs suitable by partitioning and sequence number.", ValueType::Number) \
M(PuffinFilesRead, "Number of Puffin files read (footer or deletion vector blob).", ValueType::Number) \
M(PuffinFileReadMicroseconds, "Total time spent reading Puffin files.", ValueType::Microseconds) \
M(PuffinFilesCacheHits, "Number of times parsed Puffin file content has been found in the cache.", ValueType::Number) \
M(PuffinFilesCacheMisses, "Number of times parsed Puffin file content has not been found in the cache and had to be read from disk.", ValueType::Number) \
M(PuffinFilesCacheWeightLost, "Approximate number of bytes evicted from the Puffin files cache.", ValueType::Number) \
M(VectorSimilarityIndexCacheHits, "Number of times an index granule has been found in the vector index cache.", ValueType::Number) \
M(VectorSimilarityIndexCacheMisses, "Number of times an index granule has not been found in the vector index cache and had to be read from disk.", ValueType::Number) \
M(VectorSimilarityIndexCacheWeightLost, "Approximate number of bytes evicted from the vector index cache.", ValueType::Number) \
Expand Down
4 changes: 4 additions & 0 deletions src/Core/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ static constexpr auto DEFAULT_PARQUET_METADATA_CACHE_POLICY = "SLRU";
static constexpr auto DEFAULT_PARQUET_METADATA_CACHE_MAX_SIZE = 512_MiB;
static constexpr auto DEFAULT_PARQUET_METADATA_CACHE_SIZE_RATIO = 0.5;
static constexpr auto DEFAULT_PARQUET_METADATA_CACHE_MAX_ENTRIES = 5000;
static constexpr auto DEFAULT_PUFFIN_FILES_CACHE_POLICY = "SLRU";
static constexpr auto DEFAULT_PUFFIN_FILES_CACHE_MAX_SIZE = 512_MiB;
static constexpr auto DEFAULT_PUFFIN_FILES_CACHE_SIZE_RATIO = 0.5;
static constexpr auto DEFAULT_PUFFIN_FILES_CACHE_MAX_ENTRIES = 5000;
static constexpr auto DEFAULT_QUERY_CONDITION_CACHE_POLICY = "SLRU";
static constexpr auto DEFAULT_QUERY_CONDITION_CACHE_MAX_SIZE = 100_MiB;
static constexpr auto DEFAULT_QUERY_CONDITION_CACHE_SIZE_RATIO = 0.5l;
Expand Down
Loading
Loading