Glossary Entry

Apache Spark SQL on Iceberg: Catalyst, Tungsten, and MERGE INTO

Apache Spark SQL is Spark's module for structured data, SQL, and DataFrames. Learn how Catalyst plans queries and how Spark SQL writes Iceberg tables.

Written by Alex Merced

What Apache Spark SQL Is

Apache Spark SQL is the module of Apache Spark that processes structured and semi-structured data. It provides a SQL interface and the DataFrame and Dataset APIs (in Scala, Java, Python, and R), and it plans every one of them through the same optimizer. Most modern Spark code, including PySpark DataFrame code, runs through Spark SQL rather than the low-level RDD API.

In a data lakehouse, Spark SQL is a common engine for batch ingestion, transformations between medallion layers, and table maintenance on Apache Iceberg, Delta Lake, and Apache Hudi tables.

The Catalyst Optimizer

Catalyst turns a query into an execution plan in four phases:

  1. Analysis. Resolve table and column names against the catalog (a Hive Metastore, an Iceberg REST catalog, AWS Glue) and check types.
  2. Logical optimization. Apply rule-based rewrites such as predicate pushdown, column pruning, and constant folding. A filter written after a join is moved before it when that is safe, which cuts how much data gets shuffled.
  3. Physical planning. Choose join strategies and other physical operators, for example a broadcast hash join when one side is small or a sort-merge join for two large inputs. The cost-based optimizer uses table and column statistics when they are available.
  4. Code generation. Compile parts of the plan into Java bytecode at runtime.

Since Spark 3.2, Adaptive Query Execution (AQE) is on by default. It re-optimizes the plan at stage boundaries using real shuffle statistics: coalescing small partitions, switching to a broadcast join when one side turns out small, and splitting skewed partitions.

Project Tungsten

Tungsten is Spark’s work on memory and CPU efficiency. It stores rows in a compact binary format (UnsafeRow) instead of one Java object per value, can manage memory off the garbage-collected heap for shuffle and sort buffers, and uses whole-stage code generation to fuse a chain of operators into one generated function. These changes reduce object overhead and garbage collection pressure. They do not remove the JVM: Spark still runs on it, and wide scans can still spend real time in garbage collection, which is the problem native accelerators like DataFusion Comet target. See DataFusion Comet 1.0 and Spark on Iceberg.

Spark SQL with Apache Iceberg

With the Iceberg runtime JAR and Iceberg’s SQL extensions enabled, Spark SQL gains row-level DML and maintenance procedures on Iceberg tables:

MERGE INTO lake.sales.orders t
USING updates u
ON t.order_id = u.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;

CALL lake.system.rewrite_data_files(table => 'sales.orders');
CALL lake.system.expire_snapshots(table => 'sales.orders', older_than => TIMESTAMP '2026-09-01 00:00:00');

Spark SQL also reads Iceberg metadata tables (orders.snapshots, orders.files) and supports time travel with VERSION AS OF and TIMESTAMP AS OF. For a hands-on setup with an open catalog, see Intro to Apache Iceberg with Apache Polaris and Apache Spark.

Spark SQL vs Other Iceberg Engines

Spark SQLTrinoDremioDuckDB
Best fitLarge batch ETL, streaming writes, table maintenanceInteractive SQL across many sourcesInteractive BI and federation with accelerationLocal and single-node analysis
Startup costCluster or session spin-upLong-running clusterLong-running serviceIn-process, instant
Iceberg writes and proceduresFull, including CALL proceduresWrites and some proceduresWrites and table optimizationGrowing support

Many lakehouses use Spark SQL for heavy writes and maintenance and a separate engine such as Trino, Dremio, or DuckDB for interactive reads, all sharing one Iceberg catalog.

Learn More

To learn more about the Data Lakehouse, read The Lakehouse Built for Everyone by Alex Merced. You can find this and other books by Alex Merced at books.alexmerced.com.

Related Terms

Apache Spark for the Lakehouse: Architecture, Iceberg Writes, and When to Use Other Engines

Apache Spark is an open source distributed engine for batch, SQL, and streaming. Learn how it writes Iceberg tables and when Trino, Dremio, or Flink fit better.

Broadcast Hash Join: How Spark, Trino, and Dremio Join Iceberg Tables

A broadcast hash join copies a small table to every worker so a large table joins without a shuffle. See how Spark and Trino pick it and how Iceberg stats help.

Sort-Merge Join vs Hash Join in Spark, Trino, and Iceberg

A sort-merge join shuffles and sorts both inputs on the join key, then merges. Learn when Spark picks it over hash joins and how Iceberg can skip the shuffle.

What is a Cost-Based Optimizer (CBO)?

A cost-based optimizer is the query planner that uses table statistics to estimate the cost of each execution plan and picks the cheapest, via catalog metadata.

Trino and Apache Iceberg: How It Compares to Spark and Dremio

Trino is an open source distributed SQL engine that queries data where it lives. See how it reads and writes Iceberg tables and compares to Spark and Dremio.

What is Predicate Pushdown?

Predicate pushdown is a query optimization that applies filters at storage so engines read less data. See how Parquet stats and Iceberg manifests make it work.

Apache DataFusion and Iceberg: The Embeddable Rust Query Engine

Apache DataFusion is an embeddable SQL query engine written in Rust on Apache Arrow. See how it reads Iceberg tables and compares to DuckDB, Spark, and Trino.

Work with Alex