bamaer commented on PR #8339:
URL: https://github.com/apache/hop/pull/8339#issuecomment-5644645462
Nice feature. My main concern is backwards compatibility: three of these
break pipelines that work on main and don't use the new syntax at all.
Serialization is fine — `ParameterField` is untouched and no
`@HopMetadataProperty` key changed, so existing XML loads identically. The
regressions are all behavioural.
## Blockers
**1. `getTableFields()` never migrated.** It still passes raw SQL to
`db.getQueryFields()` instead of `parseSqlParameterSpec(...).getPreparedSql()`
like every other call site. Any `?{name}` placeholder makes the driver reject
the statement; the exception is swallowed into `logError` and the method
returns null. Called from `DatabaseJoin.lookupValues()` on every pipeline start
and from `PipelineMeta` at design time.
**2. `parseSqlParameterSpec()` miscounts parameters.** It tracks only
single-quoted literals, so a `?` in a `--` / `/* */` comment or a `"quoted
identifier"` counts as positional. `data.keynrs` is now sized from this parse
rather than `meta.getParameters().size()`, so existing SQL fails with "field
not found: null" or a JDBC parameter-index error.
Both failure modes are silent in different ways. `check()` runs
`countParameters()` on the prepared SQL, so both scanners see the same string:
for a `?` in a `"quoted identifier"` they disagree (only `countParameters`
skips it) and you get a spurious mismatch remark on valid SQL; for a `?` in a
comment they agree, no remark appears, and `keynrs` is just quietly wrong.
JDBC can't do this parse: `?{name}` isn't valid JDBC, and
`getParameterMetaData()` returns only a count, never positions
(`Database.getParameterMetaData()` already catches `AbstractMethodError` and
falls back to manual counting). Usable as a cross-check, not as the parser.
Suggest one tokenizer in core returning the ordered reference list, with
`countParameters()` wrapping it. Needs to skip: `''` escaping,
`"`/backtick/`[]` identifiers, `--` and `/* */` comments, and PostgreSQL `$$`
quoting plus the `?`, `?|`, `?&` jsonb operators — that last one breaks real
pipelines.
**3. `getFields()` no longer binds incoming stream types.** This one affects
every existing transform, not just ones using `?{name}`:
- main: `param = getParameterRow(row)` — types from the **incoming row**,
values all NULL
- PR: `param = createMetadataLookupParameterRowMeta(...)` — types from the
**declared `ParameterField` list**, with dummy non-null values (`"metadata"`,
`0L`, …)
`createMetadataLookupParameterRowMeta()` never consults `row` and falls back
to `ValueMetaNone`. The type column in the positional parameter table is
commonly left unset, so existing transforms that relied on real stream types
now bind `ValueMetaNone` → `setNull(Types.VARCHAR)`. On the
`getQueryFieldsFallback` path that turns a working `WHERE num_col = ?` into
ORA-00932/ORA-01722 at design time.
If the declared types are needed for the named case, suggest preferring the
incoming row's type when the field resolves in `prev` and falling back to the
declared type only otherwise — and keeping NULL values rather than dummies.
## Should fix
**4. Stored-procedure deferral swallows connection failures.** In
`getFields()`, `db.connect()` is inside the try whose catch defers on
`isLikelyStoredProcedureSql()`. For any `exec`/`execute`/`{call` SQL, a dead
database or a genuine syntax error is discarded at `logDetailed` and
`getFields()` returns zero fields silently. Move `db.connect()` outside that
try.
**5. No validation when SQL is empty.** The closing brace before `// Look up
fields in the input stream` was removed, pulling that validation inside `if
(!Utils.isEmpty(sqlToUse))`. The `CouldNotReadFields` error is gone and a
transform that cannot run now reports nothing.
## Minor / follow-up
- The `data.parameterSpec == null ?
parseSqlParameterSpec(meta.getEffectiveSql(variables)) : ...` fallback in
`lookupValues()` is unreachable — `init()` always sets `data.parameterSpec`
first. Worth deleting rather than leaving: if it ever were reached it would
parse *unresolved* SQL while the statement was prepared from *resolved* SQL, so
the counts could disagree.
- `createUniqueOutputFieldName()` renames to `name_dbj1`, design-time
`RowMeta.renameValueMetaIfInRow` to `name_1` — same query, different field
names. `RowMeta.addValueMeta` already deduplicates, so the manual renaming
looks removable.
- `getMissingPositionalParameterFields()` checks only that a name is
declared, not that it exists in `prev`; and `check()` still adds "All fields
found" alongside a missing-field error. Only the first missing field is named.
- `getParameterRow()` is dead outside tests.
## GUI surface
`?{name}` is currently file-only — `DatabaseJoinDialog` still offers just
the positional parameter table — which conflicts with our rule against features
that exist only in a file.
This doesn't need an authoring UI. The SQL *is* the binding, so nothing
needs a second editing surface; a read-only panel under the SQL editor showing
what the parser resolved is enough:
| # | Placeholder | Input field | Type |
|---|---|---|---|
| 1 | `?{customer_id}` | customer_id | Integer |
| 2 | `?` | positional → row 1 | String |
| 3 | `?{region}` | **not found** | — |
`wSql` already has a `ModifyListener` and already calls `setPosition()` on
modify, so it's the same hook. And because it calls the same
`parseSqlParameterSpec()` the runtime uses, it can't drift — whatever it
displays is what executes. It would also have made issue 2 visible while
writing the SQL rather than at runtime.
Optional on top, both droppable: grey out the positional table when the SQL
has no bare `?`, and field-name autocomplete in the editor.
--
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]