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