/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use super::{MysqlStore, into_error, is_timeout_error}; use crate::{Deserialize, IterateParams, Key, ValueKey, write::ValueClass}; use futures::TryStreamExt; use mysql_async::{Row, prelude::Queryable}; impl MysqlStore { pub(crate) async fn get_value(&self, key: impl Key) -> trc::Result> where U: Deserialize + 'static, { let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?; let s = conn .prep(format!( "SELECT v FROM {} WHERE k = ?", char::from(key.subspace()) )) .await .map_err(into_error)?; let key = key.serialize(0); conn.exec_first::, _, _>(&s, (&key,)) .await .map_err(into_error) .and_then(|r| { if let Some(r) = r { Ok(Some(U::deserialize_owned_with_key(&key, r)?)) } else { Ok(None) } }) } pub(crate) async fn key_exists(&self, key: impl Key) -> trc::Result { let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?; let s = conn .prep(format!( "SELECT 1 FROM {} WHERE k = ?", char::from(key.subspace()) )) .await .map_err(into_error)?; let key = key.serialize(0); conn.exec_first::(&s, (&key,)) .await .map_err(into_error) .map(|r| r.is_some()) } pub(crate) async fn iterate( &self, params: IterateParams, mut cb: impl for<'x> FnMut(&'x [u8], &'x [u8]) -> trc::Result + Sync + Send, ) -> trc::Result<()> { let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?; let table = char::from(params.begin.subspace()); let begin = params.begin.serialize(0); let end = params.end.serialize(0); let keys = if params.values { "k, v" } else { "k" }; let s = conn .prep(&match (params.first, params.ascending) { (true, true) => { format!( "SELECT {keys} FROM {table} WHERE k >= ? AND k <= ? ORDER BY k ASC LIMIT 1" ) } (true, false) => { format!( "SELECT {keys} FROM {table} WHERE k >= ? AND k <= ? ORDER BY k DESC LIMIT 1" ) } (false, true) => { format!("SELECT {keys} FROM {table} WHERE k >= ? AND k <= ? ORDER BY k ASC") } (false, false) => { format!("SELECT {keys} FROM {table} WHERE k >= ? AND k <= ? ORDER BY k DESC") } }) .await .map_err(into_error)?; let mut from = begin; let mut to = end; let mut resume_key = None; loop { let mut last_key = None; let mut timed_out = false; { let mut rows = conn .exec_stream::(&s, (from.clone(), to.clone())) .await .map_err(into_error)?; loop { match rows.try_next().await { Ok(Some(mut row)) => { let value = if params.values { row.take_opt::, _>(1) .unwrap_or_else(|| Ok(vec![])) .map_err(into_error)? } else { vec![] }; let key = row .take_opt::, _>(0) .unwrap_or_else(|| Ok(vec![])) .map_err(into_error)?; if resume_key.take().is_some_and(|resumed| resumed == key) { continue; } if !cb(&key, &value)? { return Ok(()); } last_key = Some(key); } Ok(None) => break, Err(err) => { if params.first || last_key.is_none() || !is_timeout_error(&err) { return Err(into_error(err)); } timed_out = true; break; } } } } match last_key { Some(last_key) if timed_out => { if params.ascending { from.clone_from(&last_key); } else { to.clone_from(&last_key); } resume_key = Some(last_key); } _ => return Ok(()), } } } pub(crate) async fn get_counter( &self, key: impl Into> + Sync + Send, ) -> trc::Result { let key = key.into(); let table = char::from(key.subspace()); let key = key.serialize(0); let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?; let s = conn .prep(format!("SELECT v FROM {table} WHERE k = ?")) .await .map_err(into_error)?; match conn.exec_first::(&s, (key,)).await { Ok(Some(num)) => Ok(num), Ok(None) => Ok(0), Err(e) => Err(into_error(e)), } } }