authored_draft_row.rs (2949B)
1 //! Bound result columns before SQLx materializes a stored authored revision. 2 //! Invalid nullable metadata uses a noncanonical one-byte sentinel, never NULL: 3 //! corruption must not turn into an accepted missing operation or scope. 4 5 use radroots_storage::{Error, authored_draft::AuthoredDraftRevision}; 6 use sqlx::{Executor, Sqlite, sqlite::SqliteRow}; 7 8 macro_rules! select { 9 ($suffix:literal) => { 10 concat!( 11 "SELECT 12 CASE WHEN typeof(draft_id) = 'blob' AND octet_length(draft_id) = 16 THEN draft_id END AS draft_id, 13 CASE WHEN typeof(revision) = 'integer' THEN revision END AS revision, 14 CASE WHEN typeof(author) = 'blob' AND octet_length(author) = 32 THEN author END AS author, 15 CASE WHEN typeof(stage) = 'integer' THEN stage END AS stage, 16 CASE WHEN operation_id IS NULL OR (typeof(operation_id) = 'blob' AND octet_length(operation_id) = 16) 17 THEN operation_id ELSE X'00' END AS operation_id, 18 CASE WHEN typeof(payload_sha256) = 'blob' AND octet_length(payload_sha256) = 32 THEN payload_sha256 END AS payload_sha256, 19 CASE WHEN typeof(created_at_unix_ms) = 'integer' THEN created_at_unix_ms END AS created_at_unix_ms, 20 CASE WHEN typeof(updated_at_unix_ms) = 'integer' THEN updated_at_unix_ms END AS updated_at_unix_ms, 21 CASE WHEN typeof(snapshot) = 'blob' AND octet_length(snapshot) BETWEEN 1 AND 16777216 THEN snapshot END AS snapshot, 22 CASE WHEN typeof(payload_schema) = 'text' AND octet_length(payload_schema) <= 128 THEN payload_schema END AS payload_schema, 23 CASE WHEN payload_scope IS NULL OR (typeof(payload_scope) = 'blob' AND octet_length(payload_scope) = 32) 24 THEN payload_scope ELSE X'00' END AS payload_scope 25 FROM radroots_runtime_authored_draft_revisions ", 26 $suffix 27 ) 28 }; 29 } 30 31 const HEAD: &str = select!( 32 "WHERE draft_id = ? AND ? IS NULL ORDER BY radroots_runtime_authored_draft_revisions.revision DESC LIMIT 1" 33 ); 34 const REVISION: &str = select!("WHERE draft_id = ? AND revision = ?"); 35 36 pub(super) async fn load<'e>( 37 executor: impl Executor<'e, Database = Sqlite>, 38 key: &[u8; 16], 39 revision: Option<AuthoredDraftRevision>, 40 ) -> Result<Option<SqliteRow>, Error> { 41 let selected = if revision.is_some() { REVISION } else { HEAD }; 42 let revision = revision 43 .map(|value| super::i64_from_u64(value.get())) 44 .transpose()?; 45 // Both alternatives are closed compile-time SQL above. Every data value is 46 // bound separately; this private helper accepts no caller-supplied SQL. 47 sqlx::query(sqlx::AssertSqlSafe(selected)) 48 .bind(key.as_slice()) 49 .bind(revision) 50 .fetch_optional(executor) 51 .await 52 .map_err(super::map_backend) 53 } 54 55 #[cfg(test)] 56 #[cfg_attr(coverage_nightly, coverage(off))] 57 #[path = "authored_draft_row_tests.rs"] 58 mod tests;