Apache Iceberg: Library Implementations Compared

Every real Iceberg library implementation — the five official Apache language libraries plus the one notable community client — inspected at source level and cross-checked against the official implementation-status matrix, each project's own documentation, and the table spec.
Generated 2026-07-19 · repos cloned at HEAD (commits 2026-06-08 → 2026-07-19) · claim source: iceberg.apache.org/status (site/docs/status.md @ apache/iceberg 1ec1505) · spec: format/spec.md (v1–v3 adopted, v4 in development)
6
Real implementations
5 Apache + 1 community (JS)
1
Complete implementation
Java — reference, v1–v3 + v4 dev
3
Spec versions adopted
v1, v2, v3 · v4 in development
31
Docs↔code mismatches found
official matrix + project docs
0
.NET / other languages
no real implementation exists

1 · The landscape

Iceberg is not "implemented in the top 10 languages" — it is implemented for real in five languages under the Apache umbrella, plus one deliberately narrow community client in TypeScript. Nothing real exists for .NET, Ruby, PHP, etc. (C# usage goes through engines or hand-rolled metadata writing). The implementations are at radically different maturity levels, and the official status page tracks them poorly in both directions.

LibraryLanguageVersion @ HEADScale (approx.)ScopeMaturity verdict (from code)
apache/icebergJava1.11.0-SNAPSHOT (latest release 1.10.2)~110k LOC core + ~40k APIFull table format + engines (Spark, Flink, Hive, Kafka Connect)Reference. Only complete implementation: v1–v3 fully, v4 underway, views, encryption, 11 catalogs, full maintenance suite.
apache/iceberg-pythonPython (PyIceberg)0.11.0~38k LOCFull client: read + write, catalogs, CLIMature for v1/v2 Parquet read/write; v3 write hard-blocked; no equality deletes, no merge-on-read, no compaction.
apache/iceberg-rustRust0.10.0~248 source files, 12 cratesCore lib + catalogs + DataFusion integrationStrong metadata/scan layer; write side is append-only — no overwrite/delete/upsert transaction actions yet.
apache/iceberg-goGountagged pre-1.0~69k LOC (+104k test)Full client + CLIDeepest non-Java implementation: v3 deletion vectors read+write, merge-on-read deletes, compaction, orphan cleanup.
apache/iceberg-cppC++ (C++23)0.4.0-dev (released: 0.3.0)~380 source filesCore lib on Arrow; REST/SQL catalogsYoung but real and moving fast: transactions, DV read/write, branching. Hive catalog is a 100% stub; no views/ORC.
supabase/iceberg-jsTypeScript1.0.0~1k LOC srcREST Catalog HTTP client onlyPolished for its narrow scope (catalog CRUD, spec-pinned 1.11.0-rc1). Not a table-format engine: zero Parquet/Avro/manifest I/O.

2 · Feature matrix — what the code actually supports

Every cell below is derived from source inspection, not from any project's own claims. Where a cell disagrees with the official status matrix or the project's docs, it appears again in section 4.

Yes implemented, wired end-to-end Partial exists but incomplete / not wired / one-sided No not implemented n/a out of scope for the library
CapabilityJavaPyIcebergRustGoC++iceberg-js
Spec format versions
v1 read + write YesYesYesYesYesn/acatalog client; DTOs only
v2 read + write YesdefaultYesdefaultYesYesYesdefaultn/a
v3 read YesPartialparses metadata; eq-deletes unreadPartialmetadata + row lineage; no DVYesPartialtypes + row-lineage readn/a
v3 write YesNoraises NotImplementedError (metadata.py:578)Partialupgrade + lineage on appendYesincl. v2→v3 upgradePartialn/a
v4 (in development) PartialV4Metadata, Parquet manifestsNoNoNoNon/a
v3 marker features
Deletion vectors — read YesYesNoexplicit TODO; Puffin primitives idleYesYesn/a
Deletion vectors — write YesNoNoYesYesn/a
Row lineage YesPartialfields modeled onlyPartialassigned on fast-appendYesPartialread onlyn/a
Variant type Yesincl. Parquet shreddingNono VariantType class existsPartialtype defined; Parquet write rejects itYesincl. shredded variantNon/a
Geometry / geography Yesnot in ORCPartialtypes only; WKB/WKT conv raisesNoabsent from type enumYesgeoarrow WKB + bounds; nested TODONon/a
timestamp_ns / unknown / defaults YesPartialns downcast to µs on writePartialns + defaults; no unknown typeYesYesns + unknown + defaults gatedn/a
Read path
Scan planning + predicate pushdown Yesmetrics, dict, bloom, distributedYes+ REST server-side planningYesrow-group, page index, row selectionYesrow-group stats; decimal bloom TODOPartialfile-level only; row-group TODOn/a
Positional delete reads YesYesYesYesYesn/a
Equality delete reads YesNoraises on eq-delete filesYesYesYesn/a
Incremental / changelog scans Yesappend + CDC changelogPartialappend-onlyNoNoexplicitly deferredPartialboth, changelog w/o deletesn/a
Write path
Append YesYesYesfast-append onlyYesYesfast + merge appendn/a
Overwrite / rewrite YesYesNono transaction actionYesYesn/a
Row-level delete Yescopy-on-write + merge-on-readPartialCOW only; MoR silently falls backNoYesCOW + MoRYesrow-delta + delete-files actionsn/a
Upsert / merge YesYesCOWNoNorow-delta exists, no upsert APIPartialvia row-deltan/a
Position / equality delete writes YesNoPartialeq-writer exists, no commit actionYesYesn/a
Transactions, commit + retry YesYesYesYes+ multi-table txnsYesPartialcommit endpoint + idempotency keys
Evolution & snapshots
Schema evolution YesYesYesYesYesunion-by-name NotImplementedn/a
Partition evolution YesYesNospec builder only, no txn actionYesYesn/a
All partition transforms YesYesYesYesYesn/a
Time travel Yesid + timestamp + refYesid + refPartialsnapshot-id onlyYesYesid + timestamp + refn/a
Branching & tagging YesYesNomodel only; writes pin mainYesYesincl. fast-forwardn/a
File formats
Parquet (data) Yes+ vectorizedYesYesYesYesn/a
Avro (data) YesNomanifests onlyNomanifests onlyNomanifests onlyYesn/a
ORC (data) YesPartialread onlyNoNoNon/a
Puffin (stats / DV blobs) YesPartialread onlyYesr/w; DV blobs not wiredYesYesn/a
Catalogs, storage, views, ops
Catalogs (working) 11REST, Hive, Glue, JDBC, Nessie, DynamoDB, Hadoop, InMem, Snowflake, BigQuery, ECS 7REST, SQL, Hive, Glue, DynamoDB, BigQuery, InMem 4 + 2 partialREST, Glue, SQL, Mem; HMS can't commit, S3Tables partial 5REST, SQL, Glue, Hive, Hadoop 3 + stubREST, SQL, InMem; Hive = 100% stub REST onlyby design
FileIO backends YesS3, GCS, ADLS, HDFS, OSS, ECS, local YesS3, GCS, ADLS, HDFS, OSS, HF, local PartialS3, GCS, ADLS, OSS, HF, local — no HDFS PartialS3, GCS, ADLS, local — no HDFS Partiallocal + S3 only n/a
Views Yes5 view catalogsPartialREST catalog onlyPartialspec model onlyYesREST, SQL, Hive (not Glue)NoNodeclared non-goal
Maintenance (expire, compact, orphans) Yesfull action suitePartialexpire only; no compactionPartialexpire onlyYesexpire + compaction + orphan + rewrite manifestsPartialexpire + rewrite; no orphan removaln/a
Table encryption YesAES-GCM + KMS, v3-gatedNoPartialscaffolding + memory KMSNoNoheader onlyPartialencryption-key DTOs only

3 · Per-implementation profiles

Strengths and hard limits per library, with code evidence. "Hard limit" means the code raises, stubs, or simply lacks the path — not a style judgment.

Java — the reference

1.11.0-SNAPSHOT · apache/iceberg @ 1ec1505
Strengths
  • Only implementation covering the entire adopted spec: v1–v3 read/write incl. deletion vectors (BaseDVFileWriter), variant with Parquet shredding, geospatial types, row lineage, CDC changelog scans.
  • v4 development is real, not aspirational: SUPPORTED_TABLE_FORMAT_VERSION = 4, V4Metadata.java, Parquet manifests (v4-only; v3 manifests hard-assert Avro).
  • Full maintenance action suite, native table encryption (StandardEncryptionManager, v3-gated), 11 working catalogs.
Hard limits
  • Multi-arg partition transforms not implemented — PartitionField holds a single sourceId (spec reserves them).
  • ORC lacks geometry/geography (falls to UnsupportedOperationException).
  • Maintenance runners live engine-side (Spark); core ships planners.

PyIceberg — v2 workhorse, v3 wall

0.11.0 · apache/iceberg-python @ 921e111
Strengths
  • Complete v1/v2 client: append/overwrite/upsert/delete (COW), full schema & partition evolution, branch/tag, expire-snapshots, 7 catalogs, broad FileIO, CLI, Arrow-native reads with server-side REST scan planning.
  • Reads positional deletes and Puffin deletion vectors (_read_deletes PUFFIN branch).
Hard limits
  • v3 write is a hard block: model_dump_jsonNotImplementedError("Writing V3 is not yet supported") (metadata.py:578, #1551).
  • Equality deletes unreadable — scan raises (table/__init__.py:2635); no delete-file writes of any kind; merge-on-read silently falls back to copy-on-write with a warning.
  • No variant type at all; sort order never physically applied on write (sort_order_id=None hardcoded, #271); no compaction/orphan removal; views only via REST catalog.

Rust — strong skeleton, append-only pen

0.10.0 · apache/iceberg-rust @ db4f609
Strengths
  • High-quality async scan pipeline: manifest/metrics/row-group/page-index evaluators, Parquet row selection, positional and equality delete reads.
  • Sound transaction core with retry/backoff; row lineage assigned on append; DataFusion integration; Puffin r/w with theta-sketch stats.
Hard limits
  • Transaction surface is fast-append only — no overwrite, row-delta, delete, or upsert actions (transaction/mod.rs:135-170).
  • DV reads are an explicit TODO (caching_delete_file_loader.rs:54) despite DELETION_VECTOR_V1 constants and a working Puffin writer — primitives imply a capability the pipeline doesn't deliver.
  • No partition evolution action, no branch/tag API (writes pin MAIN_BRANCH), time travel by snapshot-id only, no geometry/geography, HMS catalog cannot commit (update_table → FeatureUnsupported).

Go — the quiet overachiever

untagged pre-1.0 · apache/iceberg-go @ 0968050
Strengths
  • Deepest non-Java feature set: v3 deletion vectors read and write, row lineage, shredded variant, geospatial via geoarrow WKB with bounds, merge-on-read and copy-on-write deletes, row-delta with conflict validation, multi-table transactions.
  • Operational tooling nobody else (but Java) has: bin-pack compaction, orphan-file cleanup, rewrite-manifests, and a 20+ subcommand CLI.
Hard limits
  • No upsert API; no incremental scans (explicitly deferred); REST server-side scan planning stubbed (ErrNotImplemented, #1178).
  • No ORC; no HDFS FileIO; nested geo-column bounds and decimal bloom filters are open TODOs; module is untagged (no release discipline yet).

C++ — young, real, uneven

0.4.0-dev (0.3.0 released) · apache/iceberg-cpp @ fb7cc83
Strengths
  • Genuine implementation on Arrow C Data Interface: full transaction set (fast/merge append, overwrite, row-delta, rewrite, delete-files) with commit retry; DV read and write; branching/tagging incl. fast-forward; Parquet + Avro + Puffin r/w.
  • REST catalog is production-shaped: OAuth2 with token refresh, SigV4, vended credentials, server-side scan planning.
Hard limits
  • Hive catalog is a total stub — every method returns NotImplemented (hive_catalog.cc:31-129) despite being an advertised build option.
  • No views/UDF, no ORC, FileIO is local + S3 only, no row-group predicate filtering (TODO parquet_reader.cc:222), UnionByNameWith NotImplemented, changelog scans can't handle deletes, encryption is a lone header.

iceberg-js — a catalog client, on purpose

1.0.0 · supabase/iceberg-js @ 4ccd904
Strengths
  • Zero-dependency TypeScript REST Catalog client pinned to spec apache-iceberg-1.11.0-rc1 with an automated OpenAPI conformance suite — namespaces, tables, commit/requirements, register/rename, vended-credential headers, UUIDv7 idempotency keys, conditional GETs.
  • Types model the full v1–v3 superset (row lineage, encryption keys, 22 update actions).
Hard limits
  • Not a table-format implementation: no Parquet/Avro/manifest I/O, no scan or write path — it never touches storage.
  • No views, multi-table transactions, OAuth token flow, or scan-planning endpoints; no version-conditional runtime behavior.

4 · Where the documentation is wrong

The cross-check's main finding: the official implementation-status matrix at iceberg.apache.org/status is stale in both directions, and several projects' own docs disagree with their code. Every row below was verified against source at HEAD.

Overstated docs claim support the code doesn't deliver Understated code has it, docs say it doesn't (stale) Doc bug in-project docs contradict the code

4.1 Official status matrix vs. code

Claim on iceberg.apache.org/statusDirectionWhat the code says
PyIceberg supports variant — YOverstatedNo VariantType exists anywhere in pyiceberg; the only reference is a TODO comment (transforms.py:722).
PyIceberg supports timestamp_ns — YOverstatedType classes exist, but writes downcast ns→µs with a warning (io/pyarrow.py:1466) and v3 persistence is blocked entirely.
Rust + Go support Hadoop Filesystem — YOverstatedNeither has an HDFS backend: Rust's opendal storage crate ships s3/gcs/azdls/oss/hf/fs/memory only; Go's io + gocloud layer has no hdfs scheme.
Rust writes equality deletes — YOverstatedAn EqualityDeleteWriter exists, but no transaction action can commit delete files — the only data-committing action is fast_append.
PyIceberg "Read data file" (spec v1) — NUnderstatedPyIceberg reads v1 tables fine — v1 metadata is parsed and normalized in-memory (TableMetadataV1.to_v2).
C++ column: append/write ops N, S3 N, SQL catalog N, Puffin N, timestamp_ns/unknown NUnderstatedAll exist at HEAD: full update-action suite + data writer, Arrow S3 FileIO, SQL catalog (SQLite/Postgres/MySQL), Puffin r/w, TimestampNsType and kUnknown. The column reflects ~0.1.0-era status.
Puffin: PyIceberg N, Rust N, Go NUnderstatedPyIceberg reads Puffin DVs; Rust has Puffin read and write (theta-sketch stats); Go has a full Puffin reader/writer and writes deletion vectors with it.
Expire snapshots: PyIceberg N, Rust NUnderstatedBoth implemented: MaintenanceTable.expire_snapshots (py), transaction/expire_snapshots.rs (rust).
Replace sort order / manage snapshots: PyIceberg NUnderstatedUpdateSortOrder (table/update/sorting.py) and ManageSnapshots (create/remove branch & tag) both exist.
REST createView: PyIceberg NUnderstatedReal implementation in catalog/rest/__init__.py:972 (verified: builds schema, POSTs view). The N is stale.
ORC: PyIceberg NUnderstatedORC read works for data and positional-delete files; only the write side is missing. A binary Y/N cell can't express it — the truth is "read-only".
Geometry/geography: Go NUnderstatedGo reads and writes geo columns via geoarrow WKB with bounds statistics (arrow_utils.go:382,684).

4.2 Project docs vs. their own code

ProjectDoc claimDirectionReality in code
PyIceberggeospatial.md shows creating a v3 table with geometry columns as a working exampleDoc bugThe example fails at commit: v3 serialization raises NotImplementedError (metadata.py:578). Geo WKB↔WKT conversions also raise (conversions.py:515-690).
PyIcebergAccepts write.delete.mode=merge-on-read as a table propertyDoc bugProperty accepted, then silently ignored — delete falls back to copy-on-write with only a warning (table/__init__.py:730).
C++index.md: "Full table spec support today"Overstatedv3 is partial (row-lineage read-only, no variant/geo), no views/UDF, no ORC, union-by-name schema merge NotImplemented.
C++index.md: Puffin "on the roadmap"UnderstatedPuffin reader and writer shipped in 0.3.0 — the roadmap line is stale in the opposite direction.
C++README advertises ICEBERG_BUILD_HIVE / Hive catalog clientOverstatedEvery HiveCatalog method returns NotImplemented (hive_catalog.cc:31-129); the generated Thrift client is unused. Buildable, non-functional.
GoREADME matrix: REST "Load View" unsupported; "Rewrite manifests" blankUnderstatedBoth implemented: LoadView (rest.go:2188), rewrite_manifests.go + CLI subcommand. README lags the code.
Gotransforms.go:84 comment: transforms "do not yet provide transformation functions"UnderstatedStale comment — all transforms ship working Apply/Transformer functions.
RustREADME lists HMS and S3Tables as peer catalog cratesOverstatedHMS update_table → FeatureUnsupported (can't commit writes); S3Tables lacks drop_table/register/update_namespace. Neither caveat appears in docs.
RustPublic API exposes Type::Variant and Puffin DELETION_VECTOR_V1OverstatedAPI surface implies capability the pipelines lack: Parquet writer rejects variant columns; DV blobs are neither read (TODO) nor written by the delete path.
iceberg-jsREADME: "1:1 mapping to the Iceberg REST Catalog API"OverstatedViews, multi-table transactions, OAuth token, scan-planning, and metrics endpoints are absent (some declared non-goals — but then it isn't 1:1).
iceberg-jsREADME: createNamespace(): Promise<void>; types table shows fixed(length)Doc bugCode returns CreateNamespaceResponse, not void; the parser accepts only Iceberg-canonical fixed[length] — the parenthesized form in the docs wouldn't parse.
JavaAlignedNo material mismatch found. format/spec.md is candid that v4 is "under active development and not formally adopted," which matches the code (ceiling = 4, default = 2, V4Metadata partial).

5 · Spec conformance takeaways

Measured against the adopted spec (v1–v3), the practical picture as of July 2026:

If you need…UseWhy
Full spec, v3 features, views, encryption, v4 lead timeJavaOnly complete implementation; everything else is a subset of it.
Python data engineering on v1/v2 tablesPyIcebergMature read/write + catalogs — as long as you don't need v3, equality deletes, or merge-on-read.
Non-JVM writes with v3 (DVs, MoR deletes) + table opsGoThe only non-Java library with DV write, MoR, compaction, and orphan cleanup today.
Embedding reads in a native engineRust (or C++)Rust: best scan pipeline + DataFusion, but append-only writes. C++: Arrow-native, fuller write path, younger overall.
Browser/Node catalog control-plane onlyiceberg-jsClean spec-pinned REST client; pair with an engine for data access.
.NET, Ruby, PHP, …No real implementation exists; use the REST catalog + an engine, or the C FFI of one of the native libraries.