@@ -280,10 +280,10 @@ size_t addEqEdge(std::string const& ca, std::string const& cb, bool outer, Verte
280280 return 0 ;
281281}
282282
283- // / `getAngSepFunc ` returns a pointer to the IR node for the `scisql_angSep`
284- // / call embedded in the given value expression if there is one, and null
285- // / otherwise.
286- FuncExpr::Ptr getAngSepFunc (ValueExprPtr const & ve) {
283+ // / `getFunc ` returns a pointer to the IR node for the call to the function
284+ // / with the given name and parameter count embedded in the given value
285+ // / expression if there is one, and null otherwise.
286+ FuncExpr::Ptr getFunc (ValueExprPtr const & ve, std::string const & name, size_t numParams ) {
287287 LOGS (_log, LOG_LVL_TRACE , __FUNCTION__);
288288 FuncExpr::Ptr fe;
289289 if (!ve || ve->getFactorOps ().size () != 1 ) {
@@ -294,12 +294,107 @@ FuncExpr::Ptr getAngSepFunc(ValueExprPtr const& ve) {
294294 return fe;
295295 }
296296 fe = vf->getFuncExpr ();
297- if (!fe || fe->getName () != " scisql_angSep " || fe->params .size () != 4 ) {
297+ if (!fe || fe->getName () != name || fe->params .size () != numParams ) {
298298 return FuncExpr::Ptr ();
299299 }
300300 return fe;
301301}
302302
303+ // / Helper to retrieve a scisql_angSep function from the given value expression
304+ FuncExpr::Ptr getAngSepFunc (ValueExprPtr const & ve) { return getFunc (ve, " scisql_angSep" , 4 ); }
305+
306+ // / Helper to retrieve a scisql_s2PtInCircle function from the given value expression
307+ FuncExpr::Ptr getPtInCircleFunc (ValueExprPtr const & ve) { return getFunc (ve, " scisql_s2PtInCircle" , 5 ); }
308+
309+ /* *
310+ * @brief Get `scisql_angSep` call and numeric threshold in the given comparison predicate.
311+ *
312+ * Handles `scisql_angSep(lon1, lat1, lon2, lat2) OP threshold` in either operand order.
313+ *
314+ * @param cp The comparison predicate to inspect.
315+ * @return std::pair<FuncExpr::Ptr, double> Embedded scisql_angSep() and the threshold it
316+ * is compared against, or {nullptr, NaN} if inadmissable.
317+ * @throws QueryNotEvaluableError if the threshold is a numeric constant but is negative.
318+ */
319+ std::pair<FuncExpr::Ptr, double > getAngSepJoin (CompPredicate const & cp) {
320+ FuncExpr::Ptr fe;
321+ double angSep = std::numeric_limits<double >::quiet_NaN ();
322+ switch (cp.op ) {
323+ case query::CompPredicate::LESS_THAN_OP : // fallthrough
324+ case query::CompPredicate::LESS_THAN_OR_EQUALS_OP :
325+ fe = getAngSepFunc (cp.left );
326+ angSep = cp.right ->getNumericConst ();
327+ break ;
328+ case query::CompPredicate::GREATER_THAN_OP : // fallthrough
329+ case query::CompPredicate::GREATER_THAN_OR_EQUALS_OP :
330+ angSep = cp.left ->getNumericConst ();
331+ fe = getAngSepFunc (cp.right );
332+ break ;
333+ case query::CompPredicate::EQUALS_OP :
334+ case query::CompPredicate::NULL_SAFE_EQUALS_OP :
335+ case query::CompPredicate::NOT_EQUALS_OP :
336+ case query::CompPredicate::NOT_EQUALS_OP_ALT :
337+ // While this doesn't make much sense numerically (floating
338+ // point numbers are being tested for equality), it is
339+ // technically evaluable.
340+ fe = getAngSepFunc (cp.left );
341+ if (!fe) {
342+ angSep = cp.left ->getNumericConst ();
343+ fe = getAngSepFunc (cp.right );
344+ } else {
345+ angSep = cp.right ->getNumericConst ();
346+ }
347+ break ;
348+ default :
349+ throw QueryNotEvaluableError (" Unhandled comparison operator:" + std::to_string (cp.op ));
350+ }
351+
352+ if (fe && angSep < 0.0 ) {
353+ throw QueryNotEvaluableError (
354+ " scisql_angSep() comparison threshold must be a non-negative "
355+ " number, got " +
356+ std::to_string (angSep));
357+ }
358+ return {fe, angSep};
359+ }
360+
361+ /* *
362+ * @brief Get `scisql_s2PtInCircle` call and its radius embedded in the given comparison predicate.
363+ *
364+ * @param cp The comparison predicate to inspect.
365+ * @return std::pair<FuncExpr::Ptr, double> Embedded scisql_s2PtInCircle() and its radius or {nullptr, NaN} if
366+ * the predicate is not admissible or the radius is not a numeric constant.
367+ * @throws QueryNotEvaluableError if the radius is a numeric constant but is negative
368+ */
369+ std::pair<FuncExpr::Ptr, double > getPtInCircleJoin (CompPredicate const & cp) {
370+ auto const notFound = std::make_pair (FuncExpr::Ptr (), std::numeric_limits<double >::quiet_NaN ());
371+ if (cp.op != query::CompPredicate::EQUALS_OP ) return notFound;
372+ FuncExpr::Ptr fe = getPtInCircleFunc (cp.left );
373+ double comparisonValue = std::numeric_limits<double >::quiet_NaN ();
374+ if (fe) {
375+ comparisonValue = cp.right ->getNumericConst ();
376+ } else {
377+ fe = getPtInCircleFunc (cp.right );
378+ if (!fe) {
379+ return notFound;
380+ }
381+ comparisonValue = cp.left ->getNumericConst ();
382+ }
383+
384+ if (comparisonValue != 1.0 ) {
385+ // scisql_s2PtInCircle() only returns 0 or 1, and this is only admissable when = 1.
386+ return notFound;
387+ }
388+
389+ double const radius = fe->params .back ()->getNumericConst ();
390+ if (radius < 0.0 ) {
391+ throw QueryNotEvaluableError (" scisql_s2PtInCircle() radius must be a non-negative number, got " +
392+ std::to_string (radius));
393+ }
394+
395+ return {fe, radius};
396+ }
397+
303398// / `getEqColumnRefs` returns the pair of column references in the equality
304399// / predicate embedded in the given boolean factor. If that is not what
305400// / the given boolean term corresponds to, a pair of nulls is returned instead.
@@ -523,6 +618,7 @@ size_t RelationGraph::_addSpEdges(BoolTerm::Ptr bt) {
523618 }
524619 return numEdges;
525620 }
621+
526622 // Look for a BoolFactor containing a single CompPredicate.
527623 BoolFactor::Ptr bf = std::dynamic_pointer_cast<BoolFactor>(bt);
528624 if (!bf || bf->_terms .size () != 1 ) {
@@ -532,46 +628,20 @@ size_t RelationGraph::_addSpEdges(BoolTerm::Ptr bt) {
532628 if (!cp) {
533629 return 0 ;
534630 }
535- // Try to extract a scisql_angSep() call and a numeric constant
536- // from the comparison predicate.
537- FuncExpr::Ptr fe;
538- double angSep = std::numeric_limits<double >::quiet_NaN ();
539- switch (cp->op ) {
540- case query::CompPredicate::LESS_THAN_OP : // fallthrough
541- case query::CompPredicate::LESS_THAN_OR_EQUALS_OP :
542- fe = getAngSepFunc (cp->left );
543- angSep = cp->right ->getNumericConst ();
544- break ;
545- case query::CompPredicate::GREATER_THAN_OP : // fallthrough
546- case query::CompPredicate::GREATER_THAN_OR_EQUALS_OP :
547- angSep = cp->left ->getNumericConst ();
548- fe = getAngSepFunc (cp->right );
549- break ;
550- case query::CompPredicate::EQUALS_OP :
551- case query::CompPredicate::NULL_SAFE_EQUALS_OP :
552- case query::CompPredicate::NOT_EQUALS_OP :
553- case query::CompPredicate::NOT_EQUALS_OP_ALT :
554- // While this doesn't make much sense numerically (floating
555- // point numbers are being tested for equality), it is
556- // technically evaluable.
557- fe = getAngSepFunc (cp->left );
558- if (!fe) {
559- angSep = cp->left ->getNumericConst ();
560- fe = getAngSepFunc (cp->right );
561- } else {
562- angSep = cp->right ->getNumericConst ();
563- }
564- break ;
565- default :
566- throw QueryNotEvaluableError (" Unhandled comparison operator:" + std::to_string (cp->op ));
567- }
631+
632+ // Try to extract a scisql_angSep() and numeric threshold from the comparison predicate.
633+ std::pair<FuncExpr::Ptr, double > const angSepJoin = getAngSepJoin (*cp);
634+
635+ // If that fails, try equivalent point in circle: scisql_s2PtInCircle(lon1, lat1, lon2, lat2, radius) = 1.
636+ auto const [fe, angSep] = angSepJoin.first ? angSepJoin : getPtInCircleJoin (*cp);
637+
568638 if (!fe || boost::math::isnan (angSep)) {
569- // The scisql_angSep() call and/or numeric constant is missing,
570- // or the comparison operator is invalid (e.g.
571- // "angSep < scisql_angSep(...)")
639+ // Not angSep or PtInCircle; give up
572640 return 0 ;
573641 }
574- // Extract column references from fe
642+
643+ // Extract column references from fe. Both scisql_angSep() and scisql_s2PtInCircle() take two
644+ // coordinate pairs as their first four arguments.
575645 ValueExprPtrVector::const_iterator j = fe->params .begin ();
576646 ColumnRef::Ptr cr[4 ];
577647 Vertex* v[4 ];
0 commit comments