mattcasters opened a new pull request, #7429:
URL: https://github.com/apache/hop/pull/7429
## Summary
Issue **#7427** addresses regressions and related failures in the
**Dimension Lookup / Update** transform after the validation work landed in
**#7408** (commit `5ca8eb1eb6`, PR #7408). That change added extensive field
and metadata validation but inadvertently broke a previously valid
configuration: **lookup-only mode with an empty version field**.
This branch also improves preload-cache behaviour: it replaces the cryptic
`Comparison method violates its general contract` error with actionable
validation messages, adds an option to skip zero-length validity rows during
preload, fixes a preload date-type mismatch, and adds integration tests under
`integration-tests/database/`.
---
## Root cause analysis
### 1. Version field regression (primary regression from #7408)
Commit `5ca8eb1eb6` introduced `validateFieldsAndMetadata()` in
`DimensionLookup.java`. When the version field is empty, it always threw:
> Please specify a fieldname to store the version of the dimension entry in.
That check is appropriate for **update** mode (SCD insert/update algorithm
needs a version column), but **lookup-only** mode (`update = N`) has never
required a version field. Lookup-only pipelines with `<version/>` empty
therefore started failing at init.
The same unconditional requirement existed in
`DimensionLookupMeta.checkReturns()` used by the transform verification UI.
**Fix:** Gate the version-field requirement behind `isUpdate()` in both
places:
- `DimensionLookup.validateFieldsAndMetadata()`
- `DimensionLookupMeta.checkReturns()`
### 2. Preload cache sort comparator error (pre-existing, surfaced by
testing)
`DimensionCache.compare()` is designed for **binary-search lookup**, not for
`Collections.sort()`. When two rows share the same natural key and overlapping
`[date_from, date_to)` ranges, the comparator can return `0` for non-equal
rows, violating the Comparator contract and producing:
> Comparison method violates its general contract
This is especially common when upstream flattening produces **zero-length
validity** rows (`date_from == date_to`).
**Fix:** Before `sortRows()`, call new
`DimensionCache.validateRowsForSort()` which checks:
- Invalid ranges (`from >= to`, including zero-length)
- Overlapping ranges per natural key
Errors are reported with natural key and date-range context (capped at 3
messages). `HopTransformException` is rethrown directly from `preloadCache()`
so messages are not wrapped in a generic `HopException`.
### 3. Preload lookup date type mismatch (found during integration testing)
When comparing the lookup date against preloaded rows, `java.util.Date`
(pipeline execution date) was placed directly into a row whose metadata expects
`java.sql.Timestamp` (PostgreSQL preload). This caused:
> ClassCastException: java.util.Date cannot be cast to java.sql.Timestamp
**Fix:** Convert the lookup date through the preload row's date value meta
before cache lookup:
```java
lookupRow[data.preloadFromDateIndex] =
preloadFromDateMeta.convertData(valueDateMeta, valueDate);
```
---
## Code changes
### Transform plugin (`plugins/transforms/dimensionlookup`)
| File | Change |
|------|--------|
| `DimensionLookup.java` | Version validation gated on `isUpdate()`; preload
validation call; zero-length filter; date conversion fix;
`HopTransformException` passthrough from preload; improved error logging (stack
trace on init failure) |
| `DimensionLookupMeta.java` | New `ignoreZeroLengthValidity` property
(`ignore_zero_length_validity`, default `false`); version check gated on
`isUpdate()` in `checkReturns()` |
| `DimensionCache.java` | `validateRowsForSort()`, `isZeroLengthValidity()`,
`excludeZeroLengthValidityRows()`, overlap/invalid-range helpers |
| `DimensionLookupDialog.java` | Checkbox "Ignore zero-length validity
records" below "Pre-load the cache"; enabled only when cache + preload +
lookup-only |
| `messages_en_US.properties` | Cache validation errors, ignore-option
labels/tooltips, log messages |
| `DimensionCacheTest.java` | Unit tests for validation (adjacent ranges,
overlaps, invalid ranges, null open-ended overlaps) and zero-length
detection/filtering |
### Integration tests (`integration-tests/database/`)
Wired into `main-0012-dimension-lookup-update.hwf` after existing `0012-5`:
| Test | Purpose |
|------|---------|
| **0012-6-dimension-lookup-no-version-field** | SQL setup → unit test.
Lookup-only, empty version, `preload_cache=Y`, `cache_size=0`. Asserts
regression fix. |
| **0012-dimension-lookup-zero-length-validity** | SQL setup → pipeline
(expected failure) → Success via failure hop. Asserts preload validation
catches bad dimension data. |
Supporting files (new):
- `0012-dimension-lookup-no-version.hpl` — includes downstream **Verify**
(Dummy) transform for unit-test row capture
- `0012-dimension-lookup-zero-length-validity.hpl`
- SQL scripts, golden CSV/dataset metadata, unit-test JSON
**Unit-test design note:** The Hop unit-test framework captures **input
rows** to the golden-data transform (`rowReadEvent`), not transform output.
Golden data must therefore be attached to a downstream transform (e.g. Dummy
**Verify**) whose input row meta already contains the expected lookup results.
This follows the same pattern as `0023-db-procedure`.
---
## New option: ignore zero-length validity
Property: `ignore_zero_length_validity` (default `false`)
When enabled with preload cache in lookup-only mode, rows where `date_from
== date_to` are excluded **before** cache validation and sorting. Useful when
upstream flattening produces point-in-time records that cannot participate in
range-based lookup but should not block the pipeline.
Log messages (detailed/basic) report how many rows were skipped; an empty
cache after filtering logs a basic warning.
---
## Testing
### Unit tests
```bash
./mvnw test -pl plugins/transforms/dimensionlookup
```
All `DimensionCacheTest` cases pass, including new validation and
zero-length filtering tests.
### Integration tests
```bash
integration-tests/scripts/run-tests-docker.sh PROJECT_NAME=database
```
Verified on a clean Docker run (container exit code 0):
- **0012-6**: unit test passed — keys `1`/`2` resolved to `dimension_id`
`1`/`2`, values `alpha`/`beta`
- **0012-zero-length-validity**: pipeline failed with explicit validation
message; workflow succeeded on failure hop:
- `Natural key [K1] has an invalid date range [2020/03/01, 2020/03/01)
(date-from must be before date-to)`
- Overlapping validity periods reported for the same key
- **main-0012-dimension-lookup-update**: all sub-workflows (0012-1 through
zero-length-validity) `result=[true]`
**Build note for reviewers:** A full reactor build (`-am`) may fail on
unrelated modules (e.g. corrupted `hop-action-ftp` checkstyle XML). Building
the plugin and client directly works:
```bash
./mvnw install -pl plugins/transforms/dimensionlookup -DskipTests
./mvnw clean install -pl assemblies/client -DskipTests -Dcheckstyle.skip=true
```
The Docker test script unzips `assemblies/client/target/hop-client-*.zip`
before building the image; ensure the client assembly includes the rebuilt
dimensionlookup JAR.
---
## What reviewers should focus on
1. **Regression fix correctness** — Version field is still required and
validated in update mode; lookup-only with empty version must not fail
validation or UI checks.
2. **Validation vs. comparator** — `validateRowsForSort()` uses a sort-safe
comparator (natural key, then `date_from`); the existing `compare()` for binary
search is unchanged. Confirm this separation is intentional and sufficient.
3. **Error message quality** — Failures during preload should surface
`HopTransformException` messages directly to the user, not generic wrappers.
4. **Ignore option semantics** — Filtering happens before validation;
document that ignored rows are invisible to lookup (potential data-gap risk if
misused).
5. **Date conversion** — Preload path only; non-preload lookup paths
unchanged.
6. **Integration test pattern** — Verify transform placement for golden-data
capture; failure-hop workflow for expected pipeline errors.
---
## Out of scope / known limitations
- The underlying `DimensionCache.compare()` contract issue for
`Collections.sort()` is **not** fixed by changing the comparator;
invalid/overlapping data is rejected upfront instead.
- No third integration test for the **ignore zero-length** option end-to-end
(option is covered by unit tests; could be added later).
- Unrelated pre-existing integration failure: `0029-sql-file-output` (cannot
create output folder) — not introduced by this work.
---
## File change summary
```
integration-tests/database/main-0012-dimension-lookup-update.hwf |
66 +++
plugins/transforms/dimensionlookup/.../DimensionCache.java |
208 +++++++
plugins/transforms/dimensionlookup/.../DimensionLookup.java |
36 +-
plugins/transforms/dimensionlookup/.../DimensionLookupDialog.java |
33 +-
plugins/transforms/dimensionlookup/.../DimensionLookupMeta.java |
20 +-
plugins/transforms/dimensionlookup/.../messages_en_US.properties |
10 +-
plugins/transforms/dimensionlookup/.../DimensionCacheTest.java |
158 ++++++
+ 8 new integration-test files (pipelines, workflows, SQL, datasets,
metadata)
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]