Support PPL foreach command#5613
Conversation
…special cases
- Extract ~400 lines of foreach planning from CalciteRelNodeVisitor into
a dedicated ForeachPlanner class.
- Replace string-based type plumbing (SqlTypeName names smuggled through
ForeachBinding and reparsed at runtime) with RelDataType carried on the
binding; foreach_pair_item now takes (pair, index) only and the call is
built with the plan-time type directly.
- Collapse ForeachBindingType {PAIR_ITEM, PAIR_ITER, PAIR_EXTRA, LAMBDA}
into a single PAIR_SLOT; drop the unused LAMBDA variant.
- Replace the AST arithmetic-scan heuristic for json_array element types
with operand type inspection (json_array call) / plan-time JSON parse
(string literal), using SqlTypeFamily.
- Remove the reduce-arg0 special case in CalciteRexNodeVisitor: collection
bindings are now staged on CalcitePlanContext and only activate inside
cloned lambda contexts, so non-lambda reduce args resolve against the
row naturally.
- Foreach.Mode enum replaces stringly-typed mode comparisons; AstBuilder
parses options/targets in a single pass without double-visiting
expressions.
- Delete dead code: FOREACH_TRANSFORM_* constants, FOREACH_PAIR_ITEM
registry entry, validateHomogeneousJsonArrayArguments wrapper, duplicate
collectionExpressionForMode branches.
Signed-off-by: Songkan Tang <songkant@amazon.com>
foreachTarget's logicalExpression alternative let ANTLR consider the
foreach rule during error recovery of unrelated malformed queries,
changing expected-token sets in syntax error messages (caught by
UnifiedRelevanceSearchTest#testMatchMissingArguments: 'match(' with a
missing field started reporting 26 candidate tokens instead of the
expected ','). foreach only ever needs a function call (json_array),
a string literal (JSON array text), or a field/wildcard as its target,
so accept exactly those instead of the whole expression grammar.
Signed-off-by: Songkan Tang <songkant@amazon.com>
Replace assertNotNull with verifyLogical so the tests pin the generated reduce/foreach_pair_collection/foreach_pair_item structure, placeholder slot indices, and json_array element-type inference. Signed-off-by: Songkan Tang <songkant@amazon.com>
The refactor's jsonElementType defaulted opaque expressions (an index field holding JSON text - the primary Splunk use of json_array mode) to VARCHAR, which broke numeric aggregation over such fields: reduce resolved to [ARRAY<VARCHAR>, DOUBLE, DOUBLE] and failed. Splunk returns 60 for a field holding "[10,20,30]" summed via foreach; the original branch matched that by inferring element type from usage. Bring back the usage scan for the opaque case only: if the item placeholder is consumed by arithmetic the elements are DOUBLE, else VARCHAR. json_array() calls and string literals keep the plan-time content inspection. New coverage: - CalcitePPLForeachTest: plan assertions for field-backed json_array with numeric and string usage - ForeachFieldJsonIT: field holding "[10,20,30]" sums to 60 (Splunk parity), string-content field concats, and native OpenSearch array fields (long field holding [1,2,3]) documented as rejected - the mapping types them as scalar BIGINT at plan time Signed-off-by: Songkan Tang <songkant@amazon.com>
Nested-typed fields map to ARRAY<ANY> at plan time so multivalue mode accepts and iterates them (verified: counting elements of a 2-element nested array returns 2). Pins the third field-backed collection shape alongside JSON-text fields (supported) and native scalar-mapped arrays (rejected). Signed-off-by: Songkan Tang <songkant@amazon.com>
Splunk silently no-ops when a mode is fed the wrong collection shape (verified against Splunk 10.4.0). Our behavior intentionally differs and these tests document it: - json_array mode fed a real array iterates it (total=6) - more permissive than Splunk's no-op - multivalue mode fed a JSON-text field fails at plan time with SemanticCheckException - louder than Splunk's no-op Signed-off-by: Songkan Tang <songkant@amazon.com>
Follows the structure of other command docs (timewrap, eval): syntax, parameters, placeholder table, notes on collection-mode accumulator semantics and type inference, and six runnable examples registered in docs/category.json for doctest. All example outputs verified against a live docTestCluster loaded with the doctest accounts dataset. Signed-off-by: Songkan Tang <songkant@amazon.com>
|
Note for reviewers: two items from the new-command checklist are still pending and I'll push them to this PR shortly:
|
PR Reviewer Guide 🔍(Review updated until commit 0e74b5d)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 0e74b5d
Previous suggestionsSuggestions up to commit 93b0892
|
…tion - Analyzer.visitForeach throws the standard only-for-Calcite UnsupportedOperationException instead of leaving the v2 path undefined - PPLQueryDataAnonymizer.visitForeach renders the command with mode, masked option values, masked targets (collection targets can embed literals such as JSON array strings), and anonymized eval clauses; ForeachPlaceholder masks like a column reference UT: AnalyzerTest legacy-engine rejection; anonymizer coverage for multifield, multivalue-with-options, and json_array-with-literal-target (122/122 anonymizer tests pass) Signed-off-by: Songkan Tang <songkant@amazon.com>
|
Both pending checklist items are now in (
The new-command checklist is now fully satisfied. |
|
Persistent review updated to latest commit 0e74b5d |
Description
Adds the PPL
foreachcommand (Splunk-compatible), which runs a templatedevalfor each field in a field list, each element of a multivalue (array) field, or each element of a JSON array.All four Splunk modes are supported:
multifield(default),multivalue,json_array, andauto_collections(runtime detection). Placeholders<<FIELD>>/<<MATCHSTR>>(multifield) and<<ITEM>>/<<ITER>>(collection modes) are supported, withfieldstr/matchstr/itemstr/iterstrrename options.Design
CalcitePlanContext;<<FIELD>>resolves to the current row field,<<MATCHSTR>>to the wildcard-captured segment.reducecall over the collection. Elements are packed into internal pairs[item, iter, captured-field...]by aforeach_pair_collectionUDF so the two-parameter reduce lambda can reach the loop item, the loop index, and any referenced row fields;foreach_pair_item(pair, slot)extracts a slot with its plan-time type attached. Bindings are staged on the context and only activate inside the cloned lambda context, so the reduce call's own arguments resolve against the row normally.json_array(...)calls and string literals are inspected at plan time (mixed string/number arrays rejected); for opaque expressions (a field holding JSON text — the primary Splunk use), the type is inferred from usage: arithmetic on<<ITEM>>selects DOUBLE, otherwise VARCHAR.ForeachPlannerclass rather than growingCalciteRelNodeVisitor.Behavior verified against Splunk 10.4.0
"[10,20,30]")Known limitation (documented in tests and user doc): fields whose mapping is a scalar type (e.g.
long) holding arrays are rejected by multivalue mode, because OpenSearch mappings do not declare array-ness and the plan-time type is scalar. Nested-typed fields map toARRAY<ANY>and iterate correctly.Related Issues
N/A (new PPL command)
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.