1#![warn(missing_docs)]
4
5use crate::{CRS, Expression, MyError, QString, config::config, ds::DataSource, expr::E, op::Op};
10use sqlx::{AssertSqlSafe, FromRow, Pool, Sqlite, pool::PoolOptions, sqlite::SqliteConnectOptions};
11use std::{cmp::Ordering, str::FromStr};
12use tracing::info;
13use unicode_normalization::{UnicodeNormalization, char::is_combining_mark};
14
15const GPKG_APPLICATION_ID: i32 = 0x47504B47;
16const FIND_TABLE: &str = "SELECT * FROM gpkg_contents WHERE table_name = $1";
17const FIND_SRS: &str = "SELECT * FROM gpkg_spatial_ref_sys WHERE srid = $1";
18const EPSG_AUTH: &str = "EPSG";
19
20const CQL2_CI: &str = "CQL2_CI";
22const CQL2_AI: &str = "CQL2_AI";
24const CQL2_CAI: &str = "CQL2_CI_AI";
26const CQL2_BOUNDS: &str = "CQL2_BOUNDS";
28
29#[derive(Debug, FromRow)]
31struct Pragma(String);
32
33#[derive(Debug, FromRow)]
36struct RowID(i32);
37
38#[derive(Debug, FromRow)]
40struct TSpatialRefSys {
41 organization: String,
42 organization_coordsys_id: i32,
43}
44
45#[allow(dead_code)]
47#[derive(Debug, FromRow)]
48pub(crate) struct TContents {
49 table_name: String,
50 data_type: String,
51 srs_id: Option<i32>,
52}
53
54#[derive(Debug)]
59#[allow(dead_code)]
60pub struct GPkgDataSource {
61 layer: String,
62 pool: Pool<Sqlite>,
63 srid: Option<u32>,
64}
65
66impl DataSource for GPkgDataSource {}
67
68impl GPkgDataSource {
69 pub async fn from(gpkg_url: &str, layer_name: &str) -> Result<Self, MyError> {
71 let collate_ci = |a: &str, b: &str| cmp_ci(a, b);
77
78 let collate_ai = |a: &str, b: &str| cmp_ai(a, b);
80
81 let collate_aci = |a: &str, b: &str| cmp_aci(a, b);
83
84 let collate_bounds = |a: &str, b: &str| cmp_bounds(a, b);
86
87 let pool_opts = unsafe {
91 SqliteConnectOptions::from_str(gpkg_url)?
92 .extension("mod_spatialite")
93 .collation(CQL2_CI, collate_ci)
94 .collation(CQL2_AI, collate_ai)
95 .collation(CQL2_CAI, collate_aci)
96 .collation(CQL2_BOUNDS, collate_bounds)
97 };
98
99 let pool = PoolOptions::new().connect_with(pool_opts).await?;
100 let pragma = sqlx::query_as::<_, RowID>("PRAGMA application_id")
103 .fetch_one(&pool)
104 .await?;
105 let application_id = pragma.0;
106 if application_id != GPKG_APPLICATION_ID {
107 return Err(MyError::Runtime("Unexpected application_id".into()));
108 }
109
110 let pragma = sqlx::query_as::<_, Pragma>("PRAGMA integrity_check")
112 .fetch_one(&pool)
113 .await?;
114 if pragma.0 != "ok" {
115 return Err(MyError::Runtime("Failed integrity_check".into()));
116 }
117
118 let fk_values: Vec<_> = sqlx::query("PRAGMA foreign_key_check")
120 .fetch_all(&pool)
121 .await?;
122 if !fk_values.is_empty() {
123 return Err(MyError::Runtime("Found invalid FK value(s)".into()));
124 }
125
126 let layer = sqlx::query_as::<_, TContents>(FIND_TABLE)
128 .bind(layer_name)
129 .fetch_one(&pool)
130 .await?;
131 if layer.data_type != "features" {
133 return Err(MyError::Runtime("Layer is NOT vector-based".into()));
134 }
135
136 let sql = format!(
138 r#"CREATE VIRTUAL TABLE IF NOT EXISTS "vgpkg_{0}" USING VirtualGPKG("{0}");"#,
139 layer_name
140 );
141 let safe_sql = AssertSqlSafe(sql);
142 sqlx::query(safe_sql).execute(&pool).await?;
143
144 let srid = match layer.srs_id {
145 Some(fk) => {
151 if fk < 1 {
152 let srs = sqlx::query_as::<_, TSpatialRefSys>(FIND_SRS)
157 .bind(fk)
158 .fetch_one(&pool)
159 .await?;
160 let authority = srs.organization;
162 if !authority.eq_ignore_ascii_case(EPSG_AUTH) {
163 return Err(MyError::Runtime(
164 format!("Unexpected ({authority}) Authority").into(),
165 ));
166 }
167
168 let it = srs.organization_coordsys_id;
169 let epsg = format!("{authority}:{fk}");
170 let _ = CRS::new(&epsg)?;
172 Some(u32::try_from(it)?)
173 } else {
174 info!("GeoPackage uses an undefined ({}) SRS ID", fk);
175 None
176 }
177 }
178 None => None,
179 };
180
181 Ok(Self {
182 layer: layer_name.to_owned(),
183 pool,
184 srid,
185 })
186 }
187
188 pub fn pool(&self) -> &Pool<Sqlite> {
190 &self.pool
191 }
192
193 pub fn vtable(&self) -> String {
199 format!("vgpkg_{}", self.layer)
200 }
201
202 pub fn to_sql(&self, exp: &Expression) -> Result<String, MyError> {
205 let mut e = exp.to_inner()?;
206 let it = E::reduce(&mut e)?;
207 to_sql(it)
208 }
209}
210
211fn cmp_ci(a: &str, b: &str) -> Ordering {
213 a.to_lowercase().cmp(&b.to_lowercase())
214}
215
216fn cmp_ai(a: &str, b: &str) -> Ordering {
218 let lhs = a.nfd().filter(|x| !is_combining_mark(*x)).nfc();
219 let rhs = b.nfd().filter(|x| !is_combining_mark(*x)).nfc();
220 lhs.cmp(rhs)
221}
222
223fn cmp_aci(a: &str, b: &str) -> Ordering {
226 let x = a.to_lowercase();
227 let y = b.to_lowercase();
228 let lhs = x.nfd().filter(|x| !is_combining_mark(*x)).nfc();
229 let rhs = y.nfd().filter(|x| !is_combining_mark(*x)).nfc();
230 lhs.cmp(rhs)
231}
232
233fn cmp_bounds(a: &str, b: &str) -> Ordering {
236 let a_is_unbound = matches!(a, "..");
237 let b_is_unbound = matches!(b, "..");
238 match (a_is_unbound, b_is_unbound) {
239 (true, true) => Ordering::Equal,
240 (true, false) => Ordering::Less,
241 (false, true) => Ordering::Greater,
242 (false, false) => a.cmp(b),
243 }
244}
245
246pub(crate) fn to_sql(exp: E) -> Result<String, MyError> {
247 match exp {
248 E::Null => Ok("NULL".to_owned()),
249 E::Unbounded => Ok("'..'".to_owned()),
250 E::Bool(true) => Ok("TRUE".to_owned()),
251 E::Bool(false) => Ok("FALSE".to_owned()),
252 E::Num(x) => Ok(x.to_string()),
253 E::Str(x) => qstr_to_sql(x),
254 E::Date(x) => Ok(format!("'{}'", x.date())),
255 E::Timestamp(x) => Ok(format!("'{}'", x.datetime())),
256 E::Spatial(x) => Ok(x.to_sql()?),
257 E::Id(x) => Ok(x.to_owned()),
258 E::Monadic(op, x) if op.nullable() => {
260 let is_literal = x.is_literal_or_id();
261 let lhs = to_sql(*x)?;
262 let z_op = op.to_sql();
263 if is_literal {
264 Ok(format!("{lhs} {z_op}"))
265 } else {
266 Ok(format!("({lhs}) {z_op}"))
267 }
268 }
269 E::Monadic(op, x) => match op {
270 Op::Neg | Op::Minus => {
271 let is_literal = x.is_literal_or_id();
272 let rhs = to_sql(*x)?;
273 let z_op = op.to_sql();
274 if is_literal {
275 Ok(format!("{z_op} {rhs}"))
276 } else {
277 Ok(format!("{z_op} ({rhs})"))
278 }
279 }
280 Op::CaseI => match *x {
281 E::Monadic(Op::AccentI, y) => {
282 let rhs = to_sql(*y)?;
283 Ok(format!("{rhs} COLLATE {CQL2_CAI}"))
284 }
285 _ => {
286 let rhs = to_sql(*x)?;
287 Ok(format!("{rhs} COLLATE {CQL2_CI}"))
288 }
289 },
290 Op::AccentI => match *x {
291 E::Monadic(Op::CaseI, y) => {
292 let rhs = to_sql(*y)?;
293 Ok(format!("{rhs} COLLATE {CQL2_CAI}"))
294 }
295 _ => {
296 let rhs = to_sql(*x)?;
297 Ok(format!("{rhs} COLLATE {CQL2_AI}"))
298 }
299 },
300 x => unreachable!("Unexpected ({x}) monadic operator"),
301 },
302 E::Dyadic(op, a, b) if matches!(op, Op::IsBetween) || matches!(op, Op::IsNotBetween) => {
303 match *b {
305 E::Array(rhs) => {
306 let z_op = op.to_sql();
307 let lhs = to_sql(*a)?;
308 let lo = to_sql(rhs[0].to_owned())?;
309 let hi = to_sql(rhs[1].to_owned())?;
310 Ok(format!("{lhs} {z_op} {lo} AND {hi}"))
311 }
312 _ => unreachable!("Expetced [NOT] BETWEEN's RHS expression to be an array"),
313 }
314 }
315 E::Dyadic(op, a, b) if op.spatial() => match op {
316 Op::SWithin | Op::SOverlaps | Op::STouches => reduce_precision(op, *a, *b),
317 _ => {
318 let lhs = to_sql(*a)?;
319 let rhs = to_sql(*b)?;
320 let z_op = op.to_sql();
321 Ok(format!("{z_op}({lhs}, {rhs})"))
322 }
323 },
324 E::Dyadic(op, a, b) if op.temporal() => match op {
325 Op::TAfter => t_after_sql(*a, *b),
326 Op::TBefore => t_before_sql(*a, *b),
327 Op::TDisjoint => t_disjoint_sql(*a, *b),
328 Op::TEquals => t_equals_sql(*a, *b),
329 Op::TIntersects => t_intersects_sql(*a, *b),
330
331 Op::TContains => t_contains_sql(*a, *b),
332 Op::TDuring => t_during_sql(*a, *b),
333 Op::TFinishedBy => t_finished_by_sql(*a, *b),
334 Op::TFinishes => t_finishes_sql(*a, *b),
335 Op::TMeets => t_meets_sql(*a, *b),
336 Op::TMetBy => t_met_by_sql(*a, *b),
337 Op::TOverlappedBy => t_overlapped_by_sql(*a, *b),
338 Op::TOverlaps => t_overlaps_sql(*a, *b),
339 Op::TStartedBy => t_started_by_sql(*a, *b),
340 Op::TStarts => t_starts_sql(*a, *b),
341 x => unreachable!("Unexpected ({x:?}) operator"),
342 },
343 E::Dyadic(op, a, b) if op.array() => {
344 let z_op = op.to_sql();
345 let lhs = to_sql(*a)?;
346 let rhs = to_sql(*b)?;
347 Ok(format!("{z_op}({lhs}, {rhs})"))
348 }
349 E::Dyadic(op, a, b) if matches!(op, Op::IsLike) || matches!(op, Op::IsNotLike) => {
350 let a_is_literal = a.is_literal_or_id();
351 let lhs = to_sql(*a)?;
352 let rhs = to_sql(*b)?;
353 let z_op = op.to_sql();
354 match a_is_literal {
355 true => Ok(format!("{lhs} {z_op} ({rhs})")),
356 false => Ok(format!("({lhs}) {z_op} ({rhs})")),
357 }
358 }
359 E::Dyadic(op, a, b) => {
360 let a_is_literal = a.is_literal_or_id();
361 let b_is_literal = b.is_literal_or_id();
362 let lhs = to_sql(*a)?;
363 let rhs = to_sql(*b)?;
364 let z_op = op.to_sql();
365 match (a_is_literal, b_is_literal) {
366 (true, true) => Ok(format!("{lhs} {z_op} {rhs}")),
367 (true, false) => Ok(format!("{lhs} {z_op} ({rhs})")),
368 (false, true) => Ok(format!("({lhs}) {z_op} {rhs}")),
369 (false, false) => Ok(format!("({lhs}) {z_op} ({rhs})")),
370 }
371 }
372 E::Function(x) => {
373 let params: Result<Vec<String>, MyError> = x.params.into_iter().map(to_sql).collect();
374 let params_ = params?;
375 Ok(format!("{}({})", x.name, params_.join(", ")))
376 }
377 E::Array(x) => {
380 let items: Result<Vec<String>, MyError> = x.into_iter().map(to_sql).collect();
381 let items_ = items?;
382 Ok(format!("({})", items_.join(", ")))
383 }
384 x => unreachable!("{x:?} cannot be translated to SQL"),
385 }
386}
387
388fn qstr_to_sql(qs: QString) -> Result<String, MyError> {
390 match qs.flags() {
391 0 => Ok(format!("'{}'", qs.inner())),
392 1 => Ok(format!("'{}' COLLATE {CQL2_CI}", qs.inner())),
393 2 => Ok(format!("'{}' COLLATE {CQL2_AI}", qs.inner())),
394 3 => Ok(format!("'{}' COLLATE {CQL2_CAI}", qs.inner())),
395 x => {
396 let msg = format!("String w/ '{x}' flags has NO direct SQL representation");
397 Err(MyError::Runtime(msg.into()))
398 }
399 }
400}
401
402fn reduce_precision(op: Op, a: E, b: E) -> Result<String, MyError> {
411 let a_is_id = a.is_id();
412 let b_is_id = b.is_id();
413 let (lhs, rhs) = match (a_is_id, b_is_id) {
414 (true, false) => {
415 let lhs = reduce_precision_sql(a)?;
416 let rhs = to_sql(b)?;
417 (lhs, rhs)
418 }
419 (false, true) => {
420 let lhs = to_sql(a)?;
421 let rhs = reduce_precision_sql(b)?;
422 (lhs, rhs)
423 }
424 _ => {
425 let lhs = to_sql(a)?;
426 let rhs = to_sql(b)?;
427 (lhs, rhs)
428 }
429 };
430 let z_op = op.to_sql();
431 Ok(format!("{z_op}({lhs}, {rhs})"))
432}
433
434fn reduce_precision_sql(a: E) -> Result<String, MyError> {
435 let it = format!(
436 "ST_ReducePrecision({}, 1E-{})",
437 to_sql(a)?,
438 config().default_precision()
439 );
440 Ok(it)
441}
442
443fn t_after_sql(a: E, b: E) -> Result<String, MyError> {
445 let (a_is_interval, b_is_interval, e0, e1, e2, e3) = crate::unfold_expressions!(a, b);
446 match (a_is_interval, b_is_interval) {
447 (false, false) => Ok(format!("{} > {}", to_sql(e0)?, to_sql(e2)?)),
448 (false, true) => {
450 let base = format!("{} > {} COLLATE CQL2_BOUNDS", to_sql(e0)?, to_sql(e3)?);
451 let sql = crate::check_ids!(e2, base);
452 Ok(sql)
453 }
454 (true, false) => {
455 let base = format!("{} > {} COLLATE CQL2_BOUNDS", to_sql(e0)?, to_sql(e2)?);
456 let sql = crate::check_ids!(e1, base);
457 Ok(sql)
458 }
459 (true, true) => {
460 let base = format!("{} > {} COLLATE CQL2_BOUNDS", to_sql(e0)?, to_sql(e3)?);
461 let sql = crate::check_ids!(e1, e2, base);
462 Ok(sql)
463 }
464 }
465}
466
467fn t_before_sql(a: E, b: E) -> Result<String, MyError> {
468 let (a_is_interval, b_is_interval, e0, e1, e2, e3) = crate::unfold_expressions!(a, b);
469 match (a_is_interval, b_is_interval) {
470 (false, false) => Ok(format!("{} < {}", to_sql(e0)?, to_sql(e2)?)),
471 (false, true) => {
472 let base = format!("{} < {} COLLATE CQL2_BOUNDS", to_sql(e0)?, to_sql(e2)?);
473 let sql = crate::check_ids!(e3, base);
474 Ok(sql)
475 }
476 (true, false) => {
477 let base = format!("{} < {} COLLATE CQL2_BOUNDS", to_sql(e1)?, to_sql(e2)?);
478 let sql = crate::check_ids!(e0, base);
479 Ok(sql)
480 }
481 (true, true) => {
482 let base = format!("{} < {} COLLATE CQL2_BOUNDS", to_sql(e1)?, to_sql(e2)?);
483 let sql = crate::check_ids!(e0, e3, base);
484 Ok(sql)
485 }
486 }
487}
488
489fn t_disjoint_sql(a: E, b: E) -> Result<String, MyError> {
490 let (a_is_interval, b_is_interval, e0, e1, e2, e3) = crate::unfold_expressions!(a, b);
491 match (a_is_interval, b_is_interval) {
492 (false, false) => Ok(format!("{} != {}", to_sql(e0)?, to_sql(e2)?)),
493 (false, true) => {
494 let e2_ = e2.clone();
495 let e3_ = e3.clone();
496 let s0 = to_sql(e0)?;
497 let s2 = to_sql(e2)?;
498 let s3 = to_sql(e3)?;
499 let base1 = format!("{s0} < {s2}");
500 let sql1 = crate::check_ids!(e3_, base1);
501 let base2 = format!("{s0} > {s3}");
502 let sql2 = crate::check_ids!(e2_, base2);
503 Ok(format!("({sql1}) OR ({sql2})"))
504 }
505 (true, false) => {
506 let e0_ = e0.clone();
507 let e1_ = e1.clone();
508 let s0 = to_sql(e0)?;
509 let s1 = to_sql(e1)?;
510 let s2 = to_sql(e2)?;
511 let base1 = format!("{s1} < {s2}");
512 let sql1 = crate::check_ids!(e0_, base1);
513 let base2 = format!("{s0} > {s2}");
514 let sql2 = crate::check_ids!(e1_, base2);
515 Ok(format!("({sql1}) OR ({sql2})"))
516 }
517 (true, true) => {
518 let e0_ = e0.clone();
519 let e1_ = e1.clone();
520 let e2_ = e2.clone();
521 let e3_ = e3.clone();
522 let s0 = to_sql(e0)?;
523 let s1 = to_sql(e1)?;
524 let s2 = to_sql(e2)?;
525 let s3 = to_sql(e3)?;
526 let base1 = format!("{s1} < {s2}");
527 let sql1 = crate::check_ids!(e0_, e3_, base1);
528 let base2 = format!("{s0} > {s3}");
529 let sql2 = crate::check_ids!(e1_, e2_, base2);
530 Ok(format!("({sql1}) OR ({sql2})"))
531 }
532 }
533}
534
535fn t_equals_sql(a: E, b: E) -> Result<String, MyError> {
536 let (a_is_interval, b_is_interval, e0, e1, e2, e3) = crate::unfold_expressions!(a, b);
537 match (a_is_interval, b_is_interval) {
538 (false, false) => Ok(format!("{} == {}", to_sql(e0)?, to_sql(e2)?)),
539 (false, true) => Ok(format!(
540 "({0} == {1}) AND ({0} == {2})",
541 to_sql(e0)?,
542 to_sql(e2)?,
543 to_sql(e3)?
544 )),
545 (true, false) => Ok(format!(
546 "({0} == {2}) AND ({1} == {2})",
547 to_sql(e0)?,
548 to_sql(e1)?,
549 to_sql(e2)?
550 )),
551 (true, true) => Ok(format!(
552 "({0} == {2}) AND ({1} == {3})",
553 to_sql(e0)?,
554 to_sql(e1)?,
555 to_sql(e2)?,
556 to_sql(e3)?
557 )),
558 }
559}
560
561fn t_intersects_sql(a: E, b: E) -> Result<String, MyError> {
562 let (a_is_interval, b_is_interval, e0, e1, e2, e3) = crate::unfold_expressions!(a, b);
563 match (a_is_interval, b_is_interval) {
564 (false, false) => Ok(format!("{} == {}", to_sql(e0)?, to_sql(e2)?)),
565 (false, true) => Ok(format!(
566 "NOT(({0} < {1}) OR ({0} > {2}))",
567 to_sql(e0)?,
568 to_sql(e2)?,
569 to_sql(e3)?
570 )),
571 (true, false) => Ok(format!(
572 "NOT(({1} < {2}) OR ({0} > {2}))",
573 to_sql(e0)?,
574 to_sql(e1)?,
575 to_sql(e2)?
576 )),
577 (true, true) => Ok(format!(
578 "NOT(({1} < {2}) OR ({0} > {3}))",
579 to_sql(e0)?,
580 to_sql(e1)?,
581 to_sql(e2)?,
582 to_sql(e3)?
583 )),
584 }
585}
586
587fn t_contains_sql(a: E, b: E) -> Result<String, MyError> {
589 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
590 Ok(format!(
591 "({0} < {2}) AND ({1} > {3})",
592 to_sql(e0)?,
593 to_sql(e1)?,
594 to_sql(e2)?,
595 to_sql(e3)?
596 ))
597}
598
599fn t_during_sql(a: E, b: E) -> Result<String, MyError> {
600 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
601 Ok(format!(
602 "({0} > {2}) AND ({1} < {3})",
603 to_sql(e0)?,
604 to_sql(e1)?,
605 to_sql(e2)?,
606 to_sql(e3)?
607 ))
608}
609
610fn t_finished_by_sql(a: E, b: E) -> Result<String, MyError> {
611 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
612 Ok(format!(
613 "({0} < {2}) AND ({1} == {3})",
614 to_sql(e0)?,
615 to_sql(e1)?,
616 to_sql(e2)?,
617 to_sql(e3)?
618 ))
619}
620
621fn t_finishes_sql(a: E, b: E) -> Result<String, MyError> {
622 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
623 Ok(format!(
624 "({0} > {2}) AND ({1} == {3})",
625 to_sql(e0)?,
626 to_sql(e1)?,
627 to_sql(e2)?,
628 to_sql(e3)?
629 ))
630}
631
632fn t_meets_sql(a: E, b: E) -> Result<String, MyError> {
633 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
634 let base = format!("{0} == {1}", to_sql(e1)?, to_sql(e2)?);
635 let sql = crate::check_ids!(e0, e3, base);
636 Ok(sql)
637}
638
639fn t_met_by_sql(a: E, b: E) -> Result<String, MyError> {
640 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
641 let base = format!("{0} == {1}", to_sql(e0)?, to_sql(e3)?);
642 let sql = crate::check_ids!(e1, e2, base);
643 Ok(sql)
644}
645
646fn t_overlapped_by_sql(a: E, b: E) -> Result<String, MyError> {
647 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
648 Ok(format!(
649 "({0} > {2}) AND ({0} < {3}) AND ({1} > {3})",
650 to_sql(e0)?,
651 to_sql(e1)?,
652 to_sql(e2)?,
653 to_sql(e3)?
654 ))
655}
656
657fn t_overlaps_sql(a: E, b: E) -> Result<String, MyError> {
658 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
659 Ok(format!(
660 "({0} < {2}) AND ({1} > {2}) AND ({1} < {3})",
661 to_sql(e0)?,
662 to_sql(e1)?,
663 to_sql(e2)?,
664 to_sql(e3)?
665 ))
666}
667
668fn t_started_by_sql(a: E, b: E) -> Result<String, MyError> {
669 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
670 Ok(format!(
671 "({0} == {2}) AND ({1} > {3})",
672 to_sql(e0)?,
673 to_sql(e1)?,
674 to_sql(e2)?,
675 to_sql(e3)?
676 ))
677}
678
679fn t_starts_sql(a: E, b: E) -> Result<String, MyError> {
680 let (e0, e1, e2, e3) = crate::unfold_intervals!(a, b);
681 Ok(format!(
682 "({0} == {2}) AND ({1} < {3})",
683 to_sql(e0)?,
684 to_sql(e1)?,
685 to_sql(e2)?,
686 to_sql(e3)?
687 ))
688}
689
690#[macro_export]
702macro_rules! gen_gpkg_ds {
703 ($vis:vis, $name:expr, $gpkg_url:expr, $layer:expr, $feature:expr) => {
704 paste::paste! {
705 $vis struct [<$name GPkg>](GPkgDataSource);
707
708 impl [<$name GPkg>] {
709 $vis async fn new() -> Result<Self, MyError> {
711 let gpkp = GPkgDataSource::from($gpkg_url, $layer).await?;
712 Ok(Self(gpkp))
713 }
714
715 $vis fn to_resource(r: $feature) -> Result<Resource, Box<dyn Error>> {
717 let row = $feature::try_from(r)?;
718 Ok(Resource::try_from(row)?)
719 }
720
721 $vis fn vtable(&self) -> String {
723 self.0.vtable()
724 }
725 }
726
727 impl ::core::fmt::Display for [<$name GPkg>] {
728 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
729 write!(f, "{}GPkg({})", $name, $layer)
730 }
731 }
732
733 #[::async_trait::async_trait]
734 impl StreamableDS for [<$name GPkg>] {
735 type Item = $feature;
736 type Err = MyError;
737
738 async fn fetch(
739 &self
740 ) -> Result<::futures::stream::BoxStream<'_, Result<$feature, MyError>>, MyError> {
741 let sql = format!("SELECT * FROM {}", $layer);
742 let safe_sql = AssertSqlSafe(sql);
743 let it = sqlx::query_as::<_, $feature>(safe_sql)
744 .fetch(self.0.pool())
745 .map_err(MyError::SQL);
746 Ok(Box::pin(it))
747 }
748
749 async fn stream(
750 &self
751 ) -> Result<::futures::stream::BoxStream<'_, Result<Resource, MyError>>, MyError> {
752 let rows = self.fetch().await?;
753 let resources = rows
754 .try_filter_map(|row| async move {
755 match Resource::try_from(row) {
756 Ok(x) => Ok(Some(x)),
757 Err(x) => Err(x),
758 }
759 })
760 .boxed();
761 Ok(resources)
762 }
763
764 async fn fetch_where(
765 &self,
766 exp: &Expression,
767 ) -> Result<::futures::stream::BoxStream<'_, Result<$feature, MyError>>, MyError> {
768 let where_clause = self.0.to_sql(exp)?;
769 let sql = format!(r#"SELECT * FROM "{}" WHERE {}"#, self.vtable(), where_clause);
771 let safe_sql = AssertSqlSafe(sql);
772 let it = sqlx::query_as::<_, $feature>(safe_sql)
773 .fetch(self.0.pool())
774 .map_err(MyError::SQL);
775 Ok(Box::pin(it))
776 }
777
778 async fn stream_where(
779 &self,
780 exp: &Expression,
781 ) -> Result<::futures::stream::BoxStream<'_, Result<Resource, MyError>>, MyError> {
782 let rows = self.fetch_where(exp).await?;
783 let resources = rows
784 .try_filter_map(|row| async move {
785 match Resource::try_from(row) {
786 Ok(x) => Ok(Some(x)),
787 Err(x) => Err(x),
788 }
789 })
790 .boxed();
791 Ok(resources)
792 }
793 }
794 }
795 };
796}
797
798#[macro_export]
803macro_rules! unfold_expressions {
804 ( $a: expr, $b: expr ) => {{
805 let a_is_interval = $a.is_interval();
806 let b_is_interval = $b.is_interval();
807 match (a_is_interval, b_is_interval) {
808 (false, false) => (false, false, $a, E::Null, $b, E::Null),
809 (false, true) => {
810 let t2 = $b.as_interval().expect("2nd argument is NOT an interval");
811 (false, true, $a, E::Null, t2.0, t2.1)
812 }
813 (true, false) => {
814 let t1 = $a.as_interval().expect("1st argument is NOT an interval");
815 (true, false, t1.0, t1.1, $b, E::Null)
816 }
817 (true, true) => {
818 let t1 = $a.as_interval().expect("1st argument is NOT an interval");
819 let t2 = $b.as_interval().expect("2nd argument is NOT an interval");
820 (true, true, t1.0, t1.1, t2.0, t2.1)
821 }
822 }
823 }};
824}
825
826#[macro_export]
829macro_rules! check_ids {
830 ( $a: expr, $sql: expr ) => {{
831 if $a.is_id() {
832 let id = $a.as_id().expect("Argument is not an Identifier");
833 format!("\"{}\" NOT NULL AND ({})", id, $sql)
834 } else {
835 $sql
836 }
837 }};
838
839 ( $a: expr, $b: expr, $sql: expr ) => {{
840 match ($a.is_id(), $b.is_id()) {
841 (true, true) => {
842 let id1 = $a.as_id().expect("1st argument is not an Identifier");
843 let id2 = $b.as_id().expect("2nd argument is not an Identifier");
844 format!(
845 "\"{}\" NOT NULL AND \"{}\" NOT NULL AND ({})",
846 id1, id2, $sql
847 )
848 }
849 (true, false) => {
850 let id = $a.as_id().expect("1st argument is not an Identifier");
851 format!("\"{}\" NOT NULL AND ({})", id, $sql)
852 }
853 (false, true) => {
854 let id = $b.as_id().expect("2nd argument is not an Identifier");
855 format!("\"{}\" NOT NULL AND ({})", id, $sql)
856 }
857 (false, false) => $sql,
858 }
859 }};
860}
861
862#[macro_export]
865macro_rules! unfold_intervals {
866 ( $a: expr, $b: expr ) => {{
867 let t1 = $a.as_interval().expect("1st argument is NOT an interval");
868 let t2 = $b.as_interval().expect("2nd argument is NOT an interval");
869 (t1.0, t1.1, t2.0, t2.1)
870 }};
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876
877 #[test]
878 fn test_cmp_ci() {
879 let eq = cmp_ci("abc", "ABC");
880 assert_eq!(eq, Ordering::Equal);
881
882 let eq = cmp_ci("ABC", "abc");
883 assert_eq!(eq, Ordering::Equal);
884
885 let eq = cmp_ci("aBc", "AbC");
886 assert_eq!(eq, Ordering::Equal);
887
888 let eq = cmp_ci("abcd", "ABCe");
889 assert_eq!(eq, Ordering::Less);
890
891 let eq = cmp_ci("bcd", "ACz");
892 assert_eq!(eq, Ordering::Greater);
893 }
894
895 #[test]
896 fn test_cmp_ai() {
897 let eq = cmp_ai("ÁBC", "ABC");
898 assert_eq!(eq, Ordering::Equal);
899
900 let eq = cmp_ai("ÁBC", "ABÇ");
901 assert_eq!(eq, Ordering::Equal);
902 }
903
904 #[test]
905 fn test_cmp_aci() {
906 let eq = cmp_aci("ábc", "ABÇ");
907 assert_eq!(eq, Ordering::Equal);
908 }
909}