1use crate::{
2 error::{c4error_init, Error, Result},
3 ffi::{
4 c4query_new2, c4query_release, c4query_run, c4query_setParameters, c4queryenum_next,
5 c4queryenum_release, C4Query, C4QueryEnumerator, C4String, FLArrayIterator_GetCount,
6 FLArrayIterator_GetValueAt, FLStringResult, FLValue,
7 },
8 value::{FromValueRef, ValueRef},
9 Database, QueryLanguage,
10};
11use fallible_streaming_iterator::FallibleStreamingIterator;
12use serde::Serialize;
13use serde_fleece::NonNullConst;
14use std::ptr::{self, NonNull};
15
16pub struct Query<'db> {
17 _db: &'db Database,
18 inner: NonNull<C4Query>,
19}
20
21impl Drop for Query<'_> {
22 #[inline]
23 fn drop(&mut self) {
24 unsafe { c4query_release(self.inner.as_ptr()) };
25 }
26}
27
28impl Query<'_> {
29 pub(crate) fn new<'a>(
30 db: &'a Database,
31 query_lang: QueryLanguage,
32 query: &str,
33 ) -> Result<Query<'a>> {
34 let mut c4err = c4error_init();
35 let mut out_error_pos = -1;
36 let query = unsafe {
37 c4query_new2(
38 db.inner.0.as_ptr(),
39 query_lang,
40 query.into(),
41 &mut out_error_pos,
42 &mut c4err,
43 )
44 };
45
46 NonNull::new(query)
47 .map(|inner| Query { _db: db, inner })
48 .ok_or_else(|| c4err.into())
49 }
50
51 pub fn set_parameters_fleece(
54 &self,
55 parameters: std::result::Result<FLStringResult, serde_fleece::Error>,
56 ) -> Result<()> {
57 let params = parameters?;
58 unsafe {
59 c4query_setParameters(self.inner.as_ptr(), params.as_fl_slice());
60 }
61 Ok(())
62 }
63
64 pub fn set_parameters<T>(&self, parameters: &T) -> Result<()>
65 where
66 T: Serialize,
67 {
68 let param_string = serde_fleece::to_fl_slice_result(parameters)?;
69 unsafe {
70 c4query_setParameters(self.inner.as_ptr(), param_string.as_fl_slice());
71 }
72 Ok(())
73 }
74
75 pub fn run<'a>(&'a self) -> Result<Enumerator<'a>> {
76 let mut c4err = c4error_init();
77 let it = unsafe {
78 c4query_run(
79 self.inner.as_ptr(),
80 ptr::null(),
81 C4String::default(),
82 &mut c4err,
83 )
84 };
85
86 NonNull::new(it)
87 .map(|inner| Enumerator {
88 _query: self,
89 reach_end: false,
90 inner,
91 })
92 .ok_or_else(|| c4err.into())
93 }
94}
95
96pub struct Enumerator<'query> {
97 _query: &'query Query<'query>,
98 reach_end: bool,
99 inner: NonNull<C4QueryEnumerator>,
100}
101
102impl Drop for Enumerator<'_> {
103 #[inline]
104 fn drop(&mut self) {
105 unsafe { c4queryenum_release(self.inner.as_ptr()) };
106 }
107}
108
109impl<'en> FallibleStreamingIterator for Enumerator<'en> {
110 type Error = crate::error::Error;
111 type Item = Enumerator<'en>;
112
113 fn advance(&mut self) -> Result<()> {
114 if self.reach_end {
115 return Ok(());
116 }
117 let mut c4err = c4error_init();
118 if unsafe { c4queryenum_next(self.inner.as_ptr(), &mut c4err) } {
119 Ok(())
120 } else if c4err.code == 0 {
121 self.reach_end = true;
122 Ok(())
123 } else {
124 Err(c4err.into())
125 }
126 }
127
128 #[inline]
129 fn get(&self) -> Option<&Enumerator<'en>> {
130 if !self.reach_end {
131 Some(self)
132 } else {
133 None
134 }
135 }
136}
137
138impl<'a> Enumerator<'a> {
139 fn do_get_raw_checked(&self, i: u32) -> Result<FLValue> {
140 let n = unsafe { FLArrayIterator_GetCount(&self.inner.as_ref().columns) };
141 if i >= n {
142 return Err(Error::LogicError(
143 format!("Enumerator::get_raw_checked: Index out of bounds {i} / {n}").into(),
144 ));
145 }
146
147 Ok(unsafe { FLArrayIterator_GetValueAt(&self.inner.as_ref().columns, i) })
148 }
149
150 #[inline]
151 pub fn get_raw_checked(&self, i: u32) -> Result<ValueRef<'a>> {
152 let value = self.do_get_raw_checked(i)?;
153
154 let val = unsafe { ValueRef::new(value) };
155 Ok(val)
156 }
157
158 #[inline]
159 pub fn get_checked<T>(&self, i: u32) -> Result<T>
160 where
161 T: FromValueRef<'a>,
162 {
163 let value_ref = self.get_raw_checked(i)?;
164 FromValueRef::column_result(value_ref)
165 }
166
167 #[inline]
168 pub fn get_checked_serde<'de, T: serde::de::Deserialize<'de>>(&'de self, i: u32) -> Result<T> {
169 let value = self.do_get_raw_checked(i)?;
170 let value = NonNullConst::new(value).ok_or_else(|| {
171 Error::LogicError(format!("Query parameter {i} is null, can not deserialize").into())
172 })?;
173 serde_fleece::from_fl_value(value).map_err(Error::from)
174 }
175}