/* 1 Testing translation of queries [File ~testcases.pl~] As described in Section 5 the optimizer supports a sql- like language to call queries. A primary task is to expand this language with new operators and types introduced by additional algebras. This runs the risk of possible side effects on existing translation rules. Regression tests can help to detect such errors during implementation. 1.1 Testcases Testcases are simple prolog- facts consisting of a unique testcase- number, the input- statement and the translated query- string. ---- testcase(testcaseNumber, inputStatement, expectedQuery). ---- To add a new testcase, just create a new fact, set a new ~TestcaseNumber~ followed by the query in user-level language and the expected result as string. 1.1.1 Regular queries on local relations prerequisite: * simple star select with a single relation */ %fapra 2015/16 testcase(1,select * from roads,'query Roads feed consume '). /* * select with projection and a single relation */ testcase(2,select [osm_id, name] from roads, 'query Roads feed project[Osm_id, Name] consume '). /* * select with projection, single relation and predicate (expectedQuery needs to be validated) */ testcase(3,select [osm_id, name] from roads where name starts "Fahrenbecke", 'query Roads feed filter[(.Name starts "Fahrenbecke")] \c project[Osm_id, Name] consume '). /* 1.1.2 Distributed queries using Distributed2Algebra prerequisite: Roads is distributed by RoadsB1 without index * simple star select with a single relation (no use of dmap-operator) */ testcase(4,select * from roads_d, 'query RoadsDfRandom dsummarize consume '). /* * select with a projection of one attribute and single relation */ testcase(5,select name from roads_d, 'query RoadsDfRandom dmap["", . feed project[Name] ] \c dsummarize consume '). /* * select with a projection of multiple attributes and single relation */ testcase(6,select [name, osm_id] from roads_d, 'query RoadsDfRandom dmap["", . feed project[Name, \c Osm_id] ] dsummarize consume '). /* * simple star select with a single relation and single predicate */ testcase(7,select * from roads_d where name starts "Fahrenbecke", 'query RoadsDfRandom dmap["", . feed filter[(.Name starts \c "Fahrenbecke")] ] dsummarize consume '). /* * count tupel of distributed relation supported by an index */ testcase(8,select count(*) from natural_d where type starts "forest", 'query NaturalDFunctionIndex_Type NaturalDFunctionIndex dmap2\c ["", . .. range["forest", "forest"++], 1238] \c dsummarize count '). % optional: distributed evaluation of aggregations % testcase(8,select count(*) from roads_d where name starts % "Fahrenbecke", % 'query RoadsB2_Name RoadsB2 %dloop2["", . .. range["Fahrenbecke", "Fahrenbecke"++] %count] %getValue tie[. + ..]). /* * spatial query using rtree- index */ testcase(9, select * from buildings_d where geodata intersects eichlinghofen, 'query share("eichlinghofen",TRUE); query BuildingsDSpatialIndex_GeoData \c BuildingsDSpatialIndex dmap2["", . .. \c windowintersects[eichlinghofen ] \c filter[(.GeoData intersects eichlinghofen )] filter[.Original] , 1238] \c dsummarize consume '). /* * equijoin test for both relations are partitioned by join attribute */ testcase(10, select * from [waterways_d as w1, waterways_d as w2] where w1:type=w2:type, 'query WaterwaysDFunction WaterwaysDFunction dmap2["", . \c feed {w1} .. feed {w2} hashjoin[Type_w1, Type_w2, 999997] , 1238] \c dsummarize consume '). /* * Equijoin test for both relations are partitioned, but not by join attribute */ testcase(11, select * from [roads_d as r1, roads_d as r2] where r1:name=r2:name, 'query RoadsDfRandom partitionF["LeftPartOfJoin", . feed , \c hashvalue(..Name,999997) , 0] collect2["L", 1238] RoadsDfRandom \c partitionF["RightPartOfJoin", . feed , hashvalue(..Name,999997) , 0] \c collect2["R", 1238] dmap2["", . feed {r1} .. feed {r2} \c hashjoin[Name_r1, Name_r2, 999997] , 1238] dsummarize consume '). /* * Equijoin test for both are partitoned using modulo */ testcase(12, select * from [places_d as p1, places_d as p2] where p1:population=p2:population, 'query PlacesDfModuloPop PlacesDfModuloPop dmap2["", . \c feed {p1} .. feed {p2} hashjoin[Population_p1, Population_p2, \c 999997] , 1238] dsummarize consume '). /* * Equijoin test for one relation is partitioned and the other is replicated */ testcase(13, select * from [places_d as p1, railways_d as r2] where p1:osm_id=r2:osm_id, 'query PlacesDfModuloPop dmap["", \c Railways feed {p1} . feed {r2} hashjoin[Osm_id_p1, Osm_id_r2, \c 999997] ] dsummarize consume '). /* * Equijoin test for both relations are replicated (Error case) */ testcase(14, select * from [railways_d as b1, railways_d as b2] where b1:osm_id=b2:osm_id, 'failed'). /* * Standard Join for both are partitioned (Error case) */ testcase(15, select * from [waterways_d as w1, waterways_d as w2] where w1:type#w2:type, 'failed.'). /* * Standard Join for one relation is partitioned and the other is replicated */ testcase(17, select * from [places_d as p1, railways_d as b2] where substr(p1:osm_id,1,3)=substr(b2:osm_id,1,3), 'query PlacesDfModuloPop dmap["", . \c feed {b2} Railways feed {p1} product \c filter[(substr(.Osm_id_p1, 1, 3) = \c substr(.Osm_id_b2, 1, 3))] ] dsummarize consume '). /* * Standard Join test for both relations are replicated (Error case) */ testcase(18, select * from [railways_d as b1, railways_d as b2] where substr(b1:osm_id,1,3)=substr(b2:osm_id,1,3), 'failed.'). /* * distributed spatial query using a database object in predicate */ testcase(19,select * from roads_d where geodata intersects eichlinghofen, 'query share("eichlinghofen",TRUE); query RoadsDfRandom \c dmap["", . feed filter[(.GeoData intersects eichlinghofen \c )] ] dsummarize consume '). /* * local spatial query using a database object in predicate */ testcase(20,select * from roads where geodata intersects eichlinghofen, 'query Roads feed filter[(.GeoData intersects eichlinghofen )\c ] consume '). /* * star select with projection and renamed attributes (doesn't work for non-distributed queries in basic- optimizer) */ testcase(21,select r1:name from roads_d as r1, 'query RoadsDfRandom dmap["", . feed {r1} \c project[Name_r1] ] dsummarize consume '). /* * simple select with count function */ testcase(22,select count(*) from roads_d, 'query RoadsDfRandom dsummarize count '). /* * merge filtrations with rename */ testcase(23,select * from buildings_d as x where [x:name="ho",x:name="hi"], 'query BuildingsDSpatialIndex dmap["", . feed \c filter[.Original] ] dmap["", . feed {x} \c filter[(.Name_x = "hi")] filter[(.Name_x = "ho")] ] \c dsummarize consume '). /* * merge filtrations without rename */ testcase(24,select * from buildings_d where [name="ho",name="hi"], 'query BuildingsDSpatialIndex dmap["", . feed filter[.Original] ] \c dmap["", . feed filter[(.Name = "hi")] \c filter[(.Name = "ho")] ] dsummarize consume '). /* * spatial join, both arguments distributed by join attribute */ testcase(25,select * from [buildings_d as x, buildings_d as y] where x:geodata intersects y:geodata, 'query BuildingsDSpatialIndex BuildingsDSpatialIndex \c dmap2["", . feed {x} .. feed {y} \c itSpatialJoin[GeoData_x, GeoData_y] filter[(.Cell_x = .Cell_y)] \c filter[gridintersects(grid,bbox(.GeoData_x) , \c bbox(.GeoData_y) , .Cell_x) ] \c filter[(.GeoData_x intersects .GeoData_y)] , \c 1238] dsummarize consume '). /* * spatial join, one argument replicated, one argument distributed by join attribute */ testcase(26, select * from [buildings_d as x, railways_d as y] where x:geodata intersects y:geodata, 'query BuildingsDSpatialIndex dmap["", . feed {x} \c Railways feed {y} itSpatialJoin[GeoData_x, GeoData_y] \c filter[(.Cell_x = .Cell_y)] \c filter[gridintersects(. ,bbox(.GeoData_x) , \c bbox(.GeoData_y) , .Cell_x) ] \c filter[(.GeoData_x intersects .GeoData_y)] ] \c dsummarize consume '). /* * spatial join, one argument distributed by join attribute, one argument distributed by different attribute */ testcase(27, select * from [roads_d as x, waterways_d as y] where x:geodata intersects y:geodata, 'query RoadsDfSpatial WaterwaysDFunction \c partitionF["", . feed \c extendstream(Cell: cellnumber(bbox(.GeoData) ,grid) ), ..Cell, \c 0] collect2["", 1238] dmap2["", . \c feed {x} .. feed {y} \c itSpatialJoin[GeoData_x, GeoData_y] \c filter[(.Cell_x = .Cell_y)] \c filter[gridintersects(grid,bbox(.GeoData_x) , \c bbox(.GeoData_y) , .Cell_x) ] filter[(.GeoData_x intersects \c .GeoData_y)] , 1238] dsummarize consume '). /* * spatial join, both arguments distributed by non-join attributes */ testcase(28, select * from [natural_d as x, waterways_d as y] where x:geodata intersects y:geodata, 'query NaturalDFunctionIndex partitionF["", . feed \c extendstream(Cell: cellnumber(bbox(.GeoData) ,grid) ), ..Cell, 0] \c WaterwaysDFunction partitionF["", . feed \c extendstream(Cell: cellnumber(bbox(.GeoData) ,grid) ), ..Cell, 0] \c areduce2[[], . feed {x} .. feed {y} itSpatialJoin[GeoData_x, GeoData_y] \c filter[(.Cell_x = .Cell_y)] filter[gridintersects(grid,bbox(.GeoData_x) , \c bbox(.GeoData_y) , .Cell_x) ] \c filter[(.GeoData_x intersects .GeoData_y)] , 1238] \c dsummarize consume '). /* * spatial join, one argument replicated, one argument distributed by non-join attribute */ testcase(29, select * from [natural_d as x, railways_d as y] where x:geodata intersects y:geodata, 'query NaturalDFunctionIndex dmap["", . feed {x} Railways feed {y} \c itSpatialJoin[GeoData_x, GeoData_y] filter[(.Cell_x = .Cell_y)] \c filter[gridintersects(. ,bbox(.GeoData_x) , \c bbox(.GeoData_y) , .Cell_x) ] \c filter[(.GeoData_x intersects .GeoData_y)] ] dsummarize consume '). /* * spatial join, two replicated arguments - expected fail */ testcase(30, select * from [railways_d as x, railways_d as y] where x:geodata intersects y:geodata, 'failed.'). /* * spatial join combined with simple selection */ testcase(31, select * from [roads_d as x, waterways_d as y] where [x:geodata intersects y:geodata, x:name = "Secondo <3"], 'query RoadsDfSpatial WaterwaysDFunction \c partitionF["", . feed extendstream(Cell: \c cellnumber(bbox(.GeoData) ,grid) ), ..Cell, 0] \c collect2["", 1238] dmap2["", . feed {x} .. feed {y} \c itSpatialJoin[GeoData_x, GeoData_y] \c filter[(.Cell_x = .Cell_y)] \c filter[gridintersects(grid,bbox(.GeoData_x) , bbox(.GeoData_y) \c , .Cell_x) ] filter[(.GeoData_x intersects \c .GeoData_y)] , 1238] dmap["", . \c feed {x} filter[(.Name_x = "Secondo <3")] ] \c dsummarize consume '). /* 1.2 Run Tests Once the testcases are defined, it is possible to evaluate it automatically by calling optimize on the ~inputStatement~ and comparing the output with the ~expectedQuery~. To run all available testcases simple call the rule checkAllTestCases. */ checkAllTestCases :- findall(TestCaseNo,testcase(TestCaseNo,_,_),ListOfTestCaseNo), checkTestCase(ListOfTestCaseNo),!. checkTestCase([H|T]) :- ansi_format([], 'Testing case ~w-------------\n', [H]), checkTestCase(H), write('\n\n'), checkTestCase(T). checkTestCase([]). /* The rule checkTestCase(~TestcaseNumber~) will only execute the testcase assigned to the ~testcaseNumber~. */ checkTestCase(NumTestCase) :- testcase(NumTestCase,SqlQuery,ExpectedQueryStr), optimize(SqlQuery,TranslatedQuery, _),!, string_to_atom(ExpectedQueryStr,ExpectedQuery), compareTestCase(ExpectedQuery,TranslatedQuery,NumTestCase), !. checkTestCase(NumTestCase) :- testcase(NumTestCase,_,ExpectedQueryStr), ansi_format([fg(red)], 'testcase ~w failed.\nExpected:\n', [NumTestCase]), ansi_format([], '~w' , [ExpectedQueryStr]), !. checkTestCase(NumTestCase) :- ansi_format([fg(red)], 'testcase with number ~w not found', [NumTestCase]), !. compareTestCase(ExpectedQuery,TranslatedQuery,NumTestCase) :- is_list(TranslatedQuery), atomic_list_concat(TranslatedQuery,'; query ',TransQueryAtom), compareTestCase(ExpectedQuery,TransQueryAtom,NumTestCase), !. compareTestCase(ExpectedQuery,TranslatedQuery,NumTestCase) :- atom_concat('query ', TranslatedQuery, TranslatedQueryStr), (TranslatedQueryStr == ExpectedQuery -> ansi_format([fg(green)], 'testcase ~w succeeded \n', [NumTestCase]); ansi_format([fg(red)], 'testcase ~w failed.\nExpected:\n', [NumTestCase]), ansi_format([], '>>~w<<' , [ExpectedQuery]), ansi_format([fg(red)], '\n but got:\n', []), ansi_format([], '>>query ~w<<', [TranslatedQuery])), !. %end fapra 2015/16