Skip to content

Commit ecc1ec4

Browse files
committed
Add spatial join and area restrictor tests
1 parent 0d3acda commit ecc1ec4

1 file changed

Lines changed: 260 additions & 0 deletions

File tree

src/qproc/testQueryAnaGeneral.cc

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ static const std::vector<ScisqlRestrictorTestCaseData> SCISQL_RESTRICTOR_TEST_CA
328328
"AND `o`.`objectIdObjTest`=`s`.`objectIdSourceTest`",
329329
std::make_shared<AreaRestrictorCircle>("1", "1", "1.3")),
330330

331+
// "<=>" (null-safe equality) is equivalent to "=" here since scisql_s2PtInCircle() never
332+
// evaluates to NULL.
333+
ScisqlRestrictorTestCaseData("select * from LSST.Object o, Source s "
334+
"WHERE scisql_s2PtInCircle(o.ra_Test, o.decl_Test, 1, 1, 1.3) <=> 1 "
335+
"AND o.objectIdObjTest = s.objectIdSourceTest;",
336+
"SELECT * FROM `LSST`.`Object_100` AS `o`,`LSST`.`Source_100` AS `s` "
337+
"WHERE scisql_s2PtInCircle(`o`.`ra_Test`,`o`.`decl_Test`,1,1,1.3)<=>1 "
338+
"AND `o`.`objectIdObjTest`=`s`.`objectIdSourceTest`",
339+
std::make_shared<AreaRestrictorCircle>("1", "1", "1.3")),
340+
331341
ScisqlRestrictorTestCaseData(
332342
"select * from LSST.Object o, Source s "
333343
"WHERE scisql_s2PtInEllipse(ra_Test, decl_Test, 1.2, 3.2, 2500, 1500, 0.2) = 1 "
@@ -633,6 +643,256 @@ BOOST_AUTO_TEST_CASE(ObjectSelfJoinDistance) {
633643
BOOST_CHECK_EQUAL(actual, expected);
634644
}
635645

646+
BOOST_AUTO_TEST_CASE(ObjectSelfJoinPtInCircle) {
647+
// A spatial self-join expressed with point-in-circle syntax is equivalent to a scisql_angSep()
648+
// comparison and should be recognized as a near-neighbor join.
649+
std::string stmt =
650+
"select count(*) from LSST.Object o1,LSST.Object o2 "
651+
"WHERE qserv_areaspec_box(5.5, 5.5, 6.1, 6.1) AND "
652+
"scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) = 1";
653+
std::string expected =
654+
"SELECT count(*) AS `QS1_COUNT` "
655+
"FROM `Subchunks_LSST_100`.`Object_100_%S\007S%` AS `o1`,"
656+
"`Subchunks_LSST_100`.`Object_100_%S\007S%` AS `o2` "
657+
"WHERE scisql_s2PtInBox(`o1`.`ra_Test`,`o1`.`decl_Test`,5.5,5.5,6.1,6.1)=1 "
658+
"AND scisql_s2PtInBox(`o2`.`ra_Test`,`o2`.`decl_Test`,5.5,5.5,6.1,6.1)=1 "
659+
"AND scisql_s2PtInCircle(`o1`.`ra_Test`,`o1`.`decl_Test`,"
660+
"`o2`.`ra_Test`,`o2`.`decl_Test`,0.02)=1";
661+
qsTest.sqlConfig =
662+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
663+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
664+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
665+
BOOST_CHECK(context);
666+
BOOST_CHECK(nullptr == context->secIdxRestrictors);
667+
BOOST_CHECK(nullptr != context->areaRestrictors);
668+
BOOST_CHECK(context->hasChunks());
669+
BOOST_CHECK(context->hasSubChunks());
670+
BOOST_CHECK(context->needsMerge);
671+
std::string actual2 = queryAnaHelper.buildFirstParallelQuery();
672+
BOOST_CHECK_EQUAL(actual2, expected);
673+
}
674+
675+
BOOST_AUTO_TEST_CASE(PtInCircleJoinForms) {
676+
// scisql_s2PtInCircle() call may appear on either side of "=" or "<=>"
677+
std::vector<std::string> const predicates = {
678+
"scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) = 1",
679+
"1 = scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02)",
680+
"scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) <=> 1",
681+
"1 <=> scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02)"};
682+
for (auto const& predicate : predicates) {
683+
std::string stmt = "select count(*) from LSST.Object o1,LSST.Object o2 WHERE " + predicate;
684+
qsTest.sqlConfig =
685+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
686+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
687+
BOOST_CHECK_EQUAL(qs->getError(), "");
688+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
689+
BOOST_CHECK(context);
690+
BOOST_CHECK(context->hasChunks());
691+
BOOST_CHECK(context->hasSubChunks());
692+
}
693+
}
694+
695+
BOOST_AUTO_TEST_CASE(PtInCircleJoinUnsupportedOperators) {
696+
// scisql_s2PtInCircle() only ever returns 0 or 1, so only plain equality against 1 is recognized as a
697+
// join predicate
698+
std::vector<std::string> const predicates = {
699+
"scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) != 0",
700+
"0 < scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02)"};
701+
for (auto const& predicate : predicates) {
702+
std::string stmt = "select count(*) from LSST.Object o1,LSST.Object o2 WHERE " + predicate;
703+
qsTest.sqlConfig =
704+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
705+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
706+
BOOST_CHECK_EQUAL(qs->getError(), NOT_EVALUABLE_MSG);
707+
}
708+
}
709+
710+
BOOST_AUTO_TEST_CASE(PtInCircleAntiJoin) {
711+
// A point outside the circle selects distant pairs, which cannot be evaluated using partition-local data.
712+
std::string stmt =
713+
"select count(*) from LSST.Object o1,LSST.Object o2 "
714+
"WHERE scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) = 0";
715+
qsTest.sqlConfig =
716+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
717+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
718+
BOOST_CHECK_EQUAL(qs->getError(), NOT_EVALUABLE_MSG);
719+
}
720+
721+
BOOST_AUTO_TEST_CASE(NegatedPtInCircleJoinNotMisread) {
722+
// Similar to PtInCircleAntiJoin, but with NOT.
723+
std::string stmt =
724+
"select count(*) from LSST.Object o1,LSST.Object o2 "
725+
"WHERE NOT scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) = 1";
726+
qsTest.sqlConfig =
727+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
728+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
729+
BOOST_CHECK_EQUAL(qs->getError(), NOT_EVALUABLE_MSG);
730+
}
731+
732+
BOOST_AUTO_TEST_CASE(PtInCircleJoinWithConeRestrictor) {
733+
// Join AND a spatial restrictor
734+
std::string stmt =
735+
"select count(*) from LSST.Object o1,LSST.Object o2 "
736+
"WHERE scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,6,6,0.3) = 1 AND "
737+
"scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,0.02) = 1";
738+
qsTest.sqlConfig =
739+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
740+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
741+
BOOST_CHECK_EQUAL(qs->getError(), "");
742+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
743+
BOOST_CHECK(context);
744+
BOOST_REQUIRE(context->areaRestrictors);
745+
BOOST_CHECK_EQUAL(context->areaRestrictors->size(), 1U);
746+
BOOST_REQUIRE(context->areaRestrictors->front());
747+
BOOST_CHECK_EQUAL(*context->areaRestrictors->front(), AreaRestrictorCircle("6", "6", "0.3"));
748+
BOOST_CHECK(context->hasChunks());
749+
BOOST_CHECK(context->hasSubChunks());
750+
}
751+
752+
BOOST_AUTO_TEST_CASE(AngSepAsConeRestrictor) {
753+
// scisql_angSep() with a constant center, bounded above, is equivalent to scisql_s2PtInCircle() = 1
754+
std::vector<std::string> const predicates = {
755+
"scisql_angSep(ra_PS, decl_PS, 1.5, 3) < 0.1", "scisql_angSep(ra_PS, decl_PS, 1.5, 3) <= 0.1",
756+
"0.1 > scisql_angSep(ra_PS, decl_PS, 1.5, 3)", "scisql_angSep(1.5, 3, ra_PS, decl_PS) < 0.1",
757+
"scisql_angSep(ra_PS, decl_PS, 1.5, 3) = 0.1", "scisql_angSep(ra_PS, decl_PS, 1.5, 3) <=> 0.1"};
758+
for (auto const& predicate : predicates) {
759+
std::string stmt = "select * from Object where " + predicate;
760+
qsTest.sqlConfig =
761+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_PS", "decl_PS"}}}}}));
762+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
763+
BOOST_CHECK_EQUAL(qs->getError(), "");
764+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
765+
BOOST_CHECK(context);
766+
BOOST_REQUIRE(context->areaRestrictors);
767+
BOOST_CHECK_EQUAL(context->areaRestrictors->size(), 1U);
768+
BOOST_REQUIRE(context->areaRestrictors->front());
769+
BOOST_CHECK_EQUAL(*context->areaRestrictors->front(), AreaRestrictorCircle("1.5", "3", "0.1"));
770+
BOOST_CHECK(context->hasChunks());
771+
BOOST_CHECK(!context->hasSubChunks());
772+
}
773+
}
774+
775+
BOOST_AUTO_TEST_CASE(AngSepLowerBoundNoRestrictor) {
776+
// Bounding scisql_angSep() from below selects points outside the cone; no restrictor produced
777+
std::string stmt = "select * from Object where scisql_angSep(ra_PS, decl_PS, 1.5, 3) > 0.1";
778+
qsTest.sqlConfig =
779+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_PS", "decl_PS"}}}}}));
780+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
781+
BOOST_CHECK_EQUAL(qs->getError(), "");
782+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
783+
BOOST_CHECK(context);
784+
BOOST_CHECK(nullptr == context->areaRestrictors);
785+
BOOST_CHECK(context->hasChunks());
786+
}
787+
788+
BOOST_AUTO_TEST_CASE(NegativeAngSepConeThresholdRejected) {
789+
// A negative scisql_angSep() threshold can never be satisfied, so should error
790+
std::string stmt = "select * from Object where scisql_angSep(ra_PS, decl_PS, 1.5, 3) < -0.1";
791+
qsTest.sqlConfig =
792+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_PS", "decl_PS"}}}}}));
793+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
794+
BOOST_CHECK_EQUAL(qs->getError(),
795+
"AnalysisError:scisql_angSep() comparison threshold must be a non-negative "
796+
"number, got -0.100000");
797+
}
798+
799+
BOOST_AUTO_TEST_CASE(AngSepConeRestrictorWithAngSepJoin) {
800+
// Cone restrictor AND a spatial join, both via scisql_angSep()
801+
std::string stmt =
802+
"select count(*) from LSST.Object o1,LSST.Object o2 "
803+
"WHERE scisql_angSep(o1.ra_Test, o1.decl_Test, 6, 6) < 0.3 AND "
804+
"scisql_angSep(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test) < 0.02";
805+
qsTest.sqlConfig =
806+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
807+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
808+
BOOST_CHECK_EQUAL(qs->getError(), "");
809+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
810+
BOOST_CHECK(context);
811+
BOOST_REQUIRE(context->areaRestrictors);
812+
BOOST_CHECK_EQUAL(context->areaRestrictors->size(), 1U);
813+
BOOST_REQUIRE(context->areaRestrictors->front());
814+
BOOST_CHECK_EQUAL(*context->areaRestrictors->front(), AreaRestrictorCircle("6", "6", "0.3"));
815+
BOOST_CHECK(context->hasChunks());
816+
BOOST_CHECK(context->hasSubChunks());
817+
}
818+
819+
BOOST_AUTO_TEST_CASE(UnrelatedNegativeConstantNotMistakenForAngSep) {
820+
// A "col < negative constant" predicate unrelated to scisql_angSep() must not be rejected
821+
std::string stmt = "select * from LSST.Object o1 WHERE o1.mag < -5";
822+
qsTest.sqlConfig = SqlConfig(
823+
SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test", "mag"}}}}}));
824+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
825+
BOOST_CHECK_EQUAL(qs->getError(), "");
826+
}
827+
828+
BOOST_AUTO_TEST_CASE(NegativeAngSepThresholdRejected) {
829+
// A negative scisql_angSep() threshold can never be satisfied, so it should error
830+
std::string stmt =
831+
"select * from LSST.Object o, Source s "
832+
"WHERE o.objectIdObjTest = s.objectIdSourceTest AND "
833+
"scisql_angSep(o.ra_Test,o.decl_Test,s.ra_Test,s.decl_Test) < -0.5;";
834+
qsTest.sqlConfig = SqlConfig(
835+
SqlConfig::MockDbTableColumns({{"LSST",
836+
{{"Object", {"objectIdObjTest", "ra_Test", "decl_Test"}},
837+
{"Source", {"objectIdSourceTest", "ra_Test", "decl_Test"}}}}}));
838+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
839+
BOOST_CHECK_EQUAL(qs->getError(),
840+
"AnalysisError:scisql_angSep() comparison threshold must be a non-negative "
841+
"number, got -0.500000");
842+
}
843+
844+
BOOST_AUTO_TEST_CASE(NegativePtInCircleRadiusRejected) {
845+
// A negative scisql_s2PtInCircle() radius can never be satisfied, so it should error
846+
std::string stmt =
847+
"select count(*) from LSST.Object o1,LSST.Object o2 "
848+
"WHERE scisql_s2PtInCircle(o1.ra_Test,o1.decl_Test,o2.ra_Test,o2.decl_Test,-0.5) = 1";
849+
qsTest.sqlConfig =
850+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
851+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
852+
BOOST_CHECK_EQUAL(qs->getError(),
853+
"AnalysisError:scisql_s2PtInCircle() radius must be a non-negative number, "
854+
"got -0.500000");
855+
}
856+
857+
BOOST_AUTO_TEST_CASE(NegativePtInCircleRestrictorRadiusRejected) {
858+
// Same as NegativePtInCircleRadiusRejected, but for the single-table restrictor form
859+
std::string stmt =
860+
"select * from LSST.Object o WHERE scisql_s2PtInCircle(o.ra_Test, o.decl_Test, 1, 1, -0.5) = 1";
861+
qsTest.sqlConfig =
862+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
863+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt, true);
864+
BOOST_CHECK_EQUAL(qs->getError(),
865+
"AnalysisError:scisql_s2PtInCircle() radius must be a non-negative number, "
866+
"got -0.500000");
867+
}
868+
869+
BOOST_AUTO_TEST_CASE(NegatedPtInCircleRestrictorNotMisread) {
870+
// "NOT scisql_s2PtInCircle(...) = 1" describes everything OUTSIDE the circle, which is not a bounded area
871+
// constraint
872+
std::string stmt =
873+
"select * from LSST.Object o "
874+
"WHERE NOT scisql_s2PtInCircle(o.ra_Test, o.decl_Test, 1, 1, 1.3) = 1";
875+
qsTest.sqlConfig =
876+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_Test", "decl_Test"}}}}}));
877+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
878+
BOOST_CHECK_EQUAL(qs->getError(), "");
879+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
880+
BOOST_CHECK(context);
881+
BOOST_CHECK(nullptr == context->areaRestrictors);
882+
}
883+
884+
BOOST_AUTO_TEST_CASE(NegatedAngSepRestrictorNotMisread) {
885+
// Same as above, for the scisql_angSep() as restrictor
886+
std::string stmt = "select * from Object where NOT scisql_angSep(ra_PS, decl_PS, 1.5, 3) < 0.1";
887+
qsTest.sqlConfig =
888+
SqlConfig(SqlConfig::MockDbTableColumns({{"LSST", {{"Object", {"ra_PS", "decl_PS"}}}}}));
889+
std::shared_ptr<QuerySession> qs = queryAnaHelper.buildQuerySession(qsTest, stmt);
890+
BOOST_CHECK_EQUAL(qs->getError(), "");
891+
std::shared_ptr<QueryContext> context = qs->dbgGetContext();
892+
BOOST_CHECK(context);
893+
BOOST_CHECK(nullptr == context->areaRestrictors);
894+
}
895+
636896
BOOST_AUTO_TEST_CASE(SelfJoinAliased) {
637897
// o2.ra_PS and o2.ra_PS_Sigma have to be aliased in order to produce
638898
// a result that can't be stored in a table as-is.

0 commit comments

Comments
 (0)