Skip to main content

couchbase_lite/
query.rs

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    /// convinient function to call with macros `serde_fleece::fleece`
52    /// as parameter
53    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}