99
1010import com .carrotsearch .randomizedtesting .annotations .ParametersFactory ;
1111
12+ import org .elasticsearch .common .network .NetworkAddress ;
1213import org .elasticsearch .common .settings .Settings ;
1314import org .elasticsearch .core .Tuple ;
1415import org .elasticsearch .index .query .QueryBuilder ;
4344import org .elasticsearch .xpack .esql .session .EsqlConfiguration ;
4445import org .elasticsearch .xpack .esql .stats .Metrics ;
4546import org .elasticsearch .xpack .esql .stats .SearchStats ;
46- import org .elasticsearch .xpack .esql .type .EsqlDataTypes ;
4747import org .elasticsearch .xpack .ql .expression .Alias ;
4848import org .elasticsearch .xpack .ql .expression .Expression ;
4949import org .elasticsearch .xpack .ql .expression .Expressions ;
5050import org .elasticsearch .xpack .ql .expression .ReferenceAttribute ;
51- import org .elasticsearch .xpack .ql .expression .function .FunctionRegistry ;
5251import org .elasticsearch .xpack .ql .index .EsIndex ;
5352import org .elasticsearch .xpack .ql .index .IndexResolution ;
5453import org .elasticsearch .xpack .ql .tree .Source ;
5554import org .elasticsearch .xpack .ql .type .DataTypes ;
5655import org .elasticsearch .xpack .ql .type .EsField ;
5756import org .junit .Before ;
5857
58+ import java .util .ArrayList ;
5959import java .util .List ;
6060import java .util .Map ;
6161import java .util .Set ;
62+ import java .util .stream .Collectors ;
6263
6364import static java .util .Arrays .asList ;
65+ import static org .elasticsearch .common .logging .LoggerMessageFormat .format ;
6466import static org .elasticsearch .xpack .esql .EsqlTestUtils .as ;
6567import static org .elasticsearch .xpack .esql .EsqlTestUtils .configuration ;
6668import static org .elasticsearch .xpack .esql .EsqlTestUtils .loadMapping ;
@@ -87,9 +89,8 @@ public class LocalPhysicalPlanOptimizerTests extends ESTestCase {
8789 private Analyzer analyzer ;
8890 private LogicalPlanOptimizer logicalOptimizer ;
8991 private PhysicalPlanOptimizer physicalPlanOptimizer ;
92+ private EsqlFunctionRegistry functionRegistry ;
9093 private Mapper mapper ;
91- private Map <String , EsField > mapping ;
92- private int allFieldRowSize ;
9394
9495 private final EsqlConfiguration config ;
9596 private final SearchStats IS_SV_STATS = new TestSearchStats () {
@@ -118,24 +119,9 @@ public LocalPhysicalPlanOptimizerTests(String name, EsqlConfiguration config) {
118119 @ Before
119120 public void init () {
120121 parser = new EsqlParser ();
121-
122- mapping = loadMapping ("mapping-basic.json" );
123- allFieldRowSize = mapping .values ()
124- .stream ()
125- .mapToInt (
126- f -> (EstimatesRowSize .estimateSize (EsqlDataTypes .widenSmallNumericTypes (f .getDataType ())) + f .getProperties ()
127- .values ()
128- .stream ()
129- // check one more level since the mapping contains TEXT fields with KEYWORD multi-fields
130- .mapToInt (x -> EstimatesRowSize .estimateSize (EsqlDataTypes .widenSmallNumericTypes (x .getDataType ())))
131- .sum ())
132- )
133- .sum ();
134- EsIndex test = new EsIndex ("test" , mapping );
135- IndexResolution getIndexResult = IndexResolution .valid (test );
136122 logicalOptimizer = new LogicalPlanOptimizer (new LogicalOptimizerContext (EsqlTestUtils .TEST_CFG ));
137123 physicalPlanOptimizer = new PhysicalPlanOptimizer (new PhysicalOptimizerContext (config ));
138- FunctionRegistry functionRegistry = new EsqlFunctionRegistry ();
124+ functionRegistry = new EsqlFunctionRegistry ();
139125 mapper = new Mapper (functionRegistry );
140126 var enrichResolution = new EnrichResolution (
141127 Set .of (
@@ -155,11 +141,15 @@ public void init() {
155141 ),
156142 Set .of ("foo" )
157143 );
144+ analyzer = makeAnalyzer ("mapping-basic.json" , enrichResolution );
145+ }
158146
159- analyzer = new Analyzer (
160- new AnalyzerContext (config , functionRegistry , getIndexResult , enrichResolution ),
161- new Verifier (new Metrics ())
162- );
147+ private Analyzer makeAnalyzer (String mappingFileName , EnrichResolution enrichResolution ) {
148+ var mapping = loadMapping (mappingFileName );
149+ EsIndex test = new EsIndex ("test" , mapping );
150+ IndexResolution getIndexResult = IndexResolution .valid (test );
151+
152+ return new Analyzer (new AnalyzerContext (config , functionRegistry , getIndexResult , enrichResolution ), new Verifier (new Metrics ()));
163153 }
164154
165155 /**
@@ -430,6 +420,44 @@ public void testIsNullPushdownFilter() {
430420 assertThat (query .query ().toString (), is (expected .toString ()));
431421 }
432422
423+ /**
424+ * Expects
425+ * LimitExec[500[INTEGER]]
426+ * \_ExchangeExec[[],false]
427+ * \_ProjectExec[[!alias_integer, boolean{f}#4, byte{f}#5, constant_keyword-foo{f}#6, date{f}#7, double{f}#8, float{f}#9,
428+ * half_float{f}#10, integer{f}#12, ip{f}#13, keyword{f}#14, long{f}#15, scaled_float{f}#11, short{f}#17, text{f}#18,
429+ * unsigned_long{f}#16, version{f}#19, wildcard{f}#20]]
430+ * \_FieldExtractExec[!alias_integer, boolean{f}#4, byte{f}#5, constant_k..][]
431+ * \_EsQueryExec[test], query[{"esql_single_value":{"field":"ip","next":{"terms":{"ip":["127.0.0.0/24"],"boost":1.0}},"source":
432+ * "cidr_match(ip, \"127.0.0.0/24\")@1:19"}}][_doc{f}#21], limit[500], sort[] estimatedRowSize[389]
433+ */
434+ public void testCidrMatchPushdownFilter () {
435+ var allTypeMappingAnalyzer = makeAnalyzer ("mapping-ip.json" , new EnrichResolution (Set .of (), Set .of ()));
436+ final String fieldName = "ip_addr" ;
437+
438+ int cidrBlockCount = randomIntBetween (1 , 10 );
439+ ArrayList <String > cidrBlocks = new ArrayList <>();
440+ for (int i = 0 ; i < cidrBlockCount ; i ++) {
441+ cidrBlocks .add (randomCidrBlock ());
442+ }
443+ String cidrBlocksString = cidrBlocks .stream ().map ((s ) -> "\" " + s + "\" " ).collect (Collectors .joining ("," ));
444+ String cidrMatch = format (null , "cidr_match({}, {})" , fieldName , cidrBlocksString );
445+
446+ var query = "from test | where " + cidrMatch ;
447+ var plan = plan (query , EsqlTestUtils .TEST_SEARCH_STATS , allTypeMappingAnalyzer );
448+
449+ var limit = as (plan , LimitExec .class );
450+ var exchange = as (limit .child (), ExchangeExec .class );
451+ var project = as (exchange .child (), ProjectExec .class );
452+ var field = as (project .child (), FieldExtractExec .class );
453+ var queryExec = as (field .child (), EsQueryExec .class );
454+ assertThat (queryExec .limit ().fold (), is (500 ));
455+
456+ var expectedInnerQuery = QueryBuilders .termsQuery (fieldName , cidrBlocks );
457+ var expectedQuery = wrapWithSingleQuery (expectedInnerQuery , fieldName , new Source (1 , 18 , cidrMatch ));
458+ assertThat (queryExec .query ().toString (), is (expectedQuery .toString ()));
459+ }
460+
433461 /**
434462 * Expects
435463 * LimitExec[500[INTEGER]]
@@ -493,7 +521,11 @@ private PhysicalPlan plan(String query) {
493521 }
494522
495523 private PhysicalPlan plan (String query , SearchStats stats ) {
496- var physical = optimizedPlan (physicalPlan (query ), stats );
524+ return plan (query , stats , analyzer );
525+ }
526+
527+ private PhysicalPlan plan (String query , SearchStats stats , Analyzer analyzer ) {
528+ var physical = optimizedPlan (physicalPlan (query , analyzer ), stats );
497529 return physical ;
498530 }
499531
@@ -516,7 +548,7 @@ private PhysicalPlan optimizedPlan(PhysicalPlan plan, SearchStats searchStats) {
516548 return l ;
517549 }
518550
519- private PhysicalPlan physicalPlan (String query ) {
551+ private PhysicalPlan physicalPlan (String query , Analyzer analyzer ) {
520552 var logical = logicalOptimizer .optimize (analyzer .analyze (parser .createStatement (query )));
521553 // System.out.println("Logical\n" + logical);
522554 var physical = mapper .map (logical );
@@ -527,4 +559,13 @@ private PhysicalPlan physicalPlan(String query) {
527559 protected List <String > filteredWarnings () {
528560 return withDefaultLimitWarning (super .filteredWarnings ());
529561 }
562+
563+ private String randomCidrBlock () {
564+ boolean ipv4 = randomBoolean ();
565+
566+ String address = NetworkAddress .format (randomIp (ipv4 ));
567+ int cidrPrefixLength = ipv4 ? randomIntBetween (0 , 32 ) : randomIntBetween (0 , 128 );
568+
569+ return format (null , "{}/{}" , address , cidrPrefixLength );
570+ }
530571}
0 commit comments