LCOV - code coverage report
Current view: top level - antares-sql/src - compile.rs (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 43 43
Test Date: 2026-09-21 10:31:06 Functions: 100.0 % 3 3

            Line data    Source code
       1              : // SPDX-License-Identifier: EUPL-1.2
       2              : //! AST → (SQL fragment, binds). The structure of every statement comes from
       3              : //! this module; every value a client supplied travels as a bind.
       4              : 
       5              : pub mod geo;
       6              : pub mod q;
       7              : pub mod qprefilter;
       8              : pub mod scope;
       9              : pub mod temporal;
      10              : 
      11              : #[cfg(test)]
      12              : mod tests {
      13              :     use super::{geo, q, scope};
      14              :     use serde_json::json;
      15              : 
      16              :     /// One statement carries several fragments, each numbered from the count
      17              :     /// of binds already collected. Two fragments sharing a `$n` would silently
      18              :     /// compare a jsonpath against a scope regex, so assert the whole statement
      19              :     /// references every placeholder exactly once, densely, from 1.
      20              :     #[test]
      21            2 :     fn fragments_combined_at_an_offset_never_share_a_placeholder() {
      22              :         // $1 is the tenant, as the entity query lays it out
      23            2 :         let mut binds = 1usize;
      24            2 :         let mut sql = vec!["tenant_id = $1".to_owned()];
      25              : 
      26            2 :         let node = antares_ql::parse_q("a==1|b>2").expect("parse");
      27            4 :         let c = q::compile_q(&node, "entity", binds + 1, &|t| t.to_owned()).expect("q compiles");
      28            2 :         binds += c.binds.len();
      29            2 :         sql.push(c.sql);
      30              : 
      31            2 :         let c = scope::compile_scope_q("/A;/B,/C", "scopes", binds + 1).expect("scope compiles");
      32            2 :         binds += c.binds.len();
      33            2 :         sql.push(c.sql);
      34              : 
      35            2 :         let coords = json!([2.29, 48.85]);
      36            2 :         let c = geo::compile_geo(
      37            2 :             &geo::GeoSpec {
      38            2 :                 rel: geo::Rel::Near {
      39            2 :                     max: Some(2000.0),
      40            2 :                     min: Some(500.0),
      41            2 :                 },
      42            2 :                 geometry: "Point",
      43            2 :                 coordinates: &coords,
      44            2 :                 geoproperty_iri: "",
      45            2 :             },
      46            2 :             "location",
      47            2 :             binds + 1,
      48              :         )
      49            2 :         .expect("geo compiles");
      50            2 :         binds += c.geo_binds.len() + c.num_binds.len();
      51            2 :         sql.push(c.sql);
      52              : 
      53            2 :         let statement = sql.join(" AND ");
      54            2 :         let mut seen: Vec<usize> = statement
      55            2 :             .split('$')
      56            2 :             .skip(1)
      57           52 :             .filter_map(|t| {
      58           52 :                 t.chars()
      59           52 :                     .take_while(char::is_ascii_digit)
      60           52 :                     .collect::<String>()
      61           52 :                     .parse()
      62           52 :                     .ok()
      63           52 :             })
      64            2 :             .collect();
      65            2 :         seen.sort_unstable();
      66              :         // a fragment may reference its OWN bind twice (`near` measures from
      67              :         // the same query geometry twice); what must never happen is a second
      68              :         // fragment claiming an index the first already owns, which shows up as
      69              :         // a gap at the top of the range
      70            2 :         seen.dedup();
      71            2 :         assert_eq!(
      72              :             seen,
      73            2 :             (1..=binds).collect::<Vec<_>>(),
      74              :             "placeholders must be dense and unique: {statement}"
      75              :         );
      76            2 :     }
      77              : }
        

Generated by: LCOV version 2.0-1