|
31 | 31 | import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals; |
32 | 32 | import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.NotEquals; |
33 | 33 | import org.elasticsearch.xpack.esql.plan.logical.Aggregate; |
| 34 | +import org.elasticsearch.xpack.esql.plan.logical.ChangePoint; |
34 | 35 | import org.elasticsearch.xpack.esql.plan.logical.Dissect; |
35 | 36 | import org.elasticsearch.xpack.esql.plan.logical.Drop; |
| 37 | +import org.elasticsearch.xpack.esql.plan.logical.Enrich; |
36 | 38 | import org.elasticsearch.xpack.esql.plan.logical.Eval; |
37 | | -import org.elasticsearch.xpack.esql.plan.logical.Filter; |
38 | | -import org.elasticsearch.xpack.esql.plan.logical.Fork; |
39 | 39 | import org.elasticsearch.xpack.esql.plan.logical.Grok; |
40 | | -import org.elasticsearch.xpack.esql.plan.logical.InlineStats; |
| 40 | +import org.elasticsearch.xpack.esql.plan.logical.Insist; |
41 | 41 | import org.elasticsearch.xpack.esql.plan.logical.Keep; |
42 | 42 | import org.elasticsearch.xpack.esql.plan.logical.LeafPlan; |
43 | 43 | import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan; |
44 | 44 | import org.elasticsearch.xpack.esql.plan.logical.OrderBy; |
45 | 45 | import org.elasticsearch.xpack.esql.plan.logical.Project; |
46 | 46 | import org.elasticsearch.xpack.esql.plan.logical.Rename; |
47 | 47 | import org.elasticsearch.xpack.esql.plan.logical.Sample; |
48 | | -import org.elasticsearch.xpack.esql.plan.logical.join.LookupJoin; |
| 48 | +import org.elasticsearch.xpack.esql.plan.logical.UnaryPlan; |
49 | 49 | import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; |
50 | 50 | import org.elasticsearch.xpack.esql.session.Result; |
51 | 51 |
|
@@ -90,34 +90,25 @@ public interface LogicalPlanRunner { |
90 | 90 | void run(LogicalPlan plan, ActionListener<Result> listener); |
91 | 91 | } |
92 | 92 |
|
| 93 | + |
93 | 94 | /** |
94 | | - * These commands preserve all rows, so can be swapped with {@code SAMPLE}. |
| 95 | + * These commands preserve all rows, making it easy to predict the number of output rows. |
95 | 96 | */ |
96 | 97 | private static final Set<Class<? extends LogicalPlan>> ROW_PRESERVING_COMMANDS = Set.of( |
| 98 | + ChangePoint.class, |
97 | 99 | Dissect.class, |
98 | 100 | Drop.class, |
| 101 | + Enrich.class, |
99 | 102 | EsqlProject.class, |
100 | 103 | Eval.class, |
101 | | - Filter.class, |
102 | | - Fork.class, |
103 | 104 | Grok.class, |
| 105 | + Insist.class, |
104 | 106 | Keep.class, |
105 | 107 | OrderBy.class, |
106 | 108 | Project.class, |
107 | | - Rename.class, |
108 | | - Sample.class |
| 109 | + Rename.class |
109 | 110 | ); |
110 | 111 |
|
111 | | - /** |
112 | | - * These commands keep or filter rows, so can be swapped with {@code SAMPLE}. |
113 | | - */ |
114 | | - private static final Set<Class<? extends LogicalPlan>> ROW_FILTERING_COMMANDS = Set.of(Filter.class, Sample.class); |
115 | | - |
116 | | - /** |
117 | | - * Commands that cannot be used anywhere in an approximated query. |
118 | | - */ |
119 | | - private static final Set<Class<? extends LogicalPlan>> INCOMPATIBLE_COMMANDS = Set.of(InlineStats.class, LookupJoin.class); |
120 | | - |
121 | 112 | // TODO: find a good default value, or alternative ways of setting it |
122 | 113 | private static final int SAMPLE_ROW_COUNT = 100000; |
123 | 114 |
|
@@ -154,32 +145,25 @@ private boolean verifyPlan() { |
154 | 145 | List.of(Failure.fail(logicalPlan.collectLeaves().getFirst(), "query without [STATS] cannot be approximated")) |
155 | 146 | ); |
156 | 147 | } |
| 148 | + // For now, only support unary plans. |
| 149 | + // TODO: support binary plans (e.g. join) and n-ary plans (e.g. fork). |
157 | 150 | logicalPlan.forEachUp(plan -> { |
158 | | - if (INCOMPATIBLE_COMMANDS.contains(plan.getClass())) { |
| 151 | + if (plan instanceof LeafPlan == false && plan instanceof UnaryPlan == false) { |
159 | 152 | throw new VerificationException( |
160 | | - List.of(Failure.fail(plan, "query with [" + plan.sourceText() + "] cannot be approximated")) |
| 153 | + List.of(Failure.fail(plan, "query with [" + plan.nodeName() + "] cannot be approximated")) |
161 | 154 | ); |
162 | 155 | } |
163 | 156 | }); |
164 | 157 |
|
165 | 158 | Holder<Boolean> encounteredStats = new Holder<>(false); |
166 | 159 | Holder<Boolean> hasFilters = new Holder<>(false); |
167 | 160 | logicalPlan.transformUp(plan -> { |
168 | | - if (plan instanceof LeafPlan) { |
169 | | - encounteredStats.set(false); |
170 | | - } else if (encounteredStats.get() == false) { |
| 161 | + if (encounteredStats.get() == false) { |
171 | 162 | if (plan instanceof Aggregate) { |
172 | 163 | encounteredStats.set(true); |
173 | | - } else if (ROW_PRESERVING_COMMANDS.contains(plan.getClass()) == false |
174 | | - && ROW_FILTERING_COMMANDS.contains(plan.getClass()) == false) { |
175 | | - hasFilters.set(true); // TODO: fix |
176 | | - |
177 | | -// throw new VerificationException( |
178 | | -// List.of(Failure.fail(plan, "query with [" + plan.sourceText() + "] before [STATS] cannot be approximated")) |
179 | | -// ); |
180 | | - } else if (ROW_FILTERING_COMMANDS.contains(plan.getClass())) { |
181 | | - hasFilters.set(true); |
182 | | - } |
| 164 | + } else if (ROW_PRESERVING_COMMANDS.contains(plan.getClass()) == false) { |
| 165 | + hasFilters.set(true); |
| 166 | + } |
183 | 167 | } |
184 | 168 | return plan; |
185 | 169 | }); |
|
0 commit comments