Skip to main content

helios_sof/sqlquery/
mod.rs

1//! SQL-on-FHIR v2 `$sqlquery-run` engine.
2//!
3//! Pure execution logic: parse a SQLQuery Library, materialize its `depends-on`
4//! ViewDefinitions into an in-memory SQLite database, bind `Library.parameter`
5//! values to the SQL, run the user query, and format the rows.
6//!
7//! The REST handler in `helios-rest` wires this to storage (resolving Library
8//! / ViewDefinition resources and supplying `RowStream`s from the wired
9//! `SofRunner`); this module contains no storage or HTTP concerns.
10
11pub mod bind;
12pub mod engine;
13pub mod library;
14pub mod output;
15pub mod params;
16pub mod scan;
17
18pub use bind::{BINDABLE_PARAMETER_TYPES, BoundParam, bind_supplied_params};
19pub use engine::{ColumnFhirType, InMemorySqlEngine, QueryResult, TableSchema};
20pub use library::{DependsOnView, LibraryParameter, SqlQueryLibrary, parse_sqlquery_library};
21pub use output::format_fhir_parameters;
22pub use params::{SqlQueryRunParams, extract_sqlquery_params_from_json};
23pub use scan::{
24    Placeholder, ScanError, ScanResult, SourcePosition, TableRef, scan_sql, undeclared_tables,
25};
26
27use thiserror::Error;
28
29/// Errors produced by the `$sqlquery-run` pipeline.
30#[derive(Debug, Error)]
31pub enum SqlQueryError {
32    #[error("malformed Library: {0}")]
33    MalformedLibrary(String),
34
35    #[error("SQLQuery Library has no SQL content")]
36    MissingSql,
37
38    #[error("depends-on entry missing label")]
39    MissingDependsOnLabel,
40
41    #[error("could not resolve canonical URL: {0}")]
42    UnknownCanonical(String),
43
44    #[error("too many depends-on ViewDefinitions: {count} (max {max})")]
45    TooManyDependsOn { count: usize, max: usize },
46
47    #[error("row limit exceeded ({max} rows)")]
48    RowCapExceeded { max: usize },
49
50    #[error("query exceeded {secs}s timeout")]
51    Timeout { secs: u64 },
52
53    #[error("SQL parse error: {0}")]
54    NotSelect(String),
55
56    #[error("invalid parameter binding: {0}")]
57    BindParameter(String),
58
59    #[error("invalid identifier '{0}': must not contain a double-quote")]
60    InvalidIdentifier(String),
61
62    #[error("SQLite error: {0}")]
63    Sqlite(#[from] rusqlite::Error),
64
65    #[error("composite SQL value for column '{0}' cannot be represented as a FHIR scalar")]
66    UnsupportedFhirValue(String),
67
68    /// The `SofRunner` stream feeding a `depends-on` dependency table
69    /// yielded an error — a storage failure, a backend statement timeout,
70    /// or a lost connection. The dependency was not materialized. This is
71    /// never the client's fault (the Library and its ViewDefinitions may be
72    /// perfectly well-formed) and must be surfaced as a server error, not
73    /// folded into [`SqlQueryError::MalformedLibrary`].
74    #[error("dependency source failed: {0}")]
75    SourceStream(String),
76
77    /// A failure in the server's own execution machinery rather than in the
78    /// client's request — e.g. the blocking worker that materializes a
79    /// depends-on ViewDefinition's row stream panicked. This is never
80    /// caused by malformed client input and should be surfaced as a 500,
81    /// not as a validation error.
82    #[error("internal error: {0}")]
83    Internal(String),
84}