Catalog

Catalog is the entry point for accessing iceberg tables. You can use a catalog to:

  • Create and list namespaces.
  • Create, load, and drop tables

Currently only rest catalog has been implemented, and other catalogs are under active development. Here is an example of how to create a RestCatalog:

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;

use iceberg::{Catalog, NamespaceIdent};
use iceberg_catalog_rest::{RestCatalog, RestCatalogConfig};

static REST_URI: &str = "http://localhost:8181";

/// This is a simple example that demonstrates how to use [`RestCatalog`] to create namespaces.
///
/// The demo creates a namespace and prints it out.
///
/// A running instance of the iceberg-rest catalog on port 8181 is required. You can find how to run
/// the iceberg-rest catalog with `docker compose` in the official
/// [quickstart documentation](https://iceberg.apache.org/spark-quickstart/).
#[tokio::main]
async fn main() {
    // Create the REST iceberg catalog.
    let config = RestCatalogConfig::builder()
        .uri(REST_URI.to_string())
        .build();
    let catalog = RestCatalog::new(config);

    // List all namespaces already in the catalog.
    let existing_namespaces = catalog.list_namespaces(None).await.unwrap();
    println!(
        "Namespaces alreading in the existing catalog: {:?}",
        existing_namespaces
    );

    // Create a new namespace identifier.
    let namespace_ident =
        NamespaceIdent::from_vec(vec!["ns1".to_string(), "ns11".to_string()]).unwrap();

    // Drop the namespace if it already exists.
    if catalog.namespace_exists(&namespace_ident).await.unwrap() {
        println!("Namespace already exists, dropping now.",);
        catalog.drop_namespace(&namespace_ident).await.unwrap();
    }

    // Create the new namespace in the catalog.
    let _created_namespace = catalog
        .create_namespace(
            &namespace_ident,
            HashMap::from([("key1".to_string(), "value1".to_string())]),
        )
        .await
        .unwrap();
    println!("Namespace {:?} created!", namespace_ident);

    let loaded_namespace = catalog.get_namespace(&namespace_ident).await.unwrap();
    println!("Namespace loaded!\n\nNamespace: {:#?}", loaded_namespace,);
}

You can run following code to list all root namespaces:

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;

use iceberg::{Catalog, NamespaceIdent};
use iceberg_catalog_rest::{RestCatalog, RestCatalogConfig};

static REST_URI: &str = "http://localhost:8181";

/// This is a simple example that demonstrates how to use [`RestCatalog`] to create namespaces.
///
/// The demo creates a namespace and prints it out.
///
/// A running instance of the iceberg-rest catalog on port 8181 is required. You can find how to run
/// the iceberg-rest catalog with `docker compose` in the official
/// [quickstart documentation](https://iceberg.apache.org/spark-quickstart/).
#[tokio::main]
async fn main() {
    // Create the REST iceberg catalog.
    let config = RestCatalogConfig::builder()
        .uri(REST_URI.to_string())
        .build();
    let catalog = RestCatalog::new(config);

    // List all namespaces already in the catalog.
    let existing_namespaces = catalog.list_namespaces(None).await.unwrap();
    println!(
        "Namespaces alreading in the existing catalog: {:?}",
        existing_namespaces
    );

    // Create a new namespace identifier.
    let namespace_ident =
        NamespaceIdent::from_vec(vec!["ns1".to_string(), "ns11".to_string()]).unwrap();

    // Drop the namespace if it already exists.
    if catalog.namespace_exists(&namespace_ident).await.unwrap() {
        println!("Namespace already exists, dropping now.",);
        catalog.drop_namespace(&namespace_ident).await.unwrap();
    }

    // Create the new namespace in the catalog.
    let _created_namespace = catalog
        .create_namespace(
            &namespace_ident,
            HashMap::from([("key1".to_string(), "value1".to_string())]),
        )
        .await
        .unwrap();
    println!("Namespace {:?} created!", namespace_ident);

    let loaded_namespace = catalog.get_namespace(&namespace_ident).await.unwrap();
    println!("Namespace loaded!\n\nNamespace: {:#?}", loaded_namespace,);
}

Then you can run following code to create namespace:

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;

use iceberg::{Catalog, NamespaceIdent};
use iceberg_catalog_rest::{RestCatalog, RestCatalogConfig};

static REST_URI: &str = "http://localhost:8181";

/// This is a simple example that demonstrates how to use [`RestCatalog`] to create namespaces.
///
/// The demo creates a namespace and prints it out.
///
/// A running instance of the iceberg-rest catalog on port 8181 is required. You can find how to run
/// the iceberg-rest catalog with `docker compose` in the official
/// [quickstart documentation](https://iceberg.apache.org/spark-quickstart/).
#[tokio::main]
async fn main() {
    // Create the REST iceberg catalog.
    let config = RestCatalogConfig::builder()
        .uri(REST_URI.to_string())
        .build();
    let catalog = RestCatalog::new(config);

    // List all namespaces already in the catalog.
    let existing_namespaces = catalog.list_namespaces(None).await.unwrap();
    println!(
        "Namespaces alreading in the existing catalog: {:?}",
        existing_namespaces
    );

    // Create a new namespace identifier.
    let namespace_ident =
        NamespaceIdent::from_vec(vec!["ns1".to_string(), "ns11".to_string()]).unwrap();

    // Drop the namespace if it already exists.
    if catalog.namespace_exists(&namespace_ident).await.unwrap() {
        println!("Namespace already exists, dropping now.",);
        catalog.drop_namespace(&namespace_ident).await.unwrap();
    }

    // Create the new namespace in the catalog.
    let _created_namespace = catalog
        .create_namespace(
            &namespace_ident,
            HashMap::from([("key1".to_string(), "value1".to_string())]),
        )
        .await
        .unwrap();
    println!("Namespace {:?} created!", namespace_ident);

    let loaded_namespace = catalog.get_namespace(&namespace_ident).await.unwrap();
    println!("Namespace loaded!\n\nNamespace: {:#?}", loaded_namespace,);
}

Table

After creating Catalog, we can manipulate tables through Catalog.

You can use following code to create a table:

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;

use iceberg::spec::{NestedField, PrimitiveType, Schema, Type};
use iceberg::{Catalog, NamespaceIdent, TableCreation, TableIdent};
use iceberg_catalog_rest::{RestCatalog, RestCatalogConfig};

static REST_URI: &str = "http://localhost:8181";
static NAMESPACE: &str = "default";
static TABLE_NAME: &str = "t1";

/// This is a simple example that demonstrates how to use [`RestCatalog`] to create tables.
///
/// The demo creates a table creates a table and then later retrieves the same table.
///
/// A running instance of the iceberg-rest catalog on port 8181 is required. You can find how to run
/// the iceberg-rest catalog with `docker compose` in the official
/// [quickstart documentation](https://iceberg.apache.org/spark-quickstart/).
#[tokio::main]
async fn main() {
    // Create the REST iceberg catalog.
    let config = RestCatalogConfig::builder()
        .uri(REST_URI.to_string())
        .build();
    let catalog = RestCatalog::new(config);

    // Create the table identifier.
    let namespace_ident = NamespaceIdent::from_vec(vec![NAMESPACE.to_string()]).unwrap();
    let table_ident = TableIdent::new(namespace_ident.clone(), TABLE_NAME.to_string());

    // You can also use the `from_strs` method on `TableIdent` to create the table identifier.
    // let table_ident = TableIdent::from_strs([NAMESPACE, TABLE_NAME]).unwrap();

    // Drop the table if it already exists.
    if catalog.table_exists(&table_ident).await.unwrap() {
        println!("Table {TABLE_NAME} already exists, dropping now.");
        catalog.drop_table(&table_ident).await.unwrap();
    }

    // Build the table schema.
    let table_schema = Schema::builder()
        .with_fields(vec![
            NestedField::optional(1, "foo", Type::Primitive(PrimitiveType::String)).into(),
            NestedField::required(2, "bar", Type::Primitive(PrimitiveType::Int)).into(),
            NestedField::optional(3, "baz", Type::Primitive(PrimitiveType::Boolean)).into(),
        ])
        .with_schema_id(1)
        .with_identifier_field_ids(vec![2])
        .build()
        .unwrap();

    // Build the table creation parameters.
    let table_creation = TableCreation::builder()
        .name(table_ident.name.clone())
        .schema(table_schema.clone())
        .properties(HashMap::from([("owner".to_string(), "testx".to_string())]))
        .build();

    // Create the table.
    let _created_table = catalog
        .create_table(&table_ident.namespace, table_creation)
        .await
        .unwrap();
    println!("Table {TABLE_NAME} created!");

    // Ensure that the table is under the correct namespace.
    assert!(catalog
        .list_tables(&namespace_ident)
        .await
        .unwrap()
        .contains(&table_ident));

    // Load the table back from the catalog. It should be identical to the created table.
    let loaded_table = catalog.load_table(&table_ident).await.unwrap();
    println!("Table {TABLE_NAME} loaded!\n\nTable: {:?}", loaded_table);
}

Also, you can load a table directly:

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::HashMap;

use iceberg::spec::{NestedField, PrimitiveType, Schema, Type};
use iceberg::{Catalog, NamespaceIdent, TableCreation, TableIdent};
use iceberg_catalog_rest::{RestCatalog, RestCatalogConfig};

static REST_URI: &str = "http://localhost:8181";
static NAMESPACE: &str = "default";
static TABLE_NAME: &str = "t1";

/// This is a simple example that demonstrates how to use [`RestCatalog`] to create tables.
///
/// The demo creates a table creates a table and then later retrieves the same table.
///
/// A running instance of the iceberg-rest catalog on port 8181 is required. You can find how to run
/// the iceberg-rest catalog with `docker compose` in the official
/// [quickstart documentation](https://iceberg.apache.org/spark-quickstart/).
#[tokio::main]
async fn main() {
    // Create the REST iceberg catalog.
    let config = RestCatalogConfig::builder()
        .uri(REST_URI.to_string())
        .build();
    let catalog = RestCatalog::new(config);

    // Create the table identifier.
    let namespace_ident = NamespaceIdent::from_vec(vec![NAMESPACE.to_string()]).unwrap();
    let table_ident = TableIdent::new(namespace_ident.clone(), TABLE_NAME.to_string());

    // You can also use the `from_strs` method on `TableIdent` to create the table identifier.
    // let table_ident = TableIdent::from_strs([NAMESPACE, TABLE_NAME]).unwrap();

    // Drop the table if it already exists.
    if catalog.table_exists(&table_ident).await.unwrap() {
        println!("Table {TABLE_NAME} already exists, dropping now.");
        catalog.drop_table(&table_ident).await.unwrap();
    }

    // Build the table schema.
    let table_schema = Schema::builder()
        .with_fields(vec![
            NestedField::optional(1, "foo", Type::Primitive(PrimitiveType::String)).into(),
            NestedField::required(2, "bar", Type::Primitive(PrimitiveType::Int)).into(),
            NestedField::optional(3, "baz", Type::Primitive(PrimitiveType::Boolean)).into(),
        ])
        .with_schema_id(1)
        .with_identifier_field_ids(vec![2])
        .build()
        .unwrap();

    // Build the table creation parameters.
    let table_creation = TableCreation::builder()
        .name(table_ident.name.clone())
        .schema(table_schema.clone())
        .properties(HashMap::from([("owner".to_string(), "testx".to_string())]))
        .build();

    // Create the table.
    let _created_table = catalog
        .create_table(&table_ident.namespace, table_creation)
        .await
        .unwrap();
    println!("Table {TABLE_NAME} created!");

    // Ensure that the table is under the correct namespace.
    assert!(catalog
        .list_tables(&namespace_ident)
        .await
        .unwrap()
        .contains(&table_ident));

    // Load the table back from the catalog. It should be identical to the created table.
    let loaded_table = catalog.load_table(&table_ident).await.unwrap();
    println!("Table {TABLE_NAME} loaded!\n\nTable: {:?}", loaded_table);
}