> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8a08bda2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Proporciona una interfaz de tipo tabla de solo lectura para tablas Apache Iceberg en Amazon S3, Azure, HDFS o almacenadas localmente.

# iceberg

Proporciona una interfaz de tipo tabla de solo lectura para tablas Apache [Iceberg](https://iceberg.apache.org/) en Amazon S3, Azure, HDFS o almacenadas localmente.

<div id="syntax">
  ## Sintaxis
</div>

```sql theme={null}
icebergS3(url [, NOSIGN | access_key_id, secret_access_key, [session_token]] [,format] [,compression_method] [,extra_credentials])
icebergS3(named_collection[, option=value [,..]])

icebergAzure(connection_string|storage_account_url, container_name, blobpath, [,account_name], [,account_key] [,format] [,compression_method])
icebergAzure(named_collection[, option=value [,..]])

icebergHDFS(path_to_table, [,format] [,compression_method])
icebergHDFS(named_collection[, option=value [,..]])

icebergLocal(path_to_table, [,format] [,compression_method])
icebergLocal(named_collection[, option=value [,..]])
```

<div id="arguments">
  ## Argumentos
</div>

La descripción de los argumentos coincide con la de los argumentos de las funciones de tabla `s3`, `azureBlobStorage`, `HDFS` y `file`, respectivamente.
`format` se refiere al formato de los archivos de datos de la tabla Iceberg.

Para `icebergS3`, se puede usar un parámetro opcional `extra_credentials` para pasar un `role_arn` para acceso basado en roles en ClickHouse Cloud. Consulte [Secure S3](/es/products/cloud/guides/data-sources/accessing-s3-data-securely) para ver los pasos de configuración.

<div id="returned-value">
  ### Valor devuelto
</div>

Una tabla con la estructura especificada para leer datos de la tabla Iceberg especificada.

<div id="example">
  ### Ejemplo
</div>

```sql theme={null}
SELECT * FROM icebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
```

<Warning>
  Actualmente, ClickHouse permite leer las versiones v1 y v2 del formato Iceberg mediante las funciones de tabla `icebergS3`, `icebergAzure`, `icebergHDFS` y `icebergLocal`, y los motores de tabla `IcebergS3`, `icebergAzure`, `IcebergHDFS` e `IcebergLocal`.
</Warning>

<div id="defining-a-named-collection">
  ## Definir una colección con nombre
</div>

Aquí tienes un ejemplo de cómo configurar una colección con nombre para almacenar la URL y las credenciales:

```xml theme={null}
<clickhouse>
    <named_collections>
        <iceberg_conf>
            <url>http://test.s3.amazonaws.com/clickhouse-bucket/</url>
            <access_key_id>test<access_key_id>
            <secret_access_key>test</secret_access_key>
            <format>auto</format>
            <structure>auto</structure>
        </iceberg_conf>
    </named_collections>
</clickhouse>
```

```sql theme={null}
SELECT * FROM icebergS3(iceberg_conf, filename = 'test_table')
DESCRIBE icebergS3(iceberg_conf, filename = 'test_table')
```

<div id="iceberg-writes-catalogs">
  ## Uso de un catálogo de datos
</div>

Las tablas Iceberg también se pueden usar con varios catálogos de datos, como [REST Catalog](https://iceberg.apache.org/rest-catalog-spec/), [AWS Glue Data Catalog](https://docs.aws.amazon.com/prescriptive-guidance/latest/serverless-etl-aws-glue/aws-glue-data-catalog.html) y [Unity Catalog](https://www.unitycatalog.io/).

<Warning>
  Al usar un catálogo, la mayoría de los usuarios querrán usar el motor de base de datos `DataLakeCatalog`, que conecta ClickHouse con su catálogo para descubrir sus tablas. Puede usar este motor de base de datos en lugar de crear manualmente tablas individuales con el motor de tabla `IcebergS3`.
</Warning>

Para usarlos, cree una tabla con el motor `IcebergS3` y proporcione la configuración necesaria.

Por ejemplo, al usar REST Catalog con almacenamiento MinIO:

```sql theme={null}
CREATE TABLE `database_name.table_name`
ENGINE = IcebergS3(
  'http://minio:9000/warehouse-rest/table_name/',
  'minio_access_key',
  'minio_secret_key'
)
```

O bien, con AWS Glue Data Catalog y S3:

```sql theme={null}
CREATE TABLE `my_database.my_table`  
ENGINE = IcebergS3(
  's3://my-data-bucket/warehouse/my_database/my_table/',
  'aws_access_key',
  'aws_secret_key'
)
```

<div id="schema-evolution">
  ## Evolución del esquema
</div>

Por el momento, con la ayuda de CH, puedes leer tablas Iceberg cuyo esquema ha ido cambiando con el tiempo. Actualmente admitimos la lectura de tablas en las que se han añadido o eliminado columnas y se ha modificado su orden. También puedes cambiar una columna en la que se requiere un valor por otra en la que se permite NULL. Además, admitimos la conversión de tipos permitida para tipos simples, concretamente:  

* int -> long
* float -> double
* decimal(P, S) -> decimal(P', S) donde P' > P.

Actualmente, no es posible modificar estructuras anidadas ni los tipos de los elementos dentro de arrays y maps.

<div id="partition-pruning">
  ## Poda de particiones
</div>

ClickHouse admite la poda de particiones en las consultas SELECT de tablas Iceberg, lo que ayuda a optimizar el rendimiento al omitir archivos de datos irrelevantes. Para habilitar la poda de particiones, establece `use_iceberg_partition_pruning = 1`. Para obtener más información sobre la poda de particiones de Iceberg, consulta [https://iceberg.apache.org/spec/#partitioning](https://iceberg.apache.org/spec/#partitioning)

<div id="time-travel">
  ## Viaje en el tiempo
</div>

ClickHouse admite el viaje en el tiempo en las tablas Iceberg, lo que permite consultar datos históricos con una marca de tiempo o un ID de instantánea específicos.

<div id="deleted-rows">
  ## Procesamiento de tablas con filas eliminadas
</div>

Actualmente, solo se admiten las tablas Iceberg con [eliminaciones por posición](https://iceberg.apache.org/spec/#position-delete-files).

No se admiten los siguientes métodos de eliminación:

* [Eliminaciones por igualdad](https://iceberg.apache.org/spec/#equality-delete-files)
* [Vectores de eliminación](https://iceberg.apache.org/spec/#deletion-vectors) (introducidos en v3)

<div id="basic-usage">
  ### Uso básico
</div>

```sql theme={null}
 SELECT * FROM example_table ORDER BY 1 
 SETTINGS iceberg_timestamp_ms = 1714636800000
```

```sql theme={null}
 SELECT * FROM example_table ORDER BY 1 
 SETTINGS iceberg_snapshot_id = 3547395809148285433
```

Nota: No puede especificar a la vez los parámetros `iceberg_timestamp_ms` e `iceberg_snapshot_id` en la misma consulta.

<div id="important-considerations">
  ### Consideraciones importantes
</div>

* **Las instantáneas** suelen crearse cuando:

* Se escriben datos nuevos en la tabla

* Se realiza algún tipo de compactación de datos

* **Los cambios en el esquema normalmente no crean instantáneas** - Esto da lugar a comportamientos importantes al usar viaje en el tiempo con tablas que han pasado por una evolución del esquema.

<div id="example-scenarios">
  ### Escenarios de ejemplo
</div>

Todos los escenarios están escritos en Spark porque CH todavía no admite la escritura en tablas de Iceberg.

<div id="scenario-1">
  #### Escenario 1: Cambios de esquema sin nuevas instantáneas
</div>

Considere esta secuencia de operaciones:

```sql theme={null}
 -- Crear una tabla con dos columnas
  CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
  order_number bigint, 
  product_code string
  ) 
  USING iceberg 
  OPTIONS ('format-version'='2')

- - Insertar datos en la tabla
  INSERT INTO spark_catalog.db.time_travel_example VALUES 
    (1, 'Mars')

  ts1 = now() // Un fragmento de pseudocódigo

- - Modificar la tabla para agregar una nueva columna
  ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)
 
  ts2 = now()

- - Insertar datos en la tabla
  INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)

   ts3 = now()

- - Consultar la tabla en cada marca de tiempo
  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts1;

+------------+------------+
|order_number|product_code|
+------------+------------+
|           1|        Mars|
+------------+------------+
  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts2;

+------------+------------+
|order_number|product_code|
+------------+------------+
|           1|        Mars|
+------------+------------+

  SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts3;

+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
|           1|        Mars| NULL|
|           2|       Venus|100.0|
+------------+------------+-----+
```

Resultados de la consulta en diferentes marcas de tiempo:

* En ts1 & ts2: Solo se muestran las dos columnas originales
* En ts3: Se muestran las tres columnas, con NULL en el precio de la primera fila

<div id="scenario-2">
  #### Escenario 2: Diferencias entre el esquema histórico y el actual
</div>

Una consulta de viaje en el tiempo realizada en el momento actual podría mostrar un esquema distinto del de la tabla actual:

```sql theme={null}
-- Crear una tabla
  CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_2 (
  order_number bigint, 
  product_code string
  ) 
  USING iceberg 
  OPTIONS ('format-version'='2')

-- Insertar datos iniciales en la tabla
  INSERT INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');

-- Modificar la tabla para añadir una nueva columna
  ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);

  ts = now();

-- Consultar la tabla en el momento actual usando sintaxis de marca de tiempo

  SELECT * FROM spark_catalog.db.time_travel_example_2 TIMESTAMP AS OF ts;

    +------------+------------+
    |order_number|product_code|
    +------------+------------+
    |           2|       Venus|
    +------------+------------+

-- Consultar la tabla en el momento actual
  SELECT * FROM spark_catalog.db.time_travel_example_2;
    +------------+------------+-----+
    |order_number|product_code|price|
    +------------+------------+-----+
    |           2|       Venus| NULL|
    +------------+------------+-----+
```

Esto ocurre porque `ALTER TABLE` no crea una nueva instantánea, sino que, para la tabla actual, Spark toma el valor de `schema_id` del archivo de metadatos más reciente, no de una instantánea.

<div id="scenario-3">
  #### Escenario 3:  Diferencias entre el esquema histórico y el actual
</div>

La segunda es que, al usar viaje en el tiempo, no puedes obtener el estado de la tabla antes de que se escribiera ningún dato en ella:

```sql theme={null}
-- Crear una tabla
  CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_3 (
  order_number bigint, 
  product_code string
  ) 
  USING iceberg 
  OPTIONS ('format-version'='2');

  ts = now();

-- Consultar la tabla en una marca de tiempo específica
  SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- Finaliza con error: Cannot find a snapshot older than ts.
```

En ClickHouse, el comportamiento es consistente con el de Spark. Puedes sustituir mentalmente las consultas Select de Spark por las de ClickHouse y funcionará igual.

<div id="metadata-file-resolution">
  ## Resolución del archivo de metadatos
</div>

Al usar la `función de tabla` `iceberg` en ClickHouse, el sistema necesita localizar el archivo metadata.json correcto que describe la estructura de la tabla Iceberg. A continuación, se explica cómo funciona este proceso de resolución:

<div id="candidate-search">
  ### Búsqueda de candidatos (por orden de prioridad)
</div>

1. **Especificación directa de la ruta**:
   \*Si establece `iceberg_metadata_file_path`, el sistema usará esta ruta exacta combinándola con la ruta del directorio de la tabla Iceberg.

* Cuando se proporciona este ajuste, se ignoran todos los demás ajustes de resolución.

2. **Coincidencia del UUID de la tabla**:
   \*Si se especifica `iceberg_metadata_table_uuid`, el sistema:
   \*Buscará solo archivos `.metadata.json` en el directorio `metadata`
   \*Filtrará los archivos que contengan un campo `table-uuid` que coincida con el UUID especificado (sin distinguir entre mayúsculas y minúsculas)

3. **Búsqueda predeterminada**:
   \*Si no se proporciona ninguno de los ajustes anteriores, todos los archivos `.metadata.json` del directorio `metadata` pasan a ser candidatos

<div id="most-recent-file">
  ### Selección del archivo más reciente
</div>

Después de identificar los archivos candidatos mediante las reglas anteriores, el sistema determina cuál es el más reciente:

* Si `iceberg_recent_metadata_file_by_last_updated_ms_field` está habilitado:

* Se selecciona el archivo con el valor `last-updated-ms` más alto

* En caso contrario:

* Se selecciona el archivo con el número de versión más alto

* (La versión aparece como `V` en nombres de archivo con formato `V.metadata.json` o `V-uuid.metadata.json`)

**Nota**: Todas las configuraciones mencionadas son configuraciones de la función de tabla (no configuraciones globales ni a nivel de consulta) y deben especificarse como se muestra a continuación:

```sql theme={null}
SELECT * FROM iceberg('s3://bucket/path/to/iceberg_table', 
    SETTINGS iceberg_metadata_table_uuid = 'a90eed4c-f74b-4e5b-b630-096fb9d09021');
```

**Nota**: Aunque los catálogos de Iceberg suelen encargarse de la resolución de metadatos, la función de tabla `iceberg` de ClickHouse interpreta directamente los archivos almacenados en S3 como tablas de Iceberg, por lo que es importante entender estas reglas de resolución.

<div id="metadata-cache">
  ## Caché de metadatos
</div>

El motor de tabla y la función de tabla `Iceberg` admiten una caché de metadatos para almacenar la información de los archivos de manifiesto, la lista de manifiestos y el JSON de metadatos. La caché se guarda en memoria. Esta función está controlada por la configuración `use_iceberg_metadata_files_cache`, que está habilitada de forma predeterminada.

<div id="aliases">
  ## Alias
</div>

La función de tabla `iceberg` ahora es un alias de `icebergS3`.

<div id="virtual-columns">
  ## Columnas virtuales
</div>

* `_path` — Ruta del archivo. Tipo: `LowCardinality(String)`.
* `_file` — Nombre del archivo. Tipo: `LowCardinality(String)`.
* `_size` — Tamaño del archivo en bytes. Tipo: `Nullable(UInt64)`. Si se desconoce el tamaño del archivo, el valor es `NULL`.
* `_time` — Hora de la última modificación del archivo. Tipo: `Nullable(DateTime)`. Si se desconoce la hora, el valor es `NULL`.
* `_etag` — El etag del archivo. Tipo: `LowCardinality(String)`. Si se desconoce el etag, el valor es `NULL`.

<div id="writes-into-iceberg-table">
  ## Escrituras en tablas Iceberg
</div>

A partir de la versión 25.7, ClickHouse admite modificaciones en las tablas Iceberg del usuario.

Actualmente, esta es una función experimental, por lo que primero debe habilitarla:

```sql theme={null}
SET allow_insert_into_iceberg = 1;
```

<div id="create-iceberg-table">
  ### Crear una tabla
</div>

Para crear tu propia tabla Iceberg vacía, usa los mismos comandos que para la lectura, pero especifica el esquema de forma explícita.
Las operaciones de escritura admiten todos los formatos de datos de la especificación de Iceberg, como Parquet, Avro y ORC.

<div id="example">
  ### Ejemplo
</div>

```sql theme={null}
CREATE TABLE iceberg_writes_example
(
    x Nullable(String),
    y Nullable(Int32)
)
ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/')
```

Nota: Para crear un archivo de sugerencia de versión, habilite la configuración `iceberg_use_version_hint`.
Si desea comprimir el archivo metadata.json, especifique el nombre del códec en la configuración `iceberg_metadata_compression_method`.

<div id="writes-inserts">
  ### INSERT
</div>

Después de crear una tabla nueva, puede insertar datos con la sintaxis habitual de ClickHouse.

<div id="example">
  ### Ejemplo
</div>

```sql theme={null}
INSERT INTO iceberg_writes_example VALUES ('Pavel', 777), ('Ivanov', 993);

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Pavel
y: 777

Row 2:
──────
x: Ivanov
y: 993
```

<div id="iceberg-writes-delete">
  ### DELETE
</div>

ClickHouse también admite eliminar filas adicionales en el formato merge-on-read.
Esta consulta creará un nuevo snapshot con archivos de eliminación por posición.

<div id="example">
  ### Ejemplo
</div>

```sql theme={null}
ALTER TABLE iceberg_writes_example DELETE WHERE x != 'Ivanov';

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Ivanov
y: 993
```

<div id="iceberg-writes-schema-evolution">
  ### Evolución del esquema
</div>

ClickHouse le permite agregar, eliminar, modificar o renombrar columnas con tipos simples (que no sean Tuple, Array ni Map).

<div id="example">
  ### Ejemplo
</div>

```sql theme={null}
ALTER TABLE iceberg_writes_example MODIFY COLUMN y Nullable(Int64);
SHOW CREATE TABLE iceberg_writes_example;

   ┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example              ↴│
   │↳(                                                        ↴│
   │↳    `x` Nullable(String),                                ↴│
   │↳    `y` Nullable(Int64)                                  ↴│
   │↳)                                                        ↴│
   │↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
   └───────────────────────────────────────────────────────────┘

ALTER TABLE iceberg_writes_example ADD COLUMN z Nullable(Int32);
SHOW CREATE TABLE iceberg_writes_example;

   ┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example              ↴│
   │↳(                                                        ↴│
   │↳    `x` Nullable(String),                                ↴│
   │↳    `y` Nullable(Int64),                                 ↴│
   │↳    `z` Nullable(Int32)                                  ↴│
   │↳)                                                        ↴│
   │↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
   └───────────────────────────────────────────────────────────┘

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Ivanov
y: 993
z: ᴺᵁᴸᴸ

ALTER TABLE iceberg_writes_example DROP COLUMN z;
SHOW CREATE TABLE iceberg_writes_example;
   ┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example              ↴│
   │↳(                                                        ↴│
   │↳    `x` Nullable(String),                                ↴│
   │↳    `y` Nullable(Int64)                                  ↴│
   │↳)                                                        ↴│
   │↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
   └───────────────────────────────────────────────────────────┘

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Ivanov
y: 993

ALTER TABLE iceberg_writes_example RENAME COLUMN y TO value;
SHOW CREATE TABLE iceberg_writes_example;

   ┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example              ↴│
   │↳(                                                        ↴│
   │↳    `x` Nullable(String),                                ↴│
   │↳    `value` Nullable(Int64)                              ↴│
   │↳)                                                        ↴│
   │↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
   └───────────────────────────────────────────────────────────┘

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Ivanov
value: 993
```

<div id="iceberg-writes-compaction">
  ### Compactación
</div>

ClickHouse admite la compactación de tablas Iceberg. Actualmente, puede fusionar archivos de eliminación por posición en archivos de datos mientras actualiza los metadatos. Los ID y las marcas de tiempo de instantáneas anteriores no cambian, por lo que la función de viaje en el tiempo puede seguir usándose con los mismos valores.

Cómo usarlo:

```sql theme={null}
SET allow_experimental_iceberg_compaction = 1

OPTIMIZE TABLE iceberg_writes_example;

SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;

Row 1:
──────
x: Ivanov
y: 993
```

<div id="iceberg-expire-snapshots">
  ### Expiración de instantáneas
</div>

Las tablas Iceberg acumulan instantáneas con cada operación `INSERT`, `DELETE` o `UPDATE`. Con el tiempo, esto puede dar lugar a una gran cantidad de instantáneas y archivos de datos asociados. El comando `expire_snapshots` elimina las instantáneas antiguas y limpia los archivos de datos que ya no están referenciados por ninguna instantánea conservada.

**Sintaxis:**

```sql theme={null}
ALTER TABLE iceberg_table EXECUTE expire_snapshots(
    ['timestamp']
    [, expire_before = 'timestamp']
    [, retention_period = '3d']
    [, retain_last = 100]
    [, snapshot_ids = [1, 2, 3, 4]]
    [, dry_run = 1]
);
```

De forma predeterminada, qué instantáneas se conservan lo determina la [política de retención](#iceberg-snapshot-retention-policy) (las propiedades de la tabla `min-snapshots-to-keep`, `max-snapshot-age-ms` y las anulaciones por referencia). Cuando se especifica `snapshot_ids`, se omite la política de retención y solo las instantáneas indicadas se consideran para su expiración.

**Argumentos:**

* `'timestamp'` (posicional) o `expire_before = 'timestamp'` — una cadena de fecha y hora (p. ej., `'2024-06-01 00:00:00'`) interpretada en la **zona horaria del servidor**. Actúa como mecanismo de seguridad: las instantáneas cuyo `timestamp-ms` sea igual o posterior a este valor quedan protegidas frente a la expiración, incluso si la política de retención normalmente las expiraría. Puede combinarse con `snapshot_ids`; en ese caso, las instantáneas indicadas con esa marca de tiempo o una posterior no expiran.
* `retention_period = '<duration>'` — anula `history.expire.max-snapshot-age-ms` a nivel de tabla solo para esta invocación. Las instantáneas anteriores a esta duración (medida desde ahora) pasan a ser candidatas para la expiración. El valor es una cadena de duración compuesta por uno o más pares `{number}{unit}` concatenados. Unidades admitidas: `y` (365 días), `w` (7 días), `d` (24 horas), `h` (60 minutos), `m` (60 segundos), `s` (1 segundo), `ms` (1 milisegundo). Las unidades pueden combinarse, p. ej., `'3d'`, `'12h'`, `'1d12h30m'`, `'500ms'`.
* `retain_last = N` — anula `history.expire.min-snapshots-to-keep` a nivel de tabla solo para esta invocación. Siempre se conservan al menos `N` instantáneas, independientemente de su antigüedad.
* `snapshot_ids = [id1, id2, ...]` — expira exactamente los ID de instantánea indicados (excepto las instantáneas a las que hace referencia la instantánea actual, las ramas o las etiquetas). Este modo omite por completo la política de retención y no puede combinarse con `retention_period` ni con `retain_last`.
* `dry_run = 1` — calcula qué expiraría y devuelve métricas sin escribir metadatos nuevos ni eliminar archivos.

<Note>
  `retention_period` y `retain_last` anulan solo los valores predeterminados de retención **a nivel de tabla**. Las anulaciones de retención por referencia (rama/etiqueta) configuradas en las propiedades de la tabla Iceberg (p. ej., `refs.<branch>.min-snapshots-to-keep`) nunca se anulan; siempre se aplican tal como se especifican en los metadatos de la tabla.
</Note>

**Ejemplo:**

```sql theme={null}
SET allow_insert_into_iceberg = 1;

-- Crear algunas instantáneas insertando datos
INSERT INTO iceberg_table VALUES (1);
INSERT INTO iceberg_table VALUES (2);
INSERT INTO iceberg_table VALUES (3);

-- Expirar usando solo la política de retención
ALTER TABLE iceberg_table EXECUTE expire_snapshots();

-- Expirar con fusible de seguridad: proteger las instantáneas más recientes que la marca de tiempo (sintaxis posicional)
ALTER TABLE iceberg_table EXECUTE expire_snapshots('2025-01-01 00:00:00');

-- Lo mismo usando la forma de argumento con nombre
ALTER TABLE iceberg_table EXECUTE expire_snapshots(expire_before = '2025-01-01 00:00:00');

-- Sobreescribir los parámetros de retención para una ejecución
ALTER TABLE iceberg_table EXECUTE expire_snapshots(retention_period = '3d', retain_last = 10);

-- Expirar instantáneas explícitas
ALTER TABLE iceberg_table EXECUTE expire_snapshots(snapshot_ids = [101, 102, 103]);

-- Vista previa en modo simulado (sin actualizaciones de metadatos ni eliminación de archivos)
ALTER TABLE iceberg_table EXECUTE expire_snapshots(retention_period = '1d', dry_run = 1);
```

**Salida:**

El comando devuelve una tabla con dos columnas (`metric_name String`, `metric_value Int64`) que contiene una fila por cada métrica. Los nombres de las métricas siguen la [especificación de Iceberg](https://iceberg.apache.org/docs/latest/spark-procedures/#output):

| metric\_name                          | Descripción                                                           |
| ------------------------------------- | --------------------------------------------------------------------- |
| `deleted_data_files_count`            | Número de archivos de datos eliminados                                |
| `deleted_position_delete_files_count` | Número de archivos de eliminación por posición eliminados             |
| `deleted_equality_delete_files_count` | Número de archivos de eliminación por igualdad eliminados             |
| `deleted_manifest_files_count`        | Número de archivos de manifiesto eliminados                           |
| `deleted_manifest_lists_count`        | Número de archivos de lista de manifiestos eliminados                 |
| `deleted_statistics_files_count`      | Número de archivos de estadísticas eliminados (actualmente siempre 0) |
| `dry_run`                             | `1` para el modo de simulación, `0` para la ejecución normal          |

El comando realiza los siguientes pasos:

1. Evalúa la política de retención (consulte a continuación) para determinar qué instantáneas deben conservarse
2. Si se proporcionó un argumento de marca de tiempo, también protege todas las instantáneas en esa marca de tiempo o posteriores
3. Expira las instantáneas que no se conservan según la política ni están protegidas por la salvaguarda de marca de tiempo
4. Calcula qué archivos están asociados exclusivamente a instantáneas expiradas
5. En modo normal: genera metadatos nuevos sin las instantáneas expiradas
6. En modo normal: elimina físicamente las listas de manifiestos, los archivos de manifiesto y los archivos de datos inaccesibles
7. En el modo `dry_run = 1`: omite los pasos 5 y 6 y solo devuelve las métricas calculadas

<div id="iceberg-snapshot-retention-policy">
  #### Política de retención de instantáneas
</div>

El comando `expire_snapshots` respeta la [política de retención de instantáneas de Iceberg](https://iceberg.apache.org/spec/#snapshot-retention-policy). La retención se configura mediante propiedades de tabla de Iceberg y anulaciones por referencia:

| Propiedad                              | Ámbito | Predeterminado                                                                    | Descripción                                                                                                         |
| -------------------------------------- | ------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `history.expire.min-snapshots-to-keep` | Tabla  | `iceberg_expire_default_min_snapshots_to_keep` (predeterminado `1`)               | Número mínimo de instantáneas que deben conservarse en la cadena de ancestros de cada rama                          |
| `history.expire.max-snapshot-age-ms`   | Tabla  | `iceberg_expire_default_max_snapshot_age_ms` (predeterminado `432000000`, 5 días) | Antigüedad máxima (en ms) de las instantáneas que se conservan en una rama                                          |
| `history.expire.max-ref-age-ms`        | Tabla  | `iceberg_expire_default_max_ref_age_ms` (predeterminado `∞`)                      | Antigüedad máxima (en ms) de una referencia de instantánea (rama o etiqueta) antes de eliminar la propia referencia |

Cada referencia de instantánea (`refs` en los metadatos de Iceberg) puede sobrescribir estos valores con campos por referencia: `min-snapshots-to-keep`, `max-snapshot-age-ms` y `max-ref-age-ms`.

**Evaluación de la retención:**

* **Para cada rama** (incluida `main`): se recorre la cadena de ancestros a partir de la cabecera de la rama. Las instantáneas se conservan mientras se cumpla cualquiera de estas condiciones:
  * La instantánea es una de las primeras `min-snapshots-to-keep` de la cadena
  * La antigüedad de la instantánea está dentro de `max-snapshot-age-ms` (es decir, `now - timestamp-ms <= max-snapshot-age-ms`)
* **Para las etiquetas**: la instantánea etiquetada se conserva, salvo que la etiqueta haya superado su `max-ref-age-ms`, en cuyo caso se elimina la referencia de la etiqueta
* **Las referencias distintas de `main`** cuya antigüedad supera `max-ref-age-ms` se eliminan por completo (la rama `main` nunca se elimina)
* **Las referencias colgantes** que apuntan a instantáneas inexistentes se eliminan con una advertencia
* **La instantánea actual siempre se conserva**, independientemente de la configuración de retención

**Privilegios requeridos:**

Se requiere el privilegio `ALTER TABLE EXECUTE`, que es un privilegio secundario de `ALTER TABLE` en la jerarquía de control de acceso de ClickHouse. Puede concederlo específicamente o a través del privilegio principal:

```sql theme={null}
-- Otorgar solo el permiso EXECUTE
GRANT ALTER TABLE EXECUTE ON my_iceberg_table TO my_user;

-- O conceder todos los permisos de ALTER TABLE (incluye ALTER TABLE EXECUTE)
GRANT ALTER TABLE ON my_iceberg_table TO my_user;
```

<Note>
  * Solo se admiten tablas Iceberg format version 2 (las instantáneas v1 no garantizan `manifest-list`, que es necesario para identificar de forma segura los archivos que deben limpiarse)
  * La instantánea actual siempre se conserva, incluso si es anterior a la marca de tiempo especificada
  * Requiere que la configuración `allow_insert_into_iceberg` esté habilitada
  * Requiere que la configuración `allow_experimental_expire_snapshots` esté habilitada
  * La autorización propia del catalog (autenticación del REST catalog, AWS Glue IAM, etc.) se aplica de forma independiente cuando ClickHouse actualiza los metadatos
</Note>

<div id="iceberg-remove-orphan-files">
  ### Eliminar archivos huérfanos
</div>

Los archivos huérfanos son archivos almacenados a los que no hace referencia ninguna instantánea en los metadatos de la tabla Iceberg. Se acumulan por escrituras fallidas, limpiezas parciales tras la compactación y operaciones interrumpidas, lo que provoca un crecimiento ilimitado del almacenamiento. El comando `remove_orphan_files` identifica y elimina estos archivos huérfanos.

**Sintaxis:**

```sql theme={null}
-- Forma posicional: argumento older_than sin nombre
ALTER TABLE iceberg_table EXECUTE remove_orphan_files('timestamp')

-- Forma con nombre
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
    older_than = 'timestamp',
    location = 'path',
    dry_run = 0|1
)

-- Sin argumentos: usar todos los valores predeterminados (older_than = hace 3 días)
ALTER TABLE iceberg_table EXECUTE remove_orphan_files()
```

**Parámetros:**

| Parámetro    | Tipo                      | Predeterminado                                                                | Descripción                                                                                                                                                                                                      |
| ------------ | ------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `older_than` | `String` (marca temporal) | hace 3 días (configurable mediante `iceberg_orphan_files_older_than_seconds`) | Solo considera como candidatos a archivos huérfanos los archivos cuya fecha de última modificación sea anterior a esta marca temporal. Medida de seguridad para evitar eliminar archivos de escrituras en curso. |
| `location`   | `String`                  | Ubicación de la tabla                                                         | Restringe el análisis a un subdirectorio específico dentro de la ubicación de la tabla (por ejemplo, `'data/'` o `'metadata/'`).                                                                                 |
| `dry_run`    | `UInt64`                  | `0`                                                                           | Cuando es `1`, identifica archivos huérfanos y devuelve un resumen de los resultados sin eliminar realmente nada.                                                                                                |

**Ejemplos:**

```sql theme={null}
-- Eliminar archivos huérfanos más antiguos que una marca de tiempo específica
ALTER TABLE iceberg_table EXECUTE remove_orphan_files('2026-03-01 00:00:00');

-- Dry run: previsualizar qué archivos se eliminarían
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(dry_run = 1);

-- Analizar solo el directorio de datos
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
    older_than = '2026-03-01 00:00:00',
    location = 'data/'
);

-- Combinar older_than posicional con argumentos con nombre
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
    '2026-03-01 00:00:00',
    dry_run = 1
);
```

**Salida:**

El comando devuelve una tabla con las columnas `metric_name` y `metric_value`, que muestran el número de archivos eliminados (o que se eliminarían en el modo dry\_run) por categoría. Las categorías de archivos se clasifican mediante heurísticas basadas en convenciones de nomenclatura; los archivos que no coinciden con ningún patrón específico se asignan por defecto a `deleted_data_files_count`:

| metric\_name                            | metric\_value |
| --------------------------------------- | ------------- |
| deleted\_data\_files\_count             | 5             |
| deleted\_position\_delete\_files\_count | 2             |
| deleted\_equality\_delete\_files\_count | 0             |
| deleted\_manifest\_files\_count         | 3             |
| deleted\_manifest\_lists\_count         | 1             |
| deleted\_metadata\_files\_count         | 0             |
| deleted\_statistics\_files\_count       | 0             |
| skipped\_missing\_metadata\_count       | 0             |
| failed\_deletions\_count                | 0             |

**Configuración:**

| Configuración                             | Tipo     | Predeterminado    | Descripción                                                                     |
| ----------------------------------------- | -------- | ----------------- | ------------------------------------------------------------------------------- |
| `allow_iceberg_remove_orphan_files`       | `Bool`   | `false`           | Configuración de control para habilitar la funcionalidad (experimental).        |
| `iceberg_orphan_files_older_than_seconds` | `UInt64` | `259200` (3 days) | Umbral predeterminado de `older_than` en segundos cuando se omite el argumento. |

<Note>
  * **Requiere Iceberg format version 2 (o superior).** Las tablas de la versión 1 se rechazan porque no tienen punteros `manifest-list` en las instantáneas, que son necesarios para determinar de forma segura el conjunto de archivos accesibles. Ejecutar el comando en una tabla v1 devuelve un error `BAD_ARGUMENTS`.
  * Requiere que estén habilitadas tanto la configuración `allow_insert_into_iceberg` como `allow_iceberg_remove_orphan_files`
  * Se recomienda ejecutar `expire_snapshots` antes de `remove_orphan_files` para que los archivos referenciados exclusivamente por instantáneas expiradas se limpien primero
  * Use `dry_run = 1` para ver los orphan files antes de eliminarlos
  * El umbral `older_than` protege contra la eliminación de archivos de escrituras en curso; el umbral predeterminado de 3 días proporciona un amplio margen de seguridad
</Note>

<div id="see-also">
  ## Véase también
</div>

* [motor de Iceberg](/es/reference/engines/table-engines/integrations/iceberg)
* [función de tabla de clúster de Iceberg](/es/reference/functions/table-functions/icebergCluster)
