Add PPL multikv command (fixed-schema) - #5641
Conversation
PR Reviewer Guide 🔍(Review updated until commit 9296e74)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 9296e74 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit e146e91
Suggestions up to commit e1b3333
Suggestions up to commit eedb47b
|
eedb47b to
e1b3333
Compare
|
Persistent review updated to latest commit e1b3333 |
Row-multiplying command that extracts fields from table-formatted text in an input field. Three-layer Calcite rewrite: MULTIKV_SPLIT UDF -> mvexpand (Uncollect/Correlate) -> per-column MULTIKV_EXTRACT UDF -> project. Output columns are resolved at plan time from a fields clause, forceheader, or positional noheader; a bare auto-header form is rejected with guidance. All columns emit VARCHAR (implicit per-op coercion downstream). Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
e1b3333 to
e146e91
Compare
|
Persistent review updated to latest commit e146e91 |
Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com> # Conflicts: # docs/user/ppl/index.md
|
Persistent review updated to latest commit 9296e74 |
| @Getter | ||
| public class Multikv extends UnresolvedPlan { | ||
|
|
||
| /** Default Splunk input field for multikv. */ |
There was a problem hiding this comment.
Avoid Splunk wording in comments.
| public class Multikv extends UnresolvedPlan { | ||
|
|
||
| /** Default Splunk input field for multikv. */ | ||
| public static final String DEFAULT_INPUT_FIELD = "_raw"; |
There was a problem hiding this comment.
We don't have such metadata field yet. Is it expected to be explicitly generated by user in makeresults command?
| ```ppl | ||
| source=metrics | ||
| | multikv field=raw fields pctIdle | ||
| | fields pctIdle | ||
| ``` |
There was a problem hiding this comment.
We need to print real output for each example ppl queries.
| | [explain command](cmd/explain.md) | 3.1 | stable (since 3.1) | N/A | Explain the plan of query. | | ||
| | [show datasources command](cmd/showdatasources.md) | 2.4 | stable (since 2.4) | N/A | Query datasources configured in the PPL engine. | | ||
| | [makeresults command](cmd/makeresults.md) | 3.8 | experimental (since 3.8) | No | Generate in-memory rows for testing and seeding, optionally from inline CSV/JSON data. | | ||
| | [multikv command](cmd/multikv.md) | 3.8 | experimental (since 3.8) | No | Extract fields from table-formatted text in a field, emitting one row per table data row. | |
| String inField = Multikv.DEFAULT_INPUT_FIELD; | ||
| Integer forceHeader = null; | ||
| boolean noHeader = false; | ||
| boolean rmOrig = true; // Splunk default |
There was a problem hiding this comment.
Avoid Splunk wording in comments.
|
|
||
| For a document with `procs = [{"pid":1,"cpu":0.5},{"pid":42,"cpu":9.1}]`, the query returns two rows: `(1, 0.5)` and `(42, 9.1)`. When `field=` points at a single object rather than an array, one row is returned. Nested container values are returned as-is; extract deeper fields downstream with `spath` or another `multikv field=<subfield>`. | ||
|
|
||
| ## Limitations |
There was a problem hiding this comment.
Per the RFC discussion, we should call out that users must explicitly cast expanded key-value fields from VARCHAR or ANY to the expected types when type-sensitive processing is required.
Please confirm that this reflects an outcome discussed and agreed upon with users.
| public void testMultikvFields() throws IOException { | ||
| // Declared single column: auto-detected header maps pctIdle -> its column. | ||
| JSONObject result = | ||
| executeQuery( | ||
| "source=test_multikv | eval _raw = raw | multikv fields pctIdle | fields pctIdle"); | ||
| verifySchema(result, schema("pctIdle", "string")); | ||
| verifyDataRows(result, rows("90"), rows("92")); | ||
| } |
There was a problem hiding this comment.
Add more complex ITs to let downstream commands correctly consume multikv results after casting
| * These assertions pin the presence and absence of those operators rather than the full Rex | ||
| * rendering, which is exercised end-to-end by CalcitePPLMultikvCommandIT. | ||
| */ | ||
| public class CalcitePPLMultikvTest extends CalcitePPLAbstractTest { |
There was a problem hiding this comment.
Please also add tests to CalciteExplainIT to assert printed logical plans are correct.
| && (SqlTypeUtil.isArray(probeField.getType()) | ||
| || SqlTypeUtil.isMultiset(probeField.getType())); | ||
| boolean structuredMap = probeField != null && SqlTypeUtil.isMap(probeField.getType()); | ||
| if (structuredArray || structuredMap) { |
There was a problem hiding this comment.
For input types other than ARRAY and MAP, the current implementation silently falls back to the text path and treats them as strings. Should we validate the input type here and reject unsupported types during planning?
| // Text input: discard the probe build and run the split pipeline on a fresh build. | ||
| context.relBuilder.build(); | ||
| context.setProjectVisited(savedProjectVisited); |
There was a problem hiding this comment.
The type-based dispatch is reasonable, but the current implementation visits the child twice. The first visit mutates the shared RelBuilder and CalcitePlanContext; text mode then calls build() and only restores isProjectVisited before rebuilding the child. This is not a full rollback and may affect features such as correlation binding or plan-node tracking. Could we build the child once and lower text mode from the resulting RelNode? Is there a specific reason to discard the build in text mode? The schema is known at stack peek and it seems feasible to build text mode specific MvExpand Relnode on top of it.
Description
Adds the PPL
multikvcommand: a streaming, row-multiplying command that extracts fields from an input field and emits one row per source record. It requires the Calcite (v3) engine.The input field is selected with
field=<name>(defaults to_raw); output columns are declared withfields <col>....multikvdispatches on the input field's plan-time type:VARCHAR): parse table-formatted text (for exampleps/top/netstat/dfoutput) into columns. Extracted values are typedstring.ARRAY<ANY>): explode into one row per element, reading each declared column from the element and preserving its type.MAP<VARCHAR,ANY>): read each declared column, preserving its type; emits one row.Nested container values are returned serialized (matching the merged
makeresultsconvention); extract deeper fields downstream withspathor anothermultikv field=<subfield>.Design, semantics, and deferred scope (runtime auto-header, aligned-offset parsing,
filter/rmorig, parse-to-MAP optimization) are in the RFC: #5640.Changes
MultikvAST + Calcite lowering, including thefield=<name>input selector,forceheader, andnoheader.visitMultikv: text →MULTIKV_SPLIT/mvexpand/MULTIKV_EXTRACT; array-of-objects →mvexpand+INTERNAL_ITEM; single object →INTERNAL_ITEMonly.multikv(nofields, nonoheader) is rejected at the semantic layer with actionable guidance (v1 is fixed-schema).makeresultsconvention.docs/user/ppl/cmd/multikv.mdand anindex.mdrow.Test coverage
CalcitePPLMultikvTest,AstBuilderTest#testMultikvCommand(incl.field=),PPLQueryDataAnonymizerTest.CalcitePPLMultikvCommandIT(coveringfield=, text, array-of-objects, and single-object modes),NewAddedCommandsIT#testMultikv.Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.