This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 475bc0c8c2 Issue #5524 : Document the lake table transforms on the Hop
engine (#8859)
475bc0c8c2 is described below
commit 475bc0c8c2cb3b30c6ff185c40eef528378aa2f8
Author: Ankur Kotwal <[email protected]>
AuthorDate: Tue Oct 13 03:59:35 2026 -0700
Issue #5524 : Document the lake table transforms on the Hop engine (#8859)
* Issue #5524 : Document the lake table transforms on the Hop engine
Lake table input and output now run on the Hop engine as well, so they
lose the "Spark" in their names and their documentation explains what
they do there: finding PATH and TABLE tables, deletes, types, copies,
one commit per pipeline run, the save modes and how PATH commits behave
on object stores. The catalog type becomes "Lakehouse catalog"; its key
stays spark-catalog. Merge and maintenance still run on Spark only and
keep their names.
The iceberg integration tests get a local engine round trip, the same
scenario as the existing Spark one.
Generated-by: Claude Opus 5.5
* Issue #5524 : Address review of the lake table docs
- Navigation, the transform and metadata indexes and all cross-links use
the new names (Lake table input/output, Lakehouse catalog); the list
entries are re-sorted under L.
- Dates and timestamps: the docs now say what the code does. Values
without a time zone are read and written as wall-clock time in the Hop
JVM's zone, as Parquet Input and Output do, not as UTC; timestamptz
values are instants.
- BigNumber columns: the field's length and precision are used only when
the length is greater than 0, otherwise decimal(38, 10).
- Concurrent PATH commits: every local file system is safe (hard link,
or exclusive create without hard links); only HDFS, object stores and
other non-local VFS locations need a single writer.
- The pages' marketplace id hop-tech-lakehouse resolves now that #8854
lists the plugin in the catalog; a catalog test pins that entry, and
both transform pages say which plugins they need.
- The 0004 validation pipeline describes the 0004 extract.
Generated-by: Claude Opus 5.5
* Issue #5524 : Say which lake table transforms run on the Hop engine in
the Spark lakehouse guide
Generated-by: Claude Opus 5.5
---
docs/hop-user-manual/modules/ROOT/nav.adoc | 6 +-
.../metadata-types/databricks-connection.adoc | 2 +-
.../modules/ROOT/pages/metadata-types/index.adoc | 2 +-
.../ROOT/pages/metadata-types/spark-catalog.adoc | 21 +--
.../native-spark-pipeline-engine.adoc | 2 +-
.../ROOT/pages/pipeline/spark/databricks.adoc | 2 +-
.../spark/getting-started-with-native-spark.adoc | 8 +-
.../ROOT/pages/pipeline/spark/lakehouse.adoc | 28 ++--
.../pipeline/spark/paths-and-filesystems.adoc | 4 +-
.../modules/ROOT/pages/pipeline/transforms.adoc | 4 +-
.../pipeline/transforms/spark-file-input.adoc | 2 +-
.../pipeline/transforms/spark-file-output.adoc | 2 +-
.../transforms/spark-lake-table-input.adoc | 51 ++++--
.../transforms/spark-lake-table-maintenance.adoc | 4 +-
.../transforms/spark-lake-table-merge.adoc | 6 +-
.../transforms/spark-lake-table-output.adoc | 82 ++++++++--
integration-tests/iceberg/0004-read-local.hpl | 154 ++++++++++++++++++
integration-tests/iceberg/0004-validation.hpl | 167 +++++++++++++++++++
integration-tests/iceberg/0004-write-local.hpl | 120 ++++++++++++++
integration-tests/iceberg/README.md | 5 +-
.../iceberg/main-0004-local-input-output.hwf | 180 +++++++++++++++++++++
.../metadata/unit-test/0004-validation UNIT.json | 32 ++++
.../catalog/OptionalPluginCatalogTest.java | 1 +
.../metadata/messages/messages_en_US.properties | 4 +-
.../transforms/messages/messages_en_US.properties | 28 ++--
25 files changed, 827 insertions(+), 90 deletions(-)
diff --git a/docs/hop-user-manual/modules/ROOT/nav.adoc
b/docs/hop-user-manual/modules/ROOT/nav.adoc
index 73922ce413..90b4630e79 100644
--- a/docs/hop-user-manual/modules/ROOT/nav.adoc
+++ b/docs/hop-user-manual/modules/ROOT/nav.adoc
@@ -232,6 +232,8 @@ under the License.
*** xref:pipeline/transforms/jsonoutput.adoc[JSON Output]
*** xref:pipeline/transforms/kafkaconsumer.adoc[Kafka consumer]
*** xref:pipeline/transforms/kafkaproducer.adoc[Kafka producer]
+*** xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
+*** xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
*** xref:pipeline/transforms/languagemodelchat.adoc[Language model chat]
*** xref:pipeline/transforms/ldapinput.adoc[LDAP Input]
*** xref:pipeline/transforms/ldapoutput.adoc[LDAP Output]
@@ -310,10 +312,8 @@ under the License.
*** xref:pipeline/transforms/sortedschemamerge.adoc[Sorted schema merge]
*** xref:pipeline/transforms/spark-file-input.adoc[Spark file input]
*** xref:pipeline/transforms/spark-file-output.adoc[Spark file output]
-*** xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table
input]
*** xref:pipeline/transforms/spark-lake-table-maintenance.adoc[Spark lake
table maintenance]
*** xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table
merge]
-*** xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
*** xref:pipeline/transforms/spark-sql.adoc[Spark SQL]
*** xref:pipeline/transforms/splitfields.adoc[Split Fields]
*** xref:pipeline/transforms/splitfieldtorows.adoc[Split fields to rows]
@@ -546,6 +546,7 @@ under the License.
** xref:metadata-types/graphs/graph-database-connection.adoc[Graph Database
Connection]
** xref:metadata-types/hdfs-connection.adoc[HDFS Connection]
** xref:metadata-types/hop-server.adoc[Hop Server]
+** xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]
** xref:metadata-types/mail-server-connection.adoc[Mail Server Connection]
** xref:metadata-types/masking-pattern.adoc[Masking Pattern]
** xref:metadata-types/minio-connection.adoc[Minio (S3) Connection]
@@ -565,7 +566,6 @@ under the License.
** xref:metadata-types/smb-connection.adoc[SMB Connection]
** xref:metadata-types/splunk-connection.adoc[Splunk Connection]
** xref:metadata-types/jms-connection.adoc[JMS connection]
-** xref:metadata-types/spark-catalog.adoc[Spark Catalog]
** xref:metadata-types/variable-resolver/index.adoc[Variable Resolver]
***
xref:metadata-types/variable-resolver/aws-secrets-manager-variable-resolver.adoc[AWS
Secrets Manager variable resolver]
***
xref:metadata-types/variable-resolver/azure-key-vault-variable-resolver.adoc[Azure
Key Vault variable resolver]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/databricks-connection.adoc
b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/databricks-connection.adoc
index 52ede2a816..4bc2468445 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/databricks-connection.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/databricks-connection.adoc
@@ -76,4 +76,4 @@ See xref:vfs/databricks-vfs.adoc[Databricks Volumes /
Workspace VFS].
* xref:workflow/actions/databricks-job-run.adoc[Databricks job run] — trigger
and wait for Jobs API runs
* xref:database/databases/databricks.adoc[Databricks database (JDBC)] — SQL
warehouse access
* xref:pipeline/spark/getting-started-with-native-spark.adoc[Native Spark
pipeline engine] — fat jar / MainSpark on cluster
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog] — Unity Catalog
templates for lakehouse TABLE mode
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] — Unity Catalog
templates for lakehouse TABLE mode
diff --git a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/index.adoc
b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/index.adoc
index 13995493e3..92ba47d136 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/index.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/index.adoc
@@ -71,7 +71,7 @@ xref:metadata-types/data-stream/data-stream.adoc[Data
Stream]: A Data Stream can
* xref:metadata-types/rest-connection.adoc[REST Connection]: Describes all the
metadata needed to connect to a REST api.
* xref:metadata-types/salesforce-connection.adoc[Salesforce Connection]:
Describes a reusable Salesforce connection that can be used across multiple
Salesforce transforms
* xref:metadata-types/splunk-connection.adoc[Splunk Connection]: Describes a
Splunk connection
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog]: Spark SQL catalog
configuration for Delta Lake / Apache Iceberg TABLE mode on the native Spark
engine
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]: catalog of Apache
Iceberg and Delta Lake tables for the lake table transforms in TABLE mode, on
the Hop engine and the native Spark engine
* xref:metadata-types/static-schema-definition.adoc[Static Schema Definition]:
Defines a reusable data stream layout to ensure consistency across multiple
pipelines and simplify schema management.
* xref:metadata-types/variable-resolver/index.adoc[Variable Resolver]: Use
plugins to resolve variable values with a pipeline, a key store, a vaults, or
secret managers.
* xref:hop-server/web-service.adoc[Web Service]: Allows to run a pipeline to
generate output for a servlet on Hop Server
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/spark-catalog.adoc
b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/spark-catalog.adoc
index 96ecd26dc2..86148b5511 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/spark-catalog.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/spark-catalog.adoc
@@ -16,18 +16,21 @@ under the License.
////
:imagesdir: ../../assets/images/
:page-pagination:
-:description: Spark Catalog metadata configures Spark SQL catalogs for Delta
Lake and Apache Iceberg TABLE mode on the native Spark pipeline engine.
-:page-marketplace: hop-engines-spark
+:description: Lakehouse catalog metadata describes the catalog of Apache
Iceberg and Delta Lake tables for the lake table transforms in TABLE mode, on
the Hop engine and on the native Spark pipeline engine.
+:page-marketplace: hop-tech-lakehouse
-= Spark Catalog
+= Lakehouse catalog
== Description
-*Spark Catalog* is a reusable Hop metadata type (category **Connections**)
that describes a Spark SQL catalog for open table formats on the
xref:pipeline/spark/getting-started-with-native-spark.adoc[native Spark
pipeline engine].
+*Lakehouse catalog* (called *Spark Catalog* in earlier versions) is a reusable
Hop metadata type (category **Connections**) that describes a catalog for open
table formats. Lake table transforms in **TABLE** mode reference a catalog by
name.
-Lake Table transforms in **TABLE** mode can reference a Spark Catalog by name.
Hop expands the entry into `spark.sql.catalog.<catalogName>.*` configuration
when building or preparing the session (`LakeSessionPlan` /
`SparkCatalogApplier`).
+* On the xref:pipeline/spark/getting-started-with-native-spark.adoc[native
Spark pipeline engine] Hop expands the entry into
`spark.sql.catalog.<catalogName>.*` configuration when building or preparing
the session (`LakeSessionPlan` / `SparkCatalogApplier`).
+* On the Hop engine, xref:pipeline/transforms/spark-lake-table-input.adoc[Lake
table input] and xref:pipeline/transforms/spark-lake-table-output.adoc[Lake
table output] open the catalog with the Iceberg Java library. The `hadoop` and
`rest` catalog types are supported there. The REST catalog gets the *URI*,
*Warehouse* and *Credential* (as `token`), plus the *Extra conf* lines as
catalog properties; Spark-style keys (`spark.sql.catalog.<name>.<key>`) are
accepted, so one catalog definiti [...]
-PATH-mode Iceberg tables do **not** require this metadata type — the engine
registers a Hadoop catalog for the folder that holds each table
(`hop_iceberg_<hash>`). Use Spark Catalog for production Iceberg warehouses,
REST catalogs, and other named catalogs.
+Existing catalogs keep working: the metadata key (`spark-catalog`) hasn't
changed.
+
+PATH-mode Iceberg tables do **not** require this metadata type — the engine
registers a Hadoop catalog for the folder that holds each table
(`hop_iceberg_<hash>`). Use a lakehouse catalog for production Iceberg
warehouses, REST catalogs, and other named catalogs.
Connector packaging and session matrix:
xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark engine].
@@ -141,11 +144,11 @@ When both Delta and Iceberg are used in one session, keep
**Delta** on `spark_ca
== spark-submit
-Export project metadata JSON that includes every Spark Catalog entry
referenced by the pipeline (and any used only via run configuration).
Alternatively, pass equivalent `--conf spark.sql.catalog.…` flags on the submit
command. Paths and warehouses must be reachable from the driver and executors.
+Export project metadata JSON that includes every lakehouse catalog entry
referenced by the pipeline (and any used only via run configuration).
Alternatively, pass equivalent `--conf spark.sql.catalog.…` flags on the submit
command. Paths and warehouses must be reachable from the driver and executors.
== Related pages
* xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark
engine]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
* xref:pipeline/spark/getting-started-with-native-spark.adoc[Getting started
with the native Spark pipeline engine]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc
index 8bb210e5b0..aaba21c560 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc
@@ -57,7 +57,7 @@ Use **Load configuration template** to fill the fields below
from a common deplo
Templates replace the current engine options after confirmation when the form
already has custom values. The form fields update immediately after you apply a
template. Edit values afterward as needed.
-For named Iceberg/Delta catalogs used by Lake Table transforms in **TABLE**
mode, see xref:metadata-types/spark-catalog.adoc[Spark Catalog] (**Load catalog
template**).
+For named Iceberg/Delta catalogs used by Lake Table transforms in **TABLE**
mode, see xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] (**Load
catalog template**).
[options="header",cols="1,3,1"]
|===
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/databricks.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/databricks.adoc
index 7b47216021..3b2f02ac7b 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/databricks.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/databricks.adoc
@@ -692,4 +692,4 @@ Databricks does **not** expose a standard content MD5/SHA
for volume files compa
*
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark run configuration]
* xref:pipeline/spark/paths-and-filesystems.adoc[Paths and file systems]
* xref:database/databases/databricks.adoc[Databricks JDBC] (SQL warehouse —
separate use case)
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog] (Unity Catalog
templates for lakehouse TABLE mode)
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] (Unity Catalog
templates for lakehouse TABLE mode)
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/getting-started-with-native-spark.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/getting-started-with-native-spark.adoc
index 3d2a627d2e..fd9fc36309 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/getting-started-with-native-spark.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/getting-started-with-native-spark.adoc
@@ -157,7 +157,7 @@ This is the fastest way to learn: Spark runs *inside* the
Hop process using all
TIP: On the Native Spark engine options tab, **Load configuration template**
can pre-fill common setups (*Local execution*, *Spark standalone server*,
*spark-submit*, *Databricks*, *YARN*, …). See
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark pipeline engine].
-TIP: For Iceberg **TABLE** mode, create a
xref:metadata-types/spark-catalog.adoc[Spark Catalog] entry and use **Load
catalog template** (Hadoop local/object store, REST, Hive, Glue, Nessie, …).
Each preset starts **Extra conf** with a commented `# docs:` link to vendor
documentation.
+TIP: For Iceberg **TABLE** mode, create a
xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] entry and use **Load
catalog template** (Hadoop local/object store, REST, Hive, Glue, Nessie, …).
Each preset starts **Extra conf** with a commented `# docs:` link to vendor
documentation.
. Open *Metadata* → *Pipeline run configuration* → *New*.
. Give it a clear name, for example `spark-local`.
@@ -445,9 +445,9 @@ Writes run as Spark *actions* when the engine materialises
the graph. Prefer *Sp
Classic *Spark File* transforms cover `csv` / `parquet` / `json` / `orc` /
`text` only. For ACID lake tables use the dedicated **Lake Table** transforms
(Big Data category):
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
/ xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table output]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input] /
xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
/ xref:pipeline/transforms/spark-lake-table-maintenance.adoc[Spark lake table
maintenance]
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog] metadata for Iceberg
TABLE mode
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] metadata for
Iceberg TABLE mode
Connector JARs **ship in the default Hop assembly** under
`plugins/engines/spark/lib/{delta,iceberg}` (and ride a *native-provided* fat
jar for cluster submit). Full session conf, PATH vs TABLE, and samples:
xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark engine].
@@ -711,6 +711,6 @@ image::pipeline/spark/spark-native-0099-complex.png[0099
complex sample,width=95
*
xref:pipeline/pipeline-run-configurations/pipeline-run-configurations.adoc[Pipeline
run configurations]
* xref:metadata-types/execution-information-location.adoc[Execution
information location]
* xref:metadata-types/execution-data-profile.adoc[Execution data profile]
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog]
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]
* xref:hop-gui/perspective-execution-information.adoc[Execution Information
perspective]
* xref:hop-tools/hop-conf/hop-conf.adoc[hop-conf] (fat jar, metadata export)
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/lakehouse.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/lakehouse.adoc
index e44db08e66..7f798e1254 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/lakehouse.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/lakehouse.adoc
@@ -33,11 +33,11 @@ It assumes you already have a working *Native Spark*
pipeline run configuration
|===
| Capability | How
-| Read / write ACID tables |
xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input] /
xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table output]
+| Read / write ACID tables |
xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input] /
xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
| Path-based tables | Identifier mode **PATH** (local, HDFS, object-store URIs
Spark can open)
-| Catalog tables | Identifier mode **TABLE** +
xref:metadata-types/spark-catalog.adoc[Spark Catalog] metadata (Iceberg-first).
Use **Load catalog template** on the metadata editor for
Hadoop/REST/Hive/Glue/Nessie skeletons.
+| Catalog tables | Identifier mode **TABLE** +
xref:metadata-types/spark-catalog.adoc[Lakehouse catalog] metadata
(Iceberg-first). Use **Load catalog template** on the metadata editor for
Hadoop/REST/Hive/Glue/Nessie skeletons.
| Time travel | Version / snapshot id or timestamp on Input
@@ -126,7 +126,7 @@ export SPARK_HOME=/path/to/spark-4.1.x-bin-hadoop3
./hop-conf.sh --generate-fat-jar=/tmp/hop-native-provided.jar \
--spark-client-version=native-provided
-# Export project metadata (run config + any Spark Catalog entries) to JSON,
then:
+# Export project metadata (run config + any lakehouse catalog entries) to
JSON, then:
"${SPARK_HOME}/bin/spark-submit" \
--master spark://your-master:7077 \
--conf
spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension,org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
\
@@ -163,7 +163,7 @@ Hop’s `LakeSessionPlan` scans Lake Table transforms before
the session is used
|Iceberg extensions + a Hadoop catalog per table folder,
**`hop_iceberg_<hash>`** (`type=hadoop`, warehouse = the folder that holds the
table)
|Iceberg TABLE
-|`spark.sql.catalog.<name>=org.apache.iceberg.spark.SparkCatalog` (+ `type`,
`warehouse` / `uri`, …) from xref:metadata-types/spark-catalog.adoc[Spark
Catalog] metadata
+|`spark.sql.catalog.<name>=org.apache.iceberg.spark.SparkCatalog` (+ `type`,
`warehouse` / `uri`, …) from xref:metadata-types/spark-catalog.adoc[Lakehouse
catalog] metadata
|===
IMPORTANT: Delta PATH I/O on Spark 4.1 + Delta 4.3 **requires DeltaCatalog**,
not only the extension. Iceberg bare `format("iceberg").load(path)` is **not**
used — it defaults toward HiveCatalog and fails without HMS. PATH mode uses
`hop_iceberg_<hash>.\`<table>\``, where the catalog's warehouse is the table's
parent folder, so the table is read and written at its path.
@@ -184,7 +184,7 @@ NOTE: Before Hop 2.20, Iceberg PATH tables written by the
Spark engine were stor
|Local lakes, object-store folders, quick prototypes
|**TABLE**
-|Multi-part name (`catalog.namespace.table`) + optional Spark Catalog metadata
name
+|Multi-part name (`catalog.namespace.table`) + optional lakehouse catalog
metadata name
|Production catalogs (Iceberg Hadoop warehouse, REST, …)
|===
@@ -226,10 +226,10 @@ NOTE: Before Hop 2.20, Iceberg PATH tables written by the
Spark engine were stor
== Lake Table transforms
-All four live under the **Big Data** category and are **native Spark only**
(not Beam Spark, not the local Hop engine).
+All four live under the **Big Data** category. Lake table merge and Lake table
maintenance run on the native Spark engine only; Lake table input and Lake
table output also run on the Hop engine (Apache Iceberg only). None of them run
on Beam engines.
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
— read, optional projection, time travel
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output] — append / overwrite / …; **default save mode is ErrorIfExists**
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input] —
read, optional projection, time travel
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output] —
append / overwrite / …; **default save mode is ErrorIfExists**
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
— single upstream hop is the source; target is PATH or TABLE
* xref:pipeline/transforms/spark-lake-table-maintenance.adoc[Spark lake table
maintenance] — zero-input action (OPTIMIZE, VACUUM, expire, …)
@@ -252,12 +252,12 @@ Injection keys are documented on each transform page.
Shared identity keys: `FOR
. Install Delta jars (see above) and restart Hop.
. Create a *Native Spark* run configuration (`local[*]`).
-. Pipeline: *Row Generator* (or *Spark file input*) → *Spark lake table output*
+. Pipeline: *Row Generator* (or *Spark file input*) → *Lake table output*
** Format: `delta`
** Identifier mode: `PATH`
** Table path: e.g. `+${PROJECT_HOME}/output/delta-orders+`
** Save mode: `Overwrite` for a first write (default is *ErrorIfExists*)
-. Second pipeline or hop: *Spark lake table input* on the same path → *Spark
file output* or preview.
+. Second pipeline or hop: *Lake table input* on the same path → *Spark file
output* or preview.
. Run with the native Spark run configuration.
=== Time travel sketch
@@ -295,7 +295,7 @@ Standalone *Spark lake table maintenance*:
|Do not use bare `format("iceberg")` outside Hop; ensure Iceberg transforms
run under hop-run so the per-folder `hop_iceberg_<hash>` catalogs are registered
|TABLE unresolved
-|Spark Catalog metadata exported? Catalog name matches the first segment of
`catalog.ns.table`? Warehouse / URI reachable from driver and executors?
+|Lakehouse catalog metadata exported? Catalog name matches the first segment
of `catalog.ns.table`? Warehouse / URI reachable from driver and executors?
|Destructive maintenance rejected
|Set retention hours / WHERE and enable acknowledge flag
@@ -308,9 +308,9 @@ Standalone *Spark lake table maintenance*:
* xref:pipeline/spark/getting-started-with-native-spark.adoc[Getting started
with the native Spark pipeline engine]
*
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark pipeline engine]
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
* xref:pipeline/transforms/spark-lake-table-maintenance.adoc[Spark lake table
maintenance]
* Developer notes and spike findings: `plugins/engines/spark/README.md` in the
Hop source tree
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/paths-and-filesystems.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/paths-and-filesystems.adoc
index d543c09d72..e25db56193 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/paths-and-filesystems.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/spark/paths-and-filesystems.adoc
@@ -65,7 +65,7 @@ For the full list of Hop VFS schemes, see
xref:vfs.adoc[Virtual File Systems].
|**Spark URI**
|e.g. `file:///…`, `s3a://bucket/table` — see
xref:pipeline/spark/lakehouse.adoc[Lakehouse]
-|Spark Catalog warehouse / Iceberg `io-impl`
+|Lakehouse catalog warehouse / Iceberg `io-impl`
|**Spark URI**
|Templates use `s3a://` and `file:///`
@@ -310,4 +310,4 @@ See
xref:pipeline/spark/getting-started-with-native-spark.adoc[Getting started w
*
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark pipeline engine]
* xref:vfs.adoc[Virtual File Systems]
* xref:metadata-types/minio-connection.adoc[Minio Connection]
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog]
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]
diff --git a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms.adoc
index 4844594aad..f6c5433db3 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms.adoc
@@ -157,6 +157,8 @@ Those are explained once in
xref:pipeline/formatting-values.adoc[Formatting numb
* xref:pipeline/transforms/jsonoutput.adoc[JSON Output]
* xref:pipeline/transforms/kafkaconsumer.adoc[Kafka consumer]
* xref:pipeline/transforms/kafkaproducer.adoc[Kafka producer]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
* xref:pipeline/transforms/languagemodelchat.adoc[Language model chat]
* xref:pipeline/transforms/ldapinput.adoc[LDAP Input]
* xref:pipeline/transforms/ldapoutput.adoc[LDAP Output]
@@ -229,10 +231,8 @@ Those are explained once in
xref:pipeline/formatting-values.adoc[Formatting numb
* xref:pipeline/transforms/sort.adoc[Sort Rows]
* xref:pipeline/transforms/sortedmerge.adoc[Sorted Merge]
* xref:pipeline/transforms/sortedschemamerge.adoc[Sorted schema merge]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
* xref:pipeline/transforms/spark-lake-table-maintenance.adoc[Spark lake table
maintenance]
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
* xref:pipeline/transforms/spark-sql.adoc[Spark SQL]
* xref:pipeline/transforms/splitfields.adoc[Split Fields]
* xref:pipeline/transforms/splitfieldtorows.adoc[Split fields to rows]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-input.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-input.adoc
index 75e53e46c7..dd8e201f84 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-input.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-input.adoc
@@ -94,5 +94,5 @@ Field group `FIELDS`: `NAME`, `TYPE`, `LENGTH`, `PRECISION`,
`FORMAT_MASK`.
* xref:pipeline/transforms/spark-file-output.adoc[Spark file output]
* xref:pipeline/spark/paths-and-filesystems.adoc[Paths and file systems on the
native Spark engine]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
for Delta Lake and Apache Iceberg tables
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input] for
Delta Lake and Apache Iceberg tables
*
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark pipeline engine]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-output.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-output.adoc
index bde439f3d7..fa9b4f711e 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-output.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-file-output.adoc
@@ -88,5 +88,5 @@ All options support
xref:pipeline/metadata-injection.adoc[metadata injection].
* xref:pipeline/transforms/spark-file-input.adoc[Spark file input]
* xref:pipeline/spark/paths-and-filesystems.adoc[Paths and file systems on the
native Spark engine]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output] for Delta Lake and Apache Iceberg tables
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output] for
Delta Lake and Apache Iceberg tables
*
xref:pipeline/pipeline-run-configurations/native-spark-pipeline-engine.adoc[Native
Spark pipeline engine]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-input.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-input.adoc
index cffc18c66d..836a1bfbed 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-input.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-input.adoc
@@ -16,19 +16,26 @@ under the License.
////
:documentationPath: /pipeline/transforms/
:language: en_US
-:description: Spark lake table input reads Delta Lake or Apache Iceberg tables
on the native Spark pipeline engine, with optional time travel and projection.
-:page-engines: Hop Engine=no, Single Threaded=no, Native Spark=native, Beam
Spark=no, Beam Flink=no, Beam Dataflow=no
-:page-marketplace: hop-engines-spark
+:description: Lake table input reads Apache Iceberg tables on the Hop engine
and Delta Lake or Apache Iceberg tables on the native Spark engine, with
optional time travel and projection.
+:page-engines: Hop Engine=yes, Single Threaded=no, Native Spark=native, Beam
Spark=no, Beam Flink=no, Beam Dataflow=no
+:page-marketplace: hop-tech-lakehouse
-= image:transforms/icons/spark-lake-table-input.svg[Spark lake table input
transform Icon, role="image-doc-icon"] Spark lake table input
+= image:transforms/icons/spark-lake-table-input.svg[Lake table input transform
Icon, role="image-doc-icon"] Lake table input
== Description
-*Spark lake table input* reads an open table format table
(https://delta.io/[Delta Lake] or https://iceberg.apache.org/[Apache Iceberg])
as a Spark SQL `Dataset` on the
xref:pipeline/spark/getting-started-with-native-spark.adoc[native Spark
pipeline engine].
+*Lake table input* reads an open table format table:
https://iceberg.apache.org/[Apache Iceberg] or https://delta.io/[Delta Lake].
It supports **PATH** and **TABLE** identifiers, optional field projection, and
**time travel** by version/snapshot or timestamp.
-This transform is **not** available on the local Hop engine or on Beam
engines. Connector JARs ship with the native Spark plugin; see
xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark engine].
+The transform runs on two engines:
+
+* On the *Hop engine* (local and remote) it reads Apache Iceberg tables with
the Iceberg Java library. All files are read through Hop VFS, so a table can
live anywhere Hop can reach: local disk, S3, MinIO, Azure, Google Cloud
Storage. See <<hop-engine>>.
+* On the xref:pipeline/spark/getting-started-with-native-spark.adoc[native
Spark pipeline engine] it reads Delta Lake and Iceberg tables as a Spark SQL
`Dataset`. Connector JARs ship with the native Spark plugin; see
xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark engine].
+
+It isn't available on Beam engines.
+
+The transform is part of the Lakehouse plugin (`hop-tech-lakehouse`). On the
native Spark engine the Native Spark engine plugin (`hop-engines-spark`) is
needed as well. Both can be installed with the marketplace when they aren't in
your Hop distribution.
== Options
@@ -52,8 +59,8 @@ This transform is **not** available on the local Hop engine
or on Beam engines.
|Table identifier
|Used when mode is TABLE. Multi-part name: `catalog.namespace.table`.
-|Spark Catalog metadata name
-|Optional Hop xref:metadata-types/spark-catalog.adoc[Spark Catalog] entry to
register for TABLE mode (Iceberg Hadoop/REST, …). Leave empty if the catalog is
already configured on the session.
+|Catalog metadata name
+|The xref:metadata-types/spark-catalog.adoc[lakehouse catalog] to use in TABLE
mode (Iceberg Hadoop/REST, …). On the Spark engine you can leave it empty if
the catalog is already configured on the session.
|Time travel type
|`NONE` (current), `VERSION`, or `TIMESTAMP`.
@@ -62,10 +69,10 @@ This transform is **not** available on the local Hop engine
or on Beam engines.
|Delta table version number or Iceberg snapshot id (when type is VERSION).
Supports variables.
|Time travel timestamp
-|Spark-parseable timestamp string when type is TIMESTAMP, e.g. `2024-01-15
10:30:00`.
+|Timestamp when type is TIMESTAMP, e.g. `2024-01-15 10:30:00`. The Hop engine
also accepts an ISO-8601 timestamp with an offset and epoch milliseconds.
|Extra options
-|Optional Spark read options as `key=value` lines (advanced).
+|Optional Spark read options as `key=value` lines (advanced). Not used on the
Hop engine.
|Fields
|Optional projection. When empty, all columns are returned. When set, columns
are selected/cast by name (same idea as Spark file input with a schema).
@@ -92,6 +99,24 @@ This transform is **not** available on the local Hop engine
or on Beam engines.
Tip: After two Overwrite writes, Delta version `0` is typically the first
commit. For Iceberg, inspect `+{table}.snapshots+` (or catalog metadata) for
snapshot ids.
+[[hop-engine]]
+== On the Hop engine
+
+On the local and remote Hop engines the transform reads Apache Iceberg tables.
Delta Lake tables give an error that asks you to use the Spark engine for now.
+
+*Finding the table*
+
+* *PATH* mode reads the table in the given folder. The folder can be any Hop
VFS location, for example `file:///data/orders` or `s3://lake/orders`. When the
table has a `metadata/version-hint.text` file (tables written by a Hadoop
catalog, by the Spark engine in PATH mode, or by Lake table output on the Hop
engine) the version it names is read. Otherwise the newest metadata file is
read.
+* *TABLE* mode looks the table up through the selected
xref:metadata-types/spark-catalog.adoc[lakehouse catalog]. The Hop engine
supports the `hadoop` and `rest` catalog types. A catalog name in front of the
identifier (`lake.db.orders`) is dropped, so the same identifier works on both
engines. With a REST catalog the files are read through Hop VFS, unless the
catalog's extra configuration names another `io-impl`, for example
`org.apache.iceberg.aws.s3.S3FileIO` for credentials vended by [...]
+
+*Reading*
+
+* Iceberg plans the read: it skips the files and row groups that can't match,
using the statistics in the table metadata.
+* Delete files written by other engines (position and equality deletes, for
example from Flink or Spark) are applied.
+* Without a field list all columns are returned, with these Hop types:
booleans as Boolean, integers as Integer, floating point numbers as Number,
decimals as BigNumber, dates as Date, timestamps as Timestamp, binary values as
Binary and everything else as String. Dates and timestamps without a time zone
(`date`, `timestamp`, `timestamp_ns`) are read as local dates and date-times in
the time zone of the Hop JVM, as Parquet Input does, so the wall-clock value in
the table is kept. Timesta [...]
+* When the transform runs as several copies, the copies share the table
between them. Each part of a data file is read by exactly one copy.
+* A time travel timestamp without a time zone is in the time zone of the Hop
server.
+
== Metadata injection
All options support xref:pipeline/metadata-injection.adoc[metadata injection]
(ETL Metadata Injection).
@@ -104,11 +129,11 @@ Typical pattern: template pipeline with Lake Table Input;
injecting pipeline sup
== Notes
-* Prefer Lake Table Input over *Spark file input* for `delta` / `iceberg` so
session extensions, Iceberg PATH catalogs, and probes stay consistent.
+* On the Spark engine, prefer Lake table input over *Spark file input* for
`delta` / `iceberg` so session extensions, Iceberg PATH catalogs, and probes
stay consistent.
* See the xref:pipeline/spark/lakehouse.adoc[lakehouse guide] for packaging,
PATH vs TABLE, and troubleshooting.
== Related pages
* xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark
engine]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
-* xref:metadata-types/spark-catalog.adoc[Spark Catalog]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
+* xref:metadata-types/spark-catalog.adoc[Lakehouse catalog]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-maintenance.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-maintenance.adoc
index 97029fc01f..f786fb5246 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-maintenance.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-maintenance.adoc
@@ -45,7 +45,7 @@ Connectors ship with the native Spark plugin. Session rules:
xref:pipeline/spark
|Identifier mode
|`PATH` or `TABLE` for the target table.
-|Table path / Table identifier / Spark Catalog
+|Table path / Table identifier / Lakehouse catalog
|Same semantics as other Lake Table transforms.
|Operation
@@ -120,5 +120,5 @@ Injecting pipelines can drive fleet-wide OPTIMIZE/VACUUM
across many tables; sti
== Related pages
* xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark
engine]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-merge.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-merge.adoc
index a654871ab5..b7e0c31d5f 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-merge.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-merge.adoc
@@ -45,7 +45,7 @@ Connectors ship with the native Spark plugin. Session rules:
xref:pipeline/spark
|Identifier mode
|`PATH` or `TABLE` for the **target** table.
-|Table path / Table identifier / Spark Catalog
+|Table path / Table identifier / Lakehouse catalog
|Same semantics as Lake Table Input/Output for the merge target.
|Merge condition
@@ -99,5 +99,5 @@ Useful for multi-table CDC/upsert templates where merge keys
and target tables v
== Related pages
* xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark
engine]
-* xref:pipeline/transforms/spark-lake-table-output.adoc[Spark lake table
output]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
+* xref:pipeline/transforms/spark-lake-table-output.adoc[Lake table output]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-output.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-output.adoc
index 683fdd6789..e40587fc06 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-output.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/spark-lake-table-output.adoc
@@ -16,19 +16,24 @@ under the License.
////
:documentationPath: /pipeline/transforms/
:language: en_US
-:description: Spark lake table output writes Delta Lake or Apache Iceberg
tables on the native Spark pipeline engine with ACID commit semantics.
-:page-engines: Hop Engine=no, Single Threaded=no, Native Spark=native, Beam
Spark=no, Beam Flink=no, Beam Dataflow=no
-:page-marketplace: hop-engines-spark
+:description: Lake table output writes Apache Iceberg tables on the Hop engine
and Delta Lake or Apache Iceberg tables on the native Spark engine, with ACID
commit semantics.
+:page-engines: Hop Engine=yes, Single Threaded=no, Native Spark=native, Beam
Spark=no, Beam Flink=no, Beam Dataflow=no
+:page-marketplace: hop-tech-lakehouse
-= image:transforms/icons/spark-lake-table-output.svg[Spark lake table output
transform Icon, role="image-doc-icon"] Spark lake table output
+= image:transforms/icons/spark-lake-table-output.svg[Lake table output
transform Icon, role="image-doc-icon"] Lake table output
== Description
-*Spark lake table output* writes the incoming Spark `Dataset` to a
https://delta.io/[Delta Lake] or https://iceberg.apache.org/[Apache Iceberg]
table using table-format commit semantics on the
xref:pipeline/spark/getting-started-with-native-spark.adoc[native Spark
pipeline engine].
+*Lake table output* writes the incoming rows to an
https://iceberg.apache.org/[Apache Iceberg] or https://delta.io/[Delta Lake]
table, using the table format's commits so readers never see a half-written run.
-Unlike *Spark file output* (classic file formats), the **default save mode is
ErrorIfExists** so ACID tables are not overwritten by accident.
+The **default save mode is ErrorIfExists**, so an existing table isn't
overwritten by accident.
-This transform is **native Spark only**. Connectors ship with the native Spark
plugin; see xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native
Spark engine].
+The transform runs on two engines:
+
+* On the *Hop engine* (local and remote) it writes Apache Iceberg tables with
the Iceberg Java library, through Hop VFS, and passes the rows on unchanged.
See <<hop-engine>>.
+* On the xref:pipeline/spark/getting-started-with-native-spark.adoc[native
Spark pipeline engine] it writes the incoming `Dataset` to a Delta Lake or
Iceberg table. Connectors ship with the native Spark plugin; see
xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark engine].
+
+The transform is part of the Lakehouse plugin (`hop-tech-lakehouse`). On the
native Spark engine the Native Spark engine plugin (`hop-engines-spark`) is
needed as well. Both can be installed with the marketplace when they aren't in
your Hop distribution.
== Options
@@ -51,8 +56,8 @@ This transform is **native Spark only**. Connectors ship with
the native Spark p
|Table identifier
|TABLE mode: e.g. `lake.db.orders`.
-|Spark Catalog metadata name
-|Optional xref:metadata-types/spark-catalog.adoc[Spark Catalog] Hop metadata
for TABLE mode.
+|Catalog metadata name
+|The xref:metadata-types/spark-catalog.adoc[lakehouse catalog] for TABLE mode.
Optional on the Spark engine.
|Save mode
|`ErrorIfExists` (**default**), `Append`, `Overwrite`, or `Ignore`. Choose
Overwrite or Append explicitly when you intend to replace or extend data.
@@ -61,13 +66,62 @@ This transform is **native Spark only**. Connectors ship
with the native Spark p
|Optional comma-separated partition column list.
|Coalesce
-|Optional partition count before write (e.g. `1` for a single file layout —
costs a shuffle).
+|Optional partition count before write (e.g. `1` for a single file layout —
costs a shuffle). Spark engine only.
|Extra options
-|Optional Spark write options as `key=value` lines (advanced).
+|Optional Spark write options as `key=value` lines (advanced). Spark engine
only.
|===
-== Behaviour
+[[hop-engine]]
+== On the Hop engine
+
+On the local and remote Hop engines the transform writes Apache Iceberg
tables. Delta Lake tables give an error that asks you to use the Spark engine
for now.
+
+*One commit per pipeline run*
+
+Every copy of the transform writes its own Parquet files. Nothing is visible
in the table until the whole pipeline has finished:
+
+* When the pipeline ends without errors, all files of the run are committed as
one Iceberg snapshot. The snapshot summary records the pipeline name
(`hop.pipeline`) and the run's log channel id (`hop.run-id`).
+* When the pipeline fails or is stopped, the files are deleted and the table
is left as it was. This includes a failure in a transform *after* Lake table
output: the run is rolled back, like a database transaction.
+
+*Save modes*
+
+[options="header",cols="1,2,2"]
+|===
+|Save mode |Table exists |Table doesn't exist
+
+|ErrorIfExists
+|The pipeline fails, the table is left alone
+|The table is created
+
+|Append
+|The rows are added
+|The table is created
+
+|Overwrite
+|All rows of the table are replaced by the rows of this run. The run fails
instead if another writer changed the table since the run started.
+|The table is created
+
+|Ignore
+|Nothing is written; the rows are still passed on
+|The table is created
+|===
+
+A new table gets its columns from the incoming rows (Integer as `long`, Number
as `double`, BigNumber as `decimal`, String as `string`, Boolean as `boolean`,
Date and Timestamp as `timestamp` without time zone, Binary as `binary`) and
identity partitions on the *Partition by* columns. It is created in the same
commit as its first rows, so a failed run doesn't leave an empty table behind.
When the table exists, the rows are matched to its columns by name.
+
+* A BigNumber column gets the field's length as precision (at most 38) and its
precision as scale when the length is greater than 0; otherwise it is
`decimal(38, 10)`.
+* Dates and timestamps are written as the wall-clock time in the time zone of
the Hop JVM, so they read back unchanged on the Hop engine, as with Parquet
Output. Into an existing `timestamptz` column they are written as instants.
+
+Unlike the Spark engine, Overwrite on the Hop engine keeps the table's schema
and partitioning and only replaces its rows.
+
+*Where the table is*
+
+* *PATH* mode writes the table in the given Hop VFS folder, in the layout of
Hadoop catalogs and of the Spark engine's PATH mode
(`metadata/v<N>.metadata.json` plus `version-hint.text`). The Spark and Hop
engines can read and write the same table. A folder that holds a table managed
by a catalog is refused: write that table through its catalog in TABLE mode.
+* *TABLE* mode writes through the selected lakehouse catalog. The Hop engine
supports the `hadoop` and `rest` catalog types.
+
+On a local file system (`file://`), a PATH table commit publishes the new
metadata file with an operation that fails if another writer already committed
that version: a hard link, or an exclusive create where hard links aren't
supported. Several pipelines can write the same table there; of two conflicting
commits one fails and is retried. On HDFS, object stores like S3 and other
non-local Hop VFS locations, the commit checks that the version is free and
then writes it, which isn't atomic [...]
+
+== On the Spark engine
* The write runs as a Spark **action** when the engine materialises the graph.
* An empty leaf Dataset is registered so a later engine-wide `count()` does
not re-write the table.
@@ -87,11 +141,11 @@ Use this with a template write path when landing many
tables (different paths/id
== Notes
* Requires exactly one main-stream upstream hop (the data to write).
-* For upserts, use xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark
lake table merge] instead of Overwrite.
+* For upserts on the Spark engine, use
xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
instead of Overwrite.
* Packaging, session conf, and troubleshooting:
xref:pipeline/spark/lakehouse.adoc[lakehouse guide].
== Related pages
* xref:pipeline/spark/lakehouse.adoc[Lakehouse tables on the native Spark
engine]
-* xref:pipeline/transforms/spark-lake-table-input.adoc[Spark lake table input]
+* xref:pipeline/transforms/spark-lake-table-input.adoc[Lake table input]
* xref:pipeline/transforms/spark-lake-table-merge.adoc[Spark lake table merge]
diff --git a/integration-tests/iceberg/0004-read-local.hpl
b/integration-tests/iceberg/0004-read-local.hpl
new file mode 100644
index 0000000000..b0f7fa1295
--- /dev/null
+++ b/integration-tests/iceberg/0004-read-local.hpl
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<pipeline>
+ <info>
+ <name>0004-read-local</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>iceberg PATH read + CSV extract</description>
+ <pipeline_type>Normal</pipeline_type>
+ <parameters/>
+ <capture_transform_performance>N</capture_transform_performance>
+ <created_user>-</created_user>
+ <created_date>2026/07/16 00:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/07/16 00:00:00.000</modified_date>
+ </info>
+ <notepads/>
+ <order>
+ <hop>
+ <from>Lake Input</from>
+ <to>CSV Extract</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+
+ <transform>
+ <type>SparkLakeTableInput</type>
+ <name>Lake Input</name>
+ <description/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <format>iceberg</format>
+ <identifier_mode>PATH</identifier_mode>
+ <table_path>file:///tmp/hop-it-iceberg/0004-table</table_path>
+ <table_identifier/>
+ <catalog_metadata_name/>
+ <time_travel_type>NONE</time_travel_type>
+ <time_travel_version></time_travel_version>
+ <time_travel_timestamp></time_travel_timestamp>
+ <extra_options/>
+ <fields>
+ <field>
+ <name>id</name>
+ <type>Integer</type>
+ <length>-1</length>
+ <precision>0</precision>
+ </field>
+ <field>
+ <name>name</name>
+ <type>String</type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ </fields>
+ <attributes/>
+ <GUI>
+ <xloc>160</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <transform>
+ <name>CSV Extract</name>
+ <type>TextFileOutput</type>
+ <description/>
+ <distribute>Y</distribute>
+ <custom_distribution/>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <separator>,</separator>
+ <enclosure>"</enclosure>
+ <enclosure_forced>N</enclosure_forced>
+ <enclosure_fix_disabled>N</enclosure_fix_disabled>
+ <header>Y</header>
+ <footer>N</footer>
+ <format>UNIX</format>
+ <compression>None</compression>
+ <encoding>UTF-8</encoding>
+ <endedLine/>
+ <fileNameInField>N</fileNameInField>
+ <fileNameField/>
+ <create_parent_folder>Y</create_parent_folder>
+ <file>
+ <name>/tmp/hop-it-iceberg/0004-extract/part</name>
+ <servlet_output>N</servlet_output>
+ <do_not_open_new_file_init>N</do_not_open_new_file_init>
+ <extention>csv</extention>
+ <append>N</append>
+ <split>N</split>
+ <haspartno>N</haspartno>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <SpecifyFormat>N</SpecifyFormat>
+ <date_time_format/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <pad>N</pad>
+ <fast_dump>N</fast_dump>
+ <splitevery>0</splitevery>
+ </file>
+ <fields>
+ <field>
+ <name>id</name>
+ <type>Integer</type>
+ <format>#</format>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>name</name>
+ <type>String</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ </fields>
+ <attributes/>
+ <GUI>
+ <xloc>480</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/iceberg/0004-validation.hpl
b/integration-tests/iceberg/0004-validation.hpl
new file mode 100644
index 0000000000..e32f9b2239
--- /dev/null
+++ b/integration-tests/iceberg/0004-validation.hpl
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<pipeline>
+ <info>
+ <name>0004-validation</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>Validate 0004 extract against golden</description>
+ <pipeline_type>Normal</pipeline_type>
+ <parameters/>
+ <capture_transform_performance>N</capture_transform_performance>
+ <created_user>-</created_user>
+ <created_date>2026/07/16 00:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/07/16 00:00:00.000</modified_date>
+ </info>
+ <notepads/>
+ <order>
+ <hop>
+ <from>Read extract CSV</from>
+ <to>Verify</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+ <transform>
+ <name>Read extract CSV</name>
+ <type>TextFileInput2</type>
+ <description/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <accept_filenames>N</accept_filenames>
+ <passing_through_fields>N</passing_through_fields>
+ <accept_field/>
+ <accept_transform_name/>
+ <separator>,</separator>
+ <enclosure>"</enclosure>
+ <enclosure_breaks>N</enclosure_breaks>
+ <escapechar/>
+ <header>Y</header>
+ <nr_headerlines>1</nr_headerlines>
+ <footer>N</footer>
+ <nr_footerlines>1</nr_footerlines>
+ <line_wrapped>N</line_wrapped>
+ <nr_wraps>1</nr_wraps>
+ <layout_paged>N</layout_paged>
+ <nr_lines_per_page>80</nr_lines_per_page>
+ <nr_lines_doc_header>0</nr_lines_doc_header>
+ <noempty>Y</noempty>
+ <include>N</include>
+ <include_field/>
+ <rownum>N</rownum>
+ <rownumByFile>N</rownumByFile>
+ <rownum_field/>
+ <format>Unix</format>
+ <encoding>UTF-8</encoding>
+ <length>Characters</length>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <file>
+ <name>/tmp/hop-it-iceberg/0004-extract/</name>
+ <filemask>.*\.csv</filemask>
+ <exclude_filemask/>
+ <file_required>Y</file_required>
+ <include_subfolders>N</include_subfolders>
+ <type>CSV</type>
+ <compression>None</compression>
+ </file>
+ <filters/>
+ <fields>
+ <field>
+ <name>id</name>
+ <type>Integer</type>
+ <format>#</format>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <ifnull/>
+ <position>-1</position>
+ <length>-1</length>
+ <precision>0</precision>
+ <trim_type>both</trim_type>
+ <repeat>N</repeat>
+ </field>
+ <field>
+ <name>name</name>
+ <type>String</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <ifnull/>
+ <position>-1</position>
+ <length>-1</length>
+ <precision>-1</precision>
+ <trim_type>both</trim_type>
+ <repeat>N</repeat>
+ </field>
+ </fields>
+ <limit>0</limit>
+ <error_ignored>N</error_ignored>
+ <skip_bad_files>N</skip_bad_files>
+ <file_error_field/>
+ <file_error_message_field/>
+ <error_line_skipped>N</error_line_skipped>
+ <error_count_field/>
+ <error_fields_field/>
+ <error_text_field/>
+ <bad_line_files_destination_directory/>
+ <bad_line_files_extension>warning</bad_line_files_extension>
+ <error_line_files_destination_directory/>
+ <error_line_files_extension>error</error_line_files_extension>
+ <line_number_files_destination_directory/>
+ <line_number_files_extension>line</line_number_files_extension>
+ <date_format_lenient>Y</date_format_lenient>
+ <date_format_locale>en_US</date_format_locale>
+ <shortFileFieldName/>
+ <pathFieldName/>
+ <hiddenFieldName/>
+ <lastModificationTimeFieldName/>
+ <uriNameFieldName/>
+ <rootUriNameFieldName/>
+ <extensionFieldName/>
+ <sizeFieldName/>
+ <attributes/>
+ <GUI>
+ <xloc>160</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <transform>
+ <name>Verify</name>
+ <type>Dummy</type>
+ <description/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ <GUI>
+ <xloc>400</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/iceberg/0004-write-local.hpl
b/integration-tests/iceberg/0004-write-local.hpl
new file mode 100644
index 0000000000..5f5cd9cdde
--- /dev/null
+++ b/integration-tests/iceberg/0004-write-local.hpl
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<pipeline>
+ <info>
+ <name>0004-write-local</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>iceberg PATH write 10 rows</description>
+ <pipeline_type>Normal</pipeline_type>
+ <parameters/>
+ <capture_transform_performance>N</capture_transform_performance>
+ <created_user>-</created_user>
+ <created_date>2026/07/16 00:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/07/16 00:00:00.000</modified_date>
+ </info>
+ <notepads/>
+ <order>
+ <hop>
+ <from>Seed</from>
+ <to>Lake Output</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+
+ <transform>
+ <name>Seed</name>
+ <type>DataGrid</type>
+ <description/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <data>
+ <line><item>0</item><item>n0</item></line>
+ <line><item>1</item><item>n1</item></line>
+ <line><item>2</item><item>n2</item></line>
+ <line><item>3</item><item>n3</item></line>
+ <line><item>4</item><item>n4</item></line>
+ <line><item>5</item><item>n5</item></line>
+ <line><item>6</item><item>n6</item></line>
+ <line><item>7</item><item>n7</item></line>
+ <line><item>8</item><item>n8</item></line>
+ <line><item>9</item><item>n9</item></line>
+ </data>
+
+ <fields>
+ <field>
+ <name>id</name>
+ <type>Integer</type>
+ <format>#</format>
+ <length>-1</length>
+ <precision>0</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <set_empty_string>N</set_empty_string>
+ </field>
+ <field>
+ <name>name</name>
+ <type>String</type>
+ <format/>
+ <length>-1</length>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <set_empty_string>N</set_empty_string>
+ </field>
+ </fields>
+ <attributes/>
+ <GUI>
+ <xloc>96</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <transform>
+ <type>SparkLakeTableOutput</type>
+ <name>Lake Output</name>
+ <description/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <format>iceberg</format>
+ <identifier_mode>PATH</identifier_mode>
+ <table_path>file:///tmp/hop-it-iceberg/0004-table</table_path>
+ <table_identifier/>
+ <catalog_metadata_name/>
+ <save_mode>Overwrite</save_mode>
+ <partition_by/>
+ <coalesce/>
+ <extra_options/>
+ <attributes/>
+ <GUI>
+ <xloc>320</xloc>
+ <yloc>128</yloc>
+ </GUI>
+ </transform>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/iceberg/README.md
b/integration-tests/iceberg/README.md
index 92f9e81bfa..f244e6dd51 100644
--- a/integration-tests/iceberg/README.md
+++ b/integration-tests/iceberg/README.md
@@ -17,8 +17,8 @@ limitations under the License.
# iceberg integration tests (iceberg)
-End-to-end Hop IT for **Spark Lake Table** transforms on the native Spark
engine
-using **iceberg** PATH mode.
+End-to-end Hop IT for the **Lake table** transforms with **iceberg** PATH
tables, on the
+native Spark engine and on the local Hop engine.
## Prerequisites
@@ -33,6 +33,7 @@ using **iceberg** PATH mode.
| `main-0001-input-output` | Data Grid → Lake Output → Lake Input → CSV →
golden (10 rows) |
| `main-0002-overwrite-timetravel` | Write 5, overwrite 20, read current (and
time travel where applicable) |
| `main-0003-merge` | Seed (1,a)(2,b), MERGE update/insert → golden 3 rows |
+| `main-0004-local-input-output` | Same round trip as 0001 on the local
engine: Data Grid → Lake Output → Lake Input → Text file output → golden (10
rows) |
0002 validates double Overwrite + current read. Iceberg snapshot-id time
travel is covered in Java unit tests (`SparkLakeTableTimeTravelTest`).
diff --git a/integration-tests/iceberg/main-0004-local-input-output.hwf
b/integration-tests/iceberg/main-0004-local-input-output.hwf
new file mode 100644
index 0000000000..4eea4a60e8
--- /dev/null
+++ b/integration-tests/iceberg/main-0004-local-input-output.hwf
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<workflow>
+ <name>main-0004-local-input-output</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>Lake Table Input/Output PATH round-trip on the local
engine</description>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2026/10/07 00:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/10/07 00:00:00.000</modified_date>
+ <parameters/>
+ <actions>
+ <action>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
+ <repeat>N</repeat>
+ <schedulerType>0</schedulerType>
+ <intervalSeconds>0</intervalSeconds>
+ <intervalMinutes>60</intervalMinutes>
+ <hour>12</hour>
+ <minutes>0</minutes>
+ <weekDay>1</weekDay>
+ <DayOfMonth>1</DayOfMonth>
+ <parallel>N</parallel>
+ <xloc>64</xloc>
+ <yloc>96</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>clean work dir</name>
+ <description>Remove previous table/extract data under
/tmp/hop-it-iceberg (always succeeds)</description>
+ <type>SHELL</type>
+ <attributes/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <arg_from_previous>N</arg_from_previous>
+ <arguments>
+</arguments>
+ <exec_per_row>N</exec_per_row>
+ <insertScript>Y</insertScript>
+ <loglevel>Basic</loglevel>
+ <script>#!/bin/bash
+set +e
+WORK="/tmp/hop-it-iceberg"
+echo "Cleaning IT work dir: $WORK"
+rm -rf "$WORK"
+mkdir -p "$WORK"
+exit 0
+</script>
+ <set_append_logfile>N</set_append_logfile>
+ <set_logfile>N</set_logfile>
+ <parallel>N</parallel>
+ <xloc>224</xloc>
+ <yloc>96</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0004-write-local</name>
+ <description/>
+ <type>PIPELINE</type>
+ <attributes/>
+ <filename>${PROJECT_HOME}/0004-write-local.hpl</filename>
+ <params_from_previous>N</params_from_previous>
+ <exec_per_row>N</exec_per_row>
+ <clear_rows>N</clear_rows>
+ <clear_files>N</clear_files>
+ <set_logfile>N</set_logfile>
+ <logfile/>
+ <logext/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <loglevel>Basic</loglevel>
+ <set_append_logfile>N</set_append_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <create_parent_folder>Y</create_parent_folder>
+ <run_configuration>local</run_configuration>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <parallel>N</parallel>
+ <xloc>400</xloc>
+ <yloc>96</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0004-read-local</name>
+ <description/>
+ <type>PIPELINE</type>
+ <attributes/>
+ <filename>${PROJECT_HOME}/0004-read-local.hpl</filename>
+ <params_from_previous>N</params_from_previous>
+ <exec_per_row>N</exec_per_row>
+ <clear_rows>N</clear_rows>
+ <clear_files>N</clear_files>
+ <set_logfile>N</set_logfile>
+ <logfile/>
+ <logext/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <loglevel>Basic</loglevel>
+ <set_append_logfile>N</set_append_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <create_parent_folder>Y</create_parent_folder>
+ <run_configuration>local</run_configuration>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <parallel>N</parallel>
+ <xloc>576</xloc>
+ <yloc>96</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Run unit tests</name>
+ <description/>
+ <type>RunPipelineTests</type>
+ <attributes/>
+ <test_names>
+ <test_name>
+ <name>0004-validation UNIT</name>
+ </test_name>
+ </test_names>
+ <parallel>N</parallel>
+ <xloc>752</xloc>
+ <yloc>96</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>Start</from>
+ <to>clean work dir</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>clean work dir</from>
+ <to>0004-write-local</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>0004-write-local</from>
+ <to>0004-read-local</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>0004-read-local</from>
+ <to>Run unit tests</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ </hops>
+ <notepads/>
+ <attributes/>
+</workflow>
diff --git a/integration-tests/iceberg/metadata/unit-test/0004-validation
UNIT.json b/integration-tests/iceberg/metadata/unit-test/0004-validation
UNIT.json
new file mode 100644
index 0000000000..3593a86ace
--- /dev/null
+++ b/integration-tests/iceberg/metadata/unit-test/0004-validation UNIT.json
@@ -0,0 +1,32 @@
+{
+ "variableValues": [],
+ "database_replacements": [],
+ "autoOpening": true,
+ "basePath": "",
+ "golden_data_sets": [
+ {
+ "field_mappings": [
+ {
+ "transform_field": "id",
+ "data_set_field": "id"
+ },
+ {
+ "transform_field": "name",
+ "data_set_field": "name"
+ }
+ ],
+ "field_order": [
+ "id"
+ ],
+ "data_set_name": "0001-roundtrip-golden",
+ "transform_name": "Verify"
+ }
+ ],
+ "input_data_sets": [],
+ "name": "0004-validation UNIT",
+ "description": "Golden compare for 0004-validation",
+ "persist_filename": "",
+ "trans_test_tweaks": [],
+ "pipeline_filename": "./0004-validation.hpl",
+ "test_type": "UNIT_TEST"
+}
diff --git
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/catalog/OptionalPluginCatalogTest.java
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/catalog/OptionalPluginCatalogTest.java
index 15ceb20090..32adfd3576 100644
---
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/catalog/OptionalPluginCatalogTest.java
+++
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/catalog/OptionalPluginCatalogTest.java
@@ -33,6 +33,7 @@ class OptionalPluginCatalogTest {
List<String> ids = OptionalPluginCatalog.artifactIds();
assertTrue(ids.contains("hop-tech-parquet"));
assertTrue(ids.contains("hop-engines-spark"));
+ assertTrue(ids.contains("hop-tech-lakehouse"));
assertTrue(ids.contains("hop-engines-beam"));
assertTrue(ids.contains("hop-transform-edi2xml"));
}
diff --git
a/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/metadata/messages/messages_en_US.properties
b/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/metadata/messages/messages_en_US.properties
index 374326f0c7..fedeb433e5 100644
---
a/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/metadata/messages/messages_en_US.properties
+++
b/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/metadata/messages/messages_en_US.properties
@@ -15,8 +15,8 @@
# limitations under the License.
#
-SparkCatalog.Name=Spark Catalog
-SparkCatalog.Description=Spark SQL catalog for open table formats (Iceberg
Hadoop/REST, custom)
+SparkCatalog.Name=Lakehouse catalog
+SparkCatalog.Description=Catalog for open table formats (Iceberg Hadoop/REST,
custom), used by the lake table transforms
SparkCatalog.Name.Label=Name
SparkCatalog.CatalogName.Label=Spark catalog name
SparkCatalog.CatalogName.Tooltip=Name used in spark.sql.catalog.<name> (e.g.
lake). Must match the first part of table identifiers.
diff --git
a/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/transforms/messages/messages_en_US.properties
b/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/transforms/messages/messages_en_US.properties
index 6e2b521542..5bd32b7a64 100644
---
a/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/transforms/messages/messages_en_US.properties
+++
b/plugins/tech/lakehouse/src/main/resources/org/apache/hop/lakehouse/transforms/messages/messages_en_US.properties
@@ -15,22 +15,22 @@
# limitations under the License.
#
-SparkLakeTableInput.Name=Spark lake table input
-SparkLakeTableInput.Description=Read an open table format (Delta Lake / Apache
Iceberg) with the native Spark engine
+SparkLakeTableInput.Name=Lake table input
+SparkLakeTableInput.Description=Read an Apache Iceberg table, or a Delta Lake
table on the native Spark engine
SparkLakeTableInput.Keyword=spark,delta,iceberg,lake,table,input,acid
SparkLakeTableInput.Injection.Group.Fields=Fields (optional projection)
-SparkLakeTableOutput.Name=Spark lake table output
-SparkLakeTableOutput.Description=Write an open table format (Delta Lake /
Apache Iceberg) with the native Spark engine
+SparkLakeTableOutput.Name=Lake table output
+SparkLakeTableOutput.Description=Write an Apache Iceberg table, or a Delta
Lake table on the native Spark engine
SparkLakeTableOutput.Keyword=spark,delta,iceberg,lake,table,output,acid
-SparkLakeTableInputDialog.Shell.Title=Spark lake table input
+SparkLakeTableInputDialog.Shell.Title=Lake table input
SparkLakeTableInputDialog.Format=Table format
SparkLakeTableInputDialog.IdentifierMode=Identifier mode
-SparkLakeTableInputDialog.TablePath=Spark table path (PATH mode, Hadoop URI)
-SparkLakeTableInputDialog.TablePath.Tooltip=Lake table root as a Spark/Hadoop
URI (file\:///…, s3a\://…, …). Not a Hop VFS path (s3\://, named MinIO
connection schemes).
+SparkLakeTableInputDialog.TablePath=Table path (PATH mode)
+SparkLakeTableInputDialog.TablePath.Tooltip=Root folder of the table. On the
Hop engine any Hop VFS location (file\:///…, s3\://…). On the Spark engine a
Spark/Hadoop URI (file\:///…, s3a\://…), not a Hop VFS path.
SparkLakeTableInputDialog.TableIdentifier=Table identifier (TABLE mode, e.g.
lake.db.orders)
-SparkLakeTableInputDialog.CatalogMetadataName=Spark Catalog metadata name
(TABLE mode)
+SparkLakeTableInputDialog.CatalogMetadataName=Lakehouse catalog (TABLE mode)
SparkLakeTableInputDialog.TimeTravelType=Time travel
SparkLakeTableInputDialog.TimeTravelVersion=Version / snapshot id
SparkLakeTableInputDialog.TimeTravelTimestamp=As-of timestamp
@@ -41,13 +41,13 @@ SparkLakeTableInputDialog.Column.Type=Type
SparkLakeTableInputDialog.Column.Length=Length
SparkLakeTableInputDialog.Column.Precision=Precision
-SparkLakeTableOutputDialog.Shell.Title=Spark lake table output
+SparkLakeTableOutputDialog.Shell.Title=Lake table output
SparkLakeTableOutputDialog.Format=Table format
SparkLakeTableOutputDialog.IdentifierMode=Identifier mode
-SparkLakeTableOutputDialog.TablePath=Spark table path (PATH mode, Hadoop URI)
-SparkLakeTableOutputDialog.TablePath.Tooltip=Lake table root as a Spark/Hadoop
URI (file\:///…, s3a\://…, …). Not a Hop VFS path.
+SparkLakeTableOutputDialog.TablePath=Table path (PATH mode)
+SparkLakeTableOutputDialog.TablePath.Tooltip=Root folder of the table. On the
Hop engine any Hop VFS location (file\:///…, s3\://…). On the Spark engine a
Spark/Hadoop URI (file\:///…, s3a\://…), not a Hop VFS path.
SparkLakeTableOutputDialog.TableIdentifier=Table identifier (TABLE mode, e.g.
lake.db.orders)
-SparkLakeTableOutputDialog.CatalogMetadataName=Spark Catalog metadata name
(TABLE mode)
+SparkLakeTableOutputDialog.CatalogMetadataName=Lakehouse catalog (TABLE mode)
SparkLakeTableOutputDialog.SaveMode=Save mode
SparkLakeTableOutputDialog.PartitionBy=Partition by columns (comma-separated)
SparkLakeTableOutputDialog.Coalesce=Coalesce partitions (e.g. 1 for single
file)
@@ -63,7 +63,7 @@ SparkLakeTableMergeDialog.IdentifierMode=Identifier mode
SparkLakeTableMergeDialog.TablePath=Spark target table path (PATH mode, Hadoop
URI)
SparkLakeTableMergeDialog.TablePath.Tooltip=Target lake table root as a
Spark/Hadoop URI (file\:///…, s3a\://…, …). Not a Hop VFS path.
SparkLakeTableMergeDialog.TableIdentifier=Target table identifier (TABLE mode)
-SparkLakeTableMergeDialog.CatalogMetadataName=Spark Catalog metadata name
(TABLE mode)
+SparkLakeTableMergeDialog.CatalogMetadataName=Lakehouse catalog (TABLE mode)
SparkLakeTableMergeDialog.MergeCondition=Merge condition (ON clause, t=target
s=source)
SparkLakeTableMergeDialog.MatchedAction=When matched
SparkLakeTableMergeDialog.NotMatchedAction=When not matched
@@ -80,7 +80,7 @@ SparkLakeTableMaintenanceDialog.IdentifierMode=Identifier mode
SparkLakeTableMaintenanceDialog.TablePath=Spark table path (PATH mode, Hadoop
URI)
SparkLakeTableMaintenanceDialog.TablePath.Tooltip=Lake table root as a
Spark/Hadoop URI (file\:///…, s3a\://…, …). Not a Hop VFS path.
SparkLakeTableMaintenanceDialog.TableIdentifier=Table identifier (TABLE mode)
-SparkLakeTableMaintenanceDialog.CatalogMetadataName=Spark Catalog metadata
name (TABLE mode)
+SparkLakeTableMaintenanceDialog.CatalogMetadataName=Lakehouse catalog (TABLE
mode)
SparkLakeTableMaintenanceDialog.Operation=Operation
SparkLakeTableMaintenanceDialog.RetentionHours=Retention hours (VACUUM /
EXPIRE required)
SparkLakeTableMaintenanceDialog.RetainLast=Retain last N snapshots (Iceberg
expire)