3939import java .util .regex .Pattern ;
4040
4141import static com .facebook .presto .execution .QueryState .RUNNING ;
42+ import static com .facebook .presto .plugin .clp .ClpQueryRunner .createConfigFile ;
4243import static com .facebook .presto .plugin .clp .ClpQueryRunner .createQueryRunner ;
44+ import static com .facebook .presto .plugin .clp .ClpQueryRunner .deleteConfigFile ;
4345import static com .facebook .presto .plugin .clp .metadata .ClpSchemaTreeNodeType .Boolean ;
4446import static com .facebook .presto .plugin .clp .metadata .ClpSchemaTreeNodeType .DateString ;
4547import static com .facebook .presto .plugin .clp .metadata .ClpSchemaTreeNodeType .Float ;
@@ -64,11 +66,13 @@ public class TestClpComputePushDown
6466 private DistributedQueryRunner queryRunner ;
6567 private SqlQueryManager queryManager ;
6668 private DispatchManager dispatchManager ;
69+ private String splitFilterConfigFilePath ;
6770
6871 @ BeforeClass
6972 public void setUp ()
7073 throws Exception
7174 {
75+ // Set up metadata database
7276 mockMetadataDatabase = ClpMockMetadataDatabase
7377 .builder ()
7478 .build ();
@@ -92,11 +96,29 @@ public void setUp()
9296 Float ,
9397 Boolean ,
9498 DateString ))));
99+ // Set up split filter config
100+ String splitFilterConfigJsonString = "{\n " +
101+ " \" clp\" : [\n " +
102+ " {\n " +
103+ " \" columnName\" : \" fare\" ,\n " +
104+ " \" customOptions\" : {\n " +
105+ " \" rangeMapping\" : {\n " +
106+ " \" lowerBound\" : \" fare_lb\" ,\n " +
107+ " \" upperBound\" : \" fare_ub\" \n " +
108+ " }\n " +
109+ " },\n " +
110+ " \" required\" : false\n " +
111+ " }\n " +
112+ " ]\n " +
113+ "}" ;
114+ splitFilterConfigFilePath = createConfigFile (splitFilterConfigJsonString );
95115 queryRunner = createQueryRunner (
96116 mockMetadataDatabase .getUrl (),
97117 mockMetadataDatabase .getUsername (),
98118 mockMetadataDatabase .getPassword (),
99119 mockMetadataDatabase .getTablePrefix (),
120+ Optional .empty (),
121+ Optional .of (splitFilterConfigFilePath ),
100122 Optional .of (0 ),
101123 Optional .empty ());
102124 queryManager = (SqlQueryManager ) queryRunner .getCoordinator ().getQueryManager ();
@@ -117,6 +139,7 @@ public void tearDown()
117139 if (null != mockMetadataDatabase ) {
118140 mockMetadataDatabase .teardown ();
119141 }
142+ deleteConfigFile (splitFilterConfigFilePath );
120143 }
121144
122145 @ Test
@@ -284,6 +307,43 @@ public void testComplexPushDown()
284307 "lower(city.Region.Name) = 'hello world' OR city.Name IS NULL" );
285308 }
286309
310+ @ Test
311+ public void testSplitFilterPushDown ()
312+ {
313+ // Normal case
314+ testPushDown (
315+ "(fare > 0 AND city.Name like 'b%')" ,
316+ "(fare > 0.0 AND city.Name: \" b*\" )" ,
317+ null ,
318+ "(fare_ub > 0.0)" );
319+
320+ // With BETWEEN
321+ testPushDown (
322+ "((fare BETWEEN 0 AND 5) AND city.Name like 'b%')" ,
323+ "(fare >= 0.0 AND fare <= 5.0 AND city.Name: \" b*\" )" ,
324+ null ,
325+ "(fare_ub >= 0.0 AND fare_lb <= 5.0)" );
326+
327+ // The cases of that the metadata filter column exist but cannot be push down
328+ testPushDown (
329+ "(fare > 0 OR city.Name like 'b%')" ,
330+ "(fare > 0.0 OR city.Name: \" b*\" )" ,
331+ null ,
332+ null );
333+ testPushDown (
334+ "(fare > 0 AND city.Name like 'b%') OR city.Region.Id = 1" ,
335+ "((city.Region.id: 1 OR fare > 0.0) AND (city.Region.id: 1 OR city.Name: \" b*\" ))" ,
336+ null ,
337+ null );
338+
339+ // Complicated case
340+ testPushDown (
341+ "fare = 0 AND (city.Name like 'b%' OR city.Region.Id = 1)" ,
342+ "(fare: 0.0 AND (city.Name: \" b*\" OR city.Region.id: 1))" ,
343+ null ,
344+ "((fare_lb <= 0.0 AND fare_ub >= 0.0))" );
345+ }
346+
287347 @ Test
288348 public void testClpWildcardUdf ()
289349 {
@@ -313,12 +373,23 @@ public void testClpWildcardUdf()
313373 }
314374
315375 private void testPushDown (String filter , String expectedPushDown , String expectedRemaining )
376+ {
377+ testPushDown (filter , expectedPushDown , expectedRemaining , true , null );
378+ }
379+
380+ private void testPushDown (String filter , String expectedPushDown , String expectedRemaining , String expectedSplitFilterPushDown )
381+ {
382+ testPushDown (filter , expectedPushDown , expectedRemaining , false , expectedSplitFilterPushDown );
383+ }
384+
385+ private void testPushDown (String filter , String expectedPushDown , String expectedRemaining , boolean ignoreSplitFilterPushDown , String expectedSplitFilterPushDown )
316386 {
317387 try {
318388 // We first execute a query using the original filter and look for the FilterNode (for remaining expression)
319389 // and TableScanNode (for KQL pushdown and split filter pushdown)
320390 QueryId originalQueryId = createAndPlanQuery (filter );
321391 String actualPushDown = null ;
392+ String actualSplitFilterPushDown = null ;
322393 RowExpression actualRemainingExpression = null ;
323394 Plan originalQueryPlan = queryManager .getQueryPlan (originalQueryId );
324395 for (Map .Entry <PlanNodeId , PlanNode > entry : originalQueryPlan .getPlanIdNodeMap ().entrySet ()) {
@@ -330,9 +401,13 @@ private void testPushDown(String filter, String expectedPushDown, String expecte
330401 clpTableLayoutHandle = tryGetClpTableLayoutHandleFromTableScanNode (entry .getValue ());
331402 if (clpTableLayoutHandle != null && actualPushDown == null ) {
332403 actualPushDown = clpTableLayoutHandle .getKqlQuery ().orElse (null );
404+ actualSplitFilterPushDown = clpTableLayoutHandle .getMetadataSql ().orElse (null );
333405 }
334406 }
335407 assertEquals (actualPushDown , expectedPushDown );
408+ if (!ignoreSplitFilterPushDown ) {
409+ assertEquals (actualSplitFilterPushDown , expectedSplitFilterPushDown );
410+ }
336411 if (expectedRemaining != null ) {
337412 assertNotNull (actualRemainingExpression );
338413 // Since the remaining expression cannot be simply compared by given String, we have to first convert
0 commit comments